-
express sequelize로 개발시작
-
sequelize의 리미트와 오프셋 설정을 쿼리스트링으로 가져올 시 문제 발생했다.
-
[문제점] 타입이 맞지 않아서 페이지 네이션이 정상작동하지 않았다.
-
[문제점] 타입 체크의 중요성을 깨닫고 취업시장에서도 타입스크립트를 반 필수적으로 요구하는 것을 발견했다.
-
TypeScript가 기본으로 내장되어있는 Nest.js에 관심을 가지게 되었다.
-
express sequelize 개발 완료 후 Nest.js로 마이그레이션 계획 세웠다.
-
[해결 방법] Nest.js의 ORM을 어떻게 조합할까 고민하고 직접 사용해서 테스트했다.
- Prisma
- 장점:
- 스타팅이 매우 쉽고 간결하다.
- Sequelize,TypeOrm에 비해 스타트가 쉽다는 것은 매우 큰 장점이다. Prisma ORM을 사용하기 위해서 Prisma를 설치하고 서비스 파일을 관리하는 것 이외에 다른 부가적인 설정들을 건드리지 않아도 된다. sequelize에 익숙하다면 몇몇 부분을 제외하고 Sequelize와 비슷한 방식으로 Prisma ORM을 사용 할 수 있다.
- prisma pull
- prisma pull은 Prisma에서 제공하는 강력한 기능으로 이미 작동하고 있는 데이터베이스의 스키마를 자동으로 한 번에 만들어 준다. 이렇게 함으로써 이미 구성되어있는 데이터 베이스의 스키마를 다시 작성 할 필요없이 손쉽게 가져와서 개발 시간을 단축 할 수 있게 해준다.
- 또한 스키마 변경시 prisma push하고 prisma migration해서 데이터 베이스를 관리 할 수 있다.
- 단점 :
- prisma pull
- Prisma pull은 강력한 기능이다 하지만 치명적인 단점이 존재한다. Prisma pull로 가져온 스키마를 데이터 베이스와 연동하기 위해서 prisma migration 명령어를 입력하면 높은 확률로 데이터 베이스의 초기화를 묻는 경고 메세지가 나타난다. y를 누르면 예외없이 데이터베이스를 초기화 해버린다. 한 번 migration을 진행한다면 이 후에는 경고 메세지가 나타나지 않지만, 그 한번이 치명적이다.
- 위 문제를 해결하기 위해서 여러 곳을 찾아다녔다. 하지만 공식사이트와 그 어느 사이트에서도 이 문제에 관한 해답을 찾지 못했다. 어떤 블로그에서 product 환경시에는 발생하지 않는다고 글을 보았지만 해결법을 제시하지는 않았다.
- 객체 중첩 해결방법 부재
- Sequelize에서는 객체 중첩을 간단하게 해소하기 위해서 raw:true 기능을 제공하지만 Prisma에서는 제공하지 않는다. 따라서 데이터 가공을 위해서 직접 객체 중첩을 풀어주는 메서드를 만들어야 한다. 이렇게 가공 메서드를 사용하면 데이터를 클라이언트에 제공할 때 속도 저하가 발생한다.
- 내장함수의 부재
- Sequelize는 Sequelize.fn() 내장함수를 제공한다. 이 내장함수는 SQL 쿼리를 직접 작성할 필요가 없어 코드의 복잡성이 줄어든다.
- TypeORM QueryBuilder는 메서드 체인 방식으로 쿼리를 작성할 수 있어 코드의 가독성이 상대적으로 좋다
- Prisma는 아쉽게도 SQL 쿼리를 실행할 수 있다는 것 외에 Prisma
executeRaw
가 가진 특출난 장점이 없다. 그리고 alias를 지정해 주는 옵션이 없기 때문에 alias를 지정하기 위해서 SQL 쿼리를 실행 할 수 밖에 없게 된다. 프로퍼티의 키네임을 바꾸기 위해서는 SQL 쿼리를 실행 하던가 아니면 매핑해서 데이터의 이름을 바꿔야하는 두 가지 선택지만 남게 된다. 데이터를 매핑하게 된다면 많은 데이터를 한번에 가져와야 할 때 데이터 처리 속도에서 손해를 보게되고 SQL 쿼리를 쓰자면 코드의 가독성과 일관성이 떨어질 수 있다는 점이 아쉽다.
- Sequelize :
- 장점 :
- 강력한 기능
- sequelize를 설치하고 간단한 모델설정을 마치면 손쉽게 데이터 베이스에 접근하고 사용이 가능하다.
- 내장 함수의 용이함
- Sequelize.fn() 을 사용하면 ORM사용중에도 내장 함수를 사용 할 수 있다. 의도하고 있는 특정 부분에만 내장함수를 사용해서 복잡한 쿼리문을 줄일 수 있다는 것은 큰 장점이다.
- 데이터 베이스 동기화
- 데이터 베이스 동기화도 간단하다. 모델을 이용한 싱크방식이 권장되는 방법은 아니지만 DEV단계에서 유용한 옵션으로 모델의 {force: false, alter: true} 옵션을 사용하면 데이터 베이스의 모델이 변경 되어도 데이터 베이스 드랍없이 데이터 베이스에 적용이 가능하다.
- 단점 :
- 데이터 타입에 따른 오류 발생
- 사실 TypeScript를 사용하지 않았을 때 모든 ORM에서 발생 할 수 있는 문제다. 하지만 우리팀의 경우를 소개해 보자면 GET요청으로 쿼리스트링 숫자를 받아오고 받아온 숫자를 이용해서 페이지 네이션 기능을 구현해야하는데, 이 때 리미트와 오프셋에 들어갈 인자 값의 타입이 string이라서 오류가 발생하는 문제가 있었다.
- TypeORM :
- 장점 :
- 강력한 기능
- sequelize와 대부분의 장점을 공유하면서 Sequelize와는 차별화 된 주요 기능이 두 가지 존재한다.
- 하나는 migration으로 엔티티 변경사항을 남길 수 있는
Timestamp
기능이다. 이를 통해서 데이터베이스의 변경 사항을 안전하게 적용하고 추적 할 수 있다.
- 두번째는 TypeORM에서 제공하는 QueryBuilder로 메서드 체인 방식이다. 쿼리를 작성할 수 있어 코드의 가독성이 상대적으로 좋다. 또한 메서드 체인 방식으로 이루어지기 때문에 if 분기를 통한 코드의 재사용성도 고려 할 수 있다.
- 단점
- 스타트 난이도가 높다
- TypeORM을 사용하기 위해서 의존성을 주입해 주어야하고 config 파일을 작성해야한다.
- 그런데 이 config파일의 설정 난이도가 높다. 또한 TypeORM의 버전이 업데이트가 되면서 작성해야하는 DataSource config 설정중 cli 프로퍼티가 DataSource에서 삭제되었다. 따라서 구글링으로 정보를 찾는다면 DataSource에 cil프로퍼티가 있는 자료는 보고 따라 할 수가 없게 되었다.
- migration cli명령어도 script상에서 수정 해주어야 하며 이 때, ts파일의 위치를 파악하지 못하므로 ts-node 라이브러리를 설치해서 script를 수정하기까지 해야한다.
- QueryBuilder의 자체 난이도
- 메서드 체인 방식으로 작동하는 QueryBuilder는 쿼리 빌더 메서드를 제외하고, 내부의 자동완성 기능을 지원하지 않는다. 따라서 개발자가 입력시 컬럼의 값을 명확하게 알고 있어야하며 인자를 넘겨 줄 때도 setParameter로 명시 해줘야한다.
- [궁금증] express와 Nest.js를 비교하면 데이터를 가져오는데 누가 더 빠를까?
- 처음에는 막연하게 Prisma로 작성한 Nest.js가 더 빠르겠지 생각했다. 그러나 같은 코드를 작성하고 테스트 해본 결과 Prisma를 이용해서 데이터를 가져올 때, 객체 중첩을 푸는 과정이 존재하기 때문에 express와 비교해서 약 80ms정도의 손해를 더 보았다. 따라서 express와 Nest.js의 속도비교는 체감상 무의미하고 raw쿼리와 가깝게 잘 짠 코드거나 ORM의 객체 중첩해제 기능지원 여부에 따라서 속도가 결정 된다는 것을 깨달았다.
- [결론] 위와같은 테스트 결과로 처음에는 Nest.js Prisma 조합을 채택했지만 Prisma 단점 중 데이터 베이스 초기화에서 유래되는 Technical debt 를 피하기 위해서 Nest.js와 TypeORM 조합으로 선회해서 Nest.js로 마이그레이션을 진행했다.