Skip to content

whitewise95/type-script-backend-example

Repository files navigation

Coupang Style E-commerce Backend with NestJS

Java + Spring Boot 백엔드 개발자가 NestJS로 넘어올 때 바로 감을 잡을 수 있도록 만든 학습용 클론 프로젝트다.
목표는 "쿠팡 스타일 이커머스 백엔드"를 NestJS로 설계하면서도 Spring Boot에서 익숙한 개념과 대응 관계를 자연스럽게 이해하는 것이다.

이 README는 아래 순서대로 설명한다.

  1. Spring Boot와 NestJS 개념 매핑표
  2. 프로젝트 폴더 구조 제안
  3. DB 설계
  4. 인증/인가 코드
  5. 상품/장바구니/주문 API 구현
  6. Docker 및 ECS/CodePipeline 배포 전략
  7. Swagger 적용

1. Spring Boot와 NestJS 개념 매핑표

Spring Boot NestJS 설명
@RestController @Controller() HTTP 요청 진입점
@Service @Injectable() service 비즈니스 로직 계층
@Repository TypeORM Repository / Prisma Client DB 접근 계층
@Entity TypeORM @Entity() / Prisma schema model 정형 데이터 모델
@Configuration @Module() + provider 등록 의존성 조립과 설정
@Bean, DI provider + constructor injection DI 방식은 매우 유사
Spring Security Filter Guard / Strategy / Interceptor / Middleware 요청 전후 보안 처리
UserDetailsService PassportStrategy validate JWT payload 해석 후 사용자 주입
@PreAuthorize @UseGuards() + @Roles() 메서드/핸들러 권한 제어
@Valid ValidationPipe + DTO decorator 요청 검증
@ControllerAdvice + @ExceptionHandler Exception Filter 전역 예외 처리
JPA Auditing BaseEntity + CreateDateColumn, UpdateDateColumn 생성/수정 시간 관리
application.yml .env + ConfigModule 설정 관리

왜 이렇게 매핑했는가

NestJS는 Express/Fastify 위에 올라간 프레임워크지만, 개발 경험은 상당히 Spring스럽다.
특히 Module, Injectable, Controller, Guard, Pipe 조합은 Spring의 Configuration, Bean, Controller, Filter, Validator 감각과 많이 닮아 있다.

Spring 방식과 비슷한 점

  • DI 중심 구조다.
  • 계층형 설계를 자연스럽게 유도한다.
  • 애너테이션 기반 선언형 프로그래밍이 가능하다.

Spring 방식과 다른 점

  • NestJS는 TypeScript 런타임과 Node.js 이벤트 루프 위에서 동작한다.
  • JPA처럼 영속성 컨텍스트를 자동으로 믿기보다는, 명시적인 저장과 조회 흐름을 더 자주 의식하게 된다.
  • Security도 Servlet Filter Chain보다는 Guard, Strategy, Decorator 조합으로 생각하는 편이 더 자연스럽다.

2. 프로젝트 폴더 구조 제안

src
├── common
│   ├── auth
│   │   ├── current-user.decorator.ts
│   │   ├── jwt-auth.guard.ts
│   │   ├── roles.decorator.ts
│   │   └── roles.guard.ts
│   ├── database
│   │   └── base.entity.ts
│   └── enums
│       └── user-role.enum.ts
├── modules
│   ├── auth
│   │   ├── dto
│   │   ├── entities
│   │   ├── presentation
│   │   ├── service
│   │   └── strategy
│   ├── product
│   ├── cart
│   └── order
├── app.module.ts
└── main.ts

왜 이렇게 설계했는가

Spring Boot에서 controller/service/repository/domain으로 나누는 감각을 유지하되, NestJS에서는 기능 모듈 단위로 먼저 묶는 편이 유지보수에 좋다.
그래서 auth, product, cart, order 같은 bounded context 단위로 분리하고, 공통 관심사는 common에 두었다.

Spring 방식과 비슷한 점

  • 도메인별 패키지 분리가 가능하다.
  • service 계층과 entity 계층을 분리한다.

Spring 방식과 다른 점

  • NestJS는 @Module()이 실제 조립 단위다.
  • 패키지보다 "모듈 import/export 관계"를 더 자주 의식하게 된다.

3. DB 설계

이 프로젝트는 요구사항에 맞춰 PostgreSQL과 MongoDB를 역할 분리해서 사용한다.

3-1. PostgreSQL: 정형 데이터

PostgreSQL에는 아래 데이터를 둔다.

  • users
  • products
  • carts
  • cart_items
  • orders
  • order_items

3-2. MongoDB: 로그/이력성 데이터

MongoDB에는 아래처럼 변화 이력과 로그를 둔다.

  • order_logs
  • 추후 확장: product_view_logs, payment_event_logs, delivery_tracking_logs

왜 이렇게 설계했는가

주문, 상품, 장바구니는 조인과 트랜잭션이 중요한 정형 데이터라 PostgreSQL이 적합하다.
반면 주문 생성 로그, 이벤트 이력, 조회 로그는 스키마 변경이 비교적 자유롭고 쓰기 위주라 MongoDB가 잘 맞는다.

Spring 방식과 비슷한 점

  • Spring Boot에서도 RDB + Mongo 혼합 아키텍처를 자주 쓴다.
  • 핵심 트랜잭션은 RDB에 두고, 부가 로그는 NoSQL로 분리한다.

Spring 방식과 다른 점

  • NestJS에서는 같은 앱 안에 TypeOrmModuleMongooseModule을 같이 붙이는 흐름이 비교적 간단하다.
  • Java에서 JpaRepository, MongoRepository를 나누듯, 여기서는 TypeORM Entity와 Mongoose Schema를 나눈다.

3-3. 예시 ERD

users (1) --- (N) orders
users (1) --- (1) carts
carts (1) --- (N) cart_items
products (1) --- (N) cart_items
orders (1) --- (N) order_items
products (1) --- (N) order_items

3-4. TypeORM vs Prisma 비교

TypeORM

  • Spring JPA와 가장 비슷하게 느껴진다.
  • @Entity, Repository, relation decorator가 익숙하다.
  • "JPA스럽게 연습"하려면 진입 장벽이 낮다.

Prisma

  • 타입 안전성과 schema 기반 개발 경험이 좋다.
  • SQL이 어떻게 나갈지 비교적 예측하기 쉽다.
  • relation loading과 migration 흐름이 JPA보다는 명시적이다.

Spring 개발자 기준 추천

  • 처음에는 TypeORM이 더 쉽게 들어온다.
  • 실무 생산성과 타입 안정성까지 생각하면 Prisma도 매우 강력하다.

한 줄 요약:

  • "JPA 감성으로 빠르게 익히기"는 TypeORM
  • "명시적이고 깔끔한 데이터 접근"은 Prisma

4. 인증/인가 코드

현재 예제는 JWT + Guard + Role 기반으로 작성되어 있다.

관련 파일:

어떻게 동작하는가

  1. /auth/signup, /auth/login에서 JWT 발급
  2. JwtStrategy가 토큰에서 payload 해석
  3. JwtAuthGuard가 인증 여부 검사
  4. RolesGuard@Roles() 메타데이터를 읽어 권한 검사

왜 이렇게 설계했는가

Spring Security에서 자주 쓰는 "JWT 인증 + Role 기반 인가"를 NestJS에서도 비슷한 책임 분리로 가져가기 위해서다.

Spring 방식과 비슷한 점

  • SecurityFilterChain + JWT filter + @PreAuthorize("hasRole('ADMIN')")와 역할이 비슷하다.
  • 인증과 인가를 분리한다.

Spring 방식과 다른 점

  • NestJS는 Filter Chain보다 Guard 중심으로 생각하는 것이 자연스럽다.
  • 권한 메타데이터는 커스텀 decorator로 읽는다.

Spring Security 감각으로 해석하기

  • JwtStrategy = UserDetailsService + JWT parsing 일부 역할
  • JwtAuthGuard = 인증 필터 느낌
  • RolesGuard = 메서드 보안 검사기 느낌
  • @CurrentUser() = @AuthenticationPrincipal과 비슷

Validation / ExceptionHandler 대응

  • main.ts에서 ValidationPipe를 전역 등록해 Spring의 @Valid와 비슷하게 요청 검증을 적용했다.
  • HttpExceptionFilter는 Spring의 @ControllerAdvice + @ExceptionHandler 역할로 보면 된다.

5. 상품/장바구니/주문 API 구현

관련 파일:

5-1. 상품 API

  • GET /api/products
  • GET /api/products/:productId
  • POST /api/products

POST /api/productsADMIN 권한만 허용했다.

왜 이렇게 설계했는가

상품 조회는 공개, 상품 등록은 관리자 권한으로 제한하는 전형적인 커머스 규칙을 반영했다.

Spring 방식과 비슷한 점

  • @GetMapping, @PostMapping, @PreAuthorize 조합과 유사하다.

5-2. 장바구니 API

  • GET /api/cart
  • POST /api/cart/items

장바구니는 사용자별 1개를 기본으로 보고, 아이템을 추가하는 흐름으로 단순화했다.

왜 이렇게 설계했는가

처음 연습할 때는 장바구니 멀티 버전이나 쿠폰 결합까지 넣으면 복잡도가 너무 올라간다.
그래서 "회원별 단일 장바구니"라는 많이 쓰는 모델을 먼저 잡았다.

Spring 방식과 비슷한 점

  • Service 레이어에서 cart 조회 후 create or update 하는 패턴은 Spring에서도 흔하다.

5-3. 주문 API

  • POST /api/orders
  • GET /api/orders

주문 생성 흐름:

  1. 장바구니 조회
  2. 재고 검증
  3. 주문/주문아이템 저장
  4. 상품 재고 차감
  5. MongoDB에 주문 로그 기록
  6. 장바구니 비우기

왜 이렇게 설계했는가

이커머스 핵심은 "주문은 RDB 트랜잭션", "이력은 로그 저장소"의 분리다.
연습 프로젝트에서도 이 경계를 명확히 보는 것이 중요하다.

Spring 방식과 비슷한 점

  • @Transactional 서비스 메서드 안에서 주문 생성과 재고 차감을 묶는 사고방식과 비슷하다.

Spring 방식과 다른 점

  • 현재 예제 코드는 학습용이라 간단히 작성했다.
  • 실무라면 TypeORM transaction manager나 query runner로 주문 생성과 재고 차감을 하나의 트랜잭션으로 묶어야 한다.
  • Mongo 기록은 outbox/event-driven으로 분리하는 것도 좋다.

6. Docker 및 ECS/CodePipeline 배포 전략 정리

관련 파일:

6-1. 로컬 개발

  1. .env.example을 복사해 .env 생성
  2. docker compose up -d postgres mongo
  3. npm install
  4. npm run start:dev

6-2. Docker 이미지 전략

  • 멀티 스테이지 빌드 사용
  • build stage에서 TypeScript compile
  • runtime stage에서는 production dependency만 설치

왜 이렇게 설계했는가

NestJS는 결국 Node.js 앱이므로, Spring Boot jar 배포 대신 "컨테이너 이미지" 중심 운영이 더 자연스럽다.

Spring 방식과 비슷한 점

  • 결국 ECS에 올릴 때는 Spring Boot도 Docker 이미지로 다루는 경우가 많다.

Spring 방식과 다른 점

  • JVM 튜닝보다 Node 프로세스/메모리/이벤트 루프 관점 모니터링이 더 중요하다.

6-3. ECS 배포 전략

권장 흐름:

  1. GitHub Actions 또는 CodeBuild에서 테스트/빌드
  2. Docker 이미지 생성
  3. ECR push
  4. CodePipeline or CodeDeploy로 ECS 서비스 롤링 배포

권장 AWS 구성:

  • ECS Fargate
  • ALB
  • ECR
  • RDS PostgreSQL
  • DocumentDB 또는 MongoDB Atlas
  • CloudWatch Logs
  • Secrets Manager

6-4. CodePipeline 예시 단계

  1. Source: GitHub
  2. Build: CodeBuild
  3. Push: ECR
  4. Deploy: ECS

운영 시 추가 권장 사항

  • synchronize: true는 로컬 개발에서만 사용
  • 운영은 migration 기반으로 전환
  • refresh token 분리
  • Redis 캐시와 분산락 고려
  • 주문은 트랜잭션 + 재고 동시성 제어 필요
  • 로그/이력은 eventually consistent 구조 허용

TypeORM과 Prisma를 실제 선택한다면

TypeORM을 추천하는 경우

  • Spring JPA 감각으로 빨리 적응하고 싶을 때
  • Entity 중심 설계가 더 편할 때
  • 학습 초기에 "NestJS 구조"를 먼저 익히고 싶을 때

Prisma를 추천하는 경우

  • 타입 안정성이 특히 중요할 때
  • 스키마와 migration 흐름을 명확하게 관리하고 싶을 때
  • 팀이 명시적인 쿼리 구조를 선호할 때

지금 코드에서 꼭 알아둘 점

  • 이 프로젝트는 "학습용 시작점"이다.
  • 실무 수준으로 가려면 주문 트랜잭션, 재고 동시성, 결제, 쿠폰, 배송, 리뷰, 검색, 캐시를 확장해야 한다.
  • 그래도 Spring Boot 개발자가 NestJS 감각을 잡기에는 좋은 최소 단위로 구성했다.

실행 가이드

cp .env.example .env
docker compose up -d postgres mongo
npm install
npm run start:dev

현재 이 워크스페이스에서는 node, npm이 설치되어 있지 않아 실제 빌드 검증은 수행하지 못했다.
로컬에 Node.js 20 이상을 설치한 뒤 위 명령으로 바로 실행하면 된다.

다음 추천 학습 순서

  1. 지금 예제를 TypeORM 기준으로 직접 실행해보기
  2. 주문 생성에 transaction 적용해보기
  3. Prisma 버전으로 repository 계층 다시 작성해보기
  4. Redis 캐시와 재고 분산락 추가해보기
  5. 결제 모듈과 이벤트 기반 주문 이력 확장하기

7. Swagger 적용

Swagger UI는 앱 실행 후 아래 경로에서 확인할 수 있다.

  • http://localhost:3000/docs

왜 이렇게 설계했는가

Spring Boot에서 springdoc-openapi를 붙여 Swagger UI를 보는 것처럼, NestJS도 @nestjs/swagger로 API 문서를 바로 노출하는 편이 개발 속도가 좋다.
DTO에 예시값을 넣고 Controller에 태그와 설명을 달아두면, 프론트엔드나 테스트할 때 훨씬 빠르게 확인할 수 있다.

Spring 방식과 뭐가 비슷하고 다른지

  • 비슷한 점: @Operation, @Schema, @Tag 같은 문서화 감각이 거의 같다.
  • 다른 점: NestJS는 DTO decorator와 controller decorator 조합으로 문서가 생성되고, main.ts에서 SwaggerModule을 직접 부트스트랩한다.

사용 방법

  1. /auth/signup 또는 /auth/login으로 JWT를 발급받는다.
  2. Swagger UI 우측 상단 Authorize 버튼을 누른다.
  3. Bearer <accessToken> 형식으로 넣거나 토큰 값만 넣어 인증한다.
  4. 보호된 API인 /api/cart, /api/orders, POST /api/products를 테스트한다.

About

Spring Boot 대비 개념 매핑, TypeORM vs Prisma 비교, PostgreSQL/MongoDB 역할 분리, JWT + Guard + Role 인가 흐름

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors