데이터 업로드 컴포넌트 재사용성 개선

FAST FOX·2023년 6월 6일
0

DressCode개발일지

목록 보기
6/6
post-thumbnail

개요

이 DressCode 작업은 개발자 공부를 시작하면서 동시에 시작한 작업이기 때문에 코드 곧곧에 문제점도 많고 개선해야할 부분도 많이 있다. 그 중 하나의 큰 문제가 컴포넌트의 재사용성이 떨어진다는 문제가 있었다.

이번의 문제도 데이터를 서버에 저장을 하는 것과 기존의 데이터를 업데이트하는 과정이 같은 형태의 모달에서 사용이 되는데 이전에는 재사용성의 중요성도 몰랐을 뿐더러 경우에 따라 유연하게 데이터와 함수를 사용하는 방법을 몰랐었다.

하지만 업데이트를 다시 수정하다 보니 같은 형식의 컴포넌트를 똑같이 복사해서 생성과 업데이트로 사용하는게 눈에 띄었고 이제는 수정의 필요성과 방법을 알기 떄문에 한번 고쳐보기로 했다.

문제 코드

문제 1. 형태는 같지만 동작이 달라 따로 만든 컴포넌트

AddDressPopup><UpdateModal>은 형태가 같지만 데이터 생성과 업데이트라는 차이 때문에 컴포넌트를 따로 만들어서 사용했었다.

문제 2. 너무 너무 길고 비효율적인 함수 코드

2-1 데이터 생성을 위한 함수 코드


이 자체는 엄청 길지는 않지만 (아닌가...??) 업데이트 함수와 좀 합칠 수 있는 부분도 있었다.

2-2 데이터 업데이트를 위한 함수 코드

이것도 너무 길어서 다 못가져온거지만...원하는 작동 방식에 비해서 미친듯이 길고 복잡한 코드다...

해결 과정

우선은 컴포넌트의 재활용성을 높이고 컴포넌트간의 영향력을 줄이는걸 목표로 했다.

1. 생성과 업데이트로 나눴던 컴포넌트를 통합하기

우선은 데이터 생성과 업데이트에 사용되었던 팝업창이 형태는 완전히 똑같았지만 생성과 업데이트라는 차이 떄문에 형태를 그대로 붙여넣고 함수만 바꾼 컴포넌트 2개를 사용하였다.
이를 해결하기 위해서 다음과 같이 하나의 컴포넌트를 사용하였다.

데이터를 추가하기 위한 팝업 컴포넌트

데이터를 업데이트하기 위한 팝업 컴포넌트

둘 다 같은 <AddDressPopup>이라는 컴포넌트를 사용했지만 엄연히 다르게 작동하는 컴포넌트이다. 때문에 굳이 다른 컴포넌트 파일을 생성할 필요가 없다.
데이터 생성이냐 업데이트냐에 따라 다른 함수와 데이터만 넘겨주는 방식이다.

이렇게 되면 다음 이미지와 같이 <AddDressPopup>에서 handleClothesData라는 props는 하나지만 실제로 넘어오는 함수는 달라지게 된다.

2. 통합된 컴포넌트에서 상황에 따른 데이터 설정하기

만약 데이터 생성 과정이라면 해당 컴포넌트 내부의 데이터가 비어있겠지만, 업데이트라면 기존의 데이터들이 있기 때문에 Optional Chaning과 단축평가를 활용하여 데이터 값을 설정해주었다.
( 이 데이터도 사실 각각의 데이터마다 state를 막무가내로 사용했던걸 최소한의 state 사용으로 줄였다... )

결과적으로 데이터 생성이라면 비어있는 데이터들이 될테고 그게 아니라면 기존의 데이터가 값으로 주어지게 된다.

< 데이터 생성 >

< 데이터 업데이트 >

3. Save 버튼을 눌렀을 때 상황별 데이터 처리 방식

같은 Save 버튼이지만 생성이냐 업데이트냐에 따라 서버에 어떤 요청을 보내는지가 달라지기 때문에 이를 해결해야했다.

우선 Save 버튼을 클릭했을 때 컴포넌트 내에서 작동하는 함수이다.

데이터의 첫 번재 이미지와 Season, Part에 대한 정보는 필수 정보이기 떄문에 이를 미기입시 경고문이 뜨도록 했고 그게 아니라면 데이터 처리 함수가 가동되도록 했다.
여기서 데이터 생성이냐 업데이트냐에 따라서 프로퍼티로 넘겨주는 값이 다른데, optional chaing을 사용해서 만약 selectedClothes가 있다면 selectedClothes.id를 넘겨주도록 했다.

그 다음 handleClothesData()에 대한 함수를 구현하는 것인데 내가 제일 우선시 했던 점은 컴포넌트간의 의존도를 낮추고 코드의 가독성을 높이는 것이었기 때문에 컴포넌트 내에 있던 데이터 처리 함수를 다른 wrapper function 파일로 이동시켰다.

handleClothesData()로 전달되는 wrapper function 파일에서 구현한 코드 구조는 다음과 같다.

  1. 생성과 업데이트를 위한 데이터 가공

  2. 가공된 데이터를 상황에 따라서 생성과 업데이트 함수에 전달

1. 데이터 가공

대부분의 값들은 넘겨받은 데이터를 그대로 넣었지만 이미지 데이터의 경우는 조금 달랐다.

데이터 생성의 경우 이미지를 무조건 firebase의 storage에 업로드하겠지만 업데이트의 경우 새로운 이미지를 업로드하지 않아도 되기 때문에 기존 이미지의 storage 주소를 사용하였다.
다만 imageUrl_1의 경우 무조건적으로 이미지가 있어야하기 때문에 null을 가지는 경우는 빼주었다.

2. 가공된 데이터 처리

데이터 처리 과정은 생성과 업데이트로 나뉜다.
이 둘의 코드를 보기전 데이터 저장 구조는 User라는 컬랙션 내에 사용자의 uid로 만들어진 문서가 존재하고 그 내부에 다시 clothes라는 옷들에 대한 정보를 모아놓은 컬랙션으로 구성되어있다.

< 데이터 생성 >

우선은 userDocRef로 해당 유저의 문서에 접근, clothesCollectionRef로 clothes 컬랙션에 경로를 설정하고 setDoc메소드를 이용해서 해당 경로에 위에서 handelClothes로 생성한 데이터를 저장하는 함수이다.

< 데이터 업데이트 >

내 기준에서는 확실히 생성보다 조금 더 과정이 복잡했던 업데이트 함수이다...
우선 데이터를 가공하고 clothes라는 컬랙션에 경로를 설정하는 것은 같다. 차이점은getDocs로 컬랙션의 모든 데이터를 가져오고, 내가 프로퍼티로 받아온 업데이트하고자 하는 데이터의 id로 필요한 데이터 문서의 이름을 찾고, clothesDocRef 경로를 설정해주었다.
그 다음 updateDoc()메소드를 사용해서 데이터를 수정해주었다.

마주한 오류들

프로퍼티로 넘어오는 배열은 읽기 전용이기 때문에 수정할 수 없다는 오류

수정하고자 하는 배열을 slice()메소드 또는 [...arr]를 사용해서 복사해준 다음에 사용한다.

콜백함수의 인자로 넘겨주는 값이 undefined일 수 없다는 오류


handleClothesData()함수에서 인자로 넘겨줬던 selectedClothes.id가 데이터 생성 과정의 경우 undefined가 되면서 나왔던 오류이다. 컴포넌트에 만약 selectedClothes가 없다면 자동적으로 undefined가 넘어가겠지 했는데 안전성을 위해서 그러지 못하도록 막아놓은 것 같다

이를 해결하는 방법에 AND 연산자(&&)를 사용하는 방법도 있었지만 코드가 아주 길어진다는 단점이 있었다. 그래서 나온게 바로 Optional Chaining(?.)이다. 이 Optional Chaining은 평가 대상이 undfinednull이면 평가를 종료하고 undfined를 반환해준다.

참조 자료 : Optional Chaining

마치며

기존의 코드로 올려진 사진은 몇장 없어서 큰 문제가 아닌 것 처럼 보여지지만 사실은 컴포넌트들이 의존도도 높아서 하나를 수정하면 모든걸 수정해야 했고 각각의 컴포넌트 내에서도 경우에 따른 작동 방식을 조건부로 하드 코딩을 하다보니 코드가 길어지고 복잡했다.

당장에 기존 updateModal에서 사용한 업데이트 함수만 봐도 66번째 줄에서 160번째 줄까지가 하나의 함수를 해결하는 코드였다...뭐 경우에 따라서 이게 긴 코드가 아닐 수도 있지만, 내가 원하는 동작을 수행하기 위해서 작성한 코드로는 너무 너무 길고 비효율적인 코드였던 것이다.

결국 같은 형태를 가지지만 용도가 달라 다른 컴포넌트 파일을 사용해서 만드는건 비효율적이라는 생각으로 시작한게 코드의 수도 줄이고, 가독성도 높이고, 동작 원리도 좀 더 효율적으로 바꿀 수 있게되었다.

이외에도 수많은 컴포넌트들이 문제가 있지만...일단은 이 컴포넌트로 시작해보았고 앞으로도 천천히 코드를 수정해나갈 것이다.

profile
준비하는 개발자

0개의 댓글