๐ถ Config ํ์ผ์ ์์ ํด์ผ๊ฒ ๋ค
๋ฐ์ดํฐ ETL ์ฝ๋๋ฅผ ์ ์ฒด์ ์ผ๋ก ๋ฆฌํฉํ ๋ง ํ๊ณ ์๋ค. ํ๊ฒฝ๋ ๋ค์ํ๊ณ , ๋ฐ์ดํฐ ํ์ ๋ ๋ค์ํ๋ค ๋ณด๋ ETL ํ๊ฒฝ ์ค์ ์ด ์ฌ๋ฌ๊ฐ์ธ๋ฐ, ์ ์ญ ๋ณ์๋ก ๋์ ์๋ ๊ฒ๋ค์ config๋ก ๋ค ์ ๋ฆฌํด์ผ๊ฒ ๋ค๊ณ ๋ง์๋จน์๋ค. ์ ์ญ ๋ณ์๋ก ์ ๊ฒ ๋๋ฉด ์ค์ผ๋ ๊ฐ๋ฅ์ฑ์ด ์๊ณ , ํด๋จผ ์๋ฌ๊ฐ ๋ฐ์ํ ํ๋ฅ ์ด ๋๋ค. ๊ทธ๋ฌ๋ ์ด๊ฑธ config ํ์ผ์ ํ๋ฒ์ ์ ๋ฆฌํด ๋๊ณ , ๊ฒํ ๋ง ํ๋ค๋ฉด ํด๋จผ ์๋ฌ๋ ํ์คํ ์ค์ด๋ค ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ํ์ํ ๋ณ์๋ฅผ ๋ฐ๊ณ , ๋ง๋ค์ด๋ด๋ ๊ณผ์ ๋ config ํ์ผ์ ์ ์ ์๋ง ํค๋๋ค๋ฉด ์ค์ผ ์ ์์ ๊ฒ์ด๋ค.
โ๊ทธ๋ ๋ค๊ณ config ํ์ผ์ด ์์๋?
๊ทธ๊ฑด ์๋๋ค. ์ํธํ ์ ๋ณด๋ DB ์ ์ ์ ๋ณด ๋ฑ ๊ธฐ๋ณธ์ ์ธ ๊ฒ์ ๋ค Config ํ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์๋ค. ๊ทธ๋ฌ๋ ํ์ผ ๊ฒฝ๋ก์ ์ ์ ์ ๋ณด๋ค์ด ํ์ผ ๊ฒฝ๋ก๋ง๋ค ๋ฌ๋ผ์ก๋ค. Test๋ฅผ ํ ๋๋ ์ค์ ์ด์ ํ๊ฒฝ์์๋ ์ํฅ์ด ๊ฐ๋ฉด ์ ๋๊ธฐ ๋๋ฌธ์ ๋ ์ค์ ์ ๋ฐ๊ฟ์คฌ๋ค. ์ด๋ฐ ๊ฒ๋ค์ ๋์ด์ ๋ด ๋ ธ๋๋ ฅ์ ์ฐ๊ณ ์ถ์ง ์๋ค. ํ๊ฒฝ ์ด๋ฆ๋ง ๋ฐ๊พธ๋ฉด ์์์ ๋๋ ๊ตฌ์ฑ์ Config๋ฅผ ๋ง๋ค๊ณ ์ถ๋ค.
๐ถ ์ฒซ ๋ฒ์งธ ์์ Config
{
"dev": {
"service1": {
"API_KEY": "...",
"ENDPOINT": "..."
},
"service2": {
"API_KEY": "...",
"ENDPOINT": "..."
}
},
"prod": {
"service1": {
"API_KEY": "...",
"ENDPOINT": "..."
},
"service2": {
"API_KEY": "...",
"ENDPOINT": "..."
}
}
}
์ฌ์ง์ด ์ด๊ฒ๋ ๊ณ ์น ๊ฑฐ๋ผ๋ฉด ๋ฏฟ์ผ์๊ฒ ์ต๋๊น? ์๋๋ dev ์ prod๊ฐ ํ์ผ๋ก ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋๋์ด์ ธ ์์๊ณ , ๊ณตํต์ ์ผ๋ก ์ฐ์ด๋ ์ ์ ์ ๋ณด๋ง config ํ์ผ ์์ ์์๋ค. ๊ทธ๋ฌ๋ ํ์ผ์ด ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋ถ๋ฆฌ๊ฐ ๋์ด ์์ผ๋ dev์ prod ๊ฐ ์ฝ๋๋ฅผ ๋ฐฐํฌํ ๋ ์ ๊ฒฝ ์ฐ์ฌ์ ๋ ๊ฐ์ ํ๊ฒฝ ๋ณ์๋ฅผ ํ ํ์ผ์ ๊ด๋ฆฌํ๊ณ ์ ํฉ์ณค๋ค. ์ ๋ฐ ์์ผ๋ก ๋ฐ๊พธ๋ฉด ๋ฐฐํฌํ ๋ ์ ๊ฒฝ ์ฐ์ง ์์๋ ๋๊ณ , dev, prod ํ๊ฒฝ ์ด๋ฆ๋ง ๋ฐ๊ฟ ์ฃผ๋ฉด ํด๊ฒฐ๋๋ค.
๊ทธ๋ฌ๋ ๋ฌธ์ ์ ์ด ์์๋ค. service ๋จ์์์ ๊ฒน์น๋ ๋ณ์๊ฐ ์ธ๋ค ๊ฐ ์ ๋๊ฐ ๋์ด ์ค๋ณต๋๋ ๊ฒ ๋ง์๋ค. ๋ชจ๋ ์๋น์ค์์ ๊ฒน์น์ง ์๊ณ , ํ๋ ๊ฐ์ ์๋น์ค์์๋ง ์ค๋ณต๋๋ ๋ณ์๋ ์์๋ค. ์๋น์ค ๋จ์๊ฐ ์๋๋ผ dev-prod ๊ฐ ์ค๋ณต๋๋ ๋ณ์๋ ์์๋ค. ์ด์ฒด์ ์ค๋ณต ํํฐ์๋ค.
๐ถ ๋ ๋ฒ์งธ ์์ Config
{
"dev": {
"common": {
"RETRY_COUNT": 3,
"TEAM_NAME": "data",
"TIMEZONE": "Asia/Seoul"
},
"service1": {
"API_KEY": "service1-dev-key",
"ENDPOINT": "https://dev.service1.example.com"
},
"service2": {
"API_KEY": "service2-dev-key",
"ENDPOINT": "https://dev.service2.example.com",
"EXTRA_CONFIG": "some-value"
}
},
"prod": {
"common": {
"RETRY_COUNT": 5,
"TEAM_NAME": "data",
"TIMEZONE": "Asia/Seoul"
},
"service1": {
"API_KEY": "service1-prod-key",
"ENDPOINT": "https://prod.service1.example.com"
},
"service2": {
"API_KEY": "service2-prod-key",
"ENDPOINT": "https://prod.service2.example.com"
}
}
}
๋ฆฌ๋ทฐ๋ ํ๊ณ GPT ํํ ๋ ๋ฌผ์ด๋ณธ ๊ฒฐ๊ณผ, ๊ณตํต ์๊ฒฌ์ common ํญ๋ชฉ์ ๋ง๋ค์ด ์ค๋ณต๋ ๋ณ์๋ฅผ ๋ฌถ์ผ๋ผ๋ ๊ฒ์ด์๋ค. common ํญ๋ชฉ์ผ๋ก ์ค๋ณต๋ ๋ณ์๋ฅผ ๋ฌถ์๋๋ ํจ์ฌ ๋ ํผ๋กํด์ก๋ค.
๊ทธ๋ฌ๋
common ์ผ๋ก ๋ฌถ์ธ ๋ณ์๋ค์ด dev์ prod์์ ๋ ๋ ๋ ๊ฒน์ณค๋ค ์ค๋ณต์ด ์ค์ด๋ค๊ธฐ๋ ํ์ง๋ง ์๋ฒฝํ๊ฒ ์ค์ด๋ค์ง๋ ์์์ 100ํผ์ผํธ ๋ง์กฑ์ค๋ฝ์ง๋ ์์๋ค.
๐ถ ์ธ ๋ฒ์งธ ์์ Config
๐ธConfig ํด๋ ๊ตฌ์กฐ
#config ํด๋ ๊ตฌ์กฐ
/config
โโโ base_config.json # ๋ชจ๋ ํ๊ฒฝ ๊ณตํต
โโโ dev/
โ โโโ base.json # dev ๊ณตํต ์ค์
โ โโโ service1.json # dev + service2 ๊ณตํต ์ค์
โ โโโ service2.json # dev + service1 ์ ์ฉ ์ค์
โโโ prod/
โ โโโ base.json # prod ๊ณตํต ์ค์
โ โโโ service1.json # prod + service1 ๊ณตํต ์ค์
โ โโโ service2.json # prod + service2 ์ ์ฉ ์ค์
- base_config.json: ๋ชจ๋ ํ๊ฒฝ์์ ๊ณตํต์ผ๋ก ์ฐ๋ ์ค์ (TIMEZONE, SPARK_CONF ๋ฑ)
- dev/base.json / prod/base.json: ๊ฐ ํ๊ฒฝ์ ๊ณตํต ์ค์ (TEAM_NAME, RETRY, DEFAULT_TABLE_PATH ๋ฑ)
- dev/service.json / prod/service.json: ์๋น์ค ์ ์ฉ ์ค์ (TABLE_INFO_PATH, USE_ENCRYPTION, SPECIAL_INDEX_HINTS ๋ฑ)
๋ชจ๋ ํ๊ฒฝ์์ ๊ณตํต์ผ๋ก ์ฐ๋ ์ค์ ์ ๋ ๋ฒ์งธ ์์ ํ ํ์ผ์์ ์ถ๊ฐํด ๋ฒ๋ฆฌ๋ฉด depth ๊ฐ ๋์ด๋ ๋ณด๊ธฐ ๋ถํธํด์ก๋ค. ์ฐจ๋ผ๋ฆฌ ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋๋ ๋ฒ๋ฆฌ๋ฉด ์ด๋จ๊น ์ถ์ด ์์ ๊ฐ์ด config ๊ตฌ์ฑ์ ๋ฐ๊พธ์๋ค. ์ด๋ ๊ฒ ๋ฐ๊พธ๋ฉด ํ์ผ ํ๋๋น depth๊ฐ 1์ด ๋๋ค. ๊ธธ์ด ์์ฒด๋ ๊ธธ์ง ์์ ์ฝ๊ธฐ ํธํ๋ค.
์ฅ์
๊น๋ํ ๊ตฌ์กฐ | ํ์ผ ๋จ์๋ก ์ญํ ๋ถ๋ฆฌ (env, service, global) |
์ค๋ณต ์ต์ํ | ๊ณตํต ๊ฐ์ base์ ํ ๋ฒ๋ง ์ ์ |
์์ ์ฉ์ด์ฑ | ํ๊ฒฝ/์๋น์ค ๋ณ๊ฒฝํด๋ ๋ค๋ฅธ ํ์ผ์ ์ํฅ ์์ |
ํ ์คํธ ํธ์์ฑ | dev/prod ์ค์ ๋ฐ๋ก ํ ์คํธ ๊ฐ๋ฅ |
๋จ์
๋จ์ ์ config ํ์ผ ๊ฐ์๊ฐ ๋์ด๋๋ค๋ ์ ์ด๋ค. service ๋จ์๋ก ํ๊ฒฝ ๋ณ์๋ฅผ ํ์ ํ๋๋ฐ, ํ ํ์ผ์์ ํ์ธํ๊ธฐ ๋ถํธํ๊ณ base_config, env/base, service ์์๋ก ๋ด๋ ค์์ผ ํ ์๋น์ค ์์ ์ฐ์ด๋ ํ๊ฒฝ ๋ณ์๋ฅผ ํ์ ํ ์ ์๋ค.
config ํ์ผ ๊ตฌ์ถ ํ๋ฒ ์ํด ๋์ผ๋ฉด ์ ์ฒด์ ์ผ๋ก ๋ณผ ์ผ์ ๋ณ๋ก ์์ ๊ฒ ๊ฐ์ ๋จ์ ์ ๊ฐ์ํ๊ณ ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋๋๋ ๋ฐฉ๋ฒ์ ์ ํํ๋ค. ๊ทธ๋ฌ๋ฉด ํ๊ฒฝ ๊ณตํต ๋ณ์๋ฅผ ์ถ๊ฐํ ๋ ๋ฑ ํ ๋ฒ๋ง ์ถ๊ฐํ๋ฉด ๋๋ค. ๋ฐ๊ฟ ๋๋ ๋ฑ ํ ๋ฒ๋ง ์์ ๋๋ฉด ๋๋ค. ํ ์คํธ ์ฉ๋๋ ํน์ํ ํ๊ฒฝ์์๋ service ๋จ์๋ณ๋ก ํ์ผ์ด ๋๋์ด์ ธ ์์ผ๋ service ํ์ผ์์๋ง ์์ ํ์ฌ overwrite ํด์ ์ฌ์ฉํ๋ฉด ๋๋ค. ์ง๊ธ ์ด์ํ๊ณ ์๋ ์๋น์ค์์๋ ์ด๋ฐ config ๊ตฌ์กฐ๊ฐ ๊ฐ์ฅ ์ ์ ํ๋ค.
json ํ์ผ
ํ์ผ ํ๋ํ๋ ๋ค์ฌ๋ค๋ณด๋ฉด depth=1 ์ธ ์ผ๋ฐ์ ์ธ json ํํ์ด๋ค.
โ /config/base_config.json
{
"TIMEZONE": "Asia/Seoul",
"SPARK_APP_NAME_PREFIX": "DataPipeline",
"DEFAULT_NUM_PARTITIONS": 200
}
โ /config/dev/base.json
{
"LOG_LEVEL": "DEBUG",
"TEAM_NAME": "data_dev",
"DEFAULT_SAVE_FORMAT": "parquet"
}
โ /config/dev/service.json
{
"TABLE_INFO_PATH": "/dat/home/airflow/dags/table_list/data_info_dev.txt",
"USE_ENCRYPTION": true,
"SPARK_CUSTOM_CONF": {
"spark.sql.shuffle.partitions": "50"
}
}
ํ์ผ์ ๊ฐ์๋ ๋ง์์ก์ง๋ง ๊ฐ๋ ์ฑ์ ํจ์ฌ ์ฌ๋ผ๊ฐ๋ค.
๐ถ Common ๋ณ์์ ๋ฒ์๋?
์ด๋ ์ ๋ ๊ตฌ์ฑ์ ๋ง์ณ ๋๊ณ ํ์ํ ๋ณ์๋ฅผ ๋ฃ๋๋ฐ, ๊ณตํต ์ค์ ์ ๋ฒ์๋ฅผ ์ด๋๊น์ง ํด์ผ ํ๋๋๊ฐ ๊ณ ๋ฏผ์ด์๋ค. ์๋น์ค๊ฐ ๋ค ๊ฐ ์๋ค๊ณ ์น๋ฉด, ๊ทธ์ค์ 2๊ฐ์ ์๋น์ค์๋ง ๊ฒน์ณ๋ ๊ณตํต ๋ณ์์ ๋ฃ์ด์ผ ํ๋๊ฐ, 4๊ฐ์ ์๋น์ค์์ ๋ชจ๋ ์ฌ์ฉํ ๋๋ง ๊ณตํต ์ค์ ์ ๋ฃ์ด์ผ ํ๋๊ฐ. ๋๋ ์ ์๊ฐ ๋ง๋ค๊ณ ์๊ฐํ๋ค. ์ค๋ณต์ ์ค์ด๋ ค๊ณ base ๋ณ์ ํ์ผ์ ๋ง๋ค์ด ๊ด๋ฆฌํ๊ณ ์๋๋ฐ ๊ณตํต ๋ณ์์ ์กฐ๊ฑด์ ์๊ฒฉํ๊ฒ ์ก์ผ๋ฉด ์ค๋ณต์ด ์๊ฒจ๋๊ธฐ ๋๋ฌธ์ด๋ค.
์ฑ GPT์ ๋ต๋ณ์ ์๋์ ๊ฐ๋ค.
โ "base_config์ ๋ฃ์ ์ ์๋ ์กฐ๊ฑด"
โ ๊ณตํต ์ฌ์ฉ + ๊ฐ์ ์๋ฏธ + ๊ฐ์ ๊ฐ | "log_level": "INFO"์ฒ๋ผ ๊ณตํต ์ ์ฉ ๊ฐ๋ฅํ ๊ฒฝ์ฐ | ๋ฃ๋ ๊ฒ ์ข์ |
โ ๏ธ ๊ณตํต ์ฌ์ฉ + ๊ฐ์ ์๋ฏธ + ์ฌ์ฉํ๋ ์๋น์ค๋ง ์์ | "hdfs_base_path"๊ฐ 2~3๊ฐ ์๋น์ค์์๋ง ์ฌ์ฉ, ๋๋จธ์ง๋ ๋ฌด์ | โ ๏ธ ์กฐ๊ฑด๋ถ๋ก ๋ฃ์ด๋ ๋จ |
โ ์๋ฏธ/ํํ๊ฐ ๋ค๋ฅธ ๊ฒฝ์ฐ | "ftp_path" vs "hdfs_path"์ฒ๋ผ ์ ์ฌํด ๋ณด์ฌ๋ ์๋ฏธ ๋ค๋ฆ | base์ ๋ฃ์ผ๋ฉด ์ ๋จ |
๐ ๋ ์ํฉ์?
- ๋ณ์๋ฅผ ์ฐ๋ ์๋น์ค๋ ์๊ณ , ์ ์ฐ๋ ์๋น์ค๋ ์์
- ์ฐ๋ ์๋น์ค์์๋ ๊ฑฐ์ ๊ฐ์ ์๋ฏธ๋ก ์ฌ์ฉ๋จ
- ๋์ค์ ์๋ก์ด ์๋น์ค๊ฐ ์ถ๊ฐ๋๋ฉด ์ด๊ฑธ ์ฌ์ฉํ ๊ฐ๋ฅ์ฑ๋ ์์
→ ์ด๋ด ๋ ๋ฃ์ด๋ ๊ด์ฐฎ์!
๋จ, ํด๋น ์๋น์ค์์ ์์ ์ ์ฐ๋ฉด ์ํฅ๋ ์๊ณ ,
์ค์ ๋ก๋ฉ ๊ตฌ์กฐ๊ฐ override ๋ฐฉ์์ด๋ผ ๋์ค์ ๋ค๋ฅธ ์๋น์ค์์ ํ์ํ ๋๋ง ์ฐ๋ฉด ๋ผ.
config๋ฅผ ๋ถ๋ฌ์ค๋ ๋ฐ์ ๊ทธ๋ ๊ฒ ํฐ ๋ฆฌ์์ค๋ฅผ ์๊ตฌํ์ง ์๋๋ค. ๋ด ๋ฆฌํฉํ ๋ง์ ๋ชฉ์ ์ "์ค๋ณต์ ์ค์ด์" ์ด๋ค. ๋ ๊ฐ ์ด์์ ์๋น์ค์์๋ง ๊ฒน์ณ๋ ์ผ๋จ์ base ํ์ผ์ ๋ฃ๊ธฐ๋ก ํ๋ค. ๋ค๋ง, ์ง๊ธ์ ์๋น์ค์ ์ข ๋ฅ๊ฐ ๊ทธ๋ ๊ฒ ๋ง์ง ์๊ธฐ ๋๋ฌธ์ ํ ์ ์๋ ๊ฒฐ์ ์ด๋ค. ๋ง์ฝ ํ๊ฒฝ ๋ณ์๋ฅผ ๊ด๋ฆฌํด์ผ ํ๋ ์๋น์ค์ ์ข ๋ฅ๊ฐ ์ด ์ข ๋ฅ๋ฅผ ๋์ด๊ฐ๋ค๋ฉด ์ด ๊ธฐ์ค์ ๋ ์๊ฒฉํด์ ธ์ผ ํ ๋ฏํ๋ค.
๐ถ Config๋ฅผ ๋ถ๋ฌ์ฌ ๋
config ํ์ผ์ ๋ถ๋ฆฌํ๋ฉด์๋ ์ด๊ฑธ ์ด๋ป๊ฒ ๋ถ๋ฌ์ค์ง ๊ณ ๋ฏผํ๋ค. ํ๊ฒฝ ์ด๋ฆ๊ณผ ์๋น์ค ์ด๋ฆ์ ๋ณ์๋ก ๋ฐ๊ณ base+env_base+service ๋ ์ด์ด์ฒ๋ผ ๋ฐ์์ฌ ์ ์๊ฒ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
import os
import json
class ConfigLoadError(Exception):
pass
def read_json_file(path: str) -> dict:
"""ํ์ผ ์กด์ฌ ์ JSON ๋ก๋ฉ, ์์ผ๋ฉด ๋น dict ๋ฐํ"""
try:
with open(path, 'r') as f:
return json.load(f)
except FileNotFoundError:
return {}
except json.JSONDecodeError as e:
raise ConfigLoadError(f"JSON ํ์ฑ ์ค๋ฅ: {path}") from e
def get_env_config(base_path: str, env: str) -> dict:
return read_json_file(os.path.join(base_path, env, "base.json"))
def get_service_config(base_path: str, env: str, service: str) -> dict:
return read_json_file(os.path.join(base_path, env, f"{service}.json"))
def get_global_config(base_path: str) -> dict:
return read_json_file(os.path.join(base_path, "base_config.json"))
def get_dag_config(dag_config_path: str) -> dict:
return read_json_file(os.path.join(dag_config_path, "dag_config.json"))
def get_dag_common_config(dag_config_path: str) -> dict:
return read_json_file(os.path.join(dag_config_path, "base_dag_config.json"))
def merge_dicts(*configs: dict) -> dict:
merged = {}
for config in configs:
merged.update(config)
return merged
def load_merged_config(env: str, dag_id: str) -> dict:
base_path = "/path/to/config"
dag_config_path = "/path/to/dags/dag_config"
try:
all_dag_config = get_dag_config(dag_config_path)
dag_config = all_dag_config.get(dag_id)
if not dag_config:
raise ValueError(f"DAG ์ค์ ์์: {dag_id}")
service = dag_config.get("service")
if not service:
raise ValueError(f"DAG '{dag_id}'์ 'service' ํญ๋ชฉ์ด ์์ต๋๋ค.")
return merge_dicts(
get_global_config(base_path),
get_env_config(base_path, env),
get_service_config(base_path, env, service),
get_dag_common_config(dag_config_path),
dag_config
)
except Exception as e:
raise RuntimeError(f"์ค์ ๋ก๋ฉ ์คํจ: {e}")
def load_env_config_file(env: str, filename: str) -> dict:
"""
์ค์ ํ์ผ ๋ณํฉ:
- base_config.json (์ ์ญ ๊ณตํต)
- {env}/base.json (ํ๊ฒฝ ๊ณตํต)
- {env}/{filename} (์๋น์ค ์ ์ฉ)
"""
base_path = "/path/to/config"
global_config = read_json_file(os.path.join(base_path, "base_config.json"))
env_base_config = read_json_file(os.path.join(base_path, env, "base.json"))
service_config = read_json_file(os.path.join(base_path, env, filename))
return merge_dicts(global_config, env_base_config, service_config)
์ค์ ์๋น์ค์์๋ ์ฝ๋๋ฅผ ์์ ํ์ง๋ง ์ ์ฒด์ ์ธ ํ์ ๋ณด๋ฉด ์ด๋ ๋ค. airflow๋ฅผ ๊ฐ์ด ์ฌ์ฉํ๊ณ ์์ด์ dag_id๋ฅผ ๋ฃ์ผ๋ฉด config๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๋ ํจ์๊น์ง ๋ง๋ค์๋ค.
์ผ์ฃผ์ผ ๋์ config ๊ตฌ์ฑ ์ง๋๋ผ ๋จธ๋ฆฌ ๊ตด๋ ธ๋๋ฐ ์ ์ ๊ธ๋ก ์ ๋ฆฌํ๋๊น ๊ธธ์ง ์๋ค..
config ๊ตฌ์ฑ์ ์์๋ณด๋ค ๋ง์กฑ์ค๋ฝ๋ค!! ์ด๊ฒ ์ ๋ต์ ์๋์ง๋ง ์ง๊ธ ์ด์ํ๊ณ ์๋ ์๋น์ค์ ์ต์ ํ๋ ๊ตฌ์กฐ์ธ ๊ฒ ๊ฐ๋ค. ์ด์ config์ ๋ด์ ์ ์๋ ๊ฑด ๋ชจ์กฐ๋ฆฌ ๋ด์ ๋ฒ๋ฆฌ๊ณ ์ ์ฉํ ์ฐจ๋ก์ด๋ค. config ํ์ผ ๊ด๋ฆฌ๋ ์ด๋ป๊ฒ ๋ณด๋ฉด ์ ๋ง ๊ธฐ๋ณธ์ด์ง๋ง ๋๋ ์ด๊ฒ๋ ๋ชฐ๋๊ธฐ์..!! ์ค๋ณต ์์ด ์ ๊ด๋ฆฌํด ๋ด์ผ์ง ๐