본문 바로가기

[Dev] 🎯Self Study

[리액트에서 Next로] Next.js 기초 - 데이터 패칭 (Data fetching)과 JSONplaceholder를 이용한 서버 데이터 패칭 예시

 

데이터 패칭은 렌더링과 동일하게 2가지 방식으로 되어있다.

 

CSR vs SSR 


클라이언트 사이드에서 데이터를 가져오기 위해서는

일반적으로 Use State 등에 상태값을 관리하는 리액트 훅을 실행시켜서 상태 변수를 선언하거나 

Use Effect Hook을 통해 백엔드에서 데이터를 패칭해 상태 변수에 할당하는 방식으로 동작 가능하다.

물론 리액트 Query 등을 사용하면 Use State 나 Use Effect Hook을 직접 작성하지 않고

보다 더 수월하게 데이터를 패칭할 수 있지만 어떤 방법을 사용하던 클라이언트 단에서 데이터를 패칭하는 경우

아래와 같은 문제가 발생하게 된다.

1. 번들 사이즈 증가
2. 리소스 집약적
3. 검색 엔진 최적화 어려움 (SEO)
4. 낮은 보안성
5. 서버와의 추가통신 발생 (추가적인 라운드트립 발생)

 

 

따라서 SSR 방식으로 데이터를 패칭해야한다. 


 

JSONplaceholder API를 이용한 서버 컴포넌트 데이터 패칭 예시

 

JSONPlaceholder - Free Fake REST API

{JSON} Placeholder Free fake API for testing and prototyping. Powered by JSON Server + LowDB. Tested with XV. Serving ~2 billion requests each month.

jsonplaceholder.typicode.com

 

1. app/users/page.tsx 이동
현재 이 컴포넌트는 use client가 없으므로 SSR으로 렌더링 되고 있으며
HTTP요청을 보낼 때 사용하는 패치함수를 통해 서버로 데이터를 패칭할 수 있다.

2. fetch 함수 작성
fetch (url, options) 
https://jsonplaceholder.typicode.com/users

 

패치함수는 promise를 리턴하기 때문에 응답을 얻으려면 데이터가 도착할 때까지 기다려야 한다.
이와 같은 접근 방식을 사용하면 useState를 사용해 별도의 상태 변수를 정의할 필요도 따로 사용하지 않아도 OK

그리고 이는 (zero dependency) 다른 말로는 종속성이 없음을 의미.
데이터를 패칭할 때 실행되는 코드의 번들 사이즈를 획기적으로 줄일 수 있다.
즉, fetch 함수는 Promise를 리턴. 가독성을 높이고자 Async/Await 형태로 전환 

 

import React from 'react'

const UsersPage = async () => {
  await fetch("https://jsonplaceholder.typicode.com/users")
  
  return (
    <div>
      <h1>this is user.</h1>
    </div>
  )
}

export default UsersPage


이제 데이터를 가져올 때 패치를 호출하고 모든 작업이 서버에서 진행된다. 

여기서 패치를 호출하고 데이터를 읽어오려면 패치 함수가 리턴하는 객체중에 json 함수를 호출해야 한다. 

fetch 함수가 서버로부터 받아온 데이터는 JSON 형식이기 때문에,
이를 JS가 이해할 수 있는 객체로 변환하기 위해 서는 파싱을 해야한다. 

이때 json 메소드를 호출하면 내부적으로 파싱을 하고, JS가 이해할 수 있는 객체형태의 데이터를 리턴해준다. 
json 메소드 또한 Promise를 리턴하기 때문에 메소드 실행 후 앞에 await 키워드를 작성.

 

그렇게 하기 위해서는 


1. 우선 해당 함수를 res라고 하는 변수에 담기 
2. 그리고 res 객체중에 json이라는 함수를 호출해 users라는 변수에 담기 
3. 그리고 json 함수 또한 promise를 리턴하기 때문에 away 키워드를 함수 앞에 명시해서 정상적으로 users 데이터를 받아오기

 

import React from 'react'

const UsersPage = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users")
  const users = await res.json();
  
  return (
    <div>
      <h1>this is user.</h1>
    </div>
  )
}

export default UsersPage

 

4. 받아온 사용자 users 데이터 - html 태그요소와 매핑

 

import React from 'react'

const UsersPage = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users")
  const users = await res.json();

  return (
    <div>
      <h1>this is user.</h1>
      <ul>
        {users.map((user)=><li>{user.name}</li>)}
      </ul>
    </div>
  )
}

export default UsersPage

 

타입스크립트 컴파일러가 user 인자의 형태를 정확하게 알 수 없기 때문에 경고를 출력한다.

 



타입스크립트 컴파일러에게 user 인자가 어떤 형태를 가지는지 알려주기 위해 User 인터페이스를 정의

 

인터페이스 이름 User를 작성, 그 뒤에 배열이라는 의미로 배열을 작성

users 변수 앞에 user interface 속성의 형태로 구성된 객체가 여러 개 담긴 배열이 해당 변수에 담긴다는 의미 

 


이렇게 하면 유저의 변수에 담긴 배열을 맵 메소드를 통해 순회할 때, 콜백 함수에 인자 값이 정의된 유저 인자에 마우스를 올려보면

사용자 객체의 속성이 출력 

 

-> 이것이 바로 TypeScript를 사용하는 이점 중 하나이다. 

 


그럼에도 여전히 오류가 출력되는데, 그 이유는 키 값을 주지 않았기 때문이다.
-> 키는 유저의 아이디로 전달

React의 key는 요소를 식별하고 업데이트 효율성을 향상시키는데 사용

 

import React from 'react'

// User 인터페이스를 정의
interface User {
  id : number;
  name : string;
} 

const UsersPage = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users")
  const users : User[] = await res.json();

  return (
    <div>
      <h1>this is user.</h1>
      <ul>
        {users.map((user)=><li key={user.id}>{user.name}</li>)}
      </ul>
    </div>
  )
}

export default UsersPage

 


브라우저로 돌아가 사용자 페이지로 이동해 네트워크 탭을 열어 서버에서 전달받은 문서를 보면 모든 사용자가 성공적으로 렌더링 

 

검색 엔진 봇이 본 페이지 정보와 렌더링된 페이지 정보가 일치하기 때문에 검색 엔진 최적화를 구현 가능하고,
서버로 별도의 재요청 없이 데이터를 효율적으로 렌더링하여 화면에 표시할 수 있다. 

 

- 서버에서 렌더링이 수행되기 때문에 최초의 빈 문서를 전달하는 클라이언트 컴포넌트와는 달리
완성된 데이터가 맵핑된 페이지를 확인 가능하다.


- 결과적으로 클라이언트 측에서 서버로의 별도의 라운드 트립이 발생하지 않고, 
- 검색 엔진 최적화 또한 구현됨으로써 앞서 클라이언트 컴포넌트를 사용했을 때 발생하는 문제를 깔끔하게 해결!

 


 따라서, Next.js를 사용할 때는 데이터 패칭과 관련된 동작은 가능한 한 서버 컴포넌트에서!