2023. 10. 1. 18:15ㆍProject Tours/Tour on Plantopia
1. 고민의 시작
Plantopia 프로젝트는 불가피하게 backend가 없이 진행 되었다. 우리에겐 2가지 선택권이 있었는데 node.js에서 express로 하느냐 아니면 파이어 베이스를 하느냐였다. 나를 비롯해 다른 팀원 한 명이 1차 프로젝트에서 백엔드를 하였지만 사실 쉽지 않은 결정이었다. 프론트 코스에서 와서 프론트 프로젝트에서 프론트 코드를 하나도 안 친다는 것은 조금 어려웠고 만약 시간이라도 넉넉했다면 둘 다 치는 것이 가능했지만 우리에게 주어진 시간은 얼마 남지 않았다. 일련의 소동으로 이미 3일을 소모한 상태에서 백엔드 코드가 스키마 이 외에는 아무 것도 작성되지 않았기 때문이다.
그렇게 부랴부랴 파이어 베이스를 학습해 프로젝트에 도입하였고 작성하고 나니 데이터를 처리하는 코드를 분리하는 것 좋겠다는 생각이 들었다. 거기에는 몇 가지 이유가 있는데,
1. 중복된 코드로 각 페이지마다 데이터를 불러오는데 같은 로직이 있었다. 메인 화면에도 사용자의 식물 데이터를 불러왔고 내가 맡은 myPlant 페이지에서도 사용자의 식물 데이터를 불러왔다.
2. 데이터 확인의 용이함 더 해줄 수 있다. 이전 코드에서는 상태랑 데이터 처리랑 묶여 있다보니 데이터 자체 로직이 제대로 원하는 결과값을 가져오는지 명쾌하지 않았다. 물론 그렇게 프로젝트가 크지 않다보니 코드 역시 많이 복잡하지 않아서 파악하는 것이 어렵지는 않았으나 분리한다면 모든 사람이 이해하기 쉬운 코드가 될 것임은 분명했다. 이런 이해를 위한 불필요한 노력값들도 줄여주는 것이 협업의 초석이라고 생각했다.
2. 고민 해결의 과정
처음 코드는 아래와 같았다.
고민 - 1 . 데이터 입출력 코드와 비즈니스 코드가 함께 구성되어있다.
const deletePlant = async () => {
if (plantDetail) {
if (!docId) return;
const docRef = doc(db, 'plant', docId);
const documentSnapshot = await getDoc(docRef);
const dataBeforeDeletion = documentSnapshot.data();
const q = query(
collection(db, 'plant'),
where('userEmail', '==', user?.email),
);
const querySnapshot = await getDocs(q);
if (querySnapshot.size == 1) {
await deleteDoc(docRef);
navigate('/myplant');
successNoti('내 식물이 삭제 되었습니다.');
return;
}
if (dataBeforeDeletion?.isMain) {
await deleteDoc(docRef);
const firstDocumentid = querySnapshot.docs[0].id;
const documentRef = doc(db, 'plant', firstDocumentid);
const updatedFields = {
isMain: true,
};
await updateDoc(documentRef, updatedFields);
navigate('/myplant');
successNoti('내 식물을 삭제 하였습니다.');
return;
} else {
try {
await deleteDoc(docRef);
navigate('/myplant');
successNoti('내 식물이 삭제 되었습니다.');
} catch (error) {
return;
}
}
}
};
> 이 코드는 식물 데이터를 삭제하기 위한 함수이다. 하지만 삭제에는 확인해야할 사항이 있다. 이 식물이 main 식물인지 확인을 해야한다. main 식물이 아니라면 그냥 삭제해도 되지만 main 식물이라면 삭제 후 다른 식물에게 main을 부여해야한다. 애초에 메인 식물은 항상 존재해야한다고 기획했기 때문이다. 그리고 main 식물 이더라도 만약 식물의 개수가 1개라면 그냥 삭제하도록 처리했다. 이처럼 많은 로직이 한 함수에 모조리 담겨 있는 것은 유지 보수에 어려움을 주기에 분리해주기로 하였다.
> 또한 리액트 컴포넌트에는 되도록 뷰에 관련한 코드만 설정해주어 각 파일의 목적성 그리고 관심사에 맞게 코드를 작성하고자 했다. 지금 위 코드는 식물의 세부정보에 컴포넌트에 있는 코드이다. 이를 위해서 나는 조금 더 명확히 분리를 위한 개념인 3 레이어드 아키텍쳐에 대해 간략하게 알아야했다.
> 위 그림이 4 tier이지만 가장 잘 설명해주는 그림이라고 생각했다. presentation은 뷰 담당 비즈니스는 데이터 수정 그리고 마지막 데이터 단을 하나로 묶어 데이터 입출력을 담당하는 것으로 이해했다. 프로젝트 코드에서도 데이터를 수정하는 코드가 있어 비즈니스 로직을 껴넣을 수 있었지만 따로 많은 코드가 있는 것이 아니라 파일 파기보다는 뷰 담당과 데이터 담당만 나누어 었다.
3. 적용
const deletePlant = async () => {
if (docId && user?.email) {
deletePlantDataByDocId(docId, user.email);
}
navigate('/myplant');
};
> 먼저 컴포넌트에 있던 이전 코드는 바로 위 코드로 변경되었다. api 폴더를 구성하여 파일을 userPlant.ts 파일에 데이터 처리 코드를 모아두었다. 그 중 delete 코드를 아래와 같이 변경하였다.
export const findPlantDataByDocId = async (docId: string) => {
try {
if (!docId) {
errorNoti('식물 id가 잘못되었습니다.');
return;
}
const docRef = doc(db, 'plant', docId);
const plantData = (await getDoc(docRef)).data();
return plantData;
} catch (error) {
errorNoti('식물 정보를 가져오는데 실패하였습니다.');
return;
}
};
> 먼저 아이디로 식물 데이터를 찾는 경우가 많아 따로 함수로 빼주었고
export const deletePlantDataByDocId = async (docId: string) => {
if (!docId) return;
const docRef = doc(db, 'plant', docId);
const plantData = await findPlantDataByDocId(docId);
const q = query(
collection(db, 'plant'),
where('userEmail', '==', plantData?.userEmail),
);
const userPlants = await getDocs(q);
if (userPlants.size === 1) {
await deleteDoc(docRef);
successNoti('식물을 삭제하였습니다.');
return;
}
if (plantData?.isMain == true) {
try {
await deleteDoc(docRef);
const firstPlantDataId = userPlants.docs[0].id;
const documentRef = doc(db, 'plant', firstPlantDataId);
const updatedFields = {
isMain: true,
};
await updateDoc(documentRef, updatedFields);
successNoti('식물을 삭제하였습니다.');
return;
} catch {
errorNoti('식물 삭제에 실패 하였습니다.');
return;
}
} else if (plantData?.isMain == false) {
try {
await deleteDoc(docRef);
successNoti('내 식물이 삭제 되었습니다.');
return;
} catch (error) {
errorNoti('식물 삭제에 실패 하였습니다.');
return;
}
}
};
> 만약 수정이 필요하다면 여러 컴포넌트를 돌아다닐 필요 없이 이 함수만 처리하면 된다. layer 개념은 백에서 주로 언급되는 개념이지만 파이어 베이스 덕분에 백 지식을 늘렸다.
'Project Tours > Tour on Plantopia' 카테고리의 다른 글
[Revisit Project] 압축을 통해 이미지 최적화 해보기 (0) | 2023.10.03 |
---|---|
[Revisit Project] Vite과 코드 분할로 성능 최적화 (0) | 2023.10.02 |
[Revisit Project] React에서 form을 다루는 Best Case는 무엇일까? (0) | 2023.09.29 |