공부를 하면 할 수록 프로젝트하면서 중요하게 처리해야 하는 것 중 하나가 예외처리라는 점을 느끼는 중이다..
근데 이게 너무 어렵다..
단순하게 오류가 발생하면 catch에서 오류가 발생했다는 것을 의미하는 응답을 하도록 하는 정도야 쉽지만 데이터베이스에 접근하는 과정이 있는 처리에서 오류가 발생한다면 롤백을 생각해야 한다.
그걸 간단하게 처리해줄 수 있는 어노테이션이 @Transactional 이다.
물론 이 어노테이션은 UncheckedException에 대해서만 롤백을 처리해준다.
CheckedException은 개발자가 예외처리를 할 수 있는 오류라고 판단해서 롤백을 해주지 않도록 만들었다고 한다.
하지만 방법이 없는것은 아니다. @Transactional(rollbackFor = {Exception.class}) 이렇게 rollbackFor 옵션을 통해 CheckedException이 발생했을 때도 롤백을 하도록 처리할 수 있다.
간단하게 하나의 메소드에서 모든 처리를 담당한다고 했을 때, 이 메소드에 어노테이션을 붙여주면 롤백에 대한 걱정은 크게 하지 않아도 된다.
하지만 그렇지 않은 경우에는?
여러가지를 고려해야 할 수 밖에 없다.
여러 테스트를 해보면서 알게 된 것은 상위 메소드에 @Transactional을 달아두고 하위 메소드에서 데이터베이스에 요청을 보낸 뒤 오류가 발생하는 경우에는 롤백처리가 되지 않았다.
이런 경우 하위메소드에 @Transactional을 달아줘야 문제가 해결된다.
그럼 그렇다고 모든 메소드에 어노테이션을 달아야 하는가?
매번 그런 조건을 달고서만 메소드 분리를 해야하는가?
이런 고민이 생기게 되었다.
또한, 하위메소드에서 데이터베이스에 요청을 보내지 않아도 무언가를 처리하다가 오류가 발생할 수도 있는데 그럼 그때는 다른 메소드에서 발생한 요청에 대해서는 어떻게 롤백을 해야하지?
이렇게 그냥 밑도끝도 없이 내려가게 되었다.
트랜잭션이라는 것이 '데이터베이스 상태를 변환시키기 위한 하나의 작업의 단위다.' 라고 배웠고, 상위 메소드에 달아주면 당연히 호출한 메소드들의 것들까지 다 처리해줄거라고 생각했는데 그건 또 아니었던것 같다..
그리고 또 하나의 문제가 있었다.
try-catch로 예외처리를 하는 경우에는 롤백이 동작하지 않는다.
예외가 발생하는 경우 catch에서 강제로 Exception을 발생시키도록 하면 롤백이 될거라고 생각했으나 되지 않았다.
근데 된다는 포스팅들도 있다. 그래서 이건 아직 테스트가 더 필요할 것 같다..
문제해결
문제를 해결하긴 했으나 막 만족스럽진 않고 일차원적인 해결이 아닐까 라고 생각한다.
쇼핑몰 프로젝트였고 상품 정보를 수정하는 과정이다.
서비스 호출 -> 대표 썸네일 저장 처리 -> 상품 데이터 수정 요청 -> 상품 옵션 수정 요청
-> 삭제해야할 대표 썸네일 삭제처리 -> 삭제해야할 썸네일 삭제 처리 -> 삭제해야할 썸네일 데이터 삭제 요청
-> 삭제해야할 상품 정보 이미지 삭제 처리 -> 삭제해야할 상품 정보 이미지 데이터 삭제 요청
-> 새로운 썸네일 저장 처리 -> 새로운 썸네일 데이터 리스트 삽입 요청
-> 새로운 상품 정보 이미지 저장 처리 -> 새로운 상품 정보 이미지 데이터 리스트 삽입 요청
상품 관련 처리이다 보니 겹치는 기능이 많았다. 그래서 메소드를 분리했는데
파일 저장 처리 메소드, 파일 삭제 처리 메소드가 분리되어 있으며 각 처리 메소드에서는 데이터베이스에 요청을 보내는것 까지가 초기 설계였다.
좀 편하게 정리하기 위해 각 메소드를 아래처럼 쓴다.
- 컨트롤러부터 호출되는 메소드 - 최상위 메소드
- 파일 저장 및 데이터 삽입 요청 메소드 - 저장 메소드
- 파일 삭제 및 데이터 삭제 요청 메소드 - 삭제 메소드
최상위 메소드에 @Transactional 어노테이션을 붙여주고 rollbackFor 옵션을 설정한 뒤
테스트 했을 때는 위에서 얘기했다 시피 파일 저장이나 삭제 처리에서 오류가 발생한 경우 하위 메소드에서 발생한 요청에 대해 롤백 처리가 되지 않았다.
그래서 각 하위 메소드들에 try-catch로 예외처리를 하고 강제로 Exception을 발생시켜 봤지만 그것도 안됐고, throws로 떠넘겨도 처리할 수 없었다.
이유는 throws로 떠넘겨 봤자 최상위 메소드에서 역시 try-catch로 감싸줘야 했고, catch에서 강제로 Exception을 발생시켜도 롤백은 처리되지 않았기 때문이다.
이래저래 고민해보고 테스트해보고 해봤지만 마땅한 해결책을 찾을 수 없어 메소드들의 처리 구조를 바꿔주게 되었다.
일단 하위 메소드 두개에서 데이터베이스에 요청을 보내지 않도록 수정했다.
하위 메소드들은 파일 저장, 삭제 처리 후 해당 데이터들을 상위 메소드로 리턴하도록 했다.
그럼 최상위 메소드에서는 그 데이터를 받아 데이터베이스에 요청을 보내도록 수정했다.
이렇게 처리하니 하위메소드에서 오류가 발생하더라도 최상위 메소드에서 모든 데이터베이스 요청을 처리하기 때문에 정상적으로 롤백을 수행할 수 있게 되었다.
일차원적으로 문제를 해결했다는 이유가 이것 때문이었다.
파일 저장 메소드는 상품 수정 뿐만 아니라 상품 등록 처리에서도 사용되어야 한다.
그리고 파일 삭제 메소드는 상품 삭제 처리에서 사용되어야 한다.
그나마 상품 삭제에서 파일 삭제 메소드는 cascade 설정으로 인해 파일 삭제만 처리하고 결과를 반환해도 되지만
저장 메소드는 데이터 삽입 요청이기 때문에 이걸 분리하는게 맞나 싶었다.
'중복되는 코드를 최소화 하고 재사용성을 높인다'에 이게 부합하는건지,
어차피 하위 메소드에서 데이터를 처리한 뒤 그 데이터를 반환하면 상위 메소드에서는 리턴받은 데이터로 요청만 하면 되니까 최소화를 한 것이다 라고 봐야할지 고민이 되었다.
예외처리가 너무 어렵다................................
'Project&문제해결' 카테고리의 다른 글
JDBCTemplate 동적 쿼리 문제 해결 (0) | 2023.10.20 |
---|---|
Servlet&JSP에서 Ajax Response (0) | 2023.10.20 |
REST 프로젝트에 JWT 적용 (0) | 2023.06.12 |
Eclipse에서 생성한 프로젝트 IntelliJ에서의 컴파일 문제해결 (0) | 2021.11.24 |
Naver SmartEditor2 X-frame 문제(Spring security) (0) | 2021.09.21 |