프로젝트 시작
생성
npx create-next-app@latest
선택한 옵션
- TypeScript
- App Router
- Tailwind CSS
- src 구조
처음 만난 에러
Missing <html> and <body> tags in the root layout
처음 실행하자마자 만난 에러.
원인
App Router에서는:
layout.tsx가 HTML 뼈대 역할
을 한다.
그래서 반드시:
<html>
<body>
태그가 필요했다.
수정
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ko">
<body>{children}</body>
</html>
);
}
App Router 구조 이해하기
예전 React처럼:
“컴포넌트만 만들면 되는 구조”
가 아니라,
폴더 구조 자체가 라우팅이었다.
예시
app/
├─ admin/
│ ├─ ads/
│ │ ├─ page.tsx
의미
/admin/ads
URL이 자동 생성된다.
관리자 페이지 만들기 시작
처음 만든 건 광고 목록 페이지였다.
더미 데이터 만들기
dummy-data.ts
export type AdStatus = "진행중" | "대기" | "종료";
export type Ad = {
id: number;
title: string;
position: string;
status: AdStatus;
startDate: string;
endDate: string;
};
export const ads: Ad[] = [
{
id: 1,
title: "메인 상단 배너 광고",
position: "홈 메인 상단",
status: "진행중",
startDate: "2026-05-01",
endDate: "2026-05-31",
},
];
여기서 처음 느낀 TypeScript의 장점
status 값을 마음대로 못 넣게 막아준다
예:
status: "완료" ❌
TailwindCSS 사용
처음엔 클래스가 너무 길어 보였는데,
점점:
“HTML 안에서 바로 스타일 조합”
하는 방식이 엄청 빠르다는 걸 느꼈다.
반응형도 쉽게 가능
<div className="flex flex-col gap-4 sm:flex-row">
의미
모바일:
세로 배치
sm 이상:
가로 배치
상세 페이지 만들기
다음은 동적 라우팅.
구조:
app/admin/ads/[id]/page.tsx
여기서 이해한 개념
[id] = URL 파라미터
예시
/admin/ads/1
/admin/ads/2
처음 만난 params 에러
에러
params is a Promise
원인
Next.js App Router 최신 버전에서는:
params가 Promise 형태일 수 있다
해결
type Props = {
params: Promise<{
id: string;
}>;
};
export default async function Page({ params }: Props) {
const { id } = await params;
}
여기서 이해한 핵심
App Router는 전체적으로:
async 기반 구조
라는 것.
Server Component vs Client Component
여기서 가장 많이 헷갈렸다.
처음엔 이렇게 생각했다
“수정 페이지도 서버 데이터 쓰니까 Server Component 아닌가?”
하지만 핵심 차이는 이것이었다
Server Component
데이터 조회
Client Component
브라우저 인터랙션
예시
Server Component 가능
- 데이터 가져오기
- DB 조회
- 페이지 렌더링
Client Component 필요
- useState
- onClick
- input 입력
- 모달
- 이벤트 처리
여기서 배운 핵심
“브라우저에서 동작하면 Client”
use client 에러도 경험
처음엔:
use strict
를 써놓고 왜 안 되는지 한참 헤맸다 😇
실제로 필요한 건
"use client";
수정 페이지 만들기
수정 페이지에서는:
- input
- select
- state
를 사용했다.
여기서 배운 것
폼 = 거의 Client Component
TypeScript select 이벤트도 처음 경험
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
setStatus(e.currentTarget.value as AdStatus);
}}
처음 느낌
“왜 이렇게 길어?”
그런데 이해하고 보니
TypeScript가:
“이 이벤트가 select 이벤트라는 걸 명확하게 알려주는 것”
이었다.
삭제 모달 만들기
다음은 관리자 페이지에서 자주 쓰는 기능인:
삭제 버튼 + 확인 모달
구조 분리
[id]/page.tsx
└── DeleteModal.tsx
왜 분리했나?
상세 페이지는:
async function
형태의 Server Component였고,
모달은:
- useState
- onClick
이 필요했기 때문.
여기서 이해한 개념
Server Component 안에
Client Component를 넣을 수 있다
API Route 만들기
이번엔 진짜 CRUD 느낌이 나기 시작했다.
구조
src/app/api/ads/[id]/route.ts
App Router에서 API 만드는 법
route.ts = API endpoint
DELETE API 작성
export async function DELETE() {
}
fetch 요청
await fetch(`/api/ads/${adId}`, {
method: "DELETE",
});
그런데 DELETE가 안 됐다
GET은 되는데 DELETE만:
404 Not Found
진짜 원인
문제는 params 타입이었다.
잘못된 코드
{ params }: { params: { id: string } }
해결 코드
{ params }: { params: Promise<{ id: string }> }
const { id } = await params;
여기서 배운 핵심
App Router 최신 구조에서는:
params도 async 흐름일 수 있다
Turbopack도 처음 이해하게 됨
에러 로그에 계속 보이던:
turbopack
Turbopack이란?
쉽게 말하면:
Next.js 개발 서버를 빠르게 돌려주는 최신 엔진
장점
- 빠른 실행
- 빠른 HMR
- Next.js 최적화
단점
가끔:
- route.ts 인식 문제
- 캐시 문제
- POST/DELETE 이상 동작
같은 이슈가 있음.
그래서 실무에서는
문제 생기면:
rm -rf .next
npm run dev
하거나,
next dev --no-turbo
로 잠시 끄기도 한다.
이번 프로젝트로 이해한 것들
✔ App Router 구조
폴더 구조 자체가 라우팅
✔ Server vs Client
조회는 서버
인터랙션은 클라이언트
✔ CRUD 흐름
브라우저 → API → 데이터 처리
✔ async 기반 구조
params도 Promise일 수 있다
느낀 점
처음엔:
“Next.js 너무 어려운데?”
싶었는데,
관리자 페이지를 실제로 만들면서 배우니까:
- 왜 Server Component가 필요한지
- 왜 Client Component가 필요한지
- 왜 TypeScript를 쓰는지
- 왜 App Router가 async 기반인지
조금씩 연결되기 시작했다.
특히 단순히 문법만 보는 게 아니라:
“실제 기능을 만들며 배우는 방식”
이 훨씬 이해가 잘 됐다 😄
728x90
'Web > react' 카테고리의 다른 글
| [Next.js] Next.js App Router 경계 정리표 (0) | 2026.05.18 |
|---|---|
| [Next.js] Server vs Client Component 완전 정리 (0) | 2026.05.08 |
| [Next.js] Next.js + TypeScript + Tailwind CSS 관리자 페이지 만들기 1 (1) | 2026.05.08 |
| [Next.js] 초보의 관리자 페이지 만들기 시작기 (0) | 2026.04.28 |
| [Next.js] 프로젝트 시작하기 (0) | 2026.04.24 |