ice rabbit programming

[Vue] history mode에서 subpath 접근하기(feat. 없는 페이지 처리하기) 본문

Development/Web etc.

[Vue] history mode에서 subpath 접근하기(feat. 없는 페이지 처리하기)

판교토끼 2021. 4. 28. 22:12

공식 문서 설명 : router.vuejs.org/kr/guide/essentials/history-mode.html

 

HTML5 히스토리 모드 | Vue Router

HTML5 히스토리 모드 vue-router의 기본 모드는 hash mode 입니다. URL 해시를 사용하여 전체 URL을 시뮬레이트하므로 URL이 변경될 때 페이지가 다시 로드 되지 않습니다. 해시를 제거하기 위해 라우터의

router.vuejs.org

vue는 SPA로, 처음에 모든걸 받아서 내부 router를 이용해 이동한다. 디폴트 모드로 사용하면 경로에 hash(#)가 들어가있는 것을 볼 수 있다. 예를 들어 https://my-page.com/#/home과 같은 식이다.

이 때 vue router 설정에서 history mode를 true로 주면 #이 사라진다.

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

이로써 문제 해결! 이면 참 좋겠지만.. 아쉽게도 로컬이나 정적 파일들(vue의 경우는 index.html)을 다이렉트로 접근하지 않는 이상은 root(/)가 아닌 다른 경로로 접근하면 not found에러가 발생한다. 이를 정상적으로 주기 위해서는 조치를 취해줘야 하는데, 본인이 알아낸 바로는 두 가지가 있다. 먼저 본인이 호스팅을 어떻게 하고 있는지 여부가 중요하다. 앞서 말했듯이 로컬이나 정적 파일에 직접 접근하는 경우는 별다른 조치가 필요 없다.

1. 웹 서버 nginx(혹은 apache)에서 정적 파일을 호스팅할 경우

가장 보편적으로 쓰이는 방법이다. 웹 서버에서 정적 파일을 접근하도록 하여 호스팅한다. 이 때에는 아래처럼 reverse proxy해주면 된다.

location / {
  try_files $uri $uri/ /index.html;
}

보통 reverse proxy할 때에는 proxy_pass를 더 많이 쓸 텐데, try_files는 맞는 것이 없으면 모두 전달한다고 한다.

nginx를 사용했다면 /etc/nginx에 있는 index.html을 vue 파일로 치환해줬을 것이다. 보통은 위처럼만 작성해도 동작하지만 만약 root 설정이 필요하다면 /etc/nginx로 명시해주자.

location / {
  root /etc/nginx;
  try_files $uri $uri/ /index.html;
}

 

참고로 apache는 공식 문서에 따르면 아래처럼 사용하면 된다(고 한다).

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

 

2. express.js 등으로 호스팅하는 경우

구글링을 해보면 1번 관련해서는 꽤 많은 정보를 찾을 수 있는데, 서버 사이드 렌더링 등을 위해 vue나 react 자체에 작은 서버로 호스팅을 할 경우는 관련 정보가 많이 없다(사실상 이 포스팅을 하는 이유). 공식 문서에서는 이런 미들웨어 사용을 권고한다.

미들웨어를 사용하지 않으려면, 아래처럼 static 파일을 app.use로 가져오고, 모든 경로에 대해서 send하면 된다.

// example
app.use(express.static(__dirname+'public'));
app.get('/*', (req, res) => {
  res.sendFile('./public/index.html')
})

 

보통 서버에 렌더링 연결은 SPA의 특성 상 '/'에만 연결할텐데, /*로 주면 모든 경로에 대해서 vue 파일로 연결된다.

vue 내부에서 404 처리

프론트엔드에 접근하는 모든 경로에 대해서 vue 파일로 보내기 때문에, vue 내에서 없는 페이지의 경우에는 에러 페이지 처리를 해주는 편이 좋다. 하지 않으면 (당연히) App.vue 화면과 빈 화면만이 나오게 된다.

간단하다. router 내의 routes에서, * path를 주면 된다. 공식 문서의 예시는 아래와 같다.

const router = new VueRouter({
  mode: 'history',
  routes: [{ path: '*', component: NotFoundComponent }]
})