본문 바로가기
개발일지

[개발일지] Spring Framework ➡ Spring Boot 마이그레이션 하기

by 지지 2023. 4. 16.

1년 전쯤 만들었던 Spring Framework 기반의 개인 프로젝트를 Spring Boot로 마이그레이션을 해보기로 했다.

마이그레이션이 끝나면 다시 이 프로젝트를 살려서 더 기능을 붙이고 배포까지 해보는 게 목표다!

 

마이그레이션을 진행할 프로젝트(RunningGo)의 개발환경은 아래와 같다. (OS는 Window를 사용한다.)

[Java11, Spring Framework 5.0, Maven 3.8, MyBatis, MySQL 8, JSP, JQuery]

 

마이그레이션이 진행될 환경과 버전은 아래와 같다.

[Java11, Spring Boot 2.7, Gradle, MyBatis, MySQL8]

 

MyBatis가 아닌 JPA로 바꿀까 고민했지만, 아직은 JPA를 배우고 있는 입장이라 일단 마이그레이션을 먼저 진행한 후 JPA가 어느 정도 준비됐다 싶을 때 다시 바꿔주기로 결정했다.

그리고 프론트 같은 경우는 거의 신경을 안쓸 것 같다.(그래서 위에 적지 않았다.)

즉, API 개발에 중점들 두고 포스트맨으로 호출을 할 생각이기 때문에 API 개발이 어느 정도 완성 됐다 싶을 때 Thymeleaf와 Javascript를 사용해 프론트 작업을 해줄 것 같다.(사실 하기 싫은데... 배포하려면 그래두 이쁘게 하는 게 나으니까....)

순서는 내 맘대로 하고 싶은 대로 진행했기 때문에 정답이 아니다. (정답이라는 게 없는 것 같다.)

 

마이그레이션 진행 순서

1. SpringFramework SpringBoot

2. Maven Gradle

3. (추후) JSP → Thymeleaf

 

사실 난 Gradle을 잘 모르는 상태로 Maven → Gradle을 제일 먼저 진행했었다.

'그냥 설정 파일만 바뀌는 거 아니야?', '강의 들으면서 Gradle 몇 번 써봤는데 별거 없던데?'라는 생각으로ㅜㅜ

build.gradle에서 의존성을 framework에서 boot로 변경해줘야 하는데 어떻게 해야 할지 전혀 감도 안 잡히고 계속 에러가 나서 결국 git revert로 다시 maven으로 돌려 처음부터 진행을 했다ㅋㅋ쿠ㅜㅜㅜ

나처럼 Gradle을 잘 다룰 줄 모르고, Maven이 더 익숙하다면 Spring Framework -> Spring Boot 먼저 진행해 보자!


Spring Framework  →  Spring Boot

스프링 부트는 스프링 프레임워크를 대체하는 목적이 아닌, 스프링 프레임워크를 좀 더 빠르고 쉽게 쓰기 위해 만들어진 것이기 때문에 대부분의 변경은 설정(Configration) 부분에서 일어난다.

즉, 컨트롤러나 서비스와 같은 로직은 거의 변경하지 않아도 된다!

 

1. 먼저 pom.xml에 spring-boot-starter-parent을 추가해 준다.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.10</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

스프링 부트는 starter의존성을 제공하는데, 이 의존성으로 기능에 필요한 모든 라이브러리를 가져올 수 있다.

이 의존성으로 모든 라이브러리에 대한 버전을 명시할 필요가 없다! starter가 의존성들을 모두 관리한다고 알고 있자.

 

2. 기존의 springframework-version이 명시된 코드를 지워준다.

여기까지 일단 이미지로 보자면, 빨간 박스가 새로 추가해 준 설정이고, 파란 박스가 지워준() 설정이다.

packaging과 name은 음... 다른 스프링 부트 프로젝트들에 다 추가가 되어 있길래 일단 나도 추가했다. 아마 배포 관련된 설정인 것 같다.

그리고 java.version설정도 자세히 보면 빨간 박스가 있는데, 원래 java-version이었는데 대시를 점으로 바꿔주었다.

 

3. spring-boot-starter-web을 추가해 준다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

여기까지 코드를 작성했으면 스프링 부트로 실행이 가능하다. (에러가 나겠지만..ㅎㅎ)

바로 확인해 보자.

 

4. Application.java 파일을 만들어준다.

3번까지 디펜던시를 추가해 줬으면 스프링 부트 관련 어노테이션을 사용할 수 있게 된다. (@SpringBootApplication 등)

하지만 실행을 시키면 일단 에러가 날 거라고 확신한다..!..! 정확히 어떤 에러라고는 단정 짓지 못하지만, 설정 관련한 에러들이 날 것이다.

 

일단 나는 다른 것은 건드리지 않고, pom.xml으로 다시 가서 나머지 디펜던시들을 변경해 주었다.

 

5. 스프링 프레임워크 관련 디펜던시들을 스프링 부트 디펜던시로 변경해 주기(pom.xml)

이 과정에서 처음엔 하나하나 찾아가며 추가해주려고 했지만 굳이 그럴 필요가 있나 싶어서 https://start.spring.io/의 도움을 받았다.

pom.xml을 보면서 기존에 적용되어 있는 디펜던시와 같은 디펜던시를 추가해 준 후 스프링 부트 프로젝트를 하나 만들었다. 그리고 이렇게 만들어진 스프링 부트 프로젝트의 pom.xml을 참고해 새롭게 디펜던시를 추가해 주었다!

<!-- 스프링 프레임워크 -> 스프링부트 마이그레이션 : 필요한 dependency 추가 중-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.0</version>
</dependency>

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

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

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>

기존의 디펜던시는 혹시 몰라 완전히 지우진 않고 주석처리를 해놓았다.

기존 디펜던시 주석처리!


에러 해결하기!!

위의 과정을 진행하여 설정을 대충(?) 맞춰준 후 중간점검 느낌으로 다시 Application.java를 실행해 보면 아마 에러가 날 것이다.

아직 바꿔주지 않은 설정파일들이 산더미이기 때문이다... (*. xml파일들...)

그리고 아마 이제부터 에러와의 싸움이 시작될 것이다!!! (큰 프로젝트일수록 더 많은 설정 에러가 날 것 같다..ㅜㅜ)

프로젝트의 규모나 적용했던 설정이 다 다르기에 나타나는 에러가 다 다를 것이며, 순서를 적는 것이 의미가 없을 것 같다.

그렇기에 이제부턴 내가 겪은 에러들과 해결과정을 작성해 볼 것이다.

 

1. Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

음! 듣던 중 반가운 에러다(?)

위 에러는 Database에 연결할 때 필요한 정보가 없기 때문에 발생한다. 아까 디펜던시를 추가할 때 MySQL을 선택해 주었기 때문에 스프링 부트가 Database를 연결해 달라고 내는 에러인 것이다.

기존 root-context.xml에 있는 db설정은 더 이상 스프링이 쳐다보지 않는 것 같다.

src/main/java/resources/ 경로에 application.properties (또는 application.yml)를 추가하고, db정보를 입력해 주면 된다.

application.properties

 

2. Consider defining a bean of type 'org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' 

in your configuration.

나는 비밀번호 암호화를 위해 Spring Security를 사용했었는데, 해당에러는 passwordEncoder 가 빈으로 등록되지 않아서 생기는 문제이다.

기존 스프링 프레임워크에서는 이 설정을 spring-secutiry.xml 파일에 해줬었는데, 이 파일 역시 스프링이 쳐다보지 않는 모양이다.

이제는 자바 설정파일로 따로 만들어서 @Bean으로 등록해주어야 한다. (그런데 사실 이 과정은 스프링 프레임워크를 사용했어도 설정 가능했던 부분이긴 하다. 문득 궁금한 점 : 스트링 부트는 xml에 설정해 준 bean을 아예 쳐다보지 않는 것인가??? 공부해야겠다!!)

 

기존 spring-security.xml 파일

이 파일 역시 삭제는 하지 않고 일단 전체 주석처리만 해주었다.

↓새로 작성한 SecurityConfig.java

@Configration을 달아주는 것을 잊지말자!

 

3. 예상치 못한 상황 1 - Spring Security 적용 후 프로젝트 실행 시 Spring Security가 기본으로 제공해 주는 로그인 페이지가 로드됨

스프링 프레임워크로 시큐리티를 적용했을 땐 이런 상황 없이 내가 설정해 준 view로 잘 이동을 했는데 부트로 변경하니 시큐리티가 제공하는 기본 로그인 페이지가 로드되었다.

당황스러웠다.....

해결 방법은 생각보다 간단했다.

스프링 시큐리티의 기본 설정을 disable해주는 설정이 따로 존재했다.

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().disable()
                .csrf().disable()
                .formLogin().disable()
                .headers().frameOptions().disable();
    }

    //passwordEncoder
    @Bean
    public BCryptPasswordEncoder encodePassword() {
        return new BCryptPasswordEncoder();
    }

}

처음 작성했던 SecurityConfig.java 파일에 disable 해주겠다는 코드만 추가로 작성해 주면 된다.

마이그레이션이 끝나면 구현해 볼 기능 중에 스프링 시큐리티도 예정되어 있었는데, 왠지 한층 친해진 기분이다!(?)

 

4. 예상치 못한 상황 2 - 매핑을 잘해줘도 jsp뷰가 아닌 Whitelable Error Page가 로드됨. (사실 예상했음..)

(이번 프로젝트에서 jsp가 아닌 Thymeleaf를 사용할 것이지만, 지금은 스프링 프레임워크에서 스프링 부트로 변경하는 과정'만' 진행할 것이기 때문에 일단 프레임워크를 사용할 때와 비슷하게 환경을 맞춘 후 모든 설정이 다 끝나면 그때 다시 뷰템플릿을 바꿔주는 작업을 할 것이다. 그땐 이 설정들이 다 필요 없어지게 된다...ㅎㅎ)

 

스프링 부트의 공식 문서를 보면 뷰템플릿으로 jsp를 사용하는 것을 권장하지 않고, 다른  템플릿 엔진을 추천하고 있다.

또한 spring-boot-starter-web에 내장된 톰캣은 jsp엔진을 포함하지 않는다.

 

그렇다면 스프링 부트에서 jsp를 사용하려면 어떻게 해야 할까?

먼저, pom.xml에 jsp를 사용할 수 있게 해주는 디펜던시를 추가해주어야 한다. 아래와 같이 디펜던시를 추가해 주자.

<dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-jasper</artifactId>
</dependency>

<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>jstl</artifactId>
</dependency>

아마 나처럼 spring.io의 도움을 받았다면 jasper는 이미 추가가 되어 있을 것이다. 사실 jstl은 없어도 스프링이 jsp를 찾는데 문제가 없다. 그러나 jsp의 라이브러리인 jstl을 사용하려면 해당 디펜던시도 추가해주어야 한다.

 

그리고 나서 위에서 DB를 설정할 때 사용했던 .properties파일에 jsp의 경로를 알려주어야 한다.

스프링 프레임워크를 사용할 땐 servlet-context.xml에 설정해 주었던 ViewResolver 부분에 해당한다.

위의 경로를 예를 들면, jsp 파일이 위치해야 하는 구조는 webapp/WEB-INF/views/ 가 된다.

jsp, 정적리소스 패키지 구조

나는 프레임워크를 사용했을 때 이미 이렇게 폴더구조를 만들어 놓아서 딱히 폴더를 만들거나, 파일을 옮기는 과정은 없었다.

이렇게 jsp 설정을 마치고!! 다시 한번 프로젝트를 실행시켜 보면!!!

솔직히 화면 열릴 때 쫌 감동했음...ㅋㅋㅋㅋㅋㅋㅋ

짜잔!! 잘 실행이 되는 것을 볼 수 있다!!!!

 

하지만 여기서 끝이 아니다... 스프링 프레임워크 시절(?) 구현해 놓았던 기능들이 잘 동작하는지 확인해봐야 한다.

이쯤 되면 어떤 에러가 터질지 예상이 간다. 아직 기존 xml에서 벗어나지 못한 설정들에서 에러가 터질 거라고 예상할 수 있다.

 

5. org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.

PersistenceException: ### Error querying database.  Cause: java.lang.IllegalArgumentException: 

Mapped Statements collection does not contain value for com.runninggo.toy.dao.MemberMapper.idCheck

 

회원가입 폼에서 중복확인 버튼을 누르자마자 에러가 났다. 에러 내용은 아래와 같으며 MyBatis와 관련된 에러다.

기존 스프링 프레임워크에서는 mybatis-confg.xml 파일을 아래와 같이 설정해 줬다.

조구마한 내 프로젝트,,, 작고 귀여워,,,

이제는 이 설정 또한 과감히 버리고(?) properties에 다시 설정을 해주어야 한다!!!

type-aliases-package는 mybatis-confg.xml파일에 설정해 주었던 것처럼 DB의 조회 결과 데이터를 담을 클래스 package를 지정해 주는 것이다. (즉, resultType)

mapper-locations는 실제 쿼리(mapper파일)가 위치한 곳의 경로를 작성해 주면 된다.

(나의 경우 기존 작성된 설정은 일단 주석처리를 해놓고 나중에 한 번에 삭제했다!)

 

이렇게 mybatis 설정까지 끝났으면 또 프로젝트를 실행시켜 보자.

얄루!

에러 없이 원하는 결과가 나오는 것을 확인할 수 있다!!!!!!!


여기까지 Spring Framework에서 Spring Boot로 마이그레이션을 진행해 봤다.

위에 다 적진 못했지만, xml로 작성했던 여러 설정들을 application.properties파일로 옮겨줬다.

예를 들면 메일 관련 설정이라던지, message 관련 설정 등등!!! (모든 코드는 깃헙에 있습니다!)

하다 보면 또 놓친 설정들에서 에러가 많이 날 테지만.. 그건 차차 잡아가는 걸로!!!

난 항상 처음 하는 것에 겁을 많이 먹는 편인데, 한 번 딱 해보면 해냈다는 성취감은 이로 말할 수가 없다!

 

그동안 죽어있는(?) RunningGo 프로젝트를 어떻게 다시 살려볼까 고민을 많이 했었다.

음.. 도저히 프레임워크로는 더 이상 개발을 진행할 수 없을 것 같아(설정도 힘들고.. 사실 1년 전에 진행했던 프로젝트라 까먹은 부분도 있다..) 스프링 부트로 마이그레이션 해야지 해야지 생각만 하고 드디어 마이그레이션을 진행하게 되었다.

이 과정도 그냥 spring.io에서 스프링 부트 프로젝트 하나 만들어서 소스 코드를 복붙하고 설정만 다시 잡아주는 게 편하고 빠르지 않을까 하는 생각을 많이 했다.

하지만 언제 또 하나하나 설정을 바꿔가며 마이그레이션을 진행할 기회가 있을까 싶어 딱 한 번만 진행해 보자는 생각으로 이 과정들을 지나게 되었다.

 

생각보다 어렵지 않았다!! 프레임워크 설정이 머리에서 서서히 잊혀져 갈 때쯤 이렇게 한 번씩 상기시켜주는 것도 좋은 것 같다.(?)

다음 포스팅은 Maven에서 Gradle로의 마이그레이션 과정이다. 글이 엉망인 것 같지만 차차 다듬어가기로!

 

https://github.com/jeejee1106/RunningGoV2

 

GitHub - jeejee1106/RunningGoV2: 마이그레이션 진행중인 RunningGoV2입니다.

마이그레이션 진행중인 RunningGoV2입니다. Contribute to jeejee1106/RunningGoV2 development by creating an account on GitHub.

github.com

반응형

댓글