1월에 새로운 팀으로 소속이 변경되었어요. 스프린트 주기도 2주 단위에서 1주 단위로 변경되면서 거의 매주 배포를 진행하게 되었어요. 그러다보니 자연스럽게 짧고 굵고 빠르게 개발을 하게되는 일이 많아졌어요.
새 코드를 작성할때는 문제가 되지 않았던 것들이 나중에 돌이켜보니 문제가 되는 상황들이 많이 발생했어요. 작은 기능이 될거라 생각했던 것들이 생각보다 반응이 좋아 고도화를 하게 되었다던가, 비즈니스 문제로 기능이 변경되게 되면서 더 복잡한 요구사항을 갖게된다던가 🫠
그러다보니 자연스럽게 빠르게 이해하고 고칠 수 있는 코드를 짜려고 노력하게 되었어요. 그 과정속에서 제가 고치게 됐던 몇가지 안좋았던 습관들을 소개할게요. 🙇🏻♂️
저는 이전에 특정 상태의 변경점을 캐치해서 콜백로직을 실행하는 상황에서도 useEffect
를 자주 사용했던 것 같아요. 하지만 이러한 습관이 쌓여서 결국 컴포넌트의 동작 흐름을 알아볼 수 없게 되었어요.
const App = () => {
const { data } = useQuery();
const [tab, setTab] = useState('');
const currentTabIndex = useMemo(()=> return ..., []);
useEffect(() => {
...
, [tab]);
return (
<>
</>
);
};
저는 보통 이런패턴으로 컴포넌트를 작성해요. 해당 컴포넌트와 관련이 적은 것들은 최대한 위로 올리고, 관련도가 높을 수록 아래로 배치하고 useEffect
는 마치 컴포넌트의 문처럼 열고 닫음을 표현한다고 생각해서 return
문 직전에 선언하는 편이에요. (제 개인적인 코드 작성법이에요. 🤗)
위에 작성한 코드만 봤을때는 굉장히 짧고 간략해서 어떻게 동작하는지 어떠한 값이 필요한지 바로 이해가 가능하다고 생각해요. 하지만 요구사항이 많고 처리가 복잡해질수록 의도와는 다르게 컴포넌트가 비대해질 수 있어요.
처음에는 이런 상황에 자연스럽게 useEffect
를 사용하곤 했어요. 하지만 useEffect
를 하나씩 추가할때마다 useEffect
의 실행 순서를 빠르게 파악하기도 힘들 뿐더러 요구사항은 명확하지만 어떤 타이밍에 콜백 로직이 실행되는지 이해하기 힘들어졌어요.
가끔은 아래와 같은 요구사항이 중간에 주어질 수도 있어요.
요구사항이 크게 어려운지는 모르겠어요. 😓 하지만 useEffect
의 실행 순서와 의존성 배열을 통한 영향 범위를 파악하기 힘들어지면 간단한 요구사항도 제대로 동작하지 않아 어려움을 겪을 수 있어요.
저는 결국 이러한 동작 흐름을 조금이라도 명시적으로 하기 위해 다음과 같은 방법들을 사용하고 있어요.
useEffect
가 필요한 상황인지 한번 더 고민해보기useEffect
를 여러번 호출해야하는 상황이라면 정말 필요한 값들만 의존성 배열에 삽입하기위의 방법들이 최선이 아닐 수도 있어요. 특히 세번째 방법은 파일이 많고 복잡한 프로젝트에서는 불필요한 파일 이동과 로딩시간이 발생하거나 시작적인 피로가 발생할 수 있어요. 😥
가장 확실한건 첫번째 방법이라고 생각해요. 보통은 useEffect
를 사용해야하는 로직들은 이벤트 핸들러 안에서 실행해서 해결되거나, 단순 변수 선언으로 해결되는 경우가 훨씬 많았어요. 그만큼 다시 한번 설계를 생각해봐야하지 않나 생각하는 부분이 바로 useEffect
를 사용하려 할 때 인 것 같아요.
Effect 는 말그대로 부수효과를 의미해요. 리액트의 역할은 컴포넌트를 효과적으로 렌더링함에 있지만, useEffect 를 통해 그 외의 동작도 지원하고 있어요. 그만큼 useEffect 라는 훅의 이름은 부수효과를 다룸에 있어서 예상하지 못한 결과를 가져올 수 있음을 의미한다고도 생각해요.
특정 상황에서는 어쩔 수 없이 전역상태를 정의해야 하는 경우가 발생하기도 해요. 저같은 경우에는 전에 이러한 상황에서 전역상태를 사용하려고 했던 것 같아요.
사실 이러한 상황에서 당연히 전역상태를 사용할 수 있어요. 하지만 이러한 상황에서 무조건 전역상태를 사용하게 되면 나중에는 이러한 무작정 선언되는 전역상태로 인하여 해당 상태가 미사용하는 상태인지, 해당 전역상태를 사용하는 로직을 수정해도 되는지 빠르게 확인하기 어려웠어요. 어쩔때는 상상도 못했던 전역상태가 내가 수정한 로직에서 사용되고 있어서 버그가 발생한적도 있었어요.
이러한 상황에서 전역상태를 무조건 사용하지 말자는 너무 일차원적인 생각이기도 해요. 저는 이렇게 풀어보려고 했어요.
쿼리파라미터는 어떻게 보면 리액트 생태계와 관련없이 사용할 수 있는 또다른 전역상태라고 생각해요. 게시판이나 폼 뿐만 아니라 여러 상황에서 쿼리파라미터는 항상 유용하게 사용할 수 있었어요.
const { filter } = useQueryString<{type: string}>();
const { route } = useRouteWithQuery();
// 현재 쿼리파라미터를 모두 갖고 이동
// ?type=A&filter='sort_asc'
route({ type: 'A', keep: true, replace: true });
저는 이런식으로 쿼리파라미터를 자주 사용해서 관련 훅들도 정의해두고 사용한적이 많아요. 해당 페이지나 컴포넌트 안에서만 사용되기 때문에 쿼리파라미터에 따른 영향범위를 파악할 필요가 적어서 좋았어요.
퍼널에 대한 상태를 관리하거나, 정말 작은 범위만 사용하는 상황에서도 전역상태를 정의해버리면 파악이 어려울 수 있어요. 저는 이러한 상황에서 다른 컴포넌트에서 사용할 수 없도록 가장 기본적인 방법인 Context API 를 사용해서 전역상태를 정의하기도 했어요. 혹은 사용하는 컴포넌트와 가장 가까운 폴더에 아주 작은 상태를 정의해서 그 외의 컴포넌트에서는 사용하지 않는것을 암시하기도 했어요.
모달같은 경우에는 페이지에 국한되지 않고 노출이 되어야 하는 상황이 발생하기도 해요. 저는 이러한 상황에서는 라이브러리를 사용하기도 해요.
const open = () => {
overlay.open(
({ isOpen, unmount }) => {
return (
<Modal
onClose={onClose}
>
...
</Modal>
);
...
overylay-kit
은 제가 굉장히 유용하게 사용하고있는 라이브러리에요. 보통의 모달 상태관리 방법과는 다르게 열다(모달)
정도로 직관적인 인터페이스를 제공해줘요. 직관적인 인터페이스만큼 코드만 봐도 영향범위를 알 수가 있어요.
저는 이렇게 여러가지 방법으로 전역상태를 최소화 하고 있고, 가장 최근에 개발한 개인블로그는 전역상태를 전혀 사용하지 않고 개발하기도 했어요. 전역상태를 사용하지 않으니 유지보수도 굉장히 빠르게 진행할 수 있었어요. 🤗
반복되는 작업을 더 쉽게 실행하기 위해 모듈화를 해야하는 상황에서 깊게 생각하지 않고 설계하게된다면 나중에 굉장히 후회할 수도 있어요. 저는 보통 이런상황에 있어서 추상화를 했던 것 같아요.
적절한 추상화는 좋은 결과를 낳을 수 있지만 대부분은 실패하기도 해요. 저같은 경우에는 사내 서비스를 개발할때 복잡한 폼 상태 관리를 위해 컴포넌트와 로직을 재설계해서 리팩토링을 한 경험이 있어요.
몇달이 지나고 제가 팀을 옮기게 되었고 해당 프로젝트 개발에 참여하지 않게 되었어요. 문제가 없을 줄 알았던 코드에 버그가 있었고, 제가 아닌 같이 일을 했던 팀원이 버그를 수정해야 했어요. 단순한 문법오류였지만, 팀원은 한시간이 넘도록 디버깅을 하지 못했어요. 결국엔 제가 다시 클론을 받아서 문제지점을 찾게 되었고, 회고를 하면서 어느부분이 문제였을까 고민하게 되었어요.
이 외에도, 공통으로 사용하는 모달을 관리하기 위해서 커스텀 훅을 구현하게 되었어요. 처음에는 해당 훅을 사용하기 위한 옵션이 한두개밖에 없었지만, 지금은 6개가 넘는 옵션을 갖게 되었고 다른 옵션때문에 정작 필요한 값을 props 로 넘겨주지 못하는 상황이 발생하기도 했어요.
이러한 상황에서 찾은 답은 아래와 같아요.
제가 길지않은 시간동안 이것저것 많이 부딪혀보며 느꼈던 것중에 가장 실천하기 쉬운 것들을 정리해봤어요. 이렇게 여러 부분에 대해서 고민하면서 개발을 하다보면 지루하게 느껴졌던 것들도 다른 방법으로 풀어나가면서 느끼는 쾌감이 대단하다고 느끼는 것 같아요.
제 생각을 정리하면서 오랜만에 글을 작성해봤어요. 혹시라도 잘못된 정보가 전달되었거나, 애매한 부분이 있다면 편하게 말씀해주세요!
여러분들은 무슨 고민을 하면서 개발을 하고 계신가요? 😁
Love this!