3.SW개발/Python

[Java관점]6편. Python 예외 처리 문법 – Java와 Python의 처리 구조 비교

쿼드큐브 2025. 11. 12. 10:16
반응형
반응형

6편. Python 예외 처리 문법 – Java와 Python의 처리 구조 비교

 

📚 목차
1. 예외 처리 기본 구조 - try-catch vs try-except 비교
2. 여러 예외 처리 - 다중 블록과 그룹 처리 방식
3. 리소스 자동 반환 - try-with-resources vs with open
4. 예외 발생시키기 - throw와 raise의 차이
5. Java vs Python의 예외 처리 철학과 흐름 차이
✔ 마무리 - 두 언어의 예외 처리 방식, 무엇을 배울 수 있을까?

 

Java에서 try-catch는 필수적인 예외 처리 패턴입니다. 코드에서 오류가 발생할 수 있는 부분을 안전하게 감싸고, 예외 상황에 유연하게 대응할 수 있도록 해 주죠. 그렇다면 Python에서는 어떤 방식으로 예외를 처리할까요?


바로 try-except 구문입니다. 구조는 비슷하지만 문법과 처리 철학은 다소 다릅니다. 특히 Python은 checked exception이 존재하지 않는다는 점에서 Java와 큰 차이를 보입니다.


이 글에서는 Java 개발자의 시선에서 Python의 예외 처리 구조를 비교하고, 각 문법의 활용법과 차이점을 구체적으로 살펴봅니다. try-with-resources와 with open() 같은 리소스 자동 반환 방식, throw와 raise의 차이, 다중 예외 처리 등 실무에 꼭 필요한 예외 처리 패턴들을 Java와 나란히 비교해 정리했습니다.


Python 예외 처리 방식의 철학과 장단점을 이해하면, 더 안전하고 우아한 Python 코드를 작성할 수 있습니다. 지금부터 그 차이를 하나씩 살펴보겠습니다.

예외처리 Java vs Python
예외처리 Java vs Python

1. 예외 처리 기본 구조 – try-catch vs try-except 비교

Java와 Python은 예외(Exception)를 처리하는 구조가 비슷해 보이지만, 문법과 흐름에서 명확한 차이가 있습니다.

핵심은 try 블록에서 발생할 수 있는 오류를 감지하고, catch 또는 except 블록에서 이를 처리하는 것입니다. finally 블록은 예외 발생 여부와 관계없이 항상 실행되어 자원을 정리하는 데 유용합니다.

 

✔️ Java의 try-catch-finally 구조

Java에서는 {} 괄호를 사용하여 코드 블록을 구분합니다. catch 키워드를 사용하여 특정 타입의 예외를 잡아냅니다.

try {
    // 예외가 발생할 수 있는 코드
    int result = 10 / 0; // ArithmeticException 발생
    System.out.println("결과: " + result); // 이 코드는 실행되지 않음
} catch (ArithmeticException e) {
    // ArithmeticException이 발생했을 때 실행되는 코드
    System.out.println("오류: 0으로 나눌 수 없습니다.");
    System.out.println("예외 메시지: " + e.getMessage());
} catch (Exception e) {
    // 그 외 모든 예외를 잡는 블록 (더 일반적인 예외는 뒤에 위치)
    System.out.println("예상치 못한 오류 발생!");
} finally {
    // 예외 발생 여부와 관계없이 항상 실행되는 코드 (자원 해제 등)
    System.out.println("자원 정리를 위해 항상 실행됩니다.");
}
System.out.println("프로그램이 계속됩니다.");

 

✔️ Python의 try-except-finally 구조

Python에서는 괄호 대신 콜론 :과 들여 쓰기로 코드 블록을 구분합니다. except 키워드가 Java의 catch 역할을 합니다.

try:
    # 예외가 발생할 수 있는 코드
    result = 10 / 0  # ZeroDivisionError 발생
    print(f"결과: {result}") # 이 코드는 실행되지 않음
except ZeroDivisionError:
    # ZeroDivisionError가 발생했을 때 실행되는 코드
    print("오류: 0으로 나눌 수 없습니다.")
except Exception as e:
    # 그 외 모든 예외를 잡는 블록 (더 일반적인 예외는 뒤에 위치)
    print(f"예상치 못한 오류 발생: {e}")
finally:
    # 예외 발생 여부와 관계없이 항상 실행되는 코드 (자원 해제 등)
    print("자원 정리를 위해 항상 실행됩니다.")
print("프로그램이 계속됩니다.")

 

✔️ 차이점 요약:

▪️ 키워드: catch 대신 Python은 except를 사용합니다.
▪️ 블록 구분: 괄호 {} 대신 Python은 콜론 :과 들여쓰기로 코드 블록을 구분합니다.
▪️ 단독 사용: Python에서는 try 블록 없이 except만 단독으로 사용할 수 없습니다.

 

2. 여러 예외 처리 – 다중 블록과 그룹 처리 방식

프로그램은 다양한 종류의 예외를 발생시킬 수 있으며, 각 예외에 따라 다르게 처리해야 할 때가 있습니다.

Python도 Java처럼 여러 예외를 구분하여 처리할 수 있습니다.

 

✔️Java: 다중 catch 블록

Java에서는 여러 catch 블록을 사용하여 각기 다른 예외 타입을 처리합니다. 예외 계층 구조에 따라 더 구체적인 예외를 먼저 처리하고, 그 뒤에 더 일반적인 예외를 처리하는 것이 일반적입니다.

try {
    String text = null;
    System.out.println(text.length()); // NullPointerException 발생
    int[] numbers = {1, 2};
    System.out.println(numbers[5]); // ArrayIndexOutOfBoundsException 발생 (위에서 이미 예외 발생하여 실행 안 됨)
} catch (NullPointerException e) {
    System.out.println("널 참조 예외: 변수가 null입니다.");
    // 추가적인 널 참조 예외 처리 로직
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("배열 인덱스 범위를 벗어났습니다.");
    // 추가적인 배열 인덱스 예외 처리 로직
} catch (Exception e) {
    System.out.println("기타 예상치 못한 예외 발생: " + e.getMessage());
    // 모든 다른 예외를 잡는 일반적인 처리
} finally {
    System.out.println("여러 예외 처리 시도 완료.");
}

 

✔️ Python: 다중 except 블록

Python에서도 여러 except 블록을 사용할 수 있으며, Exception 클래스를 사용하여 모든 예외를 포괄적으로 처리할 수 있습니다.

try:
    # 문자열을 정수로 변환 시도 (ValueError 발생 가능)
    # print(int("abc"))

    # 리스트 인덱스 접근 시도 (IndexError 발생 가능)
    my_list = [1, 2, 3]
    print(my_list[5]) # IndexError 발생

    # 없는 파일 열기 (FileNotFoundError 발생 가능)
    # with open("non_existent_file.txt", "r") as f:
    #    data = f.read()

except ValueError:
    print("오류: 잘못된 값 형식입니다. 숫자로 변환할 수 없습니다.")
except IndexError:
    print("오류: 리스트 인덱스 범위를 벗어났습니다.")
except FileNotFoundError:
    print("오류: 파일을 찾을 수 없습니다.")
except Exception as e: # 가장 일반적인 예외는 가장 마지막에 위치해야 합니다.
    print(f"기타 예상치 못한 예외 발생: {type(e).__name__} - {e}")
finally:
    print("여러 예외 처리 시도 완료.")

 

✔️ Python: 하나의 블록에서 여러 예외 처리

Python에서는 특정 작업에서 발생할 수 있는 여러 종류의 예외를 하나의 except 블록에서 튜플 형태로 묶어 처리할 수 있습니다.

try:
    # 이 부분에서 ValueError 또는 TypeError가 발생할 수 있는 코드를 작성
    # 예시 1: 정수로 변환할 수 없는 문자열
    # num = int("hello")

    # 예시 2: 숫자에 문자열 길이를 계산하려 할 때
    text = 123
    print(len(text))

except (ValueError, TypeError) as e:
    # ValueError 또는 TypeError 중 하나가 발생하면 이 블록이 실행됩니다.
    print(f"값 오류 또는 타입 오류 발생: {e}")
    print(f"발생한 예외 타입: {type(e).__name__}")
finally:
    print("그룹 예외 처리 완료.")

 

Java의 catch (Exception e)와 Python의 except Exception as e:는 거의 동일한 패턴입니다. 둘 다 모든 종류의 예외를 포괄적으로 잡을 때 사용하며, 이때 예외 객체를 변수 e에 할당하여 예외의 세부 정보에 접근할 수 있게 합니다.

 

3. 리소스 자동 반환 – try-with-resources vs with open

소프트웨어 개발에서 파일, 네트워크 연결, 데이터베이스 세션과 같은 외부 리소스는 사용 후 반드시 닫아주어야 합니다.

그렇지 않으면 리소스 누수(Resource Leak)가 발생하여 시스템 성능 저하, 메모리 부족, 심지어 애플리케이션 충돌로 이어질 수 있습니다.

 

자바의 try-with-resources와 파이썬의 with open()은 이러한 리소스 관리 문제를 간결하고 안전하게 해결하기 위한 각 언어의 대표적인 문법입니다.

 

✔️ Java: try-with-resources

자바의 try-with-resources는 Java 7에서 도입되었으며, AutoCloseable 인터페이스를 구현하는 객체에 적용됩니다. try 키워드 뒤의 괄호 안에 리소스를 선언하면, 해당 리소스는 try 블록이 종료될 때 자동으로 close() 메서드가 호출됩니다.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class JavaTryWithResources {
    public static void main(String[] args) {
        // try-with-resources 사용
        try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.err.println("파일을 읽는 중 오류 발생: " + e.getMessage());
        }

        // 이전 방식 (try-finally)과 비교 (동일한 기능)
        // BufferedReader oldReader = null;
        // try {
        //     oldReader = new BufferedReader(new FileReader("example.txt"));
        //     String line;
        //     while ((line = oldReader.readLine()) != null) {
        //         System.out.println(line);
        //     }
        // } catch (IOException e) {
        //     System.err.println("파일을 읽는 중 오류 발생: " + e.getMessage());
        // } finally {
        //     if (oldReader != null) {
        //         try {
        //             oldReader.close();
        //         } catch (IOException e) {
        //             System.err.println("리소스를 닫는 중 오류 발생: " + e.getMessage());
        //         }
        //     }
        // }
    }
}

- AutoCloseable 인터페이스를 구현해야 합니다. (대표적으로 InputStream, OutputStream, Reader, Writer, Connection, Statement 등)

- 여러 개의 리소스를 괄호 안에 콤마(,)로 구분하여 선언할 수 있으며, 이들은 선언된 역순으로 닫힙니다.

- try-with-resources 블록 내부에서 예외가 발생하더라도 리소스는 안전하게 닫힙니다. 리소스 닫는 과정에서 또 다른 예외가 발생하면, 원래 발생한 예외가 우선적으로 전달됩니다 (Suppressed Exception).

 

✔️ Python: with open()

파이썬의 with 문은 컨텍스트 관리자(Context Manager) 프로토콜을 따르는 객체에 적용됩니다.

with open()은 파일 객체가 이 프로토콜을 구현하고 있기 때문에 파일 처리에 특히 유용하게 사용됩니다.

with 블록을 벗어나면 해당 객체의 __exit__ 메서드가 자동으로 호출되어 리소스를 해제합니다.

# 파이썬 with open() 사용
try:
    with open("example.txt", "r") as f:
        for line in f:
            print(line, end="")
except FileNotFoundError:
    print("example.txt 파일을 찾을 수 없습니다.")
except Exception as e:
    print(f"파일을 읽는 중 오류 발생: {e}")

# 이전 방식 (try-finally)과 비교 (동일한 기능)
# f = None
# try:
#     f = open("example.txt", "r")
#     for line in f:
#         print(line, end="")
# except FileNotFoundError:
#     print("example.txt 파일을 찾을 수 없습니다.")
# except Exception as e:
#     print(f"파일을 읽는 중 오류 발생: {e}")
# finally:
#     if f:
#         f.close()

- __enter__와 __exit__ 메서드를 구현하는 컨텍스트 관리자 객체에 사용됩니다.

- open() 함수 외에도 threading.Lock, sqlite3.connect 등 다양한 라이브러리에서 with 문을 지원합니다.

- as 키워드를 사용하여 리소스를 변수에 할당합니다.

- with 블록 내부에서 예외가 발생하더라도 리소스는 안전하게 닫힙니다.

반응형

 

4. 예외 발생시키기 – throw와 raise의 차이

때로는 프로그래머가 특정 조건에서 의도적으로 예외를 발생시켜야 할 필요가 있습니다.

예를 들어, 함수에 전달된 인자의 값이 유효하지 않거나, 특정 비즈니스 로직에 위배될 때 강제로 오류를 발생시켜 호출자에게 문제를 알리는 방식입니다.

 

✔️ Java: throw
Java에서는 `throw` 키워드와 `new` 연산자를 사용하여 새로운 예외 객체를 생성하고 발생시킵니다.

public void validateAge(int age) {
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("나이는 0에서 150 사이여야 합니다.");
    }
    System.out.println("유효한 나이: " + age);
}

// 사용 예시
try {
    validateAge(-5);
} catch (IllegalArgumentException e) {
    System.out.println("예외 발생: " + e.getMessage());
}

 

✔️ Python: raise

Python에서는 raise 키워드를 사용하여 예외를 발생시킵니다.

# Python
def validate_score(score):
    if not (0 <= score <= 100):
        # ValueError는 내장된 예외 타입 중 하나입니다.
        raise ValueError("점수는 0점에서 100점 사이여야 합니다.")
    print(f"유효한 점수: {score}")

# 사용 예시
try:
    validate_score(105)
except ValueError as e:
    print(f"예외 발생: {e}")

try:
    validate_score(90)
except ValueError as e:
    print(f"예외 발생: {e}")

 

✔️ Python: 사용자 정의 예외

Python에서도 Java의 extends Exception과 비슷하게 Exception 클래스를 상속하여 사용자 정의 예외 클래스를 만들 수 있습니다. 이는 특정 애플리케이션의 비즈니스 로직에 특화된 오류를 표현하는 데 유용합니다.

# 사용자 정의 예외 클래스 정의
class InsufficientFundsError(Exception):
    """
    잔액 부족 시 발생하는 사용자 정의 예외.
    """
    def __init__(self, required, available, message="잔액이 부족합니다."):
        self.required = required
        self.available = available
        self.message = f"{message} 필요한 금액: {required}, 현재 잔액: {available}"
        super().__init__(self.message)

# 사용자 정의 예외 사용 예시
def withdraw(amount, balance):
    if amount > balance:
        raise InsufficientFundsError(required=amount, available=balance)
    return balance - amount

# 예외 처리 시도
try:
    current_balance = 50000
    withdraw_amount = 70000
    new_balance = withdraw(withdraw_amount, current_balance)
    print(f"출금 후 잔액: {new_balance}")
except InsufficientFundsError as e:
    print(f"오류: {e}")
    print(f"요구된 금액: {e.required}, 현재 잔액: {e.available}")
except Exception as e:
    print(f"알 수 없는 오류 발생: {e}")
finally:
    print("출금 시도 완료.")

 

5. Java와 Python의 예외 처리 철학과 흐름 차이

두 언어는 예외 처리의 철학에서 중요한 차이를 가집니다. 특히 "checked exception" 개념의 유무가 큰 차이점입니다.

항목 Java Python
기본 키워드 try, catch, finally try, except, finally
예외 객체 받기 catch (Exception e) except Exception as e
다중 예외 처리 여러 catch 블록 사용 여러 except 블록 또는 except (Exc1, Exc2) 튜플로 처리 가능
예외 발생 throw new Exception() raise Exception()
예외 필수 선언 throws 키워드로 메서드 시그니처에 선언 필요 선언 불필요
Checked vs Unchecked 존재: 컴파일 시점에 확인되는 Checked Exception과 런타임 Unchecked Exception 존재하지 않음: 모든 예외는 런타임 예외로 간주됨

 

✔️ 중요 개념: Python에는 Java의 checked exception 개념이 없습니다.

▪️ Java의 Checked Exception: IOException이나 SQLException처럼 컴파일러가 특정 예외 발생 가능성을 미리 감지하여 개발자에게 try-catch로 처리하거나 throws로 명시적으로 선언하도록 강제하는 예외입니다. 이는 잠재적 오류를 개발 단계에서 미리 인지하고 처리하도록 유도합니다.

▪️ Java의 Unchecked Exception: NullPointerException, ArithmeticException과 같이 런타임에 발생하는 예외이며, 컴파일러가 강제하지 않습니다. 일반적으로 프로그래밍 오류로 간주됩니다.

▪️ Python의 예외: Python의 모든 예외는 기본적으로 Java의 "Unchecked Exception"처럼 동작합니다. 즉, 함수가 어떤 예외를 발생시킬지 명시적으로 선언할 필요가 없으며, 컴파일러가 예외 처리를 강제하지 않습니다.

예외 발생 여부는 런타임에만 확인됩니다. 이로 인해 코드 작성은 더 유연해지지만, 개발자가 예외 발생 가능성을 스스로 예측하고 처리해야 할 책임이 더 커집니다.

 

✔ 마무리 - 두 언어의 예외 처리 방식, 무엇을 배울 수 있을까?

Java와 Python은 구조적으로 유사한 예외 처리 구문을 제공하지만, 그 이면에 흐르는 철학과 책임의 방식은 다릅니다.

Java는 예외를 엄격히 관리하여 안정성을 보장하려 하고, Python은 개발자에게 더 많은 자율성과 판단의 자유를 부여합니다.


Python의 try-except 문은 간결하고 유연하지만, 예외가 발생할 수 있다는 사실을 명시적으로 알리는 장치는 존재하지 않습니다.
따라서 Python 개발자는 코드 흐름을 예측하고 방어적으로 설계하는 습관이 중요합니다.

 

📌요약

🔸 Java는 정적 타입 기반 예외 처리로 안정성을 강화하며, checked exception이 존재합니다.
🔸 Python은 유연성과 간결성을 중시하며, 예외는 모두 런타임에 처리됩니다.
🔸 try-catch와 try-except, throw와 raise, try-with-resources와 with 등은 구조상 유사하지만 세부 문법과 적용 방식은 다릅니다.
🔸 Python에서는 예외 처리를 강제하지 않기 때문에, 개발자의 예외 예측력과 명확한 코드 설계 능력이 더욱 중요합니다.
🔸 유지보수가 쉬운 코드, 정확한 예외 메시지, 적절한 리소스 해제 습관은 두 언어 공통의 필수 역량입니다.

 

Java 개발자에게 Python의 예외 처리는 처음에는 느슨하게 보일 수 있지만, 그 안에는 "개발자가 주도하는 유연한 설계"라는 철학이 담겨 있습니다.

이 차이를 이해하고 활용할 수 있다면, 더욱 안전하고 우아한 Python 코드를 작성할 수 있습니다.

 

※ 게시된 글 및 이미지 중 일부는 AI 도구의 도움을 받아 생성되거나 다듬어졌습니다.
반응형

 

반응형