Spring Boot 2.x에서 Velocity 사용하기 - 1부

Ted·2021년 7월 19일
1

JAVA

목록 보기
1/2

계기

연초에 부서내 서비스들을 모두 쿠버네티스 위에서 운영하도록 이관하는 프로젝트를 맡게 되었다.

프로젝트 시작 전에 아래와 같은 이유로 Spring Boot 기반이 아닌 서비스 하나를 골라 Spring Boot 2.3 버전으로 포팅해보면서 소요되는 시간을 측정해보고자 했다.

  • 부서내 서비스별로 사용중인 기술에 대한 버전이 천차만별로 파편화 되어있어 비슷한 코드를 복붙할 때도 에러가 나는 등 (e.g. @RestController 어노테이션 부재 등) 불필요한 리소스 낭비가 발생햇음.
  • Spring Boot 2.3 이상부터 GracefulShutdown, Readiness/Liveness Probe 관련 기능 추가로 쿠버네티스 환경구성이 더 간단해 짐(Spring Boot 2.3.0 available now).
  • Spring Boot 2.3으로 전환하면 리소스 낭비도 줄이고, 쿠버네티스 이관도 더 수월할 것으로 예상됨.

맞딱뜨린 문제점

가장 처음 호기롭게 골랐던 서비스가 무려 Spring Framework 3.1 (Spring Boot 아니고 Spring Framwork 맞다) 기반으로 운영되고 있던 서비스였다.

약 10 년 전에 release 된 Spring Framework 버전을 사용하고 있던 서비스 답게 Spring Boot로의 전환에는 많은 애로사항이 있었는데, 그 중 하나가 template engine으로 velocity를 사용하고 있다는 점 이었다.

Spring Boot 기준으로 1.5 버전부터 velocity 관련 코드가 제거되어 사용할 수 없게 되었는데 (Spring Boot 1.5 Release Notes)
이는 velocity가 2010년 이후로 오랜 기간 업데이트가 없었기 때문이다.

Spring Boot 1.5 버전이 릴리즈되고 얼마 지나지 않아 7 년만에 velocity가 2.0 버전이 나오며 다시 업데이트 되기 시작했다;;(Velocity Release History)

업무 간의 의존성 분리

Velocity 대용으로 Freemarker를 써볼까 하고 수정해야 하는 파일이 몇 개나 있는지 확인중
지금 진행 중인 서비스만 velocity를 사용하는 것도 아닌데, Spring Boot 버전을 올리려고 시도할 때마다 template engine을 바꾸는 것도 함께 진행하는 게 맞나라는 생각이 들었다.

하나의 서비스당 십수개 - 수십개의 view 파일이 있는데, Spring Boot로 전환을 진행하려고 template engine을 바꾸는 건 배보다 배꼽이 더 큰 상황인 것 같았기 때문이다.

차라리 Spring Boot 전환Velocity 사용 가능에 의존하고 있는 지금의 상황에서 의존성을 끊어 주는게 더 나은 방식일 것 같다는 생각이 들었다.

이러한 이유로 Spring Boot 2.x 환경에서 dependency만 추가하면 velocity를 사용할 수 있는 모듈을 만들어 보기로 했다.

필요한 클래스 파악

아래서 진행한 코드는 GitHub에서 확인할 수 있다.

먼저 Spring Boot에서 AutoConfiguration으로 velocity 사용을 활성화 시킬 때 어떤 클래스들이 사용되는지 파악해야 했다.
다행히 Spring Boot 1.4까지는 velocity를 지원 했으니, 해당 버전에서 쓰인 클래스들을 옮겨서 작업을 진행하면 될 것 같았다.
그런데 Spring Boot Starter Velocity github를 찾아보려 하니 제거된지 시간이 좀 지난 프로젝트여서 그런지 검색이 잘 되지 않았다.
그래서 차선책으로 Spring Boot 1.4 기반의 데모 프로젝트에 Spring Boot Starter Velocity를 추가한 뒤 인텔리제이 디컴파일러의 도움을 받아 VelocityAutoConfiguration에 필요한 클래스들을 하나씩 확보해 가는 방향으로 진행하기로 했다.


위와 같이 검색해보니 spring-boot-autoconfigure dependency 안에서 velocityAutoConfiguration 클래스를 찾을 수 있었다.
확인해 보니 잘 디컴파일 되어 온전한 클래스의 내용을 확인할 수 있었고, 해당 클래스 내용을 새로 모듈로 만들 프로젝트로 복사 해 보았다.

와! 빨간색! 💀

복사하니 예상대로 import 하지 못해 컴파일 에러를 뿜어내는 여러 클래스들을 만날 수 있었다.
IDE에서 빨간색은 개발자에게 불편함을 주기때문에 매우 고통스러웠지만, 이 빨간색을 지워나가면 자연스럽게 velocity 모듈에 필요한 클래스들을 확보할 수 있을 터였다.

그래서 하나씩 클래스들을 찾아가며 옮기기 시작했고, velocity 모듈에 필요한 패키지의 위치는 아래와 같았다.

  • spring-boot-autoconfigure (org.springframework.boot.autoconfigure.velocity)
  • spring-boot (org.springframework.boot.web.servlet.view.velocity)
  • spring-context-support (org.springframework.ui.velocity)
  • spring-webmvc (org.springframework.web.servlet.view.velocity)

여러 의존성에 퍼져있던 클래스들을 하나씩 모아서 모듈을 채워나가기 시작했다.

(Optional) velocity property 바꾸기

Spring Boot에서 velocity를 사용을 하되, application.yaml에 설정은 spring.velocity. 가 아니라 좀 다르게 하고 싶을 수도 있을 것 같았다.
뭐.. legacy.velocity.
으로 바꿔서 설정을 해둔뒤 한쪽 영역에 모아두면 추후 velocity를 걷어내게 될 때 설정 파일에서 지우기 편하다거나 하는 이유 등이 있을 것 같았다.
그럴때는 위의 VelocityProperties의 @ConfigurationProperties prefix를 원하는 값으로 바꿔주자.(Configuration Properties In Spring Boot)

spring.factories ?

import에서 문제를 일으키던 클래스들을 모두 옮기고 나니 위와 같이 빨간 줄 없는 아름다운 상태로 만들 수 있었다.
이 상태로도 mvn clean install 명령을 해보면 문제없이 빌드됨을 알 수 있지만, 그 결과물을 Spring Boot에 dependency로 추가해 보면 velocity autoconfiguration이 작동하지 않음을 알 수 있다.
그 이유는 Spring Boot가 autoConfigure 작업을 할 때 VelocityAutoConfiguration 클래스를 함께 읽어야하는지 모르기 때문이다.
VelocityAutoConfiguration을 함께 읽어야 한다고 알려주고 싶을 때 쓰는 파일이 바로 spring.factories 파일이다.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.enemfk777.springboot2velocity.autoconfigure.VelocityAutoConfiguration

resources/META-INF/ 아래 spring.factories 파일을 만든 후 위와 같이 입력해 주면 Spring이 autoConfigure 작업을 할 때 VelocityAutoConfiguration 클래스도 함께 읽어서 velocity 관련 autoConfiguration을 진행하게 된다.(참고 : 최범균님 블로그 - 스프링 부트 커스텀 AutoConfiguration)

사용해보기

여기까지 진행 후 mvn clean install 후 Spring Boot 2.4 버전에 방금 만든 maven artifact를 추가하고 간단한 velocity 사용 화면을 만든후 표시 되나 확인을 했다.

<!-- pom.xml의 dependencies 부분 -->
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.enemfk777</groupId>
            <artifactId>spring-boot2-velocity</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

pom.xml에 추가한 spring-boot2-velocity artifact

//테스트용 컨트롤러
@Controller
public class TestController {
  @GetMapping("/test")
  public ModelAndView test() {
    List<String> testList = new ArrayList<>();
    testList.add("test1");
    testList.add("test2");
    testList.add("test3");
    testList.add("test4");
    ModelAndView modelAndView = new ModelAndView("main");
    modelAndView.addObject("tests", testList);
    return modelAndView;
  }
}

velocity에서 쓸 object를 담아서 반환하는 controller

<h2><span class="glyphicon glyphicon-map-marker" aria-hidden="true"></span> 테스트</h2>
<p>*테스트</p>

<div class="well">
    <p>
    <table class="table" style="background-color: #fff;">
        <tr>
            <th>이름</th>
        </tr>

        #foreach($row in $tests)
            <tr>
                <td>$!row</td>
            </tr>
        #end
    </table>
    </p>
</div>


<script>

</script>

main.vm 파일. 전달받은 List의 요소를 반복적으로 그려줄 때 velocity를 사용하고 있다.

위에 나열한 코드들로 어플리케이션을 실행해 본 결과
applycation.yaml 설정 작성이나 추가 Bean 등록 클래스 작성 없이 velocity가 잘 작동하는 걸 확인할 수 있었다.

전환을 진행중이던 프로젝트에 적용

demo 프로젝트에서 잘 작동함을 확인하고 Spring Boot로 전환을 진행중이던 프로젝트에 dependency를 추가해 적용을 시켜 보았으나 예상과달리 작동하지 않았다.
그 이유는 해당 프로젝트에서는 VelocityLayoutViewResolver를 사용중이었기 때문인데,
해당 문제의 해결 과정은 2부에서 계속 작성하도록 하겠다.

profile
Aiming for being a reliable developer

0개의 댓글