앱 라우터 환경
Next.js 13부터는 기존의 Next.js의 _app
, _document
파일을 시작점으로 pages
폴더로 라우팅하는 방법이 아닌, app
폴더에서 시작해서 page
, layout
파일로 각각의 페이지를 구성하게 되었습니다.
- app
ㄴ place
....ㄴ page.tsx // '/place'
....ㄴ layout.tsx
....ㄴ [id] // '/place/3'
ㄴ page.tsx // '/'
ㄴ layout.tsx
위와 같은 폴더 구조를 가지게 됩니다.
또한 폴더 이름은 각 페이지의 path name
이 됩니다.
- page.tsx
- 페이지를 구성하는 컴포넌트를 모아놓는 역할
- index.tsx와 비슷한 역할을 함
- layout.tsx
- index.html과 비슷한 역할
- 각 페이지의 meta data들을 설정할 수 있음
그리고 [id] 대괄호로 감싸게되면 path 파라미터가 되어 동적 라우팅이 가능하게 됩니다. 앱 라우팅 환경에서는 page.tsx
의 컴포넌트 props
속에서 params
로 가져올 수 있어 서버 컴포넌트에서도 안정적으로 동적 라우팅이 가능했습니다.
layout.tsx
는 html 파일 및 헤더의 메타태그 등을 관리하는 파일입니다.
layout.tsx
에서 children은 page.tsx
를 의미하며 이 두 파일 모두 서버 컴포넌트로 동작하고 있습니다. 직접적으로 layout.tsx
에서 값을 props로 내려줄 수는 없지만 context api
를 이용해서는 전달 할 수 있습니다.
서버 컴포넌트 / 클라이언트 컴포넌트
또한 React 18
의 서버 컴포넌트, 클라이언트 컴포넌트 기능을 활용해서 기본적으로 서버 컴포넌트 기반으로 모든 페이지들이 구성되며, React
의 life cycle
이나 DOM
조작 이벤트가 필요한 컴포넌트에서는 'use client'
라고 선언한 뒤 클라이언트 컴포넌트로 활용하게 되었습니다.
또한 서버 컴포넌트에서만 비동기 요청이 가능하고 클라이언트 컴포넌트에서는 비동기 요청이 불가능 했습니다. 따라서 주로 데이터를 받아오는 부모 컴포넌트쪽을 서버 컴포넌트로 구성하고, DOM
조작이 필요한 leaf 컴포넌트들을 클라이언트 컴포넌트로 활용하는 방법을 이용할 수 있습니다.
서버 컴포넌트에서 데이터를 불러오는 방식이 바뀌게되어 기존의 데이터를 불러오던 방식인 getInitialProps
, getServerSideProps
, getStaticProps
같은 방법에서 fetch 메서드에서 options을 이용해서 각각의 역할을 구분할 수 있게 되었습니다.
{ cache: 'force-cache' }
- getInitialProps와 같은 역할
- 이름처럼 바뀌지 않는 첫 데이터를 가져옴
{ cache: 'no-store' }
- getServerSideProps와 같은 역할
- 모든 요청에서 최신 데이터 받아옴
{ next: { revalidate: 5 } }
- revalidate옵션이 있는 getStaticProps와 같은 역할
- 5초 후 새 요청이 오면 페이지 새로 생성
fetch
를 이용한 데이터 요청은 모두 서버사이드에서 동작하는 요청입니다.
tailwind
서버 컴포넌트에서는 빌드 시점에 CSS가 적용이 되어야해서 tailwind
같은 zero runtime CSS을 사용해야했습니다. 평소에 styled-components
로 스타일링을 했었는데 styled-components
는 runtime CSS이기 때문에 SSR
에 적합한 라이브러리가 아니었습니다.
tailwind
는 유틸리티 클래스 기반의 스타일링을 할 수 있는 라이브러리로 빠르게 스타일링을 할 수 있는게 장점이라고들 하던데, 처음 접했을 때는 styled-components
와 달라 그렇게 편하게 느껴지지는 않았습니다. 또한 클래스에 길게 스타일 요소들이 늘어져있다보니 코드가 깔끔하게 보이지 않아서 힘들었습니다.
자주 쓰는 컬러코드나 애니메이션 효과, 그라데이션 효과, 폰트 같은 커스텀 스타일들을 tailwind.config.ts
에서 따로 관리해주어야 했습니다.
이런식으로 필요한 요소들을 한 군데 모아서 관리하도록 되어있었습니다. 특히 content
부분이 중요했습니다. content
속 배열에 작업하는 폴더가 존재하지 않으면 CSS가 적용되지 않기 때문입니다. common
이라는 폴더에 여러 곳에서 쓰는 컴포넌트 들을 모아놓았었는데 content
배열에 포함시키지 않아 CSS가 적용되지 않고 있었는데 원인을 찾는데 시간이 오래 걸렸었습니다.
tailwind
에서도 부모 컴포넌트에서 받은 값을 가지고 동적 스타일링이 가능했었는데요, 통째로 넣어주어야만 동적 스타일링이 가능했었습니다.
이런식으로 props로 w-20
, h-20
을 통째로 넣어주어서 동적 스타일링을 할 수 있었습니다.
Image
Next.js
에서 이미지를 처리하는 방법도 react
와 조금의 차이가 있었습니다. next에서 제공하는 Image 컴포넌트를 이용합니다.
이렇게 사용하게 되면 화면 크기가 바뀔때마다 다르게 보여주어야 할 때 동적 스타일링이 어렵게 되었습니다. 이걸 해결하기 위해 fill
이라는 옵션을 이용합니다.
fill
옵션이 true가 되면, Image
가 absolute 속성을 가진 요소처럼 바뀌게 되어 부모 요소에 고정시켜줄 display 속성을 넣어줍니다. 이렇게 설정해주게 되면 부모 컨테이너의 요소 크기만큼 동적으로 width
와 height
값을 가지게 됩니다.
이렇게 이미지 처리를 하게 되면 끝인줄 알았지만, 실제 서버에서 빌드하여 홈페이지를 들어가보니 엄청난 이미지 로딩시간이 있었습니다.
Next.js에서는 이미지를 imageSet의 브레이크 포인트마다 다른 width값을 가진 이미지를 보여주기 위해 처리를 한 뒤에 이미지를 보여주게 됩니다. 그렇게 되다보니 4k 모니터에서는 제일 작은 단위에서부터 제일 큰 단위의 imageSet까지 만들다보니 시간이 오래걸리는 것이었습니다. 이를 해결하기위해 next.config.js
에서 deviceSizes
, imageSizes
를 설정해줍니다.
이렇게 설정해주었음에도 로딩이 조금 더 있었어서 Image
컴포넌트에 loading
옵션이 기본값이 lazy였기 때문에 미리 받아올 수 있도록 eager로 부분 설정해주었습니다.
또한 build시 Next.js
쪽에서 sharp
를 설치하는 것이 더 좋다는 경고문이 발생하길래 sharp
를 설치해보았더니 이미지가 그려지는 속도가 눈에띄게 많이 좋아지는 것을 확인할 수 있었습니다.
마무리
이렇게 Next.js
를 이용하면서 리액트와 달랐던 점을 몇 가지 정리해보았습니다. 13버전 이전에서는 사용한 경험이 있었지만 앱 라우터 환경으로 바뀐 뒤에 사용해본건 처음이라 앱 라우터
, 서버 / 클라이언트 컴포넌트
를 이해하는데 어려웠지만 프로젝트를 진행하면서 많이 이해할 수 있게 되었습니다. 100% 완벽하게 마스터했다는 것은 아니지만 이번 기회를 통해 Next.js
환경에서 SSR
을 구현할 수 있게 된 것 같아 너무 기쁩니다!
Reference
Next.js Docs
Next/Image를 활용한 이미지 최적화 (카카오 기술블로그)
NEXT.JS의 이미지 최적화는 어떻게 동작하는가? (올리브영 기술블로그)