
React의 강력한 UI 라이브러리와 서버 사이드 기능을 결합한 Next.js, 데이터베이스를 쉽게 다루게 해주는 Prisma, 그리고 환경의 제약 없이 배포를 도와주는 Docker를 결합한 현대적인 웹 개발 스택 학습 정리입니다.
| 특징 | React.js (CSR) | Next.js (App Router) |
|---|---|---|
| 렌더링 | 클라이언트에서 UI 생성 | 서버에서 미리 HTML 생성 (SSR/SSG) |
| 라우팅 | 라이브러리 설치 필요 | 파일 시스템 기반 (폴더=URL) |
| 백엔드 | 별도의 API 서버 필요 | Server Actions로 직접 DB 접근 가능 |
npx create-next-app@latest my-app
# TypeScript, Tailwind CSS, App Router 필수 선택
docker-compose.yml을 통해 로컬 설치 없이 DB 환경을 즉시 구축합니다.
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mymemo_db
ports:
- "5432:5432"
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: "postgresql://.../mymemo_db"
depends_on:
- db
※ Prisma는 JavaScript / TypeScript에서 DB를 쉽게, 안전하게 쓰게 해주는 도
schema.prisma 파일에 데이터 모델을 설계합니다.npx prisma migrate dev 명령어로 DB에 테이블을 생성합니다.npx prisma generate를 통해 Type-safe한 클라이언트를 생성합니다.Next.js의 핵심 기능인 Server Actions를 사용하면 클라이언트 측 코드에서 직접 서버 함수를 호출하여 DB를 조작할 수 있습니다.
"use server" 지시어를 함수 상단에 선언revalidatePath("/")를 사용해 데이터 변경 후 화면을 즉시 갱신// src/app/page.tsx
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
export default async function Home() {
// 1. DB에서 데이터 불러오기 (Server Component의 위력!)
const memos = await db.memo.findMany({
orderBy: { createdAt: "desc" },
});
// 2. 데이터 저장을 위한 Server Action
async function createMemo(formData: FormData) {
"use server"; // 이 함수는 서버에서만 실행됨을 선언
const content = formData.get("content") as string;
if (content) {
await db.memo.create({ data: { content } });
revalidatePath("/"); // 데이터가 바뀌었으니 화면을 새로고침하라고 명령
}
}
return (
<main className="p-10 max-w-md mx-auto">
<h1 className="text-2xl font-bold mb-5">나의 메모장</h1>
{/* 메모 작성 폼 */}
<form action={createMemo} className="flex gap-2 mb-8">
<input
name="content"
className="border p-2 flex-1 rounded text-black"
placeholder="메모를 입력하세요..."
required
/>
<button className="bg-blue-500 text-white px-4 py-2 rounded">저장</button>
</form>
{/* 메모 리스트 출력 */}
<ul className="space-y-2">
{memos.map((memo) => (
<li key={memo.id} className="p-3 border-b flex justify-between">
<span>{memo.content}</span>
<span className="text-xs text-gray-400">
{memo.createdAt.toLocaleTimeString()}
</span>
</li>
))}
</ul>
</main>
);
}
Dockerfile을 작성하여 앱을 이미지화하고, docker-compose를 통해 앱 서버와 DB 서버를 하나의 네트워크로 묶어 실행합니다.
# Dockerfile 요약
FROM node:20-alpine AS base
# 1. 의존성 설치
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# 2. 빌드 단계
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Prisma Client 생성 포함
RUN npx prisma generate
RUN npm run build
# 3. 실행 단계
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
EXPOSE 3000
CMD ["npm", "start"]
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL!,
});
const adapter = new PrismaPg(pool);
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const db =
globalForPrisma.prisma ??
new PrismaClient({
adapter,
});
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = db;
}
Dockerfile (Next.js 앱의 이미지 생성)이 파일은 우리 Next.js 코드를 실행 가능한 이미지로 만드는 과정을 기술합니다.
deps, builder, runner 단계를 나누는 이유는 최종 이미지의 크기를 줄이기 위해서입니다. 실제 실행에 필요 없는 빌드 도구들을 제거하고 가벼운 결과물만 남깁니다.npx prisma generate: 빌드 시점에 Prisma 클라이언트를 생성해야 이미지 안에서도 DB 타입을 인식할 수 있습니다.docker-compose.yml (멀티 컨테이너 관리)앱과 DB처럼 여러 개의 컨테이너가 서로 협력해야 할 때 사용합니다.
depends_on: DB가 먼저 실행된 후에 Next.js 앱이 실행되도록 순서를 보장합니다.localhost가 아닌 서비스 이름(db)으로 통신할 수 있습니다. (예: postgresql://...@db:5432/...)