경력·사례·글은 한국어 원문으로 제공됩니다.

← 모든 기록

기술 글 / 개발 방식

Tailwind·shadcn 공통 UI 수정과 사용 화면 추적

CVA·Radix Slot을 쓰는 공통 Button과 Tailwind 토큰의 소비 경로를 따라갔다. color-mix로 대비를 고친 뒤 CSS 생성, 서비스 화면, Button fixture 결과를 나눠 확인했다.

작업 2026.10

약 7분 소요2,987자

#같은 저장소에 있어도 같은 버튼을 쓰지는 않았다

하나의 저장소에서 공통 코드를 다루는 일을 정리한 뒤에도 서비스별 화면은 계속 달라졌다. Tailwind와 shadcn, NativeWind를 정리할 때도 공통 설정만 바꾸고 끝낼 수는 없었다.

Tailwind는 클래스에 맞는 CSS를 만들고, shadcn 패턴으로 둔 공통 컴포넌트는 그 클래스를 조합한다. 이 저장소의 Button은 class-variance-authority의 cva()로 variant와 크기를 고르고, asChild일 때 Radix Slot으로 자식 요소에 속성을 합쳤다. CSS가 생성되는지, 컴포넌트가 그 CSS를 쓰는지, 실제 화면에서 의도한 모양으로 보이는지를 나눠 봤다. 같은 공통 패키지를 의존한다고 해서 바꾼 버튼이 모든 페이지에 나타나는 것은 아니었다.

공통 Button과 ThemeToggle을 수정한 뒤에는 실제 소비 경로를 찾았다. 앱의 진입 파일에 보이지 않더라도 기능 패키지 안에서 가져와 쓰는 경우가 있었다. 반대로 같은 저장소에 있지만 이번에 바꾼 컴포넌트를 렌더링하지 않는 서비스도 있었다.

#색상 토큰에서 버튼까지 따라갔다

공통 theme의 @theme inline은 --primary 같은 CSS 변수를 Tailwind 색상 utility에 연결했다. 그 utility를 Button이 소비하고, 서비스 화면이 Button을 렌더링하는 순서다. theme 파일만 읽으면 마지막 단계는 알 수 없었다. 변수 연결 방식은 Tailwind theme 문서에서 확인할 수 있다.

이번 대비 수정에서는 primary 버튼 배경과 link 글자색에 기존 primary 65%와 foreground 35%를 섞었다. primary hover는 55%와 45%로 바꿨고, focus ring은 foreground를 사용했다. 실제 primary 클래스의 핵심은 다음 한 줄이다.

TEXT

bg-[color-mix(in_oklab,var(--primary)_65%,var(--foreground))]

새 색상표를 각 서비스에 복사하는 대신 기존 테마 변수로 계산했다. fixture의 라이트 primary 글자 대비는 2.43:1에서 4.67:1, hover는 2.22:1에서 5.67:1로 바뀌었다. 이 값은 해당 fixture 조건의 전후 비교이며 모든 서비스 버튼의 대비 수치는 아니다.

cva()는 정해 둔 variant의 클래스를 골라줄 뿐 실제 사용 페이지를 찾아주지는 않는다. Slot도 자식 요소에 속성을 합치는 도구다. 공개 상위 구현은 CVA, 사용 방식은 Radix Slot 문서에서 볼 수 있다. 이 링크들은 이번 저장소의 Button 소스를 공개한 주소가 아니다.

#경고를 없애기 전에 용도를 봤다

서비스의 스타일을 모두 하나의 방식으로 치환하지는 않았다. 블로그의 본문·인쇄 스타일, 차트, 게임 화면의 연출 CSS는 각자 맡은 일이 있었다. 계산값이나 애니메이션, 네이티브에서 필요한 style도 따로 살폈다.

lint가 잡아낸 항목에는 실제로 잘못된 값과 의도한 예외가 함께 있었다. 예외를 무조건 제거하면 화면이 달라질 수 있었다. 유효한 예외는 좁게 남기고, 바꿔야 할 후보는 어느 화면에서 다시 확인할지 적었다.

경고 수가 줄었다는 숫자만으로 화면이 좋아졌다고 말하지 않기로 했다. 생성되지 않는 클래스를 고쳤다면 실제 CSS가 나오는지 확인하고, 버튼 대비를 바꿨다면 버튼이 보이는 화면에서 봐야 했다.

#직접 본 범위만 완료로 적었다

ThemeToggle은 선택한 5개 서비스에서 화면 너비 두 가지와 테마 두 가지를 조합해 20조건을 직접 확인했다. 공통 Button은 별도의 상태별 시험 화면인 fixture에서 24개 variant 조합·72상태를 살폈다. 서비스 화면의 테마 버튼과 공통 버튼의 시험 결과를 따로 남겼다.

Vote 웹 글꼴과 RGB 색상 값도 고쳤다. 이 결과를 모든 서비스의 실제 Button 화면을 검증한 것으로 넓히지는 않았다.

처음부터 최종 코드의 화면이 나온 것은 아니었다. 한 서비스에서는 전송한 파일이 새 코드인데도 개발 패키지 캐시가 이전 코드를 반환했다. 그 결과는 최종 증빙에서 빼고, 작업용 컨테이너를 재시작한 뒤 실제 DOM에 새 클래스가 나타나는지 확인해 다시 검사했다.

NativeWind는 네이티브 화면 쪽의 스타일 경로로 따로 살폈다. 설치돼 있다는 이유로 웹의 Button 수정이 모든 앱 화면에 적용됐다고 보지는 않았다. 추가 서비스에서는 필요한 utility가 생성되는지와 공통 컴포넌트의 소비 경로를 확인했다. 이 정적 확인을 실제 화면 검증에 합치지는 않았다. 별도 기능 패키지에서 공통 버튼을 쓰는 화면은 그 경로를 따라 후속 대상으로 남겼다.

자동 회귀가 통과한 앱에도 실기기에서 봐야 할 항목은 남았다. 초대의 만료나 앱 복귀, 글자 확대, 네이티브 그림자와 글꼴처럼 정적 검사만으로 끝내기 어려운 항목이었다. 다른 테스트가 로딩 오류로 실행되지 않았다면 버튼 검증 실패와도 구분해서 기록했다.

#한꺼번에 끝내려 하지 않았다

10월 24일에는 선택한 공통 작업을 마치고 서비스별 재개표를 남겼다. 작업 종류를 나눠 로컬 커밋했고, 그 시점에 운영 배포나 OTA를 한 것은 아니었다.

남은 항목을 모두 다음 작업의 선행조건으로 만들지는 않았다. 해당 화면을 고치거나 그 변경을 포함한 출시를 검증할 때, 관련 항목을 함께 확인하도록 했다. 공통 코드가 다시 바뀌거나 새로운 회귀가 발견되면 영향을 받는 소비자를 다시 찾는 방식이다.

전체 서비스의 디자인을 완료한 기록은 아니다. 이번에 고친 컴포넌트가 어디에 닿는지 확인하고, 직접 본 화면과 아직 보지 못한 화면을 구분한 기록이다. 공통 코드의 변경량보다 그 코드를 사용하는 곳을 아는 일이 검증 범위를 정하는 데 도움이 됐다.

작성 2026-11-03

2026년 10월 23~24일 공통 UI 작업 기록을 정리한 기록이다. 당시 선택 범위의 결과이며 전체 서비스 도입·디자인·출시 검증 완료를 뜻하지 않는다.