Akashic Records

6.1 예외 처리 본문

Python for Beginners

6.1 예외 처리

Andrew's Akashic Records 2023. 3. 21. 10:22
728x90

Python for beginners

 

예외 관련 주요 용어

파이썬의 예외 처리에서 사용되는 주요 용어들에 대해 설명하겠습니다:

  1. 예외(Exception): 프로그램 실행 중에 발생하는 오류 또는 예상치 못한 상황을 가리킵니다. 파이썬에서는 예외가 발생하면 프로그램이 중단되고, 이를 처리하지 않으면 프로그램이 예외 발생 지점에서 종료됩니다.

  2. try 블록: 예외가 발생할 가능성이 있는 코드를 이 블록 안에 넣습니다. try 구문은 필수적으로 하나 이상의 except 블록과 함께 사용되어야 합니다.

  3. except 블록: try 블록 내에서 예외가 발생했을 때 실행되는 코드 블록입니다. 특정 예외 유형을 지정하여 그 유형의 예외가 발생했을 때만 이 블록이 실행되도록 할 수 있습니다. 예외 유형을 지정하지 않으면 모든 예외를 처리합니다.

  4. else 블록: try 블록이 예외 없이 성공적으로 실행된 후 실행되는 코드 블록입니다. 이 블록은 선택적으로 사용되며, try 블록이 예외를 발생시키지 않을 때만 실행됩니다.

  5. finally 블록: 예외 발생 여부에 관계없이 실행되는 코드 블록입니다. 주로 자원을 해제하거나 정리하는 데 사용됩니다. finally 블록은 try 블록 다음에 항상 실행되므로, 프로그램이 강제 종료되기 전에 마무리 작업을 보장하는 용도로 사용됩니다.

  6. raise: 프로그래머가 의도적으로 예외를 발생시키고 싶을 때 사용합니다. 예외 클래스의 인스턴스를 생성하거나 간단히 예외 이름을 지정하여 예외를 발생시킬 수 있습니다.

  7. Traceback은 파이썬에서 예외가 발생했을 때 출력되는 오류 메시지의 일부로, 프로그램이 중단된 위치와 그 이유를 상세히 보여줍니다. 트레이스백은 예외가 발생한 시점에서 함수 호출 스택을 역추적하여, 예외가 발생한 정확한 코드 위치와 함께 그 위치까지 어떻게 도달했는지를 나타내는 정보를 제공합니다.

트레이스백은 다음과 같은 구성 요소를 포함합니다:

 - 오류 메시지: 발생한 예외의 유형과 그에 대한 설명입니다.
 - 스택 트레이스: 예외가 발생할 때까지 실행된 각 함수 호출의 리스트입니다. 각 호출 지점은 소스 파일의 이름, 발생한 코드의 줄 번호, 실행된 함수의 이름 등을 포함합니다.

try:
    # 예외가 발생할 가능성이 있는 코드
    result = 10 / 0
except ZeroDivisionError:
    # ZeroDivisionError 발생 시 실행되는 코드
    print("0으로 나눌 수 없습니다.")
else:
    # 예외가 발생하지 않았을 때 실행되는 코드
    print("결과는", result)
finally:
    # 항상 실행되는 코드
    print("연산 시도 완료.")

 

이 코드에서 10 / 0은 분명히 예외를 발생시키는 코드이기 때문에 except 블록이 실행되고 "0으로 나눌 수 없습니다."라는 메시지가 출력됩니다. 그리고 finally 블록도 실행되어 "연산 시도 완료."라는 메시지가 출력됩니다. else 블록은 예외가 발생했기 때문에 실행되지 않습니다.

충돌없는 예외 처리 코드

예외 처리를 사용하여 프로그램이 충돌하지 않도록 하는 간단한 예시를 작성해 보겠습니다. 이 예제에서는 파일 입출력을 다루며 파일을 읽고자 할 때 발생할 수 있는 여러 가지 예외를 처리합니다.

import os

# 파일 읽기 시도
try:
    read_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'readme.txt')
    # 파일 열기 시도
    with open(read_file, "r", encoding='utf-8') as file:
        content = file.read()
        print("파일 내용:")
        print(content)
except FileNotFoundError:
    # 파일이 존재하지 않을 경우
    print("에러: 파일을 찾을 수 없습니다.")
except PermissionError:
    # 파일 읽기 권한이 없을 경우
    print("에러: 파일 읽기 권한이 없습니다.")
except IOError:
    # 파일 읽기 중 기타 입출력 오류 발생
    print("에러: 파일을 읽는 중 오류가 발생했습니다.")
else:
    # 예외가 발생하지 않았을 때 실행
    print("파일 읽기가 성공적으로 완료되었습니다.")
finally:
    # 예외 발생 여부와 상관없이 항상 실행
    print("파일 읽기 시도가 완료되었습니다.")

이 코드는 다음과 같은 과정을 거칩니다:

  1. try 블록: example.txt 파일을 열고 내용을 읽습니다. 파일을 여는 동안에는 with 문을 사용하여 파일이 제대로 닫히도록 보장합니다.
  2. except 블록: 여러 종류의 예외를 각각 처리합니다.
    • FileNotFoundError: 파일이 존재하지 않을 경우 출력합니다.
    • PermissionError: 파일에 대한 읽기 권한이 없는 경우를 처리합니다.
    • IOError: 파일 입출력 과정에서 다른 오류가 발생한 경우를 처리합니다.
  3. else 블록: 예외가 발생하지 않았을 때, 파일 읽기가 성공적으로 완료되었음을 알립니다.
  4. finally 블록: 파일 읽기 시도가 완료되었음을 알리고, 이 블록은 예외 발생 여부에 상관없이 실행됩니다.

이 코드를 통해 파일 읽기 시 발생할 수 있는 다양한 예외를 안전하게 처리하고 프로그램이 예상치 못한 상황에서도 중단되지 않도록 합니다.

 

여러 파일을 다루면서 각 파일에 대해 예외 처리를 수행하는 코드 예제에서는 파일 이름의 리스트를 받아 각 파일을 순차적으로 열고 내용을 읽으며, 발생할 수 있는 예외를 개별적으로 처리합니다.

file_list = ["file1.txt", "file2.txt", "file3.txt"]  # 읽고자 하는 파일 목록

for file_name in file_list:
    try:
        # 파일 열기 시도
        with open(file_name, "r") as file:
            content = file.read()
            print(f"{file_name} 내용:")
            print(content)
    except FileNotFoundError:
        # 파일이 존재하지 않을 경우
        print(f"에러: {file_name}을(를) 찾을 수 없습니다.")
    except PermissionError:
        # 파일 읽기 권한이 없을 경우
        print(f"에러: {file_name} 읽기 권한이 없습니다.")
    except IOError:
        # 파일 읽기 중 기타 입출력 오류 발생
        print(f"에러: {file_name}을(를) 읽는 중 오류가 발생했습니다.")
    else:
        # 예외가 발생하지 않았을 때 실행
        print(f"{file_name} 읽기가 성공적으로 완료되었습니다.")
    finally:
        # 예외 발생 여부와 상관없이 항상 실행
        print(f"{file_name} 읽기 시도가 완료되었습니다.\n")

이 코드에서는 다음과 같은 처리를 수행합니다:

  1. file_list: 읽고자 하는 파일의 이름을 담은 리스트입니다.
  2. for 루프: 리스트의 각 파일 이름에 대해 반복 작업을 수행합니다.
  3. try-except-else-finally 구조:
    • try 블록에서는 파일을 열고 내용을 읽습니다.
    • except 블록은 각각의 예외 유형별로 오류 메시지를 출력하며, 발생할 수 있는 여러 예외를 처리합니다.
    • else 블록은 파일이 성공적으로 읽혔을 때 실행됩니다.
    • finally 블록은 파일 읽기 시도가 완료되었다는 메시지를 출력하며, 이는 항상 실행됩니다.

이러한 방식으로 여러 파일을 안전하게 다루면서 각 파일에 대한 예외를 개별적으로 처리할 수 있습니다. 이는 특히 다수의 파일을 다룰 때 유용하게 사용할 수 있는 패턴입니다.

 

예외 처리를 위한 규칙

예외 처리를 사용하여 프로그램 충돌을 방지하는 것은 모든 소프트웨어 개발에서 중요한 부분입니다. 효과적인 예외 처리를 위한 몇 가지 주요 가이드라인을 제공하겠습니다:

 

1. 구체적인 예외 처리: 가능한 한 구체적인 예외 유형을 지정하여 처리합니다. 이렇게 하면 예외의 원인을 더 명확하게 파악하고 적절하게 대응할 수 있습니다.

try: 
    # 가능한 오류 발생 코드 

except 
    ValueError: # ValueError에 대한 처리

 

2. 리소스 정리: 파일, 네트워크 연결 또는 데이터베이스 연결과 같은 리소스를 사용하는 코드에는 finally 블록을 사용하여 리소스를 반드시 해제하도록 합니다.

try: 
    file = open("example.txt", "r")
    data = file.read()
    
except IOError: 
    print("파일 읽기 오류") 

finally: 
    file.close()

 

 

3. 최소한의 try 블록: try 블록은 필요한 최소한의 코드만 포함해야 합니다. 너무 많은 코드를 try 블록에 넣으면 실제 문제의 원인을 파악하기 어렵습니다.

try: 
    result = x / y 

except ZeroDivisionError: 
    print("0으로 나눌 수 없습니다.")

 

4. 예외 체인 사용: Python 3에서는 raise 문을 사용하여 발생한 예외를 다른 예외로 변환할 때 원래의 예외 정보를 유지할 수 있습니다. 이를 통해 문제의 원인을 더 잘 이해할 수 있습니다.

try: 
    config = load_config() 

except FileNotFoundError as e:
    raise RuntimeError("구성 파일이 필요합니다") from e

 

 

5. 사용자 정의 예외: 표준 예외만으로 충분하지 않을 때는 사용자 정의 예외를 만들어 사용합니다. 이를 통해 애플리케이션 특정 문제를 더 명확하게 표현할 수 있습니다.

class MyError(Exception): 
    pass 

try: 
    test_function() 

except MyError as e: 
    handle_my_error(e)

 

6. 로깅: 예외가 발생할 때 중요한 정보를 로그로 남기면, 문제를 추적하고 디버그하는 데 도움이 됩니다.

import logging 

try: 
    perform_task() 
    
except Exception as e: 
    logging.error("작업 수행 중 오류 발생", exc_info=True)

 

7. 적절한 예외 발생: 필요한 경우 적절한 시점에 raise를 사용하여 예외를 발생시키고, 이를 문서화하여 다른 개발자가 이해할 수 있도록 합니다.

 

이러한 가이드라인들은 예외 처리를 통해 프로그램의 견고성을 높이고, 오류 발생 시 적절하게 대응하여 프로그램의 안정성을 보장하는 데 중요한 역할을 합니다.

 

예외 처리 유형별 예시

예외가 발생하더라도 프로그램을 계속 진행하고 싶은 경우, 예외 처리 구조를 적절히 사용해야 합니다. 이를 위해 주로 try-except 블록을 사용하며, 예외를 기록하거나 경고를 출력하는 등의 최소한의 조치를 취한 후에 프로그램의 나머지 부분을 계속 실행할 수 있습니다. 다음은 이와 관련된 몇 가지 시나리오와 코드 예시입니다.

 

시나리오 1: 예외를 로그로 기록하고 계속 진행

import logging

try:
    # 예외가 발생할 수 있는 코드
    result = 10 / 0
except ZeroDivisionError:
    # 예외를 로그에 기록하고 계속 진행
    logging.error("0으로 나누는 오류 발생, 계속 진행합니다.")
print("프로그램 계속 실행 중...")

 

시나리오 2: 예외 발생시 기본값 사용

try:
    # 예외가 발생할 수 있는 코드
    result = 10 / 0
except ZeroDivisionError:
    # 기본값을 설정하고 계속 진행
    result = 0
print(f"결과: {result}")

 

 

시나리오 3: 여러 예외를 무시하고 계속 진행

try:
    # 다양한 예외가 발생할 수 있는 복잡한 코드
    complex_calculation()
except (ZeroDivisionError, ValueError, TypeError):
    # 특정 오류들을 무시하고 계속 진행
    pass
print("복잡한 계산 후 계속 진행...")

 

시나리오 4: 예외 발생에 대한 사용자 경고 및 진행

try:
    # 예외가 발생할 수 있는 사용자 입력 처리
    age = int(input("나이를 입력하세요: "))
except ValueError:
    print("유효하지 않은 입력입니다. 0으로 설정됩니다.")
    age = 0
finally:
    print(f"입력된 나이: {age}")

 

이런 방식으로 예외 처리를 할 때는 몇 가지 주의할 점이 있습니다:

  • 예외를 너무 많이 무시하지 않도록 주의: 예외를 무시하는 것이 편리할 수 있지만, 이는 종종 코드의 실제 문제를 간과하게 만들 수 있습니다. 따라서 예외를 무시하기 전에 왜 그러한 예외가 발생하는지 충분히 이해하고 있는지 확인해야 합니다.
  • 프로그램의 나머지 부분에 영향을 주지 않는지 검토: 예외를 무시하고 계속 진행하더라도 그로 인해 프로그램의 다른 부분에 부정적인 영향을 주지 않아야 합니다.

이러한 방식으로 코드 내에서 예외를 적절히 처리함으로써 프로그램의 안정성과 사용자 경험을 향상시킬 수 있습니다.

728x90

'Python for Beginners' 카테고리의 다른 글

6.3 디버깅 기술  (0) 2023.03.21
6.2 에러 종류와 대처법  (0) 2023.03.21
5.5 디자인 패턴  (0) 2023.03.20
5.4 캡슐화  (0) 2023.03.20
5.3 다형성  (0) 2023.03.20
Comments