스타일링
Nuxt는 스타일링에 있어 매우 유연합니다. 직접 스타일을 작성하거나, 로컬 및 외부 스타일시트를 참조할 수 있습니다. CSS 전처리기, CSS 프레임워크, UI 라이브러리, Nuxt 모듈을 사용하여 애플리케이션을 스타일링할 수 있습니다.
로컬 스타일시트
로컬 스타일시트를 작성하는 경우, 자연스러운 위치는 assets/ 디렉토리입니다.
컴포넌트 내에서 가져오기
페이지, 레이아웃, 컴포넌트에서 직접 스타일시트를 가져올 수 있습니다.
JavaScript import 또는 CSS @import 문을 사용할 수 있습니다.
<script>
// 서버 사이드 호환성을 위해 정적 import 사용
import '~/assets/css/first.css'
// 주의: 동적 import는 서버 사이드에서 호환되지 않습니다
import('~/assets/css/first.css')
</script>
<style>
@import url("~/assets/css/second.css");
</style>
CSS 속성
Nuxt 설정에서 css 속성을 사용할 수도 있습니다.
스타일시트의 자연스러운 위치는 assets/ 디렉토리입니다. 경로를 참조하면 Nuxt가 애플리케이션의 모든 페이지에 포함시켜줍니다.
export default defineNuxtConfig({
css: ['~/assets/css/main.css']
})
폰트 작업하기
로컬 폰트 파일을 ~/public/ 디렉토리에, 예를 들어 ~/public/fonts에 넣으세요. 그런 다음 스타일시트에서 url()을 사용해 참조할 수 있습니다.
@font-face {
font-family: 'FarAwayGalaxy';
src: url('/fonts/FarAwayGalaxy.woff') format('woff');
font-weight: normal;
font-style: normal;
font-display: swap;
}
그런 다음 스타일시트, 페이지 또는 컴포넌트에서 폰트 이름으로 참조하세요:
<style>
h1 {
font-family: 'FarAwayGalaxy', sans-serif;
}
</style>
NPM을 통해 배포되는 스타일시트
npm을 통해 배포되는 스타일시트를 참조할 수도 있습니다. 인기 있는 animate.css 라이브러리를 예로 들어보겠습니다.
npm install animate.css
yarn add animate.css
pnpm install animate.css
bun install animate.css
그런 다음 페이지, 레이아웃, 컴포넌트에서 직접 참조할 수 있습니다:
<script>
import 'animate.css'
</script>
<style>
@import url("animate.css");
</style>
패키지는 Nuxt 설정의 css 속성에서 문자열로도 참조할 수 있습니다.
export default defineNuxtConfig({
css: ['animate.css']
})
외부 스타일시트
애플리케이션에 외부 스타일시트를 포함하려면 nuxt.config 파일의 head 섹션에 link 요소를 추가하면 됩니다. 여러 방법으로 이 결과를 얻을 수 있습니다. 로컬 스타일시트도 이 방법으로 포함할 수 있습니다.
Nuxt 설정의 app.head 속성을 사용해 head를 조작할 수 있습니다:
export default defineNuxtConfig({
app: {
head: {
link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }]
}
}
})
스타일시트 동적 추가
useHead 컴포저블을 사용해 코드 내에서 head에 동적으로 값을 설정할 수 있습니다.
useHead({
link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }]
})
Nuxt는 내부적으로 unhead를 사용하며, 전체 문서를 참고할 수 있습니다.
Nitro 플러그인으로 렌더된 Head 수정하기
더 고급 제어가 필요하다면, 후크로 렌더된 html을 가로채서 head를 프로그래밍적으로 수정할 수 있습니다.
다음과 같이 ~/server/plugins/my-plugin.ts에 플러그인을 만드세요:
export default defineNitroPlugin((nitro) => {
nitro.hooks.hook('render:html', (html) => {
html.head.push('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">')
})
})
외부 스타일시트는 렌더 차단 리소스입니다: 브라우저가 페이지를 렌더링하기 전에 반드시 로드 및 처리되어야 합니다. 불필요하게 큰 스타일을 포함한 웹 페이지는 렌더링이 더 오래 걸립니다. 자세한 내용은 web.dev에서 확인할 수 있습니다.
전처리기 사용하기
SCSS, Sass, Less, Stylus와 같은 전처리기를 사용하려면 먼저 설치하세요.
npm install -D sass
npm install -D less
npm install -D stylus
스타일시트를 작성할 자연스러운 위치는 assets 디렉토리입니다.
그런 다음 app.vue(또는 레이아웃 파일)에서 전처리기 문법으로 소스 파일을 import할 수 있습니다.
<style lang="scss">
@use "~/assets/scss/main.scss";
</style>
또는 Nuxt 설정의 css 속성을 사용할 수도 있습니다.
export default defineNuxtConfig({
css: ['~/assets/scss/main.scss']
})
전처리된 파일에 코드를 주입해야 한다면, 예를 들어 색상 변수가 있는 Sass partial 등, Vite의 preprocessors options를 사용할 수 있습니다.
assets 디렉토리에 partial 파일을 만드세요:
$primary: #49240F;
$secondary: #E4A79D;
$primary: #49240F
$secondary: #E4A79D
그런 다음 nuxt.config에서:
export default defineNuxtConfig({
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "~/assets/_colors.scss" as *;'
}
}
}
}
})
export default defineNuxtConfig({
vite: {
css: {
preprocessorOptions: {
sass: {
additionalData: '@use "~/assets/_colors.sass" as *\n'
}
}
}
}
})
Nuxt는 기본적으로 Vite를 사용합니다. webpack을 사용하고 싶다면 각 전처리기 로더의 문서를 참고하세요.
전처리기 워커(실험적)
Vite는 전처리기 사용을 빠르게 해주는 실험적 옵션을 제공합니다.
nuxt.config에서 이를 활성화할 수 있습니다:
export default defineNuxtConfig({
vite: {
css: {
preprocessorMaxWorkers: true // CPU 개수 - 1
}
}
})
싱글 파일 컴포넌트(SFC) 스타일링
Vue와 SFC의 가장 큰 장점 중 하나는 스타일링을 자연스럽게 처리할 수 있다는 점입니다. 컴포넌트 파일의 style 블록에 직접 CSS 또는 전처리기 코드를 작성할 수 있으므로, CSS-in-JS 같은 것을 사용하지 않아도 훌륭한 개발 경험을 얻을 수 있습니다. 하지만 CSS-in-JS를 사용하고 싶다면 pinceau와 같은 3rd party 라이브러리와 모듈을 찾을 수 있습니다.
SFC에서 컴포넌트 스타일링에 대한 포괄적인 참고 자료는 Vue 문서를 참고하세요.
클래스 및 스타일 바인딩
Vue SFC 기능을 활용하여 class 및 style 속성으로 컴포넌트를 스타일링할 수 있습니다.
<script setup lang="ts">
const isActive = ref(true)
const hasError = ref(false)
const classObject = reactive({
active: true,
'text-danger': false
})
</script>
<template>
<div class="static" :class="{ active: isActive, 'text-danger': hasError }"></div>
<div :class="classObject"></div>
</template>
<script setup lang="ts">
const isActive = ref(true)
const error = ref(null)
const classObject = computed(() => ({
active: isActive.value && !error.value,
'text-danger': error.value && error.value.type === 'fatal'
}))
</script>
<template>
<div :class="classObject"></div>
</template>
<script setup lang="ts">
const isActive = ref(true)
const errorClass = ref('text-danger')
</script>
<template>
<div :class="[{ active: isActive }, errorClass]"></div>
</template>
<script setup lang="ts">
const activeColor = ref('red')
const fontSize = ref(30)
const styleObject = reactive({ color: 'red', fontSize: '13px' })
</script>
<template>
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div :style="[baseStyles, overridingStyles]"></div>
<div :style="styleObject"></div>
</template>
자세한 내용은 Vue 문서를 참고하세요.
v-bind로 동적 스타일
style 블록 내에서 v-bind 함수를 사용해 JavaScript 변수와 표현식을 참조할 수 있습니다. 바인딩은 동적이므로, 변수 값이 변경되면 스타일도 업데이트됩니다.
<script setup lang="ts">
const color = ref("red")
</script>
<template>
<div class="text">hello</div>
</template>
<style>
.text {
color: v-bind(color);
}
</style>
Scoped 스타일
scoped 속성을 사용하면 컴포넌트를 독립적으로 스타일링할 수 있습니다. 이 속성으로 선언된 스타일은 해당 컴포넌트에만 적용됩니다.
<template>
<div class="example">hi</div>
</template>
<style scoped>
.example {
color: red;
}
</style>
CSS 모듈
module 속성을 사용해 CSS Modules를 사용할 수 있습니다. 주입된 $style 변수로 접근합니다.
<template>
<p :class="$style.red">This should be red</p>
</template>
<style module>
.red {
color: red;
}
</style>
전처리기 지원
SFC style 블록은 전처리기 문법을 지원합니다. Vite는 .scss, .sass, .less, .styl, .stylus 파일을 별도 설정 없이 기본 지원합니다. 먼저 설치만 하면, lang 속성으로 SFC에서 바로 사용할 수 있습니다.
<style lang="scss">
/* 여기에 scss를 작성하세요 */
</style>
<style lang="sass">
/* 여기에 sass를 작성하세요 */
</style>
<style lang="less">
/* 여기에 less를 작성하세요 */
</style>
<style lang="stylus">
/* 여기에 stylus를 작성하세요 */
</style>
자세한 내용은 Vite CSS 문서와 @vitejs/plugin-vue 문서를 참고하세요. webpack 사용자는 vue loader 문서를 참고하세요.
PostCSS 사용하기
Nuxt는 postcss가 내장되어 있습니다. nuxt.config 파일에서 설정할 수 있습니다.
export default defineNuxtConfig({
postcss: {
plugins: {
'postcss-nested': {},
'postcss-custom-media': {}
}
}
})
SFC에서 올바른 문법 하이라이팅을 위해 postcss lang 속성을 사용할 수 있습니다.
<style lang="postcss">
/* 여기에 postcss를 작성하세요 */
</style>
기본적으로 Nuxt에는 다음 플러그인이 이미 사전 구성되어 있습니다:
- postcss-import:
@import규칙 개선 - postcss-url:
url()문 변환 - autoprefixer: 벤더 프리픽스 자동 추가
- cssnano: 최소화 및 정리
여러 스타일을 위한 레이아웃 활용
애플리케이션의 서로 다른 부분을 완전히 다르게 스타일링해야 한다면, 레이아웃을 사용할 수 있습니다. 서로 다른 레이아웃에 서로 다른 스타일을 사용하세요.
<template>
<div class="default-layout">
<h1>Default Layout</h1>
<slot />
</div>
</template>
<style>
.default-layout {
color: red;
}
</style>
서드파티 라이브러리 및 모듈
Nuxt는 스타일링에 대해 의견을 강요하지 않으며, 다양한 옵션을 제공합니다. UnoCSS나 Tailwind CSS와 같은 인기 있는 라이브러리 등 원하는 어떤 스타일링 도구도 사용할 수 있습니다.
커뮤니티와 Nuxt 팀은 통합을 쉽게 해주는 많은 Nuxt 모듈을 개발했습니다. 웹사이트의 modules 섹션에서 찾아볼 수 있습니다. 시작에 도움이 될 만한 몇 가지 모듈은 다음과 같습니다:
- UnoCSS: 즉시 사용 가능한 원자적 CSS 엔진
- Tailwind CSS: 유틸리티 우선 CSS 프레임워크
- Fontaine: 폰트 메트릭 폴백
- Pinceau: 적응형 스타일링 프레임워크
- Nuxt UI: 현대 웹 앱을 위한 UI 라이브러리
- Panda CSS: 빌드 타임에 원자적 CSS를 생성하는 CSS-in-JS 엔진
Nuxt 모듈은 기본적으로 좋은 개발 경험을 제공하지만, 선호하는 도구에 모듈이 없다고 해서 Nuxt에서 사용할 수 없다는 뜻은 아닙니다! 직접 프로젝트에 맞게 설정할 수 있습니다. 도구에 따라 Nuxt 플러그인이나 직접 모듈 만들기가 필요할 수 있습니다. 만들었다면 커뮤니티와 공유하세요!
웹폰트 쉽게 불러오기
Nuxt Google Fonts 모듈을 사용해 Google Fonts를 불러올 수 있습니다.
UnoCSS를 사용하는 경우, 웹폰트 프리셋이 포함되어 있어 Google Fonts 등 주요 제공업체에서 폰트를 편리하게 불러올 수 있습니다.
고급
트랜지션
Nuxt는 Vue와 동일한 <Transition> 요소를 제공하며, 실험적인 View Transitions API도 지원합니다.
폰트 고급 최적화
CLS를 줄이기 위해 Fontaine 사용을 권장합니다. 더 고급 기능이 필요하다면, Nuxt 모듈을 만들어 빌드 프로세스나 Nuxt 런타임을 확장하는 것을 고려하세요.
LCP 고급 최적화
글로벌 CSS 파일 다운로드 속도를 높이려면 다음을 수행할 수 있습니다:
- CDN을 사용해 파일을 사용자와 물리적으로 더 가깝게 두세요
- 자산을 압축하세요, 이상적으로는 Brotli 사용
- HTTP2/HTTP3로 전송하세요
- 자산을 동일한 도메인에 호스팅하세요(다른 서브도메인 사용 금지)
이러한 대부분의 작업은 Cloudflare, Netlify, Vercel과 같은 최신 플랫폼을 사용하면 자동으로 처리됩니다. LCP 최적화 가이드는 web.dev에서 확인할 수 있습니다.
모든 CSS가 Nuxt에 의해 인라인된다면, (실험적으로) 렌더된 HTML에서 외부 CSS 파일 참조를 완전히 중지할 수 있습니다. 이것은 후크를 사용해, 모듈이나 Nuxt 설정 파일에 넣어 달성할 수 있습니다.
export default defineNuxtConfig({
hooks: {
'build:manifest': (manifest) => {
// 앱 엔트리, css 리스트 찾기
const css = Object.values(manifest).find(options => options.isEntry)?.css
if (css) {
// 배열의 끝에서 시작해 처음까지 이동
for (let i = css.length - 1; i >= 0; i--) {
// 'entry'로 시작하면 리스트에서 제거
if (css[i].startsWith('entry')) css.splice(i, 1)
}
}
},
},
})