Logo

CSS의 @layer로 스타일 우선순위 정하기

웹 개발을 하다보면 CSS를 작성할 때 원하는 스타일이 우선적으로 적용되지 않아서 골치가 아플 때가 많습니다. 특히 대규모 프로젝트에서는 여러 CSS 파일에서 스타일을 가져오다 보니 스타일의 우선순위를 파악하는 것이 쉽지 않죠. 이런 문제를 해결하기 위해 CSS에는 @layer라는 새로운 at-rule을 도입되었습니다.

이번 포스팅에서는 @layer를 사용하여 CSS 스타일 시트를 계층화하여 우선순위를 효과적으로 설정하는 방법에 대해서 알아보겠습니다.

기존의 스타일 우선순위 관리법

CSS에 @layer가 없던 시절에는 동일한 요소를 상대로 여러 스타일이 정의되어 있을 때 명시도(specificity)를 계산해야 어떤 스타일이 적용될지를 파악할 수 있었습니다. 그리고 명시도가 동일하다면 CSS 파일 상에서 나중에 나오는 스타일이 먼저 나오는 스타일보다 우선시되었습니다.

예를 들어, 동일한 <div> 요소를 상대로 정의된 아래 3개의 스타일은 모두 인라인(inline) 스타일의 명시도인 1,0,0,0보다 작기 때문에 아무런 효력을 내지 못하고, 배경색이 핑크색이 됩니다.

div {
  background: blue; /* 명시도: 0,0,0,1 */
}

#error {
  background: red; /* 명시도: 0,1,0,0 */
}

.warning {
  background: yellow; /* 명시도: 0,0,1,0 */
}

프로젝트의 규모가 커지면 CSS 명시도나 스타일 정의 순서 만으로는 스타일 우선순위를 정리하는 것이 너무 복잡해지며 실수를 일으키기 쉽습니다. 이럴 때 !important를 사용하면 명시도를 무시하고 특정 스타일의 우선순위를 끌어올릴 수 있습니다.

예를 들어, 클래스 선택자를 사용한 스타일 정의에 !important를 붙여서 인라인(inline) 스타일의 명시도를 극복하고 노란색 배경을 얻을 수 있습니다.

div {
  background: blue; /* 명시도: 0,0,0,1 */
}

#error {
  background: red; /* 명시도: 0,1,0,0 */
}

.warning {
  background: yellow !important; /* ❗ */}

하지만 여러 개발자들이 자신이 작성한 스타일의 우선순위를 끌어올리기 위해서 여기저기 !important를 남발하면 아래와 같이 난처한 상황이 펼쳐지는데요. !important가 붙어있는 스타일이 2개다 보니 다시 명시도에 따라서 스타일의 우선순위가 결정되는 것입니다. 따라서 ID 선택자의 스타일이 이겨서 빨간 배경색이 됩니다.

div {
  background: blue; /* 명시도: 0,0,0,1 */
}

#error {
  background: red !important; /* ❗❗ */}

.warning {
  background: yellow !important; /* ❗ */
}

결국 !important는 우선순위 문제는 해결하지 못하고 스타일의 유지 보수성만 떨어뜨리는 악순환에 빠질 수 있습니다.

CSS의 선택자에 대해서는 별도 포스팅에서 자세히 다루고 있으니 참고하세요.

우선순위 설정

자 그럼 지금부터 @layer를 사용하여 좀 더 우아하게 스타일의 우선순위를 관리할 수 있는지에 대해서 알아볼까요?

@layer의 핵심 기능은 레이어 간의 우선순위를 쉽게 조절할 수 있다는 점입니다. 레이어는 선언된 순서에 따라 우선순위를 가지며, 나중에 선언된 레이어가 앞선 레이어보다 우선순위가 높아집니다.

예를 들어, 다음과 같이 작성했을 때 utilities 계층의 우선순위가 가장 높고, 그 다음이 components 계층, base 계층의 우선순위는 가장 낮습니다.

@layer base, components, utilities;

참고로 이렇게 계층 간에 우선순위를 설정하면 어떤 계층 속하지 않은 일반 스타일이 가장 높은 우선순위를 가지게 됩니다.

스타일 계층화

@layer는 계층 간 우선순위를 설정할 때 뿐만 아니라, 각 계층의 스타일을 담아두기 위해서도 사용됩니다. 기본 문법은 다음과 같습니다.

@layer 레이어 이름 {
  /* 스타일 정의 */
}

예를 들어, base, components, utilities 계층에 대한 스타일을 다음과 같이 분리하여 정의할 수 있습니다.

@layer base {
  .body {
    /* 스타일 정의 */
  }
}

@layer components {
  .button {
    /* 스타일 정의 */
  }

  .card {
    /* 스타일 정의 */
  }
}

@layer utilities {
  .sr-only {
    /* 스타일 정의 */
  }
}

실습

이해를 돕기 위해서 간단한 실습을 해볼까요? greenred라는 레이어를 만들고 그 안에 서로 다른 색으로 배경색을 설정하는 스타일을 추가합니다.

먼저 red의 우선순위를 높게 설정해보겠습니다.

이번에는 green의 우선순위를 높게 설정해보겠습니다.

외부 스타일의 우선순위 조정

Bootstrap이나 Material과 같은 CSS 프레임워크를 사용하다 보면 CSS 프레임워크에서 제공하는 스타일의 우선순위가 높아서 직접 작성한 스타일이 잘 적용되지 않을 때가 있습니다. 이러한 문제도 @layer를 사용해서 해결할 수 있는데요.

우선 CSS 프레임워크에서 제공하는 외부 스타일을 불러올 때, 뒤에 layer(레이어 이름)을 붙여서 계층을 할당해줍니다. 그리고 나서 위에서 배운 방법으로 @layer를 통해 외부 스타일의 우선순위를 하양 조정하고, 자신이 작성한 스타일의 우선순위를 상향 조정합니다.

@import url("./node_modules/bootstrap/dist/css/bootstrap.min.css") layer(bootstrap);

@layer bootstrap, our-styles;

@layer our-styles {
  /* 스타일 정의 */
}

이 방법을 사용하면 CSS Reset(리셋)과 CSS Normalize(노멀라이즈)도 스타일 충돌없이 안전하게 불러올 수 있습니다.

@import url("./reset.css") layer(reset);
@import url("./node_modules/bootstrap/dist/css/bootstrap.min.css") layer(bootstrap);

@layer reset, bootstrap, our-styles;

@layer our-styles {
  /* 스타일 정의 */
}

브라우저의 내장 스타일로 인한 부작용에 대한 두 가지 대표적인 접근 방법인 CSS Reset과 CSS Normalize에 대해서는 별도 포스팅에서 자세히 다루고 있으니 참고하세요.

컴포넌트 라이브러리나 디자인 시스템을 직접 개발할 때도 @layer를 통해서 체계적으로 스타일을 나누고 우선순위를 설정할 수 있겠죠?

@import url("reset.css") layer(reset);
@import url("globals.css") layer(globals);
@import url("typography.css") layer(typography);
@import url("components.css") layer(components);
@import url("utilities.css") layer(utilities);

@layer reset, globals, typography, components, utilities;

브라우저 지원

@layer는 공식적으로 지원이 끝난 인터넷 익스플로러(Internet Explorer)를 제외한 대부분의 모던 브라우저에서 지원되고 있습니다.

마치면서

지금까지 CSS에 새롭게 추가된 at-rule인 @layer에 대해서 어떻게 스타일을 계층화하고 우선순위를 수 있는지 살펴보았습니다.

@layer는 CSS 스타일 시트를 더 효율적으로 관리할 수 있는 강력한 도구입니다. 특히 복잡한 스타일 시트나 여러 팀이 협업하는 프로젝트에서 유용하게 활용될 수 있습니다. 스타일의 우선순위를 직관적으로 제어하고 싶다면, @layer를 적극적으로 사용해보세요.

CSS의 새로운 기능을 배우고 적용하면서 더욱 깔끔하고 유지 보수하기 쉬운 스타일 작성을 하실 수 있기를 바랍니다.

CSS에서 정의한 스타일을 레이어 단위로 그룹화하여, 필요한 경우 레이어별 우선순위를 설정할 수 있습니다. 이렇게 하면 코드의 복잡도를 줄이고, 우선순위 문제를 쉽게 해결할 수 있습니다.