이 프로젝트는 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/ # 테스트 파일 (참고용)
└── ...
파일: prisma/schema.prisma
- User 모델에
todos Todo[]관계 추가 - Todo 모델에
userId Int필드와user User @relation(...)추가
완료 후 마이그레이션:
npx prisma migrate dev --name add-user-todo-relation파일: src/services/auth.service.ts
-
register()함수 구현- 이메일 중복 확인
- bcrypt로 비밀번호 해싱
- 사용자 생성
- JWT 토큰 생성
-
login()함수 구현- 이메일로 사용자 조회
- bcrypt로 비밀번호 검증
- JWT 토큰 생성
파일: src/services/todo.service.ts
-
getTodos()- 페이지네이션 포함 조회 -
getTodoById()- 단일 항목 조회 -
createTodo()- 생성 -
updateTodo()- 수정 -
deleteTodo()- 삭제 -
getStats()- 통계 (groupBy 사용)
파일: src/controllers/todo.controller.ts
-
createTodo()- 201 응답 반환 -
updateTodo()- req.params.id 파싱 getTodos가 참고용으로 제공됩니다
파일: src/middlewares/validate.middleware.ts
-
validate()미들웨어 팩토리 구현- Zod 스키마로 요청 데이터 검증
- 검증 실패 시 400 응답과 에러 상세 반환
- 파일 내 pseudo-code 참고
파일: src/middlewares/auth.middleware.ts
-
optionalAuth미들웨어 구현- 토큰이 없으면 에러 없이 통과
- 토큰이 있으면 검증 후
req.user설정
파일: src/schemas/auth.schema.ts, src/schemas/todo.schema.ts
- 이메일 정규화 (
.toLowerCase()) - 권장: 로그인 시 대소문자 불일치 방지 - 입력값 정리 (
.trim()) - 권장: 공백으로 인한 오류 방지 - 커스텀 에러 메시지 추가 (선택)
파일: 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
npm installcp .env.example .env
# .env 파일을 열어 DATABASE_URL과 JWT_SECRET 설정docker compose -f docker-compose.dev.yml up -dnpx prisma migrate dev
npx prisma generatenpm run dev테스트는 개발 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)와 충돌하지 않습니다.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/auth/register | 회원가입 | - |
| POST | /api/auth/login | 로그인 | - |
| GET | /api/auth/me | 프로필 조회 | Required |
| 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를 실제로 사용// 목록 조회 (페이지네이션 + 관계 포함)
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 },
});// 해싱 (auth.service.ts에 SALT_ROUNDS = 10 제공됨)
const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
// 검증
const isValid = await bcrypt.compare(password, user.hashedPassword);// 토큰 생성 (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를 참고하세요.