Nest.js를 배워보자/15. NestJS & Prisma 완벽 가이드

🔍 3부. CRUD 기본 및 복합 쿼리

_Blue_Sky_ 2025. 12. 3. 19:41
728x90

이제 PrismaService를 NestJS에 성공적으로 통합했으므로, 실제 데이터베이스 작업을 수행하는 CRUD(Create, Read, Update, Delete) 기본 기능과 복합 쿼리 사용법을 알아보겠습니다. 모든 예시는 PrismaService 내에서 상속받은 this.prisma.<modelName>을 통해 접근하는 메서드라고 가정합니다.


3.1. 데이터 생성 (Create)

🌟 create 및 DTO를 이용한 안전한 생성

단일 레코드를 생성할 때 사용합니다. NestJS에서는 요청 본문(request body)의 유효성을 검사하고 데이터 구조를 정의하는 DTO (Data Transfer Object)를 사용하여 안전하게 데이터를 받습니다.

사용 예시 (User 생성):

// DTO (예시)
class CreateUserDto {
  email: string;
  name?: string;
}

// 서비스 코드
async createUser(data: CreateUserDto) {
  // Prisma는 받은 DTO를 data 속성을 통해 직접 사용합니다.
  return this.prisma.user.create({
    data: {
      email: data.email,
      name: data.name,
      // 필요한 경우 여기에 추가 필드 설정
    },
  });
}

🎈 createMany를 이용한 다중 생성

단일 쿼리로 여러 레코드를 생성할 때 사용합니다. 이는 단일 create를 여러 번 호출하는 것보다 훨씬 효율적입니다.

사용 예시:

async createMultipleUsers(data: CreateUserDto[]) {
  return this.prisma.user.createMany({
    data: data, // DTO 배열
    skipDuplicates: true, // 선택 사항: 중복된 레코드는 건너뛸지 여부
  });
  // 반환 값: { count: number } - 실제로 생성된 레코드 수
}

3.2. 데이터 조회 (Read)

🔑 단일 조회 (findUnique)

고유 필드(예: @id 또는 @unique가 설정된 필드)를 기준으로 단 하나의 레코드를 조회합니다.

// ID를 기준으로 조회
async findUserById(id: number) {
  return this.prisma.user.findUnique({
    where: {
      id: id,
    },
    // select: 특정 필드만 선택
    // include: 관계된 모델(Post 등)을 함께 가져옴 (N+1 문제 방지)
  });
}

📚 목록 조회 (findMany) 및 조건 필터링

여러 레코드를 조회할 때 사용하며, 강력한 where 조건을 통해 필터링합니다.\

async findPublishedPosts(authorEmail: string) {
  return this.prisma.post.findMany({
    where: {
      published: true, // Boolean 필터링
      title: {
        contains: 'Prisma', // 제목에 'Prisma'가 포함된 경우
      },
      // 관계된 모델을 통한 필터링
      author: {
        is: {
          email: authorEmail, // 작성자의 이메일이 일치하는 경우
        },
      },
    },
    // 복합 쿼리 옵션 (3.4절 참고): orderBy, take, skip
  });
}

3.3. 데이터 수정 및 삭제 (Update & Delete)

✏️ 단일 수정 (update)

고유 필드를 기준으로 단 하나의 레코드를 찾아서 수정합니다.

async updateUserName(id: number, newName: string) {
  return this.prisma.user.update({
    where: {
      id: id,
    },
    data: {
      name: newName,
    },
  });
}

✏️ 조건부 다중 수정 (updateMany)

where 조건에 맞는 모든 레코드를 한 번에 수정합니다.\

async unpublishAllPosts() {
  return this.prisma.post.updateMany({
    where: {
      published: true, // 발행된 모든 게시물
    },
    data: {
      published: false, // 미발행으로 변경
    },
  });
  // 반환 값: { count: number } - 수정된 레코드 수
}

🗑️ 단일 삭제 (delete) 및 다중 삭제 (deleteMany)

  • delete: 고유 필드를 기준으로 하나의 레코드를 삭제합니다.
  • deleteMany: where 조건에 맞는 모든 레코드를 한 번에 삭제합니다.
// 단일 삭제
async deleteUser(id: number) {
  return this.prisma.user.delete({
    where: {
      id: id,
    },
  });
}

// 다중 삭제
async deleteDraftPosts() {
  return this.prisma.post.deleteMany({
    where: {
      published: false,
    },
  });
  // 반환 값: { count: number } - 삭제된 레코드 수
}

3.4. 복합 쿼리: 페이징, 정렬, 검색

Prisma의 findMany 메서드는 데이터 처리의 효율성을 높이는 복합 쿼리 옵션을 제공합니다.

📜 페이징 (Paging): take와 skip

대규모 데이터셋에서 특정 범위의 데이터만 가져올 때 사용합니다.

  • take: 가져올 레코드의 최대 개수 (Limit)를 지정합니다.
  • skip: 건너뛸 레코드의 개수 (Offset)를 지정합니다.
async getPaginatedPosts(page: number, pageSize: number) {
  const skip = (page - 1) * pageSize;
  
  return this.prisma.post.findMany({
    skip: skip, // 1페이지는 0, 2페이지는 pageSize만큼 건너뜀
    take: pageSize, // 한 페이지당 가져올 개수
  });
}

↕️ 정렬 (Sorting): orderBy

결과를 특정 필드를 기준으로 정렬합니다.

async getLatestUsers() {
  return this.prisma.user.findMany({
    orderBy: {
      id: 'desc', // ID를 기준으로 내림차순 (최신순) 정렬
      // 여러 필드로 정렬: [{ name: 'asc' }, { createdAt: 'desc' }]
    },
  });
}

🔍 검색 (Searching): contains, startsWith, mode

where 절 내에서 문자열 검색을 수행합니다.

필터 설명
contains 해당 문자열을 포함하는 레코드 검색 (SQL의 LIKE '%term%')
startsWith 해당 문자열로 시작하는 레코드 검색 (SQL의 LIKE 'term%')
endsWith 해당 문자열로 끝나는 레코드 검색 (SQL의 LIKE '%term')
mode: 'insensitive' 대소문자 구분 없이 검색 (PostgreSQL 등 지원 DB)
 
async searchPostByTitle(searchTerm: string) {
  return this.prisma.post.findMany({
    where: {
      title: {
        contains: searchTerm,
        mode: 'insensitive', // 대소문자 무시 (PostgreSQL 기준)
      },
    },
  });
}
728x90