컴포넌트는 UI를 구성하는 독립적인 조각입니다. 단순히 코드 구조를 나누기 위한 수단이 아니라, UI에 의미와 역할을 부여하는 최소 단위라고 생각합니다.
React는 컴포넌트 기반 아키텍처를 택하고 있습니다. 저는 이 구조가 곧 애플리케이션 전체를 컴포넌트의 조합으로 바라본다는 뜻이라고 해석했습니다. 그리고 자연스럽게 다음과 같은 질문이 떠올랐습니다.
간단한 예를 들어보겠습니다.
export default function App() {
return (
<div style={{ maxWidth: '768px', padding: '16px'}}>
<div style={{ border: '1px solid #ddd', padding: '1rem' }}>
<h2>Frontend 개발자 모집</h2>
<p>React 경험자 우대</p>
<button>지원하기</button>
</div>
<div style={{ border: '1px solid #ddd', padding: '1rem' }}>
<h2>백엔드 개발자 모집</h2>
<p>Spring 경험자 우대</p>
<button>지원하기</button>
</div>
</div>
);
}
이 코드에서는 동일한 구조의 채용 카드가 반복되고 있습니다. “채용 정보를 보여주는 UI 블록”이라는 명확한 책임이 있습니다. 지금은 두 개뿐이지만, 이런 채용 카드가 10개, 20개까지 늘어난다고 가정해보면 어떨까요?
그때마다 동일한 코드를 복붙한다면 유지보수가 어려워지고, 스타일을 수정하거나 UI를 변경할 때 실수할 확률도 높아질 거예요.
따라서 저는 이 부분을 하나의 컴포넌트로 추출하고 싶어졌습니다.
function JobCard({ title, description }) {
return (
<div style={{ border: '1px solid #ddd', padding: '1rem' }}>
<h2>{title}</h2>
<p>{description}</p>
<button>지원하기</button>
</div>
);
}
export default function App() {
return (
<div {{ maxWidth: '768px', padding: '16px'}}>
<JobCard title="Frontend 개발자 모집" description="React 경험자 우대" />
<JobCard title="백엔드 개발자 모집" description="Node.js 경험자 우대" />
</div>
);
}
컴포넌트로 추출하니 의미 단위로 추상화되고, 중복도 줄어들고, 재사용 가능성도 생겼습니다. 추후 카드 UI의 디자인이나 로직이 바뀌어도 하나의 컴포넌트만 수정하면 되니 유지보수 관점에서도 이점이 있죠. 저는 이것이 컴포넌트를 분리해야 하는 이유라고 생각했습니다.
컴포넌트를 나누다 보니 저는 다음과 같은 것들에 대해 고민하기 시작했어요. “어디까지 컴포넌트를 나눠야 할까?”, “어떤 경우에는 오히려 나누지 않는 것이 더 좋지 않을까?”
저는 이 질문에 대한 기준을 적절한 크기와 명확한 역할에서 찾았습니다. 너무 작게 쪼개면 관리가 힘들고 의존성이 불필요하게 복잡해지고, 너무 크게 만들면 재사용성이 떨어지고 유지보수가 어려워지니까요.
그래서 저는 항상 중간 정도의 추상화 수준(하나의 책임을 설명할 수 있을 정도로 작되, 재사용될 가능성이 있는 정도의 범위), 그리고 사용자 경험과 개발자 경험을 동시에 고려한 구조화를 목표로 컴포넌트를 나누고 있습니다.
“적절한 크기와 명확한 역할을 가진 단위”
컴포넌트를 나누는 기준은 단순히 코드를 나누는 것이 아니라, 각 컴포넌트가 어떤 역할과 책임을 갖고 있는지, 하나의 명확한 책임을 가지는지, 사용 방법이 직관적인지에 기반한다고 생각해요.
이러한 기준을 바탕으로, 저는 다음 세 가지 기준에 따라 컴포넌트를 분리하고 있습니다.
기능적 단위는 가장 직관적인 컴포넌트 분리 기준이지 않을까 생각해요. 특정 기능이나 작업을 수행하는 UI 요소를 독립적인 컴포넌트로 만들면 재사용성도 높아지고 유지보수도 쉬워진다고 생각합니다.
Input, Form, SearchBar, Modal처럼 특정한 역할을 가진 기능 단위는 명확하게 분리하는 것이 좋다고 생각합니다.
예시
function Input({ value, onChange, type, placeholder, ...props }) {
return <input value={value} onChange={onChange} type={type} placeholder={placeholder} {...props} />;
}
디자인 시스템에서 자주 반복되는 스타일과 인터랙션 패턴(hover, pointer 등등)을 가진 요소는 UI 패턴으로 추상화하는 게 좋다고 생각합니다.
Text, Button 같은 요소는 여러 컴포넌트에서 조합해 사용되기 때문에, 공통 스타일을 모듈화해서 관리하는 것이 효율적이라고 생각합니다.
예시
function Text({ variant = 'Title', color = 'black', children }) {
// .. 내부 스타일 조건
return <p variant={variant} color={color} >{children}</p>;
}
function JobCard({ title, description }) {
return (
<div style={{ border: '1px solid #ddd', padding: '1rem' }}>
<Text variant="Title">{title}</Text>
<Text variant="caption">{description}</Text>
<button>지원하기</button>
</div>
);
}
페이지 구조나 레이아웃 패턴도 컴포넌트로 추상화할 수 있다고 생각합니다. 이를 통해 일관된 페이지 구조를 유지하고 유지보수를 간편하게 해주기 때문에 유용하다고 생각해요.
AppLayout, FormLayout, Flex, Grid 등은 반복되는 배치 패턴을 추상화한 레이아웃 단위라고 생각합니다.
예시
function AppLayout({ children }) {
return (
<div style={{ maxWidth: '768px', padding: '16px'}}>
{children}
</div>
);
}
export default function App() {
return (
<AppLayout>
<JobCard title="Frontend 개발자 모집" description="React 경험자 우대" />
<JobCard title="백엔드 개발자 모집" description="Node.js 경험자 우대" />
</AppLayout>
);
}
재사용성을 단순히 코드 반복을 줄이는 목적으로만 보기보다는, 개발 과정에서 마주치는 의미 있는 반복 작업을 줄이기 위한 수단이라고 생각합니다.
예를 들어 반복되는 스타일이나 레이아웃 구조를 매번 새로 짤 필요가 없도록 Flex, Text, Button 등을 직접 구현해 효율적인 작업 환경을 만들고자 합니다.
컴포넌트를 중심으로 사고하고 개발하는 CDD는 제가 선호하는 개발 방식이에요.
디자인 시스템을 구성하고, 스토리북으로 컴포넌트를 문서화하고, 개발 전에 구조를 먼저 설계하는 과정은 개발자-개발자, 개발자-디자이너간 커뮤니케이션 비용을 줄여주고 결국 초반에 컴포넌트를 바로 잡음으로써 더 나은 사용자 경험과 더 깔끔한 개발 경험으로 이어진다고 느꼈습니다.
재사용성과 일관성 향상
컴포넌트를 잘게 나누고 의미 단위로 구조화하면, 다양한 페이지에서 재사용 가능하고, UI/UX 일관성도 높아지는 것을 느꼈습니다.
빠른 개발 속도
한번 정의된 컴포넌트를 조합하는 방식으로 새로운 화면을 빠르게 만들 수 있습니다.
유지보수 효율성
구조화된 컴포넌트는 변경 사항의 영향 범위를 최소화하고, 기능 수정이나 확장 시에도 안정적으로 대응할 수 있습니다.
디자인 시스템과의 시너지
디자인 시스템과 함께 사용하면 디자이너와 개발자 간의 소통이 원활해지고, 협업의 생산성이 크게 올라갑니다.
초기 커뮤니케이션 비용
컴포넌트를 어떻게 나눌지, 어느 정도까지 확장 가능성을 고려할지 등을 초반에 팀원들과 충분히 논의해야 해요.
설계의 어려움
너무 세분화하거나 역할이 불명확한 컴포넌트를 만들 경우, 오히려 구조가 복잡해질 수 있다고 생각합니다.
장기적으로 비용 절감
저는 CDD의 초기 커뮤니케이션 비용과 설계 시간을 단순한 ‘비용’이 아니라 ‘투자’라고 생각합니다. 프로젝트 후반부에 컴포넌트를 분리하거나 재구성해야 할 때 발생하는 리팩터링 비용과 비교해보면, 오히려 초기에 구조를 잘 잡아두는 것이 더 경제적이라고 느꼈습니다.
확장성과 유연성 확보
기능이나 페이지가 점점 늘어날수록, 컴포넌트 중심의 설계는 그 진가를 발휘한다고 생각합니다. 처음부터 구조화된 컴포넌트를 기반으로 개발하면, 새로운 요구사항에도 유연하게 대응할 수 있는 기반이 마련되기 때문에 장기적인 확장성 측면에서 큰 이점을 가진다고 생각해요.
예를 들어 다음과 같은 화면이 있다고 가정해볼게요.

왼쪽 화면을 보면 굵은 제목과 Input이 반복되는 구조라는 점을 알 수 있어요. 이런 반복되는 구조는 일정한 패턴을 가지기 때문에, 레이아웃 단위로 추상화할 수 있다고 생각해요.
(예를 들어 FormLayout이라는 컴포넌트를 만들어 header, subTitle, label 등을 props로 받아 표현할 수 있을 것 같아요.)
또한 Text처럼 여러 곳에서 반복적으로 사용되는 UI 요소들은 UI 패턴 단위로 추상화할 수 있을 것 같아요. 그 다음에는 사용자 입력이라는 기능적인 Input 컴포넌트를 만들고, 이를 위의 컴포넌트들과 조합하여 하나의 CardNumberForm을 완성할 수 있을 것입니다.
이처럼 화면을 구성할 때는 먼저 큰 틀에서 구조를 설계하고, 작은 단위부터 구현하여 점차적으로 상위 컴포넌트를 완성해 나가는 방식을 선호합니다.
결국 컴포넌트는 단순히 코드를 나누는 도구가 아니라, 사용자 경험과 개발자 경험을 모두 향상시키기 위한 구조라고 생각합니다.
예를 들어, 사용자가 입력해야 하는 Form 컴포넌트를 만든다고 했을 때, 에러 메시지를 일관되게 보여주는 방식이나 포커스 이동 처리 등은 사용자의 실수를 줄이고 편리하게 사용할 수 있게 만들어주는 UX 개선으로 이어집니다.
또한, 개발자 입장에서는 이런 Form 컴포넌트를 잘 추상화해두면 여러 화면에서 재사용할 수 있고, props만 바꿔도 다른 상황에 대응할 수 있어서 코드 중복을 줄이고 유지보수 효율을 높일 수 있는 DX 향상으로 이어진다고 생각합니다.
앞으로도 컴포넌트의 책임과 구조에 대해 끊임없이 고민하며, 더 나은 사용자 인터페이스를 만들고 협업의 효율을 높이는 개발자가 되고 싶습니다.
역시 세라