๐ข ๋ค์ด๊ฐ๊ธฐ ์ ์
- ์ด๋ฒ ํฌ์คํ ์์ Nuxt.js ์ ๊ฐ๋ ๊ณผ ๊ตฌ์กฐ์ ๋ํด ์์๋ณด๊ณ ๊ฐ๋จํ ์์ ๋ฅผ ๊ตฌํํด๋ณธ๋ค.
CSR vs SSR
Nuxt.js ์ ๋ํด ์์๋ณด๊ธฐ์ ์์, SSR๊ณผ CSR์ ๋ํด ์์๋ณด์.
์ด ๋๊ฐ์ง ๊ฐ๋
์ Nuxt.js์ ๊ฐ์ฅ ํฐ ํน์ง์ด๋ผ๊ณ ํ ์ ์๋ค.
CSR (Client Side Rendering)
ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ ๋๋ง.
SPA(Single Page Application)์์ ์ฌ์ฉ๋๋ ๋ฐฉ์์ด๋ค.
SPA๋, ์ต์ด ํ๋ฒ ํ์ด์ง๋ฅผ ์ ์ฒด ๋ก๋ฉํ ํ ๋ฐ์ดํฐ๋ง ๋ณ๊ฒฝํด์ ์ฌ์ฉํ ์ ์๋ ์น ์ดํ๋ฆฌ์ผ์ด์ ์ ๋งํ๋ค.
์ต์ด ํ์ด์ง๋ฅผ ๋ก๋ฉํ ์์ ๋ถํฐ๋ ํ์ด์ง ๋ฆฌ๋ก๋ฉ(๊น๋นก์) ์์ด ํ์ํ ๋ถ๋ถ๋ง ์๋ฒ๋ก๋ถํฐ ๋ฐ์์ ํ๋ฉด์ ๊ฐฑ์ ํ๋ ๋ ๋๋ง ๋ฐฉ๋ฒ์ด๋ค.
ํ์ํ ๋ถ๋ถ๋ง ๊ฐฑ์ ํ๊ธฐ ๋๋ฌธ์ ํ์ด์ง ์ด๋์ด ์์ฐ์ค๋ฝ๋ค.
์๋ฒ์์ View๋ฅผ ๋ ๋ํ์ง ์๊ณ ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์ HTML์ ๋ค์ด ๋ฐ์ ๋ค์ JS ํ์ผ์ด๋ ๊ฐ์ข ๋ฆฌ์์ค๋ฅผ ๋ค์ด ๋ฐ์ ํ ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋งํ์ฌ ๋ณด์ฌ์ฃผ๊ธฐ ๋๋ฌธ์ SSR ๋ณด๋ค๋ ์ด๊ธฐ View๋ฅผ ๋ณผ ์ ์๊ธฐ๊น์ง ์๊ฐ์ด ๊ฑธ๋ฆฐ๋ค.
View๊ฐ ๋ณด์ฌ์ง ์์ ์์ ๋ฐ๋ก ์ธํฐ๋ ์ (์ํธ์์ฉ)์ด ๊ฐ๋ฅํ๋ค.
๋๋ถ๋ถ์ ์น ํฌ๋กค๋ฌ, ๋ด๋ค์ด JS ํ์ผ์ ์คํ์ํค์ง ๋ชปํ๊ณ HTML์์๋ง ์ปจํ ์ธ ๋ฅผ ์์งํ๋ค. ๋๋ฌธ์ CSR ๋ฐฉ์ ํ์ด์ง๋ฅผ ๋น ํ์ด์ง๋ก ์ธ์ํ๊ฒ ๋๋๋ฐ, ์ด๋ ๊ฒ์์์ง์ด ์ ๋๋ก ๋ ธ์ถ๋์ง ๋ชปํ์ฌ ์นํ์ด์ง ์ ์ ์ด ์ค์ด๋ค๊ฒ ๋๋ ์์ธ์ด ๋๋ค.
๐ CSR ์ฅ์
1. ์์ฐ์ค๋ฌ์ด UX
2. ํ์ํ ๋ฆฌ์์ค๋ง ๋ถ๋ถ์ ์ผ๋ก ๋ก๋ฉ(์ฑ๋ฅ)
3. ์๋ฒ์ ํ ํ๋ฆฟ ์ฐ์ฐ์ ํด๋ผ์ด์ธํธ๋ก ๋ถ์ฐ(์ฑ๋ฅ)
4. ์ปดํฌ๋ํธ ๋ณ ๊ฐ๋ฐ ์ฉ์ด(์์ฐ์ฑ)
5. ๋ชจ๋ฐ์ผ ์ฑ ๊ฐ๋ฐ์ ์ผ๋์ ๋๋ค๋ฉด ๋์ผํ API๋ฅผ ์ฌ์ฉํ๋๋ก ์ค๊ณ ๊ฐ๋ฅ(์์ฐ์ฑ)๐ CSR ๋จ์
1. JavaScript ํ์ผ์ ๋ฒ๋ค๋งํด์ ํ๋ฒ์ ๋ฐ๊ธฐ ๋๋ฌธ์ ์ด๊ธฐ ๊ตฌ๋ ์๋ ๋๋ฆผ(webpack์ code splitting์ผ๋ก ํด๊ฒฐ)
2. ๊ฒ์ ์์ง ์ต์ ํ(SEO)๊ฐ ์ด๋ ค์
3. ๋ณด์ ์ด์(ํ๋ก ํธ์๋์ ๋น์ฆ๋์ค ๋ก์ง ์ต์ํ)
๐ SEO (Search Engine Optimzation)
์น ์ฌ์ดํธ๊ฐ ๊ฒ์ ๊ฒฐ๊ณผ์ ๋ ์๋ณด์ด๋๋ก ์ต์ ํํ๋ ๊ณผ์ .
๊ฒ์ ๋ญํฌ ๊ฐ์ ์ด๋ผ๊ณ ๋ ํ๋ค.
SSR (Server Side Rendering)
์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง.
MPA(Multiple Page Application)์์ ์ฌ์ฉ๋๋ ๋ฐฉ์์ด๋ค.
๋ง ๊ทธ๋๋ก ์๋ฒ์์ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ค ํ์ด์ง๋ฅผ ๋ชจ๋ ๋ ๋๋ง ํ์ฌ ๋์ฐ๋ ๋ฐฉ์์ด๋ค.
์์ฒญ(request) ๋ง๋ค ์๋ก๊ณ ์นจ์ด ์ผ์ด๋๋ค. ์๋ฒ์ ์๋ก์ด ํ์ด์ง์ ๋ํ ์์ฒญ์ ๊ตฌํ๋ ๋ฐฉ์์ด๊ธฐ ๋๋ฌธ์ด๋ค.
View๋ฅผ ์๋ฒ์์ ๋ ๋๋งํ์ฌ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์ ์ฒซ ๋ก๋ฉ์ด ๋งค์ฐ ์งง๋ค.
View๋ฅผ ์๋ฒ์์ ์ ๋ถ ๋ ๋๋งํ์ฌ ๋ด๋ ค์ค์ HTML์ ๋ชจ๋ ์ปจํ
์ธ ๊ฐ ์ ์ฅ๋์ด ์๊ธฐ ๋๋ฌธ์ SEO ์ ์ฉ์ ํฐ ๋ฌธ์ ๊ฐ ์๋ค.
๐ SSR ์ฅ์
1. SEO (๊ฒ์์์ง ์ต์ ํ)๐ SSR ๋จ์
1. ํ์ด์ง ์ด๋ ์ ํ๋ฉด ๊น๋นก์
2. ํ์ด์ง ์ด๋ ์ ๋ถํ์ํ ํ ํ๋ฆฟ๋ ์ค๋ณตํด์ ๋ก๋ฉ(์ฑ๋ฅ)
3. ์๋ฒ ๋ ๋๋ง์ ๋ฐ๋ฅธ ๋ถํ(์ฑ๋ฅ)
4. ๋ชจ๋ฐ์ผ ์ฑ ๊ฐ๋ฐ ์ ์ถ๊ฐ์ ์ธ ๋ฐฑ์๋ ์์ ํ์(์์ฐ์ฑ)
๐ก ์ SSR ๋ฐฉ์์ old server-side rendering ๋ฐฉ์์ด๋ค.
๊ณผ๊ฑฐ์ ํ์ด์ง๋ฅผ ์ด๋ํ ๋๋ง๋ค reload(๊น๋นก์)๊ฐ ์ผ์ด๋ฌ๋ ์ ๋ฐฉ์์ ๋ณด์ํ๊ธฐ ์ํด ์๋ก์ด SSR ๊ฐ๋ ์ด ๋ฑ์ฅํ์๋๋ฐ, ์ด ์ SSR์ ์ ์ฉํ ์ ํ๋ฆฌ์ผ์ด์ ์ Universal App์ด๋ผ๊ณ ํ๋ค.
Universal App์ SSR ๋์ ๋ฐฉ์์ ๋ํด์ Nuxt.js๋ฅผ ์ค๋ช ํ๋ฉฐ ์ด์ด๋๊ฐ๋๋ก ํ๊ฒ ๋ค.
๐ Nuxt.js๋?
Nuxt.js๋ Vue.js ํ๋ ์์ํฌ ๊ธฐ๋ฐ์ ๊ฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ถ์ ๋์์ ์ฃผ๋ ํ๋ ์์ํฌ์ด๋ค.
ํ๋ ์์ํฌ๋ฅผ ์ํ ํ๋ ์ ์ํฌ? ๋ฌด์จ ๋ป์ธ์ง ์ดํด๊ฐ ์ ๋์ง ์์ ์ ์๋ค.
ํ์ด์ ์ค๋ช
ํด๋ณด์๋ฉด...
Vue.js ํ๋ก์ ํธ์์ ์ฌ์ฉ๋๋ ์ฌ๋ฌ ์ ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฌํ๊ณ ์๋ ํ๋ ์์ํฌ๋ผ๊ณ ๋ณด๋ฉด ๋๋ค.
Nuxt.js ์ ํฌํจ๋ ๊ธฐ๋ฅ๋ค์ ๋ค์๊ณผ ๊ฐ๋ค.
- Vue 2
- Vue Router
- Vuex
- Vue Server Renderer
- vue-meta
- vue-loader
- babel-loader
- Webpack
๐ Nuxt.js์ ํน์ง
Vue + ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ตฌ์กฐ์ด๊ธฐ ๋๋ฌธ์, Nuxt.js๋ ๋ค์๊ณผ ๊ฐ์ ํน์ง์ ๊ฐ์ง๋ค.
- Vue ํ์ผ ์ฌ์ฉ
- ์ฝ๋ ๋ถํ ์๋ํ
- ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง
- ๋น๋๊ธฐ ๋ฐ์ดํฐ ๊ธฐ๋ฐ์ ๊ฐ๋ ฅํ ๋ผ์ฐํ ์์คํ
- ์ ์ ํ์ผ ์ ์ก
- ES2015+ ์ง์
- JS & CSS ์ฝ๋ ๋ฒ๋ค๋ง ๋ฐ ์์ถ
<head>
์์ ๊ด๋ฆฌ (<title>
,<meta>
, ๊ธฐํ)- ๊ฐ๋ฐ ์ค Hot module ๋์ฒด
- ์ ์ฒ๋ฆฌ๊ธฐ ์ง์ : SASS, LESS, Stylus ๋ฑ
๐ Nuxt.js๋ ์ธ์ ์ฌ์ฉํ๋๊ฐ?
๊ฒฐ์ ์ ์ผ๋ก, SEO ๊ฐ์ ์ ํ ๋ ์ฌ์ฉ๋๋ค.
๐ Nuxt.js ์ค์น
npx
npx create-nuxt-app <project-name>
yarn
yarn create nuxt-app <project-name>
npm
npm init nuxt-app <project-name>
vue-cli๋ฅผ ํตํ Nuxt.js ์ค์น
npm i -g @vue/cli
npm i -g @vue/cli-init
vue init nuxt-community/starter-template <project-name>
cd <project-name>
npm i
๐จ ์ฃผ์
vue cli init ๊ธฐ๋ฅ์ Vue CLI 2.x์ ๊ธฐ๋ฅ์ผ๋ก ํ์ฌ ๋ ๊ฑฐ์(legacy)๋ก ์ทจ๊ธ๋๊ณ ์๋ค.
cli init template ๋ฐฉ์์ ๊ถ์ฅํ์ง ์์ผ๋ ์ฐธ๊ณ .
์์ธํ
Nuxt.js ๋จ์ผ ์ค์น
npm i nuxt
๋๋ npm ์ผ๋ก ์ค์นํด๋ณด์๋ค. ์ค์น์ ์ ํํ๋ ์ต์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
cd <ํ๋ก์ ํธ ๋ช
>
npm run dev
npm run dev
๋ช
๋ น์ด ์คํ ํ ์์ ๊ฐ์ ํ๋ฉด์ด ๋ํ๋๋ฉด Nuxt.js ์ค์น ๋ฐ ์คํ ์ฑ๊ณต! ๐
๐ Nuxt.js ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
assets
- css, image, font์ ๊ฐ์ ๋ฆฌ์์ค๋ค์ ํฌํจํ๋ค.
components
- ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ๋ ์ปดํฌ๋ํธ๋ค์ ํฌํจํ๋ค.
- ํด๋น ๊ฒฝ๋ก์ ์์น๋ ์ปดํฌ๋ํธ๋ค์ Nuxt.js์ ๋น๋๊ธฐ ๋ฐ์ดํฐ ํจ์์ธ
asyncData
๋๋fetch
๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
layouts
- ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด์ ๋ํ ๋ ์ด์์์ ํฌํจํ๋ค.
- ๊ธฐ๋ณธ์ผ๋ก
default.vue
๊ฐ ์์ฑ๋์ด ์๋ค. - ๋๋ ํ ๋ฆฌ ์ด๋ฆ ๋ณ๊ฒฝ ๋ถ๊ฐ
middleware
- ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ๋ middleware๋ฅผ ํฌํจํ๋ค.
- middleware๋ ํ์ด์ง ๋๋ ๋ ์ด์์์ด ๋ ๋๋ง๋๊ธฐ ์ ์ ์คํ๋๋ค.
- middleware๋ฅผ ํ์ด์ง๋ ๋ ์ด์์์ ๋ฐ์ธ๋ฉํ์๋ค๋ฉด ํด๋น ํ์ด์ง๋ ๋ ์ด์์์ด ์คํ๋๊ธฐ ์ ์ ๋งค๋ฒ ์คํ๋๋ค.
node_modules
- Nuxtํ๋ ์์ํฌ์ ํต์ฌ ๊ธฐ๋ฅ์ ํ์ฅ, ํตํฉ, ์ถ๊ฐํ ์ ์๋ค.
- ์ฌ์ฉ์๊ฐ ์ง์ ๋ชจ๋์ ์์ฑํ ์ ์๋ค.
pages
- ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ด์ง ๊ตฌ์ฑ์ ํฌํจํ๋ค.
- ์ด ๋๋ ํ ๋ฆฌ์ ๊ตฌ์กฐ์ ๋ฐ๋ผ router๊ฐ ์๋ ์์ฑ๋๋ค.
- ๋๋ ํ ๋ฆฌ ์ด๋ฆ ๋ณ๊ฒฝ ๋ถ๊ฐ
plugins
- ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ธ๋ฉ๋ ์ธ๋ถ ํน์ ๋ด๋ถ plugins๋ฅผ ํฌํจํ๋ค.
- ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ธ์คํด์คํ ๋๊ธฐ ์ ์ ์คํํ๋ฉฐ ์ ์ญ์ ์ผ๋ก ๊ตฌ์ฑ ์์๋ฅผ ๋ฑ๋กํ๊ณ ํจ์ ๋๋ ์์๋ฅผ ์ฝ์ ํ ์ ์๋ค.
static
- ์ ์ ํ์ผ ํฌํจ
- ๊ตฌ์ฑ์ ๋ฐ๋ผ html, js ํ์ผ๋ ํฌํจ์ํฌ ์ ์๋ค.
- ๋๋ ํ ๋ฆฌ ์ด๋ฆ ๋ณ๊ฒฝ ๋ถ๊ฐ
store
- ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ๋ vuex store ํ์ผ๋ค์ ํฌํจํ๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก ๋นํ์ฑํ ์ํ
- store ๋๋ ํ ๋ฆฌ์
index.js
ํ์ผ์ ์์ฑํ๋ฉด store๊ฐ ํ์ฑํ ๋๋ค. - ๊ตฌ์ฑ์ ๋ฐ๋ผ ๋ชจ๋ ํํ์ store๋ฅผ ํ์ฑํ ์ ์๋ค.
content
- ๐ ์ต์
@nuxt/content
๋ชจ๋์ ์ฌ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ฅํ ์ ์๋ค.- Markdown, JSON, YAML, XML, CSV ์ ๊ฐ์ ํ์ผ์ ๊ฐ์ ธ์ค๊ณ ๊ด๋ฆฌํ ์ ์๋ค.
Vue.js ์ Nuxt.js์ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ ๋น๊ต
// Vue.js
npm i -g @vue/cli
vue create <ํ๋ก์ ํธ ๋ช
>
cd <ํ๋ก์ ํธ ๋ช
>
vue add vuex
vue add router
// Nuxt.js
npm init nuxt-app <ํ๋ก์ ํธ ๋ช
>
์ ๋ช
๋ น์ด๋ก ์์ฑํ ๋ ํ๋ก์ ํธ๋ฅผ ๋น๊ตํด๋ณด์๋ค.
๋ฒ ์ด์งํ Vue.js ํ๋ก์ ํธ์ Vuex์ Vue Router๋ฅผ ์ถ๊ฐํ ์ํ์ด๊ณ , Nuxt.js๋ ๋ณ๋ค๋ฅธ ์ถ๊ฐ๋ฅผ ํ์ง ์์๋ค.
Vue.js ์์ src
ํด๋์ ์๋ ๋ด์ฉ๋ค์ด Nust.js์์ ์ ๋ฐ์ ์ผ๋ก ๋ฃจํธ ๋ ๋ฒจ๋ก ์ฌ๋ผ์ ์์๋ค.
Vue.js ์ Nuxt.js ๊ฐ ๋๋ค ๊ฐ์ง๊ณ ์๋ ๋๋ ํ ๋ฆฌ๋ ์ ์ผ๋ก ์ด์ด๋ณด์๋ค.
Vue.js ํ๋ก์ ํธ์์ Router ๊ด๋ จ ๋๋ ํ ๋ฆฌ๊ฐ router
, view
์์ง๋ง Nuxt.js ํ๋ก์ ํธ์์ pages
ํด๋ ํ๋๊ฐ ๋์ ํ๋ค.
Vue.js ํ๋ก์ ํธ์์ Router๋ฅผ ์ค์ ํด์ฃผ๋ ค๋ฉด router/index.js
์์ ์ง์ ๋ผ์ฐํฐ๋ฅผ ๋ฑ๋กํด์คฌ์ด์ผ ํ๋ค.
ํ์ง๋ง Nuxt.js ๋ pages
ํด๋์ ๊ตฌ์กฐ๋๋ก ๋ผ์ฐํฐ๋ฅผ ์๋์ผ๋ก ์์ฑํด์ค๋ค๊ณ ํ๋ค ๐ฎ
Vue.js ์์ ๋ณด์ด์ง ์๋ middleware
, layouts
, plugins
๋๋ ํ ๋ฆฌ ๋ฑ๋ ํ์ธํ ์ ์์๋ค.
๐ Nuxt.js ๋ ๋๋ง ๋ชจ๋
Nuxt.js๋ Single Page App(SPA), Universal App, Static App์ ์ง์ํ๋ค.
์ด๋ nuxt.config.js
์์ mode
ํ๋กํผํฐ๋ฅผ ํตํด ์ค์ ํ ์ ์๋ค. (mode: 'spa'/'universal'
)
์ ์คํฌ๋ฆฐ์ท(๊ณต์๋ฌธ์)์ ๋ณด๋ฉด universal
์ ๋ํด "Isomorphic application (server-side rendeing + client-side navigation)" ๋ผ๊ณ ์ค๋ช
ํ๊ณ ์๋ค.
์ด๊ฑด ๋ฌด์จ ์๋ฏธ์ผ๊น?
๐ข ์ฐธ๊ณ )
ssr์ mode๋ก ์ง์ ํด ์ฃผ๋ ๋ฐฉ์์ deprecated ๋์๋ค.
build: { ssr: true/false } ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝ๋จ. ์ฐธ๊ณ
server-side rendering + client-side navigation
์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR) + ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ค๋น๊ฒ์ด์
(CSN)์ ๋ฌด์จ ๋ง์ผ๊น?
์ด๋ "๐ข ๋ค์ด๊ฐ๊ธฐ ์ ์" ๋ถ๋ถ์์ ์ธ๊ธํ์๋ ์๋ก์ด ๋ฐฉ์์ SSR, ์ฆ Universal App์ ๋์ ๋ฐฉ์์ ์๋ฏธํ๋ค.
๊ณผ๊ฑฐ์ ํ์ด์ง๋ฅผ ์ด๋ํ ๋๋ง๋ค ๊น๋นก์์ด ์ผ์ด๋ฌ๋ ์ ์ ๋ณด์ํ๊ธฐ ์ํด ์๋กญ๊ฒ ๋ํ๋ ๋ ๋๋ง ๋ฐฉ๋ฒ์ด๋ค.
์ฒซ ํ๋ฉด๋ง ๊ณผ๊ฑฐ์ ์๋ฒ ๋ ๋๋ง ์ฒ๋ผ ์์ฑ๋ HTML์ ๋ฟ๋ ค์ฃผ๊ณ (SSR), ์ด ํ์ AJAX๋ก ๋์ ๋ผ์ฐํ
์ ์ํํ์ฌ ํ์ํ ๋ฐ์ดํฐ ๋ง ๊ฐ์ ธ์ฌ ์ ์๋ค๋ฉด ์ข๊ฒ ๋ค(CSN)๊ณ ์๊ฐํ์ฌ ๋ฑ์ฅํ ๋ ๋๋ง ๋ฐฉ์์ด๋ค.
Universal App์ ๋์ ๊ณผ์ ์ ์ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
์ฒ์ ๋ฆฌํ์คํธ๊ฐ ๋์ฐฉํ๋ฉด ์๋ฒ ์ฌ์ด๋์์ nuxtServeInit
, middleware
validate()
, asyncData()
, fetch()
๋ฑ์ ๊ณผ์ ์ ๊ฑฐ์ณ์ ๋ ๋๋งํ ํ์ด์ง๋ฅผ responseํ๋ค. (๊ฐ ๊ณผ์ ์ ์๋์์ ๊ตฌ์ฒด์ ์ผ๋ก ๋ค๋ฃฌ๋ค.)
๐ ์ฌ๊ธฐ๊น์ง๊ฐ SSR(Server-side Rendering) ๋ถ๋ถ์ด๋ค. ์๋ฒ์์ ํ์ด์ง๋ฅผ ๋ ๋๋ง!
๐ ์ด ํ, nuxt-link ํ๊ทธ๋ก Navigate๊ฐ ์ด๋ฃจ์ด์ง๋๋ฐ, ์ด๋ฅผ CSN(Client-side Navigation) ์ด๋ผ๊ณ ํ๋ค.
CSN์ ๋ํด์ ๊ตฌ์ฒด์ ์ผ๋ก ์์๋ณด์.
CSN์ ํฌ๊ฒ ํ๋ฆฌํ์น + ํ์ด๋๋ ์ด์ ๊ณผ์ ์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ค.
Nuxt.js๋ Universal ๋ชจ๋์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์์ฒญํ URL์ ํตํด ๋ก๋ํด์ผํ ํ์ด์ง๋ง!(์ ๋ถ X) ์๋ฒ์์ ๋ ๋๋งํ๊ณ ํด๋ผ์ด์ธํธ์๊ฒ ๋๊ฒจ์ค๋ค.
์์ฒญ์ ๋ฐ์ ๋ ๋ง๋ค ์๋ฒ์์ ๋ ๋๋ง์ ํ๋ค๋ฉด ๋น์ฐํ ๊น๋นก์์ด ๋ฐ์ํ ์ ๋ฐ์ ์๋ค.
ํ์ง๋ง Nuxt.js์ Universal ๋ชจ๋์์ ๊น๋นก์์ด ๋ฐ์ํ์ง ์๋๋ฐ, ๊ทธ ์ด์ ๋ ๋ฐ๋ก ํ๋ฆฌ ํ์น(Pre-fetch) ๋๋ฌธ์ด๋ค.
ํ๋ฆฌํ์น๋ CSN(client side navigation) ์ ํด๋นํ๋ ๊ณผ์ ์ด๋ค.
์ฆ, Universal App์์๋ง ์ฌ์ฉ๋๋ ๋ฐฉ์์ด ์๋๋ผ Vue CLI ์ ๊ฐ์ SPA์์๋ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์์ ์์๋์.
ํ๋ฆฌ ํ์น
ํ๋ฆฌ ํ์น๋ ๋ฏธ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์จ๋ค ๋ผ๋ ๋ป์ด๋ค.
๋ง ๊ทธ๋๋ก, ๋ ๋๋ง ํด์ผํ ๋ค์ ํ์ด์ง๋ฅผ ๋ฏธ๋ฆฌ ๋ฐ์์จ๋ค๋ ๋ป.
Nuxt.js ๊ฐ ์์ธ์ ํ๋ ๊ฒ๋ ์๋๊ณ ... ์ด๋ป๊ฒ ๋ฏธ๋ฆฌ ๊ฐ์ง๊ณ ์ฌ ์ ์์๊น?
์ด๋ Nuxt.js์ ๊ธฐ๋ฅ ์ค ํ๋์ธ nuxt-link
๋ฅผ ํตํด์ ๊ฐ๋ฅํ๋ค. (Vue router์ router-link
์ ๊ฐ๋ค๊ณ ๋ณด๋ฉด ๋จ)
Nuxt.js๋ ๊ฐ์ฅ ์ฒ์ ์๋ฒ์์ ๋ฐ์ดํฐ์ ํจ๊ป HTML์ ๋ ๋๋ง ํด ์ค๊ณ ,
๊ทธ ์ดํ viewport (ํ๋ฉด์ ๋ณด์ฌ์ง๋ ํ์ด์ง) ์ nuxt-link ๋ก๋ถํฐ ๋ค์ ํ์ด์ง๋ฅผ ์์ธก ํด
๋ฐฑ ๊ทธ๋ผ์ด๋์์ ์ฒญํฌ ํ์ผ์ ๋ค์ด๋ก๋ ํด์จ๋ค.
์ด ๋, ๋ฏธ๋ฆฌ ๊ฐ์ง๊ณ ์ค๋ ๋ฐ์ดํฐ(์ฒญํฌ ํ์ผ)์ ํ์์ js์ด๋ค.
Nuxt.js๋ ์๋ code splitting(ํ์ผ ์ฉ๋์ ์ค์ด๊ธฐ ์ํด ์ฝ๋๋ฅผ ๋๋์ง ํ๋ ๊ฒ)์ ์ง์ํ๊ธฐ ๋๋ฌธ์,
์ ํ์ด์ง๋ฅผ ๋ ๋๋ง ํ๊ณ ์ถ์ ๋ ์๋ฒ์ ๋งค๋ฒ ๋ ๋๋ง ํ HTML์ ์์ฒญํ๋ ๋์ ,
๋ธ๋ผ์ฐ์ ๊ฐ ๋ ๋๋ง ํ ์ ์๋๋ก ๋๋ js ํ์ผ์ ์์ฒญํ๋ค.
์์ฝํ์๋ฉด, Universal App์ด ๊น๋นก์ ์์ด ํ์ด์ง๋ฅผ ๋ก๋ํด ์ฌ ์ ์๋ ์ด์ ๋
๋ค์ ํ์ด์ง์ ๋ํ ๋ฐ์ดํฐ(.js)๋ฅผ ๋ฏธ๋ฆฌ ๋ฐ์์ค๊ธฐ(ํ๋ฆฌ ํ์น) ๋๋ฌธ์ด๋ค.
ํ์ด๋๋ ์ด์
Hydration.
๋ ๋๋ง ๊ณผ์ ์ ๋ง์น๊ณ ๋ธ๋ผ์ฐ์ ๋ก ์ ๋ฌ๋ HTMLํ์ผ ์์ ๋จ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ค์ ์คํํ๋ ๋์์ด๋ค.
ํ์ด๋๋ ์ด์
์ผ๋ก ์ธํด SSR ์ฑ์ ๊ธฐ์กด์ SPA์ ๋์ผํ ๋์๊ณผ ๋ฐ์์ฑ์ ๋ณด์ฅํ ์ ์๊ฒ ๋๋ค.
์ฉ์ด ๊ทธ๋๋ก ๋ถ์์ ํ HTML ํ์ผ์ด๋ผ๋ '๋ง๋ฅธ ๋
'์ ์๋ฐ์คํฌ๋ฆฝํธ๋ผ๋ '๋ฌผ'์ ๋ฟ๋ฆฌ๋ ์ผ์ด๋ค.
โณ ์ ๋ฆฌ
- Universal App์ server side rendering ๊ณผ client side navigation ๊ณผ์ ์ ํตํด ๋์ํ๋ค.
- server side rendering์ ์ฐ๋ฆฌ๊ฐ ์๊ณ ์๋ ์๋ฒ์์ HTML์ ๋ ๋๋ง ํ๋ ๋ฐฉ์์ด๋ค.
- server side rendering ํ, view port์ nuxt-link ํ๊ทธ๋ฅผ ํตํด ๋ค์ ํ์ด์ง๋ฅผ ๋ฏธ๋ฆฌ ๋ค์ด๋ก๋ ํด ์จ๋ค. (ํ๋ฆฌํ์น)
- ๋ฏธ๋ฆฌ ๋ค์ด๋ก๋ ํด ์๊ธฐ ๋๋ฌธ์ ๊น๋นก์ ์์ด ํ์ด์ง ์ด๋์ด ๊ฐ๋ฅํ๋ค.
- ํ์ด์ง ์ด๋ ํ ์ด๋ฃจ์ด์ง๋ ๋์๋ค์ ํ์ด๋๋ ์ด์ ์ด๋ผ๊ณ ํ๋ค.
- ํ์ด๋๋ ์ด์ ์ผ๋ก ์ธํด Universal App์ SPA ์ ๋์ผํ ๋์๊ณผ ๋ฐ์์ฑ์ ๋ณด์ฅ ํ ์ ์๋ค.
Isomorphic application
Isomorphic ์ ์ง์ญํ๋ฉด "๋์ผํ ๊ตฌ์กฐ์" ๋ผ๋ ๋ป์ด๋ค.
๋ณดํต Isomorphic JavaScript๋ผ๋ ๋ง๋ก ๋ง์ด ์ฐ์ด๋๋ฐ, ์ผ๋ฐ์ ์ผ๋ก ๋ํ ์๋ฐ์คํฌ๋ฆฝํธ๋ผ๊ณ ๋ฒ์ญํ๋ค.
์๋ฒ์ ํด๋ผ์ด์ธํธ์ ๊ฐ์ ์ธ์ด๊ฐ ์ฐ์ธ๋ค๋ ์๋ฏธ๋ก ์๊ฐํ๋ฉด ๋๋ค.
nuxtServeInit
, middleware
validate()
, asyncData()
, fetch()
๋ฑ์ ๊ณผ์ ์ ์ต์ด ์์ฒญ์์๋ ์๋ฒ์ฌ์ด๋์์ ์ฒ๋ฆฌ ํ๋ ๋ก์ง์ด ๊ฐ์ JavaScript๋ก ์์ฑ๋์์์ ์๋ฏธํ๋ค.
๐จ ์ฌ๊ธฐ์ ์๊ธฐํ๋ ์๋ฒ ์ฌ์ด๋์ "์๋ฒ"๋ Api๋ฅผ ์ ์ํ๋ ๋ฐฑ์๋ ์๋ฒ๊ฐ ์๋๋ผ Nuxt.js์ ๋ด์ฅ๋ Express(Node.js) ์๋ฒ๋ฅผ ๋งํ๋ค. Nuxt.js๋ SSR ๊ตฌํ์ ์ํด Express ์๋ฒ๋ฅผ ๋ด์ฅํ๊ณ ์๋ค.
Express(Node.js) ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ๋์ผํ๊ฒ JavaScript๋ก ์ด๋ฃจ์ด์ ธ ์๊ธฐ ๋๋ฌธ์ Isomorphic JavaScript / Universal SSR์ด๋ผ๊ณ ํ๋ ๊ฒ์ด๋ค.
์ค์ ์ฝ๋ฉํ ๋ ํด๋น ์ฝ๋๊ฐ ์๋ฒ/ํด๋ผ์ด์ธํธ ์์ชฝ์์ ๋ชจ๋ ์คํ๋ ์ ์๋ค๋ ๊ฑธ ํญ์ ์ผ๋์ ๋๊ณ ์์ ํด์ผํ๋ค.
Static App
Nuxt.js๋ Univeral App, SPA ์ธ์๋ Static App์ ์ง์ํ๋ค.
Static App์ ๋ชจ๋ page๊ฐ pre-predering(ํ๋ฆฌ๋๋๋ง)๋ ๋น๋๋ฅผ ์์ฑํ๊ณ server๋ ํฌํจํ์ง ์๋๋ค.
์ฆ, ์์ฑ๋ ์ ์ HTML์ ์์ฑํด์ ๋ฟ๋ ค์ฃผ๋ ๋ฐฉ์์ด๋ค.
๐ ํ๋ฆฌ ๋ ๋๋ง
์๋ฒ์ ๊ฐ์ ์์ด ๋ฏธ๋ฆฌ ๋ ๋๋ง ๋ ๋ชจ๋ ํ์ด์ง์ ๋ํ HTML ํ์ผ๋ค์ ํด๋ผ์ด์ธํธ์ ์ ๊ณตํด์ฃผ๋ ๊ฒ
Static App์ ๊ตฌํํ๋ ค๋ฉด nuxt.config.js
์ target: 'static'
์ ์ถ๊ฐํ๋ฉด ๋๋ค.
๋ฐฐํฌ ์ npm run generate
์ ํ๋ฉด dist
(default)์ ๋ชจ๋ ํ์ด์ง๊ฐ ๋ ๋๋ง๋ ๋น๋๊ฐ ์์ฑ๋๋ค.
ํ์ง๋ง id๊ฐ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋๊ฒจ ๋ผ์ฐํ ํ๋ ํ์ด์ง๋ ํ๋ผ๋ฏธํฐ๊ฐ ์ ํด์ง ๊ฐ์ด ์๋๊ธฐ์ pre-rendering ๋์ง ์์ url ์ ๊ทผ์ด ๋ถ๊ฐํ ์ด์๊ฐ ์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ nuxt.config.js
์ generate property
์ต์
์ ์ด์ฉํ ์ ์๋ค.
์๋ฅผ ๋ค์ด posts
๋ผ๋ ๋ฆฌ์คํธ ํ์ด์ง๊ฐ ์๊ณ post/[id]
์ด ์์ธํ์ด์ง๋ผ๋ฉด,
generate: {
routes: function () {
return [
'/posts/id๊ฐ'
]
}
},
์ id ๊ฐ์ ๋ฃ์ด์ generate ํด์ฃผ๋ฉด id ๊ฐ์ ํด๋์ ํจ๊ป index.html
์ด ๋ฐ๋ก ์์ฑ๋๋ ๊ฑธ ๋ณผ ์ ์๋ค.
ํ์ง๋ง ๋ชจ๋ id ๊ฐ์ config ํ์ผ์ ๋ฃ์ด์ ๊ด๋ฆฌํ๊ธฐ์ ๋ฌด๋ฆฌ๊ฐ ์์ด ์ด๋ ์๋ฒ๋ก ๋ถํฐ ๊ฐ์ ๋ฐ์์ ์ค์ ํด ์ค ์ ์๋ค.
const axios = require('axios');
generate: {
routes: function () {
return axios.get('http://test.com/posts')
.then(res => {
const routes = []
for (const key in res.data) {
routes.push('/posts/' + key)
}
return routes
})
}
},
๐ Nuxt.js SSR ๋ฐฐํฌ
์ ๊ทธ๋ฆผ์ Vue ํ๋ก์ ํธ์ Nuxt SSR ํ๋ก์ ํธ์ ๋ฐฐํฌ๋ฌผ์ ๋น๊ตํด๋ณธ ๊ฒ์ด๋ค.
์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง์ ์ํด์ ๋น์ฐํ ์๋ฒ ์ฝ๋๋ฅผ ํฌํจํด ๋น๋๊ฐ ์ด๋ค์ ธ์ผํ๋ค.
๋๋ฌธ์ ๋น๋ ํ Nuxt.js ํ๋ก์ ํธ์.nuxt/dist๋ฅผ ํ์ธํด๋ณด๋ฉด client,server ๋๋ ํ ๋ฆฌ๋ก ๋๋ ์ง๊ฑธ ๋ณผ ์ ์์๋ค.
๋ฐ๋ฉด Vue.js์ dist
์์ ํด๋ผ์ด์ธํธ ํ์ด์ง ๊ตฌ์ฑ์ ํ์ํ js,css ๋๋ ํ ๋ฆฌ ๋ฑ๋ง ์กด์ฌํ๋ค.
Vue.js๋ ๋ฐฐํฌ ์ ๋ณดํต ๋ฐฑ์๋ ์๋ฒ์ resource/staticํด๋๋ฅผ output ๋๋ ํ ๋ฆฌ๋ก ์ค์ ํ์ฌ ๋น๋๋ก ๋ง๋ค์ด์ง html, css, js ๋ญ์น๋ค ์ฌ๋ ค ๋ฐฐํฌํ๋ค.
ํ์ง๋ง SSR์ ์ํ Nuxt.js๋ ๋ ๋๋ง์ ์ํ ์๋ฒ๊ฐ ์กด์ฌํด์ผํ๊ธฐ ๋๋ฌธ์ static์ผ๋ก ํ์ผ์ ์ฌ๋ฆฌ๋ ๊ฒ์ด ์๋๋ผ ๋ฐฑ์๋์ ๋ณ๊ฐ์ ๋ค๋ฅธ ํธ์คํ ์๋ฒ๋ฅผ ์ค๋นํด์ผํ๋ค.
Nuxt.js๋ SPA ๋ชจ๋๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ Single Page App์ด๋ผ๋ฉด ๊ผญ ์๋ฒ๊ฐ ๋๊ฐ ์์ ํ์๋ ์๋ค.
๐ Nuxt.js SPA / Static App ๋ฐฐํฌ
nuxt generate
๋๋ nuxt build --spa
๋ก SPA ํน์ ์ ์ ๊ฒฐ๊ณผ๋ฌผ์ ์์ฑํด๋ผ ์ ์๋ค.
์ฌ๊ธฐ์ nuxt generate
์ nuxt build --spa
์ ์ฐจ์ด์ ์nuxt generate
๋ ํ๋ฆฌ๋ ๋๋ง(prerendering)์ด ๋ SPA์ด๊ณ ,nuxt build --spa
๋ ํ๋ฆฌ๋ ๋๋ง ๋์ง ์์ SPA๋ผ๋ ๊ฒ์ด๋ค.
์ฝ๊ฒ ๋งํ๋ฉด SPA์ Static App ์ด๋ผ๋ ๊ฒ์ด๋ค.
์์ฝ
Universal App | SPA | Static App | |
mode ๊ฐ | universal | spa | universal |
๋์ ๋ฐฉ์ | ์ต์ด view๋ ์๋ฒ์์ ๋ ๋๋ง ๋์ด ๋ก๋. ์ดํ์๋ spa๋ก ๋์ | ์ต์ด view ์ ๊ทผ ์ spa๋ก ๋ก๋ ํ ์ดํ์๋ spa๋ก ๋์ | ์ต์ด view๋ ํ๋ฆฌ๋๋๋ง ๋ ํ์ด์ง ๋ก๋. ์ดํ์๋ spa๋ก ๋์ |
์๋ฒ ์ ๋ฌด | ํฌํจ(node.js ํ์) | ๋ฏธํฌํจ | ๋ฏธํฌํจ |
์๋ฒ ์์ | npm run start | - | - |
๋น๋ ์์ฑ ๋ฐฉ์ | npm run build | npm run build | npm run generate |
seo | ์ต์ ํ | X | ์ต์ ํ |
๐ข ์ฐธ๊ณ
๐ Universal App์ธ์ง SPA์ธ์ง ํ์ธํ๋ ๋ฒ
๋ธ๋ผ์ฐ์ ์์ ์ด ์ ํ๋ฆฌ์ผ์ด์ ์ด SPA์ธ์ง Universal App ์ธ์ง ํ์ธํ๋ ๋ฒ์ ๊ฐ๋จํ๋ค.
๋ธ๋ผ์ฐ์ ์์ ์ค๋ฅธ์ชฝ ๋ง์ฐ์ค ํด๋ฆญ ํ,
ํ์ด์ง ์์ค๋ณด๊ธฐ๋ฅผ ๋๋ฅด๋ฉด ์ฝ๋๋ฅผ ํ์ธํ ์ ์๋ค.
๋ธ๋ผ์ฐ์ ์์ ๋ณด์๋ ์ด๋ค ๋ฐ์ดํฐ๊ฐ ํด๋น ํ์ด์ง ์์ค์ ์กด์ฌํ๋ค๋ฉด SSR ๋ฐฉ์์ด๊ณ ,
๊ทธ๋ ์ง ์๋ค๋ฉด SPA ๋ฐฉ์์ด๋ค.
๐ Nuxt.js Routing
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
/src/router/index.js
์ ์ฝ๋๋ Vue.js ์ ๋ผ์ฐํฐ ์ค์ ํ์ผ์ด๋ค.
Vue.js ์์ /src/views
๋๋ ํ ๋ฆฌ์ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๊ณ ์ด ๋ผ์ฐํฐ ์ค์ ํ์ผ(/src/router/index.js
)์ ํด๋น ์ปดํฌ๋ํธ์ ๋ํ ๋ผ์ฐํฐ ์ค์ ์ ์ผ์ผ์ด ์
๋ ฅํด์ค์ผํ๋ค.
ํ์ง๋ง Nuxt.js์์ ๊ทธ๋ด ํ์๊ฐ ์๋ค!
Nuxt.js Router์ ๋ํ ๊ฐ๋จํ ์์ ๋ฅผ ์ดํด๋ณด์.
๋ผ์ฐํฐ๋ ํฌ๊ฒ ๋๊ฐ์ง ์ข
๋ฅ๊ฐ ์๋ค. "ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ๋ Dynamic Route"์ "ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ์ง ์๋ Basic Route" ์ด๋ค.
Basic Route
Nuxt.js๋ /pages
ํด๋์ ํ์ผ๋ง ์์ฑํด์ฃผ๋ฉด ์๋์ผ๋ก ๋ผ์ฐํ
์ด ๋๋ค./pages
์ HelloWorld.vue
ํ์ด์ง๋ฅผ ์์ฑํด๋ณด๊ฒ ๋ค.
<template>
<div>
Hello World!!
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
/pages/HelloWorld.vue
ํ์ด์ง ์์ฑ ํ localhost:3000/helloworld
์ ์ ์ํด๋ณด๋ฉด HelloWorld.vue
ํ์ด์ง๊ฐ ์ถ๋ ฅ๋๋๊ฑธ ํ์ธํ ์ ์๋ค.
์ด๋ป๊ฒ ๊ฐ๋ฅํ ์ผ์ผ๊น?
ํ์ด์ง๋ฅผ ์์ฑํ ์ฆ์ .nuxt/router.js
์์ ์๋ ์ค์ ์ด ์ด๋ค์ง๊ธฐ ๋๋ฌธ์ด๋ค.
import Vue from 'vue'
import Router from 'vue-router'
import { normalizeURL, decode } from 'ufo'
import { interopDefault } from './utils'
import scrollBehavior from './router.scrollBehavior.js'
const _64fe57bb = () => interopDefault(import('..\\pages\\HelloWorld.vue' /* webpackChunkName: "pages/HelloWorld" */))
const _2fdd1532 = () => interopDefault(import('..\\pages\\index.vue' /* webpackChunkName: "pages/index" */))
const emptyFn = () => {}
Vue.use(Router)
export const routerOptions = {
mode: 'history',
base: '/',
linkActiveClass: 'nuxt-link-active',
linkExactActiveClass: 'nuxt-link-exact-active',
scrollBehavior,
routes: [{
path: "/HelloWorld",
component: _64fe57bb,
name: "HelloWorld"
}, {
path: "/",
component: _2fdd1532,
name: "index"
}],
fallback: false
}
export function createRouter (ssrContext, config) {
const base = (config._app && config._app.basePath) || routerOptions.base
const router = new Router({ ...routerOptions, base })
// TODO: remove in Nuxt 3
const originalPush = router.push
router.push = function push (location, onComplete = emptyFn, onAbort) {
return originalPush.call(this, location, onComplete, onAbort)
}
const resolve = router.resolve.bind(router)
router.resolve = (to, current, append) => {
if (typeof to === 'string') {
to = normalizeURL(to)
}
return resolve(to, current, append)
}
return router
}
/.nuxt/router.js
์ด ํ์ผ์ ๋ฐ๋ก ๊ฑด๋ค์ง๋ ์์๋๋ฐ, HelloWorld
๋ผ์ฐํฐ๊ฐ ์ค์ ๋์๋ค.
Basic Route - ์ค์ฒฉ ๋ผ์ฐํ
์ค์ฒฉ ๋ผ์ฐํ
์ด๋ ์ ๊ทธ๋ฆผ์ฒ๋ผ ์ค์ฒฉ๋ ์ปดํฌ๋ํธ์ ๋ํ ๋ผ์ฐํฐ ์ค์ ์ ๋งํ๋ค.
๊ฒฝ๋ก๋ฅผ ๋ณด๊ณ ์ด๋ค ์ปดํฌ๋ํธ๋ค์ด ์ค์ฒฉ๋์ด์๋์ง ํ๋จํ ์ ์๋ค.
์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ ์์ ๋ฅผ ๊ตฌํํด๋ณด๊ฒ ๋ค.
<template>
<div>
<div class="container">
Container ์ปดํฌ๋ํธ ์
๋๋ค.
<nuxt-child />
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.container {
width: 300px;
height: 300px;
background-color: pink;
z-index: 0;
}
</style>
/pages/Container.vue
<template>
<div>
<div class="content1">
Content1 ์ปดํฌ๋ํธ ์
๋๋ค.
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.content1 {
background-color: lightblue;
margin: 30px;
width: 200px;
height: 200px;
}
</style>
/pages/Container/Content1.vue
<template>
<div>
<div class="content2">
Content2 ์ปดํฌ๋ํธ ์
๋๋ค.
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.content2 {
background-color: lightgoldenrodyellow;
margin: 30px;
width: 200px;
height: 200px;
}
</style>
/pages/Container/Content2.vue
์ค์ฒฉ ๋ผ์ฐํ
์ญ์ ์๋์ผ๋ก ์ค์ ๋๋ค.
์์ ์ปดํฌ๋ํธ์ ์ด๋ฆ์ด ๊ฐ์ ๋๋ ํ ๋ฆฌ๋ฅผ ์์ฑํ ๋ค
ํด๋น ๋๋ ํ ๋ฆฌ ๋ด์ ํ์ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๋ค.
์์ ์ปดํฌ๋ํธ ๋ด์ ํ์ ์ปดํฌ๋ํธ๊ฐ ๋ค์ด๊ฐ ์๋ฆฌ์ <nuxt-child />
์ ์ํด์ฃผ๋ฉด ๋.
Nuxt.js๊ฐ ์๋์ผ๋ก ๋ผ์ฐํ
์ ์๋ฃํ๋ค.
// ...
routes: [{
path: "/Container",
component: _e6f8da14,
name: "Container",
children: [{
path: "Content1",
component: _4d5cf824,
name: "Container-Content1"
}, {
path: "Content2",
component: _4d40c922,
name: "Container-Content2"
}]
}, {
path: "/HelloWorld",
component: _64fe57bb,
name: "HelloWorld"
}, {
path: "/",
component: _2fdd1532,
name: "index"
}]
// ...
/.nuxt/router.js
๋ผ์ฐํ
์ค์ ํ์ผ์ ๋ณด๋ฉด ์์ ๋ผ์ฐํฐ Container
์ children
์์ฑ์ด ์ถ๊ฐ๋๊ณ ํ์ ๋ผ์ฐํฐ๋ค์ด ์ ์๋๊ฑธ ํ์ธํ ์ ์๋ค.
Dynamic Route
์ธ๋๋ฐ + ํ์ผ์ด๋ฆ ํํ๋ก ํ์ผ์ ์์ฑํ๋ฉด ํด๋น ํ์ผ ์ด๋ฆ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ๋๋ค.
_product_id.vue
๋ผ๋ ํ์ผ์ ๋ง๋ค๊ณ ์๋์ ๊ฐ์ด ์
๋ ฅํด์คฌ๋ค.
<template>
<div>
<div>Editing Product {{ $route.params.product_id }}</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
product_id
๋ผ๋ route์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ์์ ํ๋ฉด์ ๋ฟ๋ ค์ฃผ๋ ์ฝ๋์ด๋ค.
์๋ ์ปดํ์ผ ํ .nuxt/router.js
๋ฅผ ํ์ธํด๋ณด๋ฉด ์๋์ ๊ฐ์ ์ฝ๋๊ฐ ์ถ๊ฐ๋์ด ์์ ๊ฒ์ด๋ค.
{
path: "/products/edit/:product_id?",
component: _1cad02a9,
name: "products-edit-product_id"
}
product_id
๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์จ๋ค๋ ์ค์ ์ด ์ถ๊ฐ๋์๋ค.
localhost:3000/products/edit/1 ๋ก ์ ๊ทผํ๋ฉด, ๊ฒฝ๋ก์ params๋ฅผ ๋ฐ์ ํ๋ฉด์ ๋ฟ๋ ค์ฃผ๋ ๊ฑธ ํ์ธํ ์ ์๋ค.
ํ๋ผ๋ฏธํฐ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ ๊ฐ๋ฅํ๋ค.
validate({ params, query, store }) {
return true // if the params are valid
return false // will stop Nuxt.js to render the route and display the error page
}
Nuxt.js์์ ์ ๊ณตํ๋ validate()
๋ฉ์๋๋ฅผ ์ด์ฉํ๋ฉด ํ๋ผ๋ฏธํฐ์ ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ๊ฐ๋ฅํ๋ค.validate()
๋ ์ ๋ผ์ฐํฐ๋ก ๋ค๋น๊ฒ์ดํ
(navigating)๋๊ธฐ ์ ์ call ๋๋ค.
Nuxt context ๊ฐ์ฒด๋ฅผ argument๋ก ๊ฐ๋๋ค. ์์ธํ
<template>
<div>
<div>Editing Product {{ $route.params.product_id }}</div>
</div>
</template>
<script>
export default {
validate({ params }) {
// must be a number
return /^\d+$/.test(params.product_id)
}
}
</script>
<style scoped>
</style>
product_id
๋ฅผ ์ซ์๋ง ๋ฐ๋๋ก ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ๊ณ , http://localhost:3000/products/edit/test
๋ก ์ ๊ทผํ๋ฉด
์์ ๊ฐ์ด 404์๋ฌ๋ฅผ ๋ฑ๋๋ค.
๐ Nuxt.js Store
Nuxt๋ pages
๋๋ ํ ๋ฆฌ์ ์ ์ฌํ ๊ตฌ์กฐ๋ก store
๋ฅผ ๊ตฌ์ถํ ์ ์๋ค.
Store ๋ ํฌ๊ฒ Classic ๊ณผ Module ๋ชจ๋๋ฅผ ์ ๊ณตํ๋ค.
(Store ๊ธฐ๋ฅ์ด ํ์ ์๋ค๋ฉด store
ํด๋๋ฅผ ์ง์ฐ๋ฉด ๋จ)
Classic
ํด๋์ ๋ชจ๋๋ store
๋๋ ํ ๋ฆฌ์ index.js
๋ฅผ ํ์๋ก ํ๋ค. (Vuex ์ค์ ํ์ผ)
์ด index.js
์ Vuex ์ธ์คํด์ค๋ฅผ ๋ฆฌํดํ๋ export
ํจ์๋ฅผ ๊ตฌํํ๋ฉด ๋๋ค.
์ด๋ฅผ ํตํด ์ผ๋ฐ Vue ํ๋ก์ ํธ์์ Vuex๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ฒ๋ผ ์ํ๋๋๋ก ์คํ ์ด๋ฅผ ๋ง๋ค ์ ์๋ค.
import Vuex from 'vuex'
const createStore = () => {
return new Vuex.Store({
state: ...,
mutations: ...,
actions: ...
})
}
export default createStore
Module
๋ชจ๋ ๋ชจ๋๋ ๋ํ store
๋๋ ํ ๋ฆฌ์ index.js
๋ฐ์๋ฅผ ํ์๋ก ํ๋ค. (๊ผญ index.js
์ผ ํ์ ์์. ์ํ๋ ๋ชจ๋์ ๋ค์ ์คํ์ด์ค๋ก ์ง์ )
๊ทธ๋ฌ๋ ๋ชจ๋ ๋ชจ๋์์ ์ด ํ์ผ์์ ๋ฃจํธ state
/mutations
/actions
๋ง export
์ํค๋ฉด๋๋ค.
export const state = () => ({})
์๋ฅผ ๋ค์ด, store
๋๋ ํ ๋ฆฌ ๋ด์ product.js
๋ฅผ ์์ฑํ๊ณ ์๋์ ๊ฐ์ด ๊ตฌํํ๋ฉด,product
๋ผ๋ ๋ค์์คํ์ด์ค๊ฐ ์์ฑ๋์ด ๋ชจ๋ํ๋ฅผ ํ ์ ์๋ค.
export const state = () => ({
_id: 0,
title: 'Unknown',
price: 0
})
export const actions = {
load ({ commit }) {
setTimeout(
commit,
1000,
'update',
{ _id: 1, title: 'Product', price: 99.99 }
)
}
}
export const mutations = {
update (state, product) {
Object.assign(state, product)
}
}
/store/product.js
<template>
<div>
<h1>View Product {{ product._id }}</h1>
<p>{{ product.title }}</p>
<p>Price: {{ product.price }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
created () {
this.$store.dispatch('product/load')
},
computed: {
...mapState(['product'])
}
}
</script>
/pages/product/view.vue
product
๋ชจ๋์ id
, title
, price
๋ฅผ ํ๋ฉด์ ๋ฟ๋ฆฌ๋ ์ฝ๋์ด๋ค.
์ฝ 1์ด ๋ค์ load
์ก์
์ ๊ฑฐ์น๋ฉฐ state
๊ฐ ๊ฐฑ์ ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๐จ ์ฃผ์
์ ์์ ์์ load๋ผ๋ ๊ฐ์ง API๋ฅผ ๊ตฌํํด ์ฌ์ฉํ๋ค.
์ฌ๊ธฐ์ ๋ฌธ์ ์ ์, 1์ด๋ค state๊ฐ ๊ฐฑ์ ๋๊ธฐ ์ ๊น์ง 0, Unknown ๋ฐ์์ ์ด๊ธฐํ ๋ฐ์ดํฐ๊ฐ ๋ณด์ธ๋ค๋ ๊ฒ์ด๋ค.
์๋ง ์ค API์์๋ response๊ฐ ์๋ฃ๋๊ธฐ ์ ๊น์ง ์ด๊ธฐ๊ฐ์ด ๋ณด์ด๊ฒ ๋ ๊ฒ์ด๋ค.
์ฐ๋ฆฐ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Nuxt์์ ์ ๊ณตํ๋ fetch๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์์ธํ๊ฑด ์๋์์ ์ค๋ช !
๐ Nuxt.js Layouts
Nuxt.js๋ navbar, footer, header ๋ฐ์์ ๋ ์ด์์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
์ผ๋ฐ์ ์ธ Vue ํ๋ก์ ํธ์ App.vue
์ ๋น์ทํ ๊ธฐ๋ฅ์ด๋ค.
<template>
<div>
<h1>Admin Layout</h1>
<nuxt />
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
/layouts/admin-layout.vue
<template>
<div>
admin page
</div>
</template>
<script>
export default {
layout: 'admin-layout'
}
</script>
<style scoped>
</style>
/pages/admin.vue
layouts
๋๋ ํ ๋ฆฌ์ admin-layout.vue
๋ผ๋ ๋ ์ด์์ ํ์ผ์ ๋ง๋ค๊ณ ,pages
๋๋ ํ ๋ฆฌ์ admin.vue
๋ผ๋ ํ์ด์ง๋ฅผ ๋ง๋ค์ด ์ฃผ์๋ค.admin.vue
์ layout: 'admin-layout'
์ด๋ผ๊ณ ์ ์ํด์ฃผ๋ ๊ฒ ๋ง์ผ๋ก ํด๋น ํ์ผ์ด admin-layout.vue
์ <nuxt />
์์์ ์์นํ ์ ์๋ค.
์ค์ํ ๊ฒ์, ๋ ์ด์์ ํ์ผ์ ๋ฐ๋์ <nuxt />
์์๊ฐ ํฌํจ๋์ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค.
๐ Nuxt.js Middleware
middleware
๋ pages
๋๋ layouts
๋ฅผ ๋ ๋๋งํ๊ธฐ ์ ์ ์คํํ ์ ์๋ ๊ธฐ๋ฅ์ด๋ค.
๋ฏธ๋ค์จ์ด๊ฐ ํด๊ฒฐ๋๊ธฐ ์ ๊น์ง ์ฌ์ฉ์์๊ฒ ์๋ฌด๊ฒ๋ ํ์ํ์ง ์๋๋ค.
์ด๋ Vuex ์ ์ฅ์์์ ์ ํจํ ๋ก๊ทธ์ธ์ ํ์ธํ๊ฑฐ๋, ์ผ๋ถ ๋งค๊ฐ ๋ณ์์ ์ ํจ์ฑ์ ๊ฒ์ฌํ ๋ ์ฌ์ฉํ ์ ์๋ค. (validate()
๋ฉ์๋ ๋์ )
์์ - CodeSandBox
๐ Nuxt.js Plugins
plugins
๋๋ ํ ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์ดํ๋ฆฌ์ผ์ด์
์ด ์์ฑ๋๊ธฐ ์ ์ Vue ํ๋ฌ๊ทธ์ธ์ ๋ฑ๋กํ ์ ์๋ค.
์ด๋ฅผ ํตํด Vue ์ธ์คํด์ค์ ์ฑ ์ ์ฒด์์ ๊ณต์ ํ๊ณ ๋ชจ๋ ๊ตฌ์ฑ ์์์์ ์ก์ธ์คํ ์ ์๋ค.
์๋ฅผ ๋ค์ด vue-notifications
ํ๋ฌ๊ทธ์ธ์ ์ฃผ์
ํ๋ค๊ณ ํด๋ณด์.
npm i vue-notifications
(์ฌ๊ธฐ๊น์ง Vue์ ๋์ผ)plugins
๋๋ ํ ๋ฆฌ์vue-notifications.js
ํ์ผ์ ํ๊ณ ์๋์ ๊ฐ์ด ์ ๋ ฅ.import Vue from 'vue' import VueNotifications from 'vue-notifications' Vue.use(VueNotifications)
nuxt.config.js
์plugins: ['~/plugins/vue-notifications']
์ ๋ ฅ
๋์ด๋ค. Vue ํ๋ก์ ํธ์ ํ๋ฌ๊ทธ์ธ ์ฃผ์ ๋ฒ๊ณผ ์ ์ฌํ๋ค.
Nuxt.js ๋น๋๊ธฐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
Nuxt.js๋ ์ปดํฌ๋ํธ์ mounted
ํํฌ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ๊ณผ ๊ฐ์ด,
ํด๋ผ์ด์ธํธ ์ชฝ์์ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๊ธฐ ์ํ ๊ธฐ์กด Vue ํจํด์ ์ง์ํ๋ค.
ํ์ง๋ง universal ์ฑ์ ๊ตฌํํ๊ณ ์๋ค๋ฉด, ์๋ฒ ์ธก ๋ ๋๋ง ์ค์ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋ง ํ ์ ์๋๋ก Nuxt.js ๊ด๋ จ ํํฌ(fetch
, asyncData
)๋ฅผ ์จ์ผํ๋ค.
asyncData
- ์ปดํฌ๋ํธ ๋ฐ์ดํฐ๋ฅผ ์ธํ ํ๊ธฐ ์ ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋๋ก ํ๋ค.
- ์ปดํฌ๋ํธ๋ฅผ ๋ก๋ํ๊ธฐ ์ ์ ํธ์ถ๋๋ค.
- pages ์ปดํฌ๋ใ ํธ์์๋ง ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
- Context ๊ฐ์ฒด๋ฅผ ์ฒซ๋ฒ์งธ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ, ์ด๋ฅผ ์ฌ์ฉํด ์ผ๋ถ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ์ปดํฌ๋ํธ ๋ฐ์ดํฐ๋ก ๋ฐํํ ์ ์๋ค.
- ๋ฐํ ๊ฐ์ ์ปดํฌ๋ํธ์ data์ ๋ณํฉ๋๋ค.
- ์ปดํฌ๋ํธ๋ฅผ ์ด๊ธฐํํ๊ธฐ ์ ์ ์คํ๋๊ธฐ ๋๋ฌธ์ ๋ฉ์๋ ๋ด๋ถ์์ this๋ฅผ ํตํด ์ปดํฌ๋ํธ ์ธ์คํด์ค์ ์ ๊ทผํ ์ ์๋ค.
export default {
async asyncData({ params }) {
const { data } = await axios.get(`https://my-api/posts/${params.id}`);
return { title: data.title };
},
};
fetch
- ํ์ด์ง๊ฐ ๋ ๋๋ง ๋๊ธฐ ์ ์ ๋ฐ์ดํฐ๋ฅผ Store์ ๋ฃ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.
- ๋ชจ๋ ์ปดํฌ๋ํธ์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
- ์ปดํฌ๋ํธ๋ฅผ ๋ก๋ํ๊ธฐ ์ ์ ํธ์ถ๋๋ค.
- Context ๊ฐ์ฒด๋ฅผ ์ฒซ๋ฒ์งธ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ ๊ทธ ๋ฐ์ดํฐ๋ฅผ ์คํ ์ด์ ๋ฃ์ ์ ์๋ค.
- return ๊ฐ์ Promise์ด๋ค.
- Promise๋ฅผ ๋ฐํํ๋ฉด Nuxt๋ ๋ ๋๋ง ์ ์ Promise๊ฐ ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค.
์ ์ชฝ Store Module ์์ ์์ ์ด์ด ์ค๋ช
ํ๊ฒ ๋ค.
<์ฃผ์> ๋ถ๋ถ์์ ์ค๋ช
ํ์ง๋ง, ์ด๋ค Api๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋, response๊ฐ ์ค๊ธฐ ์ ๊น์ง
์ด๊ธฐ๊ฐ์ด ๊ทธ๋๋ก ๋
ธ์ถ๋๋ค๋ ๋ฌธ์ ๊ฐ ์กด์ฌํ์๋ค.
์ด ๋ฌธ์ ๋ ์ fetch
d์ ํน์ง ์ค 6๋ฒ "Promise๋ฅผ ๋ฐํํ๋ฉด Nuxt๋ ๋ ๋๋ง ์ ์ Promise๊ฐ ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค."๋ฅผ ํตํด ํด๊ฒฐํ ์ ์๋ค.
/store/product.js
์ load
์ก์
๋ฉ์๋๊ฐ Promise๋ฅผ ๋ฐํํ๋๋ก ์์ ํ๊ณ , (async/await ๋ฅผ ์จ๋ ์๊ด ์์)/pages/products/view.vue
ํ์ผ์ fetch
๋ก ์์ ํด๋ณด์๋ค.
export const state = () => ({
_id: 0,
title: 'Unknown',
price: 0
})
export const actions = {
load ({ commit }) {
return new Promise(resolve => {
setTimeout(() => {
commit('update', { _id: 1, title: 'Product', price: 99.99 })
resolve()
}, 1000)
})
}
}
export const mutations = {
update (state, product) {
Object.assign(state, product)
}
}
/store/product.js
<template>
<div>
<h1>View Product {{ product._id }}</h1>
<p>{{ product.title }}</p>
<p>Price: {{ product.price }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
fetch() {
this.$store.dispatch('product/load')
},
computed: {
...mapState(['product'])
}
}
</script>
<style scoped>
</style>
/pages/product/view.vue
์ฐ๋ฆฐ product/load
๊ฐ ์คํ๋๊ธฐ ์ ๊น์ง ๋ ๋๋ง์ด ๋์ง ์๋ค๊ฐ, response๊ฐ ์ค๋ฉด ํ๋ฉด์ product/load
์ ๊ฒฐ๊ณผ ๊ฐ์ด ๋ฐ ๊ฒ์ด๋ผ๊ณ ์์ํ๋ค.
ํ์ง๋ง ๊ฒฐ๊ณผ์ ์ผ๋ก, product/load
์ก์
๋ฉ์๋๊ฐ ์คํ๋์ง ์์๋ค. ์์ผ๊น?
์ด์ ๋ fetch
, asyncData
๊ฐ์ Supercharged ๋ฉ์๋๋ Vue ์ปดํฌ๋ํธ๊ฐ ์์ฑ๋๊ธฐ ์ ์ ์คํ๋๋ฏ๋ก ์ปดํฌ๋ํธ this
๋ฅผ ๊ฐ๋ฆฌํค์ง ์๋๋ค. ๋๋ฌธ์ ์ ์์ ์ this.$store
๋ undefined
์ํ ์ธ๊ฒ์ด๋ค. ์ปดํฌ๋ํธ๊ฐ ์์ฑ๋ ์ดํ์ this
์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
(fetch
์ asyncData
์ ์คํ ํ์ด๋ฐ์ ์๋ lifeCycle ๋ถ๋ถ์์ ํ๋ฒ ๋ ๋ค๋ฃจ๊ฒ ๋ค.)
๊ทธ๋ ๋ค๋ฉด fetch
๋ asyncData
๋ฅผ ์ฌ์ฉํ ๋ ์ด๋ป๊ฒ Store์ ์ ๊ทผํ ์ ์์๊น?
๋ฐ๋ก Context ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
๐ Nuxt.js Context
Nuxt๋ ๋ชจ๋ ๋ฉ์๋์ Context๋ผ๋ ๋งค์ฐ ์ ์ฉํ ๊ฐ์ฒด๋ฅผ ํฌํจํ๋ ์ธ์๋ฅผ ์ ๊ณตํ๋ค.
์ฌ๊ธฐ์ ์ฑ ์ ์ฒด์์ ์ฐธ์กฐํด์ผํ๋ ๋ชจ๋ ๊ฒ์ด ์๋ค. ์ฆ, Vue๊ฐ ์ปดํฌ๋ํธ์ ๋ํ ์ฐธ์กฐ๋ฅผ ๋จผ์ ์์ฑํ ๋๊น์ง ๊ธฐ๋ค๋ฆด ํ์๊ฐ ์๋ค.
(Context๊ฐ ๋ฌด์์ ๊ฐ์ง๊ณ ์๋์ง๋ ๊ณต์๋ฌธ์๋ฅผ ์ฐธ์กฐํ์.)
์ fetch
์์ ์์ ์ปจํ
์คํธ๋ฅผ ๊ตฌ์กฐํํ๊ณ ์ฌ๊ธฐ์ Store๋ฅผ ์ถ์ถํด์ค์.
export default {
fetch ({ store }) {
return store.dispatch('product/load')
},
computed: {...}
}
/pages/product/view.vue
์ด๊ธฐ๊ฐ์ด ๋จ์ง ์๊ณ , response๊ฐ ์ค๊ณ ๋์์ผ ๋ ๋๋ง์ด ๋๋ ๋ชจ์ต์ ํ์ธํ ์ ์์๋ค.
๐ Nuxt.js ๋ฉํ ํ๊ทธ
Vue.js์์ SEO ๋ฅผ ์ํ ๋ฉํ ํ๊ทธ๋ฅผ ๋ฌ๊ธฐ ์ํด vue-meta
๋ฑ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํด์ผํ์ง๋ง,
Nust.js์์ ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ ์์ด ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ ์ ์๋ค. ๊ธฐ๋ณธ์ผ๋ก vue-meta
๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ฌ๋์ด ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ ์ญ ์ค์
export default {
head: {
title: 'my website title',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: 'my website description'
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
}
}
nuxt.config.js
Nuxt.js ์ค์ ํ์ผ(nuxt.config.js
)์ meta ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ๋ฉด ์ดํ๋ฆฌ์ผ์ด์
์ ๋ชจ๋ ๊ธฐ๋ณธ ํ๊ทธ๋ฅผ ์ ์ํ ์ ์๋ค.
SEO ๋ชฉ์ ์ผ๋ก ๊ธฐ๋ณธ ์ ๋ชฉ ๋ฐ ์ค๋ช
ํ๊ทธ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ๋ทฐํฌํธ๋ฅผ ์ค์ ํ๊ฑฐ๋ ํ๋น์ฝ์ ์ถ๊ฐํ๋๋ฐ ๋งค์ฐ ์ ์ฉํ๋ค.
์ ์ฝ๋์ ๊ฐ์ด ์ค์ ํ๋ฉด ๋ชจ๋ ํ์ด์ง์ ๋์ผํ ์ ๋ชฉ๊ณผ ์ค๋ช
์ด ํ์๋๊ฒ ๋๋ค.
์ง์ญ ์ค์
์ปดํฌ๋ํธ ํ์ผ์ <script>
ํ๊ทธ ๋ด๋ถ์ ์๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ํ์ด์ง๋ณ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ ์๋ ์๋ค.
<script>
export default {
head: {
title: 'Home page',
meta: [
{
hid: 'description',
name: 'description',
content: 'Home page description'
}
],
}
}
</script>
// ๋ฉ์๋๋ก๋ ํํ ๊ฐ๋ฅ
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
data() {
return {
title: 'Home page'
}
},
head() {
return {
title: this.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'Home page description'
}
]
}
}
}
</script>
๐ Nuxt.js LifeCycle
๋ญ๊ฐ ๊ต์ฅํ ๋ณต์กํด๋ณด์ด๋ ์ด๋ฏธ์ง์ด์ง๋ง, ์ ๋ด์ฉ์ ๋ชจ๋ ์ฝ๊ณ ์๋ค๋ฉด ์ดํดํ๊ธฐ ์์ํ ๊ฒ์ด๋ค.
๋จผ์ ๊ฐ์ด๋ฐ ๊ทธ์ด์ง ์ ์ ์ดํด๋ณด์.
๊ฐ์ด๋ฐ ์ ์ ๊ธฐ์ค์ผ๋ก, ์์ชฝ์ Vue ์ปดํฌ๋ํธ๊ฐ ์๊ธฐ๊ธฐ ์ , ์๋๋ ๊ทธ ํ๊ฐ ๋๊ฒ ๋ค.
์ข ๋ ์ฝ๊ฒ ์๊ธฐํ์๋ฉด, ์๋ Server Side, ์๋๋ Client Side์ด๋ค.
Vue Component๊ฐ ์๊ธฐ๊ธฐ ์ , Server Side์์ ํ์ด์ง๊ฐ ๋ ๋๋ง ๋๊ธฐ ์ ํด์ผํ ์ผ์ ์ํํ๋ค.
๋น์ฐํ ๋ง์ด์ง๋ง ์๋ฒ ์ฌ์ด๋์์ Vue Component๊ฐ ์์ง ์์ฑ๋๊ธฐ ์ ์ด๊ธฐ ๋๋ฌธ์ this
๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์์ ์ค๋ช
ํ๋ middleware
, validate()
, asyncData()
๋ฅผ ์ํํ๋ค.
๐ก ์ฐธ๊ณ
Nuxt.js 2.12 ์ ์ fetch()
๋ฅผ asyncData()
์ ๊ฐ์ด ์ปดํฌ๋ํธ๊ฐ ์์ฑ๋๊ธฐ ์ ์ ์คํ์ ํด์คฌ์ด์ผํ์ง๋ง,
2.12 ์ดํ์ created()
์ดํ, ์ฆ, Vue ์ปดํฌ๋ํธ๊ฐ ์์ฑ๋๊ณ ๋ ํ์ ์ฌ์ฉํ ์ ์๊ฒ ๋์๋ค.
์ ํํ๋ ๋ธ๋ผ์ฐ์ ์ DOM์ด ๋ ๋ ๋๊ธฐ ์ ์ ์คํ๋๋ค.(beforeMount
์ )
์ด๋ก์จ fetch()
์์ this
์ ์ฌ์ฉ์ด ๊ฐ๋ฅํด์ง ๊ฒ์ด๋ค.
์ฆ, fetch์ ํน์ง์ด์๋ "๋ฐ์ดํฐ๋ฅผ Store์ ๋ฃ๊ธฐ ์ํด ์ฌ์ฉ" ์ ์ ํ์ฌํญ์ด ๋์๋ค.
Vuex ์คํ ์ด ์์
์ ์ ๋ฌํ๊ฑฐ๋ ํ์ด์ง ๊ตฌ์ฑ ์์์์ ๋ณํ์ ์ปค๋ฐํ์ง ์๊ณ ๋ ๊ตฌ์ฑ ์์์ ๋ก์ปฌ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ปดํฌ๋ํธ๊ฐ ์์ฑ๋ ํ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๊ธฐ ๋๋ฌธ์ ์๋์ ๊ฐ์ด ํ์ฉ ๊ฐ๋ฅํ๋ค.
<button @click="$fetch">Refresh</button>
export default {
methods: {
refresh() {
this.$fetch()
}
}
}
๐ [Nuxt.js 2.12 fetch() ์์ธํ ์์๋ณด๊ธฐ 1]
๐ [Nuxt.js 2.12 fetch() ์์ธํ ์์๋ณด๊ธฐ 2]
์๋ฌดํผ ์์ฝํ์๋ฉด Nuxt.js์ ๋๋ถ๋ถ์ ๊ธฐ๋ฅ์ด Vue ์ปดํฌ๋ํธ๊ฐ ์์ฑ๋๊ธฐ ์ ์ ์ด๋ฃจ์ด์ง๊ณ ,
๊ทธ ์ดํ๋ Vue.js์ ๋์ผํ๋ค๋ ๊ฒ์ด๋ค. (fetch ์ ์ธ)
์ด๋ฒ ํฌ์คํ ์์ Nuxt.js์ ๋ํด ์์ ๋ณด์๋ค.
์น์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ "๋ ๋๋ง" ์ด ์๊ฐ๋ณด๋ค ๋ค์ํ ์ข ๋ฅ๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ ๊ฑธ ์ ์ ์์๋ค.
ํผ๋๋ฐฑ ๋๊ธ์ ์ธ์ ๋ ํ์์ ๋๋ค. ๐ผ
https://maxkim-j.github.io/posts/nuxt-ssr
https://khwan.kr/blog/vue/2020-03-02-nuxt-spa-ssr/
https://www.toptal.com/front-end/client-side-vs-server-side-pre-rendering
https://www.toptal.com/vue-js/server-side-rendered-vue-js-using-nuxt-js
https://seonghui.github.io/TIL/docs/vue/nuxt.html
https://developers.google.com/web/updates/2019/02/rendering-on-the-web?hl=ko