13 |
14 |
15 |
27 |
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Spring + Vue.js
2 |
3 | 스프링과 뷰가 어떻게 한 프로젝트 안에서 동작할 수 있을지 잡아본 구조이다. (꼭 Vue가 아니더라도, 어떠한 Frontend 라도 관계없다)
4 |
5 | 
6 |
7 | `Spring`과 `Vue.js`를 한 프로젝트 안에서 개발하는 간단한 예제 프로젝트!
8 |
9 | 이지만, 사실 떨어져 있어도 상관없을 만큼 관계가 없다. 그러니 개발은 각각 `Spring`, `Vue.js` 를 하면 된다.
10 |
11 | 중요한 것은 한 어플리케이션에서 효율적으로 개발하기 위한 환경일 것이다.
12 |
13 | ## Webpack
14 |
15 | > Vue Cli 를 통해 설치하면 더 안정적인 Webpack 설정을 얻을 수 있다. 하지만 지극히 Front End 위주적인 환경을 제공해주기도 하고,
16 | 이 프로젝트에서 필요한 양 이상의 설정이 더욱 복잡함을 가중시켜, 약 1년 전 설정했던 Webpack 설정을 그대로 사용했다.
17 |
18 | - 한 프로젝트에 있지만, 철저히 분리해야 복잡도를 줄일 수 있다. 그래서 src 하위에 front 라는 디렉토리를 만들고 그곳에서 모든 프론트엔드 관련 작업을 하도록 한다. (폴더 구조는 선호하는 구조로 잡으면 된다.)
19 | - 빌드 시 불필요한 파일(번들되기 전 파일, 미사용 이미지 등)이 같이 패키징 되는 더러운 상황과, 이를 방지하기 위해 빌드 전 resource directory 에 불필요한 파일을 매번 삭제해야하는 불필요한 행위를 줄여줄 수 있다.
20 |
21 | - Webpack 의 bundle 최종 경로가, Spring 의 resource static path 를 향하도록 한다.
22 | - 최종 bundle 된 파일을 다시 copy 하는 불필요한 행위를 줄여줄 수 있다.
23 | - webpack devServer 환경과 함께 사용하면, 스프링에서는 동일한 로직으로 운영, 개발 등 환경을 운영할 수 있다. (Backend Template 에서 js, css 등의 파일명을 교체하거나 경로를 조작하는 불필요한 행위를 줄여줄 수 있다.)
24 |
25 | ```js
26 | entry: {
27 | app: path.resolve(__dirname, 'src/front/main.js')
28 | },
29 | output: {
30 | path: path.resolve(__dirname, 'src/main/resources/static')
31 | ...
32 | }
33 | ```
34 |
35 | ## 개발
36 |
37 | 두번 실행하는게 귀찮지만, 비교적 간단하다.
38 |
39 | 1. Spring WebApplication 을 구동한다.
40 |
41 | 2. 터미널에서 `npm run start` 를 실행하여 webpack devServer 를 구동한다.
42 |
43 | - 이 프로젝트의 `package.json` 에는 webpack 을 구동하는 script 를 선언해두었다. `start` 명령도 그 중 하나이다.
44 | - start : webpack devServer 구동
45 | - dev : 개발 환경 webpack - 압축, 난독화되지 않은 번들 파일 생성.
46 | - build : 운영 환경 webpack - 압축, 난독화된 번들 파일 생성.
47 |
48 | 3. spring devtools 와 webpack devServer 환경에서 양쪽을 오가며 실시간으로 개발을 한다.
49 |
50 | ## 빌드
51 |
52 | 배포 또한 매우 단순하다.
53 |
54 | gradle 에서 npm 명령을 사용할 수 있는 플로그인을 사용하여, build 환경의 webpack 을 실행시켜주는 Task 를 아래와 같이 간단히 만들 수 있다.
55 |
56 | 그리고 build 과정 중 resources 를 생성하는 순서 앞에 끼어넣어준다.
57 |
58 | webpack의 bundle이 자동으로 `Spring` 의 `resources path` 하위에 파일을 넣어주므로, 추가적인 것은 필요없다.
59 |
60 |
61 | ```
62 | task webpack(type: NpmTask, dependsOn: 'npmInstall') {
63 | args = ['run', 'build']
64 | }
65 |
66 | processResources.dependsOn 'webpack'
67 | ```
68 |
69 | 아무 특별한 것 없이 `gradle` 의 `build task` 를 실행하면 된다.
70 |
71 | 그러면 build 된 jar 파일 안에 static 파일들이 잘 옮겨진 것을 볼 수 있다.
72 |
73 | 
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
4 | const webpackMerge = require('webpack-merge');
5 |
6 | const config = {
7 | entry: {
8 | app: path.resolve(__dirname, 'src/front/main.js')
9 | },
10 | output: {
11 | path: path.resolve(__dirname, 'src/main/resources/static'),
12 | filename: 'js/[name].js'
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.vue$/,
18 | loader: 'vue-loader',
19 | options: {
20 | extractCSS: true
21 | }
22 | },
23 | {
24 | test: /\.js$/,
25 | exclude: /node_modules/,
26 | use: [{
27 | loader: 'babel-loader',
28 | options: {
29 | presets: [
30 | ['es2015', {modules: false}]
31 | ]
32 | }
33 | }]
34 | },
35 | {
36 | test: /\.css$/,
37 | loader: ExtractTextPlugin.extract({
38 | fallback: "style-loader",
39 | use: "css-loader",
40 | })
41 | },
42 | {
43 | test: /\.(jpe?g|png|gif|svg)$/,
44 | loader: 'url-loader',
45 | options: {
46 | publicPath : '/static/',
47 | name : 'images/[name].[ext]',
48 | limit : 1
49 | }
50 | },
51 | {
52 | test: /\.ico$/,
53 | loader: 'file-loader',
54 | options: {
55 | publicPath : '/static/',
56 | name : 'icons/[name].[ext]',
57 | limit : 1
58 | }
59 | },
60 | {
61 | test: /\.(eot|svg|ttf|woff|woff2)$/,
62 | loader: 'url-loader',
63 | options: {
64 | publicPath : '/static/',
65 | name : 'fonts/[name].[ext]',
66 | limit : 1
67 | }
68 | }
69 | ]
70 | },
71 | plugins : [
72 | new webpack.ProvidePlugin({
73 | _ : "lodash"
74 | }),
75 | new ExtractTextPlugin('css/[name].css')
76 | ]
77 | };
78 |
79 | module.exports = function(env) {
80 | return webpackMerge(config, require(`./webpack.${env}.js`));
81 | };
82 |
--------------------------------------------------------------------------------
/src/front/template/MainHeader.vue:
--------------------------------------------------------------------------------
1 |
2 |