그리고 몇 가지 이슈들과 해법

Vue.js의 Scoped CSS 기능은 Vue.js의 내장 기능이 아니라 vue-loader 에서 제공하는 기능이다. vue-loaderSingle-File Components (SFCs) 포맷으로 코드를 작성할 수 있도록 도와주는 도구이다. 즉 Scoped CSS는 SFC 로 작성할 경우 해당되는 이야기이니 참고바란다.

Shadow DOM의 스타일 캡슐화(encapsulation) 기능과 흡사하다고 한다.

왜 Scoped CSS 인가

Scoped CSS는 특정 컴포넌트에 정의된 스타일 정의가 나도 모르게 다른 컴포넌트에 영향을 주는 사이드 이펙트를 막을 수 있다.

Vue.js를 비롯한 컴포넌트 중심의 코딩을 유도하는 프레임워크에서 작업을 하게되면 자연스럽게 컴포넌트들의 중첩이 이뤄진다. 그 과정에서 의도하지 않게 특정 컴포넌트의 스타일 정의가 다른 컴포넌트에 영향을 주게 된다.

앱의 규모가 작을때 큰 문제가 안될 수 있으나 규모가 커지면 사이드 이펙트를 관리하는것도 상당한 스트레스로 작용할 수 있고 이것을 해소하기 위한 코드들을 별도로 생산해야할 수도 있다.

BEM과 같은 CSS 작성 방법론이 모듈 단위의 재사용성 높은 스타일링이 이런 사이드 이펙트를 완화시키는데 도움이 되기는 하지만 이것만으로는 사이드 이펙트를 완전히 피하기는 힘들다.

기본적인 사용법

Scoped CSS는 특정 컴포넌트의 스타일이 해당 컴포넌트에만 적용되도록 강제하는 역할을 하게된다. 다음과 같이 스타일 태그에 scoped 속성을 명시하면 된다.

이렇게 작성하면 실제로 아래와 같이 변환된다.

위와 같이 돔 속성에 랜덤한 속성 이름(data-v-f3f3fdk)이 생성이 되고 스타일 시트의 클래스 이름은 그 속성 이름과 함께 명시(.example[data-v-f3f3fdk])되기 때문에 해당 돔에만 영향을 주도록 범위가 제한(scoped)된다.

문제1 : v-html 사용 시

하지만 v-html 디렉티브를 통해 추가되는 html은 scoped 된 속성이름이 생성되지 않는다.

이런 경우 아래와 같이 변환된다.

위와 같이 v-html로 주입된 돔에는 scoped 속성이름이 추가되지 않는다. 따라서 변환된 스타일 정의 .my .content[data-v-02564f04]는 아무것도 셀렉트하지 못한다.

문제2: 하위 컴포넌트

하위 컴포넌트에 영향을 주는 스타일을 정의하고 싶은 경우에도 문제가 된다. 왜냐하면 하위 컴포넌트의 scoped 속성이름은 부모와 다르거나 속성이름이 없기(scoped 설정을 하지 않을 경우) 때문이다.

방법은 Deep Selector를 사용하면 가능하다. 물론 원칙적으로는 하위 컴포넌트에 영향을 주는 스타일은 매우 예외적인 경우에만 사용하는 것이 좋지만 말이다.

Deep Selectors

위 문제들을 해결해주는 것이 Deep Selector이다. 아래와 같이 작성하면

<style scoped>
.a >>> .b { /* ... */ }
</style>

아래와 같이 변환된다.

.a[data-v-f3f3eg9] .b { /* ... */ }

따라서 클래스 b를 가진 돔이 어떤 scoped 속성이름을 가지고 있다고 하더라도 셀렉트는 잘 동작한다.

Sass 와 같은 pre-processor 를 사용하는 경우 a. >>> .b대신 .a::v-deep .b 혹은 .a /deep/ .b와 같이 사용해야 동일한 효과를 볼 수 있다.

다시한번 말하지만 사이드 이팩트를 제거하기 위해 scoped css 기능을 사용하는 것이기 때문에 또 다른 사이드 이팩트를 야기할 가능성이 있는 deep selector는 매우 예외적인 경우에만 사용하고, 되도록이면 컴포넌트에 옵션(BEM 용어를 빌리면 modifier)을 추가하는 방식과 같이 다른 방법이 가능한지 따져 보고 판단하길 바란다.

이 글이 마음에 드셨다면 👏🏽👏🏽와 커피한잔 후원하기 (카카오페이), 이메일로 소식을 받아보고 싶으시다면 ✉️ 이메일 구독하기 해주세요. 그리고 저희 회사에서 프론트엔드를 포함한 개발자분들 채용중입니다.

Vue.js 관련 이야기들 | working at habitfactory.co | 커피한잔 후원하기 https://bit.ly/355PDlu | 이메일 구독하기 https://bit.ly/3ax32Fn

Vue.js 관련 이야기들 | working at habitfactory.co | 커피한잔 후원하기 https://bit.ly/355PDlu | 이메일 구독하기 https://bit.ly/3ax32Fn