생각/javascript

컴포넌트 라이브러리 개발기

kyunghoonk00k 2025. 3. 24. 18:22
반응형

 

1. Accordion부터 Button까지, 다른 라이브러리에서 가져온 장점들

오늘은 제가 개발한 React 컴포넌트 라이브러리에 관한 이야기를 해보려고 합니다. 다양한 컴포넌트 라이브러리들을 연구하면서 각각의 장점을 모으고 제 스타일로 재해석한 과정을 공유하고자 합니다.

Accordion 컴포넌트의 재해석

제 라이브러리의 Accordion 컴포넌트는 여러 유명 라이브러리의 장점을 조합했습니다.

Radix UI에서 가져온 장점

Radix UI의 Accordion은 접근성에 매우 강점이 있습니다. WAI-ARIA 디자인 패턴을 준수하고, 키보드 조작성이 매우 뛰어납니다.

 다음 기능들을 제 라이브러리에 적용했습니다:

  • 헤더 및 패널 관계를 명확히 정의하는 접근성 구
  • aria-expandedaria-controlsaria-disabled 속성을 활용한 스크린 리더 지원
  • 보드 탐색 지원 (Tab, Enter, Space, ArrowDown, ArrowUp, Home, End)
 
// Radix UI의 접근성 코드를 참고하여 구현한 부분
<button
  id={headerId}
  className={`accordion-title ${classNames.title || ""}`}
  style={styles.title}
  onClick={() => !item.disabled && onToggle(index)}
  onKeyDown={handleKeyDown}
  aria-expanded={isOpen}
  aria-controls={panelId}
  aria-disabled={item.disabled}
  disabled={item.disabled}
  tabIndex={item.disabled ? -1 : 0}
>
  {item.title}
  {/* 아이콘 */}
</button>

Mantine에서 가져온 장점

Mantine의 Accordion은 사용자 경험에 초점을 맞춘 방식이 인상적이었습니다. 특히 다음 기능들을 제 라이브러리에 적용했습니다:

  • 직관적인 Props API 구조와 명확한 문서
  •  아코디언 지
  • 애니메이션 커스터마이징이 쉬운 구조

// Mantine 스타일의 컨텍스트 관리 방식을 참고하여 구현
const AccordionContext = createContext<AccordionContextValue | null>(null);

const useAccordion = () => {
  const context = useContext(AccordionContext);
  if (!context) {
    throw new Error("useAccordion must be used within an Accordion");
  }
  return context;
};
 
 

ShadCN UI에서 가져온 장점

ShadCN UI의 가장 큰 특징은 고도로 커스텀 가능한 구조와 Tailwind CSS와의 통합입니다. 다음 요소들을 제 라이브러리에 적용했습니다:

  • 유연한 커스터마이징 시스템
  • 최소한의 스타일링과 확장 가능한 인터페이스
  • 쉬운 테마 적용 가능성

// ShadCN UI의 커스텀 스타일링 접근 방식 참고
const animationStyle = {
  ...styles.content,
  '--accordion-animation-duration': typeof animationDuration === 'number' 
    ? `${animationDuration}ms`
    : `var(--accordion-duration-${animationDuration || 'normal'})`,
};
 
 

Button 컴포넌트의 재해

Button 컴포넌트도 여러 라이브러리의 장점을 조합했습니다.

여러 라이브러리에서 가져온 장점들:

  1. 조적 명확성: Mantine의 깔끔한 컴포넌트 구조를 참고했습니다.
  2. 다양한 변: Material UI와 ShadCN의 다양한 버튼 변형(variant)을 참고했습니다.
  3. 근성 고려: Radix UI의 접근성 기능(aria 속성, 키보드 지원)을 적용했습니다.
  4. 스타일 유연성: emotion/styled에서 영감을 받은 CSS 변수 기반 스타일링 시스템을 구축했습니다.
// 여러 라이브러리의 장점을 조합한 Button 컴포넌트
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(({
  children,
  variant = 'primary',
  size = 'md',
  fullWidth = false,
  isLoading = false,
  loadingText,
  // ... 다양한 옵션들
}, ref) => {
  // 복합적인 클래스명 생성
  const buttonClasses = `
    button 
    button-${variant} 
    button-${size}
    ${fullWidth ? 'button-full-width' : ''} 
    ${isLoading ? 'button-loading' : ''}
    ${rounded ? 'button-rounded' : ''}
    ${elevated ? 'button-elevated' : ''}
    ${className}
  `.trim();

  // 접근성 속성과 함께 반환
  return (
    <button 
      ref={ref}
      className={buttonClasses}
      disabled={disabled || isLoading}
      type={type}
      aria-busy={isLoading}
      aria-disabled={disabled || isLoading}
      {...rest}
    >
      {/* 로딩 상태 및 아이콘 처리 */}
    </button>
  );
});
 
 

2. 트리쉐이킹으로 사용량 최적화하기

트리쉐이킹은 모던 자바스크립트 애플리케이션에서 필수적인 최적화 기법입니다. 제 라이브러리에서는 트리쉐이킹을 적용하여 번들 사이즈를 크게 줄일 수 있었습니다.

트리쉐이킹 적용 전

트리쉐이킹을 적용하기 전에는 라이브러리 전체가 하나의 번들로 제공되었습니다. 이는 사용하지 않는 컴포넌트까지 모두 포함된다는 의미였습니다.

 

트리쉐이킹 적용 후

트리쉐이킹을 적용한 후에는 실제 사용하는 컴포넌트만 번들에 포함되어 크게 최적화되었습니다

를 들어, Button과 Input 컴포넌트만 사용하는 프로젝트의 경우:

  • 트리쉐이킹 전: 520KB (전체 라이브러리)
  • 트리쉐이킹 후: 64KB (Button 28KB + Input 36KB)

약 87.7%의 번들 크기가 감소한 것을 볼 수 있습니다!

3. 트리쉐이킹 적용 과정과 결과

트리쉐이킹 적용 방법

리쉐이킹을 적용하기 위해 다음과 같은 변경을 진행했습니다:

  1. ESM 모듈 방식 채: CommonJS 대신 ESM(ECMAScript Modules) 방식을 사용했습니
// package.json
{
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/esm/index.js",
      "require": "./dist/cjs/index.js"
    }
  },
  "sideEffects": false
}
 
 
  1. 별 컴포넌트 엔트리포인트 제공: 각 컴포넌트를 개별적으로 가져올 수 있도록 했습니다.
// 이전 방식
import { Button, Input, Accordion } from 'react-common-components-library';

// 트리쉐이킹이 가능한 방식
import Button from 'react-common-components-library/Button';
import Input from 'react-common-components-library/Input';
 
 
  1. sideEffects 속성 설: package.json에 "sideEffects": false 추가하여 웹팩에게 사이드 이펙트가 없음을 알렸습니다.
  2. 모듈 구조 최적화: 내부 의존성을 명확히 하고 순환 참조를 제거했습니다.

트리쉐이킹 적용 결과

트리쉐이킹을 적용한 후, 다양한 사용 시나리오에서 번들 크기 변화를 측정했습니다:

| 사용 시나리오 | 트리쉐이킹 전 | 트리쉐이킹 후 | 감소율 |

|------------|------------|------------|-------|

| 전체 라이브러리 | 520KB | 520KB | 0% || Button만 사용 | 520KB | 28KB | 94.6% |

| Input만 사용 | 520KB | 36KB | 93.1% |

| Button + Dialog 사용 | 520KB | 53KB | 89.8% |

| 대규모 앱(10개 컴포넌트) | 520KB | 218KB | 58.1% |

히 소규모 앱이나 특정 컴포넌트만 사용하는 경우 90% 이상의 번들 크기 감소를 달성할 수 있었습니다.

여러 유명 라이브러리의 장점을 연구하고 조합하여 제 라이브러리를 만들었습니다. Radix UI의 접근성, Mantine의 사용자 경험, ShadCN UI의 커스터마이징 기능 등 각 라이브러리의 강점을 취하면서 트리쉐이킹을 통한 최적화까지 적용했습니다.이 과정에서 얻은 가장 큰 교훈은 "좋은 컴포넌트는 단순히 잘 작동하는 것을 넘어, 접근성, 성능, 확장성을 두루 갖추어야 한다"는 것입니다. 앞으로도 계속 발전시 나가겠습니다!

반응형