Nest.js - config

크롱·2024년 10월 25일
0

Nest.js

목록 보기
10/15
post-thumbnail

ConfigModule

민감한 정보를 관리하기위해 config module을 사용해보자

app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    
    ... 
    
    ConfigModule.forRoot({
      isGlobal: true, //다른 모듈에서 config import 안해도됨
    }),
 
})
export class AppModule {}

config 모듈은 .env파일과 연결되어있어서
이제 service 파일에서 configService 를 주입한 뒤에 사용가능하다

.env

S3_BUCKET="EXAMPLE"


users.service.ts


import { ConfigService } from '@nestjs/config';

@Injectable()
export class UsersService {
  constructor(
    // Injecting ConfigService
    private readonly configService: ConfigService,
  ) {}


  public findAll(
    getUsersParamDto: GetUsersParamDto,
    limit: number,
    page: number,
  ) {
      
    const envExample = this.configService.get<string>('S3_BUCKET');  
    return envExample
  }

}

get 메소드를 통해 가져올수있다.


Jest

추후 실서버/개발/테스트 환경을 나누며 테스트해야하므로 NODE_ENV를 이용해야한다.
Jest 를 통해 테스트할경우, jest 는 항상 NODE_ENV를 test로 셋팅한다.

  • 처음에 rootDir : . 로 되어잇음 -> 'test'폴더그자체
  • ../ 로 바꿈- NENE 폴더 그자체를 테스트할수있게끔
  • modulePaths : 어떤 모듈을 테스트할건지?
    => <rootDir> : all the modules which inside the root directory.

package.json 파일

  • jest 에 "rootDir": ./ ==> NENE 폴더 그자체를 테스트할수있게끔


test/app.e2e-spec.ts

describe('AppController (e2e)', () => {
  let app: INestApplication;

 ...
 
  it('/ (GET)', () => {
    console.log(process.env.NODE_ENV); // npm run test:e2e - 결과: test
    // jest 는 실행될때 process.env.NODE_ENV의 값을 test 로 항상 값을 보냄
    return request(app.getHttpServer()).get('/').expect(404);
  });
});

jest 실행할때 진짜 NODE_ENV 가 test인지확인하려고한다.

package.json 파일

"test:e2e": "jest --config ./test/jest-e2e.json"

npm run test:e2e 해보면

진짜임!
이제 개발/실서버일때 어떻게 활용하면 될지 알아봅쉬다?



개발 환경에 따라 설정하기

package.json 파일

"start:dev": "NODE_ENV=development nest start --watch",

dev 모드일때 스크립트에 NODE_ENV=development설정을 해두자

import { ConfigModule } from '@nestjs/config';

const ENV = process.env.NODE_ENV;

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      // envFilePath:['.env.development']
      envFilePath: !ENV ? '.env' : `.env.${ENV}`,
    }),

.env.development
.env.test
이렇게 개발환경에 따라 파일을 나누면됨


만약 터미널에

npm run start:dev

로 NODE_ENV 가 development되게 하면 '.env.development' 에 연동 되므로

이렇게 Db설정값들을 '.env.development'에 설정해두면

app.module.ts

import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';

const ENV = process.env.NODE_ENV;

@Module({
  imports: [

    ConfigModule.forRoot({
      isGlobal: true,
      // envFilePath:['.env.development']
      envFilePath: !ENV ? '.env' : `.env.${ENV}`,
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        type: 'postgres',
        //entities: [User],
       ...
        port: +configService.get('DATABASE_PORT'), // +는 숫자로 변환
        username: configService.get('DATABASE_USER'),
        password: configService.get('DATABASE_PASSWORD'),
        host: configService.get('DATABASE_HOST'),
        database: configService.get('DATABASE_NAME'),
      }),
    }),
    TagsModule,
    MetaOptionsModule,
  ],
  
})
export class AppModule {}

쨘 완성



Custom configuration

src/config 폴더안에 object를 반환하는 appConfig 파일을 만들어보자

👆👆 왜 configService.get를 안썻냐면,
이 파일 자체가 configModule 안에서 쓰일것이기 때문에 이 경우 process.env 를 쓰는 것은 괜찮지만 service 파일 등 다른곳 에서는 무조건 configService 를 사용해야한다. 오류가 꽤나 있나봄


이제 이 파일을 configModule안에 사용해보자
app.module.ts 로 이동해서
이 appConfig 파일을 import 해온뒤, load 라는 프로퍼티에서 이 파일이 있다는걸 명시해야한다.
그리고 get인자는 appConfig 파일 return 값 object에 있는 data를 가져오면됨.


app.module.ts

import { ConfigModule, ConfigService } from '@nestjs/config';
import { appConfig } from './config/app.config';

@Module({
  imports: [
	...
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: !ENV ? '.env' : `.env.${ENV}`,
      load: [appConfig], //추가됨
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        type: 'postgres',
        autoLoadEntities: configService.get('database.autoLoadEntities'), 
        synchronize: configService.get('database.synchronize'),
        port: +configService.get('database.port'),
        username: configService.get('database.user'),
        password: configService.get('database.password'),
        host: configService.get('database.host'),
        database: configService.get('database.name'),
      }),
    }),
  ],
  ...
})
export class AppModule {}



Namespace

위에 appConfig 오브젝트에 environment와 database를 한꺼번에 넣었는데 만약 분리하고싶다면?

registerAs를 사용해보자

config/db.config.ts

import { registerAs } from '@nestjs/config';

export default registerAs('database', () => ({
  host: process.env.DATABASE_HOST || 'localhost',
  port: parseInt(process.env.DATABASE_PORT) || 5432,
  user: process.env.DATABASE_USER,
  password: process.env.DATABASE_PASSWORD,
  name: process.env.DATABASE_NAME,
  synchronize: process.env.DATABASE_SYNC === 'true' ? true : false,
  autoLoadEntities: process.env.DATABASE_AUTOLOAD === 'true' ? true : false,
}));

app.module.ts 로 와서 위 파일을 import하고 load 에 추가해주면된다


app.module.ts

import databaseConfig from './config/db.config';

@Module({
  imports: [
     ConfigModule.forRoot({
      ..
      load: [appConfig, databaseConfig],
     
    }),
	TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
       	...
        port: +configService.get('database.port'),
        username: configService.get('database.user'),
        password: configService.get('database.password'),
        host: configService.get('database.host'),
        database: configService.get('database.name'),
      }),
    }),
   ]

registerAs 에서 이름이 'database'이기 때문에 위에처럼 쓸 수 있당.


Partial registration

With partial registration it's possible to have module specific configuration files.
if you want to have a configuration file just for the user's module and you can do that as well.



예를들어 스케일 큰 서비스에서, 구글계정의 프로필을 불러일으키려고 google api_key 를 이용한다고 생각할때 이 api_key는 오직 userModule에서만쓰고싶다면?

users폴더에 config 파일을 registerAs를 사용해서 만들자

파일: users/config/profile.config.ts

import { registerAs } from '@nestjs/config';

export default registerAs('profileConfig', () => ({
  apiKey: process.env.PROFILE_API_KEY,
}));

users Module 파일로 간 뒤 위 파일을 설정해야한다

파일: users/users.module.ts

import profileConfig from './config/profile.config';
import { ConfigModule } from '@nestjs/config';

@Module({
  ...
  imports: [
    ...
    ConfigModule.forFeature(profileConfig),
  ],
})
export class UsersModule {}

이 파일은 app.module.ts에 inject하면안된다. 다른모듈에서도접근가능하니까!
우린 usermodule에만 접근하고싶음

그다음 실제로 해당 api_key를 사용하고싶으면 users.service.ts에서 inject 하면된다

파일: .env

PROFILE_API_KEY="somevalue"


파일: users.service.ts


import { ConfigService, ConfigType } from '@nestjs/config';
import profileConfig from '../config/profile.config';

@Injectable()
export class UsersService {
  constructor(
    ...

    @Inject(profileConfig.KEY) //보일러플레이트 코드임
    private readonly profileConfiguration: ConfigType<typeof profileConfig>,
  ) {}


  /**
   * The method to get all the users from the DB
   */
  public findAll(
    getUsersParamDto: GetUsersParamDto,
    limit: number,
    page: number,
  ) {
    
    //test profileCOnfig
    console.log(this.profileConfiguration); // { apiKey: 'somevalue' }
    console.log(this.profileConfiguration.apiKey); // 이렇게 사용가넝

    return 어쩌구
  }
 
}

Joi - validation for environment variables

팀원들끼리 .env 파일을 공유하다보면 실수로 빼먹어서 오류가 나는 경우가 있는데, 이를 방지하기 위해 Joi를 사용해서 validate 해보자

파일: config/env.validation

import * as Joi from 'joi';

export default Joi.object({
  NODE_ENV: Joi.string()
    .valid('development', 'test', 'production', 'staging')
    .default('development'),
  DATABASE_PORT: Joi.number().port().default(5432),
  DATABASE_USER: Joi.string().required(),
  ...
  PROFILE_API_KEY: Joi.string().required(),
});

app.module.ts 로 와서 위 파일을 import 하고 ConfigModule.forRoot 내부 validationSchema에 넣어준뒤

파일: app.module.ts 

import envValidation from './config/env.validation';

@Module({
  imports: [
	...
    ConfigModule.forRoot({
      isGlobal: true,
      ...
      validationSchema: envValidation,
    }),

만약 .env 파일에 PROFILE_API_KEY가 없다면

서버 실행하자마자 이런 에러가 바로뜬당.

profile
👩‍💻안녕하세요🌞

0개의 댓글