ToBe끝판왕

[ JAVA ] 예외 처리 (2) 본문

■ 프로그래밍 SKILLS/JAVA

[ JAVA ] 예외 처리 (2)

업그레이드중 2024. 8. 21. 22:22
반응형





 

 

예외 처리

 

•  try ~ catch 문을 사용해서 정상 흐름과 예외 흐름을 분리한다.

-  자바 예외 처리 매커니즘을 이해할 수 있다.

-  정상 흐름은 try 블럭에 모아서 처리

-  예외 흐름은 catch 블럭에 모아서 처리

 

•  finally

-  catch에서 잡을 수 없는 예외 발생 시, 반드시 호출되어야 하는 코드가 호출되지 않는 문제 해결 가능

-  try 블럭이 시작하기만 하면, finally 블럭은 어떤 경우라도 반드시 호출된다.

-  try ~ catch 안에서 잡을 수 없는 예외가 발생하더라도 finally는 반드시 호출 된다.

-  fianlly 코드 블럭이 끝나고 난 이후 예외가 밖으로 던져진다.

주로 try에서 사용한 자원을 해제할 때 사용

 

 

•  예외처리 소스 예시

package exception.ex2;

public class NetworkClientExceptionV2 extends Exception {

    private String errorCode;

    public NetworkClientExceptionV2(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public String getErrorCode() {
        return errorCode;
    }
}
package exception.ex2;

public class NetworkClientV2 {

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

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

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

    public void send(String data) throws NetworkClientExceptionV2 {
        if(sendError) {
            throw new NetworkClientExceptionV2("sendError",address + " 서버에 데이터 전송 실패");

            //중간에 다른 예외가 발생했다고 가정
            //throw new RuntimeException("ex");
        }
        // 전송 성공
        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;
        }
    }
}
package exception.ex2;

public class NetworkServiceV2_5 {

    public void sendMessage(String data) {
        String address = "http://www.example.com";
        NetworkClientV2 client = new NetworkClientV2(address);
        client.initError(data);     // 추가

        try {
            // try 블럭안에 하나의 정상적인 흐름을 담는다.
            client.connect();
            client.send(data);
        } catch (NetworkClientExceptionV2 e) {

            // 예외부분은 catch 블럭에서 처리
            System.out.println("[오류] 코드: " + e.getErrorCode() + ", 메시지: " + e.getMessage());

        } finally {
            client.disconnect();
        }

    }
}
package exception.ex2;

import java.util.Scanner;

public class MainV2 {

    public static void main(String[] args) throws NetworkClientExceptionV2 {

        //NetworkServiceV2_1 networkService = new NetworkServiceV2_1();
        //NetworkServiceV2_2 networkService = new NetworkServiceV2_2();
        //NetworkServiceV2_3 networkService = new NetworkServiceV2_3();
        //NetworkServiceV2_4 networkService = new NetworkServiceV2_4();
        NetworkServiceV2_5 networkService = new NetworkServiceV2_5();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("전송할 문자: ");
            String input = scanner.nextLine();
            if( input.equals("exit") ) {
                break;
            }
            networkService.sendMessage(input);
            System.out.println();
        }
        System.out.println("프로그램을 정상 종료합니다");
    }
}
// 실행 결과

전송할 문자: hello
http://www.example.com 서버 연결 성공
http://www.example.com 서버에 데이터 전송: hello
http://www.example.com 서버 연결 해제

전송할 문자: error1
[오류] 코드: connectError, 메시지: http://www.example.com 서버 연결 실패
http://www.example.com 서버 연결 해제

전송할 문자: error2
http://www.example.com 서버 연결 성공
[오류] 코드: sendError, 메시지: http://www.example.com 서버에 데이터 전송 실패
http://www.example.com 서버 연결 해제

전송할 문자: exit
프로그램을 정상 종료합니다

 

 

 


 

 

예외처리의 계층화

 

 

•  NetworkClientExceptionV3 / ConnectExceptionV3 / SendExceptionV3 로 계층화를 시킨다.

package exception.ex3.exception;

public class ConnectExceptionV3 extends NetworkClientExceptionV3{

    private final String address;

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

    public String getAddress() {
        return address;
    }
}
package exception.ex3.exception;

public class SendExceptionV3 extends NetworkClientExceptionV3{

    private final String sendData;

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

    public String getSendData() {
        return sendData;
    }
}
package exception.ex3.exception;

public class NetworkClientExceptionV3 extends Exception{

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

 

 

•  위에서 정의한 예외들로 예외처리를 하는 소스 예시

-  예외 클래스를 각각의 예외 상황에 맞추어 만들면, 각 필요에 맞는 예외를 잡아서 처리 가능

package exception.ex3;


import exception.ex3.exception.ConnectExceptionV3;
import exception.ex3.exception.NetworkClientExceptionV3;
import exception.ex3.exception.SendExceptionV3;

public class NetworkServiceV3_2 {

    public void sendMessage(String data) {
        String address = "http://www.example.com";
        NetworkClientV3 client = new NetworkClientV3(address);
        client.initError(data);     // 추가

        try {
            // try 블럭안에 하나의 정상적인 흐름을 담는다.
            client.connect();
            client.send(data);
        } catch (ConnectExceptionV3 e) {
            System.out.println("[연결 오류] 주소: " + e.getAddress() + ", 메시지: " + e.getMessage());
        } catch (NetworkClientExceptionV3 e) {
            System.out.println("[네트워크 오류] 메시지: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("[알 수 없는 오류] 메시지: " + e.getMessage());
        } finally {
            client.disconnect();
        }

    }
}
package exception.ex3;

import exception.ex3.exception.ConnectExceptionV3;
import exception.ex3.exception.SendExceptionV3;

public class NetworkClientV3 {

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

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

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

    public void send(String data) throws SendExceptionV3 {
        if(sendError) {
            //throw new SendExceptionV3(data,address + " 서버에 데이터 전송 실패: " + data);

            throw new RuntimeException("ex");

        }
        // 전송 성공
        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;
        }
    }
}
// 실행 결과

전송할 문자: hello
http://www.example.com 서버 연결 성공
http://www.example.com 서버에 데이터 전송: hello
http://www.example.com 서버 연결 해제

전송할 문자: error1
[연결 오류] 주소: http://www.example.com, 메시지: http://www.example.com 서버 연결 실패
http://www.example.com 서버 연결 해제

전송할 문자: error2
http://www.example.com 서버 연결 성공
[네트워크 오류] 메시지: http://www.example.com 서버에 데이터 전송 실패: error2
http://www.example.com 서버 연결 해제

전송할 문자: exit
프로그램을 정상 종료합니다

 

 


 

 

실무에서의 예외 처리

 

 

•  처리할 수 없는 예외

-  해당 예외의 경우, 고객에게는 오류 메시지를 보여주고 내부 개발자가 문제 상황을 빠르게 인지할 수 있도록

   오류에 대한 로그를 남겨 둬야 한다. ( 오류 메시지 + 오류 로그 )

 

-  체크 예외의 경우, 최상위 타입인 Exception으로 던지는 개발을 하지 않는다.

    ( 다른 체크 예외를 체크할 수 있는 기능의 무효화 되어 중요한 체크 예외를 다 놓치게 된다. )

 

-  언체크 예외의 경우, 예외를 공통으로 처리할 수 있는 곳을 만들어 한곳에서 해결하면 된다.

    ( 오류메시지와 개발자를 위한 에러 로그를 남기는것은 공통처리로 가능 하다. )

 

 

•  코드 예시

package exception.ex4.exception;

public class NetworkClientExceptionV4 extends RuntimeException{

    public NetworkClientExceptionV4(String message) {

        super(message);
    }
}
package exception.ex4.exception;


public class ConnectExceptionV4 extends NetworkClientExceptionV4 {

    private final String address;

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

    public String getAddress() {
        return address;
    }
}
package exception.ex4.exception;

public class SendExceptionV4 extends NetworkClientExceptionV4 {

    private final String sendData;

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

    public String getSendData() {
        return sendData;
    }
}

 

 

•  위 소스를 바탕으로 예외 처리

-  실무에서는 System.out 이나 System.err 을 통해 콘솔에 무언가를 출력하기 보다는, 주로 Slf4J, logback 같은 별도의

   로그 라이브러리를 사용해서 콘솔과 특정 파일에 함께 결과를 출력한다

package exception.ex4;

import exception.ex4.exception.ConnectExceptionV4;

public class NetworkClientV5 implements AutoCloseable{

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

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

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

    public void send(String data)  {
        if(sendError) {
            //throw new SendExceptionV3(data,address + " 서버에 데이터 전송 실패: " + data);

            throw new RuntimeException("ex");

        }
        // 전송 성공
        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()  {
        System.out.println("NetworkClientV5.close");
        disconnect();
    }
}
package exception.ex4;

public class NetworkServiceV5 {

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



        try(NetworkClientV5 client = new NetworkClientV5(address)) {
            client.initError(data);
            client.connect();
            client.send(data);
        } catch(Exception e) {
            System.out.println("[예외 확인]: " + e.getMessage());
            throw e;
        }

    }
}
package exception.ex4;

import exception.ex4.exception.SendExceptionV4;

import java.util.Scanner;

public class MainV4 {

    public static void main(String[] args) {

        //NetworkServiceV4 networkService = new NetworkServiceV4();
        NetworkServiceV5 networkService = new NetworkServiceV5();


        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("전송할 문자: ");
            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("== 개발자용 디버깅 메시지 ==");
        e.printStackTrace(System.out);  // 스택 트레이스 출력
        //e.printStackTrace();

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

    }
}
// 실행 결과


전송할 문자: hello
http://www.example.com 서버 연결 성공
http://www.example.com 서버에 데이터 전송: hello
NetworkClientV5.close
http://www.example.com 서버 연결 해제



전송할 문자: error1
NetworkClientV5.close
http://www.example.com 서버 연결 해제
[예외 확인]: http://www.example.com 서버 연결 실패
사용자 메시지: 죄송합니다, 알 수 없는 문제가 발생했습니다.
== 개발자용 디버깅 메시지 ==
exception.ex4.exception.ConnectExceptionV4: http://www.example.com 서버 연결 실패
	at exception.ex4.NetworkClientV5.connect(NetworkClientV5.java:17)
	at exception.ex4.NetworkServiceV5.sendMessage(NetworkServiceV5.java:12)
	at exception.ex4.MainV4.main(MainV4.java:24)



전송할 문자: error2
http://www.example.com 서버 연결 성공
NetworkClientV5.close
http://www.example.com 서버 연결 해제
[예외 확인]: ex
사용자 메시지: 죄송합니다, 알 수 없는 문제가 발생했습니다.
== 개발자용 디버깅 메시지 ==
java.lang.RuntimeException: ex
	at exception.ex4.NetworkClientV5.send(NetworkClientV5.java:27)
	at exception.ex4.NetworkServiceV5.sendMessage(NetworkServiceV5.java:13)
	at exception.ex4.MainV4.main(MainV4.java:24)



전송할 문자: exit
프로그램을 정상 종료합니다

 

 

 

 

※ 해당 내용은 인프런 김영한 강사님의  "실전 자바 중급1" 인강의 참고자료와 예시들을 참고하였습니다

 

 

 

반응형
Comments