
Tailwind 접근성 검사 라이브러리를 처음 만들어 npm에 배포해봤다.
프론트엔드 개발자로 일하면서 언젠가는 작은 오픈소스 라이브러리 하나쯤은 직접 만들어 보고 싶다는 생각을 계속 하고 있었다.
처음에는 Tailwind 관련 라이브러리를 만들어볼까 생각했다. 하지만 이미 Tailwind 생태계는 너무 성숙했고, 비슷한 라이브러리를 하나 더 만드는 건 큰 의미가 없다고 느꼈다.
그래서 새로운 걸 만들기보다, 기존 도구들이 잘 해결하지 못하는 문제를 찾아서 해결해보는 방향으로 바꾸었다.
그렇게 시작해서 이번에 처음으로 두 개의 npm 패키지를 배포했다.
- tailwind-a11y : Tailwind CSS 프로젝트에서 접근성(WCAG)을 빌드 타임에 검사하는 CLI
- eslint-plugin-tailwind-a11y : 같은 분석 엔진을 사용하는 ESLint 플러그인
시작하게 된 계기
접근성 검사 도구는 이미 정말 많다.
- eslint-plugin-jsx-a11y
- axe-core
- Lighthouse
- 여러 Contrast Checker
기존 도구들을 하나씩 살펴보면서 공통적으로 아쉬웠던 부분을 하나 찾았는데, 아래 코드같은 케이스들이다.
<div className="bg-white">
<p className="text-gray-400">
Hello
</p>
</div>
실제 Tailwind 프로젝트에서는 부모가 배경색을 가지고 자식이 글자색을 가지는 구조를 자주 볼 수 있다.
내가 조사한 범위에서는 이런 구조를 Tailwind 클래스를 해석해서 빌드 타임에 검사하는 도구는 찾지 못했다.
그래서
"Tailwind 클래스를 실제 CSS 값으로 복원해서 접근성을 검사해 보면 어떨까?"
라는 생각으로 프로젝트를 시작하게 되었다.
무엇을 만들었는가
이번에 만든 패키지는 두 개이다.
tailwind-a11y
CLI에서 실행하는 접근성 검사기이다.
현재는 아래 세 가지 WCAG 규칙을 검사한다.
- 색상 대비 (WCAG 1.4.3)
- 터치 타겟 크기 (WCAG 2.5.8)
- 포커스 인디케이터 (WCAG 2.4.7)
npm
https://www.npmjs.com/package/tailwind-a11y
GitHub
https://github.com/chamroro/tailwind-a11y
eslint-plugin-tailwind-a11y
같은 분석 엔진을 사용하는 ESLint 플러그인이다.
처음에는 CLI만 만들려고 했는데, 클로드로부터 리뷰를 받으면서
"새로운 CLI보다 기존 lint 과정에 자연스럽게 들어가는 편이 채택되기 쉽다." 는 의견을 받았고, 같은 엔진을 감싸는 ESLint 플러그인도 함께 만들게 되었다.
npm
https://www.npmjs.com/package/eslint-plugin-tailwind-a11y
GitHub
https://github.com/chamroro/tailwind-a11y
구현 과정
이번 프로젝트에서 가장 어려웠던 부분은 Babel AST를 이용해 JSX를 분석하고, Tailwind 클래스를 실제 CSS 값으로 복원하는 과정이었다.
색상, spacing, font size 등을 단순하게 문자열 비교하는 것이 아니라 실제 CSS 값으로 해석해야 했기 때문이다.
정적 분석 도구이다 보니 처음부터 지원 범위를 명확하게 정했다.
- 동적인 className은 지원하지 않는다.
- 컴포넌트 경계는 분석하지 않는다.
- 같은 엘리먼트와 직계 부모까지만 분석한다.
- 확신할 수 없는 경우에는 억지로 추론하지 않는다.
기능을 많이 넣는 것보다 무엇을 지원하고 무엇을 지원하지 않는지를 명확하게 하는 것이 더 중요하다고 생각했다. (처음이니까)
구현하면서 만난 버그
구현 과정에서 가장 많이 배운 건 정규식 하나가 얼마나 큰 영향을 줄 수 있는지였다.
아래와 같은 코드에서 문제가 발생했다.
className="bg-white bg-opacity-50"
bg-opacity-50이 실제 배경색처럼 잘못 인식되면서 bg-white를 덮어써 버렸고,
결국 존재하는 접근성 위반을 발견하지 못하는 미탐(False Negative)이 발생했다.
정적 분석 도구에서는 잘못된 경고(False Positive)도 문제지만,
문제가 있는데도 조용히 통과하는 경우가 더 위험하다고 생각했다.
수정한 뒤에는 회귀 테스트를 추가했다.
그런데 뒤에 포커스 인디케이터 기능을 추가하면서 거의 같은 패턴의 버그가 다시 나타났다.
focus:ring-offset-2
focus:bg-opacity-50
이번에는 실제 포커스 스타일이 존재하는 것처럼 잘못 판단하고 있었다.
비슷한 실수를 두 번 반복하면서, 버그를 수정하는 것보다 앞으로 같은 실수를 반복하지 않는 규칙을 만드는 것이 더 중요하다는 걸 많이 배웠다.
AI를 활용한 방식
이 플러그인 코드는 Claude Code가 거의 다 짜줬다.
가장 도움이 되었던 부분은 코드 생성보다 리뷰였다. 구현이 끝날 때마다 새로운 세션을 열어서 프로젝트 맥락을 모르는 상태에서 리뷰를 요청했고, 위에서 언급한 미탐 버그도 그렇게 두 번 발견할 수 있었다.
배포 직전에는 ChatGPT에게도 일부러 비판적인 피드백을 부탁했다.
그 과정에서 직계 부모까지만 보는 스코프의 한계나 검사하지 못한 경우를 사용자에게 더 명확하게 보여줄 필요가 있다는 고견(?)을 받았고,
이를 반영해 --verbose 옵션 추가와 ESLint 플러그인 추가 배포까지 진행하게 되었다.
현재 한계
현재 지원하지 않는 것도 많은데, 컴포넌트 경계 분석, 그리고 동적인 Tailwind 클래스, 커스텀 테마 색상이 있다.
가장 마음에 걸리는건 커스텀 테마 분석인데, 대부분의 기업에서 당연히 사용하는 커스텀 디자인 시스템이 있을것이기에... 토큰을 추적하지 못한다는게 좀 활용 범위에 한계가 크게 걸리는 것 같다. (지원하지 않는 경우에는 가능한 한 이유를 보여주기 위해 --verbose 옵션이 있다) 가능하면 앞으로 조금씩 범위를 넓혀갈 계획이다.
배포 후기
아직 지원하는 규칙도 적고 부족한 점도 많다. 그래도 npm 사이트에 내가 만든 라이브러리가 있다는 것은 기분 좋은 일이다.
마지막으로 혹시 Tailwind 프로젝트를 사용하고 계신다면 한 번 사용해 보시고,
- 현재 스코프가 적절한지
- --verbose 출력이 도움이 되는지
- 추가하면 좋을 접근성 규칙이 있는지
같은 의견을 GitHub Issue나 PR, 혹은 댓글로 남겨주시면 정말 감사하겠습니다.!
'Frontend > 🔨 JS' 카테고리의 다른 글
| Tailwind-a11y 개발 기록 2편 (Babel AST로 Tailwind 접근성 분석기를 만든 과정) (0) | 2026.07.27 |
|---|---|
| new Function로 함수 만들기 (0) | 2026.06.04 |
| 자바스크립트 효율적으로 처리하기 (requestAnimationFrame, requestIdleCallback, Web Workers) (0) | 2026.05.11 |
| getElementsByClassName 함수 구현하기 (+자바스크립트에서 유사배열과 배열, 변환 방법) (0) | 2025.09.03 |
| Three.js 로 움직이는 지구본 만들기 (0) | 2022.10.22 |
댓글