Ch 02. 다음 단계 — 실전 Next.js로
이 책을 마쳤다고 해서 배움이 끝난 것은 아닙니다. 오히려 이제부터 진짜 시작입니다. 실제 서비스를 만들면서 마주치는 문제들이 가장 좋은 교재입니다.
다음에 배우면 좋은 것들을 소개합니다.
상태 관리 — Zustand와 Jotai
React 앱이 복잡해지면 상태를 어디에 두고 어떻게 공유할지가 문제가 됩니다.
서버 상태 vs 클라이언트 상태를 먼저 구분하는 것이 중요합니다.
- 서버 상태: 서버에서 가져오는 데이터 (게시글 목록, 사용자 정보 등). SWR이나 React Query가 적합합니다.
- 클라이언트 상태: UI 상태 (모달 열림 여부, 선택된 탭 등). Zustand나 Jotai가 적합합니다.
Zustand는 간단하고 직관적인 상태 관리 라이브러리입니다.
import { create } from "zustand";type UIStore = { isModalOpen: boolean; openModal: () => void; closeModal: () => void;};export const useUIStore = create<UIStore>((set) => ({ isModalOpen: false, openModal: () => set({ isModalOpen: true }), closeModal: () => set({ isModalOpen: false }),}));```text**Jotai**는 원자(atom) 단위로 상태를 관리합니다. 필요한 상태만 구독하므로 불필요한 리렌더링이 없습니다.```typescriptimport { atom, useAtom } from "jotai";export const themeAtom = atom<"light" | "dark">("light");// 컴포넌트에서const [theme, setTheme] = useAtom(themeAtom);```text### 테스트 — Vitest와 Playwright코드가 올바르게 동작하는지 자동으로 확인하는 테스트는 큰 프로젝트에서 필수입니다.**Vitest**는 단위 테스트(Unit Test)에 사용합니다. Vite 기반으로 빠르고 Jest와 호환됩니다.```typescript// 파일: lib/utils.test.tsimport { describe, it, expect } from "vitest";import { slugify } from "./utils";describe("slugify", () => { it("영문을 소문자로 변환한다", () => { expect(slugify("Hello World")).toBe("hello-world"); }); it("특수문자를 하이픈으로 대체한다", () => { expect(slugify("Next.js 14")).toBe("nextjs-14"); });});```text**Playwright**는 E2E(End-to-End) 테스트에 사용합니다. 실제 브라우저에서 사용자 행동을 시뮬레이션합니다.```typescript// 파일: tests/login.spec.tsimport { test, expect } from "@playwright/test";test("로그인 후 대시보드로 이동한다", async ({ page }) => { await page.goto("/login"); await page.fill('[name="email"]', "[email protected]"); await page.fill('[name="password"]', "password123"); await page.click('[type="submit"]'); await expect(page).toHaveURL("/dashboard");});```text### 모노레포 — Turborepo여러 Next.js 앱이나 공유 패키지를 하나의 저장소에서 관리할 때 Turborepo가 유용합니다.
my-company/ ├── apps/ │ ├── web/ # Next.js 메인 앱 │ └── admin/ # Next.js 관리자 앱 ├── packages/ │ ├── ui/ # 공유 UI 컴포넌트 │ ├── utils/ # 공유 유틸리티 │ └── tsconfig/ # 공유 TypeScript 설정 └── turbo.json
Turborepo는 변경된 패키지만 빌드하고 나머지는 캐시에서 가져오므로 빌드 속도가 크게 향상됩니다.### 실시간 기능 — Socket.io와 Supabase Realtime채팅, 알림, 실시간 협업 같은 기능은 WebSocket이 필요합니다.**Socket.io**는 WebSocket을 쉽게 사용할 수 있는 라이브러리입니다. Next.js에서는 별도의 Node.js 서버가 필요합니다.**Supabase Realtime**은 PostgreSQL 데이터베이스 변경을 실시간으로 구독합니다. 인프라 설정 없이 쉽게 실시간 기능을 추가할 수 있습니다.```typescript// Supabase Realtime 예시import { createClient } from "@supabase/supabase-js";const supabase = createClient(url, key);// posts 테이블의 변경을 실시간으로 구독const channel = supabase .channel("posts") .on( "postgres_changes", { event: "INSERT", schema: "public", table: "posts" }, (payload) => { console.log("새 글이 추가됐습니다:", payload.new); } ) .subscribe();
추천 학습 자료
공식 문서가 최고의 레퍼런스입니다.
- Next.js 공식 문서:
nextjs.org/docs - React 공식 문서:
react.dev - Prisma 공식 문서:
prisma.io/docs - Auth.js 공식 문서:
authjs.dev
실습이 최선입니다. 문서를 읽는 것보다 직접 만들어보는 것이 훨씬 빠릅니다. 자신만의 프로젝트를 하나 정하고 처음부터 끝까지 만들어보세요.
커뮤니티 활용: Stack Overflow, GitHub Discussions, 각 라이브러리의 Discord 서버에서 막히는 부분을 물어볼 수 있습니다.
다음 챕터에서는 따뜻한 마무리 인사를 전합니다.