도서

(2020.12.16) 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 최신 코드로 변경하기

향로 (기억보단 기록을) 2020. 12. 16. 22:43
반응형

작년 11월 말에 스프링 부트와 AWS로 혼자 구현하는 웹 서비스를 출판 하였습니다.

Spring Boot가 2.1 -> 2.4로, IntelliJ IDEA가 2019 -> 2020으로 오면서 너무 많은 변화가 있다보니, 집필할 때와 비교해 실습에서 지속적으로 문제를 제보 받았습니다.

  • 실무에서 사용할때는 쉽게 버전을 올릴순 없지만, 책을 통한 학습단계에서는 웬만해선 최신 버전을 사용하신다는 점
  • 실습 도구들의 버전업이 너무 빠르게 이루어졌다는 점

2가지가 생각보다 크게 다가와서 한번은 가장 최신 버전으로 코드를 정리해야겠다는 생각에 글을 쓰게 되었습니다.

intro

물론 이렇게 작성된 글도 2021년에 Spring Boot 2.5, IntelliJ 2021로 버전업 되면서 크게 개편이 되면 또 이슈가 되리라 봅니다.
그래서 도대체 어느 버전까지 개편을 지원해야하나? 라는 고민이 드는데요.
일단은 해볼수 있을때까지는 해보겠습니다!

이 글은 기본적으로 스프링 부트와 AWS로 혼자 구현하는 웹 서비스를 기반으로 합니다.
해당 저서에서 사용되는 라이브러리와 도구의 버전을 현재 (2020.12.16) 가장 최신 버전으로 변환하는 방법을 소개 합니다.

변경될 도구들의 버전은 다음과 같습니다.

  • Spring Boot 2.4.1
  • Gradle 6.7.1
  • IntelliJ IDEA 2020.3
  • Junit5

Mac을 중심으로 실습하다보니, 환경마다 또 다를수가 있습니다.
이 글을 보시는 분들은 안되시는게 있으시면 댓글 남겨주시면 확인후에 답글 남기겠습니다.
다행히 책이 아닌 인터넷 글이다보니, 실시간으로 본문 수정이 가능해서 언제든 본문은 수정될 수 있습니다.

해당 실습 코드는 Github에 있으니 참고해서 진행해주시면 됩니다.

0. 기존에 구성했다면

이미 기존에 작성된 코드가 있으시다면 아래와 같이 Gradle의 버전을 올립니다.

신규로 진행하신다면 프로젝트 생성된 뒤, Gradle Wrapper의 버전을 확인 후 진행합니다.

기존 프로젝트가 있는 디렉토리로 이동 하신뒤, 아래 명령어로 업데이트를 진행합니다.

gradlew wrapper --gradle-version 6.7.1

Mac/Linux에서는 ./gradlew로, Windows에서는 gradlew로 실행하시면 됩니다.

gradle1

BUILD SUCCESSFUL이 되셨다면 아래와 같이 Gradle Wrapper 파일의 버전이 업데이트되었는지 확인합니다.

gradle2

6.7.1로 나온다면 정상적으로 성공이니 다음으로 넘어갑니다.

1. build.gradle

프로젝트의 Gradle 버전을 6.7.1로 올렸으니 build.gradle의 설정들도 Gradle6에 맞게 변경합니다.

아래와 같이 전체 코드를 변경합니다.

plugins { // (1)
    id 'org.springframework.boot' version '2.4.1' // RELEASE 삭제
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group 'com.jojoldu.book'
version '1.0.4-SNAPSHOT-'+new Date().format("yyyyMMddHHmmss")
sourceCompatibility = 1.8   

repositories {
    mavenCentral()
    jcenter()
}

// for Junit 5
test { // (2)
    useJUnitPlatform()
}

dependencies {
    //(3)
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.springframework.boot:spring-boot-starter-mustache')

    // lombok
    implementation('org.projectlombok:lombok')
    annotationProcessor('org.projectlombok:lombok')
    testImplementation('org.projectlombok:lombok')
    testAnnotationProcessor('org.projectlombok:lombok')

    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
    implementation("org.mariadb.jdbc:mariadb-java-client")
    implementation('com.h2database:h2')

    implementation('org.springframework.boot:spring-boot-starter-oauth2-client') // 권한 관련
    implementation('org.springframework.session:spring-session-jdbc') // 권한 관련

    testImplementation('org.springframework.boot:spring-boot-starter-test')
    testImplementation("org.springframework.security:spring-security-test") // 권한 관련
}

주의하실점은 // 권한 관련으로 주석친 부분은 Spring Security 챕터를 진행하신분들만 넣으셔야 합니다.
즉, 챕터5까지 아직 안가신분들은 아래 코드는 빼고, 나머지만 넣으셔야 합니다.

implementation('org.springframework.boot:spring-boot-starter-oauth2-client')
implementation('org.springframework.session:spring-session-jdbc')
testImplementation("org.springframework.security:spring-security-test")

(1) plugins{..}

  • Gradle 5부터는 Gradle Plugin 사용법에 변경이 있습니다.
  • 같은 플러그인을 Gradle 버전에 맞춰 선언 방법이 달라지다보니 Gradle Plugin 사이트에서 2가지 사용법을 모두 명시하고 있으니 버전에 맞춰 사용하시면 됩니다.
gradle3

(2) test

  • Junit5를 사용하기 위해서는 필수로 선언되어야 합니다.

(3) implementation, testImplementation

  • Gradle 6가 되면서 compile, testCompileSoft Deprecate 되었습니다.
  • 그 대신 implementation, testImplementation 가 추가되었습니다.
  • 이에 대해서는 기존에 작성된 글을 참고해보시길 추천드립니다.

build.gradle 설정이 다되셨다면 전체 테스트를 한번 수행해봅니다.

test0

그럼 아래와 같이 Test가 실패가 나는데요.

test1

이제 이 부분을 수정해보겠습니다.

2. Junit

깨진 테스트들의 이유는 Junit4의 코드를 5로 변환해야하기 때문인데요.

프로젝트 전체에서 해당 코드들을 전체 교체하겠습니다.
단축키는 다음과 같습니다.

Mac

  • CMD + Shift + R

Windows/Linux

  • Ctrl + Shift + R

자 그럼 하나씩 변경해보겠습니다.

2-1. @Test

먼저 @Test를 변경하는데, Junit4와 5는 패키지 위치만 다를뿐 이라서 import 코드만 교체하겠습니다.

test2

위에서 언급한 단축키를 통해 검색하신뒤, ReplaceAll을 클릭해 변환하시면 됩니다.

단축키가 잘 안되신다면 직접 코드를 열어서 수정하셔도 됩니다.

2-2 @RunWith

두번째는 @RunWith 입니다.
해당 어노테이션의 경우 Junit5가 되면서 @ExtendWith로 변경되었습니다.
그래서 어노테이션과 패키지위치 2개다 변경이 필요합니다.

test3

어노테이션

as-is

@RunWith

to-be

@ExtendWith

import패키지

as-is

org.junit.runner.RunWith

to-be

org.junit.jupiter.api.extension.ExtendWith

2-3. SpringRunner

3번째는 SpringRunner 입니다.
앞서 @RunWith와 마찬가지로 SpringRunner 역시 SpringExtension 로 변경되었기 때문에 클래스명과 패키지 위치 2개다 변경이 필요합니다.

test4

클래스

as-is

SpringRunner

to-be

SpringExtension

import패키지

as-is

org.springframework.test.context.junit4.SpringRunner

to-be

org.springframework.test.context.junit.jupiter.SpringExtension

2-4. @After

4번째는 테스트 메소드가 끝날때마다 수행되는 @After입니다.
마찬가지로 @After가 역시 @AfterEach 로 변경되었기 때문에 어노테이션과 패키지 위치 2개다 변경이 필요합니다.

어노테이션

test5_2

as-is

@After

to-be

@AfterEach

import패키지

test5

as-is

org.junit.After

to-be

org.junit.jupiter.api.AfterEach

2-5. @Before

마지막으로 @Before 입니다.
After와 마찬가지로 @Before가 역시 @BeforeEach 로 변경되었기 때문에 어노테이션과 패키지 위치 2개다 변경이 필요합니다.

as-is

@Before

to-be

@BeforeEach
test6_2

import패키지

test6

as-is

org.junit.Before

to-be

org.junit.jupiter.api.BeforeEach

2-6. 주의

테스트를 수행하는데 아래와 같이 에러가 발생한다면

No tests found for given includes
test-fail1

build.gradle에 아래 옵션이 빠진건 아닌지 다시 build.gradle 옵션을 확인해봅니다.

test {
    useJUnitPlatform()
}

Junit 관련된 모든 설정이 끝나셨으면 다시 테스트를 수행해봅니다.
그럼 아래와 같이 SQL 쿼리가 정상적으로 수행이 되지 않아 몇개의 테스트가 실패 하는 것을 확인할 수 있습니다.

jpa1

자 이부분을 수정해보겠습니다.

3. JPA

위 테스트의 실패 로그를 보시면 create table 쿼리가 H2에서 정상적으로 수행이 안되었기 때문임을 알 수 있는데요.

이는 Spring Boot 2.1.10 부터 MySQL 문법 지정 방식에 변화가 있었기 때문인데요.

그래서 DB 설정들의 변경이 필요합니다.

현재 프로젝트는 다음과 같이 application.properties를 가지고 있는데요.

jpa2

local 용 파일들과 real용 파일들을 구분해서 수정하겠습니다.

local용 파일이 결국 h2에 사용되기 때문입니다.

3-1. application.properties

application.properties 코드는 2개 파일을 수정해야합니다.

  • src/main/resources/application.properties
  • src/test/resources/application.properties

둘다 로컬에서 직접실행할때테스트로 실행할때 사용되는 파일들이라 동일하게 수정합니다.

as-is

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

to-be

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
spring.jpa.properties.hibernate.dialect.storage_engine=innodb
spring.datasource.hikari.jdbc-url=jdbc:h2:mem:testdb;MODE=MYSQL
spring.datasource.hikari.username=sa
  • spring.datasource.hikari.jdbc-url
    • real-db를 사용할 경우 override 됩니다.
  • spring.datasource.hikari.username
    • real-db를 사용할 경우 override 됩니다.

이렇게 변경하신뒤, 아래와 같이 localhost:8080/h2-console로 접속하셔서 정상적으로 h2 웹 콘솔로 접속이 되는지 확인합니다.

h2

3-2. application-real-db.properties

real용 DB 설정 파일인 application-real-db.properties는 다음과 같이 설정path의 변경이 있습니다.

  • ex) spring.datasource.url -> spring.datasource.hikari.jdbc-url

아래와 같이 설정 코드들을 변경합니다.

as-is

spring.jpa.hibernate.ddl-auto=none

spring.datasource.url=jdbc:mariadb://rds주소:포트명(기본은 3306)/database명
spring.datasource.username=db계정
spring.datasource.password=db계정 비밀번호
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver

to-be

spring.jpa.hibernate.ddl-auto=none
spring.jpa.show_sql=false

spring.datasource.hikari.jdbc-url=jdbc:mariadb://rds주소:포트명(기본은 3306)/database명
spring.datasource.hikari.username=db계정
spring.datasource.hikari.password=db계정 비밀번호
spring.datasource.hikari.driver-class-name=org.mariadb.jdbc.Driver

여기까지 다 되신분들은 다시 한번 전체 테스트를 수행해봅니다.

test-success

테스트 코드가 있으니 버전업 이후 기능에 대해서 검증이 편하신게 느껴지신다면 제 책의 의도가 50% 이상은 성공했다고 생각합니다.

4. spring.profiles.include

Spring Boot가 2.4가 되면서 spring.profiles.include 옵션에 변경이 있었습니다.

그래서 기존처럼 각 properties(yml) 에 spring.profiles.include=oauth 등으로 구성하신 분들은 정상작동을 안하실텐데요.

include1include2

(local-real로 실행시 real-oauth도 함께 실행되어야 하나, local-real만 실행)

앞으로는 특정 프로필 파일에서 설정하지 않고, Profile Group을 만들어서 관리하라고 합니다.

spring.profiles.group.<group>=dev, auth

여기서 <group> 이 앞으로는 실행될 profile을 대표하는 이름이 됩니다.

즉, -Dspring.profiles.active=local-group 로 실행하면 spring.profiles.group.local-group 으로 지정된 profile들이 실행된다는 의미입니다.

자 그래서 이 Profile Group을 application.properties에 일괄 등록해서 사용하겠습니다.

application.properties

spring.profiles.group.local-real=local-real, oauth
spring.profiles.group.real=real, real-db, oauth
spring.profiles.group.real1=real1, real-db, oauth
spring.profiles.group.real2=real2, real-db, oauth
include3

이제 다시 시작해보면?

아래와 같이 기존처럼 정상적으로 2개의 profile이 같이 실행되는 것을 볼 수 있습니다.

include4

마무리

끝까지 따라오시느라 고생하셨습니다.

여기서 주의할 점은, 이렇게 작성된 글 역시 시간이 흐르면 정상 작동하지 않을 것이란 점입니다.
모든 개발 도구들은 버전업을 합니다.

특히나 요즘은 분기(3개월)마다 거의 주요 버전들을 올리곤 하는데요.
언제든 안될 수 있기 때문에 여러분이 익히셔야하는것들은, 왜 안되는지 로그를 보고 검색하는 습관입니다.

처음 실습을 진행하셔서 성공하셨다면, 해당 코드들을 가지고 지속적으로 라이브러리와 도구를 버전업하면서 왜 버전업하면 안되는지 고쳐나가는 것도 좋은 공부 방법이라 생각합니다.

이번 포스팅이 그런 연습에 도움이 되시길 바래봅니다.

반응형