gradle-node-plugin 사용해보기

Dierslair·2022년 9월 12일
0

스프링

목록 보기
4/5

프로젝트 구조

Gradle 프로젝트

해당 포스트에서 사용해 볼 프로젝트 구조는 다음과 같습니다.

일반적인 WebMVC 프로젝트에서 자바스크립트를 사용하여 웹 페이지를 구성할 때 번거로운 부분은 대부분의 브라우저에서 자바스크립트 실행을 보장하기 위해 개발자가 직접 ES5 코드를 작성해야 한다는 점입니다.

물론 미리 컴파일된 babel/standalone 라이브러리를 사용하여 text/babel 타입의 스크립트를 사용하면 당장에 동작에는 문제가 없으나, 실제로 권장하는 방법은 아닙니다.

해당 라이브러리를 사용하면 스크립트를 작성하면,

<script type="text/babel" data-presets="env,stage-3">
  const someFunc = async () => {
  	const { myData } = (await axios('..')).data;
    return myData;
  }
</script>

런타임에 babel이 해당 구문을 해석하여 ES5 스크립트로 변환해서 실행시켜주기는 하나, 바벨의 폴리필이 전역 오염을 일으키는 등의 부작용이 있습니다.

따라서 ES6 이상의 구문을 사용하기 위해서는 별도의 프론트엔드 스크립트를 사전에 빌드하여 포함할 수 있도록 노드 프로젝트를 구성할 필요가 있습니다.

node 프로젝트 구성

스프링 프로젝트 내부의 src/main/resources/frontend 디렉터리에서 다음 node 프로젝트를 구성합니다.

$ npm init -y
$ npm install --save-dev webpack webpack-cli
$ npm install --save-dev @babel/core @babel/preset-env @babel/plugin-transform-runtime babel-loader
$ npm install --save @babel/runtime @babel/runtime-corejs3

ES6+ 구문을 ES5 로 컴파일하기 위한 바벨 설정은 다음과 같습니다.

// .babelrc.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: [
        '> 0.25%',
        'not dead',
        'ie >= 9',
      ],
    }],
  ],
  plugins: [
    ['@babel/plugin-transform-runtime', {
      absoluteRuntime: false,
      corejs: 3,
      helpers: true,
      regenerator: true,
    }],
  ],
};

webpack 설정은 다음과 같습니다.

// webpack.config.js
const path = require('path');
const EnvironmentPlugin = require('webpack/lib/EnvironmentPlugin');

const prefix = './src/page';

const output = '../static/js';

module.exports = ({ NODE_ENV }) => ({
  mode: NODE_ENV,
  target: ['web', 'es5'],
  entry: {
    'index': `${prefix}/index.js`,
  },
  output: {
    path: path.resolve(__dirname, output),
    filename: '[name].js',
  },
  resolve: {
    alias: {
      '~': path.resolve(__dirname, 'src'),
    },
    extensions: ['.js'],
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /[\\/]node_modules[\\/]/,
        loader: 'babel-loader',
      },
    ],
  },
  plugins: [
    new EnvironmentPlugin({
      NODE_ENV,
    }),
  ],
  devtool: false,
});

package.json에는 취향껏 빌드 스크립트를 추가합니다.

## package.json
{
  ..
  "scripts": {
    "build": "webpack --env NODE_ENV=development",
    "build:prod": "webpack --env NODE_ENV=production"
  },
  ..
}

테스트를 위해 src/main/resources/frontend/src/index.js 위치에 다음 스크립트를 작성합니다.

// index.js
const asyncFunc = () => new Promise((resolve) => {
	setTimeout(() => resolve(), 500);
});

(async () => {
	console.debug('task started..');
    await asyncFunc();
  	console.debug('task finished.');
})();

정상적으로 빌드되는지 확인합니다.

$ npm run build
---
..
modules by path ./node_modules/core-js-pure/ 164 KiB 297 modules
modules by path ./node_modules/@babel/runtime-corejs3/ 18.6 KiB
  modules by path ./node_modules/@babel/runtime-corejs3/core-js-stable/ 1.04 KiB 16 modules
  modules by path ./node_modules/@babel/runtime-corejs3/core-js/ 736 bytes 11 modules
  modules by path ./node_modules/@babel/runtime-corejs3/helpers/ 16.4 KiB 4 modules
  ./node_modules/@babel/runtime-corejs3/regenerator/index.js 448 bytes [built] [code generated]
modules by path ./src/ 201 KiB
...
webpack 5.74.0 compiled successfully in 1350 ms

ES5 에서 지원하지 않는 폴리필이 주입되는 것을 확인할 수 있습니다.

Gradle 빌드시 자바스크립트 빌드

스프링 프로젝트가 빌드될 때, 자동으로 위 노드 프로젝트를 빌드할 수 있도록 플러그인이 존재합니다.

gradle-node-plugin

해당 플러그인을 스프링부트 프로젝트의 build.gradle.kts 의 플러그인 클로저에 추가합니다.

// build.gradle.kts
plugins {
    ..
    id("com.github.node-gradle.node") version "3.4.0"
}

설정부분과 task 를 추가하면 코틀린 코드 빌드시 프론트엔드 프로젝트를 먼저 빌드할 수 있습니다.

// build.gradle.kts
import com.github.gradle.node.npm.task.NpmTask

..
node {
    // 프론트엔트 프로젝트의 node, npm 버전과 같게 합니다.
    val frontendNodeVersion = "16.17.0"
    val frontendNpmVersion = "8.15.0"

    val userHome = System.getProperty("user.home")
    // node 바이너리가 저장될 위치를 지정합니다.
    val nodeDir = file("${userHome}/.node/$frontendNodeVersion")
    // npm 바이너리 저장될 위치를 지정합니다.
    val npmDir = file("${userHome}/.npm/$frontendNpmVersion")
    // 빌드할 프로젝트의 위치를 지정합니다.
    val projectDir = file("${project.projectDir}/src/main/resources/frontend")

    version.set(frontendNodeVersion)
    npmVersion.set(frontendNpmVersion)
    download.set(true)
    workDir.set(nodeDir)
    npmWorkDir.set(npmDir)
    nodeProjectDir.set(projectDir)
}

..
// 'npm run build:prod' 를 실행하는 task를 추가합니다.
val frontendTask = tasks.register<NpmTask>("frontendTask") {
    args.set(listOf("run", "build:prod"))
}

tasks.withType<KotlinCompile> {
    // 코틀린 빌드 작업은 frontend 빌드 작업이 선행되어야 합니다.
    dependsOn(frontendTask)
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = java.sourceCompatibility.toString()
    }
}

이렇게 구성하면 ES6+ 구문의 자바스크립트를 자유롭게 사용할 수 있습니다.

주의사항

node_modules 가 깃에 업로드되지 않게 하기 위해 .gitignore 파일에 node_modules 디렉터리를 추가합니다.

profile
Java/Kotlin Backend Developer

0개의 댓글