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 {
/* 스타일 정의 */
}
}
실습
이해를 돕기 위해서 간단한 실습을 해볼까요?
green
과 red
라는 레이어를 만들고 그 안에 서로 다른 색으로 배경색을 설정하는 스타일을 추가합니다.
먼저 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)를 제외한 대부분의 모던 브라우저에서 지원되고 있습니다.
- 브라우저 지원 현황: https://caniuse.com/css-cascade-layers
마치면서
지금까지 CSS에 새롭게 추가된 at-rule인 @layer
에 대해서 어떻게 스타일을 계층화하고 우선순위를 수 있는지 살펴보았습니다.
@layer
는 CSS 스타일 시트를 더 효율적으로 관리할 수 있는 강력한 도구입니다.
특히 복잡한 스타일 시트나 여러 팀이 협업하는 프로젝트에서 유용하게 활용될 수 있습니다.
스타일의 우선순위를 직관적으로 제어하고 싶다면, @layer
를 적극적으로 사용해보세요.
CSS의 새로운 기능을 배우고 적용하면서 더욱 깔끔하고 유지 보수하기 쉬운 스타일 작성을 하실 수 있기를 바랍니다.
CSS에서 정의한 스타일을 레이어 단위로 그룹화하여, 필요한 경우 레이어별 우선순위를 설정할 수 있습니다. 이렇게 하면 코드의 복잡도를 줄이고, 우선순위 문제를 쉽게 해결할 수 있습니다.