Next.js의 .next 폴더는 무엇이며 소스 파일과 어떻게 구분하나요?
.next는 직접 작성하는 소스가 아니라 Next.js가 개발·빌드 과정에서 만드는 결과 폴더입니다. app과 package.json을 수정하는 작업과 생성 결과를 확인하는 작업을 구분합니다.
적용 조건·범위: .next가 Git 상태에 나타나면 ignore 누락과 기존 추적 여부를 git ls-files .next로 구분합니다. 오래된 결과가 의심되면 먼저 소스·환경변수·의존성·실행 명령을 비교합니다.
독립 실습: .next를 삭제하지 않고 생성 결과 확인
React 컴포넌트·TypeScript·async/await를 알고 시작합니다. Next.js 환경 준비의 루트 app/ TypeScript App Router 프로젝트를 별도 연습 폴더에서 사용합니다. 이 글의 기존 실습 기준은 Next.js 16.3.5이며 Cache Components는 끈 구성입니다. 생성된 app/layout.tsx는 유지합니다. 아래 파일은 TaskFlow·인증·DB 없이 실행하는 독립 예제입니다.
별도 연습 프로젝트의 app/page.tsx에 표시 문구를 정하고 build를 실행합니다. .next 존재와 수정 시각을 파일 탐색기에서 확인한 뒤 start 화면을 봅니다. 소스 문구를 바꾸어도 현재 production 산출물이 자동으로 새로 빌드되는지 비교합니다.
npm run build
npm run start
기대 결과: 소스와 마지막 build 결과는 다릅니다. 새 문구는 새 build 후 production에 반영되며 .next 내부를 직접 편집할 필요가 없습니다.
자가 확인: git ls-files .next 결과와 .gitignore를 비교합니다. 이미 추적한 파일은 ignore 추가만으로 추적 해제되지 않는 이유를 설명합니다.
.next는 생성 결과물입니다
소스 코드
app/
components/
...
↓ next dev / next build
.next/
.next 내부 파일은 원본이 아닙니다.
직접 수정해도 다음 dev/build에서 다시 생성될 수 있습니다.
개발 서버 실행
npm run dev
개발 과정에서도 .next가 생성됩니다.
Hot Reload, 개발용 번들, 캐시 같은 파일이 들어갈 수 있습니다.
프로덕션 빌드
npm run build
빌드가 끝나면 프로덕션 실행에 필요한 산출물이 생성됩니다.
Git에 올리지 않습니다
일반적으로 create-next-app이 만든 .gitignore에는 .next가 포함됩니다.
.next/
빌드 산출물을 소스 저장소에서 직접 관리하지 않습니다.
언제 .next를 삭제할까
개발 중 오래된 캐시 때문에 이상한 결과가 의심될 때 삭제 후 다시 실행해 볼 수 있습니다.
macOS/Linux:
rm -rf .next
npm run dev
Windows PowerShell:
Remove-Item .next -Recurse -Force
npm run dev
하지만 오류가 날 때마다 무조건 삭제하는 것은 원인 분석이 아닙니다.
먼저:
– 소스 코드 오류인지
– 환경변수 문제인지
– dependency 문제인지
– 실제 캐시 문제인지
구분합니다.
선택 실습: 할 일 앱에 적용하기
TaskFlow는 작업을 추가·완료·삭제하는 할 일 앱입니다. 이 선택 실습에서는 Next.js로 페이지와 서버 기능을 확장합니다.
app 소스와 .next 빌드 결과를 구분하고 실행 뒤 생성되는 파일과 Git 제외 설정을 확인합니다.
선택: 연재 프로젝트와 전체 자료
연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.
TaskFlow Next.js 프로젝트 적용
시작 체크포인트: 이전: Next.js Server Component와 Client Component 구분의 완료 상태에서 이어갑니다.
이전 회차 완료 ZIP으로 시작하기. 새 폴더에 풀어 현재 회차 완료본과 나란히 비교합니다.
이전 완료본과 달라진 파일: package.json. 아래 변경 설명을 따라 수정하고 전체 코드의 같은 파일과 대조합니다.
직접 확인: .next 생성 전후
독자 질문: .next를 지우면 소스 코드도 사라질까요? app/과 package.json은 직접 관리하는 소스이고 .next/는 실행 명령이 만든 결과입니다. 삭제를 시험하기 전 프로젝트 루트와 Git 상태를 확인합니다.
- 4회차 ZIP의
app/tasks/page.tsx,app/ui/task-search.tsx,package.json을 확인합니다..gitignore의.next항목과git status --short를 비교합니다. ignore 규칙은 이미 추적된 파일을 자동으로 제거하지 않습니다. npm install,npm run dev뒤/tasks에서 ‘작업 목록’ 제목·검색 입력·목록을 확인합니다. 서버를 끈 뒤 프로젝트 루트에.next/가 생성됐는지 봅니다. 내부 파일 구성은 버전과 설정에 따라 달라집니다.npm run typecheck와npm run build를 실행합니다. 이 ZIP의 build script는next build --webpack입니다. 성공했을 때만 산출물의 수정 시각,npm run start와/tasks화면을 확인합니다..next가 Git 상태에 나타나면 ignore 누락과 기존 추적 여부를git ls-files .next로 구분합니다. 오래된 결과가 의심되면 먼저 소스·환경변수·의존성·실행 명령을 비교합니다.
결과·오류 기록
이번 편집에서는 설치·개발 서버·타입 검사·빌드·start·Git 상태·브라우저를 실행하지 않았습니다. 실제 .next 내용과 화면, 빌드 결과는 미확인입니다. 직접 실행할 때에는 명령의 종료 코드, 폴더 존재 여부, /tasks의 제목을 기록하세요. start가 산출물을 찾지 못하면 이전 build 성공 여부를 확인합니다.
스스로 확인할 문제와 다음 학습
.gitignore에 .next가 있어도 이미 커밋한 파일이 자동으로 추적에서 빠질까요? 아닙니다. 다음 6회차에서는 Route Group·Private Folder·template의 경계를 학습합니다.
완료 코드 ZIP 다운로드 — next-step-05-next-output
이전 체크포인트: next-step-04-server-client
이번 회차의 변경
실행 코드는 이전 회차를 유지합니다. .next는 자동 생성되는 빌드 결과이며 ZIP에 넣거나 직접 수정하지 않습니다.
이 회차는 이전 프로젝트의 실행 코드를 유지하고 설정·오류·운영 기준을 점검합니다.
전체 파일·코드 보기
실행과 검증
Node.js 22 LTS 이상 권장(Next.js 최소 20.9). 루트 app/ 구조, CSS 사용.
npm install
npm run dev
npm run build
npm run start
전체 코드와 ZIP은 같은 완료 체크포인트입니다. 자동 생성 worker는 ZIP에 포함하며 라이브러리 파일은 코드 뷰어에서 생략합니다.
완료 체크포인트: next-step-05-next-output
실습 프로젝트 전체 코드
.gitignore
node_modules
.next
.env*
!.env.example
*.tsbuildinfo
app/_components/SiteHeader.tsx
import Link from "next/link";
export default function SiteHeader() {
return (
<header>
<nav aria-label="주 메뉴">
<Link href="/">홈</Link>
<Link href="/tasks">작업</Link>
<Link href="/settings">설정</Link>
</nav>
</header>
);
}
app/globals.css
* {
box-sizing: border-box;
}
body {
margin: 0;
color: #171717;
background: #fafafa;
font-family: Arial, sans-serif;
line-height: 1.6;
}
header,
main {
width: min(880px, calc(100% - 32px));
margin: 24px auto;
}
nav {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: center;
}
a {
color: inherit;
text-underline-offset: 3px;
}
button,
input,
select {
min-height: 44px;
padding: 8px 12px;
font: inherit;
border: 1px solid #aaa;
background: #fff;
color: #171717;
max-width: 100%;
}
button {
cursor: pointer;
}
button:disabled {
opacity: 0.6;
cursor: wait;
}
input {
display: block;
width: 100%;
}
label {
display: block;
}
form,
section,
aside {
margin: 20px 0;
}
ul {
padding: 0;
list-style: none;
}
li {
padding: 12px;
margin: 12px 0;
border: 1px solid #ccc;
overflow-wrap: anywhere;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.controls form {
margin: 0;
}
.modal {
width: min(640px, calc(100% - 32px));
border: 1px solid #777;
padding: 24px;
background: #fff;
color: #171717;
}
.modal::backdrop {
background: rgb(0 0 0 / 55%);
}
:focus-visible {
outline: 3px solid #555;
outline-offset: 3px;
}
[data-theme="dark"] {
background: #202020;
color: #fafafa;
padding: 16px;
}
[data-density="compact"] li {
padding: 8px;
margin: 8px 0;
}
img {
max-width: 100%;
height: auto;
}
app/layout.tsx
import type { Metadata } from "next";
import type { ReactNode } from "react";
import "./globals.css";
import SiteHeader from "./_components/SiteHeader";
export const metadata: Metadata = {
title: { default: "TaskFlow Next.js", template: "%s | TaskFlow" },
description: "서버와 브라우저 역할을 배우는 TaskFlow 학습 프로젝트",
};
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="ko">
<body>
<SiteHeader />
{children}
</body>
</html>
);
}
app/lib/data.ts
import type { Task } from "./types";
const tasks: Task[] = [
{
id: "task-1",
title: "App Router 공부",
description: "서버와 브라우저의 역할을 구분합니다.",
completed: false,
ownerId: "demo",
},
{
id: "task-2",
title: "TaskFlow 만들기",
description: "누적 실습을 완성합니다.",
completed: true,
ownerId: "demo",
},
];
export async function getTasks() {
return tasks;
}
export async function getTask(id: string) {
return tasks.find((t) => t.id === id);
}
export async function getTaskStats() {
const ts = await getTasks();
return { total: ts.length, completed: ts.filter((t) => t.completed).length };
}
app/lib/types.ts
export type Task = {
id: string;
title: string;
description: string;
completed: boolean;
ownerId: string;
};
app/page.tsx
import Link from "next/link";
export default function HomePage() {
return (
<main>
<h1>TaskFlow Next.js</h1>
<p>App Router 기반 작업 관리 서비스입니다.</p>
<Link href="/tasks">작업 목록 보기</Link>
</main>
);
}
app/settings/page.tsx
export default function SettingsPage() {
return (
<main>
<h1>설정</h1>
<p>TaskFlow 설정 화면입니다.</p>
</main>
);
}
app/tasks/page.tsx
import Link from "next/link";
import { getTasks } from "@/app/lib/data";
import TaskSearch from "@/app/ui/task-search";
export default async function TasksPage() {
const tasks = await getTasks();
return (
<main>
<h1>작업 목록</h1>
<TaskSearch />
<ul>
{tasks.map((task) => (
<li key={task.id}>
{task.title}
<span> · {task.completed ? "완료" : "진행 중"}</span>
</li>
))}
</ul>
</main>
);
}
app/ui/task-search.tsx
"use client";
import { useState } from "react";
export default function TaskSearch() {
const [keyword, setKeyword] = useState("");
return (
<label>
작업 검색
<input
value={keyword}
onChange={(event) => setKeyword(event.target.value)}
placeholder="검색어 입력"
/>
</label>
);
}
next-env.d.ts
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
import "./.next/types/root-params.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
next.config.ts
import type { NextConfig } from "next";
const config: NextConfig = { cacheComponents: false };
export default config;
package.json
{
"name": "taskflow-next",
"version": "1.0.5",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build --webpack",
"start": "next start",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"next": "16.3.5",
"react": "19.3.0",
"react-dom": "19.3.0"
},
"devDependencies": {
"typescript": "5.9.3",
"@types/node": "24.13.6",
"@types/react": "19.3.0",
"@types/react-dom": "19.3.0"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./*"
]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}
글 소개·학습 준비·전체 목차

Next.js · 5/38 · TaskFlow 누적 실습
Next.js next 폴더와 빌드 산출물 이해
Next.js · 5 / 38 · 필수 학습 · .next 빌드 산출물
Next.js 프로젝트를 실행하면 루트에 .next 폴더가 생깁니다. 이 폴더는 직접 작성하는 소스가 아니라 Next.js가 개발·빌드 과정에서 생성한 결과물입니다.
이번 글에서 배우는 내용
- .next 폴더 역할
- dev와 build의 차이
- 생성 파일을 직접 수정하면 안 되는 이유
- .gitignore 처리
- 캐시 문제 진단 시 삭제 기준
- build 결과 확인
목차
다음 학습
다음 글에서는 URL에는 나타나지 않는 Route Group, private folder, layout과 template의 차이를 이용해 TaskFlow 디렉터리 구조를 정리합니다.
공식 문서
이 글이 도움이 되었나요?
읽음 기록
읽음 — / 전체 39개 · 필수 38개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.