CSS를 오래 작성하다 보면 반드시 마주치는 문제가 있습니다. 바로 스타일 우선순위 충돌입니다. 외부 라이브러리의 스타일이 내 스타일을 덮어쓰거나, 컴포넌트 스타일이 글로벌 스타일과 충돌하는 상황 말이죠. 이를 해결하기 위해 !important를 남발하다 보면 코드는 금방 엉망이 됩니다.

CSS @layer는 이 문제를 우아하게 해결하는 현대적인 방법입니다.

CSS 캐스케이드 복습

브라우저가 어떤 스타일을 적용할지 결정하는 순서는 다음과 같습니다.

  1. 출처 (브라우저 기본값 < 작성자 스타일 < 사용자 스타일)
  2. 레이어 순서 ← @layer가 여기에 개입
  3. 명시도 (specificity)
  4. 작성 순서

@layer 기본 사용법

레이어를 선언하고 각 레이어에 스타일을 배치합니다. 나중에 선언된 레이어가 더 높은 우선순위를 갖습니다.

/* 레이어 순서 먼저 선언 (선택 사항이지만 권장) */
@layer reset, base, components, utilities;

@layer reset {
  *, *::before, *::after { box-sizing: border-box; margin: 0; }
}

@layer base {
  body { font-family: sans-serif; color: #333; }
  a { color: blue; }
}

@layer components {
  .card { border: 1px solid #e5e7eb; border-radius: 0.75rem; }
  .btn  { padding: 0.5rem 1rem; border-radius: 0.4rem; }
}

@layer utilities {
  .text-center { text-align: center; }
  .mt-4 { margin-top: 1rem; }
}

이 예시에서 우선순위는 utilities > components > base > reset 순입니다.

외부 라이브러리 다루기

@layer의 진짜 강점은 외부 CSS를 레이어에 넣는 것입니다. 레이어 안의 스타일은 레이어 밖의 스타일보다 항상 낮은 우선순위를 갖습니다.

/* Bootstrap을 레이어에 가두기 */
@import url('bootstrap.css') layer(bootstrap);

/* 이제 내 스타일이 항상 Bootstrap을 이깁니다 */
.btn {
  background-color: #6891f8; /* !important 없이도 적용됨 */
}
레이어 밖의 스타일은 모든 레이어보다 높은 우선순위를 가집니다. 즉, 레이어에 넣지 않은 내 커스텀 스타일은 항상 이깁니다.

중첩 레이어

레이어 안에 레이어를 만들 수도 있습니다. 디자인 시스템 구조화에 유용합니다.

@layer components {
  @layer base {
    .btn { padding: 0.5rem 1rem; }
  }

  @layer variants {
    .btn-primary { background-color: #6891f8; color: white; }
    .btn-danger  { background-color: #ef4444; color: white; }
  }
  /* variants > base */
}

실전: Tailwind + 커스텀 CSS

Tailwind CSS와 함께 사용하면 유틸리티 클래스가 컴포넌트 스타일을 덮어쓰는 문제를 깔끔하게 해결할 수 있습니다.

/* Tailwind를 레이어에 가두기 */
@import "tailwindcss" layer(tailwind);

/* 내 컴포넌트 스타일 (레이어 밖 → 항상 이김) */
.card {
  border-radius: 1rem;
  box-shadow: 0 4px 16px rgba(0,0,0,0.1);
}

브라우저 지원

Chrome 99+, Firefox 97+, Safari 15.4+ 이상에서 지원합니다. 2022년부터 모든 모던 브라우저가 지원하므로 실무에서 사용해도 안전합니다.

핵심 정리
@layer는 CSS 우선순위를 명시적으로 관리하는 도구입니다. 레이어 순서를 선언부에서 한 번에 제어하고, 외부 라이브러리는 레이어 안에 가두세요. !important와의 이별을 선언할 시간입니다.

레이어 이름 규칙과 아키텍처

@layer를 효과적으로 관리하려면 처음부터 명확한 이름 규칙과 레이어 순서를 정의해야 합니다. ITCSS(Inverted Triangle CSS) 방법론과 함께 사용하면 좋습니다.

/* 전역 styles/layers.css에서 레이어 순서 선언 (가장 먼저 임포트) */
@layer
  reset,          /* 브라우저 기본 스타일 초기화 */
  tokens,         /* 디자인 토큰 (색상, 타이포, 간격) */
  base,           /* 기본 HTML 요소 스타일 */
  vendors,        /* 외부 라이브러리 */
  layout,         /* 페이지 레이아웃 구조 */
  components,     /* 재사용 UI 컴포넌트 */
  utilities,      /* 유틸리티 클래스 */
  overrides;      /* 일회성 덮어쓰기 */

/* 각 레이어에서 선언 */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  body { margin: 0; }
}

@layer tokens {
  :root {
    --color-primary: oklch(58% 0.22 264);
    --space-4: 1rem;
  }
}

DevTools에서 레이어 디버깅

Chrome DevTools 122+에서 @layer를 시각적으로 확인하고 디버깅할 수 있습니다.

  • Elements 패널 → Styles 탭 — 적용된 스타일 옆에 레이어 이름이 표시됩니다. 어느 레이어의 규칙이 적용됐는지 한눈에 파악 가능
  • 레이어 순서 확인 — "Cascade Layers" 섹션에서 현재 페이지의 레이어 순서와 각 레이어의 규칙 수 확인
  • 오버라이드 추적 — 취소선이 그어진 스타일에서 어느 레이어가 어느 레이어를 덮어쓰는지 추적

@layer와 !important의 상호작용

!important는 레이어 시스템에서 예상과 반대로 동작합니다. 이 규칙을 이해하지 못하면 디버깅이 매우 어려워집니다.

@layer base, theme, utilities;

@layer base {
  p { color: blue !important; }   /* !important는 레이어 순서를 역전시킴 */
}

@layer utilities {
  p { color: red; }               /* 일반 선언은 utilities가 이김 */
}

/* 결과: base의 !important가 utilities의 일반 선언을 이김
   즉 p 색상 = blue */

레이어 시스템에서 !important는 레이어 우선순위를 역전시킵니다. 낮은 레이어의 !important가 높은 레이어의 일반 선언을 이기게 됩니다. 이 때문에 레이어 내부에서 !important 사용은 최소화하는 것을 강력히 권장합니다.

기존 프로젝트 마이그레이션 전략

@layer 없이 운영 중인 기존 프로젝트를 단계적으로 마이그레이션하는 방법입니다.

  • 1단계 — 외부 라이브러리만 레이어로 감싸기: @layer vendors { @import 'bootstrap.css'; }
  • 2단계 — 기존 커스텀 CSS를 @layer overrides { }로 래핑 (레이어 없는 코드보다 낮은 우선순위)
  • 3단계 — 새로 추가하는 코드는 적절한 레이어에 배치
  • 4단계 — 기존 코드를 점진적으로 적절한 레이어로 분류

한꺼번에 마이그레이션하면 예상치 못한 스타일 변경이 발생할 수 있습니다. 레이어 없는 코드는 레이어 있는 코드보다 항상 높은 우선순위를 가지므로, 기존 코드를 임의의 레이어에 넣으면 우선순위가 낮아질 수 있습니다.

@layer가 실제로 해결하는 문제

@layer가 도입되기 전, CSS 프로젝트가 커질수록 개발자들이 가장 자주 겪는 고통은 특이성(Specificity) 전쟁이었습니다. Bootstrap 같은 CSS 프레임워크를 사용하면서 특정 스타일을 덮어쓰려면 더 높은 특이성의 선택자를 작성하거나 !important를 남발해야 했습니다. 이것이 쌓이면 CSS는 점점 예측 불가능해지고, 한 곳을 수정하면 예상치 못한 곳이 깨지는 일이 반복됩니다. @layer는 이 문제를 구조적으로 해결합니다. 레이어 선언 순서가 곧 우선순위가 되기 때문에, 특이성이 낮은 선택자라도 더 높은 레이어에 있으면 이깁니다. 이 덕분에 컴포넌트 스타일을 단순하게 유지하면서도 필요한 곳에서 확실하게 재정의할 수 있습니다.

디자인 시스템을 구축하는 팀에서 @layer의 효과가 특히 큽니다. 기본 토큰 레이어, 컴포넌트 레이어, 테마 레이어, 페이지별 오버라이드 레이어처럼 계층을 명확히 나누면, 어떤 스타일이 어디서 오는지 즉시 파악할 수 있습니다. 새로운 팀원이 합류했을 때도 레이어 구조를 이해하면 전체 CSS 아키텍처를 빠르게 파악할 수 있어 온보딩이 쉬워집니다. 2024년 이후 모든 주요 브라우저에서 @layer를 지원하므로, 신규 프로젝트에서는 처음부터 레이어 아키텍처를 설계에 포함하는 것을 강력히 권장합니다.