Cronex

spring boot + vue project router문제와 404 해결 본문

spring-boot/errors

spring boot + vue project router문제와 404 해결

crone 2023. 4. 25. 01:42

이전글 : https://cronex.tistory.com/23

 

spring-boot 와 vue 이용하여 프로젝트 생성하기

spring boot 와 vue를 이용하여 프로젝트 생성하기 프로젝트를 시작하게 된 계기는 김영한님의 JPA 강의를 들으면서 vue도 다시 공부할 겸, thymeleaf 말고 vue를 이용하여 개발을 해보자 하여 시작하게

cronex.tistory.com

 

프로젝트를 생성 후 front-end에서 build한 결과물을 back-end 내 resources 하위에 static 폴더와 함께 생성하는 구조까지 만들었었습니다. 이 후 vue에 vue-router를 추가하면서 문제가 발생하였는데, build 후 spring-boot로 서버를 띄웠을 때 최초 localhost:8080/  [index.html]으로의 요청은 문제가 되지 않았습니다.

 

vue-router 설정은 아래와 같습니다.

import { createRouter, createWebHistory } from "vue-router";

import WelcomeHome from "@/components/WelcomeHome";
import CoachList from "@/components/CoachList";
import TeamList from "@/components/TeamList";

const router = createRouter({
    history: createWebHistory(),
    routes: [
        {path: "/", component: WelcomeHome},
        {path: "/team", component: TeamList},
        {path: "/coach", component: CoachList},
    ],
});

export default router;

 

App.vue

 

App.vue

App.vue

<template>
  <p>WelComeHome</p>
  <router-link to="/team">team으로 이동</router-link>
  <router-link to="/coach">Coach로 이동</router-link>
</template>

<script>
export default {
  name: "WelcomeHome"
}
</script>

<style scoped>

</style>

 

 

App.vue에 작성한대로 vue-router를 이용한 페이지 이동은 문제가 되지 않았습니다. 

vue-router를 이용한 /team으로의 이동

router path: /team

TeamList.vue

<template>
  <p>Team List comp</p>
</template>

<script>
export default {
  name: "TeamList"
}
</script>

<style scoped>

</style>

vue-router를 이용한 /coach 페이지 이동

vue router path: /coach

CoachList.vue

<template>
  <p>Coach List</p>
</template>

<script>
export default {
  name: "CoachList"
}
</script>

<style scoped>

</style>

 

 

문제

문제는 이 이후 발생했습니다..브라우저 URL로 http://localhost:8080/coach 또는 vue-router로 이동한 coach 페이지에서 새로고침을 했을 때 아래와 같은 404 error page를 만나게 되었습니다... [ vue-router의 history value를 createWebHistory()로 설정함 ]

404 error page

404 페이지가 발생한 이유를 곰곰히 생각해보니, index.html 파일 내에서의 link를 통한 페이지 이동은 vue-router를 통해 이동하기 때문에 발생하지 않았지만, http://locahost:8080/coach 이나 새로고침 같은 경우 vue-router를 통한 화면 전환이 아닌, server에게 요청을 보내고 해당 결과를 보여주려 하고 있었기 때문에 404 페이지를 만나게 된 것이었습니다.

즉, back-end server에는 /coach 에 해당하는 resourecs 나 controller가 작성되어있지 않았기 때문에 server에서는 404 page를 뱉은것이었습니다.

다시 브라우저에서 network 탭을 열어서 확인해보면 localhost:8080 요청 후 vue-router로 team으로 이동할 때 network탭을 보면 request를 보내지 않는다는 것을 확인 할 수 있습니다.

localhost:8080

 

localhost:8080/team

 

하지만 문제의 새로고침 이나 localhost:8080/team 을 브라우저에 직접 입력하게 되면 아래처럼 server에 request를 보내게 된 것입니다. 

새로고침 또는 localhost:8080/team

해결방법부터 말씀드리자면 component-scan 범위에 해당하는 package에 WebMvcConfigurer를 구현한 class를 추가해주면 됩니다.

package jpaBook.jpaShop.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;

import java.io.IOException;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/**")
                .resourceChain(true)
                .addResolver(new PathResourceResolver() {
                    @Override
                    protected Resource getResource(String resourcePath, Resource location) throws IOException {
                        Resource resource = location.createRelative(resourcePath);
                        return resource.exists() && resource.isReadable() ? resource : new ClassPathResource("/static/index.html");
//                        return super.getResource(resourcePath, location);
                    }
                });


//        WebMvcConfigurer.super.addResourceHandlers(registry);
    }
}

현재 프로젝트의 resources 구조는 아래와 같습니다.

back-end project 구조

프로젝트 구조를 보면 아시겠지만, static 하위에 build 한 결과물이 생기기 때문에 addResourceLocations에 인자 값으로 classpath:/static/** 를 주었습니다. 만약 저와 프로젝트 구조가 다르다면 그에 맞게 수정하시면 될 듯 합니다.

수정 후 localhost:8080/team 으로 요청을 보내거나 새로고침을 하고 난 후 network 탭을 보면 

localhost:8080/team

localhost:8080/team 으로 요청을 보내고 응답이 온 것을 확인할 수 있으면 reuqest의 response 탭을 보면 index.html 파일이 repsonse에 담겨있는것을 볼 수 있습니다.

response tab

<!doctype html>
<html lang="">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link rel="icon" href="/favicon.ico">
    <title>front-end</title>
    <script defer="defer" src="/js/chunk-vendors.5d66e1d9.js"></script>
    <script defer="defer" src="/js/app.c310bfbf.js"></script>
    <link href="/css/app.b2d625b8.css" rel="stylesheet">
</head>
<body>
<noscript><strong>We're sorry but front-end doesn't work properly without JavaScript enabled. Please enable it to
    continue.</strong></noscript>
<div id="app"></div>
</body>
</html>

동작이 가능한 이유를 생각해보면 

1. localhost:8080/team 으로 요청을 보내면 server에서는 설정한 값에 의해 index.html을 내려주게 된다.

2. 브라우저는 요청을 받아 index.html 을 읽으면서 index.html 파일에 있는 script와 css 요청을 서버에 다시 보낸다

3. 해당 js, css 파일을 받아 렌더링하면서 vue-router에 의해 routes로 설정한 /team page로 이동하게 되었다.

라고 할 수 있습니다. 

마지막으로 vue-router에 매칭되지 않은 path 요청을 처리하기 위해 notFound path를 매핑해주었습니다.

import { createRouter, createWebHistory } from "vue-router";

import WelcomeHome from "@/components/WelcomeHome";
import CoachList from "@/components/CoachList";
import TeamList from "@/components/TeamList";

const router = createRouter({
    history: createWebHistory(),
    routes: [
        {path: "/", component: WelcomeHome},
        {path: "/team", component: TeamList},
        {path: "/coach", component: CoachList},
        {path: "/:notFound(.*)", redirect: "/"}

});

export default router;

/notFound(.*) path를 최하위로 두어 위에 설정한 route 값에 매칭되는 것이 없을경우 /notFound(.*) path를 타게되어 index 페이지로 리다이렉트 시켜주었습니다.

/notFound(.*) 를 잘 모르시는 분들은 아래 링크를 참고하시면 될 것 같습니다.

- https://router.vuejs.org/guide/essentials/route-matching-syntax.html

 

Vue Router | The official Router for Vue.js

The official Router for Vue.js

router.vuejs.org

 

이제 locahost:8080/asudasbd 와 같은 매칭되지 않은 path 요청을 보낼경우,

locahost:8080/asudasbd

 

locahost:8080/asudasbd

서버에서는 index.html 파일을 내려주고, 

locahost:8080/asudasbd

vue-router에 의해 index 페이지로 리다이렉트 시키는 것을 볼 수 있습니다.

출처: https://stackoverflow.com/questions/38516667/springboot-angular2-how-to-handle-html5-urls