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

저번 포스트에서는 간단하게 "Hello World"를 출력하는 프로그램을 작성하고 실행시켜서 확인해 보았습니다. 이번 포스트에서는 현재 과정의 주 목적인 ListView를 구성하기 위해 Naver News API를 호출해서 데이터를 확인해보는 과정까지 개발해 보겠습니다.

 

js, jsx 파일은 /src 아래 경로에 작성하면 됩니다.

 

먼저 /src 하위에 /component 라는 폴더를 만들고 listview.component.jsx 파일을 하나 생성하고 아래 설명을 참고하여 목록 컴포넌트를 작성해 봅니다. 중간중간 코드에 추가한 부분은 아래와 같이 블럭으로 표시하겠습니다.

//ADD :: START
추가한 코드
//ADD :: END

 

먼저 컴포넌트를 생성하고, 현재 컴포넌트는 목록 컴포넌트이므로 데이터를 조회할 때마다 상태를 변경하고 목록을 갱신하기 위해 useState Hook을 사용하여 상태를 선언하고 초기 값은 null로 설정합니다.

import React from "react";
import { useEffect, useState } from "react"

const ListView = () => {
    const [articles, setArticles] = useState(null);
}

export default ListView;

목록에 필요한 데이터는 Naver API를 호출하여 받아와야 합니다. fetch API를 사용하여 Naver API를 GET 방식으로 호출하는 apiGet이라는 이름의 함수를 하나 정의합니다.

import React from "react";
import { useEffect, useState } from "react"

const ListView = () => {
    const [articles, setArticles] = useState(null);
	
    //ADD :: START
    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);
        });
    };
    //ADD :: END
}

export default ListView;

Naver Open API를 사용하기 위해서는 API 사용 신청을 하고 인증에 필요한 정보를 발급 받아야 합니다. 여기서는 검색 API를 사용하므로 아래 URL에서  오픈 API 이용신청해서 먼저 발급 받습니다.

https://developers.naver.com/products/service-api/search/search.md

 

검색 - SERVICE-API

검색 NAVER Developers - 검색 API 소개 웹, 뉴스, 블로그 등 분야별 네이버 검색 결과를 웹 서비스 또는 모바일 앱에서 바로 보여 줄 수 있습니다. 또한 ’OO역맛집’과 같은 지역 검색을 할 수도 있으

developers.naver.com

apiGet 함수는 type과 param을 전달 받도록 작성했습니다. type은 뉴스(news)인지 도서(book)인지를 결정하는 인자이고, param은 검색어입니다. 두 인자를 받아서 Naver API URL을 조립하고 GET 방식으로 호출합니다. 이 때 Naver Open API 규격에 따라 headers에 인증 정보를 전달합니다.

 

Ajax는 비동기지만 aync/await를 사용했으므로 동기처럼 사용할 수 있습니다. 조회를 하고 나면 컴포넌트의 상태를 변경해 줍니다. 위에서 선언한 setArticles 함수를 사용해서 조회한 결과로 상태를 변경합니다.

 

다음은 useEffect Hook을 사용하여 컴포넌트가 마운트 될 때 한 번만 실행할 수 있도록 두 번째 인자로 빈 배열([])을 넘겨 아래와 같이 작성합니다. "코스피"라는 단어가 들어가는 뉴스를 검색하기 위해 아래와 같이 작성해 봅니다.

import React from "react";
import { useEffect, useState } from "react"

const ListView = () => {
    const [articles, setArticles] = useState(null);

    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);
        });
    };
	
    //ADD :: START
    useEffect(() => {
        apiGet('news', '코스피');
    }, []);
    //ADD :: END
}

export default ListView;

이제 마지막으로 컴포넌트에서 Rendering 하는 부분을 작성합니다. 일단은 데이터가 어떻게 넘어오는지 아직 모르기 때문에 그냥 <ul> 태그만 하나 출력해 두겠습니다.

import React from "react";
import { useEffect, useState } from "react"

const ListView = () => {
    const [articles, setArticles] = useState(null);

    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('news', '코스피');
    }, []);
	
    //ADD :: START
    return (
    	<div className="listArea">
            <ul className="listView">

            </ul>
        </div>
    );
    //ADD :: END
}

export default ListView;

여기까지 작성했다면 일단 ListView 컴포넌트는 대강 작성했습니다. 이제 main.js 에서 ListView 컴포넌트를 사용하도록 아래와 같이 변경합니다.

import React from 'react';
import ReactDOM from 'react-dom';
import ListView from './component/listview.component.jsx';

ReactDOM.render(<ListView />, document.getElementById('app'));

이제 실행을 한 번 시켜봐야 하는데, <ListView /> 등의 JSX 요소는 React의 컴포넌트를 표현하는 확장된 형태일 뿐, 정규 HTML 문법이 아니기 때문에 브라우저가 해석을 할 수 없습니다. 브라우저가  이해할 수 있는 순수 JavaScript로 변환하는 과정인 transpile 이라는 과정을 거쳐야 하며, 이 과정은 babel을 사용해서 진행합니다.

 

webpack.config.js 파일을 열어서 babel 설정을 추가합니다.

'use strict'
const path = require('path');

module.exports = {
    entry: {
        main: ['./src/main.js']
    },
    //ADD :: START
    module: {
        rules: [{
            test: /\.jsx?$/,
            use: {
                loader: 'babel-loader'
            }
        }]
    },
    //ADD :: END
    devServer: {
        static: './public',
        host: 'localhost',
        port: 8080
    }
};

확장자가 jsx인 파일은 babel-loader를 사용해서 transpile 하겠다는 의미입니다. babel 에서 transpile에 사용할 규칙은 preset을 사용하도록 하겠습니다. 프로젝트 루트에 .babelrc 라는 파일을 생성하고 아래와 같이 작성합니다.

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}

여기까지 작성하셨으면 프로젝트의 파일 구조는 아래와 같습니다.

 

이제 yarn start 명령으로 프로젝트를 기동합니다. 잘 따라하셨다면 특별한 오류 없이 기동될 것 같습니다. 정상 기동되었다면 터미널에 아래와 같은 형태로 표시됩니다.

D:\workspace\searchNaverApi>yarn start
yarn run v1.22.17
$ NODE_ENV=development webpack-dev-server
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/, http://127.0.0.1:8080/
<i> [webpack-dev-server] Content not from webpack is served from './public' directory
[BABEL] Note: The code generator has deoptimised the styling of D:\workspace\searchNaverApi\node_modules\react-dom\cjs\react-dom.development.js as it exceeds the max of 500KB.
asset main.js 1.13 MiB [emitted] (name: main)
runtime modules 27 KiB 12 modules
modules by path ./node_modules/ 1.02 MiB
  modules by path ./node_modules/webpack-dev-server/client/ 62.1 KiB 12 modules
  modules by path ./node_modules/webpack/hot/*.js 4.4 KiB 4 modules
  modules by path ./node_modules/html-entities/lib/*.js 115 KiB 4 modules
  modules by path ./node_modules/scheduler/ 29.4 KiB 4 modules
  modules by path ./node_modules/react/ 78.1 KiB 2 modules
  modules by path ./node_modules/react-dom/ 732 KiB 2 modules
  ./node_modules/ansi-html-community/index.js 4.26 KiB [built] [code generated]
  ./node_modules/events/events.js 14 KiB [built] [code generated]
  ./node_modules/object-assign/index.js 2.17 KiB [built] [code generated]
modules by path ./src/ 15.8 KiB
  ./src/main.js 219 bytes [built] [code generated]
  ./src/component/listview.component.jsx 15.6 KiB [built] [code generated]
webpack 5.66.0 compiled successfully in 1866 ms

프로젝트가 기동되었다면 이제 데이터를 확인하기 위해 http://localhost:8080으로 접근하여 Network 탭을 확인합니다. 하지만  인터페이스가 정상 수행되지는 않았고 Chrome Console 에 아래와 같은 에러 메시지가 남아 있습니다.

Chrome 브라우저의 CORS 정잭을 예외하기 위해서 Chrome의 바로가기에 아래처럼 예외 옵션을 적용합니다.

"C:\Program Files\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C:\Chrome"

브라우저를 모두 종료 후에 새로 시작해서 다시 http://localhost:8080 으로 접근해서 Network 탭을 확인하면 아래와 같이 인터페이스 결과를 확인할 수 있습니다.

다음 포스트에서는 인터페이스한 내용으로 ListView UI를 완성해 보도록 하겠습니다.

300x250

+ Recent posts