@Joonmook
컴포넌트 분리, 많이 할수록 좋은 것이 아니다
프론트엔드 개발을 하다 보면 컴포넌트를 잘게 나누는 것이 좋은 설계라고 배우는 경우가 많다. 재사용성을 높이고, 단일 책임 원칙을 지키고, 테스트하기 쉽게 만들라는 이야기를 귀가 닳도록 듣는다. 그런데 실제로 프로젝트를 운영하다 보면 지나치게 잘게 나눠진 컴포넌트 구조가 오히려 유지보수를 더 어렵게 만드는 상황을 자주 마주하게 된다.
이 글에서는 무조건적인 컴포넌트 분리가 왜 문제가 될 수 있는지, 그리고 어떤 기준으로 컴포넌트를 나눠야 하는지에 대해 이야기해보려 한다.
지나친 분리가 만들어내는 괴물
처음 프로젝트를 설계할 때는 모든 것이 깔끔해 보인다. 버튼 하나도 컴포넌트로, 인풋 하나도 컴포넌트로, 레이블 하나도 컴포넌트로 분리한다. 심지어 텍스트 한 줄을 감싸는 컴포넌트까지 만들기도 한다. 처음에는 정말 아름다워 보인다.
그런데 6개��이 지나고 팀원이 바뀌거나 요구사항이 변경되기 시작하면, 이 아름다운 구조는 순식간에 악몽으로 변한다. 버튼 하나의 스타일을 바꾸려면 어느 컴포넌트를 수정해야 하는지 파악하는 데만 10분이 걸린다. Props가 5단계를 거쳐 전달되고 있어서 어디서 무엇이 오는지 추적이 불가능해진다.
실제로 이런 일이 생긴다
아래와 같은 구조를 생각해보자.
PageLayout
HeaderSection
NavigationWrapper
NavigationContainer
NavItemGroup
NavItem
NavItemLabel
NavItemText
이 구조에서 NavItemText의 색상을 바꾸려면 어디에 Props를 추가해야 할까. PageLayout에서 시작해서 NavItemText까지 Props를 내려보내야 할 수도 있다. 이것이 바로 Props Drilling이다. 이 문제를 해결하려고 Context나 상태관리 라이브러리를 도입하면, 이제는 단순한 색상 변경 하나에 전역 상태까지 연루된 구조가 만들어진다.
재사용성이라는 환상
컴포넌트를 잘게 나누는 가장 큰 명분은 재사용성이다. 그런데 냉정하게 생각해보면, 실제로 재사용되는 컴포넌트는 생��보다 훨씬 적다.
프로젝트 초반에 만든 UserProfileCard 컴포넌트가 다른 곳에서 재사용될 것이라 생각해서 내부를 ProfileImageWrapper, ProfileInfoSection, ProfileActionButtons 등으로 쪼개놓았다고 하자. 그런데 막상 시간이 지나보면 UserProfileCard는 딱 한 군데에서만 쓰이고 있다. 반면 그 내부 컴포넌트들은 조각나 있어서 전체적인 흐름을 파악하기 위해 파일을 수십 개 열어봐야 한다.
재사용될 것이라는 예측으로 미리 분리하는 것은 대부분 잘못된 판단이다. 실제로 재사용이 필요해지는 순간에 분리해도 늦지 않다.
YAGNI 원칙을 기억하자
소프트웨어 개발에는 YAGNI라는 원칙이 있다. You Ain't Gonna Need It, 즉 필요하지 않을 것이라는 뜻이다. 지금 당장 필요하지 않은 기능이나 구조를 미리 만들지 말라는 이야기다. 컴포넌트 분리에도 이 원칙이 그대로 적용된다. 재사용될지도 모른다는 이유만으로 지금 당장 분리하는 것은 오버엔지니어링이다.
파일이 많아질수록 인지 부하가 늘어난다
컴���넌트를 분리하면 파일이 늘어난다. 파일이 늘어나면 프로젝트 구조를 이해하는 데 더 많은 시간이 필요하다. 특히 새로운 팀원이 온보딩할 때 이 문제는 더욱 심각해진다.
파일이 30개인 프로젝트와 파일이 300개인 프로젝트 중 어느 쪽이 더 빠르게 파악할 수 있을까. 당연히 전자다. 물론 프로젝트 규모가 커지면 파일이 많아지는 것은 자연스러운 일이다. 하지만 같은 기능을 구현하는 데 파일이 불필요하게 많다면 그것은 설계 문제다.
좋은 코드는 읽기 쉬운 코드다. 컴포넌트가 수십 개로 나뉘어져 있어서 하나의 기능을 파악하기 위해 파일을 10개 이상 열어야 한다면, 그것은 좋은 구조가 아니다.
응집도를 높이는 것이 먼저다
컴포넌트 분리보다 중요한 것은 응집도다. 관련된 코드는 함께 있어야 한다. 같은 기능을 담당하는 로직, 스타일, 마크업이 여러 파일에 흩어져 있으면 수정할 때 모든 파일을 동시에 열어야 한다. 이것은 분리가 아니라 분산이다.
언제 컴포넌트를 나눠야 하는가
그렇다고 컴포넌트를 전혀 나누지 말라는 이야기가 아니다. 분리가 필요한 명확한 기준이 있다.
- 실제로 여러 곳에서 재사용되고 있을 때
- 하나의 컴포넌트가 너무 많은 책임을 지고 있어서 이해하기 어려울 때
- 독립적으로 테스트해야 할 논리적 단위가 생겼을 때
- 렌더링 최적화가 필요해서 특정 부분만 분리해야 할 때
- 팀원 간의 작업 충돌을 줄이기 위해 담당 영역을 명확히 나눠야 할 때
이 조건 중 하나라도 해당하지 않는다면, 굳이 분리할 필요가 없다. 분리는 문제를 해결하기 위한 수단이지, 그 자체가 목적이 아니다.
코드 줄 수를 기준으로 삼지 마라
어떤 사람들은 컴포넌트가 200줄이 넘으면 나눠야 한다고 말한다. 하지만 줄 수는 분리의 기준이 될 수 없다. 200줄짜리 컴포넌트가 하나의 명확한 역할을 잘 수행하고 있다면, 그것은 좋은 컴포넌트다. 반면 50줄짜리 컴포넌트라도 두 가지 이상의 서로 다른 관심사가 섞여 있다면 분리를 고려해볼 수 있다.
유지보수를 생각한다면 단순함을 선택하라
프로젝트는 만들 때보다 유지보수할 때가 더 오래 걸린다. 코드는 작성되는 횟수보다 읽히는 횟수가 훨씬 많다. 그렇기 때문에 처음부터 지나치게 정교하게 설계하는 것보다, 단순하고 이해하기 쉬운 구조를 유지하는 것이 장기적으로 훨씬 이득이다.
6개월 후의 자신이 이 코드를 볼 때 빠르게 이해할 수 있는가. 새로운 팀원이 이 구조를 파악하는 데 얼마나 걸리는가. 이런 질문이 컴포넌트를 나눌지 말지를 결정하는 더 좋은 기준이다.
지금 당장 완벽한 구조를 만들려고 하지 마라. 필요할 때 리팩터링하는 것이 처음부터 과도하게 분리하는 것보다 훨씬 낫다.
정리하며
컴포넌트 분리는 도구다. 도구는 올바른 상황에서 올바르게 사용해야 한다. 분리 자체가 좋은 개발 습관이라는 생각에서 벗어나, 왜 나누는지, 나눴을 때 실제로 이득이 있는지를 먼저 생각해보자.
지나치게 잘게 나눠진 컴포넌트 구조는 처음에는 깔끔해 보이지만, 시간이 지날수록 파악하기 어렵고 수정하기 두려운 구조가 된다. 단순함은 게으름이 아니라 선택이다. 유지보수��기 쉬운 코드는 대부분 단순한 코드다.