2P by GN⁺ 23시간전 | ★ favorite | 댓글 4개
  • 웹 브라우저에 기본 내장된 라디오 버튼이 단순한 HTML 요소임에도, Shadcn UI 라이브러리에서는 이를 여러 계층의 React 컴포넌트로 재구성함
  • Shadcn의 <RadioGroup><RadioGroupItem>Radix UI의 컴포넌트를 다시 감싸며, lucide-react 아이콘수십 개의 Tailwind 클래스를 사용
  • Radix는 접근성과 커스터마이징을 위해 ARIA 속성을 활용하지만, 실제로는 기본 <input type="radio"> 대신 버튼 요소를 재활용함
  • 단순한 CSS로도 동일한 스타일링이 가능함에도, 이 구조는 수백 줄의 코드와 여러 종속성을 추가해 불필요한 복잡성을 초래
  • 기본 HTML 요소를 재사용하지 않음으로써 성능 저하와 유지보수 부담이 커지고, 웹 개발의 단순함이 훼손됨

Shadcn 라디오 버튼 구조 분석

  • Shadcn은 <RadioGroup><RadioGroupItem> 두 컴포넌트를 통해 라디오 버튼을 구현
    • 각 컴포넌트는 @radix-ui/react-radio-group에서 가져온 프리미티브를 감싸며, lucide-reactCircleIcon을 사용
    • 45줄 이상의 코드와 3개의 외부 import가 포함되어 있으며, 30개 이상의 Tailwind 클래스로 스타일 지정
  • 단순한 원형 표시를 위해 SVG 아이콘 라이브러리를 불러오는 구조
    • CSS의 border-radius<circle> 요소로 대체 가능한 기능임

Radix UI의 역할

  • Shadcn이 사용하는 Radix는 접근성과 커스터마이징 중심의 저수준 UI 컴포넌트 라이브러리
    • Radix의 라디오 그룹 구현은 약 215줄의 React 코드와 7개의 파일을 import
  • Radix는 <button> 요소에 ARIA 속성을 추가해 라디오 버튼처럼 동작하도록 구성
    • 그러나 W3C의 ARIA 사용 제1원칙은 “가능한 경우 기본 HTML 요소를 사용할 것”으로 명시
    • Radix는 이 원칙을 따르지 않고, <input> 대신 버튼을 재활용함
  • <form> 내부에서만 숨겨진 <input type="radio">를 추가하는 구조로, 일관성이 떨어짐

CSS로 가능한 단순한 대안

  • 기본 HTML 라디오 버튼은 appearance: none, ::before, :checked, border-radius 등으로 손쉽게 스타일링 가능
    • 예시 코드에서는 의존성, 자바스크립트, ARIA 속성 없이 완전한 커스터마이징 구현
    • 동일한 효과를 Tailwind로도 구현 가능
  • “라디오 버튼 스타일링은 어렵다”는 인식은 과거의 문제이며, 현재는 순수 CSS만으로 충분한 제어 가능

복잡성의 누적 문제

  • Shadcn과 Radix를 함께 사용하면 두 개의 라이브러리와 수백 줄의 코드를 이해해야 함
    • 단순한 라디오 버튼 하나를 위해 수 KB의 자바스크립트가 추가 로드됨
    • 사용자는 버튼 토글을 위해 JS 파싱과 실행을 기다려야 함
  • 이러한 구조는 인지 부하 증가, 버그 가능성 확대, 웹 성능 저하로 이어짐

단순함으로의 회귀

  • 브라우저는 이미 라디오 버튼을 기본 제공하며, <input type="radio" name="beverage" value="coffee" /> 한 줄로 충분
  • 불필요한 추상화와 중첩된 라이브러리 사용은 웹 개발의 본래 단순성과 효율성을 해침
  • 작은 UI 요소라도 기본 기능을 재활용하는 설계가 유지보수성과 성능 모두에 유리함

react aria의 버튼 컴포넌트 보면 기절할 듯 ㅋㅋ

지루하고 현학적:

function RadioGroup({  
  className,  
  ...props  
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {  
  return (  
    <RadioGroupPrimitive.Root  
      data-slot="radio-group"  
      className={cn("grid gap-3", className)}  
      {...props}  
    />  
  );  
}  
...  

금방 끝남 오래 기억에 남음:

<input type="radio" name="beverage" value="coffee" />  

제가 frontend 분야라 오랫동안 겪은 문제인데, 뭐랄까 해결이 정말 어려운 문제이긴 합니다. 구현은 시대에 따라 계속 바뀌고 있지만 input type으로 해결하지 않는 건 어느 시대나 똑같네요...
웹브라우저의 라디오 체크박스 버튼 동작 흉내내보겠다고 접근성 관련 스펙을 따로 구현하는 건 대체 뭐하자는건지... 모르겠어요... 본문에 있듯 css로도 지금은 대안이 있는데 죽어도 컴포넌트로 구현하겠다고 하는 걸 보면 좀 웃기긴 합니다.

Hacker News 의견들
  • 프론트엔드를 자주 다루지는 않지만, React가 주류가 되던 시점부터 복잡성이 커질 조짐이 보였음
    다른 추상화 계층은 단순화되는 경향이 있지만, React는 그 기반 기술보다 훨씬 복잡한 추상화를 만들어냄
    React만 아는 개발자들이 점점 더 높은 레이어로 쌓아 올리면서 과도하게 설계된 결과를 낳는다고 느낌

    • 이제는 모두가 React가 기본값이라고 당연히 여기는 분위기가 더 문제라고 생각함
      예를 들어 Shadcn이나 Radix는 React 전용 UI 라이브러리인데, 마케팅 문구만 보면 일반적인 UI 라이브러리처럼 보임
    • 나는 15년 넘게 순수 JavaScript와 DOM API로 UI를 만들어왔음
      규모가 커지면 결국 나만의 프레임워크를 만들거나 기존 프레임워크에 불만을 품게 됐는데, React는 그 문제를 어느 정도 해결해줌
      React 자체보다 생태계의 과잉 복잡성이 문제라고 봄. React만 잘 다루면 여전히 즐겁게 쓸 수 있음
    • 이건 React만의 문제가 아니라, 사람들이 현대 CSS를 배우려 하지 않는 게 더 큰 문제라고 생각함
      Tailwind 같은 도구로 CSS를 우회하려고만 함. 나는 React로 상태를 관리하지만, 스타일링은 CSS로 직접 하는 걸 선호함
      팀원들에게 CSS를 배우게 설득하는 게 가장 어려운 일임
    • 추상화는 본래 복잡성을 감추는 철학적 도구여야 하는데, 요즘은 오히려 더 복잡하게 만드는 경우가 많음
      나는 이런 “현대적” 프레임워크를 피하고, 가능한 한 기본 기술을 선호함
    • React의 핵심은 컴포넌트 추상화
      React는 단지 “박스”를 제공하고, 그 안에 무엇을 넣을지는 개발자가 결정함. 그게 React의 진짜 힘임
  • 이 라디오 버튼 예시는 웃기면서도 인상적임
    결과물은 기본 CSS 라디오 버튼과 구분이 안 되는데, 왜 이렇게 복잡하게 만드는지 의문임
    큰 규모의 사이트 중 이런 불필요한 복잡성 없이 만들어진 사례가 있는지 궁금함

    • McMaster-Carr 사이트가 좋은 예시로 자주 언급됨. 관련 Hacker News 스레드도 있음
    • 15년 전 비디오 협업 웹앱을 만들었는데, 프론트엔드는 거의 jQuery 기반의 바닐라 구조였음
      지금보다 코드량은 많았지만, 인터페이스는 즉각 반응하는 속도감이 있었음
      Takeoff 프로젝트 코드 참고 가능
    • “이 사이트는 어때?” — 바로 이 Hacker News 자체가 그런 예시일지도 모름
    • 회사 규모가 커질수록 관리자는 표준화된 스택을 원함
      “React를 선택해서 잘린 사람은 없다”는 말처럼, 안전한 선택이 되어버림
    • UI는 모두가 볼 수 있고 의견이 많기 때문에, 복잡성이 공공재의 비극처럼 커지는 구조임
  • 개발자는 디자인 요구사항에 항상 반박할 수 있음을 기억해야 함
    React Native에서 단순한 레이아웃 문제로 4시간을 낭비하던 개발자에게 “디자인을 조금 바꿔도 되냐”고 물어보라 했더니, 10분 만에 해결됨

    • 나는 요즘 JS 없는 UI 프레임워크들(Pico.CSS, Skeleton, Bulma, Tailwind/daisyUI)을 선호함
      CSS만 잘 써도 충분히 좋은 결과를 얻을 수 있음. 혹시 이런 접근 써본 사람들의 추천이 궁금함
  • 2025년에 가장 큰 실수는 Shadcn을 선택한 것이었음
    Radix를 계속 import하는 걸 보고 첫 번째 경고, radio 컴포넌트를 보고 두 번째 경고를 느낌
    이미 프로젝트가 진행 중이라 포기하고 Copilot으로 수정했지만, 결과적으로 AI 의존도 마음에 들지 않았음
    이전 POC는 훨씬 단순하고 효율적이었음. 언젠가 전부 바닐라 HTML로 다시 만들고 싶음

    • React+NextJS+Tailwind+Shadcn 조합은 복잡함의 끝판왕
      Remix나 React Router 7은 그래도 웹 표준에 가깝게 유지하려는 시도가 있었음
      Tailwind에서 “이건 아니다” 싶었고, 친구들이 리팩터링 후에도 좋다고 하면 그때 다시 볼 생각임
    • 사실 Tailwind와 React는 잘 맞지 않음
      React에는 props 기반의 스타일링이 있는데 굳이 CSS 클래스 덩어리를 쓸 이유가 없음
      접근성 중심이라면 Radix UI만 써도 충분함
  • 브라우저의 <input> 요소, 특히 radio와 select는 커스터마이징이 어렵다는 게 문제임
    기본 라디오 버튼은 모바일에서 사용성이 떨어짐

    • 사실 네이티브 컨트롤도 CSS로 충분히 스타일링 가능함
      모바일에서 어떤 문제가 있었는지 더 구체적으로 알고 싶음
    • 글에서도 CSS로 라디오 버튼을 꾸미는 방법을 설명함. 그게 문제인가?
    • <label>로 감싸고 패딩을 주면 모바일에서도 충분히 쓸 만함
    • “select”만큼은 여전히 스타일링이 까다롭지만, 나머지는 꽤 유연하게 커스터마이징 가능함
  • 대부분의 프로젝트는 좋은 의도로 시작하지만, 어느새 200줄짜리 라디오 버튼 코드와 7개의 import로 가득 차게 됨
    이렇게 코드 부패(code rot) 가 시작됨

  • 최근 daisyUI를 써봤는데 꽤 마음에 듦
    순수 CSS 기반이라 브라우저의 새로운 기능(dialog 등) 을 잘 활용할 수 있음

    • 하지만 접근성 측면에서는 부족함이 많음
      예를 들어 Drawer는 포커스를 가두지 못하고, Accordion은 라디오 버튼을 JS 대체용으로 남용
      이런 이유로 Radix 같은 라이브러리가 복잡해질 수밖에 없음
  • 글의 요지에는 공감하지만, 디자이너가 Figma에서 만든 정확한 스타일을 모든 브라우저에서 동일하게 구현하려면 바닐라 CSS로 가능한지 궁금함
    Radix의 데모 같은 걸 완전히 재현할 수 있을까?

    • 약간의 수정만으로도 꽤 비슷하게 만들 수 있음
      CodePen 예시 참고 가능
    • 결국 그 복잡한 프레임워크 아래에서도 CSS가 핵심
      CSS만 추출해서 간단한 React 컴포넌트에 붙이면 충분함
    • 이 예시처럼 바닐라 input과 동일한 CSS를 적용하면 브라우저 호환성도 좋음
    • 글쓴이 본인이 직접 등장해, “기본 예시를 단순히 Shadcn 스타일에 맞춘 것일 뿐, 원하면 얼마든지 커스터마이징 가능하다”고 밝힘
    • 하지만 어디까지 완벽함을 추구할지 고민됨
      약간의 시각적 불일치를 피하려고 수십 KB의 코드와 유지보수 부담을 추가하는 게 과연 가치 있는가?
      남춘백(백남준)의 말처럼 “너무 완벽하면 신이 화낸다”는 생각이 듦
  • 진짜 비용은 코드가 아니라 온보딩 시간
    새로 합류한 개발자가 Radix 기반의 47줄짜리 라디오 버튼을 이해하려면 몇 주가 걸림
    반면 바닐라 방식은 하루면 만들고 20분이면 설명 가능함
    물론 Figma나 Linear처럼 접근성과 키보드 내비게이션이 중요한 제품이라면 복잡성이 정당화됨

    • 좋은 라이브러리는 내부 구조를 몰라도 쓸 수 있게 해야 하지 않을까 하는 의문이 있음
  • 많은 댓글이 Shadcn을 비판하지만, 나는 오히려 컴포넌트 구조와 재사용성을 잘 장려한다고 생각함
    Shadcn의 핵심은 “컴포넌트를 직접 소유하고 수정하라”는 철학임
    이는 기존 UI 라이브러리와는 근본적으로 다른 접근