Prisma 사용기

Jay·2023년 11월 11일
2

안녕하세요! 열일하는 프론트엔드 개발자 제이입니다.
저는 현재 프롭테크 스타트업에서 부동산 플랫폼을 개발하고 있습니다.

현재 유저 사이트, 중개사 사이트, 어드민 사이트를 관리하고 있는데요, 서비스 피봇으로 어드민 리뉴얼이 필요한 상황에서, 혼자인 백엔드 개발자의 부담을 덜어주기 위해 풀스택으로 어드민 사이트를 개발하는 과제를 맡게 되었습니다.

이번 글에서는 ORM을 프로젝트에 도입하고, Prisma로 개발하며 겪은 고민과 경험을 나누고자 합니다.

참고로 사용한 기술은 다음과 같습니다.

  • next.js 13 app router
  • mysql
  • prisma

Prisma vs TypeORM

prisma와 typeorm은 대표적인 타입스크립트 ORM인데요, 비교를 통해 prisma를 선택한 이유는 다음과 같습니다.

1. 활발한 업데이트

prisma는 업데이트 주기가 짧았습니다. 새로운 기능과 버그 픽스를 활발히 하고 있다는 뜻이기에 프로젝트를 유지보수하고 발전시키는데 도움이 되리라 판단했습니다.
저는 기술을 도입할 때 최근 업데이트 일시를 중요하게 생각하는 편인데요, 아무래도 적극적으로 관리되지 않고 있다는 생각이 들어 사용을 주저하게 되는 것 같습니다.

2. 공식문서의 가독성

prisma의 공식문서는 빠르게 기술을 이해하고 사용할 수 있도록 잘 작성되어 있어, 실무 레벨에서는 백엔드의 영역에 처음으로 도전하는 저에게 적합하다고 생각했습니다. 또한 비록 typeorm보다 늦게 출시되었지만 커뮤니티에서 활발한 토론이 이뤄지고 있고 학습 자료도 풍부했습니다.

3. 타입 안전성

typeorm은 'anyORM'이라는 별명이 있습니다😂
이 부분이 typescript를 적극 활용하고 있는 제 입장에서는 달갑지 않았고, 반대로 prisma는 type-safety를 제공하면서도 큰 이슈가 없어보였습니다.

라고 생각했지만 개발하며 timezone issue를 맞닥트리게됩니다. 이부분은 다음 섹션에 서술하겠습니다.

Prisma Client API vs Raw Query

// prisma client API
const users = await prisma.user.findMany()

// raw query
const result = await prisma.$queryRaw`SELECT * FROM User`

prisma는 다양한 방식으로 쿼리 코드를 작성할 수 있는데요, 저는 orm api의 장점을 포기하고 raw query를 사용했습니다. 이유는 다음과 같습니다.

1. 백엔드 개발자와의 원활한 소통

제가 어드민을 풀스택으로 개발하게 된 배경은 혼자인 백엔드 개발자의 공수를 덜기 위해서였고, 백엔드는 go언어를 사용하고 있습니다.

제너럴한 sql문법을 사용한다면 go언어를 사용하시는 백엔드 개발자 분에게 코드 리뷰를 비롯한 여러 도움을 받을 수 있으리라 기대했습니다.

2. 기존 쿼리 코드 이식성

비슷한 맥락으로, 어드민을 리뉴얼하지만 기존의 쿼리를 재사용하거나 참고할 수 있는 부분이 있었습니다.

3. 기술 종속성 완화

prisma는 저희 개발팀의 공통 스택이 아니기 때문에, 다른 개발자가 이 프로젝트를 유지보수하면서 prisma의 문법을 익히지 않아도 되는 장점이 있었습니다.

어드민 웹사이트를 1인 개발하면서 가장 고민했던 부분 중 하나였습니다. 여러 리스크를 문제로 이 프로젝트가 저에게 종속성이 생기지 않기를 바라면서 기술 선택이나 프로젝트 구조에 대해서도 신중하게 고민하고 개발했습니다.


프론트엔드에 특화된 저는 쿼리문을 잘 작성하는 편은 아니기에 너무 복잡한 쿼리문을 작성해야 될 경우에는 제한을 두지 않고 prisma client api를 활용할 생각이었지만, 훌륭하신 백엔드 개발자분의 도움으로 raw query로 문제없이 구현할 수 있었습니다.

이슈

1. timezone

prisma에서 timezone을 지원하지 않는 이슈가 있어 UTC-KST를 조정하는 함수를 사용하여 timzone을 조정했습니다.

import dayjs from 'dayjs';

export default function dbDayjs(time?: string | Date) {
  if (time) {
    return dayjs(time).subtract(9, 'hour');
  }
  return dayjs().add(9, 'hour');
}

자세한 정보는 아래 링크의 이슈에서 확인할 수 있습니다.
https://github.com/prisma/prisma/issues/5051

2. BigInt

클라이언트에 response값을 전달하기 위해 Json.stringify로 객체를 직렬화하려 했을 때, 서버에서 오류가 발생하였습니다. 서버 로그에 다음 내용을 확인할 수 있었습니다.

Do not know how to serialize a BigInt
사실 이건 prisma의 문제는 아니고, js의문제입니다. BigInt값은 JSON으로 직렬화할 수 없습니다.

prisma 공식문서에서는 다음과 같은 해결책을 제시하고 있습니다.

처음에는 prisma에서 제안하는 방식대로 공용 함수를 만들고, 이를 모든 컨트롤러에 적용하는 작업을 수행했습니다. 그러나 이 방식이 상당히 비효율적으로 느껴저서 조금 더 리서치를 해보니 mdn에서는 몽키패치를 제안하고 있었습니다.

런타임에서 가장 먼저 실행되는 파일에 해당 코드를 추가하여 이슈를 해결했습니다. 하지만 몽키패치는 코드 예측성을 떨어뜨리고 디버깅을 어렵게 만드는 단점이 있습니다. 그래서 저는 먼 미래의 개발자나 저 자신에게 코드의 목적과 이유를 명확히 전달하기 위해 tsdoc과 readme에 몽키패치를 적용한 이유를 추가로 기술했습니다.

마치며

풀스택 개발은 정말 다양한 측면에서 신경 써야 할 부분이 많고 어려운 일인 것 같습니다. 그럼에도 불구하고 이 프로젝트를 통해서 프론트엔드 개발자로서의 전문성을 키우는 한편, 웹 개발자로서 백엔드 지식을 습득할 수 있는 소중한 기회가 되었습니다.

다른 ORM을 사용해보지 않아 비교할 순 없지만, prisma가 매우 만족스러워 이후에 주어진 서브 API를 만드는 과제에서도 Nest.js와 연동하여 사용하였습니다.

백엔드의 영역에 도전하는 frontend 개발자 혹은 typescript orm을 찾는 분들에게 강력하게 추천드리며 글 마치겠습니다. 감사합니다!

[참고]
https://www.prisma.io/
https://github.com/prisma/prisma/issues/5051
https://npmtrends.com/prisma-vs-typeorm
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json

0개의 댓글