나는
url
이 깔끔하게 보여지는게 좋아서, vue에서history mode
를 사용하고 있다.
vue를 build 후 build된 파일을 spring에 올려서 테스트를 해보다가 특이한 이슈를 발견했다.
/
주소는 접속이 되는데 해당 주소에서 다른 주소로 이동을 하면 whitelabel error page
가 뜨면서 제대로 화면이 나오지 않았다.
해당 내용으로 좀 더 찾아보니 vue에서 history mode
를 사용해서 발생한 문제였다.
지금부터 해당 문제를 해결한 내용에 관해서 기술을 해보겠다.
history mode
를 사용하는 경우, 다른 주소로 이동하려고 할 때 클라이언트내에서 다 처리 되는게 아니라 서버에다가 이동하려는 해당 url로 요청을 보내고 응답을 받아온다.view page
요청 관련해서는 api가 정의되어 있지 않기 때문에 해당하는 controller를 찾을 수 없다.whitelabel error page
다.)vue 와 spring 모두에서 처리해야 하는 일들이 있습니다.
<script setup>
</script>
<template>
<div>
<h1>해당 페이지를 찾을 수 없습니다.</h1>
<p>요청 주소를 다시 한번 확인해주세요.</p>
</div>
</template>
<style scoped>
</style>
routes
에 아래 내용을 추가하자
{path: '/notFound', component: NotFound}
{path: "/:pathMatch(.*)*", redirect: "/notFound"}
import {createRouter, createWebHistory} from "vue-router";
import NotFound from "@/pages/NotFound.vue";
const routes = [
{path: '/', component: Home},
{path: '/login', component: Login},
{path: '/notFound', component: NotFound},
{path: "/:pathMatch(.*)*", redirect: "/notFound"}
]
export const router = createRouter({
history: createWebHistory(),
routes
})
view page
에 대한 요청으로 인식하도록 filter를 만들어서 filter chain에 등록한다.index.html
하나로 동작되는 SPA 이므로, 비지니스 로직 관련 api를 제외하고는 모두 /
로 forward 시켜주자. (/
로 forwaring 되면 index.html
로 연결이 되므로)//Forwards all routes to the main route
//This is for SPA (Single Page Application) [Ex. React, Angular, Vue]
public class HistoryModeFilter extends OncePerRequestFilter {
// '/'로 시작하고 그 뒤에 점이 .이 없는 문자열과 일치
// '/api'로 시작하는 URL은 제외
// /home 또는 /user/profile과 일치, 하지만 /api/data, /api?query, /image.jpg, /script.js 와는 일치하지 않습니다.
private Pattern patt = Pattern.compile("^(?!/api)/([^.]*)");
// Main route
private String endpoint = "/";
@Override
protected void doFilterInternal(@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain)
throws ServletException, IOException {
if (this.patt.matcher(request.getRequestURI()).matches()) {
RequestDispatcher rd = request.getRequestDispatcher(endpoint);
rd.forward(request, response);
} else {
filterChain.doFilter(request, response);
}
}
}
spring boot 3.2
사용했습니다.FilterSecurityInterceptor.class
는 deprecated
되었고, AuthorizationFilter.class
를 사용하라고 해서 사용했습니다.@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfiguration {
private static final String[] WHITE_LIST_URL = {
"/api/auth/**", //로그인 및 회원가입
"/assets/**", //js 및 css 파일
"/favicon.ico", //favicon
"/", //root 경로
"/index.html", //index.html 파일
};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(req ->
req
.requestMatchers(WHITE_LIST_URL).permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(new HistoryModeFilter(), AuthorizationFilter.class)
return http.build();
}
}