항상 컴포넌트를 어떻게 잘 나누어야 할지 고민을 많이 했었다.
요즘은 Toss Fundamentals 같은 코딩 가이드들이 잘 만들어져 있어서 많은 인사이트를 얻고 있다.
예전의 나는 많은 역할을 하는 컴포넌트를 보면 바로 “컴포넌트를 나누자”라고 생각했을 것이다. 하지만 시간이 지나면서 단순히 분리하는 것만으로는 문제가 해결되지 않는다는 걸 배웠다. 분리는 목적이 아니라 결과여야 한다. 그리고 그 결과를 결정하는 기준은 응집도, 단일 책임, 관심사 분리, 추상화 같은 원칙들이다.
얼마 전 신입 개발자분이 이런 질문을 했다.
"state가 많아져서 컴포넌트 관리가 어려운데, state를 관리하는 방법이 있을까요?"
이 질문을 듣고나서 "하나의 컴포넌트에서 많은 state를 관리한다면, 그 컴포넌트가 여러 역할을 아닌지 의심을 해보세요"라고 답변을 드렸다.
컴포넌트가 맡고 있는 역할을 분리하고, 관련된 로직끼리 응집시키고, 필요하다면 적절히 추상화해보라는 의미였다.
하지만 돌아보니 이 답변은 너무 추상적이었다. 책이나 강의에서 흔히 들을 수 있는 정답 같은 말이지만, 실제 프로젝트에서 어떤 기준으로, 무엇을 보고, 어떻게 개선해야 하는지는 제대로 설명하지 못했다.
그래서 이 글에서는 컴포넌트에 state가 많아진다는 게 무엇을 의미하는지, 어떤 원칙을 기준으로 컴포넌트를 바라봐야 하는지 과거의 나에게 설명하는 마음으로 풀어내보려고 한다.
state가 많다는 건 보통 다음 중 하나 이상을 뜻한다.
하나의 컴포넌트가 여러 역할을 처리한다 는 의미이다. UI, 검증, 데이터 가공, 네트워크 요청이 섞여 있을 가능성이 매우 높다.
다른 곳에 분산되어 있어야 할 모든 로직을 하나의 컴포넌트가 들고 있다
즉, state 개수 자체보다 왜 그 수만큼 필요하게 되었는가 가 중요하다. 이 판단을 도와주는 기준이 바로 아래 개념들이다.
리액트는 컴포넌트를 통해 UI를 독립적이고 재사용 가능하게 한다고 말한다.
여러 요구사항을 충족하여 여기저기서 재사용 가능한 "만능" 컴포넌트를 만들려면 어떻게 해야할까? 아쉽지만 하나의 컴포넌트가 모든 기능 요구사항을 충족 할 수는 없다.
요구사항은 계속 바뀌기 때문에, 하나의 책임을 갖는 여러 컴포넌트의 조합으로 요구사항을 충족해야 한다.
어떤 기준으로 컴포넌트를 설계하면 좋을까?
서로 강하게 연관된 로직끼리 같은 위치에 모여 있는 정도를 말한다. 응집도가 높으면 코드가 자연스럽게 묶이며, 분리 기준이 명확해진다. 같은 관심사로 코드를 분리해 응집도를 높이자
컴포넌트는 하나의 역할만 명확하게 담당해야 한다. 역할이 늘어날수록 state도 늘어나게 된다.
로직을 적절히 감추고 필요한 부분만 드러내는 과정이다. state가 많다는 건 세부 로직을 컴포넌트가 직접 들고 있다는 뜻이기도 하다.
여러 역할을 하는 컴포넌트를 개선해보자
이 컴포넌트는 유저의 정보를 Form으로 입력받아 유효성 검사를 하고 서버로 유저 정보를 전달하는 userForm 컴포넌트다.

이 컴포넌트의 문제점을 살펴보자
우선 로직과 UI를 분리하자 컴포넌트는 UI로직만 신경쓰고 비즈니스 로직은 훅으로 위임한다.

useFormValidation : 유효성 검증 책임만 가지는 공통 훅
useCreateUser : API 호출 책임만 가짐
useUserForm : Form 상태와 두 로직을 조합하는 역할만 수행
기존 Form 검증 로직도 유틸 함수로 분리할 수 있다.

각 검증 함수가 하나의 역할만 수행하게 되어 유지보수하기 쉬워지고 가독성도 좋아진 것 같다. 검증로직을 추상화했기 때문에 함수만 가져다 사용하면 되고, 순수함수이기 때문에 단위 테스트가 매우 쉬워졌다.
React-Hook-Form과 zod를 사용하면 더 선언적이고 표준화된 패턴을 적용할 수 있을 것 같다

컴포넌트는 오직 UI만 담당하고 유효성 검사, 데이터 페칭 등 로직은 훅, 유틸함수로 분리했다. state 개수 자체는 비슷하지만 책임이 명확하게 분리된다
컴포넌트가 무거워지는 것은 자연스러운 일이다. 하지만 왜 무거워졌는지, 그 원인이 어디에 있는지를 원칙에 따라 바라볼 수 있다면 개선 방향은 명확해진다.
응집도를 높이고 단일 책임을 지키고 관심사를 분리하고 적절히 추상화한다
우리는 "컴포넌트를 작게 분리할 수록 좋은 구조인가?" 에 대해 계속 고민해야 한다.
컴포넌트가 과하게 분리가 된다면 코드의 흐름을 한눈에 흐름을 이해하기 어렵게 만들고, 코드를 이해하기 위해 파일을 바꿔가며 이동하다 보면 인지부하가 오기 쉽다.
결국 컴포넌트를 분리한다는 것은 인지부하를 낮추어 유지보수를 하기 용이하도록 하는 행위라고 생각한다. 분리 하기 전에 왜 이 분리가 용이한지, 분리 했을 때에 어떤 부작용이 있는지 계속 고민을 해야하는 과정이다.