토비의스프링 Study(4) - 예외
4.1 사라진 SQLException
난감한 예외처리방식(1)
try{
...
}catch(SQLException e){
} // 예외를잡고 아무것도 하지않는 처리방식
예외가 발생해도 무시하고 진행되기때문에, 예외가 발생하는것보다 나쁜상황이다.
시스템오류나 이상한결과의원인이 무엇인지 찾아내기 힘든 상황이 발생할수있다.
난감한 예외처리방식(2,3)
try{
...
}catch(SQLException e){
System.out.print(e)
}
try{
...
}catch(SQLException e){
e.printStackTrace();
}
다른 로그나 메시지에 묻힐수있음
모든 예외는 적절하게 복구되던가, 작업을 중단시키고 운영자또는 개발자에게 분명하게 통보되어야함
차라리 메소드밖으로 던지는게 나을정도다.
스택영역에 에러스택이 쌓이고 pop하여 스택프레임을 출력
무의미,무책임한 throws
public void method1() throws Exception{
method2();
}
public void method2() throws Exception{
method3();
}
public void method3() throws Exception{
...
}
무슨 예외가발생할지 의미있는 정보를 얻을수없음.
복구할수있는 예외상황도 제대로 다루기힘들어짐
예외전파
메서드에서 에러가 발생한다
메서드는 에러에대한정보, 타입, 시점이 담긴객체를 만들고 런타임시스템에 전달한다. 그리고 이것을 예외를 던진다고 표현한다.
메서드가 예외를던지면 런타임시스템이 예외를 처리할 무언가를 찾기위해 시도한다
이미지 출처: https://docs.oracle.com/javase/tutorial/essential/exceptions/definition.html
Call stack = 예외가발생한 메서드에 도달하기 위해 호출된 메서드의 목록
오류가 발생한 메서드에 도달하기위해 콜스택을 이용하여 도달한다.
이미지 출처: https://docs.oracle.com/javase/tutorial/essential/exceptions/definition.html
런타임 시스템이 오류가 발생한 메서드에서 호출된 역순으로 Callstack을 타면서 예외를 처리할수있는 코드를 확인하고,
적절한 예외처리 핸들러가 발견되면 런타임시스템이 예외를 핸들러에 전달한다.
Exception을 처리할수있는 핸들러를 찾을떄까지 상위로 전달된다.
마지막 메인에서까지 에러를던진다면? 스택트레이스를 보여주고 그 쓰레드가 종료된다.
예외처리 비용
checked , unchecked exception
에러 ( 시스템에 비정상적인 상황 발생)
- OutOfMemoryError, ThreadDeath
출처: https://madplay.github.io/post/java-checked-unchecked-exceptions
Exception,checked exception (애플리케이션 코드의 작업중 예외상황발생)
checked Exception = Exception의 하위클래스중 RuntimeException을 제외한 Exception을 상속받은 Exception
주로 외부의영향으로 발생하는것들이며, 프로그램사용자들의 동작에 의해서 발생하는경우가 많다.
컴파일시점에 확인할수있는 예외
(FileNotFoundException , ClassNotFoundExcption, DataFormatException 등)
컴파일시점에 확인되어, 바로 예외처리해줘야한다.
Unchecked Exception = RuntimeException을 상속받은 Exception, Error 와 하위클래스
일반적으로 발생을 예측할수없고 복구할수없는 예외를 뜻함. 주로 외부API사용, programming errors 등으로 나타남
(ArrayIndexOutOfBoundsException, NullPointerException,ClassCastException, ArithmeticException)
= 배열범위벗어남, null의 참조변수를 호출, 형변환실수, 정수를 0으로나눔
두가지의 명확한기준은 반드시 처리 해야하냐 말아야하냐의 차이이다.
Checked Exception이 발생할가능성있는 메소드는 반드시 try~catch 로 감싸거나 throw로 던져서 처리해야한다.
반면, Unchecked Exception은 명시적인 예외처리를 하지않아도된다.
자바에서 checked, unchecked exception 나눈이유
https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html
메소드를 호출하는쪽은 그 메서드가 어떤 예외를 발생시킬수있는가에대해 반드시 알아야한다.
따라서 자바는 checked exception을 통해 해당 메서드가 발생시킬수있는 예외를 명세하도록 강제한다.
RuntimeException 은 예외를 명세하지않아도 되도록 한 이유
코드짠 개발자의 실수로인해 생기는 예외가 RuntimeException이어서, 클라이언트쪽에서(메소드호출) 이를 복구하거나 대처할수있을거라고 예상하기 힘들다. 또, 어디서든 매우빈번하게 발생할수있는게 RuntimeException 이기때문에 모든 Runtime Exception을 메서드에 명시하도록 강제하는것은 프로그램의 명확성을 떨어트릴수있다.
클라이언트가 exception을 회복할수있을거라고 예상되는경우 checked exception, 그렇지않은경우 unchecked exception 으로 만드는것이 좋다.
예외처리방법
예외복구
사용자 파일못읽어서 IOException -> 사용자에게 다른파일 이용하도록 안내해서 예외상황 해결
예외를 복구할수있으면 복구할수있을만큼 재시도 하는경우
예외회피
자신을 호출한쪽으로 throws로 던져지게하거나, catch문으로 예외를 잡은후로그를남기고 다시 예외를던지는방법
이유없이그냥 던져버리는건 무책임한 회피일수있다. 의도가 분명해야한다. 다른오브젝트에게 예외처리 책임을 분명히 지게해야한다.
예외전환
SQLExcetpion 이 날아왔는데 왜 SQLException이 발생했는지 알수없는 상황에 로그인 아이디 중복오류가 발생한다면 DuplicateUserIdException 같은 예외로 전환해서 던져주는것
복구하지못할 예외라면 런타임예외로 포장해서 던져버리고, 자세한 로그를 남기고 관리자에게는 메일통보, 사용자에게는 친절한 메시지를 보여주는방식이 바람직하다.
처리할수없는 예외는 빠르게 unchecked exception으로 날려라. 기계적인 throws로 던지지말고
4.2 예외전환의목적
- catch, throws 를 줄이기위해
- 좀더 의미있고 추상화된 예외로 바꿔주기위해
JDBC의한계
-
비표준SQL
- 페이징처리를 위해 로우의 시작위치, 개수를 지정하거나 쿼리에 조건을 포함시킴
=> SQL마다 다른 코드가 작성되어 , 특정 DB에 종속적인 코드가 된다.
- 페이징처리를 위해 로우의 시작위치, 개수를 지정하거나 쿼리에 조건을 포함시킴
-
SQLException
- 구체적인 에러코드가 DB에 종속적이다.
스프링 자체에서 DataAccessException을 만들어 SQLException 의 에러코드들을 DB별로 매핑해서 상황별로 다른 에러를 보내준다.
ORM에서는 발생하지만JDBC에는없는 예외같은경우에도 계층구조로 분류해놓았다.
스프링의 DataAccessException 은 SQLException을 unchecked exception으로 만들어줘서 불필요한 throws문을 줄이고, DB상황별 에러도 처리해주며, ORM에서만발생하는 예외도 처리해준다.
SQLException과 달리 특정 항목을 나타내는 여러 하위클래스가 있다.
예외를 좀더 정확하게 전달할수있고, 그에따라 복구가능한것은 또 잡을수도있겠다.
try {
//do data operation using JDBC abstraction layer
} catch (DatalntegrityViolationException ex) {
// Apply recovery strategy
}
JDBC 로직 try에들어가고 복구전략을위한 catch로직을 짤수있다.
try {
//do data operation using JDBC
} catch (SQLException ex) {
boolean recovered = false;
String sqlstate = sqlex.getSQLState();
if (sqlstate != null) {
String classCode = sqlstate.substring(0, 2) ;
if ("23".equals(classCode) ||"27".equals(classCode) ||
"44".equals(classCode)) {
// Apply recovery strategy
recovered = true;
}
}
if (!recovered)
throw new ApplicationSpecificExceptionf("Other SQL exception", ex);
}
SQL상태별 문자열을 확인하는방법밖에 없다.
코드로도 충분치않다. RDBMS별 구현옵션을 따져봐야한다.
Author And Source
이 문제에 관하여(토비의스프링 Study(4) - 예외), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@dudwls0505/토비의스프링-Study4-예외저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)