생각/javascript

[리액트 컴포넌트 라이브러리 고도화] 전체 컴포넌트 리팩토링 대장정

kyunghoonk00k 2025. 3. 17. 14:33
반응형

음에는 단순히 기능 동하는 컴포넌트들을 만드는 데 초점을 맞췄지만, 점차 접근성, 성능, 사용자 경험 면에서 많은 개선이 필요하다는 것을 깨달았다. 이번 에서는 우 라이브러리의 15개 핵심 컴포넌트를 떻게 전면 개선했는지 종합적으로 공유하려고 한다.

체 컴포넌트 개선 방향

먼저 전체 컴포넌트에 공통으로 적용한 개선 원칙은 다음과 같다:

  1. 접근성 강: 모든 컴포넌트에 WAI-ARIA 표준 적용
  2. 성능 최적: React.memo, useCallback, useMemo 활용
  3. 관된 API: 컴포넌트 간 일관된 Props 구조 설
  4. 유연한 확장성: 컴포지션 패턴 적용
  5. 입 안전성: TypeScript 타입 강화

이런 원칙을 바탕으로 각 컴포넌트별 리팩토링을 진행했다.

기 라이브러리와 벤치마킹

우리는 Chakra UI, Mantine, Material UI, Radix UI 등 인기 있는 라이브러리들을 벤치마킹했다.

| 라이브러리 | 장점 | 단점 | 벤치마킹 포인트 |

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

| Chakra UI | 유연한 API, 접근성 중시 | 번들 크기 큼 | 컴포넌트 API 설계 |

| Mantine | 성능 최적화, 풍부한 기능 | 복잡한 API | 내부 최적화 기법 |

| Material UI | 풍부한 컴포넌트, 안정성 | 커스터마이징 제한적 | 컴포넌트 기능 범위 |

 

Accordion부터 MenuBar까지: 컴포넌트별 개선 내용

1. Accordion 컴포넌트

// 모놀리식 구조
const Accordion = ({ items, defaultOpen }) => {
  const [openIndex, setOpenIndex] = useState(defaultOpen);
  
  return (
    <div className="accordion">
      {items.map((item, index) => (
        <div key={index} className="accordion-item">
          <div 
            className="accordion-header" 
            onClick={() => setOpenIndex(index === openIndex ? -1 : index)}
          >
            {item.title}
          </div>
          {index === openIndex && (
            <div className="accordion-content">
              {item.content}
            </div>
          )}
        </div>
      ))}
    </div>
  );
};

// 컴포지션 패턴 적용
const AccordionRoot = ({ children, type = 'single', defaultValue, value, onValueChange, collapsible }) => {
  const [state, setState] = useState(defaultValue || (type === 'multiple' ? [] : undefined));
  const controlled = value !== undefined;
  const values = controlled ? value : state;
  
  const handleValueChange = useCallback((itemValue, open) => {
    if (!controlled) {
      if (type === 'single') {
        setState(open ? itemValue : (collapsible ? undefined : itemValue));
      } else {
        setState(prev => {
          const next = [...(prev || [])];
          if (open) {
            next.push(itemValue);
          } else {
            const index = next.indexOf(itemValue);
            if (index !== -1) next.splice(index, 1);
          }
          return next;
        });
      }
    }
    
    if (onValueChange) {
      onValueChange(itemValue, open);
    }
  }, [controlled, type, collapsible, onValueChange]);
  
  const contextValue = useMemo(() => ({
    type,
    values,
    handleValueChange,
    collapsible
  }), [type, values, handleValueChange, collapsible]);
  
  return (
    <AccordionContext.Provider value={contextValue}>
      <div className="accordion" data-type={type}>
        {children}
      </div>
    </AccordionContext.Provider>
  );
};

const AccordionItem = ({ children, value, disabled }) => {
  const { type, values, handleValueChange, collapsible } = useContext(AccordionContext);
  const open = type === 'single' ? values === value : values?.includes(value);
  const headerId = `accordion-header-${value}`;
  const contentId = `accordion-content-${value}`;
  
  const toggleItem = useCallback(() => {
    if (!disabled) {
      handleValueChange(value, !open);
    }
  }, [disabled, handleValueChange, value, open]);
  
  const contextValue = useMemo(() => ({
    open,
    disabled,
    toggleItem,
    headerId,
    contentId
  }), [open, disabled, toggleItem, headerId, contentId]);
  
  return (
    <AccordionItemContext.Provider value={contextValue}>
      <div className={`accordion-item ${open ? 'expanded' : ''} ${disabled ? 'disabled' : ''}`}>
        {children}
      </div>
    </AccordionItemContext.Provider>
  );
};

// 다른 하위 컴포넌트들 (AccordionTrigger, AccordionContent 등)...

// 컴포넌트 조합
const Accordion = Object.assign(AccordionRoot, {
  Item: AccordionItem,
  Trigger: AccordionTrigger,
  Content: AccordionContent
});

근성, 애니메이션, 중첩 구조를 대폭 개선했다. Radix UI의 접근성 패턴을 적용하고 Chakra UI의 유연한 API 설계를 참고했다.

이러한 구조적 변화는 순한 코드 팩토링이 아니라 컴포넌트 철학의 변화 미한다. 모놀리식에서 컴포지션 기반으로 전환함으로써 다과 같은 이점을 얻었다:

  1. 연성 향상: 사용자가 원하는 방식으로 컴포넌트를 구성할 수 있다
  2. 확장성 강: 새로운 기능 추가가 존 코드에 영향을 주지 않는
  3. 재사용성 증가: 하위 컴포넌트들을  맥락에서도 용할 수 있다

2. AlertDialog 컴포넌트

 

// 개선 전: 기본적인 알림 다이얼로그
<AlertDialog
  isOpen={isOpen}
  onClose={onClose}
  title="확인"
  content="정말 삭제하시겠습니까?"
  confirmText="삭제"
  cancelText="취소"
/>

// 개선 후: 컴포지션 패턴과 접근성 개선
<AlertDialog open={isOpen} onOpenChange={setIsOpen}>
  <AlertDialog.Content>
    <AlertDialog.Header>
      <AlertDialog.Title>확인</AlertDialog.Title>
      <AlertDialog.Description>정말 삭제하시겠습니까?</AlertDialog.Description>
    </AlertDialog.Header>
    <AlertDialog.Footer>
      <AlertDialog.Cancel>취소</AlertDialog.Cancel>
      <AlertDialog.Action onClick={handleDelete}>삭제</AlertDialog.Action>
    </AlertDialog.Footer>
  </AlertDialog.Content>
</AlertDialog>

Radix UI의 컴포지션 패턴과 Chakra UI의 포커스 관리 기법을 참고하여 접근성과 연성을 개선했다.

3. AspectRatio 컴포넌트

 

// 개선 전: 기본 비율 컴포넌트
<AspectRatio ratio={16/9}>
  <img src="image.jpg" alt="이미지" />
</AspectRatio>

// 개선 후: 성능 최적화 및 확장성 개선
<AspectRatio
  ratio={16/9}
  maxWidth="800px"
  overflow="hidden"
  borderRadius="md"
>
  <img 
    src="image.jpg" 
    alt="이미지"
    loading="lazy"
    style={{ objectFit: 'cover' }}
  />
</AspectRatio>

Mantine의 성능 최적화 기법을 적용하, 더 양한 Props를 지원하도록 확장했다.

4. Avatar 컴포넌트

 

// 개선 전: 기본 아바타
<Avatar src="user.jpg" name="홍길동" />

// 개선 후: 다양한 기능 지원
<Avatar
  src="user.jpg"
  name="홍길동"
  size="md"
  status="online"
  statusPosition="bottom-right"
  fallback={<UserIcon />}
  badge={<Badge>NEW</Badge>}
  onError={handleImageError}
/>

Material UI의 다양한 기능과 Chakra UI의 접근성 패턴을 적용했다.

5. Button 컴포넌트

 

// 개선 전: 기본 버튼
<Button variant="primary" size="md" onClick={handleClick}>
  버튼
</Button>

// 개선 후: 복합적 기능 지원
<Button
  variant="primary"
  size="md"
  leftIcon={<SearchIcon />}
  rightIcon={<ArrowIcon />}
  isLoading={loading}
  loadingText="처리 중..."
  loadingPosition="start"
  elevated
  rounded
  fullWidth
  onClick={handleClick}
>
  검색하기
</Button>

Mantine의 아콘 포셔닝, Chakra UI의 딩 상태 리, Material UI의 엘리이션 과 등 다양한 라이브러리의 장점을 통합했다.

6. Checkbox 컴포넌트

 

// 개선 전: 기본 체크박스
<Checkbox checked={checked} onChange={handleChange}>
  동의합니다
</Checkbox>

// 개선 후: 확장된 기능
<Checkbox
  checked={checked}
  onChange={handleChange}
  indeterminate={someChecked}
  size="md"
  colorScheme="brand"
  disabled={disabled}
  required
  error={error}
  helperText="옵션을 선택하세요"
>
  이용약관에 동의합니다
</Checkbox>

Mantine의 indeterminate 상태 관리, Chakra UI의 사이즈 형 등을 적용했다.

7. Collapsible 컴포넌트

// 개선 전: 기본 컬랩서블
<Collapsible open={open} onToggle={setOpen}>
  <CollapsibleTrigger>더 보기</CollapsibleTrigger>
  <CollapsibleContent>추가 내용</CollapsibleContent>
</Collapsible>

// 개선 후: 향상된 애니메이션과 접근성
<Collapsible 
  open={open} 
  onOpenChange={setOpen}
  animationDuration={250}
  easing="ease-in-out"
>
  <Collapsible.Trigger asChild>
    <Button variant="ghost">
      {open ? '접기' : '더 보기'}
      <ChevronIcon direction={open ? 'up' : 'down'} />
    </Button>
  </Collapsible.Trigger>
  <Collapsible.Content>
    <Card>추가 내용이 여기에 표시됩니다</Card>
  </Collapsible.Content>
</Collapsible>

 

Radix UI의 asChild 패턴과 Framer Motion의 애니메이션 기법을 적용했다.

8. Command 컴포넌

 

// 개선 전: 기본 커맨드 팔레트
<Command>
  <CommandInput placeholder="검색..." />
  <CommandList>
    <CommandGroup heading="추천">
      <CommandItem value="docs">문서</CommandItem>
      <CommandItem value="settings">설정</CommandItem>
    </CommandGroup>
  </CommandList>
</Command>

// 개선 후: 키보드 내비게이션 개선 및 검색 알고리즘 최적화
<Command
  onSelect={handleSelect}
  filter={customFilter}
  recentItems={recentItems}
  maxSuggestions={5}
>
  <Command.Input
    placeholder="명령어 실행 또는 검색..."
    leftIcon={<SearchIcon />}
    autoFocus
  />
  <Command.List>
    <Command.Empty>결과 없음</Command.Empty>
    <Command.Group heading="최근 항목">
      {recentItems.map(item => (
        <Command.Item
          key={item.id}
          value={item.value}
          shortcut={item.shortcut}
          icon={item.icon}
          disabled={item.disabled}
        >
          {item.label}
        </Command.Item>
      ))}
    </Command.Group>
    <Command.Separator />
    <Command.Group heading="추천 항목">
      {/* 추천 항목들 */}
    </Command.Group>
  </Command.List>
</Command>

cmdk 라이브러리의 최적화된 검색 알고리즘, Mantine의 키보드 내비게이션, Chakra UI의 접근성 패턴을 결합했다.

9. ContextMenu 컴포넌트

 

// 개선 전: 기본 컨텍스트 메뉴
<ContextMenu>
  <ContextMenuTrigger>마우스 우클릭하세요</ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem>복사</ContextMenuItem>
    <ContextMenuItem>붙여넣기</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>

// 개선 후: 향상된 접근성과 기능
<ContextMenu>
  <ContextMenu.Trigger>마우스 우클릭하세요</ContextMenu.Trigger>
  <ContextMenu.Content>
    <ContextMenu.Item
      icon={<CopyIcon />}
      onClick={handleCopy}
      keyboardShortcut="⌘C"
    >
      복사
    </ContextMenu.Item>
    <ContextMenu.Separator />
    <ContextMenu.CheckboxItem
      checked={bold}
      onCheckedChange={setBold}
    >
      굵게
    </ContextMenu.CheckboxItem>
    <ContextMenu.SubMenu>
      <ContextMenu.SubMenuTrigger>더 보기</ContextMenu.SubMenuTrigger>
      <ContextMenu.SubMenuContent>
        <ContextMenu.Item>하위 메뉴 항목</ContextMenu.Item>
      </ContextMenu.SubMenuContent>
    </ContextMenu.SubMenu>
  </ContextMenu.Content>
</ContextMenu>

Radix UI의 접근성 최적화, Mantine의 키보드 단축키 지원, Material UI의 아이콘 통합 등을 적용했다.

10. Dialog 컴포넌트

// 개선 전: 기본 다이얼로그
<Dialog
  isOpen={isOpen}
  onClose={() => setIsOpen(false)}
  title="프로필 편집"
>
  <form>
    <input type="text" placeholder="이름" />
    <button>저장</button>
  </form>
</Dialog>

// 개선 후: 향상된 컴포지션과 접근성
<Dialog open={isOpen} onOpenChange={setIsOpen}>
  <Dialog.Trigger asChild>
    <Button>프로필 편집</Button>
  </Dialog.Trigger>
  <Dialog.Portal>
    <Dialog.Overlay className="dialog-overlay" />
    <Dialog.Content size="md" centerContent>
      <Dialog.Header>
        <Dialog.Title>프로필 편집</Dialog.Title>
        <Dialog.Description>
          프로필 정보를 수정하세요. 저장하면 즉시 반영됩니다.
        </Dialog.Description>
      </Dialog.Header>
      <form onSubmit={handleSubmit}>
        <div className="form-row">
          <Input label="이름" defaultValue={name} />
        </div>
        <div className="form-row">
          <Input label="이메일" type="email" defaultValue={email} />
        </div>
        <Dialog.Footer>
          <Dialog.Close asChild>
            <Button variant="outline">취소</Button>
          </Dialog.Close>
          <Button type="submit">저장</Button>
        </Dialog.Footer>
      </form>
    </Dialog.Content>
  </Dialog.Portal>
</Dialog>

 

Radix UI의 Portal 턴과 커스 트, Chakra UI의 중앙 정 옵션, Mantine의 사이즈 변형 등을 적용했다.

11. DropdownMenu 컴포넌트

 

// 개선 전: 기본 드롭다운 메뉴
<DropdownMenu>
  <DropdownMenuTrigger>메뉴</DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuItem>항목 1</DropdownMenuItem>
    <DropdownMenuItem>항목 2</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

// 개선 후: 다양한 항목 유형과 키보드 내비게이션
<DropdownMenu>
  <DropdownMenu.Trigger>
    <Button rightIcon={<ChevronDownIcon />}>메뉴</Button>
  </DropdownMenu.Trigger>
  <DropdownMenu.Content align="end" sideOffset={5}>
    <DropdownMenu.Item
      icon={<UserIcon />}
      onClick={handleProfile}
    >
      프로필
    </DropdownMenu.Item>
    <DropdownMenu.Item
      icon={<SettingsIcon />}
      disabled={!canAccessSettings}
    >
      설정
    </DropdownMenu.Item>
    <DropdownMenu.Separator />
    <DropdownMenu.CheckboxItem
      checked={notifications}
      onCheckedChange={setNotifications}
    >
      알림 켜기
    </DropdownMenu.CheckboxItem>
    <DropdownMenu.RadioGroup value={theme} onValueChange={setTheme}>
      <DropdownMenu.RadioItem value="light">라이트 모드</DropdownMenu.RadioItem>
      <DropdownMenu.RadioItem value="dark">다크 모드</DropdownMenu.RadioItem>
      <DropdownMenu.RadioItem value="system">시스템 설정</DropdownMenu.RadioItem>
    </DropdownMenu.RadioGroup>
    <DropdownMenu.Separator />
    <DropdownMenu.Item
      icon={<LogoutIcon />}
      variant="danger"
    >
      로그아웃
    </DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

Radix UI의 다양한 메뉴 항목 유형, Chakra UI의 아이콘 통합, Mantine의 정 옵션 등을 적용했다.

12. HoverCard 컴포넌트

 

// 개선 전: 기본 호버카드
<HoverCard>
  <HoverCardTrigger>사용자</HoverCardTrigger>
  <HoverCardContent>사용자 정보</HoverCardContent>
</HoverCard>

// 개선 후: 포지셔닝 개선 및 아이콘 지원
<HoverCard>
  <HoverCard.Trigger asChild>
    <Button variant="link">김개발</Button>
  </HoverCard.Trigger>
  <HoverCard.Content
    position="bottom"
    openDelay={200}
    closeDelay={100}
    showArrow
    width="300px"
  >
    <div className="user-card">
      <Avatar src="/avatar.jpg" name="김개발" size="lg" />
      <h3>김개발</h3>
      <p>프론트엔드 개발자</p>
      <div className="stats">
        <Stat label="게시물" value={48} />
        <Stat label="팔로워" value={2451} />
        <Stat label="팔로잉" value={123} />
      </div>
      <Button fullWidth>팔로우</Button>
    </div>
  </HoverCard.Content>
</HoverCard>

Radix UI의 포지셔닝 알고리즘, Chakra UI의 연 시간 옵션, Material UI의 카드 디자인 등을 적용했다.

13. Input 컴포넌트

 

// 개선 전: 기본 입력 필드
<Input
  value={value}
  onChange={handleChange}
  placeholder="입력하세요"
/>

// 개선 후: 다양한 기능 지원
<Input
  value={value}
  onChange={handleChange}
  label="이메일"
  placeholder="example@mail.com"
  type="email"
  leftElement={<MailIcon />}
  rightElement={
    <Button variant="ghost" size="sm" onClick={handleClear}>
      <CloseIcon />
    </Button>
  }
  size="md"
  required
  error={error}
  helperText={error ? "올바른 이메일 형식이 아닙니다" : "ex) example@mail.com"}
  autoComplete="email"
  onFocus={handleFocus}
  onBlur={validateEmail}
/>

Mantine의 /우측 요소 지원, Chakra UI의 라 및 도움말 텍스트, Material UI의 오류 상태 등을 적용했다.

14. Label 컴포넌트

 

// 개선 전: 기본 레이블
<Label htmlFor="name">이름</Label>
<input id="name" />

// 개선 후: 향상된 기능
<Label
  htmlFor="name"
  required
  size="md"
  tooltip="사용자 실명을 입력하세요"
>
  이름
  <InfoIcon className="info-icon" />
</Label>
<Input id="name" required />

 

Chakra UI의 툴팁 지원, Mantine의 필수 표시 기능 등을 적용했다.

15. MenuBar 컴포넌트

// MenuBar 컨텍스트 정의
interface MenuBarContextValue {
  activeMenuId: string | null;
  setActiveMenuId: (id: string | null) => void;
  focusedItemIndex: number;
  setFocusedItemIndex: (index: number) => void;
  menuRefs: React.MutableRefObject<Map<string, HTMLElement>>;
  onActivate?: () => void;
  onDeactivate?: () => void;
}

const MenuBarContext = createContext<MenuBarContextValue | null>(null);

// MenuBar 컴포넌트
export const MenuBar = forwardRef<HTMLDivElement, MenuBarProps>(({
  items,
  className = '',
  style,
  width,
  onActivate,
  onDeactivate,
  ariaLabel = '메뉴'
}, ref) => {
  const [activeMenuId, setActiveMenuId] = useState<string | null>(null);
  const [focusedItemIndex, setFocusedItemIndex] = useState<number>(-1);
  const menuRefs = useRef<Map<string, HTMLElement>>(new Map());
  const menuBarRef = useRef<HTMLDivElement | null>(null);

  // 컨텍스트 값 생성
  const contextValue = useMemo(() => ({
    activeMenuId,
    setActiveMenuId,
    focusedItemIndex,
    setFocusedItemIndex,
    menuRefs,
    onActivate,
    onDeactivate
  }), [activeMenuId, focusedItemIndex, onActivate, onDeactivate]);

  // ... 키보드 핸들러 등 구현 ...

  return (
    <MenuBarContext.Provider value={contextValue}>
      <div 
        ref={(node) => {
          // useRef와 forwardRef를 동시에 사용하기 위한 처리
          menuBarRef.current = node;
          
          // forwardRef에 전달
          if (typeof ref === 'function') {
            ref(node);
          } else if (ref) {
            // @ts-ignore - ref가 React.MutableRefObject 타입일 때 할당
            ref.current = node;
          }
        }}
        className={menuBarClasses}
        style={{ 
          width: width,
          ...style
        }}
        role="menubar"
        aria-label={ariaLabel}
        onKeyDown={handleKeyDown}
      >
        {items.map((item, index) => (
          <MenuBarItem
            key={item.id}
            {...item}
            index={index}
          />
        ))}
      </div>
    </MenuBarContext.Provider>
  );
});

보드 탐색, 포커스 관리 을 대폭 개선했다. Radix UI의 접근성 패, Mantine의 키보드 내비게이션 등을 적용했다.

 

앞으로의 과제

직 개선해야 할 부분도 있다:

  1. 크 모드 지원 강화: 모 컴포넌트의 다크 모드 일관성 유지
  2. RTL(오른쪽에서 왼쪽) 지: 다양한 언어 환경 지원
  3. 니메이션 최적화: 저사양 기기에서도 부드러운 UX 제공
  4. 서화 개선: 더 많은 예제와 사용 지침 제공

마치

 

좋은 컴포넌트 드란 무엇인가?

 선 과정을 통해 "좋은 컴포넌트 코드"에 대한 깊은 고 찰을 얻게 되었다.

1. 접근성을 드의 핵심으

좋은 컴포넌트는 근성을 나중에 추가하는 것이 아니라, 처음부터 설계의 핵심으로 려한다.

근성이 뛰어난 컴포넌트는 키보드 용자, 스크린 리더 사용자, 다양한 장치 사용자  모든 사용자에게 동등한 경험을 제공한다.

 

2. API 설계의 

좋은 컴포넌트 API는 단순함과 유연성 사이의 균형을  맞춘다.

 

이상적인 API는 음과 같은 특성을 갖는다:

  1. 리적인 기본값: 대부분의 사 사례를 커버하는 좋은 기본
  2. 진적 복잡성: 기본 사용은 간단하고, 필요에 따라 고급 기능 추가
  3. 관된 : 라이브러리 전체에서 동일한 패턴 유

3. 상태 관리의 명확성

좋은 컴포넌트는 상태 관리 명확하고 측 가능하다.

좋은 상태 관리는 다음 원칙을 따른다:

  1. /비제어 모드 명확화: 어떤 모드인지 확히 구분
  2. 향 데이터 흐름: 상태 변경의 방향과 임이 명확
  3. 용 최소화: 의도하지 않은 상호작용이나 부작용 방지

4. 성능을 위한 최적화

은 컴포넌트는 불필요한 렌더링과 산을 방지다.

능 최적화 핵심 원칙:

  1. 요한 만 계: 변 이터에 해서만 재계산
  2. 요한 것만 렌더링: 변경된 분만 리렌더링
  3. 모리 능의 균형: 도한 메모제이션 방지

5. 확장성과 결합도

은 컴포넌트는 낮은 결합도와 높은 확장성을 가진다.

 

확장성 좋은 컴포넌트 특징.

  1. 선택적 렌더링 함수: 내용 일부 또는 전체를 커스터마이징 가능
  2. 슬롯 기반 API: 컴포넌트의 정 부분만 대체 가
  3. 합성 패턴: 여러 작은 컴포넌트를 합해 복잡한 UI 

Accordion, MenuBar를 롯한 우 컴포넌트 이브러리의 개선 과정은 단순한 코드 업데이트가 아니라 "좋은 컴포넌트 무엇인가"에 대한 깊은 탐구의 정이었다.좋은 컴포넌트는 다음과 같은 특성을 갖추고 있다:

  1. 든 사용자를 위한 접근성: 처음부터 설계에 통합된 접근성
  2. 관적이고 유연한 API: 단순하지만 확장 가능한 터페이스
  3. 측 가능한 상태 관: 명확하고 일관된  흐름
  4. 적화된 성: 불필요한 계산과 렌더링 방지
  5. 낮은 결합: 다양한 상황에서 재사용 가능한 설

리 라이브러리는 이런 원칙들을 지키며 계속 발전하고 있다. 앞으로도 사용자 피드백과 최신 웹 표준을 반영하여 더 좋은 컴포넌트를 만들 위해 노력할 것이. 다음 글에서는 Dialog, Select, Dropdown 컴포넌트의 개선 사례를 통해 더 복잡한 인랙션 처리 방법을 공유하겠다.

반응형

'생각 > javascript' 카테고리의 다른 글

트리쉐이킹(Tree Shaking) 테스트 결과 보고서  (0) 2025.03.25
컴포넌트 라이브러리 개발기  (0) 2025.03.24
순열과 조합(코딩테스트)  (0) 2025.02.21
코딩테스트 알고리즘 정리  (0) 2025.02.17
Chart.js  (0) 2023.06.23