본문 바로가기
source-code/React

리액트를 다루는 기술 _ 14장

by mattew4483 2021. 3. 18.
728x90
반응형

눈 깜짝하니 목요일이다.


14장. 외부 API를 연동하여 뉴스 뷰어 만들기

지금까지 배운 것을 바탕으로 뉴스 뷰어 프로젝트를 진행해 볼 예정!

 

전체 흐름은...

1. 비동기 작업의 이해

2. axios로 API 호출해서 데이터 받아오기

3. newsapi API 키 발급받기

4. 뉴스 뷰어 UI 만들기

5. 데이터 연동하기

6. 카테고리 기능 구현하기

7. 리액트 라우터 적용하기

 

출발!

14-1) 비동기 작업의 이해

동기적 처리 : 요청이 끝날 때까지 기다리는 동안 중지 상태 → 그동안 다른 작업 불가

비동기적 처리 : 웹 애플리케이션 멈추지 않음 → 동시에 여러 요청 처리 가능!

 

그런데 이미 비동기 작업을 사용해본 적이 있다!

JS의 setTimeout 함수가 그 예. 

setTimeout이 사용되는 시점에서 코드가 몇 초 동안 멈추는 게 아니라,

일단 코드가 위부터 아래까지 다 호출되고 몇 초 뒤에 작성한 코드가 작동된다!

 

JS에서 비동기 작업을 할 때 가장 흔히 사용하는 방법은 콜백 함수를 사용하는 것이다!

 

이때 아무 함수에 콜백 함수를 전달하여 호출하는 것으로는 비동기적으로 호출이 불가능.

JS에서 비동기 프로그래밍을 하려면

비동기적으로 콜백 함수를 호출하는 함수비동기적으로 호출되기를 원하는 콜백 함수가 필요하다.

여기서 비동기적으로 콜백 함수를 호출하는 함수가 → setTimeout, setInterval 등!

요 녀석들은 전달받은 콜백 함수를 현재 실행 중인 싱글 스레드에서 뽑아내어 특정 장소에 보관하고,

특정 조건을 만족시킬 때 호출이 가능하게 만든다!

 

쨘. console을 찍는 함수를 increase에 callback이라는 인자로 넘겨줬다!

이를 통해 주어진 파라미터(0)는 1초 뒤에 10이 더해진 채로 increase함수에 전달(console 찍힘)될 것이다.

 

만약 1초에 걸쳐서 10, 20, 30, 40과 같이 여러 번 순차적으로 처리하고 싶다면?

뭐 이렇게 하면 되긴 될테다...?

하지만 너무 여러 번 중첩되어 코드 가독성이 엉망!

→ 이를 방지하기 위한 방법이 있다.

 

1. Promise

바로 ES6에 도입된 Promise 객체! 이게 당최 무슨 소리냐면...

 

https://joshua1988.github.io/web-development/javascript/promise-for-beginners/

먼저 new Promise() 메서드를 호출하면 대기(Pending) 상태가 된다.

 

이때 콜백 함수를 선언할 수 있고, 그 인자가 resolve와 reject!

resolve() 실행 시 이행(Fulfilled)상태가 되면서 성공 시 결과 값을 얻을 수 있고,

reject() 실행 시 실패(Rejected)상태가 된다.

 

즉 우리가 써준 코드로는...

increase 함수에서 Promise 객체를 생성.

요 녀석은 1초 뒤에 setTimeout 내부의 익명 함수를 실행시키는데...

얘네는 전달받은 number에 10을 더하고, resolve로 그 결과 값을 반환!

만일 더한 숫자가 50을 넘어가면 reject를 통해 실패 상태로 만들고 Error를 return 한다.

 

실행 시 increase(0). then을 통해 Promise에서 resolve 된 값을 받아왔다!

즉 resolve()의 결과 값 데이터를 number로 받아 console에 찍고, 다시 increase 함수에 number를 전달!

 

2. async/await

async와 await는 Promise를 더욱 쉽게 사용할 수 있도록 해주는 ES8 문법!

얘네를 사용하기 위해서는 함수 앞부분에 async 키워드를 추가하고,

해당 함수 내부에서 Promise의 앞부분에 await 키워드를 사용!

→ 이를 통해 Promise가 끝날 때까지 기다리고, 결과 값을 특정 변수에 담을 수 있다.

 

이런 식으로...!

 

14-2) axios로 API 호출해서 데이터 받아 오기

axios는? 현재 가장 많이 사용되고 있는 JS HTTP 클라이언트!

HTTP 요청을 Promise 기반으로 처리한다는 특징이 있다.

 

뉴스 뷰어를 통해 실습해볼 예정!

오늘도 프로젝트 생성 & axios 설치!

 

코드가 너무 알아보기 힘들어 Prettier도 사용하기로 했다!

프로젝트 최상위 디렉토리에 .prettierrc 파일을 만들어 주시고,

 

.prettierrc

이렇게 설정!

(들여 쓰기 공백 두 칸 / 작은따옴표 사용 / 세미클론 항상 붙이기)

 

App.js

onClick 함수에서 axiois.get 함수를 사용 → 얘는 파라미터로 전달된 주소에 대해 GET 요청을 해준다.

 

http://localhost:3000/

GET 요청한 데이터가 잘 받아와 지는 모습!

 

14-3) newsapi API 키 발급받기

뉴스 뷰어에서는 newsapi에서 제공하는 API를 사용해 최신 뉴스를 불러온 후 보여줄 것이다.

 

이를 위해 newsapi에서 API 키를(카카오 맵이 생각난다) 발급받아야 한다!

API는 https://newsapi.org/registernewsapi.org/register 에서 받을 수 있다(가입해야한다...흑)

 

더미 계정으로 만들어도 상관없다! 흐흐.

그럼 이렇게 API key를 발급해준다!

 

newsapi.org/s/south-korea-news-api 에 접속하면 한국 뉴스 API에 대한 설명서도 볼 수 있다.

 

요 녀석을 이용해볼 예정!(가입 후 들어가면 자동으로 apikey가 입력되어있다)

 

해당 API 주소를 axios.get에 넣어주면...

와우! 데이터가 잘 나타나는 모습.

 

14-4) 뉴스 뷰어 UI 만들기

styled-components를 이용해 스타일을 적용시킬 것이기 때문에, 일단 요 녀석을 설치.

 

src 디렉터리 내에 components 파일을 생성 후, NewsItem.js와 NewsList.js 파일을 생성!

NewsItem은 각 뉴스 정보를 보여주는 컴포넌트,

NewsList는 API를 요청하고, 뉴스 데이터가 들어 있는 배열을 컴포넌트 배열로 변환하여 렌더링 해주는 컴포넌트.

 

1. NewsItem 만들기

받아온 뉴스 데이터 중 우리가 사용할 녀석들은...

title, description, url, urlToImage!

 

NewsItem 컴포넌트는 articles라는 객체(뉴스 데이터에 있다!)를 props로 통째로 받아와 사용한다.

NewsItem.js

우선 스타일을 먹여 주시고,

 

NewsItem.js

제일 먼저 비구조화 객체 할당(맞나?)을 통해 article 객체 속 대응 부분을 각 변수에 담아줬다.

이를 통해 article 하나하나의 정보가 해당 key 이름에 담긴 것!

 

a 태그에서 처음 보는 게 쓰였다. rel 속성이 뭐지?

필요하면... 찾아 쓰도록... 하자....

 

2. NewsList 만들기

아직 진짜 데이터를 받지 않았으니 sampleArticle이란 객체에 예시 데이터를 담아두자.

NewsList.js

NewsItem과 유사하다!

styled-component도 지정해주고, NewsList란 컴포넌트를 만들어 NewsItem에 sampleArticle을 전달!

 

http://localhost:3000/

그럼 이렇게 요상하게 잘 뜬다!(App에 NewsList 띄워준 건 생략!)

 

14-5) 데이터 연동하기

이제 NewsList 컴포넌트에서 사용했던 API를 호출해보자!

 

useEffect를 이용해 컴포넌트가 처음 렌더링 될 때 API 요청을 보내면 되는데...

주의할 점은 useEffect에 등록하는 함수에 async를 붙이면 안 되는 것!

(useEffect에서 반환하는 값은 뒷정리 함수라서)

 

따라서 useEffect 내부에서 async/await를 사용하고 싶다면,

함수 내부에 async 키워드가 붙은 또 다른 함수를 만들어 사용해줘야 한다.

NewsList.js

데이터를 불러와 뉴스 데이터 배열을 map을 통해 컴포넌트 배열로 변환할 때...

꼭 !articles를 조회해 해당 값이 현재 null이 아닌지를 검사해야 한다!

→ 데이터가 없을 때 null에는(articacl의 초깃값) map 함수가 없기 때문에 렌더링 과정에서 오류 발생.

 

http://localhost:3000/

엄청 신기하다.

 

14-6) 카테고리 기능 구현하기

뉴스 카테고리는 총 여섯 개!

business / science / entertainment / sports / health / technology

 

요 녀석들을 보여줄 때는 한글로 보여 준 뒤, 클릭하면 영어로 된 카테고리 값을 사용하도록!

 

1. 카테고리 선택 UI 만들기

Catagories.js

categories라는 배열을 생성해줬다.

name과 text란 key를 가진 객체를 넣어줬는데, 얘네는 한글 카테고리와 실제(영어) 카테고리!

 

Catagories.js

스타일도 야무지게 먹여주고,

 

Catagories.js

categories라는 배열을 mapping!

이때 각 객체의 name(영어로 된 실제 카테고리)을 Category(스타일 컴포넌트)에 key props로 전달!

화면에는 각 객체의 text(한글로 된 카테고리)를 보여준다!

 

http://localhost:3000/

카테고리도 잘 뜨는 모습!

 

이제 App에서 category의 상태를 useState로 관리!

추가적으로 category 값을 업데이트하는 onSelect 함수도 만들어준다.

App.js

category와 onSelect 함수를 Categories 컴포넌트에 props로 전달!

NewsList 컴포넌트에도 category를 전달!

 

Categories.js

Categories에서는 props로 전달받은 onSelect를 각 Category 컴포넌트의 onClick으로 설정!

 

즉 onClick시 해당 카테고리 이름(c.name)을 onSelect 함수에 보내 category를 업데이트해주고,

업데이트되어 props로 보내진 caategory가 해당 카테고리 이름(c.name)과 같으면 active=true로 만들고,

Categories.js

Category 스타일 컴포넌트에 props 부분을 추가해 props.active이면 css를 변경시켜줬다.

 

http://localhost:3000/

이렇게! 클릭할 때마다 색이 바뀌는 모습.

 

2. API 호출 시 카테고리 지정하기

현재 뉴스 API를 요청할 때 따로 카테고리를 지정하지 않았다.

따라서 NewsList 컴포넌트에서 현재 props로 받아 온 category에 따라 카테고리 지정하도록!

 

NewsList.js

category가 무엇인지에 따라 요청을 보낼 API 주소가 동적으로 변한다!

따라서 category가 'all'이면 공백으로, 아니면 &category=카테고리 의 형태로 query란 문자열을 정의해줬다.

요 query란 녀석을 요청 보낼 API 주소에 ${ } 형태로 넣어줬다!

 

또한 category 값이 바뀔 때마다 뉴스를 새로 불러와야(API 요청을 새로 보내야)하므로,

useEffect의 두 번째 파라미터로 category를 넣어 비교 후 변하면 함수 작동되도록

(즉 NewsList는 useEffect를 통해 맨 처음, 그리고 category가 바뀔 때마다 렌더링 된다)

 

http://localhost:3000/

카테고리가 달라질 때마다 다른 뉴스가 불러와지는 모습! 와우!

 

14-7) 리액트 라우터 적용하기

방금은 카테고리 값은 useState를 통해 관리해줬다,

이번에는 이 값을 라우터의 URL 파라미터를 사용해 관리해보자!

 

1. 리액트 라우터 설치 및 적용

일단 깔아주시고,

 

index.js

index에서 리액트 라우터를 적용!

 

2. NewsPage 생성

src 디렉터리 내부에 pages라는 폴더를 만들고, NewsPage.js란 파일을 생성!

pages/NewsPage.js

이곳에서 category를 정의!

Categories 컴포넌트와 NewsList 컴포넌트도 이곳에서 띄워준다.

 

App.js / Categories.js

현재 선택된 category 값을 URL 파라미터를 통해 사용할 것이므로...

 

onSelect 함수를 따로 전달할 필요도, Categories 컴포넌트에서 현재 선택된 카테고리 값을 알려줄 필요도 없다!

 

App.js

App 컴포넌트에서는 라우터를 통해 NewsPage 컴포넌트의 path를 지정!

 

/:category? ←맨 뒤의 물음표 문자는 category 값이 선택적(있을 수도, 없을 수도)이라는 뜻!

우리는 아까 category가 없을 때 → all, 즉 전체 카테고리를 선택한 것으로 지정했다.

 

3. Categories에서 NavLink 사용하기

기존 onSelect 함수가 수행하던, 카테고리 선택하고 다른 스타일 주던 기능을 NavLink로 대체해보자.

 

이때 일반 HTML 요소(div, a, input 등)가 아닌,

특정 컴포넌트에 스타일 컴포넌트를 사용할 때는 styled(컴포넌트이름)`` 과 같은 형식을 사용한다.

 

Categories.js

NavLink 컴포넌트에 스타일 컴포넌트를 사용!

 

활성화는 activeClassName을 통해 관리하므로 &.active에 속성을 지정!

 

NavLink로 만들어진 Category 컴포넌트.

to 값은 "/카테고리이름" 으로 설정!(전체보기는 예외적으로 "/all" 대신 "/" 로 설정했다)

to 값이 "/" 를 가리키고 있을 때는 exact 값을 true로!

(이를 해주지 않으면 다른 카테고리가 선택되었을 때도 전체보기 링크에 active 스타일이 적용된다)

 

http://localhost:3000/health

URL에 따라 카테고리가 달라지는 모습!

 

728x90
반응형