본문 바로가기

Backend/Spring

[Spring/SpringMVC] spring 빌드 시 npm 라이브러리 추가하기

안녕하세요. 이번 포스팅에서는 spring 빌드 시에 npm 라이브러리를 추가하는 방법에 대해 포스팅하도록 하겠습니다.

 

저희가 흔히 타임리프 혹은 jsp와 같은 템플릿 엔진을 사용하게되면, javascript라이브러리를 링크를 통해 첨가하는 경우가 많습니다. 

 

가령

html head태그에

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>

 

위와 같이 https:~~ 링크를 통해 해당 라이브러리를 추가하는 경우를 볼 수 있습니다. 특별한 경우가 아니면 위 방식도 괜찮은 방법입니다.

 

하지만 생각을 조금 바꾸어보면, 링크주소가 변경되거나 추가 버전이 릴리즈 되어 구 버전을 더 이상 활용할 수 없게 될 수도 있습니다. 또한 네트워크 문제에 따라 서비스가 불안정해질 수도 있습니다. 만약에 위 라이브러리들을 사전에 받아놓고, 저희가 빌드 시에 추가할 수 있다면 위 방식보다 훨씬 좋은 방법일 것입니다.

 

결과적으로 저희가 첨부하고 싶은 방식은 아래와 같습니다.

<head th:fragment="head">
  <meta charset="UTF-8">
  <title>StudyOlle</title>
  <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css" />
  <link rel="stylesheet" href="/node_modules/font-awesome/css/font-awesome.min.css" />
  <script src="/node_modules/jdenticon/dist/jdenticon.min.js"></script>
  <script src="/node_modules/jquery/dist/jquery.min.js"></script>
  <script src="/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>

  <style>
        .container {
            max-width: 100%;
        }
   </style>
</head>

 

node_modules라는 디렉토리를 빌드 시에 자동으로 생성하여 저희가 필요한 라이브러리들을 넣어놓고, 서비스 도중 끊기지 않고 사전에 받아놓은 라이브러리들을 사용하게 만드는 것이 이번 포스팅의 목적입니다.

 

src/main/resources/static 디렉토리로 이동하여 npm을 설치하여 줍니다. src/main/resources/static 디렉토리 이하는 정적 리소스를 제공합니다. 이 디렉토리에 package.json 파일을 활용하여 빌드 시 필요한 라이브러리들을 받고, 정적 리소스로 서비스에 제공하겠습니다.

npm init

npm install ... (ex bootstrap, jquery)

 

가장 중요한 것은 반드시 위 디렉토리 이하에 npm을 설치해 주셔야 합니다. npm init을 한 뒤, 필요한 라이브러리들을 설치해주시면 됩니다. 

 

설치가 끝났으면 package.json 파일이 생성된 것을 보실 수 있습니다.

 

{
  "name": "static",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "echo Building frontend",
    "check": "echo Checking frontend && npm run lint && npm run test",
    "clean": "echo Cleaning frontend",
    "lint": "echo Linting frontend"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bootstrap": "^4.4.1",
    "cropper": "^4.1.0",
    "font-awesome": "^4.7.0",
    "jdenticon": "^2.2.0",
    "jquery": "^3.4.1",
    "jquery-cropper": "^1.0.1"
  }
}

 

scripts부분은 수동으로 추가해주시면 됩니다.

 

이제 빌드 설정을 하셔야 합니다. build.gradle 파일로 이동하셔서 필요한 코드를 추가해줍니다.

 

buildscript{
    ext {
        springBootVersion = '2.5.2'
    }

    repositories {
        mavenCentral()
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath "com.github.node-gradle:gradle-node-plugin:3.1.0"
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: "com.github.node-gradle.node"

node {
    version = '11.15.0'
    download = true
    nodeModulesDir = file("${projectDir}/src/main/resources/static")
}

task copyFrontLib(type: Copy) {
    from "${projectDir}/src/main/resources/static"
    into "${projectDir}/build/resources/main/static/."
}

copyFrontLib.dependsOn npmInstall
compileJava.dependsOn copyFrontLib

group = 'com.StudyProject'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
   	...
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    
    ... (dependency 생략)
    testCompile('org.projectlombok:lombok')
    testAnnotationProcessor('org.projectlombok:lombok')
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
}

test {
    useJUnitPlatform()
}

 

주의깊게 보셔야 할 부분은 우선 classpath에 node-plugin을 추가하여 주시고, plugin에 com.github.node-gradle.node를 추가해 주셔야 합니다. 위 코드와 똑같이 작성하시면 됩니다.

 

node {
    version = '11.15.0'
    download = true
    nodeModulesDir = file("${projectDir}/src/main/resources/static")
}

이부분에서 version은 현재 사용하고 계신 npm 버전을 적어주시면 됩니다. nodeModulesDir경로를 만들어 주세요.

 

task copyFrontLib(type: Copy) {
    from "${projectDir}/src/main/resources/static"
    into "${projectDir}/build/resources/main/static/."
}

 

이 부분에서 이제 src/main/resources/static에 있는 정적 리소스들을 build시에 /build/resources/main/static/으로 옮겨 서비스에서 정적 리소스로 사용할 수 있게 copy task를 수행하여 줍니다.

 

만약 성공적으로 위 작업이 끝났다면, gradle build시에 

아래와 같이 node_modules라는 디렉토리가 생깁니다. 여기에 저희가 원하는 라이브러리들이 담겨있고, 빌드 시에 /build/resources/main/static/ 로 옮겨집니다.

 

*추가로 만약에 security를 적용한 프로젝트라면 security에 node_modules 디렉토리에 대한 권한을 풀어주셔야 합니다. 그렇지 않다면 위 디렉토리에 대한 권한이 없기 때문에 빌드 시에 라이브러리를 가져올 수 없습니다.

 

<*securityConfig>

package com.communityProject.config;

... import 생략

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final DataSource datasource;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/", "/sign-up", "/login", "...", ...).permitAll()
                .anyRequest().authenticated();

        http.formLogin()
                .loginPage("/login").permitAll();
        http.logout()
                .logoutSuccessUrl("/");
    }

    @Override
    public void configure(WebSecurity webSecurity) throws Exception {
        webSecurity.ignoring()
                .mvcMatchers("/node_modules/**")
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }
}

 

여기까지 진행이 되셨다면 이제 실제로 확인을 해보시면 됩니다.

 

 

저같은 경우는 bootstrap의 기본적인 css들을 사용하였는데 정상적으로 빌드 시에 추가된 것을 볼 수 있습니다.

 

추가로 가시적이지 않지만 정상적으로 라이브러리가 빌드된 것을 확인하시고 싶으시면 f12를 눌러 Network tab에서 정상적으로 라이브러리들이 가져와졌는지 확인해보시면 됩니다. 제 경우 정상적으로 작동합니다.

 

이번 포스팅을 마치도록 하겠습니다.

 

*저의 글에 대한 피드백이나 지적은 언제나 환영합니다.