템플릿 컴포넌트 활용
Next.js App Router에서 UI를 공유하고 재사용하는 방법으로는 layout.tsx
파일로 정의하는 레이아웃 컴포넌트 가 가장 보편적입니다. 하지만 때로는 레이아웃과 유사하게 공통 UI를 제공하면서도, 페이지 이동 시 컴포넌트 인스턴스를 새로 생성하고 상태를 초기화해야 하는 특별한 경우가 발생합니다. 이때 사용되는 것이 바로 **템플릿 컴포넌트(template.tsx
)**입니다.
이 절에서는 템플릿 컴포넌트가 레이아웃 컴포넌트와 어떻게 다른지, 그리고 어떤 상황에서 템플릿 컴포넌트를 활용해야 하는지 구체적인 예시와 함께 알아보겠습니다.
템플릿 컴포넌트란 무엇인가요?
템플릿 컴포넌트는 app
디렉터리 내의 특정 라우트 세그먼트 폴더 안에 위치한 template.tsx
파일입니다. 레이아웃과 마찬가지로 children
prop을 받아 해당 라우트의 콘텐츠를 감싸는 역할을 합니다.
레이아웃과의 주요 차이점
특징 | 레이아웃 (layout.tsx ) | 템플릿 (template.tsx ) |
---|---|---|
인스턴스 유지 | 라우트 변경 시 상태를 유지하며, 한 번 마운트되면 해당 라우트 세그먼트 내에서 인스턴스가 유지됩니다. | 라우트 변경 시 항상 새로운 인스턴스가 마운트됩니다. 기존 상태가 파괴되고 새로 시작합니다. |
용도 | 헤더, 푸터, 사이드바 등 항상 고정되어야 할 UI, 데이터 캐싱이 필요한 공통 영역 | 페이지 전환 애니메이션, 상태 초기화, 로거 초기화 등 페이지 변경마다 초기화되어야 할 동작 |
적용 범위 | 자신과 모든 하위 라우트 세그먼트에 적용 | 자신과 모든 하위 라우트 세그먼트에 적용 |
기본 동작 | 기본적으로 서버 컴포넌트 | 기본적으로 서버 컴포넌트 |
템플릿 컴포넌트는 레이아웃과 페이지 사이에 위치하여, 레이아웃이 자식들을 감싸고, 템플릿이 다시 그 레이아웃의 자식(즉, 페이지)을 감싸는 형태로 작동합니다.
렌더링 순서:
layout.tsx
(루트) $\rightarrow$ template.tsx
(루트) $\rightarrow$ layout.tsx
(세그먼트) $\rightarrow$ template.tsx
(세그먼트) $\rightarrow$ page.tsx
템플릿 컴포넌트 활용 시나리오
템플릿 컴포넌트는 다음과 같은 경우에 유용합니다.
-
페이지 전환 애니메이션: 페이지가 바뀔 때마다 컴포넌트를 다시 마운트해야 하는 애니메이션 라이브러리(예: Framer Motion)를 사용할 때 유용합니다. 템플릿을 사용하면 각 페이지 전환 시 애니메이션이 자연스럽게 재실행됩니다.
-
클라이언트 컴포넌트 상태 초기화: 특정 페이지 그룹 내에서 페이지가 변경될 때마다 특정 클라이언트 컴포넌트의 로컬 상태를 초기화하고 싶을 때 사용합니다. 예를 들어, 페이지를 이동할 때마다 폼의 입력값을 초기화하고 싶을 수 있습니다.
-
성능 측정 또는 로거 초기화: 각 페이지 로드 시점을 정확히 측정하거나, 페이지 뷰마다 로거를 초기화하여 새로운 로그 세션을 시작해야 할 때 유용합니다.
템플릿 컴포넌트 구현 실습
간단한 페이지 전환 효과를 통해 템플릿 컴포넌트의 작동 방식을 이해해 봅시다. 이 예제에서는 Framer Motion 라이브러리를 사용하여 페이지 전환 애니메이션을 구현합니다.
-
Framer Motion 설치: 먼저 프로젝트에 Framer Motion 라이브러리를 설치합니다.
npm install framer-motion # 또는 yarn add framer-motion
-
src/app/dashboard/template.tsx
파일 생성:src/app/dashboard
폴더 안에template.tsx
파일을 생성합니다.my-next-app/ └── src/ └── app/ ├── dashboard/ │ ├── layout.tsx │ ├── template.tsx <- 여기에 template.tsx 생성 │ ├── page.tsx │ └── ... └── ...
-
src/app/dashboard/template.tsx
내용 작성: 템플릿 컴포넌트 내에서 Framer Motion의motion
컴포넌트를 사용하여 애니메이션을 적용합니다. 이 컴포넌트는 클라이언트 컴포넌트여야 하므로"use client"
지시어를 추가합니다.src/app/dashboard/template.tsx // src/app/dashboard/template.tsx "use client"; // 클라이언트 컴포넌트임을 명시 import { motion } from 'framer-motion'; export default function DashboardTemplate({ children }: { children: React.ReactNode }) { return ( // motion.div는 Framer Motion의 애니메이션 가능한 div 컴포넌트입니다. <motion.div initial={{ opacity: 0, y: 20 }} // 초기 상태 (투명하고 아래로 20px) animate={{ opacity: 1, y: 0 }} // 애니메이션 종료 상태 (완전히 불투명하고 제자리) transition={{ duration: 0.5, ease: "easeOut" }} // 애니메이션 지속 시간 및 가속 곡선 key={Math.random()} // 중요: key를 변경하여 페이지 이동 시마다 새 인스턴스를 강제 > {children} {/* 여기에 대시보드 페이지 콘텐츠가 렌더링됩니다 */} </motion.div> ); }
설명:
"use client"
: Framer Motion은 클라이언트 사이드에서 작동하므로, 템플릿 컴포넌트를 클라이언트 컴포넌트로 선언해야 합니다.motion.div
: Framer Motion에서 제공하는 컴포넌트입니다.initial
과animate
prop을 사용하여 애니메이션 시작점과 끝점을 정의합니다.key={Math.random()}
: 이것이 중요합니다.key
prop이 변경되면 React는 해당 컴포넌트를 완전히 새로 마운트합니다.template.tsx
는 라우트가 변경될 때마다 새 인스턴스를 생성하므로, 이key
가 변경되어 애니메이션이 매번 트리거되도록 합니다. 만약key
를 고정하거나 생략하면, 라우트 이동 시 템플릿 자체가 리렌더링되지 않아 애니메이션이 제대로 작동하지 않을 수 있습니다.
-
src/app/dashboard/layout.tsx
에 약간의 스타일 추가 (선택 사항): 애니메이션 효과를 더 잘 시각화하기 위해DashboardLayout
에 최소 높이를 지정하는 스타일을 추가할 수 있습니다.src/app/dashboard/layout.tsx // src/app/dashboard/layout.tsx (일부) // ... export default async function DashboardLayout({ children, }: { children: React.ReactNode; }) { const userInfo = await getUserInfo(); // 기존 예시 코드 유지 return ( <div style={{ display: 'flex', minHeight: 'calc(100vh - 180px)', border: '1px solid #ccc', borderRadius: '8px', overflow: 'hidden' }}> {/* ... */} </div> ); }
실습 확인:
개발 서버(npm run dev
)를 실행한 후, http://localhost:3000/dashboard
로 접속합니다. 그 다음 대시보드 메뉴의 다른 링크들(예: "개요", "분석", "설정")을 클릭해 보세요. 각 페이지로 이동할 때마다 대시보드 콘텐츠 부분이 아래에서 위로 부드럽게 나타나는 애니메이션 효과를 볼 수 있습니다.
이 애니메이션은 template.tsx
가 라우트 변경마다 새로 마운트되면서 initial
상태에서 animate
상태로 전환되기 때문에 발생합니다. 만약 이 애니메이션을 layout.tsx
에 직접 적용했다면, 레이아웃은 페이지 이동 시 상태를 유지하므로 애니메이션이 재실행되지 않았을 것입니다.
템플릿 컴포넌트 사용의 장단점
장점
- 페이지 전환 애니메이션 구현 용이: 페이지 이동마다 UI가 새로 마운트되므로 애니메이션 라이브러리와 연동하기 좋습니다.
- 클라이언트 컴포넌트 상태 초기화: 특정 라우트 그룹 내의 페이지 이동 시 클라이언트 컴포넌트의 상태를 강제로 초기화할 수 있습니다.
- 특정 로직의 재실행 보장: 페이지 뷰마다 특정 로거를 초기화하거나 성능 측정을 재설정하는 등, 매번 실행되어야 하는 로직에 유용합니다.
단점/고려사항
- 성능 오버헤드: 페이지 이동 시 컴포넌트 트리의 일부가 새로 마운트되므로, 레이아웃을 사용하는 것보다는 약간의 성능 오버헤드가 발생할 수 있습니다. 꼭 필요한 경우에만 사용해야 합니다.
- 서버 컴포넌트 상태 유지 불가: 템플릿 자체는 서버 컴포넌트일 수 있지만, 그 안에 클라이언트 컴포넌트가 있다면 해당 클라이언트 컴포넌트의 상태는 페이지 이동 시 초기화됩니다. 이는 장점이자 단점이 될 수 있습니다.
- 남용 금지: 모든 공통 UI에 템플릿을 사용하는 것은 비효율적입니다. 대부분의 공통 UI는 레이아웃 컴포넌트로 충분합니다.
템플릿 컴포넌트는 Next.js App Router에서 특정 고급 사용 사례를 위한 강력한 도구입니다. 레이아웃과의 차이점을 명확히 이해하고, 필요에 따라 적절하게 활용하는 것이 중요합니다.