[Vue.JS] SpringBoot + Gradle ํ๋ก์ ํธ ์ฐ๋
- -
๊ฐ์ธ ํ๋ก์ ํธ๋ฅผ ์งํํ๊ธฐ์ ์์ ํ๋ก์ ํธ ์ค์ ์์ ๋ฐ ์ธ์ด ์ ํ ๋ฑ ๋ค์ํ ์ ํ์ง์ ๋ฒ์ ๋ฑ ๋งค ์๊ฐ ์ ํ์ ์ฐ์์ด์์ต๋๋ค. ์คํ๋ง๋ถํธ ํ๋ก์ ํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก Gradle๊ณผ JPA๋ฅผ ํ์ฉํด์ ๋ฐฑ์๋(API)๋ฅผ ๋ง๋ค๊ณ , ํ๋ฐํธ์๋ ํ๋ ์์ํฌ๋ Vue ๋๋ React๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํฉ๋๋ค.
๋ฐฑ์๋๋ Java, SpringBoot๋ก ์ ํํ๊ณ ํ๋ก ํธ์ชฝ์ Vue๋ก ์ ํ์ ํ์ง๋ง ์๋ฒ์ ์ฌ๋ฆด ๋๋ ๊ฐ์์ ์๋ฒ ๊ตฌ๋ ๋ฐฉ์์ด ์๋ ํ๋์ ํ๋ก์ ํธ์์ SpringBoot์ ๋ด์ฅ ํฐ์บฃ์ ํฌํธ๋ง ์ฌ์ฉํ๊ณ ์ถ์์ต๋๋ค.
์ด๋ฌํ ๋ฐฉ์์ด ๊ฐ๋ฅํ์ง ๊ฐ๋ฅํ๋ค๋ฉด ์ค์ ์ ์ด๋ป๊ฒ ํ๋์ง ์๋ฆฌ๋ ๋ฌด์์ธ์ง ๊ถ๊ธ์ฆ์ด ์๊ฒจ์ ์ด์ฐธ์ ํ๋ฒ ์ฐพ์๋ณด๊ณ ํ๋ก์ ํธ ์ธํ ์ ํด๋ณด๊ฒ ์ต๋๋ค. (ํ๊ฒ ๋ค๋ ๊ฑด ๋ฐฉ๋ฒ์ด ์๋ค๋ ๊ฑฐ๊ฒ ์ฃ ? ๐)
#1. SPA ? SSR ?! CSR !?!
SPA์ ์๋ฆฌ์ธ CSR(Client Side Rendering)๊ณผ SSR(Server Side Rendering)์ ๊ธฐ๋ณธ ๊ฐ๋ ๊ฐ๋ตํ๊ฒ๋ง ์์๋ณด๊ฒ ์ต๋๋ค.
SPA ๋?
- SPA(Single Page Application)๋ ์๋ฒ๋ก๋ถํฐ ์ฒซ ํ์ด์ง๋ง ๋ฐ์์ค๊ณ ์ดํ์๋ ๋์ ์ผ๋ก ํ์ด์ง๋ฅผ ๊ตฌ์ฑํ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ป
SSR ์ด๋ ?
- SSR(Server Side Rendering), ์ฆ ์๋ฒ ์ธก์์ ๋ ๋๋ง์ด ์ผ์ด๋๋ค๋ ๋ป
- ๋ฐ์ดํฐ๊น์ง ๋ชจ๋ ์ฝ์ ๋ ์ํ์ ์์ฑ๋ HTMl์ ์๋ฒ๋ก๋ถํฐ ๋ฐ์์ค๋ ๋ ๋๋ง ๋ฐฉ์
CSR ์ด๋ ?
- CSR(Client Side Rendering)์ ๋ง ๊ทธ๋๋ก SSR๊ณผ ๋ฌ๋ฆฌ ๋ ๋๋ง์ด ํด๋ผ์ด์ธํธ ์ชฝ์์ ์ผ์ด๋๋ค๋ ๋ป
- ์น ๋ธ๋ผ์ฐ์ ์ ์ฃผ์ ์ ๋ ฅ ์ ๋ฐ์ดํฐ๊ฐ ์๋ ๋น HTMl๋ง ๋ฐ์์ค๊ณ , ๋ฐ์ดํฐ๋ ์ฌ๋ฌ static ํ์ผ๋ค์ด ๋ก๋๋ ํ์ ์๋ฒ์ ์์ฒญํด์ ๋ฐ์์ค๋ ๋ฐฉ์์ผ๋ก ๋ ๋๋ง
#2. ํ๋ก์ ํธ ์ค์
ํ์ฌ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๊ณ ์๋ ์ธํ ๋ฆฌ์ ์ด(IntelliJ IDEA) ํด์ ์ด์ฉํด์ ์์ ์ ํด๋ณผ ์์ ์ ๋๋ค. VS Code๋ ๋ค๋ฅธ ํด์ ์น์ํ์๋ค๋ฉด ํด๋น ํด์์ ์์ ํ์ ๋ ๋ฉ๋๋ค.
ํด๋น ๋ด์ฉ์์๋ SpringBoot ํ๋ก์ ํธ๋ฅผ ์์ฑํ๋ ๋ด์ฉ์ ๋ค๋ฃจ์ง ์๊ฒ ์ต๋๋ค. ์์ ์ ์์ฑํด ๋ ํฌ์คํ ์ด ์์ผ๋ ์ค์ ์ ์ฐธ๊ณ ํด ์ฃผ์๊ณ ๋ฒ์ ์ด ๋ฎ์ ์ ์์ผ๋ ์ต์ ๋ฒ์ ์ผ๋ก ์ ๊ทธ๋ ์ด๋ํด์ฃผ์๋ฉด ๋๊ฒ ์ต๋๋ค.
ํด๋น๋งํฌ๋ ์ฌ๊ธฐ๋ฅผ ํด๋ฆญํด ์ฃผ์ธ์.
1. Vue ์ค์น ๋ฐ ํ๋ก์ ํธ ์์ฑ
#์ค์น
$ npm install -g @vue/cli
#ํ์ธ
$ vue --version
#vue ํ๋ก์ ํธ ์์ฑ
$ vue create frontend
#ํ๋ฌ๊ทธ์ธ ์ธํ
์ฌํ์ธ
$ npm i
#vue ํ๋ก์ ํธ ์์
$ cd frontend
$ npm run serve
์์์๋ถํฐ ์ฐจ๋ก๋๋ก ๋ช ๋ น์ด ์คํํด์ Vue ์ค์น ๋ฐ Vue ํ๋ก์ ํธ๋ฅผ ์์ฑํด ์ค๋๋ค. ์ฌ๊ธฐ๊น์ง ์งํํ๋ค๋ฉด ์๋์ ๊ฐ์ ๊ตฌ์กฐ๊ฐ ๋์ด์์ ๊ฒ๋๋ค.
์ฌ์ค ์ฌ๊ธฐ๊น์ง๋ง ํด๋ ๋ก์ปฌ ํ ์คํธ๋ ๊ฐ๋ฅํ์ง๋ง, ์ ์ ๋ชฉํ๋ ์๋ฒ ๋ฐฐํฌ๊น์ง ์๋ฃํ๋ ๊ฒ์ ๋๋ค.
์๋ฒ์ ์ด๋๋ก ๋ฐฐํฌํ๋ ค๋ฉด Vue ํ๋ก์ ํธ์์ ์์ ํ๊ณ ๋น๋๋ ํ์ผ์ ์คํ๋ง๋ถํธ ํ๋ก์ ํธ์ ์์์ ์ผ๋ก ์ฎ๊ฒจ์์ผ ํ๊ณ ... ์๊ฐ๋ง ํด๋ ๊ท์ฐฎ์ ์์ ๊ฐ์ง ์๋์?! ์๋์๋ผ๋ฉด ์ด์ฉ ์ ์์ง๋ง.. ์... ์ ๋ ๊ทธ๋์ ์คํ๋ง๋ถํธ ๋ด์ฅ ํฐ์บฃ ์คํ ์ ์๋์ผ๋ก Vue Build๋ฅผ ์งํํ๊ณ ๊ฒฐ๊ณผ ํ์ผ๋ค์ ์คํ๋ง๋ถํธ ํ์ ๋๋ ํฐ๋ฆฌ์์ ์์ฑ๋ ์ ์๋๋ก ์ค์ ์ ํ๊ฒ ์ต๋๋ค.
์ด๋ฐ ๊ตฌ์กฐ๋ก ๋ง๋ค๊ฒ ๋๋ค๋ฉด ๋ก์ปฌ ๊ฐ๋ฐ ์์๋ ํฌํธ๋ฅผ ๋ถ๋ฆฌํด์ ์ฐ๊ณ ๊ฐ๋ฐ ์๋ฒ์ ์ฌ๋ฆด ๋๋ Jar ํ์ผ ํ๋๋ง์ผ๋ก ํ๋์ ํฌํธ๋ฅผ ๊ฐ์ง ์ํ๋ก ๋ฐฐํฌํ ์ ์๊ฒ ๋ฉ๋๋ค.
โป ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ํ๋ก์ ํธ ์ค์ ํ ์๋ ์์ต๋๋ค. ๊ฒฐ๊ณผ๋ ๋์ผํฉ๋๋ค. โป
ํ ํ๋ก์ ํธ์ ๊ฐ์ด ๋๋ ๊ฒ์ด ์๋ ๋์ผ Depth์ Vue ํ๋ก์ ํธ์ SpringBoot ํ๋ก์ ํธ๋ฅผ ๋ฐ๋ก ๋ง๋ค๊ณ ๊ฒฝ๋ก๋ง ๋ง์ถฐ์ฃผ๋ฉด ๊ฐ๊ฐ์ ์์ค๋ฅผ ๋ฐ๋ก ํ์๊ด๋ฆฌํ ์ ์์ต๋๋ค.
2. Springboot ์คํ ์ Vue.js ์๋ ๋ฐฐํฌ ์คํ ์ฝ๋ ์ถ๊ฐ
์๋ฌดํผ! ์ด๋ฒ ํฌ์คํ ์์๋ SpringBoot ํ๋ก์ ํธ ์์์ Vue ํ๋ก์ ํธ๋ฅผ ์์ฑํ๊ณ ๋น๋๋ฅผ ๋๋ฆฐ ํ ๋น๋๋ ํ์ผ์ ํ๊ฒ ์์น๋ฅผ /src/main/resources/static ์ผ๋ก ์ง์ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
// build.gradle ์ค์ ์ถ๊ฐ
plugins {
id 'com.moowork.node' version '1.3.1'
}
node {
workDir = file("./frontend")
npmWorkDir = file("./frontend")
nodeModulesDir = file("./frontend")
}
task setUp (type: NpmTask) {
description = "Install Node.js packages"
args = ['install']
inputs.files file('package.json')
outputs.files file('node_modules')
}
task buildFrontEnd (type: NpmTask, dependsOn: setUp) {
description = "Build vue.js"
args = ['run', 'build']
}
processResources.dependsOn 'buildFrontEnd'
// vue.config.js ํ์ผ์ ์ค์ ์ถ๊ฐ
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
....(์ค๋ต)
transpileDependencies: true,
outputDir: "../src/main/resources/static", // ๋น๋ ํ๊ฒ ๋๋ ํ ๋ฆฌ
devServer: {
proxy: {
// ๋ก์ปฌ ์๋ฒ ํ
์คํธ ์ ์ด์ฉ
'/api': {
// '/api' ๋ก ๋ค์ด์ค๋ฉด ํฌํธ 8081(์คํ๋ง ์๋ฒ)๋ก proxy ์ฒ๋ฆฌ
target: 'http://localhost:8081',
changeOrigin: true // cross origin ํ์ฉ
}
}
}
....(์ค๋ต)
})
// WebConfig.java
@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 requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
: new ClassPathResource("/static/index.html");
}
});
....(์ค๋ต)
}
์ฌ๊ธฐ๊น์ง ์ฑ๊ณต์ ์ผ๋ก ์ค์ ์ด ๋์ จ๋ค๋ฉด ์๋์ ๊ฐ์ ์์น์ ๋น๋๋ ํ์ผ์ด ์์ฑ์ด ๋ฉ๋๋ค.
์ถ๊ฐ๋ก, ํ์ ํ๊ณ ๊ณ์ค ๋๋ ์๋์ ๊ฐ์ด ๊น ์ด๊ทธ๋ ธ์ด ํ์ผ์ ํ๋ ๋ง๋ค์ด์ ์ค์ ํด ์ฃผ์ ์ผ ๋ค๋ฅธ ์์ ์๋ค๊ณผ์ ์ถฉ๋์ ์๋ฐฉํ์ค ์ ์์ต๋๋ค.
// .gitignore ์ ์๋์ ๋๋ ํฐ๋ฆฌ๋ Git Commit ์์ธ ์ฒ๋ฆฌ
....(์ค๋ต)
/frontend/node_modules
/src/main/resources/static
....(์ค๋ต)
๋ง์น๋ฉฐ..
ํ๋ฒ ์ค์ ํ ๋ด์ฉ์ ๋ค์ ์คํ ์ ๋ฐ์๊ฐ๋ฉด์ ๊ธฐ๋กํ๊ณ ์ถ์์ง๋ง... ์ด ๊ท์ฐฎ์์ ์ด๊ฒจ๋ด์ง ๋ชปํ์ต๋๋ค. ๋ค์์ ํฌ์คํ ํ ๋๋ ์ฉ๊ธฐ ๋ด์ ์๋ฃ์กฐ์ฌ๋ถํฐ ์ค์ , ๊ทธ๋ฆฌ๊ณ ๊ฐ๋ฐ๊น์ง ์งํํ๋ ๋ด์ฉ์ผ๋ก ๋ค์ ๋์์ค๊ฒ ์ต๋๋ค.
์ ํฌ์คํ ์ ๋ณด์๊ณ ์ค์ ์ ํ์๋ค๊ฐ ๋งํ๋ ๋ถ๋ถ์ด ์์ผ์๋ค๋ฉด ๊ฐ์ด ๊ณ ๋ฏผํด ๋ณผ ์ ์๋๋ก ๋๊ธ์ ๋จ๊ฒจ์ฃผ์ธ์. ๋์์ด ๋์ค ์๋...? ์์ ๊ฒ๋๋ค ๐๐
์์คํ ๊ณต๊ฐ ๊ฐ์ฌํฉ๋๋ค. ๐