Ch 01. 프로젝트 설계와 DB 스키마
코드를 한 줄 작성하기 전에 먼저 무엇을 만들지 명확히 해야 합니다. 막연하게 시작하면 중간에 방향을 잃기 쉽습니다.
구현할 기능 목록
이번 블로그 서비스에서 만들 기능은 다음과 같습니다.
- 회원 가입, 로그인, 로그아웃
- 게시글 목록 조회, 상세 보기, 작성, 수정, 삭제
- 댓글 작성, 삭제
- 이미지 업로드 (게시글 대표 이미지)
권한 규칙도 미리 정합니다. 게시글 수정과 삭제는 작성자 본인만 할 수 있고, 댓글 삭제도 마찬가지입니다. 이 규칙은 서버 API와 프론트엔드 양쪽에서 모두 검증합니다.
프로젝트 디렉토리 구조
blog-app/
├── prisma/
│ ├── schema.prisma
│ └── migrations/
├── server/
│ ├── api/
│ │ ├── auth/
│ │ │ ├── login.post.ts
│ │ │ ├── logout.post.ts
│ │ │ └── register.post.ts
│ │ ├── posts/
│ │ │ ├── index.get.ts
│ │ │ ├── index.post.ts
│ │ │ ├── [id].get.ts
│ │ │ ├── [id].put.ts
│ │ │ ├── [id].delete.ts
│ │ │ └── [id]/
│ │ │ ├── comments.get.ts
│ │ │ └── comments.post.ts
│ │ ├── comments/
│ │ │ └── [id].delete.ts
│ │ └── upload.post.ts
│ ├── middleware/
│ │ └── auth.ts
│ └── utils/
│ └── prisma.ts
├── stores/
│ └── auth.ts
├── pages/
│ ├── index.vue
│ ├── login.vue
│ ├── register.vue
│ └── posts/
│ ├── write.vue
│ ├── [id].vue
│ └── [id]/
│ └── edit.vue
├── middleware/
│ └── auth.ts
└── public/
└── uploads/
Prisma 스키마
데이터 모델은 세 가지입니다. User, Post, Comment이며, 관계는 User가 Post와 Comment를 여러 개 가질 수 있고, Post가 Comment를 여러 개 가질 수 있는 구조입니다. Post가 삭제되면 댓글도 함께 삭제되도록 onDelete: Cascade를 설정합니다.
// prisma/schema.prisma
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
email String @unique
password String
name String
posts Post[]
comments Comment[]
createdAt DateTime @default(now())
}
model Post {
id Int @id @default(autoincrement())
title String
content String
imageUrl String?
author User @relation(fields: [authorId], references: [id])
authorId Int
comments Comment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Comment {
id Int @id @default(autoincrement())
content String
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
postId Int
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
}
스키마를 작성했으면 마이그레이션을 실행합니다.
npx prisma migrate dev --name init
이 명령은 prisma/migrations/ 폴더에 SQL 파일을 생성하고 데이터베이스에 테이블을 만듭니다. .env 파일에 DATABASE_URL="file:./dev.db"가 설정되어 있어야 합니다.
PART 05 Ch 04에서 만든 server/utils/prisma.ts가 그대로 사용됩니다. usePrisma()를 호출하면 싱글톤 PrismaClient 인스턴스가 반환되고, Nitro 자동 임포트 덕분에 API 파일에서 import 없이 바로 쓸 수 있습니다. 새 스키마로 마이그레이션하면 Prisma가 User, Post, Comment 모델을 새로 인식하므로 유틸리티 파일을 수정할 필요는 없습니다.
서버 미들웨어 수정
PART 05 Ch 05에서 만든 서버 미들웨어는 /api/protected/ 경로만 인증 검사를 했습니다. 블로그 프로젝트는 /api/posts, /api/comments 등 직접 경로를 사용하므로, 미들웨어 전략을 변경합니다. 모든 요청에서 쿠키를 파싱해 userId를 설정하고, 403 처리는 각 핸들러가 직접 담당합니다.
// server/middleware/auth.tsimport jwt from 'jsonwebtoken'export default defineEventHandler((event) => { const token = getCookie(event, 'auth_token') if (!token) return try { const payload = jwt.verify(token, process.env.JWT_SECRET!) as { userId: number } event.context.userId = payload.userId } catch { // 만료되거나 유효하지 않은 토큰 — userId를 설정하지 않고 통과 }})
이렇게 하면 로그인한 사용자는 모든 API에서 event.context.userId를 통해 식별되고, 각 핸들러에서 if (!userId) throw createError({ statusCode: 401 })로 인증 여부를 결정합니다.