Weekly devIL 11

임선용·2022년 7월 24일
0

트러블 슈팅


게시판 수정하기

2022년 07월 08일 ~ 2022년 07월 11일

구현 목표 => (사진+동영상)이미지 파일 update - 순서 있음 / S3 스토리지 서버 부하 최소화

게시판 CRUD에서 기존의 게시판 작성의 로직과 게시판 수정에서 목표로 한 것

  • 기존의 게시판 작성은 모든 이미지를 MultipartFile로 전송한다
  • 게시판 수정은 새로운 이미지는 Multipartfile로, 이전의 이미지는 Url로 전송 할 계획
  • 작성과 수정 모두 List로 보내 순서를 알 수 있게 한다

MultipartFile과 url을 모두 받기 위해 List<Object>로 데이터들을 수신하는 DTO를 작성하였다

여기서 생기는 문제는 아직도 명확한 답을 찾지 못하였는데, 프론트에서 api를 호출할 때 url의 리스트 List<String> 또는 이미지 파일의 리스트 List<MultipartFile>로 보낼때는 List<Object>가 잘 작동을 하여 정상적인 로직이 실행된다.
(즉, url만 보낼때나 MultipartFile만 보낼 땐 작동이 잘 되었다)
하지만, 프론트에서 api를 호출할 때 url과 MultipartFile을 동시에 보내면 url의 값들을 받지 못하고, MultipartFile의 값만을 리스트로 받는 현상이 있었다
(@Modelattribute로 request를 가져올 때, MultipartFile와 String 타입이
List로 들어오면 casting 문제가 생기는 것으로 예상됨)

이를 해결하기 위하여 arrayList 사용, HashMap<String, Object>, DTO 클래스 정의, 수신부분 변경 등을 시도 해 봤지만 결과는 같았다

해결

해결법을 찾다가 대부분의 기업에서 사용한다는 방법을 배웠는데,
그것은 MultipartFile을 가능한 다루지 않는 (한번만 다루는) 것 이었다.
(정확히 말하자면, S3파일 업로드 기능을 글쓰기와 분리하는 것)

로직

  1. 이미지를 Url로 변환시켜주는 (S3에 저장하는) api를 따로 만듦
  2. FE에서 이미지를 업로드 하는 동시에 위의 api를 호출
  3. MultipartFile을 받아 String 타입의 url로 return
  4. 해당 url을 가지고 게시글 작성과 수정 api를 호출하는 로직 호출
  5. BE서버는 List<String>의 request를 처리

BE는 List<String>으로 이미지를 request 받을 수 있어 문제의 원인을 제거할 수 있었다

하지만, 이 해결법은 S3 스토리지 서버에 dummy data가 생길 수 있기 때문에,
주기적으로 dummy data를 삭제 해 주는 scheduler의 관리가 필요했다.

Scheduler의 O(N^2 문제)

2022년 07월 14일 ~ 2022년 07월 16일

위의 방법처럼 이미지를 S3에 저장하고 테이블에는 기록하지 않는다면, Scheduler를 사용할 때
사용된 모든 이미지 url과 (테이블에 존재하는) S3 스토리지 서버에 존재하는 모든 이미지 url을 비교해서 테이블에 존재하지 않지만 S3에는 존재하는 url을 삭제하는 로직이 필요하다

문제

  1. 이 방법은 두 테이블(DB, S3스토리지에서 가져온 테이블)의 데이터를 비교해야 하기때문에 기본적으로 O(N^2)의 시간복잡도를 갖는다 (물론 sort를 사용해서 O(Nlog N)으로 만들 수 도 있을 것 같다). 아무리 사용자가 적은 시간대에 실행하는 로직이라 하더라도 서버에 과한 부담이 될 것이다
  2. S3 스토리지 서버의 데이터 관리에 어려움이 있다. 만약 스케줄러가 작동되기 바로 전에 사용자가 글을 올리거나 수정하려고 이미지를 S3서버에 저장했다면, 사용자는 "게시글의 이미지를 찾을 수 없습니다."라는 예외 메세지를 받게 될 것이다. 이를 해결하기 위해 스케줄러에서 S3스토리지에 저장된 시간을 조회해 생성된 지 한 시간이 지났는데도 게시판에 등록되지 않는 이미지를 삭제하는 조건을 추가하려 했지만, S3 파일의 생성시간을 가져오는것이 쉽지 않았다

해결

물리적 삭제가 아닌 논리적 삭제(Soft Delete)를 이용

  1. S3 스토리지에 이미지를 저장할 때, DB에 goods_id값을 null로 저장한다
    (아직 글이 등록된 상태가 아니므로 당연히 null이기도 하다)
  2. 글이 등록될 때 요청이 온 이미지들의 컬럼을 goods_id를 해당 글의 id로 등록한다
  3. S3 스토리지에 저장은 됐지만, 사용되지 않는 (게시글 등록 시 요청이 오지 않은)
    dummy data들은 goods_id가 null이 유지된다
  4. Scheduler 작동 시 DB에서 데이터를 찾는다. goods_id가 null이고, created_at이 한 시간이 지난 데이터를 삭제하며 해당 데이터의 url로 S3 스토리지의 dummy data도 삭제한다

(sequence 컬럼이 있는 이유 : 사용자가 이미지 등록 이후, 게시글 등록 전에 이미지의 순서를 변경할 수 있는데, 이것을 위해 row의 순서를 바꾸긴 힘듦)

결과

DB를 통해 이미지를 관리하면 Scheduler는 O(N)의 시간복잡도를 갖고, created_at 과 같은 컬럼을 통해 삭제하는 조건을 다양하게 조절할 수 있다

추가적인 소득 - 이미지가 바로 DB에 저장되므로 index로 값을 찾을 수 있다 (이를 위해 FE에 map의 형식으로 id와 url을 보내주었다). 그래서 글을 등록할 때의 request를 indexList<Long>로 주는 방식으로 수정하였다. url로 db에서 값을 찾는 것 보다 index로 찾는 것이 서버에 부담이 적고, 빠르기 때문에 채택하였다

profile
백엔드 개발 공부중

0개의 댓글