2023년 3월 22일

코딩 - Serverless 을 위한 Spring Boot 기반 응용프로그램 개발하기 : Part 1

◼︎ 환경 

  • Model : MacBook Pro (14-inch, 2021)
  • CPU : Apple M1 Pro
  • MENORY : 16GB
  • DISK : 512 GB SSD
  • OS : macOS 13.2.1 (22D68)
  • TOOLS : Visual Studio Code


왜 Spring Boot 인가 ?

Spring Boot 는 개발자에게 여러 가지 이점을 제공하는 강력한  프레임워크이다. 아래는 몇 가지 주요 이점이다.

  1. 더 빠른 개발: Spring Boot는 애플리케이션을 빠르고 쉽게 빌드하고 배포할 수 있는 방법을 제공. 사전 구성된 템플릿 세트와 다양한 라이브러리가 있어 개발 속도 향상.
  2. 간소화된 구성: Spring Boot는 작성해야 하는 상용구 코드의 양을 줄여주는 간소화된 구성 메커니즘을 제공.
  3. 자동 구성: Spring Boot의 자동 구성 기능은 프로젝트에 추가된 종속성을 기반으로 애플리케이션을 자동으로 구성. 수동 구성이 필요하지 않으므로 오류 발생 가능성이 줄어들고 시간이 절약.
  4. 프로덕션 준비 완료: Spring Boot 는 상태 확인, 메트릭 및 모니터링과 같이 애플리케이션을 프로덕션에 바로 사용할 수 있도록 하는 데 필요한 기능을 제공.
  5. 임베드 가능한 서버: Spring Boot 는 별도의 서버에 애플리케이션을 배포할 필요가 없는 임베디드 서버를 포함되어 배포가 더 쉽고 빠릅니다.
  6. 다른 Spring 프로젝트와의 손쉬운 통합: Spring Boot는 Spring Data 및 Spring Security와 같은 다른 Spring 프로젝트와 원활하게 통합되므로 복잡한 애플리케이션을 더 쉽게 빌드할 수 있음.
  7. 대규모 커뮤니티 지원: Spring Boot 는  대규모 개발자 커뮤니티 보유. 이를 통해 프레임워크가 지속적으로 개선되고 발전.

전반적으로 Spring Boot는 여러 가지 장점을 제공하는 강력한 프레임워크로서 현대적이고 확장 가능하며 강력한 애플리케이션을 구축하는 데 이상적인 선택이 될 수 있다. 

이미 Spring 을 사용하는 경우 Spring Boot 로 변경할 이유가 있을까?
Spring 을 기반으로하는 애플리케이션을 더 간단하고 빠르게 개발하고 배포할 수 있는 방법을 찾고 있다면 Spring Boot가 좋은 선택이 될 수 있다.  그러나 이미 구성 프로세스에 익숙하고 Spring Boot 가 제공하는 추가 기능이 필요하지 않다면 전환할 필요가 없을 수도 있다.

개발 도구 준비하기 

Visual Studio Code 환경에서 Spring Boot 개발을 위해 ⑴ VS Code 를 설치하고 ⑵ Spring Boot Extension Pack 확장팩을 설치한다.
 

Install the Spring Boot Extension Pack




어떤 버전의 Spring Boot 를 사용할 것인가 ?

Spring Boot 는 버전에 따라 호환되는 자바 버전에 차이가 있다. 이런 이유에서 사용(희망)하는 자바 버전을 고려하여 Spring Boot 버전을 선택해야한다.

2022 자바 생태계 현황 보고서 (https://newrelic.com/resources/report/2022-state-of-java-ecosystem)에 따르면 현재 48% 이상의 애플리케이션이 프로덕션 환경에서 Java 11을 사용하고 있으며(2020년 11.11%에서 증가), Java 8이 근소한 차이로 2위를 차지하며 46.45%의 애플리케이션이 프로덕션 환경에서 이 버전을 사용하고 있다.


운영 환경의 LTS 버전과 비교했을 때 비-LTS Java 버전에 대한 사용률은 극히 낮았으며, 비-LTS Java 버전을 사용하는 애플리케이션의 비율은 2.7%에 불과했다. 


최신 Spring Boot 버전은 3.x 이며 (https://www.marcobehler.com/guides/spring-and-spring-boot-versions) 최소 17 이상의 자바 버전을 요구하고 있다.  참여 프로젝트에서 주로 Spring 5.x 를 사용하고 있었고 운영 배포를 고려하여 가장 많이 사용하고 있는 자바 11 버전을 지원하는 Spring Boot  는 2.x  버전을 선택했다.


Spring Boot 프로젝트 만들기

요즘 개발 도구는 VS Code 를 주력으로 사용하고 있어 고민없이 VS Code 에서 새로운 Spring Boot 프로젝트 생성하는 것만 고려했다. VS Code 에서 Spring Boot 프로젝트 생성은 ① Control + Shift + P 키를 입력하여 명령 팔레트 창을 실행 Spring Initializer 을 선택하여 프로젝트를 생성한다. 또는 ② EXPLORER 에서 Create Java Project 버튼을 클릭하고 Spring Boot 을 선택한다. (참고: https://spring.io/quickstart )

① Control + Shift + P > pring Initializer : Create a Gradle Project .. 선택

 EXPLORER 에서 Create Java Project 버튼을 클릭하여 프로젝트 생성
 




Spring Boot 프로젝트 생성은 아래와 같은 옵션을 선택했다.  지금까지 자바 프로젝트는 Maven 을 사용했었는데 이번에는 Gradle 을 선택했다. 

  • Project : Gradle - Groovy
  • Language : Java
  • Spring Boot : 2.7.9
  • Packaging : Jar
  • Java : 11

프로젝트 생성시에 스타터(Starters) 의존성을 세팅하는 과정이 있는데 Spring Boot 의 우수성 중 하나가 스타터을 기반으로하는 편리한 의존성에 관리에 있는것 같다.  기존 Spring 개발 환경에서는 많은 시간을 투자하여 의존성을 관리해야 했었는데, 이러한 문제를 해결하기 위하여 도입된 Spring Boot 스타터는  약 50개 이상의 다양한 사전에 정의된 스타터들이 제공되고 있다.  (참고 : Starters) Starter 사용의 장점은 아래와 같다. 

  1. 개발자의 의존성 구성 시간이 줄어들어 생산성이 향상
  2. 추가해야 할 종속성 수가 줄어들기 때문에 POM 관리가 더 쉬워짐.
  3. 테스트를 거쳐 프로덕션에 사용할 수 있고 지원되는 종속성 구성.
  4. 종속 요소의 이름과 버전을 기억할 필요가 없음.

스타터는 아래와 같이 dependecies 항목에 추가하여 사용할 수 있다. 단지 이것으로 필요한 관련 된 모든 라이브러리들의 의존성이 해결된다. 
 
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'


DataSource 추가하기 

데이터접속을 위한 DataSource 설정이 조금 다른 부분이 있는데 기존 Spring 방식을 사용할 수도 있고 Spring Boot 에 제공하는 기본 설정을 사용할 수 도 있다. 가능한 Spring Boot 의 기능을 최대한 사용하려는 목적이 있어 Spring Boot 의 기본 설정(application.properties)을 사용하여 설정하였다. 설정은 가독성을 고려하여 추가설정없이 지원되는 yml 형식을 사용하였다. 리소스 위치는 config 폴더 이하에 저장하여 사용하였다. ( 기본적으로 Spring Boot 는 자동으로 기본 설정 파일을 로드하는 규칙이 있는데 리소스의 루트 또는 config 폴더를 검색하여 로드하도록 되어있다.)
 
Spring Boot 는 다양한 Connection Pool 기술은 제공하고 있는데 HikariCP 을 권장하고 있다.  HikariCP 성능 팁 설정(MySQL) 을 참고하여 다음과 같이 설정하였다. (설정을 위한 예시가 없어 조금 시간이 소요되었다.) 

➜ 편리하게도 spring-boot-starter-jdbc 또는 spring-boot-starter-data-jpa “starters” 추가하면 HikariCP 의존성도 자동으로 추가된다. *-data-jpa starter 를 사용하면 디폴트로 Hibernate  기반 JPA 을 사용할 수 있도록 관련 빈들이 자동으로 구성 및 생성된다.

---
logging:
level:
root: "info"
com.zaxxer.hikari: "debug"
org.springframework.jdbc.core: "trace"
---
spring:
datasource:
url: "jdbc:mysql://xxx.xxx.xxx.xx:3306/xxx"
username: "*****"
password: "*****"
hikari:
data-source-properties:
cachePrepStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
useLocalSessionState: true
rewriteBatchedStatements: true
cacheResultSetMetadata: true
cacheServerConfiguration: true
elideSetAutoCommits: true
maintainTimeStats: false
view raw applicaiton.yml hosted with ❤ by GitHub

JDBC Programming

Spring Boot JDBC는 (*-data-jdbc starter 을 사용한 경우) 시작 중에 DataSource, JdbcTemplate 및 NamedParameterJdbcTemplate와 같은 데이터베이스 관련 빈이 구성 및 생성되며, 이를 사용하려면 원하는 빈을 @Autowired 하고 사용하면 된다. ( 코드 참고 : https://mkyong.com/spring-boot/spring-boot-jdbc-examples/) 

package com.mkyong.repository;
import com.mkyong.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
@Repository
public class JdbcBookRepository implements BookRepository {
// Spring Boot will create and configure DataSource and JdbcTemplate
// To use it, just @Autowired
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int count() {
return jdbcTemplate
.queryForObject("select count(*) from books", Integer.class);
}
@Override
public int save(Book book) {
return jdbcTemplate.update(
"insert into books (name, price) values(?,?)",
book.getName(), book.getPrice());
}
@Override
public int update(Book book) {
return jdbcTemplate.update(
"update books set price = ? where id = ?",
book.getPrice(), book.getId());
}
@Override
public int deleteById(Long id) {
return jdbcTemplate.update(
"delete books where id = ?",
id);
}
@Override
public List<Book> findAll() {
return jdbcTemplate.query(
"select * from books",
(rs, rowNum) ->
new Book(
rs.getLong("id"),
rs.getString("name"),
rs.getBigDecimal("price")
)
);
}
// jdbcTemplate.queryForObject, populates a single object
@Override
public Optional<Book> findById(Long id) {
return jdbcTemplate.queryForObject(
"select * from books where id = ?",
new Object[]{id},
(rs, rowNum) ->
Optional.of(new Book(
rs.getLong("id"),
rs.getString("name"),
rs.getBigDecimal("price")
))
);
}
@Override
public List<Book> findByNameAndPrice(String name, BigDecimal price) {
return jdbcTemplate.query(
"select * from books where name like ? and price <= ?",
new Object[]{"%" + name + "%", price},
(rs, rowNum) ->
new Book(
rs.getLong("id"),
rs.getString("name"),
rs.getBigDecimal("price")
)
);
}
@Override
public String getNameById(Long id) {
return jdbcTemplate.queryForObject(
"select name from books where id = ?",
new Object[]{id},
String.class
);
}
}


Logging

Spring Boot는  로깅을 구현하는 log4j, logback 등과 같은 로깅 프레임워크에 대한 추상화 기능 제공을 위하여 SLF4J (Simple Logging Facade for Java) 를 사용하고 있다. Lombok (어노테이션 기반의 유틸리티 라이브러리) 을 사용하면 좀더 간단하게 @Slf4j을 클래스 상단에 정의하는 것 만으로 바로 로깅 기능을 할 수 있다.


다음은 SLF4J 기반의 로깅 예이다. Lombok 을 사용하는 경우 Logger 선언 없이 @Slf4j 어노테이션만 선언하여 바로 log 변수로 사용할 수 있다.

public class LoggingTest {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoggingTest.class);
public void test(){
log.info("logging ....... {}", "hello");
}
}
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LombokLoggingTest {
public void test(){
log.info("logging ......... {}", "hello");
}
}

Lombok 는 dependecies 항목에 아래와 같이 추가하여 이용한다. 로깅 이외에도 다양한 어노테이션들을 제공하고 있고 잘 사용하면 코딩 더욱 쉬워질 수 있다.

dependencies {
implementation 'org.projectlombok:lombok'
compileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}

plugins {
id 'java'
id 'org.springframework.boot' version '2.7.9'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'xxxx'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
bootJar.enabled = true
repositories {
mavenCentral()
mavenLocal()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
runtimeOnly 'com.mysql:mysql-connector-j'
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'net.sf.ehcache:ehcache:2.10.9.2'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-acl:5.7.7'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
view raw build.gradle hosted with ❤ by GitHub
 

참고자료

  1. Building an Application with Spring Boot
  2. Migrating from Spring to Spring Boot
  3. Spring Boot in Visual Studio Code
  4. Spring Quickstart Guide
  5. Hikari Configuration for MySQL in Spring Boot 2
  6. Project Lombok
  7. What is Project Lombok and how to use it with Spring Boot?

댓글 없음:

댓글 쓰기