Загружаем каталог…
Загружаем каталог…
Python으로 실제 문제 해결하기: 설계 과정, 표준 라이브러리, 로깅, 데코레이터까지 Python 문법을 한 번 훑고 나면 이런 고민이 생긴다. 문법은 알겠는데 실제 프로그램은 어떻게 만들어야 할까? 실제 개발에서는 문법을 많이 외우는 것보다 문제를 작게 나누고 적절한 도구를 조합하는 능력 이 더 중요하다. 이 글에서는 간단한 로그 백업 프로그램을 설계하는 과정을 중심으로 다음 내용을 연결해본다. 문제를 코드로 바꾸는 과정 표준 라이브러리 활용 pathlib , datetime , shutil 명령행 인자 처리 로깅 assert lambda 데코레이터 예외 처리와 재시도 코딩 전에 문제를 정의한다 다음과 같은 요구사항이 있다고 해보자. 특정 폴더의 로그 파일을 날짜별 ZIP 파일로 백업하고 싶다. 바로 코드를 작성하기보다 먼저 필요한 조건을 정리한다. 백업 대상 폴더는 어디인가? 백업 파일은 어디에 저장할 것인가? 백업 파일명은 어떻게 만들 것인가? 대상 폴더가 없으면 어떻게 처리할 것인가? 작업 결과를 어떻게 확인할 것인가? 실패했을 때 어떤 정보를 남길 것인가? 이렇게 문제를 구체화하면 코드로 옮겨야 할 단계가 보이기 시작한다. 문제를 작은 단계로 나누기 이번 프로그램은 다음처럼 나눌 수 있다. 원본 디렉터리 확인 백업 디렉터리 생성 현재 날짜와 시간으로 파일명 생성 ZIP 파일 생성 처리 결과 기록 오류 발생 시 예외 처리 각 단계에 이름을 붙일 수 있다면 함수로 분리할 후보가 된다. def create_backup_directory(): ... def create_backup_filename(): ... def compress_directory(): ... 문제를 먼저 나누고 나면 코드의 구조도 자연스럽게 잡힌다. 표준 라이브러리를 먼저 확인하자 운영체제의 외부 zip 명령을 직접 실행하는 방법도 있지만 Python 자체에도 압축 기능이 있다. 예를 들어 zipfile 을 사용할 수 있다. from zipfile import ZipFile 폴더 전체를 간단히 압축하는 상황이라면 shutil.make_archive() 도 편리하다. import shutil shutil.make_archive( "backup", "zip", "logs", ) 외부 프로그램을 호출하기 전에 Python 표준 라이브러리로 해결할 수 있는지 먼저 확인하는 습관 을 들이면 운영체제 의존성과 추가 설치를 줄일 수 있다. pathlib 로 경로 다루기 파일 경로를 문자열로 직접 이어붙이는 것보다 Path 를 사용하면 코드가 명확해진다. from pathlib import Path source_dir = Path("logs") backup_dir = Path("backups") 하위 경로 연결: log_file = ( source_dir / "access.log" ) 폴더 생성: backup_dir.mkdir( parents=True, exist_ok=True, ) 존재 여부 확인: if not source_dir.exists(): print( "원본 폴더가 없습니다." ) 날짜 기반 파일명 만들기 백업 파일끼리 겹치지 않게 현재 날짜와 시간을 파일명에 넣을 수 있다. from datetime import datetime now = datetime.now() timestamp = now.strftime( "%Y%m%d_%H%M%S" ) print(timestamp) 예: 20260929_231500 파일명: backup_name = ( f"logs_{timestamp}" ) 백업 함수 만들기 이제 기능을 함수로 묶어보자. from datetime import datetime from pathlib import Path import shutil def backup_directory( source_dir: Path, backup_dir: Path, ) -> Path: if not source_dir.exists(): raise FileNotFoundError( f"원본 폴더가 없습니다: " f"{source_dir}" ) backup_dir.mkdir( parents=True, exist_ok=True, ) timestamp = ( datetime .now() .strftime( "%Y%m%d_%H%M%S" ) ) archive_base = ( backup_dir / f"logs_{timestamp}" ) archive_path = ( shutil.make_archive( str(archive_base), "zip", root_dir=source_dir, ) ) return Path( archive_path ) 사용: source = Path("logs") destination = Path( "backups" ) backup_file = ( backup_directory( source, destination, ) ) print(backup_file) 처음부터 모든 기능을 넣지 않는다 작은 프로그램도 한 번에 완성하려고 하면 디버깅이 어려워질 수 있다. 다음처럼 단계적으로 발전시키는 편이 좋다. 1단계 폴더 하나를 압축한다. 2단계 날짜 기반 파일명을 적용한다. 3단계 백업 폴더를 자동 생성한다. 4단계 예외 처리를 추가한다. 5단계 로그를 남긴다. 6단계 필요하다면 오래된 백업을 정리한다. 작동하는 최소 버전부터 만들고 조금씩 개선하는 방식 이 문제를 찾고 수정하기 쉽다. 자주 사용하는 표준 라이브러리 Python에는 설치 없이 바로 사용할 수 있는 모듈이 많이 포함되어 있다. 모듈 주요 용도 pathlib 파일과 디렉터리 경로 json JSON 변환 datetime 날짜와 시간 logging 로그 기록 sys Python 실행 환경 os 운영체제 관련 기능 shutil 파일·폴더 고수준 작업 zipfile ZIP 압축 파일 re 정규 표현식 argparse CLI 인자 처리 collections 확장 자료구조 모든 모듈을 외우기보다는 필요한 기능이 생겼을 때 표준 라이브러리에 있는지 찾아보는 습관 이 중요하다. sys 로 실행 환경 확인하기 import sys print(sys.version) print(sys.version_info) 명령행 인자는 sys.argv 로도 읽을 수 있다. import sys print(sys.argv) 다음처럼 실행했다고 하자. python app.py users.json 첫 번째 추가 인자는 다음처럼 가져올 수 있다. filename = sys.argv[1] 간단한 경우에는 충분하지만 옵션이 많아지면 argparse 가 더 편리하다. argparse 로 CLI 만들기 import argparse parser = ( argparse.ArgumentParser() ) parser.add_argument( "--source", required=True, ) parser.add_argument( "--destination", default="backups", ) args = parser.parse_args() print(args.source) print(args.destination) 실행: python backup.py --source logs --destination backups 이제 소스 코드 수정 없이 실행할 때 백업 대상과 저장 위치를 바꿀 수 있다. print() 와 logging 의 차이 개발 초반에는 print() 만으로도 상태를 확인할 수 있다. 하지만 프로그램을 실제로 반복 실행한다면 다음 정보가 필요해질 수 있다. 언제 발생했는가? 정상 정보인가, 경고인가, 오류인가? 어떤 모듈에서 발생했는가? 예외의 traceback은 무엇인가? 파일에도 저장해야 하는가? 이런 상황에서는 logging 모듈이 적합하다. 기본 로깅 import logging logging.basicConfig( level=logging.INFO, ) logging.debug( "디버그 정보" ) logging.info( "정상 처리" ) logging.warning( "주의가 필요함" ) logging.error( "오류 발생" ) logging.critical( "치명적인 오류" ) 대표적인 로그 수준은 다음 순서로 심각도가 높아진다. DEBUG INFO WARNING ERROR CRITICAL 모듈별 로거 만들기 import logging logger = logging.getLogger( __name__ ) logger.info( "프로그램 시작" ) __name__ 을 사용하면 로그가 어느 모듈에서 발생했는지 구분하기 쉬워진다. 예외 traceback 기록하기 try: result = 10 / 0 except ZeroDivisionError: logger.exception( "계산 중 오류 발생" ) logger.exception() 은 현재 처리 중인 예외의 traceback을 함께 기록한다. 단순히 오류 메시지만 출력하는 것보다 문제 원인을 추적하기 쉽다. assert 는 언제 사용할까? 개발 중 코드 내부에서 반드시 참이라고 가정하는 조건 을 확인할 때 사용할 수 있다. users = ["kim"] assert len(users) > 0 조건이 거짓이면 AssertionError 가 발생한다. 메시지도 추가할 수 있다. assert ( len(users) > 0 ), "사용자 목록은 비어 있으면 안 됩니다." 외부 입력 검증에 assert 를 쓰지 말자 다음처럼 사용자 입력이나 비즈니스 규칙 검증에 의존하는 것은 적절하지 않다. assert age >= 0 Python을 최적화 옵션으로 실행하면 assert 문이 제거될 수 있기 때문이다. 외부 입력은 명시적인 조건과 예외로 검사하는 편이 안전하다. if age < 0: raise ValueError( "나이는 0 이상이어야 합니다." ) lambda 아주 짧은 일회성 함수를 표현할 때 사용할 수 있다. users = [ { "name": "kim", "score": 85, }, { "name": "lee", "score": 95, }, { "name": "park", "score": 80, }, ] 점수 기준 정렬: users.sort( key=lambda user: ( user["score"] ) ) 복잡한 로직이라면 일반 함수가 더 읽기 쉽다. def get_score(user): return user["score"] users.sort( key=get_score ) 데코레이터란? 데코레이터는 기존 함수를 감싸서 공통 기능을 추가하는 구조다. 예를 들어 함수 실행 시간을 측정해보자. from functools import wraps from time import perf_counter def measure_time(func): @wraps(func) def wrapper( *args, **kwargs, ): start = ( perf_counter() ) result = func( *args, **kwargs, ) elapsed = ( perf_counter() - start ) print( f"{func.__name__}: " f"{elapsed:.4f}초" ) return result return wrapper 적용: @measure_time def process_data(): return sum( range(1_000_000) ) 호출: process_data() 개념적으로 다음 코드와 비슷하다. process_data = ( measure_time( process_data ) ) 데코레이터는 어디에서 사용할까? 실제 Python 코드에서는 다음과 같은 용도로 자주 볼 수 있다. 로그인 여부 확인 권한 검사 실행 시간 측정 로깅 캐싱 재시도 트랜잭션 API 라우팅 예를 들어 FastAPI의 다음 문법도 데코레이터다. @app.get("/users") def get_users(): ... Python 웹 개발을 공부한다면 데코레이터의 동작 원리를 알아두면 프레임워크 코드를 이해하기 쉬워진다. 재시도 로직 외부 네트워크 요청은 일시적으로 실패할 수 있다. 간단한 재시도 함수를 만들어보자. from time import sleep def retry( func, attempts=3, delay=1, ): last_error = None for attempt in range( attempts ): try: return func() except ConnectionError as error: last_error = error is_last = ( attempt == attempts - 1 ) if not is_last: sleep(delay) raise last_error 중요한 점은 다음처럼 모든 예외를 무조건 재시도하지 않는 것이다. # 권장하지 않는 예 except: ... 프로그래밍 오류까지 재시도해버리면 실제 버그를 숨길 수 있다. 재시도 가능한 예외만 구체적으로 지정하는 편이 좋다. 실제 문제 해결 흐름 Python으로 프로그램을 만들 때 다음 순서를 반복하면 좋다. 1. 문제 정의 무엇을 자동화하거나 해결할 것인지 명확히 한다. 2. 입력과 출력 결정 무엇을 받아 어떤 결과를 만들지 정한다. 3. 작은 단계로 분해 각 단계에 이름을 붙일 수 있을 정도로 나눈다. 4. 기존 도구 조사 내장 함수나 표준 라이브러리에 이미 필요한 기능이 있는지 확인한다. 5. 최소 동작 버전 구현 먼저 정상적인 경우가 동작하도록 만든다. 6. 예외 처리 현실적으로 어떤 실패가 발생할지 생각한다. 7. 로그 추가 문제가 발생했을 때 원인을 추적할 정보를 남긴다. 8. 리팩터링 중복을 없애고 지나치게 큰 함수를 나눈다. 완성 예제: 로그 백업 CLI 지금까지 배운 내용을 하나의 프로그램으로 연결해보자. import argparse import logging import shutil from datetime import datetime from pathlib import Path logging.basicConfig( level=logging.INFO, format=( "%(asctime)s " "%(levelname)s " "%(message)s" ), ) logger = logging.getLogger( __name__ ) def backup_directory( source_dir: Path, backup_dir: Path, ) -> Path: if not source_dir.exists(): raise FileNotFoundError( f"원본 폴더 없음: " f"{source_dir}" ) backup_dir.mkdir( parents=True, exist_ok=True, ) timestamp = ( datetime .now() .strftime( "%Y%m%d_%H%M%S" ) ) archive_base = ( backup_dir / f"backup_{timestamp}" ) archive_path = ( shutil.make_archive( str(archive_base), "zip", root_dir=source_dir, ) ) return Path( archive_path ) def parse_args(): parser = ( argparse.ArgumentParser() ) parser.add_argument( "--source", required=True, ) parser.add_argument( "--destination", default="backups", ) return parser.parse_args() def main(): args = parse_args() try: archive = ( backup_directory( Path(args.source), Path( args.destination ), ) ) except FileNotFoundError as error: logger.error(error) return except OSError: logger.exception( "백업 처리 중 " "시스템 오류" ) return logger.info( "백업 완료: %s", archive, ) if __name__ == "__main__": main() 실행: python backup.py --source logs --destination backups 이 프로그램에는 여러 Python 핵심 개념이 함께 사용된다. 함수 모듈 pathlib datetime shutil argparse 예외 처리 로깅 __name__ == "__main__" 문법을 따로 외우던 단계에서 실제 문제를 해결하는 프로그램으로 연결되는 지점 이다. 핵심 정리 실제 개발에서는 문법 암기보다 문제를 작은 단계로 분해하는 능력이 중요하다. 외부 명령이나 패키지를 사용하기 전에 Python 표준 라이브러리에서 해결할 수 있는지 확인해볼 수 있다. pathlib , datetime , shutil , argparse , logging 은 자동화 프로그램에서 활용도가 높다. logging 을 사용하면 운영 중 발생한 문제를 print() 보다 체계적으로 추적할 수 있다. assert 는 내부 개발 가정을 확인하는 도구이며 외부 입력 검증에는 의존하지 않는 편이 좋다. lambda 는 짧은 일회성 함수에 적합하지만 복잡한 로직은 일반 함수가 읽기 쉽다. 데코레이터는 기존 함수에 공통 기능을 추가하는 구조이며 웹 프레임워크에서도 자주 사용된다. 프로그램은 최소 동작 버전부터 만들고 예외 처리, 로깅, 리팩터링을 점진적으로 추가하는 방식이 유지보수에 유리하다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
Python으로 실제 문제 해결하기: 설계 과정, 표준 라이브러리, 로깅, 데코레이터까지. Python으로 실제 문제 해결하기: 설계 과정, 표준 라이브러리, 로깅, 데코레이터까지 Python 문법을 한 번 훑고 나면 이런 고민이 생긴다. 문법은 알겠는데 실제 프로그램은 어떻게 만들어야 할까? 실제 개발에서는 문법을 많이 외우는 것보다 문제를 작게 나누고 적절한 도구를 조합하는 능력 이 더 중요하다. 이 글에서는 간단한 로그 백업 프로그램을 설계하는 과정을 중심으로 다음 내용을 연결해본다. 문제를 코드로 바꾸는 과정 표준 라이브러리 활용 pathlib , datetime , shutil 명령행 인자 처리 로깅 assert lambda 데코레이터 예외 처리와 재시도 코딩 전에 문제를 정의한다 다음과 같은…
Открыть источник