리팩토링 후기 (부제: 사진미리 보기 기능 구현 성공)

Jin·2022년 10월 17일
0

refactoring

목록 보기
1/1
post-thumbnail

1. 소개

SSAFY에서 한 마지막 프로젝트로서 부동산 중개 플랫폼을 만든적이 있다.
사용자가 매물을 올릴때 업로드할 사진을 미리볼수 있게 사진 미리보기 기능을 구현하였었는데, 문제는 수정할 때였다.
처음 매물을 작성할때 프론트단에서 FormData 타입으로서 서버에 요청을 보낸다. 요청이 정상적으로 갔을때 서버에서는 이미지를 AWS S3에 업로드 하였다.
매물 정보를 받을때 서버는 매물 이미지의 S3 주소만을 다른 정보(매물 주소, 가격 등) 과 함께 전송해주었기에, 수정할 경우 이 S3 주소를 File형식으로 바꾸어 줘야 했다.

2. 접근법

1. URL을 파일 형식으로 변경하기

이 부분은 크게 어렵지 않았다. 해당 정보를 찾아본 결과 다른 블로그에서 해당 정보에 대한 글을 찾을 수 있었고 이를 활용하여 쉽게 해결하였다. 궁금하신분들은 해당 블로그에서 보시길

2. 추가 할 사진이 있으면 기존 사진들과 합치기

서버에서 받아온 기존 사진들을 FileList로 변경을 하였으니, 새롭게 추가할 사진이 있다면 이를 FileList(기존 사진들)에 추가하면 되는 간단한 일이라고 생각했다. 하지만 생각보다 간단하지 않았다....

접근법은 이러했다.
1. new DataTrasfer() 만들기
2. 기존에 사진들과 새롭게 추가할 사진들을 하나의 배열로 합치기
3. 합친 배열을 forEach를 통해 각각의 file들을 1번에서 생성한 datatrasnfer.items에 add하기
4. add가 완료 된 dataTrasfer을 setState를 통해 저장시키기
5. 새롭게 추가된 이미지 파일들을 URL.createObjectURL을 통해 변환
6. 이를 setPreviewImage에 추가

이 때만 하더라도 서버에 전송할 state와 이미지 미리보기 state를 따로 구분해서 구현하였기 때문에 쓸모 없는 작업이 많았고 이에 따라 코드도 복잡할 정도로 많았다.

리팩토링 후

이미 설정된 이미지가 있을 때 이미지 추가를 처리하는 함수를 분기함으로서, 이미 설정된 이미지가 있을때와 없을때를 구분하는 조건 하나만 필요해졌다.

# createObjectUrl을 사용한 이유

  1. 속도 빠르다.
    : readAsDataURL은 blob을 읽고 데이터를 URL로 변환하는데 작업 시간이 필요하며 비동기식으로 동작하지만, createObjectURL은 동기식으로 즉시 동작하여 URL을 생성하기에 속도가 더 빠르다.
  2. 사용 가능 최대 용량
    : readAsDataURL 은 10mb createObjectURL는 최대 800mb까지 가능하다
  3. 메모리
    : readAsDataURL 은 많은 문자열을 포함한 base64를 리턴한다. 이는 blob url보다 많은 메모리를 사용하지만, url을 더 이상 사용하지 않을때 가비지 컬렉터에 의해 제거된다.
    반면 createObjectURL 은 hash로 리턴을 하여 메모리는 상대적으로 적지만, 문서를 unload하거나 revokeObjectURL을 실행하기 전까지 계속 메모리에 존재한다.
    가비지 컬렉터에 의해 자동으로 정리되는걸 보면 readAsDataURL을 사용하는게 좋아보이지만, 둘의 메모리 크기가 너무 차이가 난다.

3. 제거 할 사진이 있으면 제거하기

  1. 해당 사진의 idx를 받아 splice(idx, 1) 처리
  2. setPreviewImage를 통해 새롭게 state 저장
  3. new DataTransfer 생성
  4. 파일 image들 배열로 변경 후 slice(idx,1) 처리
  5. 4번에서 생성한 배열을 forEach로 돌며 3번에서 생선한 dataTransfer에 add 해주기
  6. dataTransfer을 setState처리

최대한 간단하게 정리한다고 정리해봤는데 너무나 복잡하고 불필요한 과정들이 많았다... 하지만 저때는 저게 나의 최선이었다.

리팩토링 후

DataTransfer을 사용하니 코드가 불필요하게 길어져 File[]를 사용하였다. 훨씬 가독성있고 선언적으로 코드가 구현된것을 볼 수 있다.

3. 문제점

dataTransfer.files를 setState로 처리하는데 계속 빈 리스트를 반환하는것이었다. 왜 그럴까 하며 MDN에서 Datatransfer.fils에 대해 읽으며 아래와 같은걸 발견할수 있었다.

그렇다 drop 이벤트로만 접근할수 있는 것이었고, 그 외에는 empty가 되는것이었다. 사실 여기서 어떻게 해야 할지 전혀 감이 오지 않았다. 그래서 서버 담당자와 이야기를 해보았지만 서버쪽에서는 할 수 있는게 없었고, 결국 내가 해결을 했어야 했다.

4. 해결

DataTransfer가 아닌 File[] 사용하기.
DataTransfer.files는 drop 이벤트 일때만 배열을 반환하였지만, File[]은 그렇지 않았다. 왜 이걸 진작 생각하지 못했을까...

아무튼 서버에서 API를 통해 기존의 데이터를 받아온뒤 사진 url들을 File형식으로 바꾼후 forEach로 File[]에 push를 해주었다. 이후, forEach가 끝이 나면 File[]를 setState에 해줌으로써 url로 들어오던 사진들을 FileList로 변환하여 state에 저장하는것을 구현할 수 있었다.

5. 결과

before

After

리팩토링을 하기전의 코드들은 내가 리팩토링을 하려고 다시봤을때 이게 뭘 위한 코드인지, 무슨 작업을 하는 코드인지 이해하는데 많은 시간이 필요했다(다시보니 부끄러울 정도이다). 사진 미리보기 기능 구현과 함께 함수들을 분기함으로서, 아직 부족하지만 불필요한 코드들을 줄였고 시간이 지났을때 다시 보아도 빠르게 이해할 수 있는 코드가 되었다고 생각한다.

출처 : https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/files
https://stackoverflow.com/questions/31742072/filereader-vs-window-url-createobjecturl
https://velog.io/@kykim/readAsDataURL-vs-createObjectURL

profile
내가 다시 볼려고 작성하는 블로그. 아직 열심히 공부중입니다 잘못된 내용이 있으면 댓글로 지적 부탁드립니다.

0개의 댓글