유동

[Node] 기존 JavaScript와 Express 에서 Typescript와 Prisma로의 전환 본문

node.js

[Node] 기존 JavaScript와 Express 에서 Typescript와 Prisma로의 전환

동 선 2024. 5. 31. 17:17

서론

  • 약 10달전 Express + Javascript + Raw SQL(pg 라이브러리)를 사용해서 첫 프로젝트를 진행했습니다. https://github.com/DongSeonYoo/clog-backend
    • 이후 지금까지 Typescript를 기반으로 Nestjs나 express 를 이용해서 프로젝트를 몇개 진행하였지만 시간에 쫒기거나, 비공개레포인 경우여서 이번에 각잡고 기존 프로젝트를 개선하려고 합니다. 기획 다시하거나 팀원들 구하려면 귀찮기때문에..

뭐 쨋든 기존에 했었던 프로젝트니까 프로젝트에 대한 요구사항도 아주 잘 알고있고, 당시 개발할때 힘들었던 점을 잘 알고 있으니 그런 부분에 대해서 개선점을 찾아보면서 진행하려고 합니다.

서론은 여기까지 하고 기존 프로젝트 설명으로 가겠습니다

 

기존 프로젝트의 대략적인 설명

기술 스택

언어 및 프레임워크: Express & Javascript
데이터베이스: Postgresql, Redis(인증번호 보관용도), mongoDB(알림기능)
기타: AWS s3 (정적파일 보관용도), notion (api 명세서)
ORM: X (raw SQL)

해당 프로젝트의 한계점

1. 프로그래밍 언어

  • 해당 프로젝트가 api가 대략 90개정도로 많은 편은 아니지만 javascript언어의 특성상 코드베이스가 복잡해질수록 유지보수가 지옥수준으로 떨어집니다. IDE의 자동완성과 타입추론기능도 사용하지 못하고, 픽하면 런타임에서 에러가 나서 굉장히 힘들었었던 기억이 납니다 사실 힘든지도 몰랐음

2. ORM

  • ORM을 사용하지 않고 PostgreSQL 데이터베이스 드라이버를 그대로 사용하면서, 스키마 변경 사항에 유연하게 대처하지 못했습니다. 데이터베이스의 컬럼이나 제약조건을 하나 수정할 때마다 코드에서 문자열을 찾아가며 수정해야 했습니다. 이는 매우매우 번거롭고 오류 발생 가능성을 높였습니다.
  • 무서운 이야기: (만약 컬럼이름이 수정된다면?? OR 테이블 구조가 변경된다면??)

3. 어플리케이션 아키텍처

  • api 엔드포인트 라우터 하나에 모든 로직을 몰빵했습니다.
  • 때문에 한 라우터에 validation, db 접근로직, 예외처리가 존재해 다른 라우터에도 중복된 코드가 정말 매우매우 많았습니다.
  • 아키텍처에 대한 명확한 이해가 부족하였고, 여러 아키텍처를 적용하기보다는 한 라우터에 모든 로직을 몰아넣었습니다. 이로 인해 라우터가 몇백 줄이나 되는 경우도 있었고, 이는 유지보수에 매우 어려움을 초래했습니다.때문에 한 라우터가 몇백줄이나 되는 해괴한 현상이 펼쳐졌고 이는 곧 지옥행열차를 탔습니다
  • 백엔드: (죽여줘..)

Typescript 도입

  • 타입 안정성을 높여 최대한 컴파일 타임에 개발자가 코드를 쥐락펴락할수있게 합니다.

ORM 도입

  • 평문으로 SQL날리고 결과값을 런타임에서 확인하는 노가다를 피하기 위해 ORM을 사용할것입니다.
  • node진영에는 자바진영의 JPA와 같은 표준 ORM이 없어 다양한 ORM이 존재합니다. 이중에서 저는 prisma를 사용할겁니다.

왜 Prisma?

  • 저는 node진영에서 제일 인기있는 Typeorm과 Prisma를 둘다 사용해봤습니다. Prisma client가 지원하는 강력한 db스키마 연동능력과 타입 안정성에 반해 푹 빠져버렸습니다...

Typeorm 예제

  • Typeorm 예제

 

Prisma 예제

  • prisma 예제

사실 위에서 보시다시피 prisma의 type-safety한 기능 하나때문이라도 안쓸 이유가 없다고 생각했습니다.

  • Prisma의 타입 안전성 하나만으로도 선택할 이유가 충분합니다. 추가로 Prisma는 마이그레이션 기능, prisma db push, prisma db pull과 같은 명령어를 통해 데이터베이스 스키마와 쉽게 연동할 수 있습니다.
  • 추가로 Typeorm은 0.3.0 버전부터 많은 버그 리포트들이 존재하지만 반영이 원할하게 진행되고 있지 않아보입니다.
    typeorm last commit
  • prisma는 지속적인 관리가 이루어지는것같네요
    prisma last commit

이 글을 쓰는 시점에서 prisma의 새로운 기능이 도입된다고 합니다.

 

ORM 선정은 끝났고.. 프레임워크는 왜 Nestjs?

Nest 이야기하기 전 express부터..

  • 일단 제일 유명한 근본 express를 먼저 이야기하자면, 클라이언트로부터 오는 http request를 어플리케이션단에서 손쉽게 핸들링 할 수 있는 기본적인 기능부터, 라우팅, 템플릿 엔진, 파일 서비스 등 백엔드 개발에 필요한 기본적인 기능을 제공합니다. 하지..만
  • 그 이상은 백엔드 개발자의 몫입니다. express로 시작을 하면, index.js부터 개발자의 입맛대로 하나하나 파일을 만들어가며 어플리케이션 구조를 만들어야 합니다. 어찌보면 자유로워서 좋을 수도 있다고 생각하겠지만, 어플리케이션이 커질수록 복잡도가 증가하고. 특히 서로다른 성향을 가진 개발자들끼리 협업을할때엔 난이도가 굉장히 올라갑니다.
  • 이러한 치명적인 단점을 개선하기 위해서 node진영에 많은 백엔드 프레임워크들이 생겨났습니다. koa, hapi, fastify 등 백엔드 프레임워크들이 속속 출시되었지만, 근본적인 문제인 아키텍쳐를 제공해주지는 않습니다.
  • 사실 express는 프레임워크라고 보기엔 라이브러리 느낌이 강한 프레임워크입니다.
    • 뭐가 프레임워크고 뭐가 라이브러리냐? 의 핵심은 IoC에 있다고 봅니다. 라이브러리는 내가 주도권을 가지고 각각의 의존성을 설계하고 능동적으로 사용하는 반면에, 프레임워크는 내가 작성한 어플리케이션이 프레임워크에 의해 사용됩니다.
    • 프레임워크에게 개발자 개인이 제어해야 할 영역을 맡기는것입니다. (여기서는 아키텍쳐가 되겠네요) express는 그런 점에 비해서는 라이브러리에 더 가깝겠지요

Nestjs

  • 앞서 말한 근본적인 문제를 해결하기 위해, Nestjs라는 백엔드 프레임워크가 등장했습니다. 공식문서를 보면
  • nestjs 공식문서
  • (그 어떤 프레임워크도 이러한 문제를 해결하지 못해서, 우리가 해결했다)
  • 실제로 nestjs를 사용해보니, 굉장히 우수한 부분이 많았습니다. nest new로 프로젝트를 생성함과 동시에, module, controller, service를 기본적으로 만들어주고, 미들웨어, 가드, 파이프, 인터셉터 등 체계적인 기능과, 각 모듈간의 의존성 주입을 통해 효율적이고 체계적으로 아키텍쳐를 구상할 수 있습니다.
  • 또 빌트인 기능이 굉장히 많아 @nestjs/common에 웬만한 기능들이 내장되어있어 필요에 따라 사용할 수 있습니다.

결론

그래서 결론은뭐냐구요?

  • 이미 만들어진 백엔드 어플리케이션을 밑바닥부터 다시 만들어보며 당시 아쉬웠던 부분들을 리팩토링하고, 좋은 백엔드 구조란 무엇인지 탐구할 것입니다. 또한, 배포와 인프라 관련 지식도 새롭게 배워 적용해보려고 합니다