Logo

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

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

하지만 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 요소마다 적절한 클래스 이름을 생각해야되는데 이 게 은근히 개발자에게 스트레스가 될 수 있습니다. 게다가 클래스 이름이 장황해지기 때문에 CSS를 작성하다가 오타를 내기도 쉽고 전반적으로 가독성이 떨어져 스타일을 한 눈에 파악하기도 어려워집니다.

@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 가상 클래스(pseudo class)를 사용할 수도 있고, CSS Nesting의 문법을 써서 & 기호를 사용할 수도 있습니다.

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

@scope 사용법 2

@scope은 선택자가 없이도 단독으로 사용할 수 있습니다. 문법은 다음과 같은 형태를 띄는데요. 바로 스타일을 적용하고 싶은 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과 더불어 최근에 CSS에 있었던 가장 영향력이 큰 변화라고 생각합니다.

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