TypeScript를 이해하기 위해서 이전에 만들었던 IMS를 TypeScript 적용 버전으로 다시 만들고 있었습니다.
기본적인 기능은 구현이 완료되었으나, Order 페이지를 작성하려고하니 순간 이런 생각이 들었습니다.
‘코드가 너무 더럽다…!’
그래서 왜 이런 생각이 들었는지 코드를 보면서 한번 분석해보고 이를 해결하기 위해 어떤 부분을 주의해야할지 회고를 하는 포스팅입니다.
(여기서 분석한 문제들을 해결해서 꼭 다시 만들겁니다.)
문제점. 코드가 복잡하다!
저는 기본적으로 ‘개발자의 아이덴티티 = 코드를 잘 쓰는 것’이라고 생각합니다.
이 말이 무슨 말이냐면 다른 사람이 ‘너 이거 만들어 봐’하면 누구든 개발은 할 수 있다고 생각합니다.
하지만, 만약 다른 사람이 ‘너 이거 잘 만들어 봐’라고하면 상황은 바뀝니다.
코드를 잘 작성하는 것은 다른 사람들도 코드를 통해 구조 & 기능을 한눈에 알아볼 수 있는 것이라고 생각합니다.
즉, 유지 보수에 용이한 코드가 잘 작성된 코드라고 생각합니다. 따라서, 유지보수를 위해서는 코드가 복잡해서는 안됩니다.
만약 동일한 기능을 한다면 이를 함수나 훅으로 만들어서 재사용성을 줄이는 것이 코드의 복잡도를 낮추는 방법일 것입니다.
그렇다면 부끄럽지만 제가 작성한 코드를 한번 살펴보겠습니다.
위 사진에서 미니맵을 살펴보면 아시겠지만 App.tsx의 길이가 상당합니다.
App.tsx는 설계상 두 가지의 상태 변수(IMS에 등록된 아이템을 관리하는 itemList, IMS에 등록하고자 하는 아이템을 관리하는 orderItemList)를 가지고 있어야하며 이는 전역에서 사용되는 변수입니다.
그렇다면 왜 코드가 이렇게 길어졌을까요?
시작. 변수의 상태를 관리하기 위해서 useReducer를 사용하고 있다.
-> useReducer에 넘겨주기 위한 콜백함수 dispatch를 정의해야한다.
-> itemList에 들어갈 dispatch와 orderItemList에 들어갈 dispatch를 모두 App.tsx에서 정의하고 있다.
-> ...
시작. 전역에서 사용될 변수니까 context를 통해서 넘겨줘야지! (이걸로 props drilling을 방지하자!)
-> createContext를 App.tsx에서 정의하고 있다.
-> TypeScript 에러를 피하기 위해 Context에 등록된 변수의 타입을 검사하는 훅을 App.tsx에서 만들고 있다.
-> ...
해결방안. 그렇다면 좋은 코드는 어떻게 작성해야할까?
이미 우리는 정답을 알고 있습니다.
재사용되는 부분을 함수나 훅으로 만들어서 필요한 경우 적재적소에 꺼내와서 사용하면 됩니다.
한 가지 주의해야할 부분은 함수와 훅의 차이를 알아야합니다.
차이는 아주 간단합니다. 바로 상태를 관리하면 훅, 아니면 함수로 정의하면 됩니다.
아래는 제가 처음 커스텀 훅을 만들면서 겪었던 문제였습니다.
const callBackSearchButton = useCallback(() => {
...
return useSearchButton(list, search);
}, [list, search]);
위 callBackSearchButton 함수는 리팩토링된 코드라서 당시 문제 상황은 아닙니다. (당시 코드를 기록해두지 않아서 글로 설명을 해보겠습니다..)
사진에서 작성된 코드는 사용자가 검색버튼을 눌렀을 때, props로 검색 대상 list와 사용자가 입력한 search 변수를 전달받아서 list를 필터링하는 ‘훅’으로 만들었습니다.
그리고 사진 아래에서 정의된 코드는 HTML의 form 태그에서 submit에 들어갈 함수였고, 로직대로라면 함수 내부에서 필터링된 결과값을 전달받아서 리턴해야하는 상황이었습니다.
하지만, 문제는 훅은 함수 내부에서 사용할 수 없다는 것입니다.
그 이유는 함수 내부 혹은 조건문 내부에서 훅이 선언된다면 훅의 호출 순서가 꼬일 수 있기 때문입니다.
즉, 이러한 문제는 우리가 생각한 로직대로 코드가 동작하지 않을 수 있기 때문에 React에서 지원하지 않는 방식입니다.
이 부분에서 조금 고민을 해보니 굳이 위 로직을 훅으로 만들 필요가 없다는 것을 깨달았습니다.
왜냐하면, 함수는 단순히 특정 작업을 수행하기 위해 작성된 코드 블록으로 특정 상태나 라이프사이클과는 무관하게 수행됩니다.
반면, 훅은 상태나 라이프사이클을 핸들링할 수 있으며 상태 관리 혹은 useEffect를 사용하기에 용이하다는 특징이 있기 때문입니다.
그렇다면 위에서 작성된 useSearchButton은 상태를 관리하고 있나요?
아닙니다. 단순히 전달받은 매개변수에 특정 로직을 추가하기 위한 함수로써 동작하고 있습니다.
따라서, 훅을 함수로 변경하여 문제를 해결할 수 있었습니다.
이처럼 훅과 함수를 별도 디렉토리 분리해서 관리한다면 코드를 더욱 깔끔하게 작성할 수 있을거라고 생각됩니다.
리팩토링하는 방법 & 과정은 아래 유튜브를 참고해봤습니다.
참고 유튜브
1.Refactoring a React Component (Design Patterns) - Cosden Solutions
2.How I Write Clean Code in React
3.Top 6 React Hook Mistakes Beginners Make
우선, 여기까지 정리하고 다시 한번 만들어보면서 또 다른 문제가 생긴다면 추후 다시 포스팅하여 정리해보도록 하겠습니다.