--
지난 게시글에서는 NAVER API를 호출해서 데이터를 받아오고 정리되지 않은 형태로 <li>로 출력하는 부분까지 진행해 보았습니다.
남겨진 문제점은 아래 내용 정도입니다.
1. CSS를 추가해야 함
2. 제목과 요약 내용에 html 태그가 포함되어 있음
3. 일시에 대한 format 정리 필요
한 가지씩 해결해보도록 하겠습니다.
ListView의 NewsRow를 표시할 수 있는 css를 main.css 가장 하단에 추가합니다.
.listView{margin-top:20px;border-top:1px solid #DDD;}
.listView li{border-bottom:1px solid #DDD;padding:10px 15px;display:inline-block;}
.listView li .title {padding:10px 0px;color:#000;font-size:1.6rem;font-weight:400;}
.listView li .title a{color:#000;}
.listView li .cont span{display:inline-block;color:#555;}
.listView li .cont span.date{margin-bottom:5px;color:#999;font-size:1.2rem}
.listView li .bookImg{position:relative;float:left;}
.listView li .bookDesc{float:left;width:100%;}
모양을 잡아주면 NewsRow가 아래와 같이 표시됩니다.
NAVER API에서는 검색 keyword로 사용한 단어에 <b> 태그를 추가하여 전달하여 해당 데이터는 html로 표시할 수 있도록 처리해야 합니다.
jQuery나 vanilla에서는 보통 아래와 같은 구문을 사용했었습니다.
//jQuery
$('#title').html(htmlText);
//vanilla
document.getElementById('title').innerHTML = htmlText;
하지만 보통 어디선가 전달 받은 html을 DOM에 바로 binding 하게 되면 해당 htmlText에 악성코드가 전달되었을 경우에는 위험한 코드가 실행될 수도 있기 때문에 반드시 필요한 경우가 아니면 사용을 지양했었습니다.
React에서는 아예 해당 속성의 명칭에 위험하다는 것을 표시해두었습니다.
사용법은 아래와 같습니다.
<div className="title">
<a href="#" dangerouslySetInnerHTML={{__html: htmlText}}></a>
</div>
htmlText를 bind하고자 하는 element에 dangerouslySetInnerHTML 속성을 추가하고 위와 같이 작성하면 해당 데이터가 element 하위에 html 형식으로 append 됩니다.
위의 방법으로 제목과 내용을 처리한 코드는 아래와 같습니다.
<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>
처리 후 다시 실행해보면 아래 그림과 같이 html이 bind 된 것을 확인할 수 있습니다.
마지막으로 일자 데이터를 formatting 하는 방법을 알아보겠습니다. 현재는 Date 형식이 그대로 아래와 같이 표시되고 있습니다.
Sun, 13 Feb 2022 01:10:00 +0900
해당 문자열을 그대로 new Date하여 처리해도 되지만 많이들 사용하는 moment library를 사용하여 변환 출력해보겠습니다. 먼저 moment library를 설치하도록 하겠습니다.
yarn add moment@2.29.1
사용하는 형식은 아래와 같습니다.
moment(date).format(format);
moment('20220206').format('YYYY.MM.DD HH:mm:ss');
//2022.02.06 00:00:00
우리 코드에 적용해보면 현재 pubDate를 API에서 그대로 받아서 사용하는 부분을 아래와 같이 변경합니다.
const pubDate = moment(props.row.pubDate).format('YYYY.MM.DD HH:mm');
위 코드까지 적용하면 아래와 같이 표시됩니다.
목록에서 데이터를 받아서 표시하는 부분까지 구현해 보았고, 현재는 News에 대한 Layout만 잡았습니다.
현재까지 작업한 전체 화면의 모습은 아래와 같습니다.
현재까지 작업한 프로젝트 구조는 아래와 같습니다.
searchNaverApi
├── node_modules
│ ├── ...
├── public
│ ├── css
│ │ ├── main.css
│ ├── image
│ │ ├── icoSearch.png
├── src
│ ├── components
│ │ ├── layout
│ │ │ ├── Header.jsx
│ │ │ ├── TabList.jsx
│ │ ├── view
│ │ │ ├── ListView.jsx
│ ├── config
│ │ ├── const.js
│ ├── pages
│ │ ├── Main.jsx
│ ├── main.js
├── .babelrc
├── package.json
├── webpack.config.js
└── yarn.lock
현재까지 작업한 ListView.jsx의 코드는 아래와 같습니다.
import moment from 'moment';
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 = 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 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;