Java 예외 - 실무에서의 처리 공통 예외 처리 구현

네트워크 예제를 가지고 처리가 불가능한 예외를 공통으로 처리하기

언체크 예외

                  +------------------+
                  | RuntimeException |
                  +---------+--------+
                            |
                +-----------+------------+
                | NetworkClientException |
                +-----------+------------+
                            |
+-----------------------+   |   +------------------------+
| ConnectException      +---+---+ SendException          |
| field::String address |       | field::String sendData |
+-----------------------+       +------------------------+
  • 언체크 예외 사용에는 RuntimeException 상속 받아 예외 객체를 생성한다.
  • NetworkClientException 생성한 예외 객체과 상속 받은 자식은 모두 언체크 예외가 되었다.

NetworkClientException.class

public class NetworkClientException extends  RuntimeException {
    public NetworkClientException(String message) {
        super(message);
    }
}

ConnectException.class

public class ConnectException extends NetworkClientException {

    private final String address;

    public ConnectException(String message, String address) {
        super(message);
        this.address = address;
    }

    public String getAddress() {
        return address;
    }
}

SendException.class

public class SendException extends NetworkClientException {
    private final String sendData;

    public SendException(String sendData, String message) {
        super(message);
        this.sendData = sendData;
    }

    public String getSendData() {
        return sendData;
    }
}

NetworkClient.class

public class NetworkClient {
    private final String address;
    public boolean connectError;
    public boolean sendError;

    public NetworkClient(String address) {
        this.address = address;
    }

    public void connect() {
        // exception error
        if (connectError) {
            throw new ConnectException(address, address + " is Connect Open Failed");
        }

        // Connect Success
        System.out.println(address + " Connect Open Success");
    }

    public void send(String data) {
        if (sendError) {
            throw new SendException(data, address + " Send Failed" + data);
        }
        // 전송 성공
        System.out.println(address + " Send Message data: " + data);
    }

    public void disconnect() {
        System.out.println(address + " Connect Closed Success");
    }

    public void initError(String data) {
        if (data.contains("error1")) {
            connectError = true;
        }

        if (data.contains("error2")) {
            sendError = true;
        }
    }
}

NetworkService.class

public class NetworkService {

    public void sendMessage(String data) {
        String address = "http://kiioio.com";
        NetworkClient client = new NetworkClient(address);
        client.initError(data); // 오류 코드로 반환.

        try {
            client.connect();
            client.send(data);
        } finally {
            client.disconnect();
        }

    }
}
  • 네트워크, 데이터베이스 예외는 공통 예외 처리로 여기서 진행하지 않는다.
  • 연결과 관련된 예외를 없애준다.
  • ConnectException, SendException 예외가 발생하여도 해당 오류는 복구가 불가능하다.
  • ConnectException, SendException 예외 발생 시 밖으로 던진다.
  • 해결 할 수 없는 예외 없어져 본래의 코드에 집중할 수 있어서 가독성이 좋아졌다.

Main.class

public class Main {
    public static void main(String[] args) {

        NetworkService networkService = new NetworkService();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("Send Message data: ");
            String input = scanner.nextLine();
            if (input.equals("exit")) {
                break;
            }

            // 공통 예외 처리
            try {
                networkService.sendMessage(input);
            } catch (Exception e) {
                exceptionHandler(e);
            }
            System.out.println();
        }
        System.out.println("네트워크 프로그램 종료");
    }

    // 공통 예외 처리 메소드
    private static void exceptionHandler(Exception e) {
        // 공통 처리 로직
        System.out.println("알 수 없는 문제가 발생하였습니다.");
        System.out.println("[DEBUG] ");
        e.printStackTrace(System.out);
    }
}

Send Message data: error2
http://kiioio.com Connect Open Success
http://kiioio.com Connect Closed Success
알 수 없는 문제가 발생하였습니다.
[DEBUG]
SendException: http://kiioio.com Send Failederror2
at NetworkClient.send(NetworkClient.java:22)
at NetworkService.sendMessage(NetworkService.java:10)
at Main.main(Main.java:18)

Send Message data: error1
http://kiioio.com Connect Closed Success
알 수 없는 문제가 발생하였습니다.
[DEBUG]
ConnectException: http://kiioio.com
at NetworkClient.connect(NetworkClient.java:13)
at NetworkService.sendMessage(NetworkService.java:9)
at Main.main(Main.java:18)

Send Message data: exit
네트워크 프로그램 종료

Main 에서 공통 예외를 처리되어 오류 문구를 사용자에게 표시하고 다른 개발자에게 인지시켜 오류를 처리한다.

  • Exception 잡아 해결 못하는 예외는 공통으로 처리가 가능하다.
  • 예외는 객체이므로 메소드 인자로 객체를 전달한다.

exceptionHandler() 메소드

  • 개발자가 해결할 수 없는 예외 발생 시 사용자에게 알림을 보내도록 처리해야한다.
    • 디테일 오류 문구는 보내지 않아도 된다.
  • 개발자는 문제를 찾아 디버깅에 용이한 오류 문구를 남겨야 한다.
  • 예외는 객체이므로 instanceof 키워드로 객체 타입을 확인하여 다운캐스팅된 자식클래스를 처리할 수 잇다.

printStackTrace() 메소드

  • 예외 메시지의 스택 메시지를 출력한다.
  • 예외가 발생한 지점을 추적할 수 있다.
  • printStackTrace(System.out) 인자로 표준 출력한다.
  • printStackTrace(System.err) 인자로 표준 오류로 출력한다.
    • IDE 에서는 빨강색이 표시
    • 콘솔로만 표시된다.
    • 스프링에서 파일 저장되는 SLF4J 또는 Logback 으로 사용해서 활용되지 않음


다운캐스팅으로 예외를 별도 처리하기

만약 특정 예외가 들어온 경우 다운캐스팅하여 멤버필드를 불려와 메시지를 표시할 수 있다.

Main.class

// 공통 예외 처리 메소드
private static void exceptionHandler(Exception e) {
    // 공통 처리 로직
    System.out.println("알 수 없는 문제가 발생하였습니다.");
    System.out.println("[DEBUG] ");
    e.printStackTrace(System.out);

    if (e instanceof SendException error) {
        System.out.println("[Send Error] Send Mesage: " + error.getSendData());
    }
}

Send Message data: error2
http://kiioio.com Connect Open Success
http://kiioio.com Connect Closed Success
알 수 없는 문제가 발생하였습니다.
[DEBUG]
SendException: http://kiioio.com Send Failederror2
at NetworkClient.send(NetworkClient.java:22)
at NetworkService.sendMessage(NetworkService.java:10)
at Main.main(Main.java:18)
[Send Error] Send Mesage: error2

다운캐스팅된 SendException 객체를 불려와 멤버필드를 불려와 보냈던 메시지가 무엇인지 출력할 수 있다.