Logo

CSS의 @scope으로 스타일 범위 제한하기

CSS에서 스타일이 적용되는 범위를 제한하는 일(CSS Scoping)은 오랫동안 웹 개발자들에게 굉장히 커더란 숙제였습니다. 소규모 웹사이트에서는 스타일 충돌을 막기 위해서 BEM(Block Element Modifier)와 같이 클래스의 이름을 일관적으로 짓기 위한 섬세한 규칙을 정해놓고 모든 개발자들이 기억하고 따라야 했습니다. 어느 정도 규모가 있는 앱 애플레케이션을 개발할 때는 React, Svelte, Vue와 같은 프론트엔드 프레임워크의 도움을 받거나 CSS 모듈 또는 CSS-in-JS를 사용해야 특정 컴포넌트에만 스타일을 적용할 수 있었습니다. 뿐만 아니라, 한 단계 더 나아가Tailwind와 같이 아예 CSS 작성을 피하고 유틸리티 클래스만 쓰는 라이브러리도 요즘 많이 사용됩니다.

하지만 CSS에 비교적 최근에 추가된 at-rule인 @scope을 사용하면 이러한 궁여지책을 사용하지 않고, 아주 효과적으로 스타일의 범위를 특정 HTML 요소나 컴포넌트로 제한할 수 있습니다. 이번 포스팅에서는 CSS의 새로운 기능인 @scope에 대해 소개하고, 어떻게 활용하는지 간단한 실습을 진행해보겠습니다.

기존 스타일 범위 제한법

우선 옛날에 @scope이 없던 시절에 스타일의 범위를 제한하는 것이 얼마나 불편했는지를 보여드릴께요.

다음과 같은 HTML 마크업을 스타일해야한다고 가정을 해보겠습니다.

<h2 class="title">제목</h2>
<p class="paragraph">단락</p>

<section class="info">
  <h2 class="info__title">정보 제목</h2>
  <p class="info__paragraph">정보 단락</p>
</section>

<section class="danger">
  <h2 class="danger__title">위험 제목</h2>
  <p class="danger__paragraph">위험 단락</p>
</section>

<h2><p> 요소가 여러 곳에서 사용되고 있기 때문에 서로 다른 스타일을 적용하려면 클래스를 달아줘야 합니다. BEM과 유사한 명명 규칙을 사용하여 클래스 이름을 지었습니다.

이제 HTML 요소에 달아준 클래스 이름을 선택자로 사용하여 CSS로 스타일링을 해줄 수 있습니다.

.title {
  color: green;
}

.info {
  background: skyblue;
  padding: 0.5rem;
}

.info__title {
  color: blue;
}

.info__paragraph {
  font-style: italic;
}

.danger {
  background: pink;
  padding: 0.5rem;
}

.danger__title {
  color: red;
}

.danger__paragraph {
  letter-spacing: 1rem;
}

어떤가요? 별 거 아닌 스타일인데 클래스가 너무 많아서 HTML과 CSS 코드 둘 다 상당히 복잡하고 지저분해 보이죠? 😵‍💫

이렇게 옛날 방식으로 스타일의 범위를 제한하면 클래스 이름이 장황해지기 때문에 코딩을 하다가 오타를 내기도 쉽고 전반적으로 가독성이 떨어져 스타일을 한 눈에 파악하기도 어려워집니다. 뿐만 아니라, HTML 요소마다 적절한 클래스 이름을 생각해야되는데 이 게 은근히 개발자에게 스트레스가 될 수 있습니다.

@scope 사용법 1

@scope를 사용하는 첫 번째 방법은 선택자와 함께 사용하는 것인데요. 다음과 같은 형태로 사용합니다.

@scope (부모 선택자) {
  자식 선택자 {
    /* 스타일 규칙들 */
  }
  자식 선택자 {
    /* 스타일 규칙들 */
  }
}

이렇게 @scope을 사용하면 자식 선택자로 선언한 스타일 규칙들이 부모 선택자 범위 내로 한정되어 적용이 됩니다.

예를 들어, 위에서 작성한 HTML 코드에서 꼭 필요한 2개의 최상위 클래스만 남겨두고 클래스를 모두 제거해보겠습니다.

<h2>제목</h2>
<p>단락</p>

<section class="info">
  <h2>정보 제목</h2>
  <p>정보 단락</p>
</section>

<section class="danger">
  <h2>위험 제목</h2>
  <p>위험 단락</p>
</section>

이제 @scope를 사용해서 CSS 코드를 재작성해보겠습니다. 각 범위 안에서는 클래스 선택자 대신에 그냥 간단하게 타입 선택자를 사용한 것을 볼 수 있습니다.

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

h2 {
  color: green;
}

@scope (.info) {
  :scope {
    background: skyblue;
    padding: 0.5rem;
  }

  h2 {
    color: blue;
  }

  p {
    font-style: italic;
  }
}

@scope (.danger) {
  & {
    background: pink;
    padding: 0.5rem;
  }

  & h2 {
    color: red;
  }

  & p {
    letter-spacing: 1rem;
  }
}

어떤가요? 범위 별로 스타일이 묶여 있으니 훨씬 보기 편하지 않나요?

이렇게 @scope을 사용하면 HTML 문서를 각 섹션별로 효과적으로 스타일을 격리시킬 수 있습니다. info 클래스 안에 정의한 <h2><p> 요소에 대한 스타일은 완전히 격리되어 있기 때문에 밖에 있는 <h2><p> 요소의 스타일에 아무런 영향을 주지 않습니다.

여기서 한 가지 유심히 보실 점은 어떻게 범위의 최상위 요소를 선택하느냐인데요. :scope 가상 클래스(pseudo class)를 사용할 수도 있고, CSS Nesting의 문법을 써서 & 기호를 사용할 수도 있습니다.

선택자의 중첩이 가능하게 하는 CSS Nesting 대해서는 별도 포스팅에서 자세히 다루고 있으니 참고하세요.

@scope 사용법 2

@scope은 선택자가 없이 단독으로 사용할 수 있는데요. 문법은 다음과 같은 형태를 띱니다. 바로 스타일을 적용하고 싶은 HTML 요소 바로 아래 다른 자식 HTML 요소와 함께 <style> 요소를 두는 겁니다.

<요소>
  <style>
    @scope {
      선택자 {
        /* 스타일 규칙들 */
      }
      선택자 {
        /* 스타일 규칙들 */
      }
    }
  </style>
  <자식 요소>
    ...
  </자식 요소>
  <자식 요소>
    ...
  </자식 요소>
</요소>

그러면 위에서 작성한 HTML과 CSS 코드는 이렇게 하나의 HTML로 합쳐질 것입니다.

<style>
  h2 {
    color: green;
  }
</style>
<h2>제목</h2>
<p>단락</p>

<section>
  <style>
    @scope {
      :scope {
        background: skyblue;
        padding: 0.5rem;
      }

      h2 {
        color: blue;
      }

      p {
        font-style: italic;
      }
    }
  </style>
  <h2>정보 제목</h2>
  <p>정보 단락</p>
</section>

<section class="danger">
  <style>
    @scope {
      & {
        background: pink;
        padding: 0.5rem;
      }

      & h2 {
        color: red;
      }

      & p {
        letter-spacing: 1rem;
      }
    }
  </style>
  <h2>위험 제목</h2>
  <p>위험 단락</p>
</section>

이 방법은 특히 React와 같이 JSX를 사용하는 프런트엔드 프레임워크에서 빛을 발휘할 수 있습니다. 별도의 라이브러리 없이도, HTML과 CSS만으로 특정 컴포넌트에만 스타일을 적용할 수 있기 때문입니다.

라디오 버튼을 밑바닥 부터 제대로 스타일하고 싶으시다면 관련 포스팅을 참고 바랍니다.

@scope의 브라우저 지원

이 글을 쓰는 현 시점에서 파이어폭스를 제외한 크롬, 사파리를 포함한 대부분의 모던 브라우저에서 @scope를 지원하고 있습니다. 파이어폭스에도 지원되는 것은 시간 문제겠죠? 지금부터 관심있게 보셔도 좋을 것 같습니다.

물론 폴리필(polyfill)이나 PostCSS와 같은 CSS 전처리기를 쓰시면 당장 쓰실 수 있는 기능입니다.

마치면서

지금까지 CSS에 새롭게 추가된 at-rule인 @scope에 대해서 간단한 예제를 통해서 살펴보았습니다.

컴포넌트 기반의 웹 프로그래밍이 대세가 되면서 스타일을 컴포넌트 단위로 제한하는 것은 많은 웹 개발자들이 아주 오래동안 손꼽아 기다려왔던 기능입니다. 그래서 개인적으로 CSS Variables, CSS Nesting과 더불어 @scope이 CSS에 있었던 가장 영향력이 큰 변화 중 하나라고 생각합니다.

읽기 편하고 유지 보수가 쉬운 현대적인 CSS를 작성하시는데 본 포스팅이 도움이 되었으면 좋겠습니다.