온라인 강의 플랫폼의 강의(Lecture) CRUD API를 NestJS + Prisma로 구현했다.
전체 흐름은 Controller → Service → Prisma(DB) 3계층 구조다.
POST /lectures/sections/:sectionId/lectures → 강의 생성
GET /lectures/:lectureId → 강의 상세 조회
PATCH /lectures/:lectureId → 강의 수정
DELETE /lectures/:lectureId → 강의 삭제
강의를 생성할 때는 어느 섹션에 넣을지 알아야 하므로 (섹션 하나에 여러 강의가 들어갈 수 있음) URL에 sectionId를 포함시켰다.
반면 조회/수정/삭제는 lectureId만으로 대상을 특정할 수 있으므로 단순하게 /:lectureId로 설계했다.
async create(sectionId: string, createLectureDto: createLectureDto, userId: string): Promise<Lecture> {
const section = await this.prisma.section.findUnique({
where: {
id: sectionId,
},
include: {
course: true,
},
});
if (!section) {
throw new NotFoundException('Section not found');
}
if (section.course.instructorId !== userId) {
throw new ForbiddenException('You are not the instructor of this section');
}
const lastLecture = await this.prisma.lecture.findFirst({
where: {
sectionId: sectionId,
},
orderBy: {
order: 'desc',
},
});
const order = lastLecture ? lastLecture.order + 1 : 0;
return this.prisma.lecture.create({
data: {
...createLectureDto,
order,
section: {
connect: {
id: sectionId,
},
},
course: {
connect: {
id: section.courseId,
},
},
},
});
}
처리 순서:
sectionId로 섹션 존재 여부 확인 → 없으면 NotFoundExceptionsection.course.instructorId === userId 검사 → 다르면 ForbiddenExceptionorder 조회 후 +1order 자동 증가 로직:
const lastLecture = await this.prisma.lecture.findFirst({
where: { sectionId },
orderBy: { order: 'desc' },
});
const order = lastLecture ? lastLecture.order + 1 : 0;
섹션 내 강의 순서를 DB 레벨에서 직접 관리한다.
첫 번째 강의면 order = 0, 이후엔 마지막 강의의 order + 1을 자동으로 부여한다.
// 1. 존재 여부 확인
const lecture = await this.prisma.lecture.findUnique({
where: { id: lectureId },
include: {
course: { select: { instructorId: true } },
},
});
if (!lecture) throw new NotFoundException('...');
// 2. 권한 확인
if (lecture.course.instructorId !== userId)
throw new ForbiddenException('...');
// 3. 실제 작업 수행
course를 include할 때 select: { instructorId: true }로 필요한 필드만 가져온다.
course 전체를 끌어오지 않으므로 조금이라도 최적화가 된다..
| 상황 | 예외 | HTTP |
|---|---|---|
| lectureId가 UUID 형식이 아님 | BadRequestException (Pipe 자동 처리) | 400 |
| 섹션 / 강의가 존재하지 않음 | NotFoundException | 404 |
| 본인 강의가 아님 | ForbiddenException | 403 |