--
저번 게시글이 너무 길어질까 우려하여 전체적인 프로젝트 구조나 소스 정리를 따로 하지 않았습니다. 이번 게시글은 저번 게시글에서 했던 내용을 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 코드는 아래와 같습니다.
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 코드는 아래와 같습니다.
import React from 'react';
import ReactDOM from 'react-dom';
import Root from './Root';
ReactDOM.render(<Root />, document.getElementById('app'));
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 코드는 아래와 같습니다.
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 코드는 아래와 같습니다.
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;