학습 기록

Python 공부 기록 #21 - configparser로 파일 경로와 설정값을 ini 파일로 분리하기

0. 목차

  • 인트로: 설정 하나 바꾸려고 코드를 열었다
  • configparser는 어떤 도구일까?
  • config.ini 파일 만들기
  • Python에서 설정값 읽어오기
  • 숫자와 참·거짓 값 가져오기
  • 크롤링 코드와 로그 설정에 연결하기
  • 삽질했던 부분들
  • 아웃트로: 코드와 설정을 따로 관리하기

1. 인트로: 설정 하나 바꾸려고 코드를 열었다

지난 글에서는 RotatingFileHandler로 로그 파일의 최대 크기와 보관 개수를 정해봤습니다.

근데 저장 폴더를 바꾸거나 로그 용량을 조절할 때마다 Python 파일을 열어 숫자와 경로를 직접 고쳐야 했습니다. 회사 PC와 집 PC의 폴더 구조가 다르면 같은 코드 안에서 경로를 계속 바꾸게 되고요.

작은 수정이라도 따옴표나 숫자를 잘못 건드리면 코드가 실행되지 않을 수 있었습니다.

자주 바뀌는 값은 코드 밖에 두고, Python은 그 값을 읽기만 하게 만들면 편하지 않을까?

찾아보니까 Python 기본 모듈인 configparser로 .ini 설정 파일을 읽을 수 있었습니다. 오늘은 파일 경로와 로그 설정을 코드에서 분리해 봤습니다.


2. configparser는 어떤 도구일까?

configparser는 .ini 형식의 설정 파일을 읽고 쓰는 Python 기본 모듈입니다. 따로 설치할 필요가 없습니다.

.ini 파일은 설정을 주제별 구역으로 나누고, 각 값을 이름 = 값 형태로 적습니다.

[PATH]
input_file = results/blog_posts.xlsx
log_file = logs/crawler.log

[LOG]
max_bytes = 5242880
backup_count = 5

[PATH]와 [LOG]는 설정을 묶는 구역입니다. 그 아래에는 Python 코드에서 꺼내 쓸 이름과 값을 적었습니다.

코드를 모르는 사람도 경로나 숫자 정도는 비교적 쉽게 바꿀 수 있다는 점이 괜찮아 보였습니다.


3. config.ini 파일 만들기

먼저 Python 파일과 같은 폴더에 config.ini를 만들었습니다.

[PATH]
input_file = results/blog_posts.xlsx
output_folder = results/checked
log_file = logs/crawler.log

[LOG]
max_bytes = 5242880
backup_count = 5

[CRAWLER]
headless = true

경로에는 Windows의 역슬래시 대신 슬래시를 썼습니다. Python의 Path가 Windows에서도 슬래시가 들어간 경로를 처리해 주기 때문에 보기 편했습니다.

max_bytes에는 5MB에 해당하는 5 * 1024 * 1024의 계산 결과인 5242880을 적었습니다. .ini 파일에는 Python 계산식을 넣는 것이 아니라 실제 값을 적어야 했습니다.


4. Python에서 설정값 읽어오기

이제 configparser로 파일을 읽어봤습니다.

import configparser
from pathlib import Path

config = configparser.ConfigParser()
config.read("config.ini", encoding="utf-8")

input_file = Path(config["PATH"]["input_file"])
output_folder = Path(config["PATH"]["output_folder"])

print("입력 파일:", input_file)
print("저장 폴더:", output_folder)

먼저 ConfigParser()로 설정을 담을 객체를 만들었습니다. read()에는 읽을 파일 이름과 한글 깨짐을 막기 위한 encoding="utf-8"을 넣었습니다.

config["PATH"]["input_file"]은 [PATH] 구역에서 input_file 값을 가져옵니다. 읽어온 값은 문자열이므로 파일 경로로 쓰기 편하게 Path()로 감쌌습니다.

설정 파일의 값만 바꿔도 출력되는 경로가 달라졌습니다. 이거 되겠는데? 싶었습니다.


5. 숫자와 참·거짓 값 가져오기

.ini에서 읽은 값은 기본적으로 모두 문자열입니다. 로그 용량과 보관 개수는 숫자로 바꿔야 했습니다.

max_bytes = config.getint("LOG", "max_bytes")
backup_count = config.getint("LOG", "backup_count")
headless = config.getboolean("CRAWLER", "headless")

print(type(max_bytes))
print(type(headless))

getint()는 값을 정수로, getboolean()은 True 또는 False로 가져옵니다.

true, yes, on, 1은 참으로 읽을 수 있고 false, no, off, 0은 거짓으로 읽을 수 있다고 합니다. 직접 int()나 조건문을 반복해서 쓰지 않아도 돼서 코드가 짧아졌습니다.


6. 크롤링 코드와 로그 설정에 연결하기

지난 글의 로그 설정과 합쳐봤습니다.

import configparser
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path

config = configparser.ConfigParser()
config.read("config.ini", encoding="utf-8")

input_file = Path(config["PATH"]["input_file"])
log_file = Path(config["PATH"]["log_file"])
max_bytes = config.getint("LOG", "max_bytes")
backup_count = config.getint("LOG", "backup_count")

log_file.parent.mkdir(parents=True, exist_ok=True)

handler = RotatingFileHandler(
    log_file,
    maxBytes=max_bytes,
    backupCount=backup_count,
    encoding="utf-8"
)

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[handler]
)

if input_file.exists():
    logging.info("입력 파일 확인 완료: %s", input_file)
else:
    logging.error("입력 파일을 찾을 수 없습니다: %s", input_file)

이제 Python 코드에는 특정 폴더나 로그 용량이 직접 들어가지 않습니다. 회사 PC에서는 config.ini의 경로만 회사 폴더로 바꾸고, 집에서는 개인 폴더로 바꾸면 됩니다.

실행 순서는 그대로 두고 환경마다 달라지는 값만 설정 파일에서 관리할 수 있게 됐습니다.


7. 삽질했던 부분들

처음에는 config.ini에 아래처럼 숫자 계산식을 적었습니다.

max_bytes = 5 * 1024 * 1024

근데 getint()는 이 문장을 계산하지 못했습니다. .ini는 Python 코드가 아니므로 5242880처럼 계산이 끝난 숫자를 적어야 했습니다.

또 구역 이름을 [Path]로 만들고 코드에서는 config["PATH"]로 찾았습니다. 설정 이름은 대소문자 차이 때문에 헷갈릴 수 있어서 구역 이름을 모두 대문자로 맞췄습니다.

마지막으로 작업 스케줄러에서 실행하니 config.ini를 찾지 못하는 경우가 있었습니다. 실행 기준 폴더가 Python 파일 위치와 다를 수 있기 때문이었습니다. 다음처럼 현재 Python 파일을 기준으로 경로를 만들면 해결할 수 있었습니다.

base_dir = Path(__file__).resolve().parent
config_file = base_dir / "config.ini"

config.read(config_file, encoding="utf-8")

자동 실행에서 상대 경로가 이상하다면 현재 작업 폴더보다 __file__ 기준 경로를 먼저 확인하면 되겠습니다.


8. 아웃트로: 코드와 설정을 따로 관리하기

오늘은 configparser로 파일 경로와 로그 설정을 .ini 파일에 분리해 봤습니다.

핵심 정리

  1. .ini 파일은 설정값을 구역별로 나눠 저장함
  2. ConfigParser().read()로 설정 파일을 읽음
  3. 경로는 문자열로 읽은 뒤 Path()로 바꿈
  4. 숫자는 getint(), 참·거짓은 getboolean()으로 가져옴
  5. 자동 실행에서는 __file__ 기준으로 설정 파일 경로를 정함

이제 할 수 있는 것들

  • 회사 PC와 집 PC의 경로를 설정 파일에서 바꾸기
  • 로그 용량과 보관 개수를 코드 수정 없이 조절하기
  • 크롤링 실행 옵션을 한곳에서 관리하기
  • Python 코드는 그대로 두고 실행 환경만 변경하기

Python 공부를 처음 시작했을 때는 모든 값을 코드 안에 적는 게 당연하다고 생각했습니다. 근데 업무 자동화 파일이 늘어나니 코드와 설정을 나누는 이유가 조금씩 보였습니다.

다음 포스팅에서는 설정 파일이 없거나 필요한 항목이 빠졌을 때 프로그램이 바로 멈추지 않도록 기본값을 넣고, 오류 메시지를 로그에 남기는 방법을 공부해 보겠습니다.