입문 #07 - ListView 만들기 (1/2)

16525 단어 ReactReact

지난 게시글까지 화면 상단의 Header와 Tab Menu까지 만들어 보았습니다. 일단 화면이 잘 동작하는지 확인해 보시고 다음 부분을 진행하시면 됩니다.

ListView Component

먼저 /src/components/view/ListView.jsx 파일을 하나 생성합니다.
이 즈음해서 오랜만에 프로젝트 구조 확인을 하겠습니다. 파일을 이제까지 여러 개 생성해왔으니 프로젝트 구조가 맞지 않아 곤란하신 분들도 있을 듯 합니다.

searchNaverApi
 ├── node_modules
 │    ├── ...
 ├── public
 │    ├── css
 │    │    ├── main.css
 │    ├── image
 │    │    ├── icoSearch.png
 ├── src
 │    ├── components
 │    │    ├── layout
 │    │    │    ├── Header.jsx
 │    │    │    ├── TabList.jsx
 │    │    ├── view
 │    │    │    ├── ListView.jsx
 │    ├── pages
 │    │    ├── Main.jsx
 │    ├── main.js
 ├── .babelrc
 ├── package.json
 ├── webpack.config.js
 └── yarn.lock

각 파일들의 역할은 이제까지의 게시물에 모두 설명이 되어 있습니다. 혹시나 뭔가 잘 되지 않으시는 분들은 한 번 구조 먼저 체크해보시고 진행하시면 좋을 것 같습니다.

ListView.jsx 파일의 내용을 아래와 같이 코딩합니다.

import React, { useState } from 'react';

const ListView = () => {

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

    return (
        <ul className='listView'>
        {
            articles &&
            articles.map((v, inx) => {
                return <NewsRow key={inx} row={v} />
            })
        }
        </ul>
    );
};

export default ListView;

많이 보던 구조입니다. ListView 라는 Component를 하나 생성하고 articles이라는 state를 하나 관리합니다. 초기값은 null 입니다. 그리고 UI Rendering 할 때 articles state에 값이 있으면 배열의 개수만큼 Row를 표시하고 아니면 아무것도 출력하지 않도록 구성했습니다.

NewsRow에 넘기는 key props는 배열의 index를 사용하는 것이 좋지 않다고 했으나 현재 API data에 key로 사용할만한 데이터가 없어 일단 배열의 index를 사용하겠습니다.

useEffect

이제 NAVER API를 호출해 보겠습니다. Component 기반으로 작성한 React 프로그램에서는 Component를 생성한 후 componentDidMount 시점에 인터페이스를 하도록 구성했습니다. 하지만 우리는 지금 Hook을 사용하고 있으므로 useEffect를 사용하도록 하겠습니다.

useEffect에 대한 설명은 React Reference에 아주 잘 설명되어 있으므로 아래 Link를 참고합니다.
https://ko.reactjs.org/docs/hooks-effect.html

일단 우리는 componentDidMount 시점에 1회만 데이터를 받아오도록 구성해야 하니 아래와 같이 useEffect를 추가합니다.

useEffect(() => {
    let apiUrl = 'https://openapi.naver.com/v1/search/news?query=올림픽';
    axios.get(apiUrl, {
        headers: {
            'Content-Type': 'application/json',
            'X-Naver-Client-Id': NAVER_CLIENT_ID,
            'X-Naver-Client-Secret': NAVER_CLIENT_SECRET
        }
    })
    .then(({data}) => {
        setArticles(data.items);
    })
    .catch(e => {
        console.error(e.stack);
    });
}, []);

axios.get method를 이용해서 NAVER NEWS API를 '올림픽'이라는 keyword로 Query 하는 로직입니다. 데이터를 받아온 후에는 setArticles function을 호출하여 state를 갱신합니다.

일단 useEffect의 마지막 인자는 빈 배열([])로 설정해서 Mount되는 시점에만 한 번 실행하도록 합니다. 이 부분은 나중에 검색 값이 변경되면 useEffect를 다시 실행해야 하므로 비교할 값을 설정하도록 하겠습니다.

config

위 소스코드에서 사용한 NAVER_CLIENT_ID와 NAVER_CLIENT_SECRET은 NAVER Developer 사이트에서 사용신청을 해서 받으면 되는 값입니다. 아래 URL을 참고합니다.

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

실제 애플리케이션을 작성하게 되면 해당 값들은 별도로 설정 파일로 분리해 놓거나, 애플리케이션 시작 시점에 서버에서 받아오는 등의 처리를 하게 될 것이므로 일단 별도의 파일로 분리해놓겠습니다.

/src/config/const.js 파일을 하나 생성하고 아래와 같이 코드를 참고하여 작성합니다.

export const NAVER_CLIENT_ID = '발급한 값';
export const NAVER_CLIENT_SECRET = '발급한 값';

NewsRow

이제 목록의 한 Row를 표시하는 Component를 작성해보겠습니다. 명칭은 NewsRow로 아래와 같이 작성해 보겠습니다.

const NewsRow = (props) => {

    const title = props.row.title;
    const pubDate = props.row.pubDate;
    const desc = props.row.description;

    return (
        <li>
            <div className="title">
                <a href="#">{title}</a>
            </div>
            <div className="cont">
                <span className="date">{pubDate}</span>
                <span>{desc}</span>
            </div>
        </li>
    );
};

목록에 표시될 Row를 <li> 태그로 만들어 준 후에 props로 넘어온 값을 목록의 element에 표시해주는 코드입니다.

먼저 어떤 데이터가 넘어오는지 알아야 작성할 수 있습니다. 앞의 게시글에서 네이버에서 넘겨주는 sample data를 한 번 보여드렸지만 다시 한 번 보도록 하겠습니다.

description: '피겨 여자 싱글의 간판 유영 선수는 베이징<b>올림픽</b> ...'
link: 'https://news.naver.com/main/read.naver?mode=LSD...'
originallink: 'https://news.sbs.co.kr/news/endPage.do?new...'
pubDate: 'Fri, 11 Feb 2022 07:48:00 +0900'
title: '[영상] <b>올림픽</b> '주무기' 트리플 악셀 가다듬는 유영의 연습 현장'

데이터가 너무 긴 부분은 잘라서 표시했습니다.

Main에 ListView 추가

일단 여기까지 작성한 후에 중간 실행을 한 번 해보려고 하는데 아직 Main.jsx에 ListView Component를 추가하지 않았습니다. Main.jsx에 추가해 보겠습니다. 아래 코드를 참고합니다.

const Main = (props) => {
    return (
        <>
            <Header />
            <div className="content">
                <TabList />
                <ListView />
            </div>
        </>
    );
};

여기까지 작업하고 한 번 실행시켜 보면 아래와 같이 화면이 표시됩니다.

현재까지 작성한 코드는 아래와 같습니다.

소스코드

[Main.jsx]

import React from "react";
import Header from "../components/layout/Header.jsx";
import TabList from "../components/layout/TabList.jsx";
import ListView from "../components/view/ListView.jsx";

const Main = (props) => {
    return (
        <>
            <Header />
            <div className="content">
                <TabList />
                <ListView />
            </div>
        </>
    );
};

export default Main;

[const.js]

export const NAVER_CLIENT_ID = '발급한 값';
export const NAVER_CLIENT_SECRET = '발급한 값';

[ListView.jsx]

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { NAVER_CLIENT_ID, NAVER_CLIENT_SECRET } from '../../config/const';

const NewsRow = (props) => {
    const title = props.row.title;
    const pubDate = props.row.pubDate;
    const desc = props.row.description;

    return (
        <li>
            <div className="title">
                <a href="#">{title}</a>
            </div>
            <div className="cont">
                <span className="date">{pubDate}</span>
                <span>{desc}</span>
            </div>
        </li>
    );
};

const ListView = () => {

    const [articles, setArticles] = useState(null);
    useEffect(() => {
        let apiUrl = 'https://openapi.naver.com/v1/search/news?query=올림픽';
        axios.get(apiUrl, {
            headers: {
                'Content-Type': 'application/json',
                'X-Naver-Client-Id': NAVER_CLIENT_ID,
                'X-Naver-Client-Secret': NAVER_CLIENT_SECRET
            }
        })
        .then(({data}) => {
            setArticles(data.items);
        })
        .catch(e => {
            console.error(e.stack);
        });
    }, []);

    return (
        <ul className='listView'>
        {
            articles &&
            articles.map((v, inx) => {
                return <NewsRow key={inx} row={v} />
            })
        }
        </ul>
    );
};

export default ListView;

좋은 웹페이지 즐겨찾기