SW 전공

SW 전공 이론 학습

이론 목록으로 돌아가기

예외 처리와 C·Java·Python 코드 판독

컴파일·실행·논리 오류와 예외 전파를 이해하고 C·Java·Python 코드 결과를 체계적으로 판독한다.

예상 읽기 9

1. 프로그램 오류의 분류

오류발생 시점·특징예시
구문·컴파일 오류번역 단계에서 발견괄호 누락, 존재하지 않는 변수
링크 오류목적 모듈 결합 단계함수 선언은 있으나 정의를 찾지 못함
실행 오류·예외실행 중 발생0 나눗셈, 잘못된 인덱스, 파일 없음
논리 오류실행은 되지만 결과가 잘못됨경계 조건 오류, 잘못된 공식
CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
소스 코드
   │
   ├─ 컴파일 오류 ─► 수정
   ▼
목적 코드·바이트코드
   │
   ├─ 링크·로딩 오류 ─► 수정
   ▼
실행
   ├─ 예외·실행 오류 ─► 처리 또는 종료
   └─ 논리 오류 ─────► 테스트·디버깅으로 발견

2. 컴파일러와 인터프리터

  • 컴파일러: 프로그램 단위로 번역해 목적 코드나 중간 표현을 만든다.
  • 인터프리터: 실행 과정에서 소스 또는 중간 표현을 해석한다.
  • 현대 언어 구현은 컴파일과 인터프리트, JIT를 함께 사용할 수 있으므로 둘을 완전히 배타적인 언어 분류로 단정하지 않는다.
CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
C: 소스 → 전처리 → 컴파일 → 어셈블 → 링크 → 실행파일
Java: 소스 → javac → 바이트코드 → JVM 해석·JIT → 실행
Python 구현 예: 소스 → 바이트코드 → 가상머신 실행

3. Java 예외 처리

JAVA코드 영역 안에서 좌우로 이동할 수 있습니다.
try {
    int value = Integer.parseInt(text);
    System.out.println(100 / value);
} catch (NumberFormatException e) {
    System.out.println("숫자 형식 오류");
} catch (ArithmeticException e) {
    System.out.println("0으로 나눌 수 없음");
} finally {
    System.out.println("정리 작업");
}
CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
try 진입
  │
  ├─ 정상 완료 ────────────────┐
  │                            ▼
  └─ 예외 발생 → 일치 catch 실행
                               │
                               ▼
                          finally 실행
                               │
                               ▼
                       다음 문장 또는 전파
  • throw: 예외 객체를 직접 발생
  • throws: 메서드 선언에서 호출자에게 전파 가능성을 표시
  • 검사 예외와 비검사 예외는 컴파일러의 처리 강제 여부가 다르다.
  • finally는 일반적으로 정상·예외 경로 모두에서 실행되지만 프로세스 강제 종료 같은 특수 상황은 예외가 될 수 있다.

자원은 try-with-resources를 사용하면 역순으로 자동 닫을 수 있다.

4. Python 예외 처리

PYTHON코드 영역 안에서 좌우로 이동할 수 있습니다.
try:
    value = int(text)
    result = 100 / value
except ValueError:
    print("숫자 형식 오류")
except ZeroDivisionError:
    print("0으로 나눌 수 없음")
else:
    print(result)
finally:
    print("정리 작업")
  • raise: 예외 발생
  • except: 예외 처리
  • else: try에서 예외가 없을 때 실행
  • finally: 정리 코드
  • 너무 넓은 예외를 잡고 아무 작업도 하지 않으면 오류 원인을 숨길 수 있다.

5. C의 오류 처리

C에는 Java·Python과 같은 언어 차원의 예외 구문이 없다. 일반적으로 반환값, 출력 매개변수, errno, 오류 코드를 사용한다.

C코드 영역 안에서 좌우로 이동할 수 있습니다.
int divide(int a, int b, int *result) {
    if (result == NULL || b == 0) {
        return 0;
    }
    *result = a / b;
    return 1;
}

호출자는 반환값을 반드시 확인해야 한다.

6. 코드 실행 결과를 푸는 순서

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
① 언어와 버전·전제 확인
② 변수의 자료형·초기값 기록
③ 함수 호출마다 새 지역 범위 표시
④ 괄호·우선순위·단락평가 적용
⑤ 반복별 변수 변화를 표로 작성
⑥ 객체 별칭과 변경 가능성 확인
⑦ 예외 발생 지점과 catch·finally 흐름 표시
⑧ 출력 시점과 형식을 순서대로 기록

7. C 코드 판독 예시

C코드 영역 안에서 좌우로 이동할 수 있습니다.
#include <stdio.h>

int f(int x) {
    static int total = 0;
    total += x;
    return total;
}

int main(void) {
    printf("%d ", f(2));
    printf("%d", f(3));
    return 0;
}

출력은 2 5이다. 정적 지역변수 total은 함수 호출이 끝나도 값이 유지된다.

C 코드 문제에서는 평가 순서와 부수효과가 문제되는 정의되지 않은 동작을 정상 계산 문제처럼 풀지 않는다.

8. Java 코드 판독 예시

JAVA코드 영역 안에서 좌우로 이동할 수 있습니다.
class Parent {
    int value = 1;
    int get() { return value; }
}

class Child extends Parent {
    int value = 2;
    @Override int get() { return value; }
}

Parent p = new Child();
System.out.print(p.value + " ");
System.out.print(p.get());

대표적인 결과는 1 2이다.

  • 필드 접근은 선언 타입 기준의 숨김 규칙에 영향을 받는다.
  • 재정의된 인스턴스 메서드는 실제 객체 타입에 따라 실행된다.

9. Python 코드 판독 예시

PYTHON코드 영역 안에서 좌우로 이동할 수 있습니다.
def append_value(value, items=[]):
    items.append(value)
    return items

print(append_value(1))
print(append_value(2))

기본 인수 객체는 함수 정의 시 한 번 생성되므로 출력은 [1], [1, 2]가 된다. 일반적으로는 None을 기본값으로 사용해 호출마다 새 리스트를 만든다.

10. 디버깅과 테스트

디버깅은 오류 원인을 찾아 수정하는 과정이다.

  • 브레이크포인트와 한 단계 실행
  • 변수·호출 스택 확인
  • 로그와 추적 출력
  • 최소 재현 예제 작성
  • 정상·경계·예외 입력 테스트
  • 가정 하나씩 검증

오류가 나타난 지점과 잘못된 상태가 처음 만들어진 지점을 구분해야 한다.

11. 예외 전파와 catch 순서

예외는 현재 블록에 일치하는 처리기가 없으면 호출 스택을 따라 바깥으로 전파된다. 상속 관계의 예외를 여러 개 잡을 때는 구체적인 하위 예외를 먼저 둔다.

JAVA코드 영역 안에서 좌우로 이동할 수 있습니다.
try {
    readFile();
} catch (java.io.FileNotFoundException e) {
    // 더 구체적인 처리
} catch (java.io.IOException e) {
    // 더 넓은 처리
}

상위 예외를 먼저 잡으면 뒤의 하위 예외 처리기는 도달할 수 없어 컴파일 오류가 된다.

12. finally의 return 함정

finally에서 return하면 try나 catch가 반환하려던 값 또는 전파하려던 예외를 덮어쓸 수 있다.

JAVA코드 영역 안에서 좌우로 이동할 수 있습니다.
static int f() {
    try { return 1; }
    finally { return 2; }
}

이 메서드는 2를 반환하지만 오류 원인을 숨길 수 있으므로 finally에서는 정리만 수행하고 반환·예외 덮어쓰기를 피하는 것이 좋다. Python도 finallyreturn이 기존 반환이나 예외를 가릴 수 있다.

13. 자원 닫기 순서와 억제 예외

Java의 try-with-resources는 선언의 역순으로 자원을 닫는다.

JAVA코드 영역 안에서 좌우로 이동할 수 있습니다.
try (A a = new A(); B b = new B()) {
    work();
} // b.close() 후 a.close()

본문 예외와 close 예외가 함께 발생하면 본문 예외가 주 예외로 전파되고 닫기 예외는 suppressed exception으로 기록될 수 있다.

Python의 with는 컨텍스트 관리자의 __enter____exit__를 이용한다. __exit__가 참을 반환하면 해당 예외를 처리한 것으로 간주해 전파를 막을 수 있다.

14. C 동작 분류

분류의미예시
정의되지 않은 동작표준이 결과를 요구하지 않음부호 있는 정수 오버플로, 해제 메모리 역참조
구현 정의 동작구현이 한 선택을 정하고 문서화일반 char의 부호 여부
미지정 동작허용된 여러 결과 중 하나, 문서화 의무 없음일부 평가 순서 선택

정의되지 않은 동작을 특정 출력으로 묻는 문제는 타당하지 않다. 문제에서 구현·비트 폭·언어 버전을 명시했는지도 확인한다.

15. 예외 연결과 원인 보존

Python에서는 raise NewError() from original로 새 예외와 원래 원인을 연결할 수 있다. Java에서는 예외 생성자의 cause나 initCause를 이용할 수 있다. 단순히 새 예외만 던져 원인을 버리면 장애 분석이 어려워질 수 있다.

확인 문제

  1. Java에서 상위 예외 catch를 하위 예외 catch보다 먼저 두면 왜 문제가 되는가?
  2. try { return 1; } finally { return 2; }의 반환값은 무엇이며 왜 권장되지 않는가?
  3. try-with-resources의 두 자원 A, B는 어떤 순서로 닫히는가?
  4. C의 일반 char 부호 여부는 정의되지 않은 동작과 구현 정의 동작 중 어느 것인가?
  5. Python의 raise NewError() from original은 무엇을 보존하는가?