S3 upload 적용기 & transaction 처리 방식
다음과 같이, media service를 구현하였다.
기존에는 Board 이미지와, Board를 생성하는 로직을 하나의 API 에서 처리하도록 하였다.
하지만 upload가 되지 않을 때, 롤백 시켜주는 예외 처리할 게 많기 때문에 가독성이 너무 별로였다.
@Override
public Long saveBoard(final String json, final List<MultipartFile> files, final String userId) {
final User user = findUserByUserId(userId);
final Board board = getBoard(json, user.getUserId());
boardRepository.insertBoard(board);
saveImages(files, user.getUserId(), board.getBoardId());
return board.getBoardId();
}
private void saveImages(
final List<MultipartFile> files,
final String userId,
final Long boardId
) {
try {
mediaService.insertMedias(boardId, files, "board/" + userId);
} catch (final Exception e) {
boardRepository.deleteBoard(boardId);
throw new BoardException("파일 저장에 실패하였습니다.");
}
}
수정 시에도 예외 처리가 필요하다. 트랜 잭션 처리를 위해서 중간 객체인 boardTransactionService 또한 만들어줬어야 했다..
@Override
public void modify(
final Long boardId, final String userId,
final String json, final List<MultipartFile> files
) {
final BoardModifyRequest boardModifyRequest =
(BoardModifyRequest) JsonUtil.readValue(json, BoardModifyRequest.class);
final User user = findUserByUserId(userId);
final Board board = findBoardByBoardId(boardId);
validateSameMember(userId, board.getUserId());
final Board modifyBoard = Board.builder()
.boardId(boardId)
.userId(user.getUserId())
.subject(boardModifyRequest.getSubject())
.content(boardModifyRequest.getContent())
.build();
boardTransactionService.updateBoard(modifyBoard);
modifyMedias(board, files);
}
private void modifyMedias(final Board board, final List<MultipartFile> files) {
try {
uploadService.deleteMedias(findFileUrls(board.getBoardId()));
mediaService.insertMedias(board.getBoardId(), files, "board/" + board.getUserId());
} catch (final Exception e) {
boardTransactionService.updateBoard(board);
throw new BoardException("게시글 수정에 실패하였습니다.");
}
}
삭제 시도 마찬가지 이다..
@Override
public void delete(final Long boardId, final String userId) {
final User user = findUserByUserId(userId);
final Board board = findBoardByBoardId(boardId);
validateSameMember(user.getUserId(), board.getUserId());
boardTransactionService.deleteBoard(boardId);
deleteMedias(board);
}
private void deleteMedias(final Board board) {
try {
final List<String> fileUrls = findFileUrls(board.getBoardId());
uploadService.deleteMedias(fileUrls);
} catch (final Exception e) {
boardTransactionService.insertBoard(board);
throw new BoardException("게시글 삭제에 실패하였습니다.");
}
}
다음과 같이 board를 생성하는 로직에 너무 많은 서비스들이 들어 있었다. Board 서비스에 너무 많은 책임이 있다고 판단하였다.

또한 테스트도 너무 어려웠다..