(2023.11.03)
아직 DB 연결 안하고 vue랑 springBoot 로 axios , api 로 데이터 왕복까지 세팅
▷ 환경
▶ backend
> PORT : http://localhost: 8088
> application.properties에 server.port = 8088 하나만 씀 mvc패턴 정의 안함.
- jdk
> Version: 1.8
> sts4 구동 시 JavaSE11 버전 썼더니 안된다고 오류떠서 정정함
> jdk 적용 시 프로젝트 우측 마우스에서 build path 랑 properties > project facets 둘 다 바꿔줘야댐
- sts
> Version: 4
> spring.io 에서 maven, 1.8, 스프링 버전은 아무거나, (lombok, spring web, jpa 사용)
> 스프링 버전도 맞지 않았는지 오류가 떠서 pom.xml에서
# pom.xml
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
버전을 2.6.3으로 정의
> 아직 db 연결을 안해서
# pom.xml
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> -->
이거 주석처리
> 일단 값 확인만 하려고 controller단에서 RestController로 String값 return만 해주었다. 근데 여기서도 문제였던 건 내가 패키지명을 메인 패키지 경로와 어긋나게 만들어서 controller mapping에 들어오지 못했음.
예를 들면 src/main/java 밑에 만약 com.vueSpring.Pname 가 PnameApplication.java가 만들어져 있는 default 경로라면 controller 또한 최소한 com.vueSpring.Pname.controller 가 되던 com.vueSpring.Pname.~~~.controller 가 되던 기본 경로 안에서 만들어야 한다는 것임.
> tomcat이던 뭐던 서버를 설치할 필요도 위 내용말고 그 어떤 세팅도 서버에서 할 필요없음 front단에 vue 내용을 back단에 npm run build해서 static에 front내용을 옮긴 후 톰캣으로 열어서 사용하는 게 아니라
back 프로젝트 따로 front 프로젝트 따로 실행시키는 즉 back에서 spring boot app 으로 서버를 구동시키고, front단 run으로 페이지를 열고 api 요청 시 back과 front가 서로 넘나드는 완전히 분리되어 있지만 서버로 연결된 생태계임.
.
▶ front
> PORT : http://localhost: 3000
- visual studio, node
- vue3
> Version: 3
> vuex, router 등등 다 install @next(최신버전)
> front 리로드 시 back 단에서도 반응하기 위해 --watch 사용 , port도 3000번으로 여기서 정의해준다.
# package.json
"scripts": {
"serve": "vue-cli-service serve --port 3000",
"build": "vue-cli-service build --watch",
"lint": "vue-cli-service lint"
},
> cors를 해결하기 위한 엄청난 여정
# PageAbout.vue
<template>
<div class="about">
<h1>This is an about page1</h1>
<h1>{{ connectData1 }}</h1>
<h1>This is an about page2</h1>
<button v-on:click="get1">springBootTest1</button>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "PageAbout",
components: [],
data() {
return {
connectData1: "noneData",
};
},
methods: {
get1: function () {
axios
.get("http://localhost:8088/api/vueSpringTest/test2")
.then((response) => {
this.connectData1 = response.data;
})
.catch((error) => {
// 예외 처리 작업
console.error(error);
});
},
},
mounted() {},
};
</script>
<style></style>
# main.js
import { createApp } from "vue";
import App from "./App.vue";
import { router } from "./routes/index.js";
import { store } from "./store/index.js";
import "./assets/common.css";
import axios from "axios";
const app = createApp(App);
app.use(router);
app.use(store);
app.mount("#app");
/* axios */
// axios.defaults.baseURL = "http://localhost:8088";
app.config.globalProperties.axios = axios;
일단 main.js에서 axios 세팅하고 보여질 페이지에서 경로와 메서드를 작성하여 실행하니 cors 에러가 났음. 대충 서로 사용하려는 origin 즉 근원의 주소값이 충돌나는 거임. 이 메서드를 사용하려할 때 부터 에러가 날걸 알았지만 다른 사람이 써놓은 거니까 그냥 속는셈치고 썼는데 진짜 지옥이 펼쳐져버렸음
(일단 여기서 get으로 보낼 url은 당연히 back단에 서버 주소임)
main.js에서 baseURL을 작성하는 것도 본 페이지에서 직접 /api앞에 주소를 입력하는 것도 안되니 검색했을 때 사람들이가장 많이 사용하는 해결책은 vue.config.js 문서에
# vue.config.js
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 3000,
historyApiFallback: true,
proxy: {
"/api": {
target: "http://localhost:8088",
ws: true,
changeOrigin: true,
},
},
},
});
다음과 같이 devServer > proxy 에 /api target을 정해주면 통신을 시도하는 페이지에서 url에 /api 라는 주소를 포함할 시 target에 주소값으로 매칭해주는데 결론은 proxy를 이용해 우회시켜 cors를 막아주는 방법이다.
처음엔 이것도 안돼서 주소값 앞에 무슨 우회주소를 붙이거나 별짓 다했는데 무튼 위 방법을 썼을 때 결정적으로 해결이 안된 이유는 console 에서 /api값이 proxy에서 작성한 target 값인 8088이 아닌 3000번으로 가는 것이다. 즉 proxy가 먹지 않은 것. cors가 발생하는 것도 아니고 그냥 답이 없었다.
하지만 정말 똑똑하시고 대단하시고 멋지신 슈퍼 구세주 매니저님께서 이유를 찾아주셨다.
axios를 main.js에서
app.config.globalProperties.axios = axios;
로 선언한 게 vue3 문법에 어긋났는 지 axios 객체에 인스턴스가 생성되지 않았던 것. 인스턴스 생성조차 되지 않아서 인지 proxy가 감지조차 하지 않았다.
# PageAbout.vue
<template>
<div class="about">
<h1>This is an about page1</h1>
<h1>{{ connectData1 }}</h1>
<h1>This is an about page2</h1>
<button v-on:click="get1">springBootTest1</button>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "PageAbout",
components: [],
data() {
return {
connectData1: "noneData",
};
},
methods: {
get1: function () {
axios.create({
// baseURL: process.env.BASE_API, // api base_url
// baseURL: "http://localhost:8088",
// timeout: 20000, // request timeout
});
axios
.get("/api/vueSpringTest/test2")
.then((response) => {
this.connectData1 = response.data;
})
.catch((error) => {
// 예외 처리 작업
console.error(error);
});
},
},
mounted() {},
};
</script>
<style></style>
그래서 axios.create({})를 해주면 axios의 인스턴스를 생성하게 되고 안에 내용을 비워놓더라도 인스턴스 생성한 것 만으로도 proxy가 감지를 해서 vue.config.js 에 위처럼 정의, main.js는 axios를 싹다 주석 처리, 위처럼 작성해서 결국 통신 성공.
▷ 느낀점
난 지금까지 MVC 패턴을 jsp 구조로만 사용해서 spring boot 안에서 front와 back단을 함께 사용하고 view를 web-inf에 views에서 jsp 파일을 사용했음. 그래서 우물 안 개구리로 이해하고 있었음.
이 구조 자체가 mvc에 대표적인 형태라 생각했지만 내가 이해한 view단을 사용하는 방식은 완전히
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
를 적용한 jsp 구조였던 것.
node에 vue.js를 사용하니까 react도 마찬가지이고 MVC가 어떤 생태계인지 확실하게 보였음.
backend에서 model service controller mapper 등을 사용하여 오로지 front단 내부에서의 데이터의 유입 및 이동을 보다 편하게 제어하기 위해 사용됨. front에서는 back은 (DB 제외) api 요청으로 axios 통신을 통해 주고 받는 용도로만 사용함.
vue나 react의 장점은 jsp와 달리 서버에서 dom을 전부 완성 시키고 배포하여 매우 빠름. 그래서 더욱 각광받고 있는데 확실히 javascript를 잘한다고 vue와 react를 잘하는 게 절때 아님. 나는 js에 자신이 있었지만 vue를 이번에 처음 사용하면서 vue만에 독보적인 생태계와 사용방식을 이해해야만 했고, 극한까지 router, session, method의 사용 등을 축약시키고 정리할 수 있어서 그냥 말이 vue.js지 vue.vue임 vue언어를 새로 배우는 기분, 그렇게 어렵진 않지만 생초보자에겐 진입장벽이 꽤 높은 것 같다. 그래서 잘 모르는 사람들은 vue를 한다고 하면 프론트 개발해? 하겠지만 백단에서 query랑 service 좀 만지는 거 제외하면 그냥 vue에서 front, back을 모두를 아울러 개발하는 것 같다.
아무튼 이제 mvc 적용해서 데이터 다시 주고 받고 db연결 예정
(본 글은 오로지 제 지식으로만 이루어진 완전히 주관적인 내용으로 부분 참고하시길 바랍니다..~~ )