🎉 berenickt 블로그에 온 걸 환영합니다. 🎉
Back
NestJs
23-Config 모듈-환경변수

1. ENV 파일 작성

1.1 환경변수

  • 환경변수 : 프로그램이 동작하는 환경에서 설정값이나 비밀 정보를 저장하고 사용하는 변수
  • 환경변수의 중요성
    • [보안] API 키, 데이터베이스 비밀번호 등 민감한 정보를 코드에 직접 작성하지 않고 환경변수로 처리 할 수 있다
    • [유연성] 애플리케이션을 다양한 환경 (개발, 테스트, 운영)에서 쉽게 설정하고 실행할 수 있도록 도와준다
    • [유지보수성] 설정 변경 시 코드 수정 없이 환경변수 파일을 통해 간단히 업데이트 할 수 있어 유지보수성을 높인다
    • [통일성] 동일한 애플리케이션을 여러 환경에서 일관된 방식으로 배포하고 운영 할 수 있도록 한다
  • 환경변수 사용처
    • 데이터베이스 연결 정보
    • JWT 토큰 시크릿
    • API 키
    • 애플리케이션 포트
    • 환경 구분 (개발, 테스트, 운영)

1.2 환경변수 파일

NestJS에서는 환경변수를 쉽게 다루도록 config 모듈을 제공한다.

1
yarn add @nestjs/config

루트파일에 .env을 만든다.

1
JWT_SECRET=berenickt
2
HASH_ROUNDS=10
3
PROTOCOL=http
4
HOST=localhost:3000
5
6
DB_HOST=127.0.0.1
7
DB_PORT=5808
8
DB_USERNAME=postgres
9
DB_PASSWORD=postgres
10
DB_DATABASE=postgres
  • gitignore에 .env를 등록하면, github에 제외된다.
  • auth/const/auth.const.ts
  • common/const/env.const.ts
  • app.module.ts
    • 의 모든 값들을 env에 정의한다.

1.3 사용법

(1) 환경변수 모듈 등록

app.module.ts
1
import { ConfigModule } from '@nestjs/config'
2
3
@Module({
4
imports: [
5
PostsModule,
6
ConfigModule.forRoot({
7
envFilePath: '.env', // ConfigModule을 사용해 .env 파일을 불러온다.
8
isGlobal: true, // ConfigModule을 전역으로 사용한다.
9
}),
10
],
11
})
12
export class AppModule {}

(2) 환경변수 사용

1
import { ConfigService } from '@nestjs/config'
2
3
@Injectable()
4
export class AppService {
5
constructor(private configService: ConfigService) {}
6
7
getDatabaseHost(): string {
8
return this.configService.get<string>('DB_HOST')
9
}
10
11
getAPIKey(): string {
12
return this.configService.get<string>('EXTERNAL_API_KEY')
13
}
14
}

2. 환경변수 적용 - 1

app.module.ts에서 config 모듈을 불러온다.

app.module.ts {1:3}
1
import { ClassSerializerInterceptor, Module } from '@nestjs/common'
2
import { TypeOrmModule } from '@nestjs/typeorm'
3
4
import { APP_INTERCEPTOR } from '@nestjs/core'
5
import { AppController } from './app.controller'
6
import { AppService } from './app.service'
7
import { PostsModule } from './posts/posts.module'
8
import { PostsModel } from './posts/entities/posts.entity'
9
import { UsersModule } from './users/users.module'
10
import { UsersModel } from './users/entities/users.entity'
11
import { AuthModule } from './auth/auth.module'
12
import { CommonModule } from './common/common.module'
13
import { ConfigModule } from '@nestjs/config' // 추가
14
15
@Module({
16
imports: [
17
PostsModule,
18
ConfigModule.forRoot({
19
envFilePath: '.env', // ConfigModule을 사용해 .env 파일을 불러온다.
20
isGlobal: true, // ConfigModule을 전역으로 사용한다.
21
}),
22
TypeOrmModule.forRoot({
23
// 데이터베이스 타입
24
type: 'postgres',
25
host: '127.0.0.1',
26
port: 5808,
27
username: 'postgres',
28
password: 'postgres',
29
database: 'postgres',
30
// entities폴더에 작성한 PostsModel 가져오기
31
entities: [PostsModel, UsersModel],
32
synchronize: true,
33
}),
34
UsersModule,
35
AuthModule,
36
CommonModule,
37
],
38
controllers: [AppController],
39
providers: [
40
AppService,
41
{
42
provide: APP_INTERCEPTOR,
43
useClass: ClassSerializerInterceptor,
44
},
45
],
46
})
47
export class AppModule {}

common/const/env-keys.const.ts 파일을 만든다.

common/const/env-keys.const.ts
1
// 서버 프로토콜 -> http / https
2
export const ENV_PROTOCOL_KEY = 'PROTOCOL'
3
// 서버 호스트 -> localhost:3000
4
export const ENV_HOST_KEY = 'HOST'
5
// JWT 토큰 시크릿 -> berenickt
6
export const ENV_JWT_SECRET_KEY = 'JWT_SECRET'
7
// JWT 토큰 해시 라운드 수 -> 10
8
export const ENV_HASH_ROUNDS_KEY = 'HASH_ROUNDS'
9
// 데이터베이스 호스트 -> localhost
10
export const ENV_DB_HOST_KEY = 'DB_HOST'
11
// 데이터베이스 포트 -> 5808
12
export const ENV_DB_PORT_KEY = 'DB_PORT'
13
// 데이터베이스 사용자 이름 -> postgres
14
export const ENV_DB_USERNAME_KEY = 'DB_USERNAME'
15
// 데이터베이스 사용자 비밀번호 -> postgres
16
export const ENV_DB_PASSWORD_KEY = 'DB_PASSWORD'
17
// 데이터베이스 이름 -> postgres
18
export const ENV_DB_DATABASE_KEY = 'DB_DATABASE'

auth.const.ts에서 각각의 변수에 오른쪽 마우스 클릭해서

  • go to reference(정의로 이동)을 누르면,
  • 현재 해당 변수를 참조하는 모든 파일을 볼 수 있다.
  • 아니면 auth.const.ts를 지우고, 찾아서 import를 다시 하면 된다.
  • auth 서비스에 env-keys들로 값을 수정한다.
auth.service.ts
1
// 생략
2
@Injectable()
3
export class AuthService {
4
// 생략
5
verifyToken(token: string) {
6
try {
7
return this.jwtService.verify(token, {
8
secret: this.configService.get<string>(ENV_JWT_SECRET_KEY),
9
})
10
} catch (err) {
11
throw new UnauthorizedException('토큰이 만료됏거나 잘못된 토큰입니다.')
12
}
13
}
14
15
rotateToken(token: string, isRefreshToken: boolean) {
16
const decoded = this.jwtService.verify(token, {
17
secret: this.configService.get<string>(ENV_JWT_SECRET_KEY),
18
})
19
// 생략
20
}
21
22
async registerWithEmail(user: RegisterUserDto) {
23
const hash = await bcrypt.hash(
24
user.password, //
25
parseInt(this.configService.get<string>(ENV_HASH_ROUNDS_KEY)),
26
)
27
// 생략
28
}
29
}

3. 환경변수 적용 - 2

이제 common/const/env.const.ts 파일은 삭제한다. 마찬가지로 에러난 곳에 가서 import를 수정한다.

common.service.ts
1
// 생략
2
@Injectable()
3
export class CommonService {
4
constructor(private readonly configService: ConfigService) {}
5
// 생략
6
private async cursorPaginate<T extends BaseModel>() {
7
// 생략
8
// 생략
9
const lastItem = data.length > 0 && data.length === dto.take ? data[data.length - 1] : null
10
const PROTOCOL = this.configService.get<string>(ENV_PROTOCOL_KEY)
11
const HOST = this.configService.get<string>(ENV_HOST_KEY)
12
const nextUrl = lastItem && new URL(`${PROTOCOL}://${HOST}/${path}`)
13
// 생략
14
}
15
}

posts 서비스에도 마찬가지로 configService만들고, env 적용한다.

posts.service.ts
1
// 생략
2
3
@Injectable()
4
export class PostsService {
5
constructor(
6
@InjectRepository(PostsModel)
7
private readonly postsRepository: Repository<PostsModel>,
8
private readonly commonService: CommonService,
9
private readonly configService: ConfigService,
10
) {}
11
// 생략
12
13
async cursorPaginatePosts(dto: PaginatePostDto) {
14
// 생략
15
const lastItem = posts.length > 0 && posts.length === dto.take ? posts[posts.length - 1] : null
16
const PROTOCOL = this.configService.get<string>(ENV_PROTOCOL_KEY)
17
const HOST = this.configService.get<string>(ENV_HOST_KEY)
18
const nextUrl = lastItem && new URL(`${PROTOCOL}://${HOST}/posts`)
19
// 생략
20
}
21
}

4. process 객체를 이용해 환경변수 불러오기

app 모듈에서 config 모듈을 쓰니, process 객체로 환경변수를 쓴다.

app.module.ts
1
// app.module.ts 생략
2
@Module({
3
imports: [
4
PostsModule,
5
ConfigModule.forRoot({
6
envFilePath: '.env',
7
isGlobal: true,
8
}),
9
TypeOrmModule.forRoot({
10
// 데이터베이스 타입
11
type: 'postgres',
12
host: process.env[ENV_DB_HOST_KEY],
13
port: parseInt(process.env[ENV_DB_PORT_KEY]),
14
username: process.env[ENV_DB_USERNAME_KEY],
15
password: process.env[ENV_DB_PASSWORD_KEY],
16
database: process.env[ENV_DB_DATABASE_KEY],
17
// entities폴더에 작성한 PostsModel 가져오기
18
entities: [PostsModel, UsersModel],
19
synchronize: true,
20
}),
21
UsersModule,
22
AuthModule,
23
CommonModule,
24
],
25
controllers: [AppController],
26
providers: [
27
AppService,
28
{
29
provide: APP_INTERCEPTOR,
30
useClass: ClassSerializerInterceptor,
31
},
32
],
33
})
34
export class AppModule {}