Skip to content

Latest commit

 

History

History
285 lines (223 loc) · 8.83 KB

File metadata and controls

285 lines (223 loc) · 8.83 KB

TODO API Starter Project

이 프로젝트는 PoolC 백엔드 커리큘럼의 실습 프로젝트입니다. 커리큘럼을 따라가며 TODO 표시된 부분을 구현하세요.

프로젝트 구조

backend/
├── src/
│   ├── config/         # 환경변수 설정
│   ├── controllers/    # 요청 처리 (제공됨)
│   ├── lib/            # Prisma 클라이언트
│   ├── middlewares/    # 미들웨어 (일부 구현 필요)
│   ├── routes/         # 라우터 정의 (제공됨)
│   ├── schemas/        # Zod 검증 스키마 (개선 필요)
│   ├── services/       # 비즈니스 로직 (구현 필요!)
│   ├── types/          # TypeScript 타입 정의
│   ├── app.ts          # Express 앱 설정
│   └── index.ts        # 서버 진입점
├── prisma/
│   └── schema.prisma   # DB 스키마 (관계 추가 필요)
├── tests/              # 테스트 파일 (참고용)
└── ...

구현해야 할 TODO 목록

1. Prisma 스키마 (Chapter 3)

파일: prisma/schema.prisma

  • User 모델에 todos Todo[] 관계 추가
  • Todo 모델에 userId Int 필드와 user User @relation(...) 추가

완료 후 마이그레이션:

npx prisma migrate dev --name add-user-todo-relation

2. 인증 서비스 (Chapter 4)

파일: src/services/auth.service.ts

  • register() 함수 구현
    • 이메일 중복 확인
    • bcrypt로 비밀번호 해싱
    • 사용자 생성
    • JWT 토큰 생성
  • login() 함수 구현
    • 이메일로 사용자 조회
    • bcrypt로 비밀번호 검증
    • JWT 토큰 생성

3. Todo 서비스 (Chapter 3)

파일: src/services/todo.service.ts

  • getTodos() - 페이지네이션 포함 조회
  • getTodoById() - 단일 항목 조회
  • createTodo() - 생성
  • updateTodo() - 수정
  • deleteTodo() - 삭제
  • getStats() - 통계 (groupBy 사용)

4. Todo 컨트롤러 (Chapter 2)

파일: src/controllers/todo.controller.ts

  • createTodo() - 201 응답 반환
  • updateTodo() - req.params.id 파싱
  • getTodos가 참고용으로 제공됩니다

5. 검증 미들웨어 (Chapter 2)

파일: src/middlewares/validate.middleware.ts

  • validate() 미들웨어 팩토리 구현
    • Zod 스키마로 요청 데이터 검증
    • 검증 실패 시 400 응답과 에러 상세 반환
    • 파일 내 pseudo-code 참고

6. Optional Auth 미들웨어 (Chapter 4)

파일: src/middlewares/auth.middleware.ts

  • optionalAuth 미들웨어 구현
    • 토큰이 없으면 에러 없이 통과
    • 토큰이 있으면 검증 후 req.user 설정

7. Zod 스키마 개선

파일: src/schemas/auth.schema.ts, src/schemas/todo.schema.ts

  • 이메일 정규화 (.toLowerCase()) - 권장: 로그인 시 대소문자 불일치 방지
  • 입력값 정리 (.trim()) - 권장: 공백으로 인한 오류 방지
  • 커스텀 에러 메시지 추가 (선택)

8. 테스트 작성 (Chapter 5)

파일: tests/todo.test.ts

it.todo()로 표시된 테스트를 구현하세요. 참고용 테스트가 제공됩니다.

  • should return a single todo - GET /api/todos/:id
  • should return 404 for non-existent todo
  • should update a todo - PATCH /api/todos/:id
  • should delete a todo - DELETE /api/todos/:id
  • should return todo statistics - GET /api/todos/stats
  • should return 401 without authentication

시작하기

1. 의존성 설치

npm install

2. 환경변수 설정

cp .env.example .env
# .env 파일을 열어 DATABASE_URL과 JWT_SECRET 설정

3. 데이터베이스 설정 (Docker)

docker compose -f docker-compose.dev.yml up -d

4. Prisma 마이그레이션

npx prisma migrate dev
npx prisma generate

5. 개발 서버 실행

npm run dev

6. 테스트 실행

테스트는 개발 DB와 격리된 별도의 PostgreSQL 컨테이너에서 실행됩니다.

# 방법 1: 한 번에 실행 (권장)
# DB 시작 → 마이그레이션 → 테스트 → DB 종료
npm run test:ci

# 방법 2: 개발 중 반복 테스트
npm run test:db:up       # 테스트 DB 시작 (포트 5433)
npm run test:db:migrate  # 마이그레이션 적용
npm run test:watch       # watch 모드로 테스트 반복
npm run test:db:down     # 작업 완료 후 DB 종료

💡 테스트 DB는 포트 5433을 사용하므로 개발 DB(5432)와 충돌하지 않습니다.

API 엔드포인트

인증 (Auth)

Method Endpoint Description Auth
POST /api/auth/register 회원가입 -
POST /api/auth/login 로그인 -
GET /api/auth/me 프로필 조회 Required

TODO

Method Endpoint Description Auth
GET /api/todos 목록 조회 Optional
GET /api/todos/stats 통계 조회 Required
GET /api/todos/:id 단일 조회 Required
POST /api/todos 생성 Required
PATCH /api/todos/:id 수정 Required
DELETE /api/todos/:id 삭제 Required

스크립트

명령어 설명
npm run dev 개발 서버 실행 (hot reload)
npm run build TypeScript 빌드
npm run start 프로덕션 서버 실행
npm run lint ESLint 검사
npm run test 테스트 실행 (DB가 실행 중이어야 함)
npm run test:watch watch 모드로 테스트 실행
npm run test:coverage 테스트 커버리지 확인
npm run test:ci 전체 테스트 자동화 (DB 시작→테스트→종료)
npm run test:db:up 테스트 DB 컨테이너 시작
npm run test:db:down 테스트 DB 컨테이너 종료
npm run test:db:migrate 테스트 DB 마이그레이션
npm run db:migrate 개발 DB 마이그레이션
npm run db:studio Prisma Studio 실행

힌트

밑줄(_) 접두사 매개변수

TODO 함수의 매개변수에 _userId, _data 등 밑줄 접두사가 붙어 있습니다:

export const getTodos = async (_userId: number, _params: TodoQueryParams) => {

이는 TypeScript의 noUnusedParameters 설정 때문입니다. 사용하지 않는 매개변수 앞에 _를 붙이면 컴파일 에러를 방지합니다. 구현할 때는 _를 제거하고 사용하세요:

export const getTodos = async (userId: number, params: TodoQueryParams) => {
  // userId와 params를 실제로 사용

Prisma CRUD 패턴

// 목록 조회 (페이지네이션 + 관계 포함)
const todos = await prisma.todo.findMany({
  where: { userId },
  include: { user: { select: { id: true, name: true } } },
  skip: (page - 1) * limit,
  take: limit,
  orderBy: { createdAt: "desc" },
});

// 단일 조회 (관계 포함)
const todo = await prisma.todo.findFirst({
  where: { id, userId },
  include: { user: { select: { id: true, name: true } } },
});

// 생성
const todo = await prisma.todo.create({
  data: { ...data, userId },
});

// 수정
const todo = await prisma.todo.update({
  where: { id },
  data,
});

// 삭제
await prisma.todo.delete({ where: { id } });

// 통계 (groupBy)
const groups = await prisma.todo.groupBy({
  by: ["status"],
  where: { userId },
  _count: { status: true },
});

bcrypt 사용법

// 해싱 (auth.service.ts에 SALT_ROUNDS = 10 제공됨)
const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);

// 검증
const isValid = await bcrypt.compare(password, user.hashedPassword);

JWT 사용법

// 토큰 생성 (expiresIn은 env.JWT_EXPIRES_IN 사용)
const token = jwt.sign({ userId, email }, JWT_SECRET, { expiresIn: "7d" });

// 토큰 검증
const decoded = jwt.verify(token, JWT_SECRET) as JwtPayload;

💡 JWT 만료 시간: 개발 편의를 위해 기본값은 7d입니다. 프로덕션에서는 JWT_EXPIRES_IN=1h로 설정하고 Refresh Token을 구현하는 것이 보안상 권장됩니다.

프론트엔드 연동

이 프로젝트에는 React로 구현된 프론트엔드가 함께 제공됩니다. 백엔드 구현을 완료한 후 프론트엔드와 연동하여 실제 동작을 확인할 수 있습니다.

# 프론트엔드 실행 (별도 터미널)
cd ../frontend
npm install
npm run dev
# http://localhost:5173 에서 실행됩니다

💡 백엔드(localhost:3000)와 프론트엔드(localhost:5173)를 동시에 실행해야 합니다. CORS 설정이 이미 포함되어 있습니다.

자세한 내용은 프론트엔드 README를 참고하세요.

참고 자료