# Configuration

## Configuration

응용 프로그램은 다른 **환경**에서 실행되는 데 사용되곤 합니다. 환경에 따라 다양한 구성 변수 세트를 사용해야 합니다. 예를 들어, 로컬 환경은 특정 데이터베이스 자격 증명에 의존할 가능성이 매우 높으며 로컬 DB 인스턴스에만 유효합니다. 이 문제를 해결하기 위해 키-값 쌍을 보유하는`.env` 파일을 활용했습니다. 이 방법은 매우 편리하므로 각 키는 특정 값을 나타냅니다.

그러나 `프로세스` 전역 객체를 사용하는 경우 테스트된 클래스가 직접 사용할 수 있으므로 테스트를 깨끗하게 유지하기가 어렵습니다. 또 다른 방법은 로드된 구성 변수로 `ConfigService`를 노출하는 추상화 계층 인 `ConfigModule`을 작성하는 것입니다.

## Installation

특정 플랫폼은 환경 변수를`process.env` 전역에 자동으로 연결합니다. 그러나 로컬 환경에서는 수동으로 관리해야 합니다. 환경 파일을 구문 분석하기 위해 [dotenv](https://github.com/motdotla/dotenv) 패키지를 사용합니다.

```bash
$ npm i --save dotenv
$ npm i --save-dev @types/dotenv
```

## Service

먼저 `ConfigService` 클래스를 만들어 봅시다.

```typescript
@@filename()
import * as dotenv from 'dotenv';
import * as fs from 'fs';

export class ConfigService {
  private readonly envConfig: { [key: string]: string };

  constructor(filePath: string) {
    this.envConfig = dotenv.parse(fs.readFileSync(filePath))
  }

  get(key: string): string {
    return this.envConfig[key];
  }
}
@@switch
import * as dotenv from 'dotenv';
import * as fs from 'fs';

export class ConfigService {
  constructor(filePath) {
    this.envConfig = dotenv.parse(fs.readFileSync(filePath))
  }

  get(key) {
    return this.envConfig[key];
  }
}
```

이 클래스는`.env` 파일의 경로 인 `filePath`라는 단일 인수를 취합니다. `get()`메소드는 환경 파일 내에 정의된 각 속성을 보유하는 개인 `envConfig` 오브젝트에 액세스할 수 있도록 제공됩니다.

마지막 단계는 `ConfigModule`을 만드는 것입니다.

```typescript
@@filename()
import { Module } from '@nestjs/common';
import { ConfigService } from './config.service';

@Module({
  providers: [
    {
      provide: ConfigService,
      useValue: new ConfigService(`${process.env.NODE_ENV}.env`),
    },
  ],
  exports: [ConfigService],
})
export class ConfigModule {}
```

`ConfigModule`은 `ConfigService`를 등록하고 이를 내 보냅니다. 또한 `.env` 파일의 경로를 전달했습니다. 이 경로는 실제 실행 환경에 따라 다릅니다. 이제 어디서나 `ConfigService` 를 주입하고 전달된 키를 기반으로 특정 값을 가져올 수 있습니다. 샘플 `.env` 파일은 다음과 같습니다.

```typescript
DATABASE_USER = test
DATABASE_PASSWORD = test
```

## Using the ConfigService

`ConfigService`에서 **환경 변수**에 액세스하려면 변수를 주입해야합니다. 따라서 먼저 모듈을 가져와야합니다.

```typescript
@@filename(app.module)
@Module({
  imports: [ConfigModule],
  ...
})
```

그런 다음 주입 토큰을 사용하여 주입할 수 있습니다. 기본적으로 토큰은 클래스 이름과 같습니다 (예: `ConfigService`).

```typescript
@@filename(app.service)
@Injectable()
export class AppService {
  private isAuthEnabled: boolean;
  constructor(config: ConfigService) {
    // Please take note that this check is case sensitive!
    this.isAuthEnabled = config.get('IS_AUTH_ENABLED') === 'true';
  }
}
```

> info **힌트** 모든 모듈에서 `ConfigModule`을 가져 오는 대신 `ConfigModule`을 전역 모듈로 선언할 수도 있습니다.

## Advanced configuration

방금 기본 `ConfigService`를 구현했습니다. 그러나 이 접근 방식에는 몇 가지 단점이 있습니다.

* 환경 변수에 대한 이름 및 유형 누락 (IntelliSense 없음)
* 제공된`.env` 파일의 **확인** 부족
* env 파일은 부울을 문자열 (`'true'`)로 제공하므로 매번 부울로 캐스트해야합니다.

## Validation

제공된 환경 변수의 유효성 검증부터 시작하겠습니다. 필요한 환경 변수가 제공되지 않았거나 사전 정의된 요구 사항을 충족하지 않으면 오류가 발생할 수 있습니다. 이를 위해 npm 패키지 [Joi](https://github.com/hapijs/joi)를 사용합니다. Joi를 사용하면 객체 스키마를 정의하고 이에 대해 JavaScript 객체의 유효성을 검사합니다.

Joi 설치 및 유형 (**TypeScript** 사용자의 경우) :

```bash
$ npm install --save @hapi/joi
$ npm install --save-dev @types/hapi__joi
```

패키지가 설치되면 `ConfigService`로 이동할 수 있습니다.

```typescript
@@filename(config.service)
import * as dotenv from 'dotenv';
import * as Joi from '@hapi/joi';
import * as fs from 'fs';

export interface EnvConfig {
}

export class ConfigService {
  private readonly envConfig: EnvConfig;

  constructor(filePath: string) {
    const config = dotenv.parse(fs.readFileSync(filePath));
    this.envConfig = this.validateInput(config);
  }

  /**
   * Ensures all needed variables are set, and returns the validated JavaScript object
   * including the applied default values.
   */
  private validateInput(envConfig: EnvConfig): EnvConfig {
    const envVarsSchema: Joi.ObjectSchema = Joi.object({
      NODE_ENV: Joi.string()
        .valid(['development', 'production', 'test', 'provision'])
        .default('development'),
      PORT: Joi.number().default(3000),
      API_AUTH_ENABLED: Joi.boolean().required(),
    });

    const { error, value: validatedEnvConfig } = Joi.validate(
      envConfig,
      envVarsSchema,
    );
    if (error) {
      throw new Error(`Config validation error: ${error.message}`);
    }
    return validatedEnvConfig;
  }
}
```

`NODE_ENV`와 `PORT`에 기본값을 설정했으므로 환경 파일에 이러한 변수를 제공하지 않으면 유효성 검사가 실패하지 않습니다. 그럼에도 불구하고 명시적으로 `API_AUTH_ENABLED`를 제공해야 합니다. 스키마의 일부가 아닌 .env 파일에 변수가 있는 경우 유효성 검사에서도 오류가 발생합니다. 또한 Joi는 env 문자열을 올바른 유형으로 변환하려고 시도합니다.

## Class properties

각 구성 속성마다 getter 함수를 추가해야합니다.

```typescript
@@filename(config.service)
get isApiAuthEnabled(): boolean {
  return Boolean(this.envConfig.API_AUTH_ENABLED);
}
```

## Usage example

이제 클래스 속성에 직접 액세스 할 수 있습니다.

```typescript
@@filename(app.service)
@Injectable()
export class AppService {
  constructor(config: ConfigService) {
    if (config.isApiAuthEnabled) {
      // Authorization is enabled
    }
  }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jakekwak.gitbook.io/nestjs/techniques/configuration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
