[12.29] 내일배움캠프[Spring] TIL-42
1. Spring Exception

ResponseHeader
에 응답 코드 담아서 보냄(403..500...200..)
HttpStatus,ResponseEntity
- 코드 따고 들어가보면
enum
으로 다 정의되어 있음!! 엄청 편리..!

- 여태까지 우리의 코드에서는 Client의 실수 ( ex.중복 이름, 중복된 어떤 작업실행) 했을 때,
throw
로 Exception을 던져줬었다. -> 500 인데 그러면 클라이언트 실수가 아니라 서버코드 오류라는 뜻 -> 서버 코드 오류가 아닌데도 서버코드 오류 찾고 있는거에요...하아..!
- 그렇기 때문에 응답을 보낼 때 예외가 아닌
errorMessage
,httpStatus
로 구성된 Json
타입으로 보내줘야 한다는 이야기가 된다..
{
"errorMessage":"중복된 폴더명을 제거해 주세요! 폴더명: 신발",
"httpStatus":"BAD_REQUEST"
}
- 그 전 프로젝트에서도 했었지만, 저러한
Json
형태로 오류 메세지와 상태코드를 응답으로 내려주기 위해서는
JavaClass객체에 변수를 초기화 한 상태로 반환해줘야 했었다.
-> 그래서 ResponseMsgStatusDto를 만들어서 반환해줬었다.
public ResponseEntity randomLogic(){
try{
....비지니스 로직 수행
} catch(IllegalArgumentExcetion e){
...비지니스 로직 수행 중 오류 발생
return new ResponseEntity(ResponseMsgStatusDto,HttpStatus.BAD_REQUEST)
}
}
- 이런식으로 반환해주자...!
- 근데 이렇게 비즈니스 로직마다 다 이렇게 넣어주기에는 코드의 중복도 많고, 굉장히 비효율적이다..!!!
@ExceptionHandler
Exception
을 모듈화 한 AOP
라고 쉽게 생각하자!
- 다음과 같은 코드를 해당 비즈니스 로직 수행하는
Controller
에 추가하자!
@ExceptionHandler({ IllegalArgumentException.class })
public ResponseEntity randomLogic(){
try{
....비지니스 로직 수행
} catch(IllegalArgumentExcetion e){
...비지니스 로직 수행 중 오류 발생
return new ResponseEntity(ResponseMsgStatusDto,HttpStatus.BAD_REQUEST)
}
}
- 근데 이것도 많이 모듈화 되었지만,
Controller
는 한개가 아니다... 이것 마저 중복이 싫다!!!!
Global Exception
@ControllerAdvice
@RestControllerAdvice
= @ControllerAdvice
+ @RestController
@RestControllerAdvice
public class RestApiExceptionHandler{
@ExcetpionHandler(value = { IllegalArgumentException.class })
public ResponseEntity<Object> handleApiRequestException(IllegalArgumentException e){
return new ResponseEntity(ResponseMsgStatusDto,HttpStatus.BAD_REQUEST)
}
}
2. Spring Transaction
- 우리는 항상 비즈니스 로직 즉 DB와의 입출력을 하는 부분의 함수에
@Transactional
을 사용했음.
- 만약 데이터를 5개를 저장하는 중 3번째 데이터에서 Exception이 터졌다면 어떻게 될까?라는 의문에서 시작
Transaction
- 데이터베이스에서 데이터에 대한 하나의 논리적 실행단계
- 더 이상 쪼갤 수 없는 최소단위의 작업
- 하나의 최소 단위의 작업에 여러가지 데이터 변경을 넣으면, 모두 저장 Or 아무것도 저장 안됨을 보장해야함.
- 어노테이션이 아닌 트렌젝션 매니저를 사용해서 폴더 생성하는 코드 예시
public List<Folder> addFolders(List<String> folderNames, User user) {
TransactionStatus status = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
List<Folder> existFolderList = folderRepository.findAllByUserAndNameIn(user, folderNames);
List<Folder> savedFolderList = new ArrayList<>();
for (String folderName : folderNames) {
if (isExistFolderName(folderName, existFolderList)) {
throw new IllegalArgumentException("중복된 폴더명을 제거해 주세요! 폴더명: " + folderName);
} else {
Folder folder = new Folder(folderName, user);
folder = folderRepository.save(folder);
savedFolderList.add(folder);
}
}
transactionManager.commit(status);
return savedFolderList;
} catch (Exception ex) {
transactionManager.rollback(status);
throw ex;
}
}
- 우리는 어노테이션 하나로 트렌젝션의 원자성을 보장하는 Commit , Rollback을 쉽게 사용하고 있었던 것.