티스토리 뷰

첨부파일 API(업로드/다운로드/삭제)는 만들어놨는데, 게시글 기능이랑 따로 놀고 있었다.

게시글을 지워도 파일은 안 지워지고, 글 쓸 때 파일을 붙이려면 API를 두 번 불러야 했다. 좀 이걸 개선해보려고 한다

 

- 게시글 삭제 시 파일이 안 지워지는 버그

files 테이블은 board_id를 ON DELETE CASCADE로 참조하고 있어서, 게시글을 지우면 DB의 files 행은 자동으로 같이 지워진다.

근데 이건 DB 얘기고 디스크에 있는 실제 파일은 CASCADE가 손댈 수 없는 영역이다. 

BoardService.deleteBoard를 보니 boardRepository.deleteBoard(id)만 부르고 끝이었고, 그래서 게시글을 지울 때마다 디스크에 안 쓰는 파일이 계속 쌓이고 있었다. 

 // FileService.java
 // 게시글 삭제 시 호출됨. 해당 게시글의 파일들을 디스크에서만 삭제 (DB는 CASCADE로 이미 처리됨)
  public void deleteFilesFromDisk(Long boardId) throws IOException {
    // 삭제되기 전에 파일 목록을 미리 조회해둠
    List<FileEntity> files = fileRepository.selectFilesByBoardId(boardId);
    // 목록을 돌면서 디스크에서 하나씩 삭제
    for (FileEntity file : files) {
      Path filePath = Paths.get(file.getFilePath()).resolve(file.getStoredName());
      Files.deleteIfExists(filePath);
    }
  }
  
//
  
 // BoardService.java
public void deleteBoard(Long id, String password) throws IOException {
  BoardEntity board = boardRepository.selectBoardDetail(id);
  if (!board.getPassword().equals(password)) {
    throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
  }
  fileService.deleteFilesFromDisk(id);   // 먼저: 디스크 파일 정리
  boardRepository.deleteBoard(id);       // 그다음: DB에서 게시글 삭제 (CASCADE)
}

중요한건 순서다.

boardRepository.deleteBoard(id)를 먼저 부르면 CASCADE가 files 행을 먼저 지워버려서, 그다음엔 이 게시글에 어떤 파일이 있었는지 알 방법이 없어진다. 그래서 디스크 정리를 먼저 하고, DB 삭제를 나중에 했다.

 

- 게시글 등록 시 파일을 같이 보내기

 

지금까지 JSON은 @RequestBody, 파일은 @RequestParam으로 완전히 다른 요청으로 받았다. 두번이나 요청을 받는 구조가 별로다. 이번엔 하나의 요청 안에 게시글 정보(JSON)랑 파일들을 같이 실어보았다

 

multipart는 요청을 여러 칸으로 나누는 형식이라고 했었는데, 그 칸 중 하나에 파일 대신 JSON 텍스트를 넣을 수도 있다. 이 칸을 객체로 변환해서 받는 어노테이션이 @RequestPart다. @RequestBody multipart 버전이라고 보면 된다.

 

// BoardController.java
@PostMapping("/api/boards")
public BoardResponse createBoard(
    @RequestPart("request") @Valid BoardCreateRequest request,  //JSON -> BoardCreateRequest
    @RequestParam(value = "files", required = false) List<MultipartFile> files
) throws IOException {
  return boardService.createBoard(request, files);
}


// BoardService.java
public BoardResponse createBoard(BoardCreateRequest request, List<MultipartFile> files) throws IOException {
  BoardEntity board = boardMapper.toEntity(request);
  board.setViewCount(0);
  board.setCreatedAt(LocalDateTime.now());
  boardRepository.insertBoard(board);   // 이 순간 board.getId()에 생성된 id가 채워짐

  if (files != null && !files.isEmpty()) {
    fileService.uploadFiles(board.getId(), files);
  }
  return boardMapper.toResponse(board);
}

 

insertBoard로 id가 생기자마자 그 id로 기존 uploadFiles를 그대로 불렀다. 파일은 옵션이라 files 없거나 비어있으면 그냥 건너뛴다

 

- 게시글 수정 시 파일 추가/삭제

요구사항은 첨부된 파일 삭제 가능, 취소하면 삭제되지 않음 이었는데, 여기서 갈림길이   있었다

  1. 수정 요청에 파일 정보를 어떻게 실을지? 지울 id + 새로 추가할 파일을 따로 받을지, 아니면 최종 파일 목록을 통째로 받아서 서버가 비교할지
  2. 저장해야만 반영을 누가 책임질지? 프론트가 저장 버튼 누르기 전엔 API를 아예 안 부르는 걸로 충분한지, 아니면 서버가 임시 변경사항을 들고 있어야 하는지

둘 다 후자가 더 정교하지만 훨씬 복잡하다. 시간 관계상 전자(라고 쓰고 쉬운 쪽)를 선택했다 (ㅋㅋ)

 

//BoardService.java
public BoardResponse updateBoard(Long id, BoardUpdateRequest request, List<MultipartFile> files) throws IOException {
  BoardEntity board = boardRepository.selectBoardDetail(id);

  if (!board.getPassword().equals(request.password())) {
    throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
  }

  board.setCategoryId(request.categoryId());
  board.setTitle(request.title());
  board.setContent(request.content());
  board.setUpdatedAt(LocalDateTime.now());
  boardRepository.updateBoard(board);

  // 지울 파일이 있으면 하나씩 삭제 (기존 deleteFile 재사용)
  if (request.deleteFileIds() != null) {
    for (Integer fileId : request.deleteFileIds()) {
      fileService.deleteFile(fileId);
    }
  }

  // 새로 추가할 파일이 있으면 업로드 (기존 uploadFiles 재사용)
  if (files != null && !files.isEmpty()) {
    fileService.uploadFiles(id, files);
  }
  return boardMapper.toResponse(board);
}

 

쉬운 쪽을 선택하니 새로 만든 로직이 거의 없었다. 파일 삭제 API 만들 때 이미 짰던 fileService.deleteFile(id), 업로드 API 만들 때 짰던 fileService.uploadFiles(id, files)를 그대로 갖다 썼다. 쉬운 설계가 재사용 가능한 설계랑 같은 의미인걸까

테스트

실제로 서버를 켜서 게시글 생성(파일 포함) → 파일 목록 조회 → 게시글 수정(기존 파일 삭제 + 새 파일 추가) → 파일 목록 재조회까지 전체 흐름을 확인했다. 지운 파일은 목록에서도, uploads/ 폴더에서도 사라졌고, 새로 추가한 파일만 남았다

 

엄청 복잡할 줄 알았는데 재사용 가능한 방향으로 가다보니 코드를 재활용해서 시간이 많이 소요되지는 않았다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/08   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함