카카오클라우드 AIaaS 교육/AIaaS를 위한 머신러닝&AI

[스나이퍼팩토리] AIaaS 마스터 클래스 9주차 DAY 41 - python 심화

mangji2 2025. 5. 26. 11:47

[1] python 심화 수업 복습 - 파일 입출력, 예외 처리, 함수형 프로그래밍

 1. 파일 입출력 (File I/O)

  • 데이터 영속성: 프로그램 종료 후에도 정보 보존 가능
  • 대용량 데이터 처리: 메모리에 전부 못 담는 데이터 처리
  • 데이터 공유: 다른 프로그램, 사용자 간 공유
  • 설정 관리/로깅: 환경설정, 로그 기록 등에도 활용

🔹 파일 열기와 닫기

file = open('파일경로', '모드')
# 작업 수행
file.close()

🔹 파일 열기 모드 요약

'r' 읽기
'w' 쓰기 (기존 내용 삭제)
'a' 추가 (기존 내용 유지)
'b' 바이너리 모드
't' 텍스트 모드 (기본값)
'x' 배타적 생성 (존재 시 오류)
'+' 읽기/쓰기 모두 가능

🔹 경로

  • 절대 경로: 루트부터 지정
  • 상대 경로: 현재 디렉토리를 기준으로 지정

🔹 파일 읽기

read()       # 전체 내용 읽기
readline()   # 한 줄 읽기
readlines()  # 모든 줄 리스트로 반환
  • 파일은 iterable이므로 for line in file: 가능

🔹 파일 쓰기

write('문자열')
writelines(['a\n', 'b\n'])

🔹 바이너리 파일 처리

  • 모드: 'rb', 'wb', 'ab'
  • encoding 미사용, 직접 byte 처리
  • 예시: 이미지, 오디오, zip 파일 등
#바이너리 파일 쓰기
data = bytes([0*48, 0*65, 0*6C, 0*6F])

with open('binary_data.bin', 'wb') as file:
	file.write(data)
    
#이미지 파일 복사하기
    	#원본 파일 읽기
        with open(source_file, 'rb') as source:
        	image_data = source.read()
        #대상 파일에 쓰기
        with open(destination_file,'wb') as destination:
        	destination.write(image_data)

🔹 CSV 파일 처리

  • csv 모듈 사용: csv.reader, csv.writer, csv.DictReader, csv.DictWriter
  • newline='' 설정 중요 (Windows 줄바꿈 문제 방지)

🔹 with 구문

with open('파일.txt', 'r') as f:
    content = f.read()

 

def xor_encrypt_decrypt(intput_file, output_file, key):
	
    with open(input_file,'rb') as infile:
    	data = infile.read()
    key_bytes = key.encode() if isinstance(key,str) else bytes
    key_len = len(key_bytes)
    
    encrypted_data = bytearray(len(data))
	for i in range(len(data)):
    	encrypted_data[i] = data[i] ^ key_bytes[i%key_len]
    
    with open(output_file,'wb') as outfile:
    	outfile.write(encrypted_data)
#암호화
xor_encrypt_decrypt('example.txt','secret.enc','mykey123')
#복호화
xor_encrypt_decrypt('secret.enc','example.txt','mykey123')
  • 컨텍스트 매니저 사용으로 자동으로 파일 닫힘 (예외 발생해도 안전)

 2. 예외 처리 (Exception Handling)

🔹 예외와 에러

구분 설명
에러 문법 오류 등 실행 자체 불가
예외 실행 중 발생하는 문제로, 처리 가능

🔹 예외 처리 구문

try:
    실행할_코드
except 예외종류 as e:
    처리_코드
else:
    예외 없이 성공했을 때
finally:
    항상 실행되는 코드 (자원 정리 등)

🔹 주요 예외 종류

  • FileNotFoundError, PermissionError
  • TypeError, ValueError, ZeroDivisionError
  • IndexError, KeyError, NameError, AttributeError

🔹 사용자 정의 예외

class MyCustomError(Exception):
    pass

🔹 예외 체이닝

raise CustomError("메시지") from 원래예외

🔹 assert 문

assert 조건, "실패 메시지"
  • 조건이 False면 AssertionError 발생

🔹 logging 모듈

import logging
logging.basicConfig(level=logging.ERROR)
logging.error("에러 메시지")
  • 수준: DEBUG < INFO < WARNING < ERROR < CRITICAL

 3. 함수형 프로그래밍

🔹 개념

  • 순수 함수: 같은 입력 → 항상 같은 출력, 부작용 없음
  • 불변성: 데이터 변경 없이 새 객체 생성
  • 고차 함수: 함수를 인자로 받거나 반환
  • 일급 함수: 함수를 변수처럼 취급

🔹 Python 함수형 프로그래밍 도구

  • lambda: 이름 없는 익명 함수
  • map(func, iterable): 요소에 함수 적용
  • filter(func, iterable): 조건 만족하는 요소 필터링
  • reduce(func, iterable): 누적 처리 (from functools import reduce)

🔹 클로저 (Closure)

def outer():
    x = 10
    def inner():
        return x
    return inner
  • 내부 함수가 외부 변수 기억

🔹 데코레이터 (Decorator)

def decorator(func):
    def wrapper(*args, **kwargs):
        print("전처리")
        result = func(*args, **kwargs)
        print("후처리")
        return result
    return wrapper

@decorator
def say_hello():
    print("Hello")
  • 기존 함수에 기능을 추가하는 함수

🔹 커링 (Currying) & 부분 적용

from functools import partial

def power(base, exponent):
    return base ** exponent

square = partial(power, exponent=2)

●  정리

  • 파일 입출력은 텍스트, CSV, JSON, 바이너리 등 모든 포맷을 with로 안전하게 처리해야 함
  • 예외 처리를 통해 코드의 안정성과 사용자 경험을 개선하고, logging으로 오류 기록
  • 함수형 프로그래밍은 코드의 예측 가능성과 가독성을 높이는 강력한 도구이며, 고차 함수, 람다, 클로저, 데코레이터 등을 적극 활용

[2] 과제

파일 입출력, 예외처리에 관한 과제를 정리해 보도록 하겠다.

 

문제 

다양한 파일 형식(텍스트, CSV, JSON, 바이너리)을 읽고 쓸 수 있는 파일 처리기를 구현하는 문제다.

구현된 파일 처리기는 파일이 존재하지 않거나, 접근 권한이 없거나, 파일 형식이 잘못된 경우 등 다양한 오류 상황에 적절히 대응하도록 구현해 보았다.

#과제 파일 처리기 구현
#다양한 유형의 파일(텍스트, CSV, JSON, 바이너리)을 읽고 쓸 수 있어야 합니다
#파일이 존재하지 않거나, 권한이 없거나, 형식이 잘못된 경우 등 다양한 오류 상황을 적절히 처
#사용자 정의 예외 계층 구조를 설계하고 구현
#오류 발생 시 로깅을 통해 문제를 기록
#모든 파일 작업은 컨텍스트 매니저(`with` 구문)를 사용

 

코드

import os
import csv
import json
import logging

# 로깅 설정
logging.basicConfig(
    filename='log.txt',
    level=logging.ERROR,
    format='%(asctime)s [%(levelname)s] %(message)s'
)
 
class FileHandlerError(Exception):
    """FileHandler에서 발생하는 일반적인 오류를 위한 사용자 정의 예외."""
    pass

class FileNotFoundErrorCustom(FileHandlerError):
    """파일을 찾을 수 없을 때 발생하는 사용자 정의 예외."""
    pass

class PermissionErrorCustom(FileHandlerError):
    """파일 접근 권한이 없을 때 발생하는 사용자 정의 예외."""
    pass

class InvalidFormatError(FileHandlerError):
    """지원하지 않는 파일 형식이 지정되었을 때 발생하는 사용자 정의 예외."""
    pass

class FileHandler:
    def __init__(self, file_path, mode='r', file_type='text'):
        self.file_path = file_path
        self.mode = mode
        self.file_type = file_type
        self.file = None

    def __enter__(self):
        if not os.path.exists(self.file_path) and 'r' in self.mode:
            raise FileNotFoundErrorCustom(f"File not found: {self.file_path}")
        try:
            self.file = open(self.file_path, self.mode, newline='', encoding='utf-8' if 'b' not in self.mode else None)
            return self
        except PermissionError:
            raise PermissionErrorCustom(f"Permission denied: {self.file_path}")
        except Exception as e:
            logging.error(f"Unexpected error opening file: {e}")
            raise FileHandlerError(f"Unexpected error: {e}")

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()

    def read(self):
        try:
            if self.file_type == 'text':
                return self.file.read()
            elif self.file_type == 'csv':
                reader = csv.reader(self.file)
                return list(reader)
            elif self.file_type == 'json':
                return json.load(self.file)
            elif self.file_type == 'binary':
                return self.file.read()
            else:
                raise InvalidFormatError(f"Unsupported file type: {self.file_type}")
        except Exception as e:
            logging.error(f"Read error in {self.file_path}: {e}")
            raise FileHandlerError(f"Read error: {e}")

    def write(self, data):
        try:
            if self.file_type == 'text':
                self.file.write(data)
            elif self.file_type == 'csv':
                writer = csv.writer(self.file)
                writer.writerows(data)
            elif self.file_type == 'json':
                json.dump(data, self.file, indent=4)
            elif self.file_type == 'binary':
                self.file.write(data)
            else:
                raise InvalidFormatError(f"Unsupported file type: {self.file_type}")
        except Exception as e:
            logging.error(f"Write error in {self.file_path}: {e}")
            raise FileHandlerError(f"Write error: {e}")

 

우선 로깅 설정 함수를 보면

logging.basicConfig(
    filename='log.txt',
    level=logging.ERROR,
    format='%(asctime)s [%(levelname)s] %(message)s'
)

 

로그 메세지를 log.txt 파일에 저장하도록 지정하고, error 레벨 이상의 메세지만 기록하도록 설정했다. 

그리고 format 변수에 로그 메세지의 형식을 지정하였다.

 


def __enter__ 메소드는

with FileHandler(...) as f: 구문에서 with 문이 시작될 때 자동으로 호출된다.

def __enter__(self):
        if not os.path.exists(self.file_path) and 'r' in self.mode:
            raise FileNotFoundErrorCustom(f"File not found: {self.file_path}")
        try:
            self.file = open(self.file_path, self.mode, newline='', encoding='utf-8' if 'b' not in self.mode else None)
            return self
        except PermissionError:
            raise PermissionErrorCustom(f"Permission denied: {self.file_path}")
        except Exception as e:
            logging.error(f"Unexpected error opening file: {e}")
            raise FileHandlerError(f"Unexpected error: {e}")

 

if not os.path.exists(self.file_path) and 'r' in self.mode: 

이 조건문을 통해 파일이 존재하지 않고 읽기 모드로 열려고 할 경우 FileNotFoundErrorCustom 예외를 발생시킨다.

또한 try .. except 블록을 통해 파일을 여는 과정에서 발생할 수 있는 예외를 처리하고 성공적으로 파일을 열면 self를 반환하여 as f: 부분에 할당될 수 있도록 하였다.

 

 

def read(self) 메소드는는 현재 file_type에 따라 파일을 읽고 그 내용을 반환한다.

def read(self):
        try:
            if self.file_type == 'text':
                return self.file.read()
            elif self.file_type == 'csv':
                reader = csv.reader(self.file)
                return list(reader)
            elif self.file_type == 'json':
                return json.load(self.file)
            elif self.file_type == 'binary':
                return self.file.read()
            else:
                raise InvalidFormatError(f"Unsupported file type: {self.file_type}")
        except Exception as e:
            logging.error(f"Read error in {self.file_path}: {e}")
            raise FileHandlerError(f"Read error: {e}")

 

각각의 file type에 따라 파일을 읽고 지원하지 않는 file_type인 경우 InvalidFormatError 예외를 발생시키는 코드를 작성하였다.

try ... except 블록으로 읽기 과정에서 발생할 수 있는 모든 예외를 잡아서 logging.error로 기록하고 FileHandlerError 예외를 다시 발생시킨다.


 

정리하면

with문을 사용해 파일을 열고 닫는 과정을 자동으로 처리해주고 text,csv,json,binary등 다양한 형식의 파일 읽기/쓰기를 하나의 클래스에서 통합적으로 처리하게 하여 재사용성을 높아지게 하였다.

또한 파일이 없거나, 권한 문제가 있거나, 예상치 못한 오류가 발생할 경우 사용자 정의 예외를 발생시키고, 로그 파일에 오류 정보를 기록하여 디버깅을 용이하게 할 수 있는 것 같다.


본 후기는 [카카오엔터프라이즈x스나이퍼팩토리] 카카오클라우드로 배우는 AIaaS 마스터 클래스 (B-log) 리뷰로 작성 되었습니다.