다양한 히스토리 모드
라우터 인스턴스를 만들 때 사용하는 history 옵션을 통해 다양한 히스토리 모드 중 하나를 선택할 수 있습니다.
HTML5 모드
HTML5 모드는 createWebHistory()로 생성하며 권장되는 모드입니다:
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})createWebHistory()를 사용하면 URL이 https://example.com/user/id처럼 "일반적인" 형태가 됩니다. 보기 좋죠!
하지만 문제가 하나 있습니다. 우리 앱은 싱글 페이지 클라이언트 사이드 앱이기 때문에, 서버 구성이 올바르지 않으면 사용자가 브라우저에서 https://example.com/user/id에 직접 접근했을 때 404 오류를 보게 됩니다. 이건 좋지 않죠.
걱정할 필요는 없습니다. 이 문제를 해결하려면 서버에 간단한 catch-all fallback 라우트를 추가하면 됩니다. URL이 어떤 정적 asset과도 매치되지 않으면, 앱이 들어 있는 동일한 index.html 페이지를 반환해야 합니다. 다시 깔끔해집니다!
Hash 모드
hash 히스토리 모드는 createWebHashHistory()로 생성합니다:
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})내부적으로 전달되는 실제 URL 앞에 hash 문자(#)를 사용합니다. URL의 이 부분은 서버로 전송되지 않기 때문에 서버 측에서 특별한 처리가 필요하지 않습니다. 하지만 SEO에는 좋지 않은 영향을 줍니다. 이 점이 중요하다면 HTML5 히스토리 모드를 사용하세요.
메모리 모드
메모리 히스토리 모드는 브라우저 환경을 가정하지 않으므로 URL과 상호작용하지 않으며 초기 내비게이션도 자동으로 트리거하지 않습니다. 이 때문에 Node 환경과 SSR에 적합합니다. createMemoryHistory()로 생성하며, app.use(router)를 호출한 뒤 초기 내비게이션을 직접 push해야 합니다.
import { createRouter, createMemoryHistory } from 'vue-router'
const router = createRouter({
history: createMemoryHistory(),
routes: [
//...
],
})권장되지는 않지만 브라우저 애플리케이션 안에서도 이 모드를 사용할 수 있습니다. 다만 히스토리가 없기 때문에 뒤로 또는 앞으로 이동할 수 없습니다.
서버 설정 예제
참고: 아래 예제는 앱을 루트 폴더에서 제공한다고 가정합니다. 하위 폴더에 배포한다면 Vue CLI의 publicPath 옵션과 관련된 라우터의 base 속성을 사용해야 합니다. 또한 아래 예제도 루트 폴더 대신 하위 폴더를 사용하도록 조정해야 합니다(예: RewriteBase /를 RewriteBase /name-of-your-subfolder/로 변경).
Apache
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>mod_rewrite 대신 FallbackResource를 사용할 수도 있습니다.
nginx
location / {
try_files $uri $uri/ /index.html;
}Native Node.js
const http = require('http')
const fs = require('fs')
const httpPort = 80
http
.createServer((req, res) => {
fs.readFile('index.html', 'utf-8', (err, content) => {
if (err) {
console.log('We cannot open "index.html" file.')
}
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
})
res.end(content)
})
})
.listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort)
})Express with Node.js
Node.js/Express에서는 connect-history-api-fallback middleware 사용을 고려해 보세요.
Internet Information Services (IIS)
- IIS UrlRewrite를 설치합니다
- 사이트 루트 디렉터리에 다음과 같은
web.config파일을 만듭니다:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Handle History Mode and custom 404/500" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>Caddy v2
try_files {path} /Caddy v1
rewrite {
regexp .*
to {path} /
}Firebase hosting
다음을 firebase.json에 추가하세요:
{
"hosting": {
"public": "dist",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}Netlify
배포 파일에 포함되는 _redirects 파일을 만드세요:
/* /index.html 200vue-cli, nuxt, vite 프로젝트에서는 이 파일을 보통 static 또는 public 폴더 아래에 둡니다.
문법에 대한 자세한 내용은 Netlify 문서에서 확인할 수 있습니다. netlify.toml을 생성해 리디렉션 을 다른 Netlify 기능과 함께 사용할 수도 있습니다.
Vercel
프로젝트 루트 디렉터리에 다음 내용의 vercel.json 파일을 만드세요:
{
"rewrites": [{ "source": "/:path*", "destination": "/index.html" }]
}주의 사항
이 방식에는 주의할 점이 있습니다. 이제 찾을 수 없는 모든 경로가 index.html 파일을 제공하게 되므로 서버는 더 이상 404 오류를 보고하지 않습니다. 이 문제를 해결하려면 Vue 앱 안에 catch-all 라우트를 구현해 404 페이지를 표시해야 합니다:
const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/:pathMatch(.*)', component: NotFoundComponent }],
})또는 Node.js 서버를 사용 중이라면 서버 측에서 라우터를 사용해 들어오는 URL을 매치하고, 어떤 라우트도 매치되지 않으면 404로 응답하는 방식으로 fallback을 구현할 수 있습니다. 자세한 내용은 Vue 서버 사이드 렌더링 문서를 참고하세요.