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 서비스에 너무 많은 책임이 있다고 판단하였다.

Untitled

구현