Next.js
강의

#0 - Next.js App Router Co-location 패턴

중년개발자
중년개발자

@loxo

30일 전

36

잘 돌아가는 팀에는 명확한 역할 분담이 있다.
기획자는 기획을, 디자이너는 디자인을, 개발자는 구현을 맡는다. 하지만 이들이 서로 다른 건물에 흩어져 있다면 협업 비용은 급격히 올라간다.

Next.js App Router의 Co-location 패턴은 이 문제를 코드 레벨에서 해결한다.
서버 컴포넌트는 데이터와 책임을, 클라이언트 컴포넌트는 상호작용과 경험을 맡되, 둘을 같은 기능 단위 안에 둔다.
역할은 분리하되, 거리는 가깝게 유지하는 구조다.

이 글에서는 App Router 환경에서 Co-location 패턴이 어떤 의미를 가지는지, 그리고 실제 프로젝트에서 어떻게 적용되는지를 단계적으로 설명한다.

Next.js App Router Co-location 패턴

개요

Next.js App Router에서 서버/클라이언트 컴포넌트를 함께 배치하는 공식 권장 패턴입니다. 관련 파일을 기능 단위로 묶어 관리의 편의성과 유지보수성을 높입니다.

파일 구조

bash
app/ └── posts/ └── [id]/ ├── page.tsx # Server Component (라우트 진입점) ├── PostDetailClient.tsx # Client Component (상호작용 담당) ├── loading.tsx # 로딩 UI (선택 사항) ├── error.tsx # 에러 경계 (선택 사항) └── not-found.tsx # 404 UI (선택 사항)

Server Component (page.tsx)

데이터 페칭, 메타데이터 생성, SEO 처리를 담당합니다. 클라이언트 상호작용이 필요한 부분은 Client Component로 위임합니다.

typescript
import { postService } from '@/services/post.service'; import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; import { PostDetailClient } from './PostDetailClient'; type Props = { params: Promise<{ id: string }>; }; // SEO 메타데이터 생성 export async function generateMetadata({ params }: Props): Promise<Metadata> { const { id } = await params; const post = await postService.getPost(id); return { title: `${post.title} - 내 사이트`, description: post.content?.substring(0, 160), }; } // Server Component - 데이터 페칭 export default async function PostDetailPage({ params }: Props) { const { id } = await params; let post; try { post = await postService.getPost(id); } catch { notFound(); } if (!post) { notFound(); } // 서버에서 가져온 데이터를 클라이언트 컴포넌트로 전달 return <PostDetailClient post={post} />; }

Client Component (PostDetailClient.tsx)

사용자 이벤트(클릭, 입력 등)와 브라우저 API 접근, 상태 관리(useState, useEffect)를 담당합니다.

typescript
'use client'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; import type { Post } from '@/types/post'; interface PostDetailClientProps { post: Post; // 서버에서 전달받은 데이터 } export function PostDetailClient({ post }: PostDetailClientProps) { const router = useRouter(); const [isBookmarked, setIsBookmarked] = useState(false); // 클라이언트 측 상호작용 const handleBookmark = () => { setIsBookmarked(!isBookmarked); }; return ( <div> <h1>{post.title}</h1> <p>{post.content}</p> <button onClick={handleBookmark}> {isBookmarked ? '북마크 해제' : '북마크'} </button> </div> ); }

핵심 원칙 (Key Principles)

원칙설명
Co-location (공동 배치)관련된 파일들을 동일한 폴더에 함께 둠으로써 응집도를 높입니다.
Separation (역할 분리)서버는 데이터 페칭, 클라이언트는 상호작용을 담당합니다.
Performance (성능)서버 컴포넌트를 활용하여 클라이언트 번들 사이즈를 줄입니다.
Maintainability (유지보수)라우트 단위로 코드가 그룹화되어 있어 찾기 쉽고 수정이 용이합니다.

사용 시점 (When to Use)

Server ComponentClient Component
데이터 페칭 (Data Fetching)useState, useEffect 사용 시
데이터베이스 직접 조회onClick, onChange 등 이벤트 처리
API 호출 (보안 민감 정보)브라우저 전용 API (window, document 등)
메타데이터 생성 (SEO)애니메이션 및 트랜지션
무거운 의존성 라이브러리 사용실시간 업데이트 (Real-time updates)

장점 (Benefits)

  1. SEO 향상: 검색 엔진에 최적화된 서버 렌더링 콘텐츠 제공
  2. 성능 최적화: 클라이언트 자바스크립트 번들을 줄여 초기 로딩 속도 향상
  3. 보안 강화: API 키나 민감한 로직을 서버에 숨길 수 있음
  4. 스트리밍 지원: Suspense를 통한 점진적 페이지 렌더링 가능

참고 자료 (References)

목차

#App Router#Co-location#React#TypeScript#next.js 16

댓글 0

Ctrl + Enter를 눌러 등록할 수 있습니다
※ AI 다듬기는 내용을 정제하는 보조 기능이며, 최종 내용은 사용자가 확인해야 합니다.