모던 리액트 deep dive 스터디 - 6주차 발표 정리
주제: 11장. Next.js 13과 리액트 18
Next.js 13
📋 app 디렉터리의 등장
Next 13 버전 이전까지 모든 페이지는 각각의 물리적으로 구별된 파일로 독립되어 있었습니다.
만약 페이지 공통으로 무언가를 넣어야 한다면, 그곳은 _document와 _app이 유일했죠.
이마저도 _document와 _app은 목적이 서로 달랐습니다.
_document는 <html>, <body> 태그를 수정하거나 서버 사이드 렌더링 시 CSS-in-JS를 지원하기 위한 코드를 삽입하는 제한적 용도,
_app은 글로벌하게 React, Next 엘리먼트를 컨트롤 하는 용도로 사용되어야 했습니다.
무언가 페이지 공통 레이아웃을 유지할려면 그 방법은 _app에서만 했어야 했죠.
하지만, 다음에 설명할 Next 13에서 나온 app 레이아웃으로 이 한계를 극복할 수 있게 되었습니다
한가지, 명심해야할 것은 app 디렉터리 내부에서는 모든 컴포넌트가 기본적으로 서버 컴포넌트로 동작합니다.
자세한 내용은 밑에서 더 살펴보겠습니다.
Next.js 13 app 레이아웃 구성 방법
라우팅
라우팅 방식의 변화
/pages => /app
1. 라우팅 정의
라우팅을 정의하는 방법에서 변화가 생겼습니다.
/pages/a/b.tsx를 Next 12에서는 /a/b로 변환이 되지만, Next13에서의 /app/a/b.tsx는 /a로 변환이 됩니다.
이유는 Next 13에서 app디렉터리 내부에서의 파일명은 라우팅 명칭에 아무런 영향을 미치지 못하기 때문입니다.
작성한 파일명은 예약어로 제한됩니다.
2. layout.js
layout의 특징에 대해서 우선 살펴봅시다.
- 그려야할 컴포넌트를 props로 받아서 렌더링 해야한다.
- layout 내부에는 반드시 export default로 내보내는 컴포넌트가 있어야 한다.
- 내부에서 비동기 작업을 수행할 수 있다.
루트에는 단 하나의 layout만이 존재할 수 있고, 이 layout은 모든 페이지에 영향을 미치는 공통 레이아웃입니다. (html, head 등과 같은 공통적인 내용들을 다루죠. _app과 _document를 하나로 대체할 수 있게 되었습니다.)
폴더에 존재하는 layout은 그 하위 폴더 및 주소에 영향을 끼칩니다.
이는 모든 애플리케이션에 영향을 미치는 것이 아닌 오로지 자신과 자식들의 라우팅에만 영향을 미치는 것이죠.
이 덕분에 애플리케이션에서 레이아웃을 더 유연하게 구성할 수 있게 되었습니다.
또한, 공통 레이아웃을 다룰 시에 _document.jsx에서 <Html/>, <Body/>, <Head/>처럼 Next.js에서 제공하는 태그를 사용해야하는 번거로움도 사라졌습니다.
3. page.js
page.js는 props로 params와 searchParams를 받을 수 있습니다.
params는 동적 라우트 파라미터(ex. [...id])의 값을 받아올 수 있고, searchParams는 URLSearchParams의 값을 받아올 수 있습니다.
*URLSearchParams
?a=1&b=2
=> { a : '1', b : '2' }
다만, 이 값은 layout에는 제공되지 않습니다.
같은 페이지에서 search parameter만 다르게 하여 라우팅을 시도할 경우 layout은 리렌더링을 수행하지 않습니다.
만약 search parameter에 의존적인 작업을 수행해야 한다면, 그 작업은 page.js 내에 작성되어야 합니다.
4. error.js
공통 에러 컴포넌트인 error.js는 에러 정보를 가지고 있는 error객체를 가지고 있어 특정 라우팅별로 서로 다른 에러 UI를 렌더링할 수 있게 해줍니다.
*주의
- 에러 바운더리는 클라이언트에서만 작동하므로 error 컴포넌트는 클라이언트 컴포넌트이어야 합니다.
- 같은 수준의 layout에서 에러가 발생할 경우, error 컴포넌트로 이동하지 않습니다. 페이지 렌더링시 Layout이 error 컴포넌트보다 더 상위에 있기 때문입니다. => 더 상위 error 컴포넌트나 app/global-errror.js 활용
5. not-found.js
특정 라우팅 하위의 주소를 찾을 수 없을 때 404 페이지를 렌더링 시켜줍니다.
만약, 전체 애플리케이션에 대한 404를 노출하고 싶다면 app/not-found.js를 서버 컴포넌트로 구성하면 됩니다.
6. loading.js
Suspens 기반으로 로딩을 렌더링
7. route.js
/app/api에 디렉터리 라우팅을 지원하며, 파일명은 route.js로 통일되었습니다.
이 파일에 예약어를 정의해 두면 HTTP 요청에 맞게 해당 메서드를 호출 할 수 있습니다.
route.js는 request, context와 같은 파라미터들을 받을 수 있으며, route.js 가 존재하는 폴더에는 page.jsx가 존재할 수 없습니다.
리액트 서버 컴포넌트
위에서는 Next 13에 대한 이야기를 하였습니다.
이번에는 리액트 18에서 도입된 리액트 서버 컴포넌트에 대해 이야기해보려고 합니다.
서버 컴포넌트 ? 뭔가 SSR과 많이 유사할 것 같지 않나요?
하지만, 이 둘은 완전히 다른 개념입니다.
서버 컴포넌트란?
서버 컴포넌트란 하나의 언어, 하나의 프레임워크, 그리고 하나의 API의 개념을 사용하면서 서버와 클라이언트 모두에서 컴포넌트를 렌더링할 수 있는 기법을 말합니다. 즉, 컴포넌트 일부는 클라이언트에서 일부는 서버에서 렌더링이 되는 것이죠.
모든 컴포넌트는 서버 컴포넌트 혹은 클라이언트 컴포넌트가 될 수 있습니다.
📌 서버 컴포넌트
서버 컴포넌트는 서버에서 딱 한번만 실행되기 때문에 상태를 가질 수 없고, 렌더링 생명주기도 사용할 수없습니다.
이로 인해, effect나 state에 의존하는 사용자 정의 훅은 사용할 수 없습니다. (서버에서 제공할 수 있는 기능만을 사용한다면 사용할 수 있습니다.)
브라우저에서 실행되는 것이 아니므로 DOM API나 window, document 등에 접근할 수 없습니다.
📌 클라이언트 컴포넌트
클라이언트 컴포넌트는 반대로 브라우저 환경에서만 실행되므로 서버 컴포넌트를 불러오거나 서버 전용 훅, 유틸리티를 불러올 수 없습니다.
리액트는 모든 것을 다 공용 컴포넌트로 판단하여서 모든 컴포넌트를 다 서버에서 실행 가능한 것으로 분류합니다.
만약, 클라이언트 컴포넌트를 명시적으로 나타내고 싶다면 'use Client'를 사용하면 됩니다.
+) use Client : 클라이언트 컴포넌트를 의미하는 지시자로 다른 코드나 import 문보다 위에 작성되어야 합니다. 이것을 사용하면 해당 컴포넌트는 클라이언트 컴포넌트임을 보장합니다.
서버 사이드 렌더링과 서버 컴포넌트의 차이는 무엇일까요?
우선, 이 둘은 둘중에 하나만 택하여 사용하는 것이 아닌 상호보완적인 개념입니다.
서버 사이드 렌더링은 초기에 인터렉션은 불가능하지만, 정적인 HTML을 빠르게 내려주는데 초점을 맞추고 있죠.
반면에, 서버 컴포넌트는 보통 사용자와의 상호 작용에 대한 응답으로 데이터를 동적으로 조작하고 렌더링하는 데 초점을 맞추고 있습니다.
따라서, 서버 사이드 렌더링은 초기 로딩 및 SEO를 개선하고, 서버 컴포넌트는 동적인 콘텐츠를 처리하고 사용자와의 상호 작용을 지원하도록 할 수 있습니다.
서버 컴포넌트는 어떻게 작동될까요?
1. 먼저, 서버가 렌더링 요청을 받습니다. 루트에 있는 컴포넌트는 항상 서버 컴포넌트이며, 리액트 서버 컴포넌트를 사용하는 모든 페이지는 항상 서버에서 시작됩니다.
2. 서버는 받은 요청에 따라 컴포넌트를 JSON으로 직렬화 합니다. 브라우저 이후에 이 결과물을 받아 역직렬화를 수행하고 렌더링을 수행합니다.
이러한 이유로, 서버 컴포넌트에서 클라이언트 컴포넌트로 props를 넘길 때 반드시 직렬화 가능한 데이터를 넘겨야 하는 것입니다.
+) 모든 객체를 직렬화 할 수 는 없습니다.
예로 함수의 실행 컨텍스트, 스코프, 클로저는 직렬화 할 수 없기 때문에 function은 직렬화가 불가능합니다.
직렬화 과정은 모든 서버 컴포넌트를 실행하여 json 객체 형태의 트리로 재구성할 때까지 진행됩니다. 이때, 리액트 서버 컴포넌트일 경우는 건너 뜁니다. 건너 뛸 때는 그냥 넘어가는 것이 아닌 해당 위치가 리액트 서버 컴포넌트가 렌더링 되는 위치라는 placeholder를 배치해줍니다.
3. 브라우저가 리액트 컴포넌트 트리를 구성합니다. 클라언트 컴포넌트를 받았다면 클라이언트에서 렌더링, 서버에서 만들어진 결과를 받았다면 이 정보를 기반으로 리액트 트리를 그대로 만듭니다. 최종적으로 이 트리를 렌더링해 브라우저의 DOM에 커밋합니다.
Next.js에서의 리액트 서버 컴포넌트
루트 컴포넌트는 무조건 서버 컴포넌트이다.
Next.js에서 루트 컴포넌트는 각 페이지에 존재하는 page.js가 있고, 추가적으로 layout.js도 존재한다.
따라서, page.js와 layout.js는 반드시 서버 컴포넌트여야 한다.
Next.js에서 서버 컴포넌트를 도입하면서 달라지는 부분들.
1. /app 디렉터리 내부에서는 getServerSideProps, getStaticProps, getInitialProps가 삭제되었다. 또한, fetch API를 확장하여 fetch 요청에 대한 내용을 서버에서는 렌더링이 한 번 끝날 때까지 캐싱을 한다. 이는 중복된 요청을 방지할 수 있다.
2. 정적인 라우팅에 대해서는 기본적으로 빌드 타임에 렌더링을 미리 해두고 캐싱해 재사용한다. 동적인 라우팅에 대해서는 서버에 매번 요청이 올 때마다 컴포넌트를 렌더링 한다.
만약, 동적인 주소이지만 getStaticPath처럼 특정 주소에 대해 캐싱을 하고 싶다면, generateStaticParams를 사용하면 된다.
3. revalidate를 사용하여 데이터의 유효 시간을 정해두고 시간이 지났을 때 다시 데이터를 불러와 렌더링하는 것이 가능하다.
4. 스트리밍 기능을 사용할 수 있다. 스트리밍이란, 하나의 페이지가 다 완성 될 때까지 기다리는 것이 아니라 HTML을 작은 단위로 쪼개서 완성되는 대로 클라이언트로 점진적으로 보내는 것을 말한다.
이는 최초 바이트까지의 시간(TTFB)과 최초 콘텐츠풀 페인팅(FCP)을 개선하는 데 큰 도움을 준다.
터보 팩의 등장
웹팩의 뒤를 이을 터보팩이라는 것이 추가되었습니다.
이는 네트워크 트래픽을 줄이고 페이지 이동 간의 전환 속도를 향상시키는 데 중점을 둡니다.
아직은 개발 모드에서만 제한적으로 사용 가능합니다.
서버 액션(alpha)
서버 액션 기능은 API를 굳이 생성하지 않더라도 함수 수준에서 서버에 직접 접근해 데이터 요청 등을 수행할 수 있는 기능입니다.
"use server"
async function serverAction() {
// 서버에 바로 접근하는 코드
}
서버 액션을 만들려면 use server라는 지시자를 선언해야하며, 함수는 반드시 async여야 합니다.
또한, 클라이언트 컴포넌트 내에서 정의될 수 없습니다.
만약, 클라이언트 컴포넌트에서 서버 액션을 쓰고 싶다면 'use server'로 서버 액션만 모여 있는 파일을 별도로 import해야 합니다.
그렇다면, 서버액션이 수행할 수 있는 작업들은 무엇이 있을까요?
1. form의 action
return (
<form action={서버액션함수}>
<button>버튼</button>
</form>
)
action props를 추가해서 양식 데이터를 처리할 URI를 넘겨줄 수 있습니다.
여기서 함수는 서버에서 수행되게 됩니다.
'use server'로 선언돼 있는 내용을 빌드 시점에 미리 클라이언트에서 분리시키고 서버로 옮김으로써 클라이언트 번들린 결과물에는 포함되지 않고 서버에서만 실행되는 것입니다.
PH와 같은 서버 기반 웹 애플리케이션과 유사해보이지만, 가장 큰 차이점은 모든 과정이 페이지 새로고침이 없이 수행된다는 점입니다.
2. input의 submit과 image formAction propd으로도 서버 액션을 추가할 수 있습니다.
3. startTransition에서도 서버 액션을 활용할 수 있습니다.
4. sever mutation이 필요없는 작업이라면 이벤트 핸들러에 바로 넣어도 됩니다.
정리
리액트 18과 Next.js 13은 큰 변화를 가져왔고 기존에 작성하던 패러다임에 많은 변화를 가져다 줄 것으로 보입니다.
특히, 리액트 서버 컴포넌트는 이전에 없었던 새로운 패러다임으로 Node.js를 위시하는 서버 환경의 이해가 더욱 더 중요해질 것으로 보여집니다.
'프로그래밍언어 > React' 카테고리의 다른 글
모던 리액트 deep dive 스터디 - 8주차 발표 정리 (0) | 2024.04.27 |
---|---|
모던 리액트 deep dive 스터디 - 7주차 발표 정리 (1) | 2024.04.20 |
모던 리액트 deep dive 스터디 - 5주차 발표 정리 (0) | 2024.04.06 |
모던 리액트 deep dive 스터디 - 4주차 발표 정리 (0) | 2024.03.30 |
모던 리액트 deep dive 스터디 - 3주차 발표 정리 (1) | 2024.03.22 |