Vue.js는 컴포넌트 기반 아키텍처를 제공하여 확장 가능하고 유지보수가 용이한 애플리케이션을 구축하는 데 강력한 프레임워크입니다. Vue의 핵심 기능 중 하나인 Scoped CSS는 컴포넌트 내에서 정의된 스타일이 해당 컴포넌트에만 적용되도록 보장하여 애플리케이션 전반에 걸친 의도치 않은 스타일 영향력을 방지합니다. 그러나 v-html
과 같은 동적 콘텐츠 렌더링 방식을 사용할 때 Scoped CSS를 효과적으로 적용하는 데 어려움을 겪을 수 있습니다.
이 블로그 포스트에서는 Scoped 스타일과 v-html
과 관련된 일반적인 문제를 살펴보고, 그 근본 원인을 이해하며, Vue의 최신 기능을 활용하여 이를 해결하는 방법에 대해 논의하겠습니다.
목차
Vue의 Scoped CSS 이해하기
Scoped CSS는 컴포넌트에 특정한 스타일을 작성할 수 있게 해줍니다. 이는 컴포넌트 내에서 정의된 스타일이 다른 컴포넌트에 영향을 미치지 않도록 보장합니다. Vue는 각 요소에 고유한 데이터 속성을 자동으로 추가하여 스타일의 적용 범위를 제한합니다.
Scoped CSS의 예제
<template>
<div class="example">
<p>이것은 scoped된 단락입니다.</p>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.example {
background-color: #f0f0f0;
}
p {
color: blue;
}
</style>
위 예제에서:
.example
클래스와p
태그에 대한 스타일은 해당 컴포넌트에만 적용됩니다.- 다른 컴포넌트에서 동일한 클래스나 태그가 있어도 영향을 받지 않습니다.
v-html
의 도전 과제
Vue의 v-html
디렉티브는 동적으로 원시 HTML 콘텐츠를 렌더링할 수 있게 해줍니다. 이는 API에서 가져온 콘텐츠나 사용자 생성 콘텐츠를 표시할 때 유용합니다. 그러나 Scoped CSS를 사용하는 컴포넌트 내에서 v-html
을 사용할 경우, 동적으로 렌더링된 콘텐츠에 스타일이 예상대로 적용되지 않는 문제가 발생할 수 있습니다.
왜 이런 일이 발생할까요?
Scoped CSS는 컴포넌트의 요소에 고유한 속성을 추가하여 스타일을 적용합니다. 그러나 v-html
을 통해 렌더링된 콘텐츠는 이러한 고유 속성을 받지 않기 때문에 Scoped 스타일이 적용되지 않습니다.
예제 시나리오
다음은 v-html
을 사용하여 마크다운 콘텐츠를 렌더링하는 컴포넌트 예제입니다:
<template>
<div class="content" v-html="renderedContent"></div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import MarkdownIt from 'markdown-it'
const props = defineProps(['content'])
const markdownParser = new MarkdownIt({
html: true,
breaks: true,
linkify: true,
})
const renderedContent = computed(() => {
return markdownParser.render(props.content || '')
})
</script>
<style scoped>
.content {
font-family: Arial, sans-serif;
color: #333;
}
ul, ol {
padding-left: 20px;
}
</style>
이 예제에서:
- 컴포넌트는
v-html
을 사용하여 마크다운 콘텐츠를 렌더링합니다. ul
과ol
요소에 패딩을 적용하려 하지만, Scoped CSS로 인해 스타일이 적용되지 않습니다.
문제 식별하기
Scoped 스타일을 v-html
과 함께 사용하면 동적으로 삽입된 HTML에 고유한 속성이 적용되지 않아 스타일이 의도대로 적용되지 않는 문제가 발생합니다. 특히 ul
, ol
, li
요소에서 들여쓰기가 제대로 적용되지 않는 현상이 나타날 수 있습니다.
일반적인 문제점
- 들여쓰기 부족: 리스트(
ul
,ol
)에 원하는 들여쓰기가 적용되지 않습니다. - 스타일 미적용:
v-html
을 통해 삽입된 다른 HTML 요소에도 스타일이 적용되지 않습니다.
해결 방법: :deep()
선택자 사용하기
Vue는 Scoped CSS의 경계를 넘어 자식 컴포넌트나 특정 요소에 스타일을 적용할 수 있는 :deep()
선택자를 제공합니다. 이는 v-html
로 렌더링된 콘텐츠에 스타일을 적용하는 데 특히 유용합니다.
왜 :deep()
인가?
- Scoped 스타일 유지: Scoped 스타일의 이점을 유지하면서 특정 요소에 스타일을 적용할 수 있습니다.
- 유연성 제공: Scoped CSS의 제한을 우회하여 필요한 부분에만 스타일을 적용할 수 있습니다.
deprecated된 ::v-deep
대체
과거에는 ::v-deep
조합자를 사용하여 유사한 결과를 얻었지만, 이제는 ::v-deep
이 deprecated 되어 :deep()
구문을 사용하는 것이 권장됩니다.
구문
:deep(selector) {
/* 스타일 정의 */
}
최고의 실천 방법
가능한 한 Scoped CSS 사용 유지:
- 컴포넌트 스타일의 격리를 유지하여 의도치 않은 스타일 영향력을 방지합니다.
동적 콘텐츠에는
:deep()
활용:v-html
이나 자식 컴포넌트에 스타일을 적용할 때:deep()
을 선택적으로 사용합니다.
전역 스타일의 과도한 사용 피하기:
scoped
속성을 제거하고 전역 스타일을 사용하는 것은 스타일 충돌을 유발할 수 있으므로 신중하게 사용합니다.
스타일 정리 및 주석 추가:
:deep()
을 사용하는 부분에 명확한 주석을 추가하여 코드의 가독성을 높입니다.
컴포넌트 간 스타일 충돌 테스트:
:deep()
으로 적용한 스타일이 다른 컴포넌트에 영향을 미치지 않는지 확인합니다.
결론
Vue.js에서 동적으로 렌더링된 콘텐츠를 스타일링하는 것은 Scoped CSS와 v-html
디렉티브를 사용할 때 도전 과제가 될 수 있습니다. 그러나 :deep()
선택자를 활용하면 Scoped 스타일의 이점을 유지하면서도 특정 요소에 필요한 스타일을 적용할 수 있습니다. 이를 통해 v-html
로 삽입된 ul
, ol
요소의 들여쓰기가 정상적으로 적용되며, 스타일 충돌 없이 깔끔한 UI를 유지할 수 있습니다.
스타일을 조직적으로 관리하고 :deep()
을 적절히 사용함으로써 Vue.js 애플리케이션의 스타일링 문제를 효과적으로 해결할 수 있습니다. 최신 Vue의 모범 사례를 따르며 유지보수가 용이한 코드베이스를 구축하시기 바랍니다.
'Vue' 카테고리의 다른 글
Vue 3의 <script setup> 간결하고 효율적인 컴포넌트 정의 방식 (1) | 2024.10.19 |
---|---|
Vue 컴포넌트에서 자주 사용하는 속성들 (2) | 2024.10.15 |
Vuex에서 Getter, Mutation, Action의 역할 (0) | 2024.10.15 |