-
- Postgresql은 자체적으로 구축해서 사용중인 홈 서버에 Docker로 올려서 사용했습니다.
-
- Redis도 Postgresql과 마찬가지로 홈 서버에 Docker를 이용해 사용했습니다.
src
├── auth // 인증/인가
│ └── jwt
├── common // 공통 컴포넌트
│ ├── decorators
│ ├── dto
│ ├── enum
│ ├── interfaces
│ └── utils
├── config // 설정 관련(DB, ORM, ...)
├── exceptions // 커스텀 예외 및 에러 클래스
├── interceptors // 커스텀 인터셉터
├── orders // 주문 도메인
│ └── dto
├── products // 상품 도메인
│ └── dto
├── users // 유저 도메인
│ └── dto
└── validations // 유효성 검사
- auth
- 인증/인가와 관련된 컴포넌트
ex) jwt, google, github, ...etc
- common
- 전역으로 공통적으로 사용될 파일들로 구성
ex) Dto, Decorator, enum, interface, utils
- config
- database, orm 설정 등 추가적으로 특정 패키지의 설정이 들어갈 때 해당 디렉토리에서 모듈화하도록 구성
- exceptions
- 커스텀하게 특정 예외나 에러를 처리하기 위한 클래스들이 위치할 디렉토리
ex) HttpExceptionFilter
- interceptors
- AOP를 위한 특정 역할을 위한 커스텀한 Interceptor 클래스들이 위치할 디렉토리
ex) Logging Interceptor, Transform Interceptor
- orders
- 주문 관련 도메인의 파일로 구성
- products
- 상품 관련 도메인의 파일로 구성
- users
- 유저 관련 도메인의 파일로 구성
- validations
- 각종 유효성 검사를 위한 파일로 구성
ex) 환경변수 및 데이터베이스 설정 Validation
- 주문 조회
(GET /orders)
- 주문 상세 조회
(GET /orders/{id})
- 주문 접수 요청
(POST /orders)
- 주문 완료 요청
(POST /orders/complete/{id})
- 주문 취소 요청
(POST /orders/cancel/{id})
- 주문 상품 부분 취소 요청
(POST /orders/partial-cancel)
- 주문 수정
(PUT /orders)
- 주문 삭제
(DELETE /orders)
- 상품 조회
(GET /products)
- 상품 상세 조회
(GET /products/{id})
- 상품 등록
(POST /products)
- 상품 수정
(PUT /products/{id})
- 상품 삭제
(DELETE /products/{id})
- 회원 전체 조회
(GET /users)
- 회원가입
(POST /users/register)
- 로그인
(POST /users/login)
Nest.js
와 TypeORM
은 튜토리얼을 제외하면 처음 사용해서 트랜잭션을 처리하는 방법을 몰라 여러 방법을 조사해서 한 가지 방법을 선택했습니다.
각종 블로그와 여러 문서들에는 트랜잭션을 사용하는 방법은 3가지가 소개되어있습니다.
- @Transaction Decorator 사용
- Transaction 객체를 생성해서 이용하는 방법
- QueryRunner를 이용하여 DB 커넥션을 수동으로 관리하는 방법
TypeORM
공식문서의 v0.3.0
ChangeLog에 따르면 기존에는 3가지가 있었지만 현재는 1, 2번의 방법이 모두 Deprecated
된 상태로 3번의 방법인 QueryRunner
를 이용하는 방법을 사용했습니다.
아래 코드와 같이 queryRunner객체를 생성하고 연결 후 트랜잭션을 시작하고 서비스 로직을 실행합니다.
만약 에러가 발생하면 catch 블록으로 빠지게 되고 rollback을 하게 되고 서비스 로직이 무사히 끝나면 finally 블록으로 가서 커넥션을 release시킵니다.
async someFunc(requestOrderDto: RequestOrderDto): Promise<Entity> {
const queryRunner = this.connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
/* ... Service Logic ... */
return data;
} catch (err) {
await queryRunner.rollbackTransaction();
throw err;
} finally {
await queryRunner.release();
}
}
Nest.js에서도 AOP를 위해 제공하는 패턴이 있고 거기서 사용한 패턴은 총 3가지 입니다.
-
HttpExceptionFilter
- 애플리케이션 단에서 발생하는 모든 예외를 캐치해서 처리하는 글로벌 필터입니다.
- API에서 발생한 에러일 경우 의도한 리스폰스 포맷으로 응답하도록 구현해놓고 그 이외의 예외는 Nest.js의 기본 내장 예외 필터가 처리하게 됩니다.
Sample Response
-
{ "success": false, "statusCode": 401, "timestamp": "2022-10-10T17:27:53.754Z", "path": "/users/login", "message": { "statusCode": 401, "message": "로그인에 실패했습니다.", "error": "Unauthorized", "stacktrace": "UnauthorizedException: 로그인에 실패했습니다.\n at UsersService.verifyUserAndSignJwt (/Users/jcjeong/project/product-order-management/src/users/users.service.ts:94:13)" } }
-
TransformInterceptor
- 모든 API의 표준 응답 포맷을 적용하기 위해 사용한 인터셉터입니다.
- 각 API의 결과 데이터를 아래 객체에서 data넣어서 응답하게 됩니다.
-
{ "success": true, "data": [] }
-
LoggingInterceptor
주문 및 취소 시 알림 요구사항을 보고 만든 부분입니다.
Redis를 Bull Message Queue라는 패키지를 이용해서 메세지 브로커로서 이용했습니다.
아래 그림의 플로우는 다음과 같습니다.
- 클라이언트가 Orders Controller로 주문 접수 요청 또는 주문의 상품 부분 취소 요청을 한다.
- 해당 요청에 대응되는 Service Layer의 메소드를 통해 요청을 처리한다.
- 요청이 성공일 경우에 결과 데이터를 Order Queue로 주문 접수일 경우 requestOrder란 키값으로 부분 취소 요청일 경우 partialCancel이란 키값으로 메세지를 전달한다.
- Orders Consumer에서는 지정해놓은 키값들에 메세지가 생성되면 구독해서 대응되는 메소드에서 로직을 처리
그림에서는 Orders Controller와 Users Controller가 가장 앞단에 존재하지만 사실 메세지 브로커로 메세지를 생성해서 전달하는 프로듀서 역할은 각 Service들이 하고 있습니다.
Node.js의 버전은 현재 공식 Node.js Relase Repository에 따른 유지보수 중인 최소 LTS 버전인 14 이상을 사용하시면 됩니다.
과제는 현재 Active 상태의 LTS인 16버전을 기준으로 개발했습니다.
$ npm install
환경 변수는 프로젝트의 루트 패스 기준으로 .env
라는 이름으로 파일을 생성해서 아래 내용을 기입해주시면 됩니다.
- Postgresql 정보
- Application 정보
- PORT
- SECRET_KEY
- Redis 정보
DB_TYPE = postgres
DB_HOST =
DB_PORT =
DB_USERNAME =
DB_PASSWORD =
DB_NAME = nest
# app
NODE_ENV = develop
PORT = 3000
ADMIN_USER =
ADMIN_PASSWORD =
SECRET_KEY = some_secert_key_nest_js
COOKIE_SECRET = some_secert_key_nest_js
# postgresql db
POSTGRES_DB =
POSTGRES_USER =
POSTGRES_PASSWORD =
# redis config
REDIS_HOST =
REDIS_PORT =
데모를 실행해보실 때는 npm run start
로 실행해보시면 됩니다.
# development
$ npm run start
# watch mode
$ npm run start:dev
# debug mode
$ npm run start:debug
# production mode
$ npm run start:prod
환경변수에서 포트 부분을 변경할 경우 해당 포트로 기입하면 됩니다.
http://localhost:3000/api
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov