해당 프로젝트에서 “지역”의 자료형이 상당히 난감했음.
바로 아래와 같은 자료형을 가지고 있다.
{
"경기도": {
"안산시" : {
"단원구" : {
"고잔동" : "고잔동"
"중앙동" : "중앙동"
},
"상록구" : {
"사동" : "사동"
}
}
}
}
원하는 값들이 key로 들어가 있는 경우는 처음이라 당황했음.
그리고 이 자료형에서
이런 기능을 구현해 구현해야했음.
<Grid container rowSpacing={1} columnSpacing={1}>
//위 자료형을 string[]로 파싱
{parseRegion(regions).map((region) => {
return (
//12를 기준으로 3을 먹겠다.
<Grid key={region} item xs={3}>
<Button
variant="outlined"
endIcon={<DeleteIcon />}
onClick={() => {
handleDeleteRegion(region);
}}
>
{region}
</Button>
</Grid>
);
})}
</Grid>
MUI의 Grid를 이용하여 한줄에 4개씩 표기하도록 하였음,.
dfs알고리즘을 사용해서 제작함.
여기서 만난 다양한 TypeError가 많이 성장시켜주었다.
//app/utils/parseRegion.ts
function parseRegion(region: Object | undefined, regionArray: string[] = [], level: number = 0, regionString = '') {
if (region === null || region === undefined) return [];
//마지막 띄어쓰기 지우기
if (level !== 0) regionArray.push(regionString.slice(0, -1));
//지역은 동레벨 까지만 존재하기 떄문에 도,시,구,동,[4]에 도착했을 때는 반환해준다.
if (level === 4) {
return [];
}
const keys = Object.keys(region);
for (let i = 0; i < keys.length; i += 1) {
const key = keys[i];
parseRegion(region[key as keyof typeof region], regionArray, level + 1, `${regionString}${key} `);
}
return regionArray;
}
export default parseRegion;
만나는 모든 key들을 regionArray에 넣어주고 결과적으론 regionArray를 반환한다.
여기서 regionArray는 참조로 재귀함수 안으로 전달된다.
parseRegion(region[key as keyof typeof region])
위와 같이 자료형을 선언해준 이유는 바로 typescript 때문이다.
//app/components/bannerAndRegionPage/regionBoard.tsx
const handleDeleteRegion = async (deleteRegion: string) => {
const regionArray = deleteRegion.split(' ').map((e) => {
return e as keyof typeof regions;
});
const newRegion = regions || {};
switch (regionArray.length) {
case 1:
delete newRegion[regionArray[0]];
break;
case 2:
delete newRegion[regionArray[0]][regionArray[1]];
break;
case 3:
delete newRegion[regionArray[0]][regionArray[1]][regionArray[2]];
break;
case 4:
delete newRegion[regionArray[0]][regionArray[1]][regionArray[2]][regionArray[3]];
break;
default:
return;
}
//updateRegion은 region queryKey를 초기화 시켜준다.
await updateRegion({ region: newRegion });
};
지울 때는
//app/components/bannerAndRegionPage/addRegionField.tsx
import { Box, Button, TextField } from '@mui/material';
import deepmerge from 'deepmerge';
import React, { FormEvent, useState } from 'react';
import useUpdateCommon from '../../hooks/useUpdateCommon';
function AddRegionField({ regions }: { regions: Object | undefined }) {
const [city, setCity] = useState('');
const [town, setTown] = useState('');
const [village, setVillage] = useState('');
const { updateRegion } = useUpdateCommon();
const addRegion = async (e: FormEvent) => {
e.preventDefault();
let addedRegion = {};
village.split(' ').forEach((newVillage) => {
addedRegion = deepmerge(addedRegion, {
경기도: {
[city]: {
[town]: {
[newVillage]: newVillage,
},
},
},
});
});
const newRegion = regions ? deepmerge(regions, addedRegion) : addedRegion;
await updateRegion({ region: newRegion });
setCity('');
setTown('');
setVillage('');
};
return (
<form onSubmit={(e) => addRegion(e)}>
<Box display="flex" justifyContent="space-between">
<div className="flex gap-3">
<TextField
// 시 입력
/>
<TextField
// 구 입력
/>
<TextField
// 동 입력
/>
</div>
<Button>
서비스 지역 추가하기
</Button>
</Box>
</form>
);
}
export default AddRegionField;
여기서 주목해야할 부분은 deepMerge인데
기존에 존재하는 지역에 겹치는 건 빼고 안 겹치는 건 넣어주기 위해선 단순히 object를 더하는 것으로 해결이 되지 않고 깊은 비교를 통해 합치기 위해 deepMerge를 사용했다.
deepMerge에 대한 더 자세한 내용은 링크를 통해 잘 정리해준 분이 있어 참고하면 좋을 것 같다.
💡 기능 명세서 상으로 동을 띄어쓰기로 연결했을 때 모든 동을 추가할 수 있어야 하기 때문에 `village.split(' ').forEach`를 사용했다.