포스트를 티스토리로 이전했습니다. 아래 링크로 접속해 주세요.

https://redballs.tistory.com/entry/React-%EA%B8%B0%EC%B4%88-%EB%AA%A9%EB%A1%9D-%EB%A7%8C%EB%93%A4%EA%B8%B0-01-Concept?category=564726

--

저번 게시글이 너무 길어질까 우려하여 전체적인 프로젝트 구조나 소스 정리를 따로 하지 않았습니다. 이번 게시글은 저번 게시글에서 했던 내용을 Wrap-up 하겠습니다.

전체적인 내용이 종료되면 github에 정리하여 공유하겠습니다.

프로젝트 구조

현재까지의 프로젝트 구조는 아래와 같습니다.

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

reducer.js

reducer.js 코드는 아래와 같습니다.

import {applyMiddleware, createStore} from 'redux'
import {composeWithDevTools} from 'redux-devtools-extension';
import logger from 'redux-logger';

export const CUR_TAB = 'CUR_TAB';
export const setCurTab = tabId => ({type: CUR_TAB, tabId});

const rootReducer = (state = {tabId: 'news'}, action) => {
    switch (action.type) {
        case CUR_TAB:
            return {
                ...state,
                tabId: action.tabId
            };
        default:
            return state;
    }
};

const enhancer = composeWithDevTools(applyMiddleware(logger));
export const store = createStore(rootReducer, enhancer);

main.js

main.js 코드는 아래와 같습니다.

import React from 'react';
import ReactDOM from 'react-dom';
import Root from './Root';

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

Root.js

Root.js 코드는 아래와 같습니다.

import React from 'react';
import { Provider } from 'react-redux';
import { store } from './config/reducer';
import Main from './pages/Main.jsx';

const Root = () => {
    return (
        <Provider store={store}>
            <Main />
        </Provider>
    );
};

export default Root;

TabList.jsx

TabList.jsx 코드는 아래와 같습니다.

import React from 'react';
import { useDispatch } from 'react-redux';
import { setCurTab } from '../../config/reducer';

const Tab = (props) => {
    const tabName = props.tab.tabName;
    const isOn = props.tab.isOn;
    const tabId = props.tab.id;
    const dispatch = useDispatch();

    const changeTab = () => {
        document.querySelector('.tabList li a.on').classList.remove('on');
        document.querySelector('.tabList li a#' + tabId).classList.add('on');
        dispatch(setCurTab(tabId));
    };

    return (
        <li role="presentation" style={{minWidth: props.minWidth + 'px'}}>
            <a href="#" 
                role="tab" 
                tabIndex="0" 
                id={tabId}
                aria-selected={isOn.toString()} 
                className={isOn ? 'on' : ''}
                onClick={changeTab}>
                <span>{tabName}</span>
            </a>
        </li>
    );
};

const TabList = () => {

    const tabList = [
        {tabName: '뉴스', id: 'news', isOn: true},
        {tabName: '도서', id: 'book', isOn: false},
        {tabName: '영화', id: 'movie', isOn: false}
    ];
    const minWidth = Math.floor(window.innerWidth / tabList.length);

    return (
        <div className="tabBox">
            <ul className="tabList" role="tablist">
            {
                tabList &&
                tabList.map(v => {
                    return <Tab key={v.id}
                                tab={v}
                                minWidth={minWidth} />

                })
            }
            </ul>
        </div>
    );
};
export default TabList;

ListView.jsx

ListView.jsx 코드는 아래와 같습니다.

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

const NewsRow = (props) => {
    const title = props.row.title;
    const pubDate = moment(props.row.pubDate).format('YYYY.MM.DD HH:mm');
    const desc = props.row.description;

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

const BookRow = (props) => {

    const image = props.row.image;
    const title = props.row.title;
    const author = props.row.author;
    const desc = props.row.description;

    return (
        <li>
            <a href="#" className="bookRow">
                <div className="bookImg">
                    <img src={image} />
                </div>
                <div className="bookDesc">
                    <div className="title" dangerouslySetInnerHTML={{__html: title}} />
                    <div className="cont">
                        <span className="author">{author}</span>
                        <span dangerouslySetInnerHTML={{__html: desc}} />
                    </div>
                </div>
            </a>
        </li>
    );
};

const ListView = () => {

    const [articles, setArticles] = useState(null);
    const tabId = useSelector(state => state.tabId);

    useEffect(() => {
        let apiUrl = 'https://openapi.naver.com/v1/search/' + tabId + '?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);
        });
    }, [tabId]);

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

export default ListView;

0개의 댓글

Powered by GraphCDN, the GraphQL CDN