학습목표¶

  • 예외처리
  • 파일 입출력
  • 예외? 에러보다는 마일드한 에러(의도하지 않는 상황발생)
  • 예외처리를 통해서 예외상황을 알리고 대처할 수 있도록 하는 방법
  • 시스템의 비정상적인 종료를 막고 정상적인 흐름으로 시스템을 종료 시키기 위한 방법
  • 파이썬 xxxxError -> Exception
  • raise xxxxError 발생
  • Exception이 예외의 최상위 부모 try : 예외발생 코드 except : try 블록에서 발생된 예외를 처리하는 영역 else : 예외가 발생하지 않았을 떄 수행하는 영역 finally : 예외 발생여부와 상관없이 항상 수행하는 영역
In [8]:
lst = [1,2,3] 

try :
    for idx in range(len(lst)+1):
        print(lst[idx])
except IndexError as e :
    print(f'{e} 예외 발생 함')
else :
    print('예외가 발생하지 않았을 때 수행하는 블럭')
finally :
    print('예외발생 여부와 상관없이 수행하는 블럭')
print('>>>> 정상종료')
    
1
2
3
list index out of range 예외 발생 함
예외발생 여부와 상관없이 수행하는 블럭
>>>> 정상종료
In [41]:
def getUserInfo():
    ''' 사용자의 정보를 입력받고, 형식 오류나 유효성을 처리하는 기능'''
    try:
        name = input('이름을 입력하세요 : ').strip()
        if not name.isalpha():
            raise ValueError('이름은 문자만 포함해야 합니다!!')
            
        age = input('나이를 입력하세요 : ').strip()
        if not age.isdigit():
            raise ValueError('나이는 숫자만 입력하세요!!')
        age = int(age)
        if age < 0 or age > 120:
            raise ValueError('1~120 사이의 숫자만 입력하세요!!')
            
        email = input('이메일 주소를 입력하세요 : ').strip()
        if '@' not in email or '.' not in email :
            raise ValueError('올바를 이메일 형식이 아닙니다!!')
        
        
    except Exception as ve:
        print(f'입력오류 {ve}')
    else :
        return {
            "name" : name,
            "age" : age,
            "email" : email
        }
    finally :
        print('입력정보를 정상적으로 처리 하였습니다!!')
        
        
        
In [42]:
result = getUserInfo()
print(result)
입력오류 이름은 문자만 포함해야 합니다!!
입력정보를 정상적으로 처리 하였습니다!!
None
In [81]:
# Quiz
# 예외처리 관점, 보안적 관점
# 매개변수로 전달받은 리스트타입 요소의 값을 거듭제곱해서 반환하여 출력하고자 한다

def lstPrt(lst: list) -> list:
    try:
        
        for idx in range(len(lst)):
            value = lst[idx]

            value = str(value)

            if not value.isdigit():
                raise ValueError(f'{value}는 숫자가 아닙니다!')
            value = int(value)
                
            print(f'{value ** 2}')

    except Exception as e:
        print(f'예외 발생: {e}')
    else:
        print('예외가 발생하지 않았을 때 수행하는 블럭')
    finally:
        print('예외 발생 여부와 상관없이 수행하는 블럭')
    print('>>>> 정상종료')
In [152]:
# Quiz
# 예외처리 관점 (요소의 타입), 보안적 관점(매개변수의 타입)
# 매개변수로 전달받은 리스트타입 요소의 값을 거듭제곱해서 반환하여 출력하고자 한다

# def lstPrt(lst: list) -> list:
#     if not isinstance(lst, list):
#         raise TypeError('매개변수 타입은 반드시 list 형태')
    
#     result = []
#     for data in lst:
#         if not isinstance(data, (int, float)):
#             print('숫자가 아닌 값이 포함 됨')
#             continue
#         else :
#             result.append(data ** 2)        
#     return result

import logging
# level
# debug, info, warning, error, critical
# format (%s -%s etc....)
# format key : asctime, levelname, name, message, etc .....z

logging.basicConfig(level=logging.WARNING, 
                    format="%(asctime)s - %(levelname)s - %(message)s", force=True)

# 예외, 보안, 로깅
def lstPrt(lst: list) -> list:
    
    # 보안관점에서 입력검증
    if not isinstance(lst, list):
        raise TypeError('매개변수 타입은 반드시 list 형태')
    
    result = []
    for idx, data in enumerate(lst):
        if not isinstance(data, (int, float)):
            logging.warning(f'{idx}에 숫자가 아닌 값이 포함된 타입 {type(data).__name__}')
            continue
        try :
            result.append(data ** 2)
        except Exception as e:
            logging.error(f'예상치 못한 예외 발생 : {e}')
    return result
    
In [153]:
tmp = [10,20,30,40,'seop',50,60]
result = lstPrt(tmp)
print(result)
2025-10-31 14:24:25,296 - WARNING - 4에 숫자가 아닌 값이 포함된 타입 str
[100, 400, 900, 1600, 2500, 3600]
In [87]:
# 에러메시지 노출 - print()
# 서버환경 print() -> 로깅(level=warning/info/error) 일반화된 메시지 제공이 안전
# import logging
import logging
logging.basicConfig(level=logging.WARNING)

data = 'jslim'
try:
    print(data ** 2)
except TypeError as t:
    logging.warning(f'숫자가 아닌값을 발견 : {data}')
WARNING:root:숫자가 아닌값을 발견 : jslim
In [ ]:
import logging
# level
# debug, info, warning, error, critical
# format (%s -%s etc....)
# format key : asctime, levelname, name, message, etc .....z

logging.basicConfig(level=logging.WARNING, 
                    format="%(asctime)s - %(levelname)s - %(message)s",
                   filename='shieldus.log',
                   filemode='a')

파일 입출력¶

  • 제공하는 함수는 대부분 예외발생시키고 있음.
  • txt, json 허용 (.csv, .xls 분석을 위한 데이터타입이 필요한데 Pandas - DataFrame)
  • open(filePath, mode='r|w|a|b', encoding='utf-8')
  • with open() as file:
In [183]:
import logging
# level
# debug, info, warning, error, critical
# format (%s -%s etc....)
# format key : asctime, levelname, name, message, etc .....z

logging.basicConfig(level=logging.WARNING, 
                    format="%(asctime)s - %(levelname)s - %(message)s", force=True)

filePath = './data/greeting.txt'.strip()
try :
    file = open(filePath, mode='r', encoding='utf-8')
except:
    logging.error(f'파일을 열 수 없습니다.')
    
print('type - ', type(file))
#print('dir - ', dir(file))
print(file.read(), type(file.read())) # str
# print(file.readlines(), type(file.readlines())) # list
file.close()
type -  <class '_io.TextIOWrapper'>
강사님과 함께하는 즐겁지 아니하지 아니한 파이썬수업
그렇지만 열공하자
오늘은 즐거운 금요일
불금인데.....방콕이 답이다 <class 'str'>
In [208]:
# with open은 file.close()를 포함하고 있음 안써도 됨

filePath = './data/greeting.txt'.strip()
with open(filePath, mode='r', encoding='utf-8') as file :
    lst = file.readlines()
    for txt in lst:
        print(txt.strip('\n'))
강사님과 함께하는 즐겁지 아니하지 아니한 파이썬수업
그렇지만 열공하자
오늘은 즐거운 금요일
불금인데.....방콕이 답이다
In [213]:
# data = '안녕하세요~ 한 주 수고많으셨구요... 즐거운 금요일 되시길 바랍니다.'
data = {'id' : 'xxxx' , 'pwd' : 'xxxx'}
print('type - ', type(data))

# dict -> json , json -> dict
import json

filePath = './data/msg.json'.strip()
with open(filePath, mode='w', encoding='utf-8') as file :
    # file.write(data)
    json.dump(data, file)
type -  <class 'dict'>
In [217]:
print('json 형식의 파일을 읽어들인다면? - ')
import json

filePath = './data/msg.json'.strip()
with open(filePath, mode='r', encoding='utf-8') as file :
    # file.write(data)
    loadData = json.load(file)
    print(loadData, '-', type(loadData))
    print(loadData['id'])
json 형식의 파일을 읽어들인다면? - 
{'id': 'xxxx', 'pwd': 'xxxx'} - <class 'dict'>
xxxx
In [350]:
# Quiz
# 사용자의 이름을 두자리만 출력하고 나머지는 마스킹처리(su********)
# 로깅포맷 
# 로깅포맷형태를 파일로 저장 userAccess.log
# decorator 이용해서 (보안적인 측면을 강화)
# 예외관련해서 필요한 부분이 있다면 추가

from datetime import datetime
import logging

logging.basicConfig(level=logging.WARNING, 
                    format="%(asctime)s - %(levelname)s - %(message)s", force=True)

filePath = './data/userAccess.log'.strip()
def getProfile(user : dict) -> None :
    try :
        if not isinstance(user, dict):
            raise TypeError('매개변수 타입은 반드시 dict 형태')
            
        start = datetime.today()
        name = user['name'] 
        mask = name[:2] + '*' * (len(name) - 2)
        end = datetime.today()
        time = (end - start)
        data = (f'검색한 시간 - {start}, 사용자 - {mask}, 실행시간 정보 - {time}')
        with open(filePath, mode='w', encoding='utf-8') as file :
            file.write(data)
        print(data)
    except Exception as e:
        logging.error(f'예상치 못한 예외 발생 : {e}')
In [351]:
# caller
user = {'name' : 'superadmin', 'authenticated' : True}
# 관리자가 사용자 정보를 확인하고 한다.
# 관리자가 사용자의 정보를 검색한 시간, 사용자이름, 실행시간 정보를 userAccess.log 파일로 저장하고 싶다면
getProfile(user)
검색한 시간 - 2025-10-31 16:43:06.260883, 사용자 - su********, 실행시간 정보 - 0:00:00.000008
In [355]:
import logging

name = user['name']
print(name)
maskedName = name[:2] + '*' * (len(name) - 2)
print(maskedName)

logging.basicConfig(level=logging.WARNING, 
                    format="%(asctime)s - %(levelname)s - %(message)s", force=True)
superadmin
su********
In [ ]:
# Quiz
# 사용자의 이름을 두자리만 출력하고 나머지는 마스킹처리(su********)
# 로깅포맷 
# 로깅포맷형태를 파일로 저장 userAccess.log
# decorator 이용해서 (보안적인 측면을 강화)
# 예외관련해서 필요한 부분이 있다면 추가
from time import time, sleep
def secureLog():
    def wrapper():
        pass

@secureLog
def getProfile(user : dict) -> None :
    print(f'{user['name']} 프로필을 관리자가 검색합니다.')
    sleep(5)
In [ ]: