인프런 커뮤니티 질문&답변

권정익님의 프로필 이미지
권정익

작성한 질문수

김영한의 실전 자바 - 중급 1편

섹션 10. try - with - resources

해결된 질문

작성

·

135

·

수정됨

0

질문입니다!

상황 1 .

우선 NetworkClientExceptionV5는 RuntimeException을 상속 받은 상태입니다.

public class NetworkClientExceptionV5 extends RuntimeException {

    public NetworkClientExceptionV5(String message) {
        super(message);
    }
}

그러면 NetworkClientExceptionV5를 상속받은 ConnectException과 SendException도 RuntimeException이 되는 거구요.

public class ConnectExceptionV5 extends NetworkClientExceptionV5 {

    private final String address;

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

    public String getAddress() {
        return address;
    }
}
public class SendExceptionV5 extends NetworkClientExceptionV5 {

    private final String data;

    public SendExceptionV5(String data, String message) {
        super(message);
        this.data = data;
    }

    public String getData() {
        return data;
    }
}

 

 

상황 2 .

그리고 NetworkClientV5는 AutoCloseable을 구현했습니다.

 

import exception.ex5.exception.ConnectExceptionV5;
import exception.ex5.exception.SendExceptionV5;

public class NetworkClientV5 implements AutoCloseable {

    private final String address;
    private boolean connectError;
    private boolean sendError;

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

    public void connect() {
        if (connectError) {
            throw new ConnectExceptionV5(address, address + " 서버 연결 실패");
        }
        // 연결 성공
        System.out.println(address + " 서버 연결 성공");
    }

    public void send(String data) {
        if (sendError) {
            throw new SendExceptionV5(data, address + " 서버에 데이터 전송 실패 : " + data);
        }
        // 전송 성공
        System.out.println(address + " 서버에 데이터 전송 : " + data);
    }

    public void disconnect () {
        System.out.println(address + " 서버 연결 해제");
    }

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

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

    @Override
    public void close() throws Exception {
        System.out.println("NetworkClientV5.close");
        disconnect();
    }
}

 

==질문입니다!!==

NetworkServiceV5에서

public class NetworkServiceV5 {

    public void sendMessage(String data) {
        String address = "http://example.com";

        try (NetworkClientV5 client = new NetworkClientV5(address)) {
            client.initError(data); // 추가
            client.connect();
            client.send(data);
        }
    }
}

try (NetworkClientV5 client = new NetworkClientV5(address))에서 컴파일 오류가 납니다.

client에서 올라오는 예외들은 다 RuntimeException이므로 따로 throws를 안 해줘도 되는데 왜 해결하려고 하면 throws를 선언하거나 catch를 해야한다고 하는 건가요?

RuntimeException은 예외를 throws 선언없이 자동으로 밖으로 던지기에 catch를 안 해도 상관이 없을텐데 왜 alt + enter을 누르면 throws 선언과 catch를 해야만 된다고 하는 건지 이해가 가지 않습니다.

제가 생각하는 건 이런식으로 흘러간다고 생각했습니다.

main()에서 (error1 | error2)를 입력 받음 -> sendMessage를 호출 -> sendMessage에서 코드 진행 -> (client.connect | client.send)를 만나서 NetworkClientV5에 들어감 -> (ConnectException | SendException)을 터트림 -> NetworkClientV5에서 예외를 밖으로 던짐 (런타임이라서 throws 선언 안 해도 던져짐) -> NetworkService로 예외가 날라옴 -> 여기서도 처리가 불가능 하니, 예외를 밖으로 던짐 (여기서 throws 선언을 안해도 던져져야 한다고 생각함. 날아온 예외는 RuntimeException이기 때문에) -> main으로 예외가 넘어옴 -> main에는 catch (Exception e)가 있어서 예외를 잡고 정상흐름으로 돌아옴.

 

마지막은 메인메서드 입니다!

import exception.ex5.exception.SendExceptionV5;

import java.util.Scanner;

public class NetworkmainV5 {

    public static void main(String[] args) {
        NetworkServiceV5 service = new NetworkServiceV5();
        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.print("전송할 문자 : ");
            String input = scanner.nextLine();

            if (input.equals("exit")) {
                break;
            }

            try {
                service.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("==개발자용 디버깅 메시지==");
        // 스택 트레이스 출력
        e.printStackTrace(System.out);
        // 스택 트레이스 출력 System.err
        //e.printStackTrace();

        // 필요하면 예외별로 별도의 추가 처리 가능
        if (e instanceof SendExceptionV5 sendEx) {
            System.out.println("[전송 오류] 전송 데이터 = " + sendEx.getData());
        }
    }
}

 

답변 1

2

안녕하세요, 인프런 AI 인턴입니다.

님의 질문에 대한 답변입니다.

try (NetworkClientV5 client = new NetworkClientV5(address)) 에서 컴파일 오류가 발생하는 이유는 NetworkClientV5 클래스의 close() 메소드 때문입니다. close() 메소드가 Exception을 throw 하도록 선언되어 있습니다. 이는 곧 close() 메소드가 RuntimeException이 아닌, 체크 예외(check exception)을 발생시킬 수 있다는 것을 의미합니다. AutoCloseable 인터페이스의 close() 메소드는 체크 예외를 던질 수 있으며, 이로 인해 try-with-resources 블록을 사용할 때 해당 예외를 처리하도록 강제되는 것입니다.

따라서, 님이 직접 처리한 RuntimeException들(예: ConnectExceptionV5, SendExceptionV5)과는 별개로, close() 메소드의 예외 처리를 위해 try 블록 밖에 throws Exception 선언을 하거나, catch 블록을 추가하여 해당 예외를 처리해야 합니다. NetworkClientV5close() 메소드가 체크 예외를 던질 가능성이 있기 때문에 컴파일러는 예외 처리를 요구하는 것입니다.

이와 관련된 예외 처리 방식을 코드에 반영하려면, NetworkServiceV5 클래스 내에서 sendMessage 메소드를 다음과 같이 업데이트할 수 있습니다:

public class NetworkServiceV5 {

    public void sendMessage(String data) {
        String address = "http://example.com";

        try (NetworkClientV5 client = new NetworkClientV5(address)) {
            client.initError(data);
            client.connect();
            client.send(data);
        } catch (RuntimeException e) {
            // RuntimeException 처리
        } catch (Exception e) {
            // AutoCloseable의 close() 메서드로 인한 Exception 처리
        }
    }
}

이렇게하면 try 블록 내에서 발생할 수 있는 RuntimeExceptionAutoCloseableclose() 메서드로 인해 발생할 수 있는 체크 예외 모두를 처리할 수 있습니다.

권정익님의 프로필 이미지
권정익

작성한 질문수

질문하기