1. ENV 파일 작성
1.1 환경변수
환경변수: 프로그램이 동작하는 환경에서 설정값이나 비밀 정보를 저장하고 사용하는 변수- 환경변수의 중요성
- [보안] API 키, 데이터베이스 비밀번호 등 민감한 정보를 코드에 직접 작성하지 않고 환경변수로 처리 할 수 있다
- [유연성] 애플리케이션을 다양한 환경 (개발, 테스트, 운영)에서 쉽게 설정하고 실행할 수 있도록 도와준다
- [유지보수성] 설정 변경 시 코드 수정 없이 환경변수 파일을 통해 간단히 업데이트 할 수 있어 유지보수성을 높인다
- [통일성] 동일한 애플리케이션을 여러 환경에서 일관된 방식으로 배포하고 운영 할 수 있도록 한다
- 환경변수 사용처
- 데이터베이스 연결 정보
- JWT 토큰 시크릿
- API 키
- 애플리케이션 포트
- 환경 구분 (개발, 테스트, 운영)
1.2 환경변수 파일
NestJS에서는 환경변수를 쉽게 다루도록 config 모듈을 제공한다.
1yarn add @nestjs/config
루트파일에 .env을 만든다.
1JWT_SECRET=berenickt2HASH_ROUNDS=103PROTOCOL=http4HOST=localhost:300056DB_HOST=127.0.0.17DB_PORT=58088DB_USERNAME=postgres9DB_PASSWORD=postgres10DB_DATABASE=postgres
- gitignore에
.env를 등록하면, github에 제외된다. auth/const/auth.const.tscommon/const/env.const.tsapp.module.ts- 의 모든 값들을 env에 정의한다.
1.3 사용법
(1) 환경변수 모듈 등록
app.module.ts
1import { ConfigModule } from '@nestjs/config'23@Module({4imports: [5PostsModule,6ConfigModule.forRoot({7envFilePath: '.env', // ConfigModule을 사용해 .env 파일을 불러온다.8isGlobal: true, // ConfigModule을 전역으로 사용한다.9}),10],11})12export class AppModule {}
(2) 환경변수 사용
1import { ConfigService } from '@nestjs/config'23@Injectable()4export class AppService {5constructor(private configService: ConfigService) {}67getDatabaseHost(): string {8return this.configService.get<string>('DB_HOST')9}1011getAPIKey(): string {12return this.configService.get<string>('EXTERNAL_API_KEY')13}14}
2. 환경변수 적용 - 1
app.module.ts에서 config 모듈을 불러온다.
app.module.ts {1:3}
1import { ClassSerializerInterceptor, Module } from '@nestjs/common'2import { TypeOrmModule } from '@nestjs/typeorm'34import { APP_INTERCEPTOR } from '@nestjs/core'5import { AppController } from './app.controller'6import { AppService } from './app.service'7import { PostsModule } from './posts/posts.module'8import { PostsModel } from './posts/entities/posts.entity'9import { UsersModule } from './users/users.module'10import { UsersModel } from './users/entities/users.entity'11import { AuthModule } from './auth/auth.module'12import { CommonModule } from './common/common.module'13import { ConfigModule } from '@nestjs/config' // 추가1415@Module({16imports: [17PostsModule,18ConfigModule.forRoot({19envFilePath: '.env', // ConfigModule을 사용해 .env 파일을 불러온다.20isGlobal: true, // ConfigModule을 전역으로 사용한다.21}),22TypeOrmModule.forRoot({23// 데이터베이스 타입24type: 'postgres',25host: '127.0.0.1',26port: 5808,27username: 'postgres',28password: 'postgres',29database: 'postgres',30// entities폴더에 작성한 PostsModel 가져오기31entities: [PostsModel, UsersModel],32synchronize: true,33}),34UsersModule,35AuthModule,36CommonModule,37],38controllers: [AppController],39providers: [40AppService,41{42provide: APP_INTERCEPTOR,43useClass: ClassSerializerInterceptor,44},45],46})47export class AppModule {}
common/const/env-keys.const.ts 파일을 만든다.
common/const/env-keys.const.ts
1// 서버 프로토콜 -> http / https2export const ENV_PROTOCOL_KEY = 'PROTOCOL'3// 서버 호스트 -> localhost:30004export const ENV_HOST_KEY = 'HOST'5// JWT 토큰 시크릿 -> berenickt6export const ENV_JWT_SECRET_KEY = 'JWT_SECRET'7// JWT 토큰 해시 라운드 수 -> 108export const ENV_HASH_ROUNDS_KEY = 'HASH_ROUNDS'9// 데이터베이스 호스트 -> localhost10export const ENV_DB_HOST_KEY = 'DB_HOST'11// 데이터베이스 포트 -> 580812export const ENV_DB_PORT_KEY = 'DB_PORT'13// 데이터베이스 사용자 이름 -> postgres14export const ENV_DB_USERNAME_KEY = 'DB_USERNAME'15// 데이터베이스 사용자 비밀번호 -> postgres16export const ENV_DB_PASSWORD_KEY = 'DB_PASSWORD'17// 데이터베이스 이름 -> postgres18export 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()3export class AuthService {4// 생략5verifyToken(token: string) {6try {7return this.jwtService.verify(token, {8secret: this.configService.get<string>(ENV_JWT_SECRET_KEY),9})10} catch (err) {11throw new UnauthorizedException('토큰이 만료됏거나 잘못된 토큰입니다.')12}13}1415rotateToken(token: string, isRefreshToken: boolean) {16const decoded = this.jwtService.verify(token, {17secret: this.configService.get<string>(ENV_JWT_SECRET_KEY),18})19// 생략20}2122async registerWithEmail(user: RegisterUserDto) {23const hash = await bcrypt.hash(24user.password, //25parseInt(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()3export class CommonService {4constructor(private readonly configService: ConfigService) {}5// 생략6private async cursorPaginate<T extends BaseModel>() {7// 생략8// 생략9const lastItem = data.length > 0 && data.length === dto.take ? data[data.length - 1] : null10const PROTOCOL = this.configService.get<string>(ENV_PROTOCOL_KEY)11const HOST = this.configService.get<string>(ENV_HOST_KEY)12const nextUrl = lastItem && new URL(`${PROTOCOL}://${HOST}/${path}`)13// 생략14}15}
posts 서비스에도 마찬가지로 configService만들고, env 적용한다.
posts.service.ts
1// 생략23@Injectable()4export class PostsService {5constructor(6@InjectRepository(PostsModel)7private readonly postsRepository: Repository<PostsModel>,8private readonly commonService: CommonService,9private readonly configService: ConfigService,10) {}11// 생략1213async cursorPaginatePosts(dto: PaginatePostDto) {14// 생략15const lastItem = posts.length > 0 && posts.length === dto.take ? posts[posts.length - 1] : null16const PROTOCOL = this.configService.get<string>(ENV_PROTOCOL_KEY)17const HOST = this.configService.get<string>(ENV_HOST_KEY)18const nextUrl = lastItem && new URL(`${PROTOCOL}://${HOST}/posts`)19// 생략20}21}
4. process 객체를 이용해 환경변수 불러오기
app 모듈에서 config 모듈을 쓰니, process 객체로 환경변수를 쓴다.
app.module.ts
1// app.module.ts 생략2@Module({3imports: [4PostsModule,5ConfigModule.forRoot({6envFilePath: '.env',7isGlobal: true,8}),9TypeOrmModule.forRoot({10// 데이터베이스 타입11type: 'postgres',12host: process.env[ENV_DB_HOST_KEY],13port: parseInt(process.env[ENV_DB_PORT_KEY]),14username: process.env[ENV_DB_USERNAME_KEY],15password: process.env[ENV_DB_PASSWORD_KEY],16database: process.env[ENV_DB_DATABASE_KEY],17// entities폴더에 작성한 PostsModel 가져오기18entities: [PostsModel, UsersModel],19synchronize: true,20}),21UsersModule,22AuthModule,23CommonModule,24],25controllers: [AppController],26providers: [27AppService,28{29provide: APP_INTERCEPTOR,30useClass: ClassSerializerInterceptor,31},32],33})34export class AppModule {}