** 위의 글은 실무레벨로 다시 용어를 적립한 글이여서 매우 많이 참고하여 적는다. 그렇기 때문에 상단에 박제하겠다.
일단 공통 컴포넌트의 기준을 정할때 경계를 명확히하기 전에 SRP(Single Responsibility Principle) : 단일 책임 원칙에 대해서 명확하게 하고 갈 필요가 있어서 정리하게 되었다.
“SOLID 원칙 중에서 그 의미가 가장 잘 전달되지 못한 원칙은 바로 단일 책임 원칙(SRP)이다. 아마도 현저히 부적절한 이름 때문이기도 할 것이다.” - 로버트 C. 마틴(클린 아키텍처)

: 콘웨이 법칙에 입각해 소프트웨어 구조를 조직의 커뮤니케이션 구조와 유사하게 만들 수 있도록 도와주는 원칙이다.
SOLID 원칙의 대전제 : 콘웨이 법칙
소프트 웨어 구조는 해당 소프으퉤어를 개발한 조직의 커뮤니케이션 구조와 같아야 한다.
컴포넌트를 동작으로 쪼개면 기준이 애매하고 자잘하게 컴포넌트가 쪼개질 수 있다.
단일한 동작을 가지는 것은 컴포넌트가 아니라 순수한 함수 한정이다.
“SRP의 최종 버전은 다음과 같다. 하나의 모듈은 하나의, 오직 하나의 액터에 대해서만 책임져야 한다.” - 로버트 C. 마틴(클린 아키텍처)
모듈 == 컴포넌트
액터 == 사용자 또는 책무 단위
🔥 즉, SRP 원칙을 지킨다는 것은 컴포넌트를 설계할 때 요구사항을 전달하는 책무단위로 설계한다
요구사항을 전달하는 책무 기반 컴포넌트 설계글을 정리하면서 제일 어려웠던 부분이였다.
컴포넌트의 경계는 그 컴포넌트가 감당해야할 변경의 범위와 일치해야한다.
기획과 디자인 조직이 분리되어 있다면, 해당 책임을 서로 다른 컴포넌트로 분리하고, 반대로 디자이너가 기획까지 수행하는 조직(내가 리팩토링을 할려는 프로젝트)이라면, UI와 비즈니스 규칙이 결합된 컴포넌트로 해도 무방하다.
조직 구조에 따라 컴포넌트의 경계가 달라진다는 말로 나는 이해를 했다.
위와 같은 이론이라면 팀의 구조가 바뀌면 이전에 짰던 컴포넌트들이 SRP 원칙에 위반된다는 말에 더 가깝다. 다시 정리를 해보자면
컴포넌트의 경계 = 변경의 이유(Responsibility)
즉, 컴포넌트가 언제 수정되어야 하는가를 기준으로 나눠야한다는 뜻이다.
피드백의 예시를 그대로 쓰자면,
// A 컴포넌트: a 비즈니스 로직 전담
function AComponent() {
const data = useALogic(); // a 관련 로직만 수행
return <AView data={data} />;
}
❌ 잘못된 경우
// A 컴포넌트 내부에서 a와 b 로직을 모두 처리
function AComponent() {
const aData = useALogic();
const bData = useBLogic(); // b 비즈니스 로직까지 함께 수행
return <AView aData={aData} bData={bData} />;
}
b 로직이 바뀌면 A도 수정해야 함
→ “A는 a 비즈니스만 책임진다”는 단일 책임 원칙(SRP)을 깨트림
→ 즉, A가 b의 책무까지 침범한 것
책임: 데이터가 어떤 디자인과 연결될지, 화면 단위 컴포넌트 구조 정의
변경 이유: 기획/UX 변경
function SingleSection({ title, more, items }) {
return (
<Section>
<Section.Title title={title} />
<Section.More text={more} />
<Section.SingleListView items={items} />
</Section>
);
}
테스트 예시: 기획서 제약사항 검증
describe('Section.Title', () => {
it('섹션 타이틀은 최대 15자 이내로 보여져야 합니다', () => {
const title = "15자가 넘으면 15자까지만 보여집니다.";
const { getByText } = render(<Section.Title title={title} />);
expect(getByText(title.substring(0, 15))).toBeInTheDocument();
});
});
책임: 시각 요소, 레이아웃, 패딩, 마진, 색상 등
변경 이유: UI/디자인 정책 변경
// Atom: BaseButton
function BaseButton({ children, className, onClick }) {
return <button className={`base-button ${className}`} onClick={onClick}>{children}</button>;
}
// Molecule: BrandButton
function BrandButton({ brand, children, onClick }) {
const styles = {
naver: "bg-[#03c75a] text-white",
kakao: "bg-[#fee500] text-[#3c1e1e]",
google: "bg-white border text-gray-700",
};
return <BaseButton className={styles[brand]} onClick={onClick}>{children}</BaseButton>;
}
책임: API 호출, 상태 관리, 로직 처리
변경 이유: 서비스 정책/비즈니스 로직 변경
// Hook 기반 로그인 로직
function useLogin() {
const [loading, setLoading] = useState(false);
const login = async (credentials) => {
setLoading(true);
await authApi.login(credentials);
setLoading(false);
};
return { login, loading };
}
// 화면에서 훅과 UI 연결
function LoginButton({ brand }) {
const { login, loading } = useLogin();
return <BrandButton brand={brand} onClick={() => login({})} />;
}
SRP 기준: 화면 단위 컴포넌트는 “UI + 훅 연결” 역할만

위의 글을 보면 아토믹 디자인 개념을 사용해서 각 컴포넌트가 하나의 역할만 하도록 정리할 수 있고 이렇게 하면 디자이너와 개발자가 같은 단위(컴포넌트)를 기준으로 이야기할 수 있게 된다고하고 디자인 컴포넌트와 1:1 매칭을 할 수 있게 된다고 한다.
즉, 디자인의 최소단위와 코드의 최소 단위를 똑같이 맞춘다는 뜻이다. 이렇게 되면 디자이너와 개발자가 같은 기준으로 소통이 가능하다는 장점이 있는 것이다.
컴포넌트를 atom, molecule, organism, template, page의 5가지 레벨로 나누는 것


Atom : 더 이상 분해할 수 없는 기본 컴포넌트

Molecule : 여러개의 atom으로 결합하여 고유한 특성을 가지는 것

Organism : 여러개의 atom, moluecule, organism

위의 예시는 header organism인데
로고: atom + 네비게이션 : moluecule + search form : moluecule 로 구성되어 있다.
동작 단위가 아니라 책무 단위까지라는 말은 위의 글을 통해서 이해를 했다. 카카오 테크 블로그 컴포넌트 관련 다른 글을 읽어도 폴더가
이렇게 아토믹 디자인 단위: atom > molecules > organisms > template > page 순으로 되어 있는데
components/
┣ atoms/
┃ ┗ Button.tsx
┣ molecules/
┃ ┗ SearchBar.tsx
┣ organisms/
┃ ┗ Header.tsx
┣ templates/
┃ ┗ MainLayout.tsx
┗ pages/
┗ HomePage.tsx
이런 구조는 사실 대규모 디자인 시스템 팀이나 토스,네이버,카카오 같은 조직형 아키텍처에서 가능한 방식인 것 같다. 대규모 디자인 시스템 프로젝트가 아닌 소규모 프로젝트를 자주 경험한 나로서는 오히려 디자인 단위와 코드 단위가 1:1로 맞추는 건 이론적인 이야기인 것 같다. 왜냐면, 프론트하면서 제가 디자인까지 담당하기 때문이죠..
여기서 요점은 SRP는 단위책임원칙에서 책임은 동작단위가 아니라는 것!! 책무 단위라는 것이다. 위의 디자인 단위와 코드 단위를 1:1로 맞추는 경험을 나도 하고 싶지만,, 내가 속한 상황에서는 책임이 다르고, 변경 주기가 다를 때만 파일을 나눈다로 해야한다.
components/
┣ common/
┃ ┣ Button.tsx // Atom
┃ ┗ Input.tsx // Atom
┣ layout/
┃ ┗ Header.tsx // Organism
┗ pages/
┗ Home.tsx // Page
요로콤..
필요할 때만 분리하고, 변경 주기가 다를 때만 파일로 나눈다.
Organism 단위 + common 폴더 구조로 간다.
>> Atomic의 개념은 유지하되 파일은 최소화하기
나는 프로젝트할때 item > box > container > section 으로 컴포넌트를 세분화 했었다. 아토믹 디자인과 명칭은 다르지만 단계가 같았다. 그러나 이를 나누는 기준을 명확하게 문서와하지 못 해 리팩토링 시 아토믹 디자인 정의 기준에 따라 명확하게 할 것이다.
src/
┣ components/
┃ ┣ common/ ← Atom, Molecule 수준 (재사용 UI)
┃ ┃ ┣ Button.tsx
┃ ┃ ┣ Input.tsx
┃ ┃ ┗ Modal.tsx
┃ ┣ layout/ ← Organism 수준 (헤더, 푸터, 사이드바 등)
┃ ┃ ┣ Header.tsx
┃ ┃ ┗ Footer.tsx
┃ ┣ sections/ ← 페이지 내 큰 블록 (Organism)
┃ ┃ ┣ ProfileSection.tsx
┃ ┃ ┣ CoinChartSection.tsx
┃ ┃ ┗ RankingSection.tsx
┃ ┗ pages/ ← Template/Page 수준
┃ ┗ MyPage.tsx
┣ hooks/
┣ store/
┣ utils/
┗ styles/
버튼을 예시로 가져와서 적용해보자.
버튼에는 종류가 두 가지가 있는데, 하나는 브랜드별 시각만 있는 버튼이고 다른 하나는 로그인 동작을 하는 버튼이다.
1. BaseButton : 버튼의 기본 구조, 공통 스타일 제공 (padding, border, radius)
2. BrandButton : 브랜드별 시각적 스타일 책임 (color, logo, hover 색상)
3. ActionButton : (옵션) 기능적 상태 관리 (loading, disabled)
BaseButton.tsx -> components/common/ (재사용 UI, Atom)
BrandButton.tsx -> components/common/brand/ (공통 UI의 파생 스타일, Molecule)
ActionButton.tsx -> features/auth/ OAuth 로직까지 포함한 “로그인 버튼”(토큰 처리, 중복클릭 방지, 로딩 등) 도메인 폴더로
src/
┣ components/
┃ ┗ common/
┃ ┣ BaseButton.tsx ← 공통 버튼(Atom)
┃ ┣ brand/
┃ ┃ ┣ BrandButton.tsx ← 브랜드 스타일만 책임(Molecule)
┃ ┃ ┗ brandTokens.ts ← 브랜드 색/로고 토큰
┃ ┗ ...
┗ features/
┗ auth/
┗ OAuthLoginButton.tsx ← OAuth 로직(도메인) 포함
짧은 지식이지만 중간에 해석이 모호한 부분이 있어 글 남깁니다.
디자이너가 기획까지 하는 팀
→ 한 사람이 UI와 동작을 같이 책임지니까, 굳이 나눌 필요 없음
해당 문장에 대해서입니다.
전제는 다음과 같습니다.
프로젝트의 규모가 커짐에 따라 디자이너와 기획의 팀이 달라진다면??
기존에 약 100개의 컴포넌트를 만들어 두었을 때
본문의 글처럼 SRP를 해석하면 모두 책임 소재가 분명한 단일 책임을 가지는 100개의 컴포넌트였는데
단순히 팀이 갈라진다는 이유로 한 순간에 100개의 컴포넌트는 SRP를 위반하게 됩니다.
또한 컴포넌트라는 단어가 아티텍처 관점과 소프트웨어 관점에서 다르게 사용 되는 것 같습니다. 이것에 대해 조금 모호할 수는 있지만 카카오 블로그에서 이야기 하는 것은 아키텍처 관점입니다.
아키텍처에선 컴포넌트를 배포 가능한 단위로 봅니다. 가령 라이브러리 혹은 실행파일 등이 될 것입니다.
그런데 글을 작성하신 분의 예시에선 view와 logic을 말씀하시는 것을 보니 소프트웨어 관점으로 보입니다. 뷰만을 가지고 배포 할 수 없고, 로직만 가지고 배포 할 수 없듯 컴포넌트의 범위가 조금 다릅니다.
저도 현업을 하며 배워나가는 단계의 개발자이기 때문에
제 의견이 많이 틀릴 수 있습니다. 다만 블로그의 다른 글들을 보면 공부를 진행 중이신 것 같아 도움이 되었으면 하는 마음에 주제 넘게 조금 적어보았습니다.
글 잘 보고 갑니다.
좋은 하루 보내시기 바랍니다.