ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 디자인 린트는 무엇을 검증하고 무엇을 놓치나 — 토큰 표준이 안정판에 도달한 뒤에도 남은 것
    IT 2026. 8. 12. 22:00

    "AI가 만든 화면이 디자인대로 나왔는가"를 자동으로 확인하는 방법 중, 지금 실제로 가장 잘 작동하는 것은 디자인 린트다. 색과 간격이 약속된 값에서 나왔는지를 코드 단계에서 기계가 검사한다. 최근 1년 사이 이 갈래에 큰 사건이 하나 있었고, 그 사건이 무엇을 해결했고 무엇을 그대로 남겨뒀는지가 이 글의 주제다.

    결론부터 적으면 이렇다. 디자인 린트는 "이 값이 맞는 값인가"를 판정하지 않는다. "이 값이 허용 목록에서 나왔는가"를 판정한다. 이 둘의 차이는 사소해 보이지만, 자동화가 어디까지 갈 수 있는지의 경계선이 정확히 여기에 그어진다. 그리고 웹이 아닌 기기 환경으로 넘어가면 이 경계선이 한 칸 더 뒤로 밀린다.

    최근 1년의 진전 — 토큰의 '표현'이 표준화됐다

    2025년 10월 28일, W3C 커뮤니티 그룹인 Design Tokens Community Group이 디자인 토큰 명세의 첫 안정 버전(2025.10)을 발표했다. 수년간의 초안 상태를 끝내고 벤더 중립 포맷이 확정된 것이다. 이 명세는 테마 전환, 최신 색 공간, 도구 간 상호운용을 표준으로 규정한다.

    왜 이게 린트 이야기에 중요한가? 린트가 "허용 목록에서 골랐는가"를 검사하려면 허용 목록이 먼저 있어야 하고, 그 목록이 디자인 도구와 웹과 안드로이드와 iOS에서 같은 것을 가리켜야 한다. 표준 이전에는 팀마다 토큰 포맷이 달랐고, 그래서 린트 규칙도 팀마다 새로 짜야 했다. 표준 안정판은 이 바닥을 깔아준 사건이다. Style Dictionary 같은 변환 도구가 하나의 토큰 파일에서 웹 CSS 변수, 안드로이드 Compose 테마, iOS SwiftUI 상수를 함께 뽑아낼 수 있게 됐다.

    다만 표준화된 것은 토큰을 어떻게 적어두는가이지 어떤 토큰이 옳은가가 아니다. 이 구분이 이 글 전체를 관통한다.

    웹에서의 강제 — 코드에 박힌 값을 거부한다

    웹에서 디자인 린트는 성숙해 있다. 대표적인 형태는 정적 분석기 규칙으로, 코드에 색상값이나 간격값을 직접 적으면 경고를 띄우고 승인된 토큰을 쓰도록 유도한다. 오픈소스 쪽에서는 Figma 파일에서 스타일이 빠진 레이어를 훑는 플러그인, React 코드베이스에서 디자인 시스템 이탈을 집계하는 명령줄 도구 같은 것들이 나와 있다.

    // 개념 예시 — 정적 분석기가 "허용 목록 밖의 값"을 거부하는 방식
    // 핵심은 판정 대상이 '값' 자체라는 점이다. 의미는 보지 않는다.
    module.exports = {
      create(context) {
        return {
          // JSX/CSS-in-JS 안의 리터럴 색상값을 찾는다
          Literal(node) {
            if (typeof node.value !== 'string') return;
    
            // #RRGGBB 같은 하드코딩 색상 → 위반
            // 토큰 참조(var(--color-...) 또는 tokens.color.*)만 통과시킨다
            if (/^#[0-9a-f]{3,8}$/i.test(node.value)) {
              context.report({
                node,
                message: '하드코딩 색상. 승인된 디자인 토큰을 사용하세요.',
              });
            }
          },
        };
      },
    };
    

    이 규칙이 무엇을 하는지 정확히 보자. 코드에 #0052CC가 있으면 막고 color.background.brand를 쓰게 한다. 그런데 개발자가 color.background.brand 대신 color.background.danger를 썼다면 어떻게 될까? 규칙은 통과시킨다. 둘 다 승인된 토큰이기 때문이다. 브랜드 파랑이 들어갈 자리에 경고 빨강이 들어가도, 형식 검사는 아무 말도 하지 않는다. 린트가 보는 것은 "목록 안에 있는가"뿐이고, "이 자리에 맞는가"는 애초에 검사 대상이 아니다.

    기기 환경에서는 경계선이 한 칸 더 밀린다

    웹 린트가 이만큼이라도 하는 데는 조건이 하나 있다. 웹은 실행 중에 화면의 상태를 열어 볼 수 있다. 문서 객체 모델과 계산된 스타일이 런타임에 노출되므로, 정적 코드 검사에 더해 "실제로 화면에 적용된 값"까지 대조할 수 있다. 코드에는 토큰을 썼는데 상속·재정의 때문에 엉뚱한 값이 최종 적용된 경우를 잡아낼 여지가 있다는 뜻이다.

    네이티브 앱에는 그 창구가 없다. 실행 중인 화면의 최종 스타일을 외부에서 조회할 표준 통로가 사실상 없고, 그래서 검사는 소스 코드 정적 분석으로 후퇴한다. 안드로이드 쪽에서 실제로 쓰이는 방식이 정확히 그 모습이다.

    // 개념 예시 — Kotlin 정적 분석기 커스텀 규칙
    // 컴파일은 멀쩡히 되지만 디자인 시스템을 벗어나는 코드를 잡는다.
    class HardcodedDesignValue : Rule() {
    
        override fun visitDotQualifiedExpression(expr: KtDotQualifiedExpression) {
            // Color.Red 처럼 프레임워크 기본 팔레트를 직접 참조 → 위반
            if (expr.text.startsWith("Color.") && expr.text !in allowedTokenRefs) {
                report(CodeSmell(issue, Entity.from(expr), "테마 토큰을 사용하세요"))
            }
        }
    
        override fun visitBinaryExpression(expr: KtBinaryExpression) {
            // 24.dp 같은 숫자 리터럴 간격 → 위반 (Spacing.md 등으로 유도)
            if (DP_LITERAL.matches(expr.text)) {
                report(CodeSmell(issue, Entity.from(expr), "간격 토큰을 사용하세요"))
            }
        }
    }
    

    이 방식은 잘 작동하지만, 검사 시점이 빌드 전으로 고정된다는 대가를 치른다. 런타임에 테마가 갈리는 경우 — 다크 모드, 사용자가 키운 글자 크기, 기기별 화면 밀도, 제조사 커스텀 테마 — 는 소스만 봐서는 결과를 알 수 없다. 웹에서는 그나마 실행 후 대조로 일부 메울 수 있던 구멍이, 기기 환경에서는 그대로 열려 있다. 여기에 더해 하나의 토큰 파일이 각 플랫폼 관용구로 변환되는 과정에서 같은 토큰이 플랫폼마다 다르게 보이는 문제가 겹친다. 변환은 표준화됐지만 각 플랫폼의 렌더링 규칙까지 표준화된 것은 아니기 때문이다.

    무엇이 게이트를 통과하고 무엇이 빠져나가나

    diagram

    다이어그램 설명. 맨 위의 "이 자리에는 브랜드 파랑"이라는 사람의 결정이 표준 토큰 파일로 내려오고, 거기서 웹·안드로이드·iOS 세 갈래로 변환된다. 최근 1년의 성과는 이 변환 구간이 표준으로 굳어졌다는 것이다. 그런데 그 아래 검사 단계에서 웹만 두 겹(정적 검사 + 실행 중 계산값 대조)을 갖고, 두 기기 플랫폼은 한 겹(정적 검사)뿐이다. 그리고 어느 경로로 오든 게이트를 통과한 뒤의 결과는 셋으로 갈린다. 초록으로 표시한 "목록 밖의 값·하드코딩·명명 규칙 위반"만 확실히 잡히고, 빨강으로 표시한 두 갈래 — 목록 안에 있지만 그 자리에 틀린 토큰, 그리고 런타임에야 결정되는 테마·글자 크기·화면 밀도의 조합 — 은 그대로 빠져나간다. 표준화가 왼쪽 절반을 정리했지만 오른쪽 절반은 손대지 못했다는 것이 지금의 그림이다.

    정리 — 자동화된 것과 남은 것

    지난 1년의 진전은 분명하다. 토큰 포맷이 표준 안정판에 도달하면서 "허용 목록"이라는 검사의 전제가 플랫폼을 가로질러 공유 가능해졌다. 웹·안드로이드·iOS가 같은 소스에서 나온 토큰을 쓰고, 각 플랫폼의 정적 분석기가 그 목록 이탈을 빌드 단계에서 막는다. 여기까지는 기계가 확실히 지킨다.

    남은 것은 둘이다. 첫째, 의미 판정 — 승인된 토큰들 사이에서 어느 것이 이 자리에 맞는지는 여전히 검사 대상 밖이다. 둘째, 기기 환경의 런타임 공백 — 실행 중 화면 상태를 조회할 통로가 없어 검사가 빌드 전으로 고정되고, 테마·글자 크기·밀도 조합은 사실상 미검증으로 남는다.

    그래서 디자인 린트를 도입할 때의 현실적인 기대치는 "디자인대로 됐는지 확인해 주는 도구"가 아니라 "명백히 아닌 것을 걸러 사람이 볼 양을 줄여주는 도구"다. 이 구분을 흐리면 게이트가 초록불인데 화면은 틀린 상황에서 원인을 못 찾는다. 기계가 무엇을 검사하지 않는지를 정확히 아는 것이, 무엇을 검사하는지 아는 것만큼 중요하다.


    참고한 공개 자료:

    • Design Tokens Community Group — 명세 첫 안정 버전(2025.10) 발표, 2025-10-28 — https://www.w3.org/community/design-tokens/2025/10/28/design-tokens-specification-reaches-first-stable-version/
    • Design Tokens Format Module 2025.10 — https://www.designtokens.org/tr/2025.10/format/
    • Style Dictionary — DTCG 포맷 지원 문서 — https://styledictionary.com/info/dtcg/
    • design-lint — Figma 레이어 스타일 누락 검사 플러그인 (오픈소스) — https://github.com/bylapidist/design-lint
    • ui-drift — React 코드베이스 디자인 시스템 이탈 검사 CLI (오픈소스) — https://github.com/pcabel85/ui-drift
    • Detekt 커스텀 규칙으로 디자인 시스템 강제하기 (커뮤니티 글) — https://www.kmpbits.com/posts/detekt-custom-rules-kmp

    이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.

Designed by Tistory.