기존에 Select 컴포넌트 코드는 이러합니다.
'use client';
import React, { useState } from 'react';
import { Icon } from '../Icon';
type Props = {
contents: string[];
size?: 'md' | 'lg';
placeholder?: string;
};
export function Select({ contents, size, placeholder }: Props) {
const [selectedContent, setSelectedContent] = useState(placeholder ? placeholder : contents[0]);
const [openSelect, setOpenSelect] = useState(false);
return (
<div
className={`${openSelect ? 'rounded-lg' : 'rounded-lg border border-gray-100'} text-b'${
size === 'md' ? 'md:min-w-26 w-fit min-w-24' : 'md:min-w-66 w-60'
} cursor-pointer border border-gray-200 bg-white text-start text-base font-semibold text-gray-400 md:text-lg`}
>
<div
className={`${
openSelect
? 'border-b-2 border-gray-100 hover:rounded-lg hover:rounded-b-none'
: 'border-0'
} flex justify-between ${
size === 'md' ? 'items-center py-1 pl-3 pr-2 text-sm' : 'px-5 py-2 md:py-3'
} hover:rounded-md hover:bg-gray-50`}
onClick={() => setOpenSelect(!openSelect)}
>
{selectedContent}
<Icon
name="arrowDown"
className={`transform transition-transform duration-300 ${
openSelect ? 'rotate-180' : ''
} ${size === 'md' ? 'w-5' : ''} }`}
/>
</div>
{openSelect && (
<div className="flex flex-col">
{contents.map((item, key) => (
<div
onClick={() => {
setSelectedContent(contents[key]);
setOpenSelect(!openSelect);
}}
className={`cursor-pointer border-gray-100 ${
size === 'md' ? 'px-3 py-1 text-sm' : 'px-5 py-2 md:py-3'
} last:border-none hover:bg-gray-50 hover:last:rounded-b-md`}
key={key}
>
{item}
</div>
))}
</div>
)}
</div>
);
}
모든 로직과 스타일이 하나의 컴포넌트 안에 꽉 묶여 있었습니다.
선택된 항목 상태 관리부터 드롭다운 토글, 아이템 렌더링, 스타일링까지… 모든 역할이 한 파일에 들어있는 모습입니다.
Select 뿐만 아니라 GroupingComponent도 있는데 groupName만 추가된 형태임에도 새로운 컴포넌트를 만들어야됐습니다 
이 경험을 계기로 자연스럽게 이런 질문을 던지게 됐습니다.
Q1. Select 컴포넌트의 ‘책임’은 어디까지여야 할까?
Q2. 조금 다른 UI/동작을 위해 새로운 컴포넌트를 만드는 것이 과연 효율적인가?
Q3. 재사용성과 확장성을 모두 고려한 UI 컴포넌트 설계 기준은 무엇인가?
이 고민은 단순한 리팩토링을 넘어, "컴포넌트는 기능 단위가 아니라 역할 단위로 쪼개야 한다"는 깨달음으로 이어졌습니다.
Effective Component 강의 「지속 가능한 성장과 컴포넌트」를 보고 다음과 같은 인사이트를 얻었습니다.
변하는 것과 변하지 않는 것을 나누자.
데이터는 같지만 UI는 달라질 수 있다면 UI와 데이터를 분리하자.
useCalendar)💡 변화하는 부분은
훅, 보여지는 부분은컴포넌트로 분리
하나의 역할을 가진 컴포넌트를 여러 개 조합해서 구성하자.
TriggerMenuItem→ 이벤트 흐름: onClick → onChange

이렇게 하면 각 컴포넌트는 서로를 몰라도 되고, 독립적으로 재사용 가능
데이터 주입받는 방식도 컴포넌트처럼 분리해야 한다.
이 둘을 분리하면 외부 의존성 없이도 관리하기 쉬움

예: 인터페이스 정의부터 고민하기
만약 이 Select에 MultiSelect나 검색 기능이 추가된다면, 어디부터 손대야되는지 막막했습니다.
기능을 더하려 할수록 점점 커지고, 더 복잡해지는 컴포넌트. 확장에는 약하고, 변경에는 민감한 구조라는 걸 느꼈습니다.
때문에 변경에 유연한 컴포넌트 형태로 리팩토링 해야겠다고 생각이 되었습니다.
영상에서 dropdown컴포넌트를 다음과같이 구조화한 예시를 보여주었습니다
DropdownTrigger : 열고 닫는 역할 Menu : 리스트의 wrapper Item : 실제 선택할 항목 onClick → onChange변경된 컴포넌트는 서로의 존재를 몰라도 동작합니다.
그리고 이러한 구조는 확장성과 재사용성 유지보수성을 높입니다
“이 컴포넌트의 의도는 무엇인가?”
“기능보다 표현 방식이 더 중요할 수도 있다.”
“리팩토링은 기능을 더하는 것이 아니라, 이해를 더하는 과정이다.”

Select 컴포넌트는 역할별로 책임을 나눠 확장성과 재사용성을 고려한 구조로 설계했습니다.
index.ts: Select 관련 컴포넌트를 통합 export하는 진입 파일SelectMain.tsx: Select의 루트 컴포넌트로, Context를 제공하고 전체 구조를 제어TriggerButton.tsx: 선택된 값을 보여주고 드롭다운을 열고 닫는 버튼 역할OptionList.tsx: 드롭다운 영역을 구성하며, 내부에 여러 Option을 포함하는 파일Option.tsx: 실제 선택 가능한 항목OptionGroupName.tsx: 옵션을 그룹으로 묶을 때 사용하는 제목 컴포넌트Select.context.tsx: 선택 상태, 열림 여부 등을 관리하는 Context를 정의useSelectMain.ts: Select 내부 로직을 담당하는 커스텀 훅Select.stories.tsx: Storybook용 시각화 테스트 파일각 파일이 명확한 역할을 가지도록 분리함으로써,기능 추가나 유지보수가 훨씬 쉬워졌습니다.
useSelectMain: 선택 상태 및 열림 여부 등 4가지 상태를 지역 상태로 관리합니다
selected: 현재 선택된 항목
isOpen: 드롭다운 열림 여부
setIsOpen: 드롭다운 열고 닫기 제어
handleSelect: 옵션 선택 핸들러
SelectMain: UI 뼈대를 구성하고, context로 하위 컴포넌트에 상태 전달
SelectContext.Provider: context를 통해 하위 컴포넌트로 상태 전파
useSelectContext: 하위 컴포넌트들이 context 값을 받아 사용

구조로 나타내면 이렇게 표현할 수 있습니다
[useSelectMain] ← 지역 상태 생성
│
▼
[SelectMain] ← 상태를 context로 감싸서 하위에 전달
│
▼
[SelectContext.Provider] ← context 전파
│
▼
[useSelectContext()] ← 하위 컴포넌트가 context 값 사용
├── Option → onSelect, size
└── OptionList → size
└── TriggerButton → size,onSelect, selected
그리고 마지막으로는 만들어진 컴포넌트를 조립해서

각각의 컴포넌트처럼 사용할 수 있도록 만들었습니다
이번 리팩토링을 통해,
그동안 컴포넌트를 나눈다 = 파일/폴더 단위로 나눈다 라고만 생각했던 저 자신을 돌아보게 되었습니다.
컴포넌트는 단순히 폴더에 따라 분리하는 것이 아니라,
그 안에서도 역할(기능 vs UI) 기반으로 더 깊이 있게 나눌 수 있고,
필요한 기능을 조립해서 사용하는 ‘블록형 사고방식’이 가능하다는 것을 새롭게 체감했습니다.
하나의 Select 컴포넌트 안에서도
단순히 쓰기 좋게 만드는 게 아니라,
나중에 내가 다시 볼 때도 유용한 구조로 만드는 것이 진짜 설계라는 걸 느꼈습니다.
잘 만든 컴포넌트는 나에게 다시 되돌아온다 라는 생각이 가장 들었고
이 구조가 앞으로 제 프로젝트에서도 재사용될 수 있다는 생각에 설계의 중요성을 더 실감하게 됐습니다.
이번 경험을 바탕으로
에 더 깊이 고민하며 성장해 나가고 싶습니다.
https://www.youtube.com/watch?v=fR8tsJ2r7Eg&t=46s
https://velog.io/@aeong98/%EC%BB%B4%ED%8C%8C%EC%9A%B4%EB%93%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%8C%A8%ED%84%B4%EC%9C%BC%EB%A1%9C-Select-%EB%A7%8C%EB%93%A4%EA%B8%B0
https://ui.shadcn.com/docs/components/dropdown-menu