Nuxt3) 시작하기
* Installation
https://nuxt.com/docs/getting-started/installation
Installation · Get Started with Nuxt
Get started with Nuxt quickly with our online starters or start locally with your terminal. You can start playing with Nuxt 3 in your browser using our online sandboxes: Play on StackBlitzPlay on CodeSandbox Start with one of our starters and themes direct
nuxt.com
공식문서와 해당 유튜브 강의를 참고하였다.
Nuxt는 기본적으로 Auto Import를 제공한다.
(npm이 작동하지 않는다면 yarn으로 실행하자.)
npx nuxi init <project-name>
cd <project-name>
npm i
nuxt와 모듈을 설치한다.
// nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
import { resolve } from 'path';
export default defineNuxtConfig({
alias: {
'@': resolve(__dirname, '/'),
},
css: ['~/assets/main.scss'],
ssr: true,
modules: ["@pinia/nuxt"],
components: [
{
path: "~~/components/",
},
],
devServer: {
port: 4000,
},
})
입맛에 맞게 컨피규레이션 파일을 작성한다.
npm i pinia @pinia/nuxt
스토어를 사용하는 pinia를 사용하기로 했다.
assets 폴더에 main.scss를 생성한다.
npm i scss
* Routing
pages 폴더를 만들면 svelte처럼 라우팅이 잡힌다.
index.vue 페이지를 만들면 루트페이지로 인식된다.
ex) http://localhost:4000 -> index.vue
- pages/events.vue
- pages/events/index.vue
- pages/events/:eventId
위 형태로 라우팅을 잡으면 된다.
<template>
<div>
<NuxtLayout>
<header>
<ul>
<li>
<NuxtLink to="/">Home</NuxtLink>
</li>
<li>
<NuxtLink to="/events">Events</NuxtLink>
</li>
</ul>
</header>
<NuxtPage />
</NuxtLayout>
</div>
</template>
마지막으로 app.vue 파일을 위와 같이 수정해주면 index.vue가 인식되고 라우팅이 잡힌다.
NuxtLink를 사용하면 페이지 이동이 가능하다.
// pages/events/[eventId.vue]
<template>
<div>
event id: {{ $route.params.eventId }}
</div>
</template>
<script setup>
const route = useRoute();
const {eventId} = route.params;
console.log(eventId)
</script>
http://localhost:4000/events/123 접속시 파라미터 123을 불러올 수 있다.
* Components
// components/Alert.vue
<template>
<div id="alert-component">
Alert component.
</div>
</template>
<script setup>
</script>
<style lang='scss'>
#alert-component{
background: green;
}
</style>
컴포넌트 폴더에 생성하면 된다.
// pages/index.vue
<template>
<div>
index page
<Alert/>
</div>
</template>
import가 필요없다....
components/Profile/Header/Avatar.vue 구성을 한다.
// components/Profile/Header/Avatar.vue
<template>
<div class="ProfileHeaderAvatar">
PHA
</div>
</template>
<script setup>
</script>
<style lang='scss'>
</style>
구분하기위한 클래스명을 지정해준다.
굳이 지정 안해도 작동에 문제 없음.
<template>
<div>
index page
<Alert/>
<ProfileHeaderAvatar />
</div>
</template>
아무 페이지에서 해당 컴포넌트를 사용할 수 있다.
* Layouts
layouts/default.vue 파일을 생성하면 자동적으로 기본 레이아웃으로 적용된다.
// layouts/default.vue
<template>
<div class="DefaultLayout">
<p>Default layout</p>
<slot/>
</div>
</template>
<slot /> 태그를 사용해 엘리먼트 위치를 조정한다.
레이아웃 하위에 메인(pages/index.vue)가 하위 요소로 구성된다.
// layouts/custom.vue
<template>
<div class="CustomLayout">
<p>Cumtom layout</p>
<slot/>
</div>
</template>
<style lang="scss">
.CustomLayout{
background: gray;
}
</style>
커스텀 레이아웃은 배경색을 변경했다.
새로운 페이지에 다른 레이아웃을 적용해보자.
// pages/custom.vue
<template>
<div>
Custom Page
</div>
</template>
<script setup>
definePageMeta({
layout: 'custom'
})
</script>
스크립트에서 레이아웃을 적용한다.
적용된다.
* Assets
에셋 폴더에 이미지를 추가한다.
// pages/index.vue
<template>
<div>
<img src="~/assets/images/moon.png" alt=""/>
index page
<Alert/>
<ProfileHeaderAvatar />
</div>
</template>
기본적인 사용법은 위와 같다.
//nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
alias: {
asserts: '/<rootDir>/assets',
},
})
컨피그 파일을 수정하면 다음처럼 태그를 사용할 수 있다.
<img src="@/assets/images/moon.png" />
* Public
만약 이미지 파일을 assets 폴더에서 public 폴더로 옮기면 url에서 이미지를 확인할 수 있다.
ex) http://localhost:4000/moon.png
public 폴더는 공개서버로 취급된다.
태그에서는 다음처럼 사용할 수 있다.
<img src="/moon.png" />
* Composables
취지는 자주 사용하는 코드들의 함수 집합.
파일명, 함수명에 use라는 이름을 앞에 사용한다.
// composables/useUtils.ts
export const useUtils = () => {
const sayHello = () => console.log('hello');
return {
sayHello
};
}
// pages/index.vue
<template>
<div>
<img src="~/assets/images/moon.png" alt=""/>
<!-- <img src="@/assets/images/moon.png" alt=""/> -->
index page
<Alert/>
<ProfileHeaderAvatar />
</div>
</template>
<script setup>
const { sayHello } = useUtils();
sayHello();
</script>
VueUse라는 라이브러리는 composables 같은 함수를 제공하며 아주 유용하다고 한다.
VueUse
Collection of essential Vue Composition Utilities
vueuse.org
* Plugins
Plugin은 useNuxtApp()에서 전역으로 호출 가능하며, 커스텀 플러그인을 전역함수로 활용할 수 있다.
- plugins/myPlugin.ts
- plugins/myOtherPlugin/index.ts
위와 같은 형식을 맞춰야한다. (공식문서 참고)
// plugins/myPlugin.ts
export default defineNuxtPlugin(nuxtApp => {
return {
provide: {
sayHi: (msg: string) => console.log(`Hi ${msg}`),
},
}
})
// pages/index.vue
<template>
<div>
<img src="~/assets/images/moon.png" alt=""/>
<!-- <img src="@/assets/images/moon.png" alt=""/> -->
index page
<Alert/>
<ProfileHeaderAvatar />
</div>
</template>
<script setup >
const { sayHello } = useUtils();
sayHello();
// console.log(useNuxtApp())
const { $sayHi } = useNuxtApp();
$sayHi('Jun');
</script>
useNuxtApp()을 콘솔에 찍어보면 '$플러그인'으로 저장되어있다.
이 함수를 불러 사용하면 된다.
* Middleware
~.global.ts 파일명에 글로벌을 붙이면 전역 미들웨어로 작동한다.
단일 미들웨어를 사용하려면 해당 페이지에 아래와 같이 작성한다.
// middleware/check.ts
export default defineNuxtRouteMiddleware((to, from) => {
console.log('hi')
})
// pages/index.vue
<template>
<div>
<img src="~/assets/images/moon.png" alt=""/>
<!-- <img src="@/assets/images/moon.png" alt=""/> -->
index page
<Alert/>
<ProfileHeaderAvatar />
</div>
</template>
<script setup >
definePageMeta({
middleware: "check"
})
const { sayHello } = useUtils();
sayHello();
// console.log(useNuxtApp())
const { $sayHi } = useNuxtApp();
$sayHi('Jun');
</script>
미들웨어 이름은 라우팅과 동일하게 파일명을 따라간다.
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
const isLoggedIn = false;
// redirect destination
if(isLoggedIn){
return navigateTo(to.fullPath);
}
// redirece login page
return navigateTo('/login');
})
위와 같이 전역 미들웨어를 예시로 구성했다.
from은 내가 미들웨어를 실행하기 전의 위치. to는 이동할 위치를 의미한다.
navigateTo() 함수를 사용하여 페이지를 전환시킬 수 있다.
*** 구현되지 않은 페이지로 이동시키면 무한로딩 될 수 있으니 주의하자
* Modules
Modules · Nuxt
Comprehensive UI toolkit for Vue.js and Nuxt.js that empowers you to build amazing user interfaces with ease. With Vunix, you can leverage the power of popular utility-first CSS frameworks like Tailwind CSS, Unocss, WindiCss, or use your own custom CSS cla
nuxt.com
모듈은 라이브러리이다. 누구나 npm으로 퍼블리시 가능.
모듈을 설치 후, nuxt.config.ts에 명시를 해야한다.
처음에 언급한 pinia도 모듈에서 설치한 라이브러리이다.
각 모듈에는 제공한 개발자가 작성한 공식문서를 볼 수 있다.
* State Management
단일 스코프에서 선언한 변수를 다른곳에서 사용하기 위한 방법.
스테이트를 활용하면 다른 컴포넌트, 스코프에서도 데이터(변수 등)를 활용할 수 있다. (Vuex, Pinia)
작은 프로젝트에서는 useState,
큰 포르젝트에서는 pinia를 추천한다고 한다.
https://www.vuemastery.com/blog/nuxt-3-state-mangement-pinia-vs-usestate
Nuxt 3 State Management: Pinia vs useState | Vue Mastery
What should you use for state management in Nuxt 3? Let’s compare Nuxt’s useState to Pinia.
www.vuemastery.com
아래는 피니아에 대한 공식문서와 공식 페이지이다.
https://nuxt.com/modules/pinia
Pinia Module · Nuxt
📣 Just Released! Our official workshop to level up from Nuxt 2 to Nuxt 3. Register now
nuxt.com
https://pinia.vuejs.org/ssr/nuxt.html
Pinia | The intuitive store for Vue.js
Intuitive, type safe, light and flexible Store for Vue
pinia.vuejs.org
store 폴더에 스토어를 생성한다.
// store/myStore.ts
import { defineStore } from "pinia"
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const name = ref('Eduardo')
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, name, doubleCount, increment }
})
// pages/index.vue
<script setup >
import { useCounterStore } from '~/stores/myStore';
const store = useCounterStore();
console.log(store)
</script>
사용할 페이지에서 스토어를 호출하고 활용한다.
* Server
https://nuxt.com/docs/guide/directory-structure/server
server/ · Nuxt Directory Structure
Nuxt automatically scans files inside the ~/server/api, ~/server/routes, and ~/server/middleware directories to register API and server handlers with HMR support. Each file should export a default function defined with defineEventHandler(). The handler can
nuxt.com
구성은 아래와 같다.
// server/api/hello.get.ts or server/api/hello.ts
export default defineEventHandler((event) => {
return {
api: "works",
}
})
공식문서의 예제대로 코드를 짜고 http://localhost:4000/api/hello 접속을 하면
위와 같은 결과가 떨어진다.
// pages/index.vue
<script setup >
const response = await $fetch('/api/hello');
console.log(response);
</script>
프론트 사이드에서 api를 호출한다.
async는 선언 할 필요가 없다.
결과는 다음과 같다.
// server/routes/hello.ts
export default defineEventHandler((event) => {
return 'hello world from routes folder <a href="/">Go home</a>'
})
routes 폴더에 파일을 생성한다.
해당 url로 접속해본다.
http://localhost:4000/hello
JSON 형태의 리턴으로 변경하면 api 구성과 동일하게 작동한다.
/api 프리픽스를 엔드포인트에 붙이는지의 차이다.
api 폴더에서는 파일명으로 메소드를 나눌 수 있다.
~.get.ts
~.post.ts
~.patch.ts
routes 폴더에서는 ~.all.ts의 형태로 작동한다. 모든 메소드를 받는 것.
명확한 구현을 위해서는 api폴더에서 RestAPI를 구현하는게 좋겠다.
* useLazyFetch
CSR(Client Side Rendering)에서 사용.
pending -> skeleton이 필요할 때 사용한다.
반대로 useFetch는 SSR에서 사용.
// server/hello.get.ts
export default defineEventHandler(async (event) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([
{
id: 1,
name: 'jun'
},{
id: 2,
name: 'JUN'
},
]);
}, 2000)
})
})
api에서 2초 대기를 걸어둔다.
// pages/index.vue
<template>
<div>
<img src="~/assets/images/moon.png" alt=""/>
<!-- <img src="@/assets/images/moon.png" alt=""/> -->
<h1>Index Page</h1>
<p>{{ pending ? 'Loading' : data }}</p>
</div>
</template>
<script setup >
const {data, pending} = await useLazyFetch('/api/hello');
</script>
2초간 'Loading' 문구가 뜨고 이후 데이터가 로딩된다.
* SEO & Header
https://nuxt.com/docs/getting-started/seo-meta
SEO and Meta · Get Started with Nuxt
useHeaduseSeoMetaComponents
nuxt.com
-- 모든 페이지에 적용하기
https://nuxt.com/docs/api/configuration/nuxt-config#head
Nuxt Configuration Reference · Nuxt
defineNuxtRouteMiddleware
nuxt.com
=> nuxt.config.ts 파일에 app/head에 작성한다.
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
app:{
head: {
title: 'Study nuxt',
meta: [
{
name: 'description',
content: 'This is description',
},
{
name: "viewport",
content: "width=device-width, initial-scale=1"
},
{
charset: "utf-8"
}
],
link: [],
style: [],
script: [],
noscript: []
}
},
})
-- Page by page로 적용하기
https://nuxt.com/docs/getting-started/seo-meta#usehead
SEO and Meta · Get Started with Nuxt
useHeaduseSeoMetaComponents
nuxt.com
=> Page script마다 useHead를 사용한다.
<script setup lang="ts">
useHead({
title: 'My App',
meta: [
{ name: 'description', content: 'My amazing site.' }
],
bodyAttrs: {
class: 'test'
},
script: [ { innerHTML: 'console.log(\'Hello world\')' } ]
})
</script>
-- Component마다 적용하기
https://nuxt.com/docs/getting-started/seo-meta#components
SEO and Meta · Get Started with Nuxt
useHeaduseSeoMetaComponents
nuxt.com
<script setup>
const title = ref('Hello World')
</script>
<template>
<div>
<Head>
<Title>{{ title }}</Title>
<Meta name="description" :content="title" />
<Style type="text/css" children="body { background-color: green; }" />
</Head>
<h1>{{ title }}</h1>
</div>
</template>
* Hooks
https://nuxt.com/docs/api/advanced/hooks
Lifecycle Hooks · Nuxt
defineNuxtRouteMiddleware
nuxt.com
-- 전역으로 사용하기
// nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
import { resolve } from 'path';
export default defineNuxtConfig({
hooks: {
ready: (ctx) => console.log(`Context: ${ctx}`)
})
-- 단일로 사용하기
https://nuxt.com/docs/guide/going-further/hooks
Lifecycle Hooks · Nuxt Advanced
Nuxt provides a powerful hooking system to expand almost every aspect using hooks. This feature is powered by unjs/hookable. These hooks are available for Nuxt Modules and build context. nuxt.config export default defineNuxtConfig({ hooks: { 'close': () =>
nuxt.com
// plugins/myPlugin.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:start', () => {
console.log('hello plugin')
})
})
* Nuxt-Content
Content made easy for Vue developers
The file-based CMS for your Nuxt application, powered by Markdown and Vue components.
content.nuxtjs.org
Javascript, HTML 없이 md으로 구현 가능한 static 페이지를 만들 때 사용한다.
SEO에 최적화되어있음.
npm install --save-dev @nuxt/content
위 폴더 구조를 띄며, URL에 /about /profile 등 라우팅이 가능하다.
* API docs
docus 라이브러리를 활용한다.
Home
The best place to start your documentation.
docus.dev