`Config`

Config

키워드 목록 기반의 간단한 설정(configuration) API예요.

Example

이 모듈은 주로 애플리케이션 설정을 정의할 때 쓰는데, 보통 config/config.exs에서 사용합니다:

import Config

config :some_app,
  key1: "value1",
  key2: "value2"

import_config "#{config_env()}.exs"

import Configconfig/2, config/3, config_env/0, config_target/0, import_config/1 함수를 가져와서 설정 관리에 활용하게 해줘요.

config/2config/3은 특정 애플리케이션의 키-값 설정을 정의해요. Mix가 시작하면 설정 파일을 자동으로 평가해 위의 설정을 :some_app의 애플리케이션 환경(application environment)에 저장하며, 다음과 같이 접근할 수 있습니다:

"value1" = Application.fetch_env!(:some_app, :key1)

마지막 줄의 import_config "#{config_env()}.exs"는 현재 설정 환경에 따라 다른 설정 파일(예: config/dev.exs, config/test.exs)을 불러옵니다.

Config는 또한 설정을 평가하고 읽기 위한 저수준 API를 Config.Reader 모듈 아래에 제공해요.

라이브러리에서 애플리케이션 환경 피하기 {: .info}

다른 개발자들이 쓸 라이브러리를 작성한다면 애플리케이션 환경을 피하는 게 일반적으로 권장돼요. 애플리케이션 환경은 사실상 전역(global) 저장소이기 때문입니다. 또한 라이브러리가 의존성으로 사용될 때는 라이브러리의 config/config.exs가 평가되지 않는다는 점도 기억하세요. 설정은 항상 현재 프로젝트를 설정하기 위한 것이기 때문이에요. 자세한 내용은 "라이브러리에서 애플리케이션 설정 사용하기"를 참고하세요.

use Mix.Config에서 마이그레이션하기

Elixir의 Config 모듈은 v1.9에서 use Mix.Config를 대체하기 위해 도입됐어요. use Mix.Config는 Mix에 한정된 것이었고 이제 deprecated(더 이상 권장되지 않음) 상태입니다.

use Mix.Config 대신 Config를 세 단계로 활용할 수 있어요. 첫째, 설정 파일 맨 위의 use Mix.Configimport Config로 바꿉니다.

둘째, import_config/1 호출에 와일드카드 문자(*)가 없는지 확인하고, 있다면 와일드카드 조회를 직접 수행해야 합니다. 예를 들어 이렇게 되어 있었다면:

import_config "../apps/*/config/config.exs"

이렇게 바꿔야 해요:

for config <- "../apps/*/config/config.exs" |> Path.expand(__DIR__) |> Path.wildcard() do
  import_config config
end

마지막 단계는 설정 파일의 모든 Mix.env() 호출을 config_env()로 바꾸는 것입니다.

프로젝트 파일 안에서도 Mix.env()를 쓰지 않도록 주의해야 해요. 런타임에 환경을 확인하려면 설정 키를 추가하면 됩니다:

# config.exs
...
config :my_app, env: config_env()

그러면 다른 스크립트나 모듈에서는 Application.fetch_env!/2로 환경을 얻을 수 있어요:

# router.exs
...
if Application.fetch_env!(:my_app, :env) == :prod do
  ...
end

Mix 모듈의 함수에 접근할 수 있는 곳은 mix.exs 파일과, 항상 Mix.Tasks 네임스페이스 안에 있는 커스텀 Mix 태스크뿐입니다.

config/runtime.exs

런타임 설정에는 config/runtime.exs 파일을 사용할 수 있어요. 이 파일은 Mix와 릴리즈(mix release로 조립한 것) 양쪽에서 애플리케이션이 시작되기 직전에 실행됩니다.

출처: Config

본문

Config 모듈은 애플리케이션 설정을 키워드 목록으로 손쉽게 정의하도록 도와주는 API예요. 설정이라고 해서 특별한 게 아니라, 결국 "어떤 애플리케이션의 어떤 키에 어떤 값을 둔다"는 묶음이에요. 아래처럼 씁니다:

config :logger,
  level: :warn

config :logger,
  level: :info,
  truncate: 1024

config/2는 주어진 root_key(첫 번째 인자)를 설정해요. 같은 키에 대해 여러 번 호출하면 키워드 목록은 항상 깊은 병합(deep-merge)됩니다. 위 예시에서 :logger에는 leveltruncate가 합쳐져서 최종적으로 [level: :info, truncate: 1024]가 돼요. 키워드가 아닌 충돌하는 키는 나중에 지정한 값이 이기죠.

config/3은 첫 번째 인자와 두 번째 인자를 함께 묶어 설정합니다. 예를 들어 Ecto 설정처럼요:

config :ecto, Repo,
  log_level: :warn,
  adapter: Ecto.Adapters.Postgres,
  metadata: [read_only: true]

config :ecto, Repo,
  log_level: :info,
  pool_size: 10,
  metadata: [replica: true]

그러면 :ecto 애플리케이션의 Repo 키 최종 값은 다음과 같아요:

Application.get_env(:ecto, Repo)
#=> [
#=>   log_level: :info,
#=>   pool_size: 10,
#=>   adapter: Ecto.Adapters.Postgres,
#=>   metadata: [read_only: true, replica: true]
#=> ]

여기서도 키워드인 metadata는 깊게 병합되어 두 호출의 내용이 합쳐지고, 키워드가 아닌 log_level 같은 값은 나중 값이 이깁니다.

config_env/0은 설정 파일이 실행되고 있는 환경을 돌려줘요. Mix 프로젝트에서 이 함수는 해당 설정 파일이 실행되는 환경을 반환하고, 릴리즈에서는 mix release 실행 시 지정한 MIX_ENV를 반환합니다. 주로 조건부 설정에 쓰여요:

if config_env() == :prod do
  config :my_app, :debug, false
end

config_target/0은 그와 유사하게, 설정 파일이 실행되는 target을 반환합니다. import_config/1은 주어진 파일에서 설정을 불러오는데, 파일이 없으면 오류를 던져요. 상대 경로라면 현재 설정 파일이 있는 디렉터리를 기준으로 확장됩니다.

import_config "#{config_env()}.exs"

read_config/1은 (v1.18에서 추가) 이전 config/2config/3 호출에서 설정한 값을 파일 간에 공유하기 쉽게 읽어주는 함수예요. 애플리케이션 환경을 직접 읽는 게 아니라, 설정 파일 안에서 정의된 값만 읽는다는 점이 다릅니다. 설정되지 않은 root_key에 대해서는 nil을 돌려줍니다.

# In config/config.exs
config :my_app, foo: :bar

# In config/dev.exs
config :another_app, foo: read_config(:my_app)[:foo] || raise "missing parent configuration"

설정은 "읽는 시점"에 따라 컴파일 타임 설정과 런타임 설정으로 나뉘어요. config/config.exs는 컴파일 시점 설정이라 그 값은 컴파일 중에 사용됩니다. 반대로 config/runtime.exs는 애플리케이션이 시작되기 직전에 실행되므로, 배포 환경(예: 프로덕션 서버 주소)처럼 컴파일 시점에 알 수 없는 값을 다룰 때 쓰는 게 자연스러워요.

더 알아보기

  • Config.Reader 모듈은 설정을 평가하고 읽는 저수준 API를 제공합니다.
  • 설계 안티패턴 문서의 "라이브러리에서 애플리케이션 설정 사용하기" 항목을 참고하세요.
  • 릴리즈에서의 런타임 설정은 Elixir 릴리즈 가이드에서 더 자세히 다룹니다.