본 포스트에서는 리액트를 이용하여 네이버 API로 데이터를 검색해서 목록 화면을 구성해 보겠습니다.
 
 

저번 포스팅까지 해서 목록을 구성하고 탭 메뉴를 만들어서 탭을 전환할 때마다 탭에 맞는 Naver API를 호출하도록 해보았습니다. 현재까지 작업한 파일 및 폴더 구조는 아래와 같습니다.

먼저 만들어 볼 UI를 확인해 보겠습니다. 01. Concept 포스팅에서 보면 아래와 같습니다.

UI는 위와 같이 구성하고, 스크롤 영역에는 포함시키지 않고 상단에 고정합니다. 그리고 키워드를 입력하고 검색 버튼을 터치하면 현재 탭에 맞는 검색 결과를 표시합니다.

 

/src/component/ 경로에 searchbar.component.jsx 파일을 생성하고 아래와 같이 UI Rendering 부분을 작성합니다.

import React from "react";

const SearchBar = () => {
    return (
        <div className="header">
            <input type="text" className="iptSearch" />
            <button type="button" className="search">
                <span>검색</span>
            </button>
        </div>
    )
};

export default SearchBar;

생성한 SearchBar 컴포넌트를 사용하기 위해 app.js 에 SearchBar 컴포넌트를 추가합니다.

import React from "react";
import { atom, RecoilRoot } from "recoil";
import ListView from "./component/listview.component.jsx";
import TabList from "./component/tablist.component.jsx";
import SearchBar from "./component/searchbar.component.jsx";

export const selectedTabId = atom({
    key: 'tabId',
    default: 'news'
});

const App = () => {
    return (
        <RecoilRoot>
            <div>
                {/* ADD :: Start */}
                <SearchBar />
                {/* ADD :: End */}
                <ListView />
                <TabList />
            </div>
        </RecoilRoot>
    )
};

export default App;

상단에 고정할 수 있도록 stylesheet를 아래와 같이 대강 추가해 줍니다.

.header{padding:10px;border-bottom:1px solid #DDD;position:fixed;top:0;display:inline-block;z-index:100;width:100%;background-color:#FFF;}
.iptSearch{width:calc(100% - 110px);border:1px solid #EEE;line-height:27px;padding:0px 10px;margin-right:10px;}
button.search{padding:5px 10px;}

Header 영역이 추가되면서 목록 영역은 상단 부분의 자리를 좀 비켜줘야 합니다. 목록 영역의 class인 listArea class에 상단 여백을 추가합니다.

.listArea{width:100%;margin-bottom:50px;margin-top:52px;}

여기까지 하면 검색에 사용할 컴포넌트를 생성하고 페이지에 컴포넌트를 추가했고, 컴포넌트의 모양을 잡아주는 stylesheet도 만들었습니다. 실행시켜 보면 아래와 같은 화면을 확인할 수 있습니다.

 

UI를 어느 정도 만들었으니 이제 검색 기능을 구현해 보겠습니다. 먼저 app.js 에 검색 키워드 상태 값 지정에 필요한 atom을 하나 추가로 생성합니다. 기본 검색어는 "코로나"로 지정했습니다.

import React from "react";
import { atom, RecoilRoot } from "recoil";
import ListView from "./component/listview.component.jsx";
import TabList from "./component/tablist.component.jsx";
import SearchBar from "./component/searchbar.component.jsx";

export const selectedTabId = atom({
    key: 'tabId',
    default: 'news'
});

//ADD :: Start
export const searchKeyword = atom({
    key: 'keyword',
    default: '코로나'
});
//ADD :: End

const App = () => {
    return (
        <RecoilRoot>
            <div>
                <SearchBar />
                <ListView />
                <TabList />
            </div>
        </RecoilRoot>
    )
};

export default App;

다음은 SearchBar 컴포넌트에서 검색어를 입력하고 [검색] 버튼을 터치하면 입력한 검색어를 상태 값에 저장하는 기능을 아래와 같이 구현합니다. 먼저 RecoilState를 선언한 후 [검색] 버튼을 터치하면 setter를 호출하여 저장합니다.

import React from "react";
import { useRecoilState } from "recoil";
import { searchKeyword } from "../app";

const SearchBar = () => {
    //ADD :: Start
    const [keyword, setKeyword] = useRecoilState(searchKeyword);

    const search = () => {
        const searchKeyword = document.querySelector('#keyword').value;
        setKeyword(searchKeyword);
    };
    //ADD :: End

    return (
        <div className="header">
            <input type="text" className="iptSearch" id="keyword" />
            {/* onClick 추가 */}
            <button type="button" className="search" onClick={search}>
                <span>검색</span>
            </button>
        </div>
    )
};

export default SearchBar;

값을 저장하는 부분까지 진행했으니 이제 ListView 컴포넌트에서 저장한 값을 추출해서 사용하도록 구현합니다. 먼저 위와 동일하게 RecoilState를 하나 더 선언합니다.

const [keyword, setKeyword] = useRecoilState(searchKeyword);

useEffect를 구현한 부분에 keyword를 하드코딩 값이 아닌 keyword 값으로 변경해주고, 탭 아이디나 키워드가 변경되면 다시 인터페이스를 수행하도록 변경합니다.

useEffect(() => {
    apiGet(selTabId, keyword);
}, [selTabId, keyword]);

ListView 컴포넌트의 전체 소스코드는 아래와 같습니다.

const ListView = () => {

    const [articles, setArticles] = useState(null);
    const [selTabId, setSelTabId] = useRecoilState(selectedTabId);
    const [keyword, setKeyword] = useRecoilState(searchKeyword);

    const apiGet = async (type, param) => {
        const apiUrl = 'https://openapi.naver.com/v1/search/' + type + '?query=' + param;
        const resp = await fetch(apiUrl, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'X-Naver-Client-Id': CLIENT_ID,
                'X-Naver-Client-Secret': CLIENT_SECRET
            }
        });
        resp.json().then(data => {
            setArticles(data.items);
        });

    };

    useEffect(() => {
        apiGet(selTabId, keyword);
    }, [selTabId, keyword]);

    return (
        <div className="listArea">
            <ul className="listView">
            {
                articles &&
                (selTabId === 'news') ?
                    articles.map((v, inx) => {
                        return <NewsRow key={inx} row={v} />
                    })
                : (selTabId === 'book') ?
                    articles.map((v, inx) => {
                        return <BookRow key={inx} row={v} />
                    })
                : ''
            }
            </ul>
        </div>
    );
}

여기까지가 끝인 것 같습니다. 검색을 해보고 탭 이동을 해보면 검색 결과가 정상적으로 잘 표시됨을 확인할 수 있습니다.

 

300x250

+ Recent posts