민감한 정보를 관리하기위해 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
메소드를 통해 가져올수있다.
추후 실서버/개발/테스트 환경을 나누며 테스트해야하므로 NODE_ENV
를 이용해야한다.
Jest 를 통해 테스트할경우, jest 는 항상 NODE_ENV
를 test로 셋팅한다.
.
로 되어잇음 -> 'test'폴더그자체../
로 바꿈- NENE 폴더 그자체를 테스트할수있게끔modulePaths
: 어떤 모듈을 테스트할건지?<rootDir>
: all the modules which inside the root directory.package.json 파일
./
==> 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 {}
쨘 완성
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 {}
위에 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'이기 때문에 위에처럼 쓸 수 있당.
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 어쩌구
}
}
팀원들끼리 .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가 없다면
서버 실행하자마자 이런 에러가 바로뜬당.