Backend/Spring

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

멍분이 2021. 8. 5. 00:13

안녕하세요. 이번 포스팅에서는 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에서 정상적으로 라이브러리들이 가져와졌는지 확인해보시면 됩니다. 제 경우 정상적으로 작동합니다.

 

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

 

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