728x90
이전 글들 중 유지보수가 편리한 코드를 만드는 강좌의 정리글을 올린적이 있다.
내가 구현한 코드를 보던 중 너무 의존성이 강하게 묶여있는 코드를 발견해 이를 리팩토링 하는 기록을 남기고자 한다.
코드
const ShowPosition = ({ positions, tyleInfoSetFun, operatorImgData }: positionsInterface) => {
const ePositions = positions.map(e => e.ePosition);
// 객체형으로 선택됬는지 여부를 보관하기
const [selectedState, setSelectedState] = useState<selectedStateInterface>({
position: {
duelist: false,
recon: false,
sentinels: false,
controllers: false,
noDicision: false,
},
operator: []
});
// API에 정보 전달을 위해 모아두는 상태
const [selected, setSelected] = useState<selectedInterface>({
position: [],
operator: [],
});
// 선택한 이미지들
const [positionImg, setPositionImg] = useState<operaotrsImgInterface>({
duelist: [],
recon: [],
sentinels: [],
controllers: [],
});
/**
* 포지션 선택시 선택한 포지션의 상태를 갱신해주는 함수
* (리팩토링 필수)
* @param ePosition
*/
const selectPosition = (ePosition: string) => {
const existIdx = selected.position.findIndex(e => e === ePosition);
// 선택한 포지션을 다시 눌렀을 때 제거해주는 로직
if (existIdx !== -1) {
const remainPos = selected.position.filter(e => e !== ePosition)[0];
const newImgState = positionImg[ePosition].map((imgInfo: ImgInterface) => {
if (imgInfo.state === true) {
imgInfo.state = false;
return imgInfo;
}
else {
return imgInfo;
}
});
setSelected({
position: remainPos ? [remainPos] : [],
operator: [...selected.operator.filter(e => e[0] !== ePosition)],
});
setSelectedState({
position: { ...selectedState.position, [ePosition]: false },
operator: remainPos ? positionImg[remainPos] : []
});
setPositionImg({
...positionImg,
[ePosition]: newImgState,
})
}
// 포지션 추가 로직
else if (selected.position.length < 2 && existIdx === -1) {
setSelected({
...selected,
position: [...selected.position, ePosition]
});
setSelectedState({
position: { ...selectedState.position, [ePosition]: true },
operator: [...positionImg[ePosition]]
});
}
else {
alert('포지션은 2개 까지 선택 가능합니다.(수정 예정)');
}
};
/**
* 선택한 포지션의 오퍼레이터를 선택하는 함수이며 2개까지 선택 가능하다.
* @param ePosition
* @param opName
* @param idx
*/
const selectOperator = (ePosition: string, opName: string, id: number, idx: number): void => {
const existCheck = selected.operator.filter(e => e[1] === opName);
const clickedOpPos = [...positionImg[ePosition]];
// 선택한 포지션을 다시 눌렀을 때 제거해주는 로직
if (existCheck.length !== 0) {
clickedOpPos[idx].state = false;
setSelected({
...selected,
operator: [...selected.operator].filter(e => e[1] !== opName)
});
setPositionImg({
...positionImg,
[ePosition]: clickedOpPos
});
}
else if (selected.operator.length < 2 && existCheck.length === 0) {
clickedOpPos[idx].state = true;
setSelected({
...selected,
operator: [...selected.operator, [ePosition, opName, `${id}`]]
});
setPositionImg({
...positionImg,
[ePosition]: clickedOpPos
});
}
else {
alert('주 요원은 2개 까지 선택 가능합니다.(수정 예정)');
}
}
...
이 코드들 중 selectPosition함수와 selectOperator함수가 컴포넌트의 상태와 세터함수에 의존성을 강하게 띄우고 있는 모습을 볼 수 있다.
이러한 코드는 수정사항이 생기면 로직을 수정하기 힘든 코드라고 생각한다.
심지어 컴포넌트의 상태를 직접 가져와 사용하기에 더욱더 힘들어 질것이라 본다....
내가 왜 이런 코드를....
내가 작업할 것은 이렇게 state와 컴포넌트 내부에 있는 함수들을 적절히 분리한뒤 길을 정리해 줄 것이다.
포지션 선택 함수
이 함수는 사용자가 포지션을 선택 하면 기존 상태에서 새롭게 상태레 변화를 주는 함수이다.
이 함수와 다음 함수에서 set함수를 어떻게 처리할까 고민했다.
지금은 새롭게 적용할 상태를 받아 컴포넌트 내부에서 set함수를 사용해 적용해주는 함수를 하나더 선언해 줄것이다.
글로 정리하던 중 customHook으로 함수를 추출해 로직을 정리해 컴포넌트 내부에 customHook으로 만든 상태만 존재하게끔 해주는 방법을 알게 되어 조만간 적용할 예정이다.
import { ImgInterface, operaotrsImgInterface, selectedInterface, selectedStateInterface } from 'types/newTyle';
/**
* 포지션 선택시 선택한 포지션의 상태를 갱신해주는 함수
* @param ePosition
* @param selected
* @param positionImg
* @param selectedState
*/
export const selectPosition = (
ePosition: string,
selected: selectedInterface,
positionImg: operaotrsImgInterface,
selectedState: selectedStateInterface
) => {
const existIdx = selected.position.findIndex(e => e === ePosition);
// 선택한 포지션을 다시 눌렀을 때 제거해주는 로직
if (existIdx !== -1) {
const remainPos = selected.position.filter(e => e !== ePosition)[0];
const newSelected = setSelected(selected, ePosition, remainPos);
const newSelectesState = setSelectedState(ePosition, selectedState, positionImg, remainPos);
return {
newSelected: newSelected,
newSelectesState: newSelectesState,
};
}
// 포지션 추가 로직
else if (selected.position.length < 2 && existIdx === -1) {
const newSelected = setSelected(selected, ePosition, 'add');
const newSelectesState = setSelectedState(ePosition, selectedState, positionImg, 'add');
return {
newSelected: newSelected,
newSelectesState: newSelectesState,
};
}
else {
alert('포지션은 2개 까지 선택 가능합니다.(수정 예정)');
}
};
const setSelected = (selected: selectedInterface, ePosition: string, remainPos?: string) => {
return remainPos === 'add' ?
{
...selected,
position: [...selected.position, ePosition]
}
:
{
position: remainPos ? [remainPos] : [],
operator: [...selected.operator.filter(e => e[0] !== ePosition)]
}
}
const setSelectedState = (ePosition: string, selectedState: selectedStateInterface, positionImg: operaotrsImgInterface, remainPos?: string) => {
return {
position: { ...selectedState.position, [ePosition]: remainPos === 'add' ? true : false},
operator: remainPos === 'add' ? [...positionImg[ePosition]] : (remainPos !== undefined ? positionImg[remainPos] : [])
}
};
/**
* 포지션 선택시 선택한 포지션의 상태를 갱신해주는 함수
* @param ePosition
* @param selected
* @param positionImg
* @param selectedState
*/
const onclickPosition = (
ePosition: string,
selected: selectedInterface,
positionImg: operaotrsImgInterface,
selectedState: selectedStateInterface
) => {
const result = selectPosition(ePosition, selected, positionImg, selectedState);
if (result) {
setSelected(result.newSelected)
setSelectedState(result.newSelectesState)
}
}
캐릭터 선택 함수
이 함수는 포지션을 선택한 뒤, 그 포지션에 속한 캐릭터들의 선택을 도와주는 함수이다.
import { operaotrsImgInterface, selectedInterface } from 'types/newTyle';
/**
* 선택한 포지션의 오퍼레이터를 선택하는 함수이며 2개까지 선택 가능하다.
* @param selected
* @param positionImg
* @param ePosition
* @param opName
* @param idx
*/
export const selectOperator = (
selected: selectedInterface,
positionImg: operaotrsImgInterface,
ePosition: string,
opName: string,
id: number,
idx: number
) => {
const existCheck = selected.operator.filter(e => e[1] === opName);
const clickedOpPos = [...positionImg[ePosition]];
// 선택한 포지션을 다시 눌렀을 때 제거해주는 로직
if (existCheck.length !== 0) {
clickedOpPos[idx].state = false;
return {
newSelected: {
...selected,
operator: [...selected.operator].filter(e => e[1] !== opName)
},
newPositionImg: {
...positionImg,
[ePosition]: clickedOpPos
}
};
}
else if (selected.operator.length < 2 && existCheck.length === 0) {
clickedOpPos[idx].state = true;
return {
newSelected: {
...selected,
operator: [...selected.operator, [ePosition, opName, `${id}`]]
},
newPositionImg: {
...positionImg,
[ePosition]: clickedOpPos
}
};
}
else {
alert('주 요원은 2개 까지 선택 가능합니다.(수정 예정)');
}
};
/**
* 선택한 포지션의 오퍼레이터를 선택하는 함수이며 2개까지 선택 가능하다.
* @param selected
* @param positionImg
* @param ePosition
* @param opName
* @param idx
*/
const onClickOperator = (
selected: selectedInterface,
positionImg: operaotrsImgInterface,
position: string,
name: string,
id: number,
idx: number
) => {
const result = selectOperator(selected, positionImg, position, name, id, idx)
if (result) {
setSelected(result.newSelected);
setPositionImg(result.newPositionImg);
}
}
내 생각
이 작업을 하고난 뒤 생각한 것은 상태 관리가 굉장히 엉켜있다는 느낌을 느꼇다.
또한 아까도 이야기 했듯이 customHook으로 만들어 컴포넌트 내부의 코드들을 좀더 깔끔히 정리해 봐야겠다는 생각이다.
개선할 사안이 있다면 계속해서 유지보수할 예정이다.
728x90
'프로젝트 > TYLEGG' 카테고리의 다른 글
불필요한 데이터 요청의 최적화(React-Query or TanstackQuery) (0) | 2024.04.16 |
---|