파이썬 공부
들어가며
지난 글에서는 키(Key)와 값(Value)을 쌍으로 묶어 데이터를 관리하는 딕셔너리(Dictionary)에 대해 배웠습니다. 이제 우리는 리스트와 딕셔너리를 활용하여 대부분의 데이터를 효과적으로 구조화할 수 있게 되었습니다.
지금까지 우리는 파이썬의 기본적인 문법부터 데이터 구조, 함수, 모듈, 파일 입출력까지 배우며 제법 그럴듯한 프로그램을 만들 수 있는 힘을 길렀습니다. 하지만 우리가 만든 프로그램들은 아직 ‘온실 속 화초’와 같습니다. 예상된 입력과 정해진 시나리오 안에서는 잘 동작하지만, 조금만 예상치 못한 상황이 발생하면 그대로 멈춰버리고 맙니다.
예를 들어, 사용자가 숫자를 입력해야 할 곳에 문자를 입력하거나, 프로그램이 열려고 하는 파일이 삭제되어 존재하지 않는다면 어떻게 될까요? 아마 프로그램은 빨간색 오류 메시지를 내뿜으며 그 자리에서 실행을 중단할 것입니다.
이번 글에서는 이처럼 프로그램 실행 중에 발생하는 예기치 못한 오류에 대처하고, 프로그램이 중단되지 않고 안정적으로 계속 실행되도록 만드는 예외 처리(Exception Handling) 기법에 대해 알아보겠습니다.
예외(Exception)란 무엇일까?
프로그래밍에서 예외(Exception)란, 프로그램의 정상적인 흐름을 방해하는 예기치 않은 사건이나 오류를 의미합니다. 문법이 틀리는 ‘구문 오류(Syntax Error)’와는 달리, 예외는 문법적으로는 올바르지만 실행 중에 발생하는 문제입니다.
몇 가지 흔한 예외 상황을 살펴보겠습니다.
1
2
3
4
5
6
7
8
# ZeroDivisionError: 0으로 나누려고 할 때 발생
# 10 / 0
# ValueError: 자료형은 맞지만 값이 부적절할 때 발생
# int('안녕하세요')
# FileNotFoundError: 존재하지 않는 파일을 열려고 할 때 발생
# open('없는파일.txt', 'r')
이런 코드를 실행하면 프로그램은 해당 지점에서 즉시 실행을 멈추고, 어떤 오류가 발생했는지 알려주는 추적 정보(Traceback)를 출력합니다. 사용자는 복잡한 오류 메시지를 마주하게 되고, 프로그램은 그대로 꺼져버립니다. 이것은 결코 좋은 사용자 경험이 아닙니다.
오류를 잡아내는 그물: try 와 except
파이썬은 이러한 예외 상황에 대처할 수 있도록 try와 except라는 강력한 도구를 제공합니다. 이를 이용하면 예외가 발생하더라도 프로그램이 멈추지 않고, 미리 준비해 둔 대안 코드를 실행하도록 만들 수 있습니다.
try...except 문의 기본 구조는 다음과 같습니다.
1
2
3
4
try:
# 예외가 발생할 가능성이 있는 코드 블록
except:
# 예외가 발생했을 때 실행될 코드 블록
사용자로부터 숫자를 입력받는 코드를 예로 들어보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 예외 처리가 없는 경우
# 사용자가 "abc" 같은 문자를 입력하면 ValueError가 발생하며 프로그램이 멈춥니다.
# age_str = input("나이를 숫자로 입력하세요: ")
# age = int(age_str)
# print(f"당신의 내년 나이는 {age + 1}살 입니다.")
# 예외 처리를 적용한 경우
try:
age_str = input("나이를 숫자로 입력하세요: ")
age = int(age_str)
print(f"당신의 내년 나이는 {age + 1}살 입니다.")
except ValueError: # ValueError 라는 특정 예외만 잡습니다.
print("잘못된 입력입니다. 숫자로만 입력해주세요!")
print("프로그램이 정상적으로 종료되었습니다.")
- 파이썬은 먼저
try블록 안의 코드를 실행합니다. - 아무 문제 없이 코드가 실행되면,
except블록은 건너뛰고 다음 코드로 넘어갑니다. - 만약
try블록 안에서ValueError예외가 발생하면, 파이썬은 즉시 실행을 멈추고 해당 예외를 처리하는except ValueError블록으로 점프합니다. except블록의 코드를 실행한 후, 프로그램은 멈추지 않고 계속해서 다음 코드를 이어나갑니다.
이처럼 except 뒤에 특정 예외의 종류를 명시해주면, 해당하는 종류의 예외만 골라서 처리할 수 있습니다. 이는 어떤 오류가 발생했는지 명확히 알고 그에 맞는 대처를 할 수 있게 해주므로 매우 중요합니다.
어떤 일이 있어도 반드시: finally
때로는 예외 발생 여부와 상관없이, try 블록이 끝난 후에 반드시 실행되어야 하는 코드가 있을 수 있습니다. 예를 들어, 파일을 열어서 작업하는 도중 오류가 발생하더라도 파일은 반드시 닫아주어야 합니다. 이러한 경우에 finally를 사용합니다.
1
2
3
4
5
6
7
8
9
10
try:
print("try 블록: 리소스를 사용합니다.")
# 일부러 오류를 발생시켜 봅니다.
# result = 10 / 0
except ZeroDivisionError:
print("except 블록: 0으로 나눌 수 없습니다.")
finally:
print("finally 블록: 리소스를 항상 해제합니다.")
print("프로그램의 나머지 부분")
위 코드에서 ZeroDivisionError가 발생하여 except 블록이 실행되지만, 그 이후에도 finally 블록의 코드는 어김없이 실행되는 것을 볼 수 있습니다. 만약 오류가 발생하지 않았더라도 finally 블록은 실행됩니다. 이처럼 finally는 작업의 마무리나 뒷정리를 보장하는 역할을 합니다.
마무리하며
이번 글에서는 프로그램의 안정성을 크게 높여주는 예외 처리(Exception Handling)에 대해 배웠습니다. 예외는 프로그램 실행 중에 발생하는 오류이며, try...except 문을 사용해 이러한 예외를 ‘잡아서’ 프로그램이 멈추는 것을 막고 적절한 조치를 취할 수 있다는 것을 알게 되었습니다. 또한 예외 발생 여부와 상관없이 항상 실행되어야 하는 코드는 finally 블록에 작성한다는 것도 확인했습니다.
견고하고 신뢰성 있는 프로그램을 만들기 위해 예외 처리는 선택이 아닌 필수입니다. 이제 우리는 오류가 발생하더라도 쉽게 무너지지 않는, 훨씬 더 튼튼한 프로그램을 만들 수 있는 기술을 갖추게 되었습니다.
지금까지 우리는 파이썬의 핵심 문법과 기본적인 프로그래밍 개념들을 모두 다루었습니다. 이제부터는 프로그램을 더 체계적이고 효율적으로 설계하는 방법론으로 나아갈 차례입니다. 다음 글에서는 현대 프로그래밍의 핵심 패러다임 중 하나인 객체 지향 프로그래밍(Object-Oriented Programming)의 첫걸음, 클래스(Class)와 객체(Object)에 대해 알아보겠습니다.