데이터 흐름

클라이언트 요청
  ↓
① Middleware (글로벌)
  ↓
② Guard (인증/인가)
  ↓
③ Interceptor (Before) - 요청 전처리
  ↓
④ Pipe (유효성 검증/변환)
  ↓
⑤ Controller Handler (실제 로직)
  ↓
⑥ Service (비즈니스 로직)
  ↓
⑦ Interceptor (After) - 응답 후처리
  ↓
⑧ Exception Filter (에러 처리)
  ↓
클라이언트 응답

시각적 흐름

Request
→ Middleware (CORS, 로깅)
→ Guard (JWT 검증, 인증/인가)
→ Interceptor (타이머 시작, 로깅, 변환, 캐싱)
→ Pipe (DTO 검증, 데이터 변환/검증)
→ Controller
→ Service
← Interceptor (타이머 종료, 응답 변환)
← Exception Filter (에러 처리)
Response

추천 폴더 구조

src/
├── common/                    # 공통 모듈
│   ├── filters/              # Exception Filter
│   ├── guards/               # Guard
│   ├── interceptors/         # Interceptor
│   ├── pipes/                # Custom Pipe
│   ├── decorators/           # Custom Decorator
│   └── utils/                # 유틸리티
├── config/                   # 설정 파일
│   ├── database.config.ts
│   └── app.config.ts
├── modules/                  # 기능별 모듈
│   ├── user/
│   │   ├── user.controller.ts
│   │   ├── user.service.ts
│   │   ├── user.module.ts
│   │   ├── dto/              # DTO
│   │   ├── entities/         # Entity
│   │   └── repositories/     # Repository
│   └── auth/
│       ├── auth.controller.ts
│       ├── auth.service.ts
│       ├── auth.module.ts
│       ├── strategies/       # Passport Strategy
│       └── guards/           # Auth Guard
├── database/                 # DB 관련
│   ├── migrations/
│   └── seeds/
├── main.ts
└── app.module.ts

주요 데코레이터 치트시트

Controller 관련

@Controller('users')           // 라우트 경로 정의
@Get(':id')                    // HTTP GET 메서드
@Post()                        // HTTP POST 메서드
@Put(':id')                    // HTTP PUT 메서드
@Patch(':id')                  // HTTP PATCH 메서드
@Delete(':id')                 // HTTP DELETE 메서드
 
@Param('id')                   // URL 파라미터
@Query('search')               // 쿼리 파라미터
@Body()                        // Request Body
@Headers('authorization')      // HTTP 헤더
@Req()                         // Request 객체 전체
@Res()                         // Response 객체 전체

DI 관련

@Injectable()                  // Provider로 등록
@Inject('TOKEN')               // 커스텀 토큰으로 주입
@Optional()                    // 선택적 의존성
 
@Module({
  imports: [],                 // 다른 모듈 가져오기
  controllers: [],             // 컨트롤러 등록
  providers: [],               // 프로바이더 등록
  exports: []                  // 다른 모듈에서 사용 가능하도록 export
})

Validation 관련

@IsString()                    // 문자열 검증
@IsNumber()                    // 숫자 검증
@IsEmail()                     // 이메일 검증
@IsNotEmpty()                  // 빈 값 방지
@MinLength(8)                  // 최소 길이
@MaxLength(20)                 // 최대 길이
@IsOptional()                  // 선택적 필드
@IsEnum(UserRole)              // Enum 검증
@ValidateNested()              // 중첩 객체 검증
@Type(() => Date)              // 타입 변환

보안/권한 관련

@UseGuards(AuthGuard)          // Guard 적용
@UseInterceptors(LoggingInterceptor)  // Interceptor 적용
@UsePipes(ValidationPipe)      // Pipe 적용
@UseFilters(HttpExceptionFilter)  // Exception Filter 적용
 
@SetMetadata('roles', ['admin'])  // 메타데이터 설정
@Public()                      // 커스텀 데코레이터 (인증 제외)

Provider Scope 동작 방식

DEFAULT (싱글톤)
├── 애플리케이션 시작 시 1번 생성
├── 모든 요청에서 동일한 인스턴스 공유
└── 가장 일반적이고 성능이 좋음

REQUEST (요청마다)
├── 각 HTTP 요청마다 새 인스턴스 생성
├── 요청 종료 시 인스턴스 소멸
├── 요청별 격리된 상태 필요 시 사용
└── 성능 오버헤드 있음

TRANSIENT (임시)
├── 주입될 때마다 새 인스턴스 생성
├── 인스턴스 공유 안 됨
└── 특수한 경우에만 사용

Scope 설정 예시

@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {}
 
@Injectable({ scope: Scope.TRANSIENT })
export class TransientService {}

모듈 간 의존성 패턴

CoreModule (Global)
  ├── DatabaseModule
  ├── ConfigModule
  └── LoggerModule
       ↓ (모든 모듈에서 사용 가능)

AuthModule
  ├── imports: [UserModule]
  └── exports: [AuthService, JwtStrategy]
       ↓

UserModule
  ├── imports: [AuthModule]  ❌ 순환 참조!
  └── 해결: forwardRef(() => AuthModule)

SharedModule
  ├── CommonService
  ├── UtilityService
  └── exports: [CommonService, UtilityService]
       ↓ (필요한 모듈에서 import)

FeatureModule1, FeatureModule2, ...

라이프사이클 훅 순서

애플리케이션 시작
  ↓
1. onModuleInit()        // 모듈 의존성 해결 후
  ↓
2. onApplicationBootstrap()  // 모든 모듈 초기화 완료 후
  ↓
[애플리케이션 실행 중]
  ↓
3. onModuleDestroy()     // 종료 신호 받은 후
  ↓
4. beforeApplicationShutdown()  // 연결 종료 전
  ↓
5. onApplicationShutdown()  // 종료 처리
  ↓
애플리케이션 종료

라이프사이클 훅 구현 예시

@Injectable()
export class UsersService implements OnModuleInit, OnModuleDestroy {
  onModuleInit() {
    console.log('UsersService initialized');
  }
 
  onModuleDestroy() {
    console.log('UsersService cleanup');
  }
}

HTTP 상태 코드 가이드

성공 (2xx)
├── 200 OK                 - 일반적인 성공
├── 201 Created            - 리소스 생성 성공
├── 204 No Content         - 성공, 응답 바디 없음 (DELETE 등)

클라이언트 에러 (4xx)
├── 400 Bad Request        - 잘못된 요청 (validation 실패)
├── 401 Unauthorized       - 인증 실패 (로그인 필요)
├── 403 Forbidden          - 권한 없음 (인가 실패)
├── 404 Not Found          - 리소스 없음
├── 409 Conflict           - 충돌 (중복 등)
├── 422 Unprocessable Entity  - 처리 불가능한 요청

서버 에러 (5xx)
├── 500 Internal Server Error  - 서버 내부 오류
├── 502 Bad Gateway        - 게이트웨이 오류
└── 503 Service Unavailable  - 서비스 사용 불가

아키텍처 & 모듈 구조화

모듈 설계

  • 기능별로 모듈 분리 (UserModule, AuthModule 등)
  • CoreModule에 공통 기능 집중 (DB, Logger 등)
  • SharedModule로 공통 서비스/유틸 공유
  • 순환 참조 방지 - forwardRef() 사용 최소화

계층 구조

  • Controller: 라우팅 및 요청/응답만 처리
  • Service: 비즈니스 로직 집중
  • Repository: 데이터 접근 로직 분리
  • DTO: 입출력 데이터 타입 정의

의존성 주입 (DI)

  • Constructor 기반 DI 사용
  • Provider scope 이해 (DEFAULT, REQUEST, TRANSIENT)
  • Custom Provider 활용 (useClass, useValue, useFactory)
  • 인터페이스 기반 설계로 테스트 용이성 확보

에러 처리 & 유효성 검증

Exception Handling

  • 전역 Exception Filter 구현
  • HTTP 표준 예외 사용 (BadRequestException, NotFoundException 등)
  • 커스텀 Exception 클래스 정의
  • 에러 로깅 및 모니터링 연동

DTO & Validation

  • class-validator 데코레이터 활용
  • class-transformer로 타입 변환
  • ValidationPipe 전역 설정
  • 입력값 화이트리스트 설정 (whitelist: true)
  • 알 수 없는 속성 거부 (forbidNonWhitelisted: true)

Pipe 활용

  • ParseIntPipe, ParseUUIDPipe 등 빌트인 Pipe 사용
  • 커스텀 Pipe로 복잡한 변환 로직 처리
  • 파라미터/쿼리/바디 각각 적절한 검증

보안 & 인증/인가

인증 (Authentication)

  • Passport 전략 활용 (JWT, Local 등)
  • 비밀번호 해싱 (bcrypt)
  • Refresh Token 전략 구현
  • 토큰 만료 시간 적절히 설정

인가 (Authorization)

  • Guard로 권한 검증
  • Role-based 또는 Policy-based 접근 제어
  • 커스텀 데코레이터로 메타데이터 정의 (@Roles())
  • Reflector로 메타데이터 조회

보안 Best Practices

  • Helmet 미들웨어로 HTTP 헤더 보안
  • CORS 설정 명확히
  • Rate Limiting (throttler) 적용
  • SQL Injection 방지 (ORM 파라미터 바인딩)
  • XSS 방지 (입력값 sanitization)
  • 민감 정보 환경변수로 관리 (ConfigModule)

데이터베이스 & ORM

TypeORM / Prisma 패턴

  • Entity 정의 시 관계 명확히 설정
  • 마이그레이션 파일로 스키마 버전 관리
  • Repository 패턴 활용 (TypeORM의 경우)
  • 쿼리 빌더 vs Raw Query 적절히 선택

트랜잭션

  • 데이터 일관성 필요 시 트랜잭션 사용
  • QueryRunner 또는 @Transactional 데코레이터 활용
  • 트랜잭션 범위 최소화

성능 최적화

  • N+1 문제 해결 (eager loading, join)
  • 인덱스 적절히 설정
  • 페이지네이션 구현
  • 커넥션 풀 설정 최적화

TypeScript 사용 주의사항

타입 안정성

  • any 타입 사용 최소화
  • strict 모드 활성화 (tsconfig.json)
  • 명시적 타입 정의 (함수 반환 타입 포함)
  • Generic 적극 활용

NestJS 타입 활용

  • DTO 클래스에 타입 명확히
  • Partial, Pick, Omit 유틸리티 타입 활용
  • 데코레이터 타입 추론 활용
  • Request, Response 타입 명시 (@Req(), @Res())

Interface vs Type vs Class

  • DTO는 class 사용 (validation 데코레이터)
  • 서비스 인터페이스는 interface
  • Union/Intersection은 type alias
  • Entity는 class (ORM 데코레이터)

개발 체크리스트

새 기능 개발 시

  1. DTO 정의 및 validation 규칙 설정
  2. Service에 비즈니스 로직 작성
  3. Controller에 엔드포인트 정의
  4. Guard/Interceptor 필요 시 적용
  5. 단위 테스트 작성
  6. 통합 테스트 작성

배포 전 확인

  • 환경변수 모두 설정됐는지
  • 마이그레이션 실행 완료
  • 에러 로깅 정상 작동
  • 성능 테스트 완료
  • 보안 취약점 검토