[Develog] NestJS와 TypeORM (Entity 클래스 쉽게 만드는 법)
2024.03.11- -
1. Overview
새로운 프로젝트를 기획했고 이를 이제 개발을 시작하려고 한다. 프론트엔드와 백엔드를 모두 다루어야 하는데 나는 주도메인(primary-domain)이 백엔드이기 때문에 백엔드를 개발하는 것이 비교적 쉬워 백엔드를 먼저 구축한 후에 프론트엔드에서 UI와 API 연결을 마무리 하는 식으로 진행을 할 것이다.
2. 진행 및 완료사항
2-1. 기술 스택 선정
기술 스택을 선정함에 있어서는 신기술을 이용하고자 하는 방향으로 잡았고 이전 프로젝트를 Express.js와 react native를 사용한 경험이 있어 여기서 업그레이드 시켜 Express.js는 NestJS로, React는 NextJS로 진행해보기로 하였고 이에 대한 공부를 진행하였고 현재도 꾸준히 진행 중이다.
NextJS를 공부할 때 공식문서의 Learn 챕터가 굉장히 도움이 많이 되었는데 만약 NextJS를 시작한다면 해당 페이지를 참고하는 것을 추천한다.
2-2. TypeORM 알아보기
프로젝트 기획을 진행해오면서 ERD 설계를 마쳤고 이를 기반으로 MySQL 데이터베이스를 구축한 다음, 이를 NestJS에서 연결하는 과정에서 TypeORM이라는 기술을 접하게 되었고 이에 대한 조사를 진행하였다.
먼저 ORM이란 Object Relational Mapping의 약어로 '객체로 연결을 해준다'라는 의미를 가진 기술이다. 한 마디로 애플리케이션과 데이터베이스를 연결할 때 우리가 흔히 아는 'SELECT * FROM USER'와 같은 SQL 언어(DML, DDL 등)를 사용하는 것이 아닌애플리케이션 개발 언어로 데이터베이스를 접근할 수 있게 해주는 툴이다.
그니까 Spring의 ORM(JPA)은 DB연결을 Java 문법으로, NodeJS의 ORM(TypeORM, Prisma...)은 JavaScript(or TypeScript)언어로 하도록 툴을 제공해주는 것이다.
위 말을 보면 알겠지만 관계형 DB(RDB) 조작하는 것도 SQL말고 자바스크립트로 해결하려고 하는 사람들이 많았다.
그래서 NestJS도 NodeJS 기반이기 때문에 NodeJS 기반의 다양한 ORM 툴을 사용할 수 있는데 가장 보기에 사용하기 편할 것 같은 TypeORM을 사용하여 이번 프로젝트에 적용하기로 결정하였다.
그러나 npm trend를 살펴보면 실제로는 typeorm보다는 prisma나 sequelize를 사용하고 있는 경향이 보이긴 하지만 TypeORM도 Prisma 못지 않게 증가 추세를 보이고 있으며 프레임워크 관리를 잘 하고 있기도 하고, 실제로 사용해봤을 때 문법적인 측면에서 더 내 취향에 맞는 느낌이 들었다. 하지만 Prisma에서는 TypeORM이 제공하지 않는 타입 체킹이 있어 이를 선호한다면 Prisma를 사용하는 것도 좋은 선택일 것 같다.
자 그럼 이제 TypeORM을 사용하여 DB 관리를 하게 될텐데 우리의 Nest 프로젝트에서 DB와 연결을 해주기 위해선 관련된 각종 패키지를 설치하고 제일 중요한 Entity 클래스를 프로젝트 내에 생성해주어야 한다. 실제로 TypeORM 관련 설정을 할 때 이 Entity를 불러와 TypeORM에 등록을 해주어야지 변경사항이 우리의 DB 테이블에 자동으로 반영이 되게 된다.
3. 개발 중 발생한 이슈 및 해결방법
3-1. TypeORM DB 동기화 방식
이전에 내가 진행했던 프로젝트에서는 ORM을 사용하지 않았기 때문에 먼저 ERDCloud나 AQueryTool과 같은 ERD 스키마 시각화 툴을 사용하여 Entity와 Relation에 대한 테이블을 도식화한 다음 해당 사이트들에서 제공하는 DDL 변환 기능을 통해 나온 쿼리문을 MySQL 워크벤치(DataGrip)로 들어가 콘솔에 입력해주어 스키마와 테이블을 생성하는 방식으로 진행을 했었다.
처음에는 TypeORM을 사용해본적이 없던 터라 이와 같은 과정 속에서 Entity 클래스가 존재해야 하는 이유가 잘 납득이 가지 않았었다. 추측컨데 Entity 클래스의 용도를 단순히 우리가 구축한 데이터베이스가 어떻게 생겼는지를 정의하기 위해서로 이해하고 있었다. 그러나 실제로 Entity 클래스의 용도는 우리의 데이터베이스와 직접적으로 연결되어 각 테이블을 클래스로 관리하기 위함이며 TypeORM에서 제공하는 각종 데코레이터 등을 통해 이를 더 쉽게 도와주기 때문이었다.
그러고보니 쿼리문을 직접 작성하는 것이 아니라면 테이블에 접근하는 코드 방식은 당연히 NestJS의 클래스와 연관될 것이라는 것을 깨닫게 되니 그제서야 퍼즐이 맞춰지는 느낌이었다.
그래서 TypeORM 설정 중에 synchronize 옵션을 true로 주게 되면 자동으로 DB에 변경사항이 업데이트 되는 것이었으며, 마이그레이션 파일을 통해서는 DB에 변경사항을 수동으로 업데이트 해줄 수 있도록 구성이 되어 있던 것이다. 말 그대로 DB와 관련된 모든 것은 SQL 적인 관점에서 완전히 벗어나서 NestJS에서 모든 것을 해결할 수 있게 된다.
그렇다면 또 의문이 드는 점이 하나 생긴다.
프로젝트를 시작할 때 초기 구축한 ERD를 기반으로 Entity 클래스 파일을 테이블 당 하나씩 생성해주어야 한다는 말이 된다.
테이블이 몇 개 없다면 금방하겠지만 프로젝트 규모가 크다면 이에 들어가는 비용이 굉장히 크다고 생각했다. 그도 그럴 것이 앞서 말한 것과 같이 ERDCloud에서 ERD를 구성하고 DDL로 변환하여 콘솔에 복사 붙여넣기만 하면 끝나는 작업을 만든 ERD를 보면서 일일히 파일 하나씩 만들어주는 것은 굉장히 귀찮은 일처럼 보였다.
그래서 조금 찾아보니 typeorm-model-generator라는 npm 라이브러리가 있었고 해당 라이브러리가 제공하는 기능은 DDL을 통해 먼저 DB에 스키마와 테이블을 만들고 제공하는 명령어를 통해 프로젝트 폴더 내에 Entity 클래스 파일을 자동으로 만들어주는 것이다.
그래서 처음에 이 라이브러리를 봤을 때 "와 이거다" 라는 생각으로 적용을 해봤지만 생각보다 별로였고 중요한 점은 typeorm 최신 버전에서 제공하는 데코레이터 기반 코드로 생성되지 않는다는 점이다. 그렇게 되면 결국 만들어진 파일을 사용하지 않은 방식과 마찬가지로 직접 다 작성을 해주어야 하는 상황인 것이다.
실제로 해당 라이브러리 이슈를 보니 굉장히 뒤떨어진 라이브러리임을 알 수 있었고 다른 방식을 모색해야 했다...
이 라이브러리의 개발자가 해당 라이브러리를 더 이상 관리하지 않기 시작한 이유는 당시 TypeORM의 많은 버그들로 ORM 툴로 TypeORM보다 다른 툴을 사용하기를 원하기 때문이라고 한다.
이런 걸 보면 다음 프로젝트에서는 Prisma나 drizzle과 같은 차세대 ORM으로 이전해보는 것도 고려해볼만한 것 같다.
(이 개발일지를 작성하고 꽤 오랜 시간이 지난 현 시점에서 실제로 계속 사용해본 결과 어쨋든 지금은 되돌릴 수가 없기 때문에 이악물고 불편한 점들을 무시하면서 개발을 하고 있는 감이 없지 않아 있기 때문에 당분간은 계속 억지로 모르는 척하고 개발을 할 생각이지만 다음 번에는 괜찮은 라이브러리를 좀 찾아볼 것이다.)
해결 방법
어쨋든 다시 돌아와서 해당 라이브러리를 사용할 수는 없었기 때문에 그럴 바에야 차라리 그냥 아예 처음부터 ERD 짠 거 보면서 직접 파일을 만들고 데코레이터를 달아주는 것이 물론 귀찮긴 하지만 요구사항에 적합한 코드를 작성할 수 있기 때문에 해당 방식으로 진행하기로 했다.
그러나 예상대로 테이블 하나 만들고 귀찮음을 느끼게 되었는데, 문득 평소 ChatGPT 광신도 였던 나는 이것도 GPT로 해결할 수 있지 않을까 싶은 생각이 들었고 이를 실행에 옮겨보기로 결심했다.
방식은 다음과 같다.
- ERD 툴에서 DDL로 변환한 쿼리문을 복사한다.
- GPT에게 쿼리문을 던져주고 NestJS Entity 클래스를 만들어달라고 한다.
- 각 Entity 클래스에 복사 붙여넣기 한다.
- 끝!
결과는 예상보다 더 만족스러웠고 약간의 수정만 하면 손쉽게 모든 Entity를 만들어낼 수 있게 되었다.
DB를 연결하는 작업에는 코드로 모델을 만들어서 DB에 적용하는 Code-First 방식과 이미 만들어진 스키마를 프로젝트에 가져와 파일 형태로 만들어주는 Schema-First 방식이 존재한다. 이와 대해 정리한 내용은 여기를 참고하면 된다.
- Prisma에서는 db pull이라고 데이터베이스 스키마를 그대로 가져와 .prisma 파일을 만들어주는 기능이 있는데 이것이 Schema-First 방식이고 앞서 살펴본 typeorm-model-generator 역시 Schema-First 방식으로 볼 수 있다.
4. 오늘 새롭게 알게 된 내용
.
5. Reference
- https://velog.io/@kisuk623/%EC%99%9C-Prisma%EB%A5%BC-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C-anyORM
- https://github.com/Kononnable/typeorm-model-generator/issues/329
6. Takeaway
Code-first와 Schema-first 중에서 우리가 이번 프로젝트에 적용할 방식은 어떤 걸로 하는 것이 가장 좋은 선택일지를 생각해보았는데 결론은 Code-first 방식이었다.
그 이유는 어차피 데이터베이스 상에 DDL로 스키마를 미리 구축해 놓고 typeorm-model-generator를 사용해서 entity를 만들 수 있다고 하더라도 그 엔티티를 결국 수정을 해야했고 해당 라이브러리는 legacy이기 때문에 그냥 앗싸리 처음부터 견고한 Entity 클래스를 만들어버리자는 것이 나의 생각이었던 것이다.
그런데 운이 좋게도 GPT가 나온 시점에서 DDL을 GPT에게 주면서 클래스를 만들어달라고 하니 너무 잘 만들어주기 때문에 이를 그냥 복사 붙여넣기 해서 클래스를 만들고 입맛에 맞게 수정하면 Prisma 못지 않게 DB 연결을 아주 깔끔하게 매듭을 지을 수 있다.
'프로젝트' 카테고리의 다른 글
mysql 커넥션 풀 오류 해결 과정 (1) | 2024.08.26 |
---|---|
[프로젝트] 데이터를 불러오는 데 너무 오래걸리는 문제 해결 및 성능 개선(feat. 데이터베이스 join) (0) | 2024.06.20 |
[Devlog] 개발 시작부터 지금까지의 여정 (1) | 2024.06.02 |
[Develog] 골치아픈 로그인 및 인증 - 2. 적용 (Next-auth@v5/Auth.js) (0) | 2024.04.24 |
[Develog] 골치아픈 로그인 및 인증 - 1. 이론 정리 (세션, 쿠키, JWT) (0) | 2024.04.13 |
소중한 공감 감사합니다