본문 바로가기
개발/NestJS

NestJS 설정 관리의 진화: nestjs-library-config 도입기

by coking 2024. 11. 18.

안녕하세요! 오늘은 NestJS 프로젝트에서 환경 변수 관리를 더 효율적으로 할 수 있게 해주는 nestjs-library-config를 소개해드리려고 합니다. 프로젝트를 진행하다 보면 .env 파일이 점점 커지면서 "이 설정값이 대체 어디서 쓰이는 거지?" 하는 고민이 생기시죠? 저도 같은 고민을 했었고, 이를 해결하기 위해 도입한 방법을 공유해보려고 합니다. 😊

기존 설정 관리의 문제점

# 점점 커져가는 .env 파일...
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
REDIS_HOST=localhost
REDIS_PORT=6379
AWS_ACCESS_KEY=xxxxx
AWS_SECRET_KEY=xxxxx
EMAIL_SERVICE_API_KEY=xxxxx
JWT_SECRET=xxxxx
JWT_EXPIRATION=3600
... (끝없이 늘어나는 설정값들)

이런 경험 다들 있으시죠? 🤔

  1. "이 환경 변수가 어디서 쓰이는지 찾기 어려워..."
  2. "숫자인데 문자열로 들어오네? 매번 형변환 해줘야 하나..."
  3. "필수값인데 누가 설정 안 해놨네... 런타임 에러 발생!"

nestjs-library-config 시작하기

먼저 라이브러리를 설치해볼까요?

npm install @woowa/nestjs-library-config

AbstractConfigService - 설정 관리의 핵심 🔧

가장 먼저 이해해야 할 것은 AbstractConfigService입니다. 이 친구가 우리의 설정 관리를 편하게 만들어주는 핵심이에요!

"AbstractConfigService가 뭐가 좋은데요?" 🤔

다음과 같은 편리한 기능들을 모두 자동으로 제공합니다:

  1. 자동화된 초기화

    • 환경 변수 로드
    • 설정값 매핑
    • 값 변환
    • 유효성 검증
  2. 타입 안정성 보장

    • 컴파일 타임에 타입 체크
    • IDE 자동 완성 지원
  3. 확장 가능한 구조

    • 필요한 경우 커스텀 검증 로직 추가 가능

실전: 알림 서비스 설정 구현하기

실제 예시를 통해 어떻게 사용하는지 알아볼까요?

// notification.config.service.ts
import { AbstractConfigService } from '@nestjs-library/config';
import { Injectable } from '@nestjs/common';
import { Expose, Transform } from 'class-transformer';
import { IsNotEmpty, IsString } from 'class-validator';

@Injectable()
export class NotificationConfigService extends AbstractConfigService<NotificationConfigService> {
  @Expose({ name: 'KEY' })
  @Transform(({ value }) => value)
  @IsString()
  @IsNotEmpty()
  key: string;

  @Expose({ name: 'USERID' })
  @Transform(({ value }) => value)
  @IsString()
  @IsNotEmpty()
  userId: string;

  @Expose({ name: 'SENDERKEY' })
  @Transform(({ value }) => value)
  @IsString()
  @IsNotEmpty()
  senderKey: string;
}

모든 설정 한번에 등록하기

// app.module.ts
@Module({
  imports: [
    ConfigModule.register({
      configList: [NotificationConfigService, JwtConfigService],
    }),
    // 다른 모듈들...
  ],
})
export class AppModule {}

비동기 설정이 필요할 때는?

JWT 모듈처럼 비동기 설정이 필요한 경우도 깔끔하게 처리할 수 있습니다:

// jwt.module.ts
@Module({
  imports: [
    JwtModule.registerAsync({
      imports: [ConfigModule.forFeature(JwtConfigService)],
      inject: [JwtConfigService],
      useFactory: async (jwtConfigService: JwtConfigService) => ({
        secret: jwtConfigService.jwtAccessKeySecret,
        signOptions: { expiresIn: '60s' },
      }),
    }),
  ],
})
export class AuthModule {}

설정값 실제 사용하기

설정 클래스를 만들었다면, 실제 서비스에서 이렇게 사용할 수 있습니다:

// notification.service.ts
import { Injectable } from '@nestjs/common';
import { NotificationConfigService } from './notification.config.service';

@Injectable()
export class NotificationService {
  constructor(
    private readonly configService: NotificationConfigService,
  ) {}

  async sendNotification(message: string) {
    // 설정값들을 타입 안전하게 사용할 수 있습니다!
    const apiKey = this.configService.key;
    const userId = this.configService.userId;
    const senderKey = this.configService.senderKey;

    // 이제 이 값들을 사용해서 알림을 보내는 로직을 구현...
    console.log(`Sending notification with key: ${apiKey}, userId: ${userId}`);
  }
}

⭐ 실무 꿀팁!

  1. 자주 사용하는 설정값 캐싱하기

    @Injectable()
    export class NotificationService {
      private readonly apiKey: string;
    
      constructor(private readonly config: NotificationConfigService) {
        this.apiKey = config.key; // 자주 사용하는 값은 캐싱
      }
    }
  2. 환경별 설정 분기

    @Injectable()
    export class EmailService {
      constructor(private readonly config: EmailConfigService) {}
    
      async sendEmail() {
        if (this.config.isTestMode) {
          // 테스트 환경용 로직
          return;
        }
        // 실제 이메일 발송 로직
      }
    }

정리하며 🎉

nestjs-library-config를 도입하면서 얻은 장점들을 정리해보면:

  1. 설정값의 사용처가 명확해졌습니다

    • 각 모듈별로 필요한 설정이 명시적으로 드러나요
    • 어떤 설정이 어디서 사용되는지 쉽게 파악할 수 있죠
  2. 타입 안정성이 보장됩니다

    • 컴파일 타임에 오류를 잡을 수 있어요
    • IDE 자동 완성의 도움을 받을 수 있습니다
  3. 유효성 검증이 자동화되었습니다

    • 필수값 검증이 자동으로 이루어져요
    • 잘못된 타입의 값이 들어올 수 없습니다
  4. 코드 리뷰가 쉬워졌습니다

    • 설정 관련 변경사항을 한눈에 파악할 수 있어요
    • 각 모듈의 설정 의존성이 명확해졌죠

처음에는 "설정 관리를 이렇게까지?" 싶었지만, 프로젝트가 커질수록 이 방식이 주는 이점을 실감하게 될 거예요. 특히 여러 개발자가 함께 일하는 프로젝트에서는 더욱 빛을 발합니다! ✨

참고 자료

댓글