일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 집합_SET
- 메소드오버로딩
- oracle
- 오라클
- 객체 비교
- NestedFor
- 다형성
- 환경설정
- 인터페이스
- 예외미루기
- exception
- GRANT VIEW
- 생성자오버로드
- 컬렉션 타입
- 자동차수리시스템
- 자바
- 사용자예외클래스생성
- abstract
- EnhancedFor
- cursor문
- 정수형타입
- 어윈 사용법
- 대덕인재개발원
- 한국건설관리시스템
- 참조형변수
- 제네릭
- Java
- 컬렉션프레임워크
- 추상메서드
- 예외처리
- Today
- Total
거니의 velog
(3) 외부 API를 연동하여 뉴스 뷰어 만들기 3 본문
5. 데이터 연동하기
* 이제 NewsList 컴포넌트에서 이전에 연습 삼아 사용했던 API를 호출해 보도록 하자. 컴포넌트가 화면에 보이는 시점에 API를 요청해 볼 것이다. 이때 useEffect를 사용하여 컴포넌트가 맨 처음 렌더링되는 시점에 API를 요청하면 된다. 여기서 주의할 점은 useEffect에 등록하는 함수에 async를 붙이면 안 된다는 것이다. useEffect에서 반환해야 하는 값은 뒷정리 함수이기 때문이다.
* 따라서 useEffect 내부에서 async/await 를 사용하고 싶다면, 함수 내부에 async 키워드가 붙은 또 다른 함수를 만들어서 사용해 주어야 한다.
* 추가로 loading이라는 상태도 관리하여 API 요청이 대기 중인지 판별할 것이다. 요청이 대기 중일 때는 loading 값이 true가 되고, 요청이 끝나면 loading 값이 false가 되어야 한다.
[NewsList.js]
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import NewsItem from './NewsItem';
import axios from '../../node_modules/axios/index';
const NewsListBlock = styled.div`
box-sizing: border-box;
padding-bottom: 3rem;
width: 768px;
margin: 0 auto;
margin-top: 2rem;
@media all and (max-width: 768px) {
width: 100%;
padding-left: 1rem;
padding-right: 1rem;
}
`;
const NewsList = () => {
const [articles, setArticles] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
// async를 사용하는 함수 따로 선언
const fetchData = async () => {
setLoading(true);
try {
const response = await axios.get(
'https://newsapi.org/v2/top-headlines?country=kr&apiKey=8aa6de08f72745be9bbb2a4e91ef45ae',
);
setArticles(response.data.articles);
} catch (e) {
console.log(e);
}
setLoading(false);
};
fetchData();
}, []);
// 대기 중일 때
if (loading) {
return <NewsListBlock>대기 중...</NewsListBlock>;
}
// 아직 articles 값이 설정되지 않았을 때
if (!articles) {
return null;
}
// articles 값이 유효할 때
return (
<NewsListBlock>
{articles.map((article) => (
<NewsItem key={article.url} article={article} />
))}
</NewsListBlock>
);
};
export default NewsList;
* 데이터를 불러와서 뉴스 데이터 배열을 map 함수를 사용하여 컴포넌트 배열로 변환할 때 신경써야 할 부분이 있다. map 함수를 사용하기 전에 꼭 !articles를 조회하여 해당 값이 현재 null이 아닌지 검사해야 한다. 이 작업을 하지 않으면, 아직 데이터가 없을 때 null에는 map 함수가 없기 때문에 렌더링 과정에서 오류가 발생한다. 그래서 애플리케이션이 제대로 나타나지 않고 흰 페이지만 보이게 된다.
* 이제 뉴스 정보가 잘 보이는지 확인해 보자.
- http://localhost:3000/
* 뉴스가 잘 보이는 것을 확인할 수 있다.
6. 카테고리 기능 구현하기
* 이번에는 뉴스의 카테고리 선택 기능을 구현해 보겠다. 뉴스 카테고리는 총 여섯 개이며, 다음과 같이 영어로 되어 있다.
- business (비즈니스)
- entertainment (연예)
- health (건강)
- science (과학)
- sports (스포츠)
- technology (기술)
* 화면에 카테고리를 보여 줄 때는 영어로 된 값을 그대로 보여 주지 않고, 다음 그림처럼 한글로 보여 준 뒤 클릭했을 때는 영어로 된 카테고리 값을 사용하도록 구현할 것이다.
(1) 카테고리 선택 UI 만들기
* 먼저 components 디렉터리에 Categories.js 컴포넌트 파일을 생성하여 다음 코드를 작성해 보자.
import React from 'react';
import styled from 'styled-components';
const categories = [
{
name: 'all',
text: '전체보기',
},
{
name: 'business',
text: '비즈니스',
},
{
name: 'entertainment',
text: '엔터테인먼트',
},
{
name: 'health',
text: '건강',
},
{
name: 'science',
text: '과학',
},
{
name: 'sports',
text: '스포츠',
},
{
name: 'technology',
text: '기술',
},
];
const CategoriesBlock = styled.div`
display: flex;
padding: 1rem;
width: 768px;
margin: 0 auto;
@media all and (max-width: 768px) {
width: 100%;
overflow-x: auto;
}
`;
const Category = styled.div`
font-size: 1.125rem;
cursor: pointer;
white-space: pre;
text-decoration: none;
color: inherit;
padding-bottom: 0.25rem;
&:hover {
color: #495057;
}
& + & {
margin-left: 1rem;
}
`;
const Categories = () => {
return (
<CategoriesBlock>
{categories.map((c) => (
<Category key={c.name}>{c.text}</Category>
))}
</CategoriesBlock>
);
};
export default Categories;
* 위 코드에서는 categories 라는 배열 안에 name 과 text 값이 들어가 있는 객체들을 넣어 주어서 한글로 된 카테고리와 실제 카테고리 값을 연결시켜 주었다. 여기서 name은 실제 카테고리 값을 가리키고, text 값은 렌더링할 때 사용할 한글 카테고리를 가리킨다.
* 다 만든 컴포넌트는 App에서 NewsList 컴포넌트 상단에 렌더링하자.
[App.js]
import React from 'react';
import NewsList from './components/NewsList';
import Categories from './components/Categories';
const App = () => {
return (
<>
<Categories />
<NewsList />
</>
);
};
export default App;
* 다음과 같이 상단에 카테고리 목록이 나타나는가?
* 이제 App에서 category 상태를 useState로 관리하겠다. 추가로 category 값을 업데이트하는 onSelect 라는 함수도 만들어 줄 것이다. 그러고 나서 category 와 onSelect 함수를 Categories 컴포넌트에게 props 로 전달해 주자. 또한, category 값을 NewsList 컴포넌트에게도 전달해 주어야 한다.
[App.js]
import React, { useCallback, useState } from 'react';
import NewsList from './components/NewsList';
import Categories from './components/Categories';
const App = () => {
const [category, setCategory] = useState('all');
const onSelect = useCallback((category) => setCategory(category), []);
return (
<>
<Categories category={category} onSelect={onSelect} />
<NewsList category={category} />
</>
);
};
export default App;
* 다음으로 Categories에서는 props로 전달받은 onSelect를 각 Category 컴포넌트의 onClick으로 설정해 주고, 현재 선택된 카테고리 값에 따라 다른 스타일을 적용시켜 보자.
import React from 'react';
import styled, { css } from 'styled-components';
const categories = [
{
name: 'all',
text: '전체보기',
},
{
name: 'business',
text: '비즈니스',
},
{
name: 'entertainment',
text: '엔터테인먼트',
},
{
name: 'health',
text: '건강',
},
{
name: 'science',
text: '과학',
},
{
name: 'sports',
text: '스포츠',
},
{
name: 'technology',
text: '기술',
},
];
const CategoriesBlock = styled.div`
display: flex;
padding: 1rem;
width: 768px;
margin: 0 auto;
@media all and (max-width: 768px) {
width: 100%;
overflow-x: auto;
}
`;
const Category = styled.div`
font-size: 1.125rem;
cursor: pointer;
white-space: pre;
text-decoration: none;
color: inherit;
padding-bottom: 0.25rem;
&:hover {
color: #495057;
}
${(props) =>
props.active &&
css`
font-weight: 600;
border-bottom: 2px solid #22b8cf;
color: #22b8cf;
&:hover {
color: #3bc9db;
}
`}
& + & {
margin-left: 1rem;
}
`;
const Categories = ({ onSelect, category }) => {
return (
<CategoriesBlock>
{categories.map((c) => (
<Category
key={c.name}
active={category === c.name}
onClick={() => onSelect(c.name)}
>
{c.text}
</Category>
))}
</CategoriesBlock>
);
};
export default Categories;
* 다음과 같이 선택된 카테고리가 청록색으로 보이는가? 다른 카테고리도 클릭하면 잘 선택된다.
(2) API를 호출할 때 카테고리 지정하기
* 지금은 뉴스 API를 요청할 때 따로 카테고리를 선택하지 않고 뉴스 목록을 불러오고 있다. NewsList 컴포넌트에서 현재 props로 받아온 category에 따라 카테고리를 지정하여 API를 요청하도록 구현해 보자.
[NewsList.js]
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import NewsItem from './NewsItem';
import axios from '../../node_modules/axios/index';
const NewsListBlock = styled.div`
box-sizing: border-box;
padding-bottom: 3rem;
width: 768px;
margin: 0 auto;
margin-top: 2rem;
a {
color: #333;
}
@media all and (max-width: 768px) {
width: 100%;
padding-left: 1rem;
padding-right: 1rem;
}
`;
const NewsList = ({ category }) => {
const [articles, setArticles] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
// async를 사용하는 함수 따로 선언
const fetchData = async () => {
setLoading(true);
try {
const query = category === 'all' ? '' : `&category=${category}`;
const response = await axios.get(
`https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=8aa6de08f72745be9bbb2a4e91ef45ae`,
);
setArticles(response.data.articles);
} catch (e) {
console.log(e);
}
setLoading(false);
};
fetchData();
}, [category]);
// 대기 중일 때
if (loading) {
return <NewsListBlock>대기 중...</NewsListBlock>;
}
// 아직 articles 값이 설정되지 않았을 때
if (!articles) {
return null;
}
// articles 값이 유효할 때
return (
<NewsListBlock>
{articles.map((article) => (
<NewsItem key={article.url} article={article} />
))}
</NewsListBlock>
);
};
export default NewsList;
* 원래 category 값이 무엇인지에 따라 요청할 주소가 동적으로 바뀌고 있다. category 값이 all 이라면 query 값을 공백으로 설정하고, all이 아니라면 "&category=카테고리" 형태의 문자열을 만들도록 했다. 그리고 이 query를 요청할 때 주소에 포함시켜 주었다.
* 추가로 category 값이 바뀔 때마다 뉴스를 새로 불러와야 하기 때문에 useEffect의 의존 배열(두 번째 파라미터로 설정하는 배열)에 category를 넣어 주어야 한다.
* 만약 여러분이 이 컴포넌트를 클래스형 컴포넌트로 만들게 된다면 componentDidMount와 componentDidUpdate에서 요청을 시작하도록 설정해 주어야 하는데, 함수형 컴포넌트라면 이렇게 useEffect 한 번으로 컴포넌트가 맨 처음 렌더링될 때, 그리고 category 값이 바뀔 때 요청하도록 설정해 줄 수 있다.
* 여기까지 작업을 마쳤다면 브라우저를 열어서 다른 카테고리를 선택해 보자. 카테고리에 따른 뉴스가 잘 나타나는가?
'React > React_뉴스 뷰어 앱 만들기' 카테고리의 다른 글
(4) 외부 API를 연동하여 뉴스 뷰어 만들기 4 (0) | 2023.12.12 |
---|---|
(2) 외부 API를 연동하여 뉴스 뷰어 만들기 2 (0) | 2023.12.12 |
(1) 외부 API를 연동하여 뉴스 뷰어 만들기 1 (1) | 2023.12.12 |