Throwable
: 최상이 예외Error
: 메모리 부족이나 심각한 시스템 오류와 같이 애플리케이션에서 복구가 불가능한 시스템 예외Exception
- 애플리케이션 로직에서 사용할 수 있는 실질적인 최상위 예외
- Exception과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다. 단 RuntimeException은 예외
RuntimeException
: 언체크 예외, 런타임 에외 - 컴파일러가 체크하지 않는 언체크 예외
- RuntimeException과 그 자식 에외는 모두 언체크 예외이다
사용자 정의 예외 계층
예외 계층화의 장점
- 부모의 예외를 잡거나 던지면 자식 예외도 함께 잡거나 던질 수 있다
- 특정 예외를 잡아서 처리하고 싶다면 하위예외를 잡아서 따로 처리하면 된다
사용자정의 최상위 예외
public class NetworkClientExceptionV3 extends Exception{
public NetworkClientExceptionV3(String message) {
super(message);
}
}
네트워크 예외와 관련된 예외는 NetworkClientException
을 상속받을 수 있다
이렇게 예외 계층을 만들면 포괄적인 예외처리가 필요할 땐 NetworkClientException
을 예외처리하면되고 특정 예외를 잡아서 처리하고 싶을 땐 자식 예외를 예외처리하면 된다.
연결 오류예외
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;
}
}
ConnectException
은 연결 오류에 대한 예외로 NetworkClientException
을 상속받는다.ConnectException
예외 클래스는 자체적으로 address
네트워크 연결주소를 필드로 보관하며 get메서드를 통해서 해당 주소를 받을 수 있다.
데이터 전송오류 예외
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;
}
}
SendException
예외 클래스는 네트워크 연결은 성공했으나 데이터를 전송하는 과정에서 발생하는 예외를 처리하기위한 클래스이다.SendException
클래스 내부에는 전송하고자하는 데이터를 내부적으로 필드를 통해 보관하며 이 또한 get메서드를 통해 데이터를 조회할 수 있다.
예외를 발생시키는 클라이언트 코드
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, " 서버 연결 실패");
}
System.out.println(address + "서버 연결 성공");
}
public void send(String data) throws SendExceptionV3 {
if (sendError) {
throw new SendExceptionV3(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;
}
}
}
initError()
메서드를 통해 예외가 발생하는 문자열이 입력될 경우 예외가 발생하게 된다
connect()
- 만약 "error1"문자열이 입력될 경우
ConnectException
예외가 발생하게 되는데 해당 예외는throws
를 통해 서비스 계층으로 예외를 넘긴다. - 정상 문자열이 넘어올 경우에는 정상흐름으로 넘어간다
send()
- 만약 "error2"문자열이 입력될 경우
SendException
예외가 발생하며connect()
메서드와 마찬가지로 서비스 계층으로 예외를 넘긴다 - 정상 문자열이 넘어올 경우에는 정상흐름으로 넘어간다
disconnect()
- 네트워크 연결과정에서 생성된 자원을 해제하는 메서드로 정상흐름으로 넘어던, 예외가 발생하던 반드시 호출되야하는 메서드이므로 예외처리 과정에서는
finally
키워드에 작성한다.
예외를 잡는 서비스 계층
public class NetworkServiceV3_1 {
public void sendMessage(String data) {
String address = "http://example.com";
NetworkClientV3 client = new NetworkClientV3(address);
client.initError(data);
try {
client.connect();
client.send(data);
} catch (ConnectExceptionV3 e) {
System.out.println("[연결 오류] 주소: " + e.getAddress() + ", 메시지: " + e.getMessage());
} catch (SendExceptionV3 e) {
System.out.println("[전송 오류] 전송 데이터: " + e.getSendData() + ", 메시지: " + e.getMessage());
} finally {
client.disconnect();
}
}
}
try
의 body부분에는 정상흐름 로직을 작성함으로써 정상흐름과 오류처리 부분에대한 코드를 분리할 수 있다catch
가 2개 있는 것을 확인할 수 있는데 각각ConnectException
(연결 오류),SendException
(전송 오류)를 처리하는 catch부분으로 네트워크 연결과정에서 연결 오류가 발생할 수도있고, 전송오류가 발생할 수도있기때문에 catch를 2개 선언해주어야한다.
핵심인 부분은 catch를 어떻게 작성했냐인데
각각의 예외는 처리하는 방법이 다르다는 것을 확인할 수 있다 쉽게 말하면 예외에 맞는 예외 메시지를 제공함으로써 예외 메시지를 명확하게 함으로써 개발자는 에러메시지를 명확하게 확인할 수 있다.
===한마디로 예외 계층을 분리함으로써 연결 오류 예외에 대한 메시지, 전송 오류 예외에 대한 메시지를 구분하여 예외를 명확하게 확인할 수 있다===
예외처리의 일반적인 방법
체크예외를 사용하게되면 컴파일러를 통해 사전에 누락되는 예외를 방지할 수 있어 좋은 예외처리방법일 수 있지만, 실제로는 수많은 라이브러리를 사용하다보니 라이브러리에서 발생하는 예외들을 throw받으면서 특정 계층에서 엄청난 양의 예외처리를 해야하는 불상사가 발생하게 된다.
거기다 이런 예외들은 보통 물리적인 환경문제라던가, 애플리케이션 코드에서 해결할 수 없는 예외들이 대부분이다.
때문에 최근 실무에서의 예외처리 방법은 RuntimeException
을 사용하여 대부분의 예외들을 생략하여 넘기고 특정 계층에서 공통적으로 예외처리하는 방법을 선호한다.
RuntimeException 계층 생성
public class NetworkClientExceptionV4 extends RuntimeException{
public NetworkClientExceptionV4(String message) {
super(message);
}
}
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;
}
}
public class SendExceptionV4 extends NetworkClientExceptionV4{
private final String data;
public SendExceptionV4(String data, String message) {
super(message);
this.data = data;
}
public String getData() {
return data;
}
}
SendException
과 ConnectException
예외는 NetworkClientException
을 상속받고 NetworkClientException
클래스는 RuntimeException
을 상속받기 때문에 런타임(언체크)예외 계층을 생성했다.
특정 문자열 입력시 런타임 예외를 발생하는 Client코드
public class NetworkClientV4 {
private final String address;
private boolean connectError;
private boolean sendError;
public NetworkClientV4(String address) {
this.address = address;
}
public void connect() {
if (connectError) {
throw new ConnectExceptionV4(address, "[연결 오류] 서버: " + address);
}
System.out.println("정상 연결");
}
public void sendMessage(String data) {
if (sendError) {
throw new SendExceptionV4(address, "[전송 오류] 서버: " + address + ", 전송 데이터: " + data);
}
System.out.println("데이터 전송 성공: " + data);
}
public void disconnect() {
System.out.println("연결 종료");
}
public void initError(String data) {
if (data.equals("error1")) {
connectError = true;
}
if (data.equals("error2")) {
sendError = true;
}
}
}
런타임 예외를 throws로 그대로 다음 메서드로 던진다.
(원한다면 catch로 잡을 수 있다)
public class NetworkServiceV4 {
public void sendMessage(String data) {
String address = "https://example.com";
NetworkClientV4 client = new NetworkClientV4(address);
client.initError(data);
try{
client.connect();
client.sendMessage(data);
} finally {
client.disconnect();
}
}
}
예외 공통처리 Main메서드 (핵심)
public class MainV4 {
public static void main(String[] args) {
NetworkServiceV4 networkService = new NetworkServiceV4();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("전송할 문자: ");
String input = sc.nextLine();
if (input.equals("exit")) {
break;
}
try {
networkService.sendMessage(input);
} catch (Exception e) {
exceptionHandler(e);
}
}
System.out.println("프로그램을 정상 종료합니다.");
}
private static void exceptionHandler(Exception e) {
System.out.println("사용자 메시지: 죄송합니다 알 수 없는 문제가 발생했습니다");
System.out.println("=== 개발자용 디버깅 메시지 ===");
e.printStackTrace();
if (e instanceof SendExceptionV4 sendException) {
System.out.println("전송 오류입니다." + sendException.getData());
}
}
}
exceptionHandler(Exception e)
이 메서드로 프로그램의 모든 예외를 처리하였다.
만약 좀 더 디테일한 예외 처리(예외 메시지)가 필요할 경우 if
문과 instanceof
을 사용하여 특정 예외를 처리할수도 있다.
'Language > Java' 카테고리의 다른 글
[Java] 람다 표현식과 함수 인터페이스 (0) | 2024.11.08 |
---|---|
[JAVA] 자바의 정렬 인터페이스 Comparable과 Comparator (0) | 2024.07.04 |
해시 알고리즘 (0) | 2024.06.29 |
[JAVA - 자료구조] HashSet (0) | 2024.06.29 |
[JAVA] 불변객체 (0) | 2024.06.24 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!