자바 스터디 9주차

김성훈·2022년 9월 24일
0
post-thumbnail

자바의 예외 처리에 대해 학습하세요


학습할 것 (필수)

자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
자바가 제공하는 예외 계층 구조
Exception과 Error의 차이는?
RuntimeException과 RE가 아닌 것의 차이는?
커스텀한 예외 만드는 방법


Error(에러)와 Exception(예외의 차이)

에러(error)란 컴퓨터 하드웨어의 오동작 또는 고장으로 인해 응용프로그램에 이상이 생겼거나 JVM 실행에 문제가 생겼을 경우 발생하는 것을 말한다.
이 경우 개발자는 대처할 방법이 극히 제한적이다.
하지만 예외(Exception)은 다르다.
예외란 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류를 말한다.
예외가 발생하면 프로그램이 종료가 된다는것은 에러와 동일하지만 예외는 예외처리(Exception Handling)을 통해 프로그램을 종료 되지 않고 정상적으로 작동되게 만들어 줄 수 있다.
자바에서 예외는 Try Catch문을 통해 해줄 수 있다.

밑에서 자세하게 알아보자

자바가 제공하는 예외 계층 구조


예외 클래스의 구조다.
모든 예외 클래스는 Throwable 클래스를 상속받고 있으며, Throwable은 최상위 클래스 Object 의 자식이다.

Throwable을 상속받는 클래스는 Error와 Exception이 있다.
Error는 시스템 레벨의 심각한 수준의 에러를 의미한다.
Effective Java 에서는 이 클래스를 개발자가 상속 받지도 잡지도(catch) 말라고한다

반변에 Exception은 개발자가 로직을 추가하여 처리할 수 있다.
Exception은 수많은 자식 클래스를 가지고 있다.
그 중 우리는 RuntimeException에 주목해야 한다.
RuntimeException은 CheckedException과 UnCheckedException을 구분하는 기준이다.
Exception의 자식 클래스 중 RuntimeException을 제외한 모든 클래스는 CheckedException이며, RuntimeException과 그의 자식 클래스들을 UnCheckedException이라 부른다.


Checked Exception이 발생할 가능성이 있는 메소드라면 반드시 try/catch로 감싸거나 throw로 던져서 처리해야 한다. 즉, 꼭 처리해야 하는 Exception이다.

반면에, Unchecked Exception은 명시적인 예외처리를 하지 않아도 된다.

이 예외는 피할 수 있지만 개발자가 부주의해서 발생하는 경우가 대부분이고, 미리 예측하지 못했던 상황에서 발생하는 예외가 아니기 때문에 굳이 로직으로 처리를 할 필요가 없도록 만들어져 있다.

예외처리 (Try Catch)

      try {
            if ( accessToken != null ) {
                // 유효한 토큰인지 확인
                jwtTokenProvider.validateToken(accessToken);
                this.setAuthentication(accessToken);
            } else if ( refreshToken != null ) {
                jwtTokenProvider.validateToken(refreshToken);
            } else {
                request.setAttribute("exception", ErrorCode.TOKEN_NULL);
            }
        } catch (SignatureException | SecurityException | MalformedJwtException e) {
            request.setAttribute("exception", ErrorCode.WRONG_TYPE_TOKEN);
            e.printStackTrace();
        } catch (ExpiredJwtException e) {
            request.setAttribute("exception", ErrorCode.EXPIRED_TOKEN);
            e.printStackTrace();
        } catch (UnsupportedJwtException e) {
            request.setAttribute("exception", ErrorCode.UNSUPPORTED_TOKEN);
            e.printStackTrace();
        } catch (EmptyTokenException e) {
            request.setAttribute("exception", ErrorCode.TOKEN_NULL);
        } catch (IllegalArgumentException e) {
            request.setAttribute("exception", ErrorCode.WRONG_TOKEN);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
          //예외발생여부에 관계없이 상항 수행되어야 하는 문장적는다.
          }
        filterChain.doFilter(request, response);
       }

try 블록에는 예외가 발생할 수 있는 코드가 위치한다.
try 블록의 코드가 예외없이 정상 실행되면 catch 블록의 코드는 실행되지 않고 finally 블록의 코드를 실행한다.(finally 블록은 생략 가능하다)
try catch 문을 주로 사용하는 구간은 데이터베이스에 데이터를 주고 받을 때와 유효성 검증에서 많이 사용한다.
데이터베이스를 거쳐 올때나 유효성 검사를 할때는 변수가 많이 생기기 때문에 try catch문은 필수다.
그리고 finally 에는 데이터 베이스와의 연결을 끊어주는 코드를 주로 삽입한다.
하지만 지금은 데이터베이스 연결을 자동으로 끊어주기 때문에 사용하는 일은 거의 없다.
try->catch->finally 순으로, 발생 하지 않은 경우 try->finally 순으로 실행

  • getMessage()
    : 발생한 예외클래스의 인스턴스에 저장된 메세지를 얻을 수 있음.
    오류에 대한 기본적인 내용을 알수 있지만 상세하지 않다. 
  • printStackTrace()
    예외발생당시의 호출스택(Call Stack) 에 있었던 메서드이 정보와 예외 메세지를 콘솔 화면에 출력한다.
    getMessage 와는 다르게 printstackTrace는 리턴값이 없다.
    이 메소드를 호출하면 메소드가 내부적으로 예외 결과를 화면에 출력한다.

    printStackTrace는 가장 자세한 예외 정보를 제공한다.

예외 던지기

throw e

throw 키워드를 사용하여 프로그래머가 고의로 예외를 발생시킬 수 있다.

예외던지기 Flow
1. try 안에서 연산자 new를 이용해서 발생시키려는 예외클래스의 객체생성.
2. 키워드 throw를 사용해서 생성한 예외를 catch 던진다.
3. catch 에서 던진 예외를 잡아서 확인 또는 처리한다.

public static void main (String[] args) {
    try {
        Exception e = new Exception("고의발생"); // 1. 예외 생성
        throw e; // 2. 예외 던지기 
    } catch (Exception e) {
        System.out.println("에러메세지:" + e.getMessage());
        e.printStackTrace(); //예외정보 출력 
    }
    System.out.println("프로그램 정상 종료.");
}

//결과
에러메세지: 고의발생

java.lang.Exception: 고의발생
     at ExceptionEx9.main(ExceptionEx9.java:4)
     
프로그램 정상 종료.

throws

예외가 발생한 메소드를 호출한 곳으로 예외 객체를 넘기는 방법.
자바의 예외 처리 방법은 예외가 발생한 지점에서 try-catch 혹은 try-catch-finally 라고 살펴봤다.
하지만 이 블럭을 사용하지 않아도 예외가 발생한 메소드를 호출한 지점으로 예외를 전달하여 처리하는 방법이 있다.
-> 이때 사용되는 예약어가 throws 이다.

public class Test{
	static void callDriver() throws ClassNotFoundException{
		Class.forName("oracle.jdbc.driver.OracleDriver");
		System.out.println("완료");
	}

	public static void main(String[] args){
		try{
			callDriver();
		}catch(ClassNotFoundException e){
			System.out.println("클래스를 찾을 수 없습니다.");
		}finally{
			System.out.println("시스템 종료");
		}
	}

}

callDrvier() 메소드에서는 ClassNotFoundException 에 대한 처리를 직접 메소드 내에서 try/catch로 처리하지 않고 throws 하여 호출한 영역에서 처리하도록 한다.

커스텀한 예외 만드는 법

커스텀 예외를 만들때 참고해야 할 4가지 Best Practices

  1. Always Provide a Benefit
    자바 표준 예외에 포함되어 있는 다양한 장점을 가지는 기능들이 있다.
    이미 JDK가 제공하고 있는 방대한 수의 예외들과 비교했을 때 만들고자 하는 커스텀 예외가 어떠한 장점을 제공하지 못한다면 어떠한 장점도 제공할 수 없는 예외를 만들기보다 UnsupportedOperationException 이나, IllegalArugmentException 과 같은 표준 예외 중 하나를 사용하는 것이 낫다.
  1. Follow the Naming Convention
    JDK가 제공하는 예외 클래스들을 보면 클래스의 이름이 모두 "Exception으로 끝난다.
    이러한 네이밍 규칙은 자바 전체에 사용되는 규칙으로 만들려고 하는 커스틈 예외 클래스도 이 네이밍 규칙을 따르는 것이 좋다.
  1. Provide javadoc Comments for your Exception Class
    많은 커스텀 예외들이 어떠한 javadoc 코멘트도 없이 만들어진 경우가 있다.
    기본적으로 API의 모든 클래스, 멤버변수, 생성자들에 대해서는 문서화 하는 것이 일반적인 Best Practices이다.

그래서 예외 또한 문서화가 필요하다


/**
 * The MyBusinessException wraps all checked standard Java exception and enriches them with a custom error code.
 * You can use this code to retrieve localized error messages and to link to our online documentation.
 * 
 * @author TJanssen
 */
public class MyBusinessException extends Exception { ... }
  1. Provide a Constructor That Sets the Cause
    커스텀 예외를 던지기 전에 표준 예외를 Catch 하는 케이스가 많다.
    보통 캐치된 예외에는 제품에 발생한 오류를 분석하는데 필요한 중요한 정보가 있는데 이처럼 에러에 대한 상세 정보를 제공해야 한다.
   public List<OpportunityEntity> getOpportunityEntitiesFromExcelFile() throws IOException {
        this.memberMap = getMemberMapFromRepository(memberRepository);
        Sheet sheet = getSheet(sheetName);
        if ( sheet == null ) {
            throw new ExcelSheetNameNotFountException(ErrorCode.NOT_FOUND_SHEET);
        }
        Iterator<Row> rowIterator = sheet.rowIterator();

  NOT_FOUND_SHEET(404,  "엑셀 시트('opp')를 찾을 수 없습니다"),

<중요> Exception과 RuntimeException

Exception 과 RuntimeException은 예외의 원일을 기술하고 있는 Throwable 을 받을 수 있는 생성자 메소드를 제공한다.

만들고자 하는 커스텀 예외도 이렇게 하는 것이 좋다.

발생한 Throwable 를 파라미터를 통해 가져올 수 있는 생성자를 최소한 하나를 구현하고 수퍼클래스에 Throwable을 전달해줘야 한다.


대표적인 예외

ArithmeticException
정수를 0으로 나눌경우 발생

ArrayIndexOutOfBoundsExcetion
배열의 범위를 벗어난 index를 접근할 시 발생

ClassCastExcetion
변환할 수 없는 타입으로 객체를 반환 시 발생

NullPointException
존재하지 않는 레퍼런스를 참조할때 발생

IllegalArgumentException
잘못된 인자를 전달 할 때 발생

IOException
입출력 동작 실패 또는 인터럽트 시 발생

OutOfMemoryException
메모리가 부족한 경우 발생

NumberFormatException
문자열이 나타내는 숫자와 일치하지 않는 타입의 숫자로 변환시 발생

<출처>

https://coding-factory.tistory.com/280

https://www.notion.so/3565a9689f714638af34125cbb8abbe8

profile
"한 명이 걷는 천 걸음 보다 천 명이 함께 걷는 한 걸음이 성공의 시작이고 완성이다"

0개의 댓글