본문 바로가기

개발 공부

Prisma CRUD 예시

 

 

CRUD는 단순하지만 DB 성능과 데이터 정합성을 결정짓는 핵심 로직입니다.

조회(Read) select로 최소 데이터만, include는 관계 필요 시만 사용
생성(Create) Zod 검증 + 트랜잭션 + 중복 방지 (@@unique)
수정(Update) 권한 검증 + updateMany 통한 조건 기반 안전 수정
삭제(Delete) onDelete: Cascade 또는 Soft Delete 전략 병행

 

 

데이터 조회 (Read)

예시 

export async function getUserByClerkId(clerkId: string) {
  return prisma.user.findUnique({
    where: { clerkId },
    include: {
      _count: { select: { followers: true, following: true, posts: true } },
    },
  });
}

 

  • select vs include 구분
    • select: 필요한 필드만 반환 → 응답 크기 최소화.
    • include: 관계 데이터(posts, followers 등)가 꼭 필요할 때만 사용.
  • _count는 관계형 데이터의 수만 빠르게 가져오는 Prisma 내부 최적화 쿼리입니다.
  • 대용량 relation은 include를 남발하면 직렬화 비용이 급격히 커집니다. 

 

_count

Prisma에서 리스트 전체를 join하지 않고, 관계의 “개수만” 빠르게 조회할 수 있는 내장 서브쿼리 기능입니다.

export async function getUserByClerkId(clerkId: string) {
  return prisma.user.findUnique({
    where: { clerkId },
    include: {
      _count: {
        select: {
          followers: true,
          following: true,
          posts: true,
        },
      },
    },
  });
}
  • 이렇게 하면 Prisma가 실제로는 아래와 유사한 SQL 서브쿼리를 날립니다
SELECT
  "User"."id",
  "User"."name",
  (
    SELECT COUNT(*) FROM "Follow" WHERE "Follow"."followingId" = "User"."id"
  ) AS "followers",
  (
    SELECT COUNT(*) FROM "Follow" WHERE "Follow"."followerId" = "User"."id"
  ) AS "following",
  (
    SELECT COUNT(*) FROM "Post" WHERE "Post"."authorId" = "User"."id"
  ) AS "posts"
FROM "User"
WHERE "User"."clerkId" = 'user_123';
  • join 없이 서브쿼리로 count만 따로 계산하기 때문에 followers 전체를 join해서 가져오는 것보다 훨씬 빠릅니다.

 

결과 구조

{
  "id": "user_123",
  "name": "Appbae Princess",
  "_count": {
    "followers": 15,
    "following": 6,
    "posts": 42
  }
}

 

 


 

 

 

데이터 생성 (Create)

예시

export async function syncUser() {
  try {
    const { userId } = await auth();
    const user = await currentUser();
    if (!userId || !user) return;

    const existingUser = await prisma.user.findUnique({ where: { clerkId: userId } });
    if (existingUser) return existingUser;

    const dbUser = await prisma.user.create({
      data: {
        clerkId: userId,
        name: `${user.firstName || ''} ${user.lastName || ''}`,
        username: user.username ?? user.emailAddresses[0].emailAddress.split('@')[0],
        email: user.emailAddresses[0].emailAddress,
        image: user.imageUrl,
      },
    });
    return dbUser;
  } catch (error) {
    console.log('Error in syncUser', error);
  }
}

 

  • Clerk 연동 시 syncUser는 로그인 직후 1회만 실행되면 충분합니다.
  • 이미 존재하는 사용자는 findUnique로 중복 방지.
  • Prisma의 create는 Promise 기반이므로 try/catch로 감싸 로그를 남겨야 디버깅이 용이합니다.
  • 생성 시 Zod 검증을 활용하면 타입 안전성을 유지할 수 있습니다.
const CreatePostSchema = z.object({
  authorId: z.string(),
  title: z.string().min(1),
  body: z.string().optional(),
});

export async function createPost(input: z.infer<typeof CreatePostSchema>) {
  const data = CreatePostSchema.parse(input);
  return prisma.post.create({ data });
}

 

 


데이터 수정 (Update)

예시

export async function updatePost(input: { postId: string; requesterId: string; title?: string; body?: string }) {
  const { postId, requesterId, ...patch } = input;

  const post = await prisma.post.findUnique({ where: { id: postId }, select: { authorId: true } });
  if (!post) throw new Error('Post not found');
  if (post.authorId !== requesterId) throw new Error('Forbidden');

  return prisma.post.update({ where: { id: postId }, data: patch });
}

 

  • 수정 전 반드시 권한 검증: post.authorId와 요청자 ID 일치 확인.
  • 다중 조건 업데이트는 updateMany로 안전하게 수행 가능.
  • updatedAt 컬럼을 사용하면 optimistic update도 구현 가능.

 


 

데이터 삭제 (Delete)

예시

export async function deletePost(postId: string, requesterId: string) {
  const post = await prisma.post.findUnique({ where: { id: postId }, select: { authorId: true } });
  if (!post) throw new Error('Post not found');
  if (post.authorId !== requesterId) throw new Error('Forbidden');

  await prisma.post.delete({ where: { id: postId } });
}

 

  • 삭제 전 권한 검증은 필수.
  • Prisma 스키마에서 onDelete: Cascade 설정 시 관계 데이터 자동 삭제.

 

 


 

 

정리

기능 주요 메서드 핵심 개념 실무 주의점
조회 findUnique, findMany select / include, _count 큰 relation은 지양, 커서 페이지네이션 필수
생성 create, $transaction Zod 검증, 유저 동기화 중복 방지, 에러 로깅
수정 update, updateMany 권한 검증, 낙관적 락 조건 불일치 시 0건 처리 주의
삭제 delete, update Cascade / Soft Delete 실데이터 삭제 여부 명확히 설계

 

  

'개발 공부' 카테고리의 다른 글

shadcn/ui (2) example) Button · Card  (0) 2025.11.17
shadcn/ui (1)  (0) 2025.11.16
Prisma DB 연결  (0) 2025.11.09
Prisma 관계(Relation)  (0) 2025.11.02
Prisma 스키마 (2) - 인덱스, 유니크, 복합키  (0) 2025.11.01