2주간의 사전교육기간이 이렇게 종료됐다. 정말 방대하고 또 어려운 지식을 2주동안 머리에 쑤셔 넣으려니 머리에 쥐가 날 것 같지만(이미 난 것 같다), 그만큼 또 많이 배워간다고 생각하니 내심 뿌듯하기도 하다.
이번주에는 Next.js에 대해 제대로 톺아보는 시간을 가져봤다. Next.js가 무엇인지, 그리고 어떻게 활용하는지 등을 세세하게 살펴보았다. 교육기간동안 배운 내용을 통해 프로젝트를 잘 진행하고, Next.js로 만든 내 개인 블로그를 보다 나은 방향으로 리팩토링할 계획이다.
서버 컴포넌트 / 클라이언트 컴포넌트와 함께 나를 가장 헷갈리게 했던 개념이다. 이 둘에 대해 알아보기 전에, 먼저 하이드레이션에 대해서 알아보자.
하이드레이션(Hydration)은 서버에서 렌더링된 정적 HTML 콘텐츠를 클라이언트 측에서 동적인 웹 애플리케이션으로 활성화하는 과정을 말한다. 일반적으로 다음과 같은 과정을 거치며 하이드레이션이 일어난다.
하이드레이션은 초기 로딩 속도를 개선해 사용자가 빠르게 컨텐츠를 볼 수 있고, 검색 엔진이 초기 HTML 콘텐츠를 크롤링해 SEO가 향상되며, 서버와 클라이언트 간의 작업을 효율적으로 분배해 성능이 최적화된다는 장점을 가지고 있다. 그렇다면 SSR와 CSR은 하이드레이션과 어떤 연관성을 가지고 있을까?
HTML과 CSS, JavaScript를 불러오면 이후에 화면을 렌더링하는 방식이다. JavaScript를 한번 불러오면 페이지 간의 이동이 빠르지만, 대신 JS를 불러올 때 로딩이 오래 걸리면 사용자는 초기 페이지를 늦게 확인할 수 밖에 없다.
그러한 CSR의 단점을 개선하기 위해서, SSR은 HTML과 CSS를 서버에서 만들어 사용자(클라이언트)에게 보내준다. 이후 하이드레이션 과정을 거쳐 클라이언트에서 JavaScript를 불러오면, 최종적으로 정적인 웹 콘텐츠가 동적으로 변환된다. 따라서 SSR을 사용하면 사용자는 화면을 조금 더 일찍 확인할 수 있다.
Next.js는 바로 이러한 서버 사이드 렌더링 기반의 프레임워크이다. 기존 React CSR의 한계를 극복하고, 개발자가 쉽게 SSR을 구현할 수 있도록 도와주며 다양한 성능 최적화 기법을 지원한다.
서버 컴포넌트와 클라이언트 컴포넌트는 React 18에서 도입된 새로운 개념인 RSC 등을 기반으로 한다.
서버 컴포넌트(Server Component)는 서버에서 렌더링되고 실행되는 컴포넌트로, 클라이언트로 전송되는 JS 번들의 크기를 줄이며 서버의 리소스에 직접 접근할 수 있다. 따라서 클라이언트에서 상호작용 하지 않아도 되는 UI를 구성하는데 적합하며, 기본적으로 Next.js의 컴포넌트는 모두 서버 컴포넌트의 형태로 구성된다.
서버에서 직접 DB나 파일 시스템에 접근할 수 있고, API 키나 토큰같은 민감한 정보를 클라이언트에 노출하지 않고 사용할 수 있다는 장점을 가지고 있다. 다만 클라이언트 컴포넌트와 다르게 상태를 가지거나 이벤트 핸들러와 브라우저 API를 사용할 수 없다.
반면 클라이언트 컴포넌트(Client Component)는 기존의 리액트 컴포넌트처럼 리액트의 모든 기능을 사용할 수 있으며, 상호작용이 필요한 UI 부분에 사용하는 것이 적합하다. 따라서 이를 적절하게 사용하는 것이 Next.js를 잘 활용하는 방법이라고 할 수 있다.
Next.js의 라우팅에 대해 설명하기에 앞서, 라우트, 라우팅, 라우터를 짧게 정의하고 넘어가자.
Next.js는 파일 기반 라우팅을 지원한다. 말 그대로 내가 Next.js를 사용한 프로젝트에서 만드는 폴더와 파일을 통해 라우팅을 하게 된다는 것이다. 간단한 예시를 살펴보자.
app/
├── page.tsx
├── layout.tsx
├── blog/
│ ├── page.tsx
│ └── posts/
│ └── [slug]/
│ └── page.tsx
└── components/
├── Header.tsx
└── Footer.tsx
이렇게 구성된 프로젝트 구조는 다음과 같은 라우팅을 지원한다:
/
/blog
blog/posts
blog/posts/게시글-slug
Next.js는 이러한 라우팅 방식을 Page Router와 App Router라는 라우터로 처리하고 있다. 기존 Page Router에서 발전된 방식을 사용하고 있는 것이 App Router이며, Next.js도 현재 App Router를 기반으로 프로젝트를 만드는 것을 권장하고 있다. 사실 블로그를 만들 때에도 Page Router가 아닌 App Router를 사용했는데, 왜 Vercel은 Next.js에 App Router라는 새로운 라우팅 시스템을 도입했는지 궁금했었다. 정리하자면 다음과 같다.
먼저 Page Router는 pages
디렉토리 내의 파일 구조가 곧 라우트 구조가 되는 방식이다. 또한 공통의 레이아웃을 만들기 위해서는 컴포넌트를 수동으로 중첩해야 한다는 문제가 있었다. 데이터 페칭 방법으로는 getStaticProps
, getServerSideProps
, getInitialProps
등을 사용했다.
App Router는 모든 컴포넌트가 기본적으로 서버 컴포넌트이며, 필요에 따라 'use client'
를 명시함으로써 클라이언트 컴포넌트로 전환이 가능하다. 데이터 페칭 역시 컴포넌트 내에서 직접 비동기 함수를 사용할 수 있으며, 레이아웃의 경우 자동 중첩 레이아웃을 지원한다. 이외에도 병렬 라우트와 같은 고급 라우팅 패턴을 지원하며, 스트리밍 기법을 통해 내장된 최적화를 지원한다.
개인적으로 스트리밍 기법이 흥미롭게 느껴졌는데, 이를 통해 보다 나은 SSR을 지원할 수 있다. 스트리밍 기법은 전체 페이지가 서버에서 렌더링될 때까지 기다리지 않고, 준비된 부분부터 점진적으로 클라이언트에 전송하는 기술이다.
예를 들어 위와 같은 페이지가 있을 경우, Next.js의 스트리밍 기법은 준비가 완료되는 컴포넌트를 순차적으로 클라이언트에 보내준다. 이를 통해 초기 페이지 로딩 시간을 단축할 수 있고, 사용자는 일부 컨텐츠를 먼저 확인할 수 있다는 장점이 있다. App Router에서는 Suspense
를 활용해 특정 컴포넌트에 스트리밍 기법을 적용할 수 있다.
Next.js는 App Router를 기반으로 다양한 파일 기반 라우팅 방법을 지원한다.
app/
├── page.tsx
├── about/
│ └── page.tsx
├── contact/
│ └── page.tsx
└── products/
└── page.tsx
먼저 가장 기본적인 라우팅 기법이다. 폴더와 페이지를 사용해 간편하게 만들 수 있다.
app/
├── page.tsx
├── products/
│ ├── page.tsx
│ └── [id]/
│ └── page.tsx
└── users/
└── [userId]/
└── posts/
└── [postId]/
└── page.tsx
동적 라우팅 기법은 위와 같다. [id], [userId], [postId]가 동적인 세그먼트를 기반으로 라우팅을 지원한다.
이번주에 Next.js를 학습하면서 가장 어려웠던 부분이라고 생각하는데, 바로 캐싱과 관련된 부분이다. 사실 지금도 이해가 완벽하게 되진 않아서 블로그에 기록한 뒤 계속해서 다시 읽어보려고 한다.
miss: 특정 메커니즘에 대한 캐시가 존재하지 않음. 원본 데이터 소스에서 데이터를 가져와야 함.
hit: 요청된 데이터가 캐시에 있어 즉시 사용 가능함
set: 새로운 데이터를 캐시에 저장함. 일반적으로 miss 이후에 발생.
Next.js의 캐싱 과정을 자체적으로 Request Memoization, Data Cahce, Full Route Cache, Router Cache 4가지의 방법을 가진다.
revalidateCache
등의 방법으로 무력화 가능하다.revalidate
옵션을 사용해 캐시 유효기간을 지정할 수 있다.이러한 캐싱의 문제점은 최신 데이터를 서버에서 보내줘도 클라이언트가 캐싱되어 있는 데이터를 먼저 보여주기 때문에, 실시간 데이터 흐름이 강조되는 서비스에서는 문제가 발생할 수도 있다는 것이다. 따라서 캐싱 무력화 방법을 사용해 적절하게 적용하는 것이 중요하다고 한다.
정말 극적인 효과를 보고 있는 것은 아니지만, 꾸준하게 운동을 가고 루틴에 맞춰 살다 보니 몸 상태가 점점 예전으로 돌아가고 있는 게 조금이나마 느껴지고 있다 👍🏻
수업시간에 배운 내용을 기록하면서 듣다 보니 잠깐 놓치거나 이해가 되지 않는 부분이 있어도 집에 가서 다시 확인할 수 있었다 🧑🏻💻
정리하는 것 까지는 좋지만, 돌아와서 나만의 언어로 다시 가공하지 않는다면 그냥 따라쓰기에 불과하다고 느껴졌다. 최근 글 하나를 읽게 됐는데, 제텔카스텐(Zetelkasten) 메모 방식을 소개하는 글이다. 이 방법이 도움이 될 것 같다.
그래서 지금은 제텔카스텐 메모를 꾸준히 시도하고 있다. 물론 제대로 활용하기까지는 시간이 걸리겠지만, 제텔카스텐의 핵심 개념인 '하나의 메모에는 한 가지 생각만 정리한다'를 지키려고 노력하고 있다.
이렇게 2주간의 사전직무교육이 모두 마무리됐다. 길다면 길고, 짧다면 짧은 기간이었지만, 그 기간동안 배워가는 것 만큼은 정말 많았던 것 같다. 사실 아직도 소화가 덜 된 기분이긴 한데, 프로젝트를 진행하면서 개념을 본격적으로 활용해볼 예정이다.
프로젝트를 시작하기 전 팀이 정해지고 이런저런 계획을 세우다 보니 조금은 불안하기도 하다. 오랜만의 팀장 경험이고, 스스로의 실력에 대한 확신도 100%는 아닌 것 같다. 하지만 늘 그래왔듯 잘 해낼 수 있을 거라고 믿고 이렇게 회고를 마무리해본다. 다음 주에는 프로젝트 회고와 함께 돌아와야지 😁
본 후기는 본 후기는 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 3기 과정(B-log) 리뷰로 작성 되었습니다.
#유데미 #udemy #웅진씽크빅 #스나이퍼팩토리 #인사이드아웃 #미래내일일경험 #프로젝트캠프 #부트캠프 #React #리액트프로젝트 #프론트엔드개발자양성과정 #개발자교육과정