Spring Boot에 Vue 프로젝트를 빌드해서 Spring Boot 프로젝트 만 실행해도 API서버와 화면을 모두 사용할 수 있도록 구상하고 있습니다.
Vue와 Spring Boot를 연동을 해보겠습니다.
1. Vue 빌드
Vue프로젝트를 실행시킬 명령어 스크립트는 package.json에 있습니다.
- serve
- 명령어 : npm run serve
- Vue프로젝트를 직접 실행시킬때는 8081 포트로 설정했습니다.
- build
- 명령어 : npm run build
- Vue 프로젝트 빌드 스크립트입니다.
Vue프로젝트를 빌드했을때 Spring Boot에 생성하려고 합니다.
build 파일 생성 경로는 vue.config에서 설정해줄 수 있습니다.
outputDir에 빌드 파일을 생성하려고 하는 상대 경로를 설정해줍니다.
build 명령어를 수행하면
Spring Boot프로젝트의 실행 모듈인 scm_api에 /static 디렉토리 하위에 빌드 파일이 생성되었습니다.
Vue 프로젝트를 빌드할때마다 /static에 존재하는 빌드 파일이 재생성 됩니다.
2. Vue - Spring Boot 연동
Spring Boot를 실행해서 화면이 잘 뜨는지 확인합니다.
Vue프로젝트를 직접 실행했을때는 잘 나오던 화면이 404에러를 내며 Not Found에러를 발생시킵니다.
이유는 Vue의 router 경로로 실행 되던것이 Spring Boot와 합쳐져서 실행되다보니 router 경로가 아닌 url로 인식하고 존재하지 않는 api라고 에러를 내는 것이였습니다.
/main이라는 url로 Get method 요청을 보냅니다.
2.1 Vue 구조
SPA(Single Page Application) - 단일 페이지 애플리케이션
Vue는 SPA구조로 index.html을 기본으로 다른 화면을 띄우는 식으로 동작하고 있습니다.
그렇기 때문에 /static 하위에 index.html 파일이 존재하는 것을 확인할 수 있는데, Spring Boot에서 url이 아닌 router로 읽을 수 있도록 Spring Boot MVC에서 index.html을 바라보게 해야합니다.
2.2 삽질
- ErrorController
- 존재하지 않는 url을 호출했을때 Get method로 /error 를 요청했을때 정적 리소스인 /static/index.html을 호출하게 하는 방법입니다.
- 하지만 여전히 index.html를 찾을 수 없다는 에러를 발생시킵니다.
- ResourceHandler
- Spring MVC에서 Resource를 읽도록 설정을 추가하는 방법입니다.
- 하지만 여전히 index.html을 찾을 수 없다는 에러를 발생시킵니다.
- (나중에 보니까 @EnableWebMvc 어노테이션을 추가해서 안되는 거였습니다.)
2.3 정적 파일 읽어오기
몇 시간의 삽질을 하던 중 index.html을 읽을 수 있는 방법 중 url로 넘어왔을때 api요청이 아닌 경우 index.htm을 읽을 수 있도록 하는 resolver를 추가하는 방법을 찾을 수 있었습니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/**") //1
.addResourceLocations("classpath:/static/") //2
.resourceChain(true)
.addResolver(new CustomPathResourceResolver()); //3
}
}
//4
public class CustomPathResourceResolver extends PathResourceResolver {
@Override
public Resource getResource(String resourcePath, Resource location) throws IOException {
Resource requestResource = location.createRelative(resourcePath);
return requestResource.exists() && requestResource.isReadable()
? requestResource : new ClassPathResource("/static/index.html");
}
}
- addResourceHandler
- 정적 파일을 요청을 매핑할 Path 선언 - addResourceLocation
- 정적 파일 위치 선언 - addResolver
- 요청으로 온 url이 정적 파일 요청인지 api 요청인지 확인하는 resolver 추가 - CustomPathResourceResolver
- 해당 url이 정적 파일 요청인지 판별해서 /static/index.html 파일을 읽도록 하는 resolver
이렇게 설정해 주었더니 localhost:8080/main을 url이 아닌 index.html 파일을 읽어 왔습니다.
기타 이슈. @EnableWebMvc
정적 파일을 읽어오기를 성공하는 과정에서 약간의 이슈가 있었는데, localhost:8080으로 요청했을때는 또 존재하지 않는 파일이라고 에러를 발생시키는 걸 보고 당황했습니다.
문제를 파악하는 과정에서 ResourceHandler를 추가해주는 WebConfig클래스에서 @EnableWebMvc와 관련된 문제를 찾을 수 있었습니다.
@Configuration
//@EnableWebMvc 제거
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/**")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new CustomPathResourceResolver());
}
}
Servlet Web Applications :: Spring Boot
For servlet application, Spring Boot includes support for embedded Tomcat, Jetty, and Undertow servers. Most developers use the appropriate starter to obtain a fully configured instance. By default, the embedded server listens for HTTP requests on port 808
docs.spring.io
스프링에서 기본적으로 제공하는 웹 기능에서 추가를 하고 싶은 경우에는 @Configuration과 @EnableWebMvc를 함께 사용하면 안된다고 합니다.
@EnableWebMvc는 스프링의 MVC를 완전제어 할 수 있는 어노테이션으로써, 스프링의 기본 설정들이 일부 무시된다고 합니다.
이러한 것때문에 localhost:8080/main으로 요청은 index.html을 읽을 수 있지만 localhost:8080 요청은 읽을 수 없는게 등록되지 않은 resolver로 인해서 정상적으로 정적 파일을 읽지 못한 것 같습니다.
(정확히 어떤 설정이 제외되어 이런 문제가 발생하는지는 찾지 못했습니다.)
그래서 Resolve와 관련해서 커스텀하고 싶은 설정에 대해서 추가하고 싶은 경우에는 @Configuration 어노테이션만 사용해서 추가하면 됩니다.
3. Rest API 요청, 응답
Spring Boot에서 화면을 띄우는 것까지 성공을 했습니다.
다음은 화면에서 API 서버에 http 호출을 해보겠습니다.
3.1 axios
Vue에서 http 호출을 하기 위해서는 axios라는 패키지를 많이 사용한다고 합니다.
npm install axios를 통해 패키지를 설치해줍니다.
axios가 성공적으로 설치되면 main.js에서 app에서 사용할수 있도록 세팅이 되어있습니다.
저는 각 vue파일에서 http 통신을 할때 api.get(), api.post() 등으로 호출하고자 api.js라는 모듈을 만들었습니다.
http 통신을 테스트 해보기 위해 먼저 로그인 화면을 만들어 보겠습니다.
로그인 버튼을 눌렀을때 호출되는 login()함수에서 api.post()를 호출하게 해보았습니다.
cors 에러가 발생합니다.
3.2 CORS
CORS란 Cross Origin Resource Sharing의 준말로, 브라우저에서 http 요청이 왔을 경우 Http 헤더에서 허용되지 않은 요청의 경우 막는 보안 메커니즘입니다.
CORS가 필요한 이유는 악의를 가지고 허용되지 않은 곳에서 서버에게 요청이 왔을 경우를 막기 위한 것으로 브라우저 단에서 막아줍니다.
그렇기 때문인지 검색을 헀을때 많은 사람들이 화면과 API서버를 개발할때 항상 겪는 문제인것 같았습니다.
알아보니 두가지 방법이 있었습니다.
- Vue 프로젝트에서 proxy서버로 호출하기
- Spring Boot에서 CORS 허용하기
저는 개발 단계에서 많이 쓰인다는 proxy서버로 호출하는 방법을 사용했습니다.
vue.config에서 proxy 호출을 선언해주었습니다.
저는 API 서버에 api 요청을 했을때 /api를 붙여서 호출하여 Rest ful API호출을 구분해주려고 해서 /api를 추가해주었습니다.
그리고 api.js에서 기본이 되는 BASE_URL을 제외해주었습니다.
응답도 입력해준 값으로 정상적으로 반환됩니다.
3.2.1 proxy
여기서 궁금했던게 proxy 호출을 적용해서 CORS를 어떻게 통과가 되는가?? 였습니다.
요약하면 브라우저를 속이는 http 호출을 하는 것입니다.
예를 들어 Vue 포트가 3000이고 Spring Boot 포트가 8080이라고 가정하겠습니다.
Vue에서는 localhost:3000/api 을 호출하는 것이지만, 실제로는 Vue 서버의 프록시 설정을 통해 실제 API 서버(localhost:8080/api)로 호출하게되는 것입니다.
CORS는 브라우저에서 수행되는 보안 매커니즘입니다. 그래서 브라우저 입장에서는 같은 도메인으로 요청하는 것으로 보이기 때문에 CORS에러가 발생하지 않은 것이었습니다.
제가 생각했을때는 우회하는 방법이 올바른 방법이 아니라고 생각했지만 개발 환경에서는 일반적으로 사용되는 방법이기 때문에 선택하게 되었고 추후에 API 서버에서 CORS를 허용하는 부분을 적용해볼 예정입니다.
참조
https://gkawjdgml.tistory.com/162
[구축] vue & spring boot 연동
vue 프로젝트를 서버 프로젝트와 연동하기 위한 작업 내용을 정리해보았다.window OS, intelliJ IDE로 작업되었다. 연동이라는 단어로 표현했다싶이 vue와 spring, 각각의 프로젝트를 연결하는 작업에 대
gkawjdgml.tistory.com
https://steady-hello.tistory.com/52
[스프링 부트] 정적 리소스 - static resource
기본적으로 Spring Boot는 classpath의 /static 또는 ServletContext의 root를 기준으로 아래의 경로에서 정적 컨텐츠를 제공한다. 기본적으로 Spring MVC의 ResourceHttpRequestHandler를 사용하는데 이를 커스터 마이
steady-hello.tistory.com
스프링 부트 + Vue.js 연동 같은 프로젝트 일 때 router 설정
스프링 부트 프로젝트에 vue.config.js 로 npm build 하여 /resources/static/ 경로에 index.html로 만들었을 경우 import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' import Login from "@/compone
cokes.tistory.com
https://mangkyu.tistory.com/176
[Spring] 설정 자동화와 설정의 변경, @EnableWebMvc와 WebMvcConfigurer
이번에는 @EnableWebMvc 어노테이션과 WebMvcConfigurer 인터페이스에 대해 알아보도록 하자. 1. Spring에서 제공하는 설정의 자동화와 변경 [ @Enable~ 을 이용한 설정 자동화 ] Spring 기반의 프로젝트를 구축
mangkyu.tistory.com
'프로젝트' 카테고리의 다른 글
[토이프로젝트] - 5. 멀티모듈 Bean 등록 (1) | 2024.10.13 |
---|---|
[토이프로젝트] - 4. Spring Boot 멀티 모듈 yml 설정 (0) | 2024.10.13 |
[토이프로젝트] - 2. Vue 설치 (8) | 2024.10.09 |
[토이프로젝트] - 1. 멀티모듈 세팅 (4) | 2024.10.09 |
[ERROR] Can't commit changes from multiple changelists at once (0) | 2023.03.19 |