본문 바로가기

JAVA

예외 처리(Exception Handling)에 대해 알아보자

예외 처리(Exception Handling)란

먼저 예외(Exception)은 Application 실행 중 발생하는 예기치 못한 상황 또는 오류를 말한다.
예를 들어 처리가 필요한 값이 Null이거나 작업에 필요한 파일을 찾지 못하는 등 예외 상황이 발생하는 경우이다.
이를 적절하게 처리하고 대응하는것이 예외 처리(Exception Handling) 이라 하며 적절한 조치를 통해 Application이 강제종료되게 하지 않고 이어나갈 수 있도록 해야한다.

예외 처리의 구현

예외 처리는 try-catch 문을 사용해서 구현한다. try 블록내에 예외가 발생할 수 있는 코드를 구현 하고 catch 블록에 발생할 가능성이 있는 Exception을 작성하고, 다른 방법으로는 예외를 호출자에게 전달하는 throws를 사용 할 수도 있다. finally를 사용해서 예외 발생 여부와 상관없이 실행되는 구문을 작성한다. 이를 통해 적절한 처리를 하거나 오류 메시지 출력, 로그 기록 등의 작업을 수행한다.

throws

예외 발생시 메소드 호출자에게 전달하여 예외 처리 책임을 넘기는 구문이다.
public void throwsException() throws CheckedException{
	...
}​

메소드 옆 throws로 작성하며 호출자에게로 예외를 전달한다. 이로인해 코드의 중복을 줄이고 예외를 중앙 집중화 시켜 가독성과 유지보수성이 향상된다.

class ExamException extends Exception {
}

public class Sample {
    public void oneCheck(int one) throws ExamException {
        if(one == 1) {
            throw new ExamException();
        }
        System.out.println("숫자 1이 아님");
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        try {
            sample.oneCheck(1);
            sample.oneCheck(2);
        } catch (ExamException e) {
            System.out.println("ExamException이 발생했습니다.");
        }
    }
}

 

ExamException이 발생했습니다.


위 코드의 경우 oneCheck 메소드에서 발생한 Exception을 호출한 곳인 main 메소드에서 처리하게 구현했다.
첫번째 코드인 Sample.oneCheck 메서드에서 Exception이 발생하여 catch를 통해 종료되었다.

finally

예외 발생 여부와 상관없이 실행되는 블록이다. catch의 다음 블록에 작성하며 아래와 같은 순서대로 작성한다.
try{ 
	...
} catch (Exception e){
	...
} finally {
	...
}​
class ExamException extends Exception {
}

public class Sample {
    public void oneCheck(int one) throws ExamException {
        if(one == 1) {
            throw new ExamException();
        }
        System.out.println("숫자 1이 아님");
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        try {
            sample.oneCheck(1);
            sample.oneCheck(2);
        } catch (ExamException e) {
            System.out.println("ExamException이 발생했습니다.");
        } finally {
        	sample.oneCheck(2);
        }
    }
}
ExamException이 발생했습니다.
숫자 1이 아님

 이전 throws에서는 exception이 발생함에 따라 sample.oneCheck(2)가 실행되지 않았었다. 하지만 반드시 실행시켜야 할 경우 finally 구문을 사용하여 exception이 발생하더라도 실행 시킬 수 있다.

Exception과 RuntimeException

예외는 두가지로 나뉜다.  Exception과 RuntimeException과 이다.
Exception은 컴파일 시점에서 판단하고 RuntimeException의 경우 실행 시점에서 판단한다.
때문에 Exception은 aplication 실행시 예상이 가능한 예외라서 Checked Exception, RuntimeException은 실행 전까지는 확인이 어렵기 때문에 UncheckedException이라고도 한다.

예외 처리를 했을때와 안했을때

 

    @GetMapping("exceptionTest")
    public void exceptionTest(){
        userService.ExceptionMethod("");
    }

	public void ExceptionMethod(String str){
    	try{
        	str = str.subString(3);
        }catch (StringIndexOutOfBoundsException e){
        	System.out.println("전달된 값이 인덱스보다 작습니다.");
        }
}​

빈 문자열 str을 자르는 메소드를 작성했다. 빈 문자열을 3으로 자르려고 하기 때문에 StringIndexOutOfBoundsException 예외가 발생할 것이고 그에 따른 처리를 확인한다.

 

이와 같이 예외가 발생했지만 Application은 그대로 작동됨을 알 수 있다.

 

하지만 만약 예외 처리를 하지 않았을 경우

    @GetMapping("exceptionTest")
    public void exceptionTest(){
        userService.ExceptionMethod("");
    }

	public void ExceptionMethod(String str){
        	str = str.subString(3);
	}

이와 같이 StringIndexOutOfBoundsException 이 발생하며 서버가 종료 됐다. 이로써 예외처리를 적절하게 한다면 예외가 발생하더라도 서버가 종료되지 않음을 알 수 있었다.

 


전역 예외 처리(GlobalExceptionHandlr)

전역 예외 처리란 Spring/SpringBoot에서 Application 전체에서 발생하는 에러를 중앙에서 처리하는 방법이다.
이는 예외 처리에 대한 반복적인 작성을 줄이고 한곳에서 Exception을 처리 할 수 있는 장점을 가진다.
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NullPointerException.class)
    public ResponseEntity<String> handleException(Exception e){
        return ResponseEntity.ok("데이터가 없습니다.");
    }
}

@RestController
public class UserController {

    @GetMapping("nullException")
    public ResponseEntity<String> nullException(
            @RequestParam(value = "data",required = false) String data
    ){
        int length = data.length();
        return ResponseEntity.ok("성공");
    }
 }

전역 예외 처리를 하는 GlobalExceptionHandler의 경우 클래스에 @ControllerAdvice을 작성하여 구현한다.

 

@ExceptionHandler를 통해 특정 예외를 지정하여 예외 처리를 할 수 있는데 예제에 작성된 Exception의 경우 NullPointerException을 지정하고 발생시 "데이터가 없습니다."를 응답한다.

 

Controller에 작성된 nullException 메소드는 NullPointerException을 발생 시키기 위해 문자열의 길이를 반환하는 코드를 작성한 상태이다.

 

실행 결과

NullPointerException을 발생시키기 위해 Params를 지정하지 않고 요청을 보냈으며 GlobalExceptionHandler의 handleException이 작동하여 "데이터가 없습니다." 문자열을 출력한 것을 볼 수있다. 또한 예외 처리를 함으로써 서버도 종료되지 않고 정상작동됨을 확인하였다.

 


CustomException

CustomException은 사용자가 정의한 예외 클래스로 특정 상황이 발생했을 때 발생시키는 예외이다. Exception 클래스를 상속받아 구현한다.
    public class AdminException extends Exception{
        public AdminException(){
        }
    }

    @ExceptionHandler(AdminException.class)
    public ResponseEntity<String> adminHandler(){
        return ResponseEntity.ok("관리자만 접근 가능합니다.");
    }


    @GetMapping("onlyadmin")
    public ResponseEntity<?> onlyAdmin(
            @RequestParam(value = "name") String name
    ) throws AdminException {
        if (!name.equals("admin")) {
            throw new AdminException();
        }
        return ResponseEntity.ok("관리자 확인");
    }​


AdminException 이라는 customException을 만들고 그것을 GlobalExceptionHandler에 전역 예외처리 하였다.
파라미터로 받은 name의 변수가 admin이 아니라면 AdminException이 발생하도록 만들었다.
결과는 성공적으로 admin의 경우 문제없이 리턴되었고, admin이 아닐 경우 Exception이 발생하여 관리자만 접근이 가능합니다. 라는 문구가 리턴 되었다.

 

'JAVA' 카테고리의 다른 글

Call By Value와 Call By Reference  (0) 2023.06.19