SeongJae Oh

Delivery Master

NestJS를 이용한 배달주문 어플리케이션의 백엔드를 구현해보았습니다. PM2를 활용한 무중단 배포, JWT토큰을 이용한 Stateless 인증, Swagger를 이용한 API 명세 등이 들어가있습니다.

Project Environment

  • NestJS Framework
  • MariaDB / TypeORM
  • JWT / Redis
  • Sentry / Webhook(discord)
  • PM2

프로젝트 개발 동기 및 개요

NestJS 프레임워크를 이용한 RestAPI의 연습용 프로젝트의 목적으로 시작하였습니다.

PM2를 이용한 무중단 배포와 에러 추적을 위해 에러 발생 시 NestInterceptor를 통해 에러를 Sentry와 Discord 웹훅을 통해 쉽고 빠르게 확인할 수 있도록 하였고 stateless한 사용자 인증을 위해 JWT토큰을 사용하였습니다.

프로젝트 스크린샷

image
image

Auth(JWT + Redis)

JWT(JSON Web Token)는 선택적 서명 및 선택적 암호화를 사용하여 데이터를 만들기 위한 인터넷 표준으로, 페이로드는 몇몇 클레임(claim) 표명(assert)을 처리하는 JSON을 보관하고 있습니다. 토큰은 비공개 시크릿 키를 사용하여 서명되지만 클라이언트 측에서 노출될 수 있어 추가적인 악의적 접근에 대한 방어가 필요합니다.

노출되거나 탈취된 토큰을 방어하기 위해 Access, Refresh라는 두 개의 JWT 토큰으로 나누어 관리하며 기본적으로 클라이언트가 API에 접근하는 데 사용되는 키는 Access 키 입니다. 이 Access키의 재발급을 위해 사용되는 토큰이 Refresh토큰이며 이을 Redis에 보관하여 검증절차를 다중으로 만들고 보안을 강화하였습니다.

image
image

Error Tracking(Sentry & Discord Webhook)

Winston같은 에러 기록 모듈을 사용해서 에러만 기록하기보단 이에 더해 실시간으로 에러 발생 시 인터셉터로 캐치하여 상세한 에러 내역과 발생 시간 등을 편하게 볼 수 있도록 제공해주는 Sentry와, Slack 대신 Discord의 무료 웹훅을 사용하여 알림을 전송해 모바일로도 바로바로 에러 내역을 받아볼 수 있도록 만들었습니다.

image
image

프로젝트 코드 일부

깃허브 저장소 주소로 대체합니다. [Click Me!]

프로젝트 후기

RestAPI 컨벤션에 맞는 제대로 된 백엔드 프로젝트를 한 번 만들어보고 싶다 생각하여 CRUD만 작성하려 하였으나 이것저것 붙이다 보니 의도했던 규모보다는 살짝 커졌습니다. 첫 번째로, 사용자 인증 부분인 JWT에서 상당한 고생을 했습니다.

액세스 토큰이 만약 탈취당하면?
- 위험을 줄이도록 만료기간 대폭 축소
그래도 액세스 토큰이 만약 탈취당하면?
- 쉽게 볼 수 없도록 httpOnly 속성 사용
그러면 재발급마다 로그인을 해야하니 UX측면에서 나쁘지 않은가?
- 리프레쉬 토큰을 사용해서 액세스 재발급하도록 이중인증
만약, 리프레쉬도 탈취당하면?
- 탈취 후 쉽게 변조할 수 없도록 서버 DB에 보관 후 검증
DB에 보관하여 서버 리소스를 사용하면 stateless의 의미가 없어 세션방식이랑 차이가 없지 않은가?
- DB에서 제거 후 DB커넥션 리소스를 사용하지 않고 인메모리 방식이라 빠른 속도의 Redis 사용
이렇게 레디스에 보관하여도 탈취당하면 만료시간 전까지는 위험하지 않은가?
- 액세스 토큰 재발급 시 리프레쉬도 재발급 후 재저장(RefreshTokenRotation)

위와 같이 꼬리에 꼬리를 무슨 질문을 스스로에게 던지고 선례를 찾아보며 할 수 있는 한 최대한의 JWT보안을 신경써보자는 생각에 많은 고민을 하였습니다.

두 번째로 TypeScript로 작성하는 NestJS 프레임워크이지만 컴파일 후 실제 작동되는 서버는 JS환경이기에 사용자가 악의적으로 틀린 값을 입력하면 그대로 에러가 발생하는 부분을 하나씩 유효성 검증을 체크하며 위협을 제거해야 하는 부분을 알게 되었고, 이 문제를 해결하며 실시간으로 에러가 보고되면 좋지 않을까 하는 생각에 Sentry, Webhook(Discord)를 이용한 실시간 알림 등의 모듈을 추가로 붙여 구현하며 아직도 제가 모르는 꿀같은 모듈들이 많을 것이라는 기대감이 생겼습니다.

다른 기발한 아이디어가 있으신가요?

주니어 개발자로써 많은 것을 배우고 제작해보고싶습니다. 현재 프로젝트에 추가되었으면 하는 기능이나, 완전히 새 프로젝트에 대한 아이디어가 있으시면 저에게 연락주시면 감사하겠습니다.