Post

아토믹 디자인 변형으로 컴포넌트 모듈화하기

아토믹 디자인 변형으로 컴포넌트 모듈화하기

배경과 목표

단순히 컴포넌트를 만드는 데 그치지 않고, 기획 단계부터 웹 접근성과 개발자 경험을 동시에 개선하고자 했습니다. 기존 프로젝트에서는 컴포넌트가 여기저기 흩어져 위치 파악이 어렵고, 재사용성과 유지보수성이 떨어지는 문제가 있었습니다. 이를 해결하기 위해 아토믹 디자인(Atomic Design)을 변형한 구조를 제안하고, 접근성과 효율성을 모두 잡는 공통 컴포넌트를 설계했습니다.

주요 목표는 다음과 같았습니다.

  • 웹 접근성 확보: 스크린 리더와 키보드 네비게이션으로 모든 사용자가 동등하게 이용 가능하도록
  • 개발자 경험 개선: 컴포넌트 계층화로 위치 파편화를 줄이고 직관적인 구조 제공
  • 재사용성 강화: commondomain으로 분리해 모듈화와 도메인별 독립성 유지

개선 과정

1. 아토믹 디자인 변형 제안

아토믹 디자인의 모듈화 철학을 기반으로, 프로젝트 특성에 맞게 계층을 단순화했습니다.
전통적인 Atom-Molecule-Organism 대신 common(공통 컴포넌트)-shared(조합 컴포넌트)-[도메인명](도메인별 컴포넌트)으로 분류했습니다.

1
2
3
4
5
6
// 구조 예시

src/components/
├── common/         # 기본 단위 컴포넌트 (Button, Input, Switch 등)
├── shared/         # 공통 조합 컴포넌트 (Header, Pagination, Form 등)
└── auth/           # 도메인별 컴포넌트 (SignUpSuccessModalContent 등)
  • common: Button, Input, Switch 같은 작은 단위의 재사용 가능한 컴포넌트
  • shared: common 컴포넌트를 조합해 만든 공통 로직이 포함된 컴포넌트(예: Header, Pagination, Form). 페이지나 도메인에서 반복적으로 사용할 수 있는 조합을 모듈화
  • [도메인명]: auth/SignUpSuccessModalContent, portfolio/PortfolioCard처럼 특정 도메인에 종속된 컴포넌트

설계 의도: 초기에 common만으로 관리했지만, 페이지마다 반복되는 조합(예: 페이지네이션, 폼)이 늘며 효율이 떨어졌어요. 이를 해결하기 위해 shared를 도입해 common을 기반으로 한 중간 계층을 설계했고, 단순히 개별 컴포넌트를 넘어 프로젝트 전반의 UI 패턴을 체계적으로 모듈화하는 계층적 사고를 적용했습니다.

2. 웹 접근성을 고려한 구현과 commonshared 확장

Switch 컴포넌트 (common)

키보드 이벤트와 ARIA 속성을 적용해 기본 단위를 접근성 있게 설계했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 이 코드에서 스타일링 관련 클래스(className 등)는 생략되었습니다.
const Switch = ({ isOn, disabled, label, onToggle }) => {
  return (
    <label>
      <div
        role="button"
        aria-label={`toggle button ${isOn ? 'on' : 'off'}`}
        aria-pressed={isOn}
        tabIndex={0}
        onClick={!disabled ? onToggle : undefined}
        onKeyDown={e => handleKeyDown(e, onToggle, disabled)}
      >
        <span />
      </div>
      {label && <span>{label}</span>}
    </label>
  )
}
  • Enter/Space 키로 토글 가능, aria-pressed로 스크린 리더 지원

TextInput 컴포넌트 (common)

포커스 상태와 오류 메시지를 강화해 접근성을 높였습니다.

1
2
3
4
5
6
7
8
9
10
11
12
// 이 코드에서 스타일링 관련 클래스(className 등)는 생략되었습니다.
const TextInput = forwardRef(({ error, startAdornment, ...props }, ref) => {
  return (
    <div>
      {startAdornment && <span>{startAdornment}</span>}
      <input
        ref={ref}
        {...props}
      />
    </div>
  )
})

Pagination 컴포넌트 (shared)

commonButton을 조합해 재사용성과 접근성을 모두 잡은 계층적 확장

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 이 코드에서 스타일링 관련 클래스(className 등)는 생략되었습니다.
export const Pagination = ({
  currentPage,
  pageButtons,
  hasNextPageGroup,
  hasPreviousPageGroup,
  goToPage,
  goToNextPageGroup,
  goToPreviousPageGroup,
}) => {
  return (
    <div> 
      <Button
        variant="text"
        onClick={goToPreviousPageGroup}
        disabled={!hasPreviousPageGroup}
      >
        <IcChevronLeft />
      </Button>
      {pageButtons.map(page => (
        <Button
          variant="text"
          key={page}
          onClick={() => goToPage(page)}
          aria-label={`${page}번 페이지로 이동`}
          aria-current={currentPage === page ? "page" : undefined}
        >
          {page}
        </Button>
      ))}
      <Button
        variant="text"
        onClick={goToNextPageGroup}
        disabled={!hasNextPageGroup}
      >
        <IcChevronRight />
      </Button>
    </div>
  )
}
  • Button을 재사용해 페이지네이션 로직을 모듈화. common의 단순 UI를 넘어, shared에서 동적 데이터와 사용자 인터랙션을 통합하며 설계의 깊이를 더했습니다.

Form 컴포넌트 (shared)

common 컴포넌트를 조합해 만든 재사용 가능한 폼. 자세한 내용은 React Hook Form을 활용한 폼 상태 관리와 UI 로직 분리에서 확인할 수 있습니다.

성과

  • 웹 접근성 향상: ARIA 속성과 키보드 네비게이션으로 모든 사용자가 동일한 경험을 누릴 수 있게 됨
  • 개발자 경험 개선: common, shared, domain 분리로 컴포넌트 위치가 직관적이고, shared를 통해 중복 조합 로직 감소
  • 유지보수성 강화: 모듈화로 코드 중복 감소, shared를 통해 프로젝트 전반의 UI 패턴을 재사용 가능하게 설계

기획부터 구조 설계, 구현까지 참여하며, 팀의 생산성과 사용자 경험을 동시에 끌어올렸습니다.

This post is licensed under CC BY 4.0 by the author.
-->