2025. 3. 13. 14:54ㆍSpring Microservice
🚀Spring Boot 기반 Licensing Service 프로젝트 분석
마이크로서비스 아키텍처에서 개별 서비스들은 독립적으로 구성 및 관리되어야 합니다. 이번 글에서는 Licensing Service 프로젝트의 pom.xml을 분석하며, Spring Cloud Config Server, PostgreSQL, Docker 빌드 등의 핵심 기능을 살펴보겠습니다.
📌 프로젝트 개요
항목 | 설명 |
프로젝트 명 | licensing-service |
버전 | 0.0.2-SNAPSHOT |
부모 프로젝트 | spring-boot-starter-parent (v3.4.3) |
JDK 버전 | Java 17 |
Spring Cloud 버전 | 2024.0.0 |
데이터베이스 | PostgreSQL |
컨테이너화 지원 | Docker 이미지 빌드 및 배포 (Spotify dockerfile-maven-plugin) |
🗂️ 파일 구성
📦 chapter05
├── 📂 configserver # Spring Cloud Config Server 프로젝트
│
├── 📂 licensing-service # 라이선스 관리 서비스 (Licensing Service)
│ ├── 📂 src
│ │ ├── 📂 main
│ │ │ ├── 📂 java/com/optimagrowth/license
│ │ │ │ ├── LicenseServiceApplication.java # Licensing Service 메인 클래스
│ │ │ │ ├── controller/ # REST 컨트롤러
│ │ │ │ ├── model/ # 엔티티 및 DTO
│ │ │ │ │ ├── ExampleProperties.java # 추가됨
│ │ │ │ ├── repository/ # JPA Repository
│ │ │ │ ├── service/ # 비즈니스 로직
│ │ │ │ ├── config/ # Spring Cloud Config 설정
│ │ │ ├── 📂 resources
│ │ │ │ ├── application.yml # Licensing Service 설정(미사용)
│ │ │ │ ├── bootstrap.yml # Config Server와 연결 설정
│ │ ├── 📂 test
│ ├── pom.xml # Maven 프로젝트 파일
│ ├── Dockerfile # Docker 이미지 빌드 파일
│ ├── README.md # 프로젝트 설명 파일
│
├── 📂 docker # Docker compose 관련 설정
|
├── pom.xml # 전체 프로젝트 Maven 파일
├── build-for-m1.sh # mac os를 위한 빌드 스크립트 파일
└── README.md # 프로젝트 전체 설명 파일
🏗 pom.xml 분석
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.optimagrowth</groupId>
<artifactId>licensing-service</artifactId>
<version>0.0.2-SNAPSHOT</version>
<name>License Service</name>
<description>Ostock Licensing Service</description>
<properties>
<java.version>17</java.version>
<docker.image.prefix>ostock</docker.image.prefix>
<!-- 아래 링크에서 스프링 부트와 스프링 클라우드의 버전 호환을 확인해야 한다 -->
<!-- https://spring.io/projects/spring-cloud -->
<spring-cloud.version>2024.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-bootstrap -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.4</version>
</dependency>
</dependencies>
<!-- 스프링 클라우드 BOM 정의 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- This plugin is used to create a docker image and publish the image to docker hub-->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<configuration>
<repository>${docker.image.prefix}/${project.artifactId}</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
<executions>
<execution>
<id>default</id>
<phase>install</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
🏗 📌 주요 구성 요소 분석
📂 1. 프로젝트 부모 설정
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
✅ spring-boot-starter-parent 을 상속받아 Spring Boot 프로젝트에서 기본 설정 제공
✅ Spring Boot의 BOM (Bill of Materials) 관리 로 버전 충돌 방지
🛠 2. 프로젝트 속성 (Properties)
<properties>
<java.version>17</java.version>
<docker.image.prefix>ostock</docker.image.prefix>
<spring-cloud.version>2024.0.0</spring-cloud.version>
</properties>
✅ Java 17 사용 (java.version=17)
✅ Docker 이미지 저장소 접두사 (docker.image.prefix=ostock)
✅ Spring Cloud 버전 지정 (spring-cloud.version=2024.0.0)
📦 3. 프로젝트 의존성 관리
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
✅ HATEOAS 지원 → REST API에서 링크 기반 탐색을 가능하게 함
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
✅ Spring Boot Web 지원 → REST API를 제공
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
✅ Spring Cloud Config 지원 → Config Server에서 설정을 가져옴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
✅ JPA + PostgreSQL 설정 → 데이터 저장을 위한 ORM 및 데이터베이스 드라이버 추가
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
✅ Lombok 지원 → Getter/Setter, 생성자 자동 생성
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
✅ Spring Boot Configuration Processor → @ConfigurationProperties 자동 완성 지원
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.4</version>
</dependency>
✅ Spring Cloud Bootstrap 설정 → Config Server에서 환경설정을 먼저 로드
📌 4. Spring Cloud BOM 관리
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
✅ Spring Cloud 라이브러리의 버전 관리를 BOM 방식으로 통합
✅ Spring Cloud 버전 충돌 방지
🏗 5. 빌드 플러그인 설정
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
✅ spring-boot-maven-plugin → Spring Boot 애플리케이션을 실행 가능한 JAR 파일로 패키징
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<configuration>
<repository>${docker.image.prefix}/${project.artifactId}</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
✅ Spotify의 dockerfile-maven-plugin 을 사용하여 Docker 이미지 빌드 및 배포
✅ JAR_FILE 변수를 통해 컨테이너 내에서 Spring Boot JAR 실행 가능
<executions>
<execution>
<id>default</id>
<phase>install</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
✅ mvn install 실행 시 자동으로 Docker 이미지 빌드 및 푸시
✅ 컨테이너 배포를 자동화
🏗 bootstrap.yml 분석
spring:
application:
name: licensing-service
profiles:
active: dev
config:
import: "configserver:http://configserver:8071/"
✅ 애플리케이션 이름 설정: licensing-service
✅ 활성 프로필 설정: dev
✅ Spring Cloud Config Server에서 설정 가져오기:
- configserver:http://configserver:8071/을 통해 configserver에서 설정을 불러옴
- Config Server가 실행되지 않으면 애플리케이션이 정상적으로 시작되지 않을 가능성이 있음
📜 Source Code 분석
📌 ExampleProperties 클래스 분석
📂 패키지: com.optimagrowth.license.model
📜 파일명: ExampleProperties.java
이 클래스는 Spring Boot의 프로퍼티 바인딩 및 동적 설정 갱신을 담당하는 구성(설정) 속성 클래스입니다.
Spring Cloud Config 및 Spring Boot의 @ConfigurationProperties를 활용하여 설정 값을 로드하고,
@RefreshScope를 사용하여 동적으로 값을 변경할 수 있도록 합니다.
🔍 클래스 구조 분석
package com.optimagrowth.license.model;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
@Component
@RefreshScope
@ConfigurationProperties(prefix = "example")
public class ExampleProperties {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
📂 1. 패키지 선언
package com.optimagrowth.license.model;
✅ 설명:
- 이 클래스는 model 패키지에 속하며, 애플리케이션 설정 값을 저장하는 모델 역할을 합니다.
📦 2. import 문
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
✅ 각 import 설명
- @ConfigurationProperties:
- application.yml 또는 application.properties에서 설정 값을 바인딩하는 데 사용.
- @RefreshScope:
- Spring Cloud Config에서 설정 값을 동적으로 변경 가능하도록 함.
- @Component:
- Spring이 빈(Bean)으로 등록할 수 있도록 지정.
📌 3. 클래스 선언
@Component
@RefreshScope
@ConfigurationProperties(prefix = "example")
public class ExampleProperties {
✅ 설명
- @Component:
- Spring 컨텍스트에서 관리되는 빈(Bean)으로 등록됨.
- @RefreshScope:
- Spring Cloud Config를 사용할 때 설정 변경을 반영할 수 있도록 함.
- /actuator/refresh 엔드포인트를 호출하면, 새로운 설정 값이 적용됨.
- @ConfigurationProperties(prefix = "example"):
- application.yml 또는 application.properties 파일에서 example.property 값을 읽어와 이 클래스의 필드에 자동으로 바인딩.
🔹 4. 속성(property) 필드 및 Getter/Setter
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
✅ 설명
- private String property;
- example.property 값을 저장하는 필드.
- getProperty() 및 setProperty(String property)
- Getter와 Setter를 제공하여 다른 클래스에서 값을 읽거나 변경할 수 있도록 함.
- Spring이 자동으로 값을 바인딩할 수 있도록 함.
📄 ExampleProperties 동작 방식
🔹 1. config 서버에서 아래 설정 값 로드
example:
property: "I AM XXX"
- example.property 값이 "I AM XXX"로 설정됨.
- ExampleProperties 클래스의 property 필드에 자동으로 바인딩됨.
🔹 2. /actuator/refresh 호출 시 설정 값 변경 가능
- Spring Cloud Config 서버에서 설정을 변경한 후 /actuator/refresh 엔드포인트를 호출하면 값이 즉시 업데이트됨.
- @RefreshScope가 없으면 설정 변경이 반영되지 않음.
📌 주요 기능 요약
✅ Spring Boot 프로퍼티 바인딩 → @ConfigurationProperties(prefix = "example")
✅ Spring Cloud Config 설정 갱신 가능 → @RefreshScope
✅ Spring 빈(Bean)으로 등록 → @Component
✅ 설정 값 로드 및 변경 가능 → application.yml 파일에서 값 설정
📌 이 클래스가 활용되는 예시 코드
🔹 ExampleProperties 값을 사용하는 Controller
package com.optimagrowth.license.controller;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import com.optimagrowth.license.model.ExampleProperties;
@RestController
@RequestMapping(value="v1/organization")
public class LicenseController {
private final ExampleProperties exampleProperties;
public LicenseController(ExampleProperties exampleProperties) {
this.exampleProperties = exampleProperties;
}
@GetMapping("/property")
public String getExampleProperty() {
return exampleProperties.getProperty();
}
// 생략...
}
✅ /config/property 엔드포인트를 호출하면 현재 설정된 example.property 값을 반환합니다.
🔹 ExampleProperties 클래스는 Spring Boot의 프로퍼티 바인딩과 Spring Cloud Config 설정 변경 기능을 결합한 구성 클래스입니다.
🔹 @ConfigurationProperties를 사용하여 application.yml에서 값을 자동으로 바인딩하고, @RefreshScope를 사용하여 설정 변경을 동적으로 적용할 수 있도록 구성되었습니다.
🔹 Spring Cloud Config를 활용하는 마이크로서비스 환경에서 설정 값을 중앙에서 관리하고 동적으로 반영할 때 유용하게 활용됩니다.
📌 LicenseController 클래스 수정
📂 패키지: com.optimagrowth.license.controller
📜 파일명: LicenseController.java
package com.optimagrowth.license.controller;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.optimagrowth.license.model.ExampleProperties;
import com.optimagrowth.license.model.License;
import com.optimagrowth.license.service.LicenseService;
@RestController
@RequestMapping(value="v1/organization")
public class LicenseController {
private final ExampleProperties exampleProperties;
@Autowired
private LicenseService licenseService;
public LicenseController(ExampleProperties exampleProperties) {
this.exampleProperties = exampleProperties;
}
@GetMapping("/property")
public String getExampleProperty() {
return exampleProperties.getProperty();
}
@GetMapping(value="/{organizationId}/{licenseId}")
public ResponseEntity<License> getLicense(
@PathVariable("organizationId") String organizationId,
@PathVariable("licenseId") String licenseId) {
License license = licenseService.getLicense(licenseId, organizationId);
license.add(
linkTo(methodOn(LicenseController.class).getLicense(organizationId, license.getLicenseId())).withSelfRel(),
linkTo(methodOn(LicenseController.class).createLicense(license)).withRel("createLicense"),
linkTo(methodOn(LicenseController.class).updateLicense(license)).withRel("updateLicense"),
linkTo(methodOn(LicenseController.class).deleteLicense(license.getLicenseId())).withRel("deleteLicense")
);
return ResponseEntity.ok(license);
}
@PutMapping
public ResponseEntity<License> updateLicense(@RequestBody License request) {
return ResponseEntity.ok(licenseService.updateLicense(request));
}
@PostMapping
public ResponseEntity<License> createLicense(@RequestBody License request) {
return ResponseEntity.ok(licenseService.createLicense(request));
}
@DeleteMapping(value="/{licenseId}")
public ResponseEntity<String> deleteLicense(@PathVariable("licenseId") String licenseId) {
return ResponseEntity.ok(licenseService.deleteLicense(licenseId));
}
}
📌 주요 수정 부분 요약
✅ @RequestMapping 어노테이션 vaule 속성 변경
✅ ExampleProperties 빈 필드 추가
✅ 컨스트럭터 추가
✅ 컨트롤러 메서드 수정
🚀 Licensing Service의 Dockerfile 구성 및 분석
📌 개요
Spring Boot 기반 Licensing Service 프로젝트의 Dockerfile을 분석하고, 단계별로 설명합니다. 이 Dockerfile은 멀티 스테이지 빌드(Multi-Stage Build)를 활용하여 효율적인 컨테이너 이미지를 생성하도록 구성되었습니다.
🛠️ Dockerfile 구성
🔹 Stage 1: 애플리케이션 빌드 및 패키징
먼저, Java 애플리케이션을 빌드하고 필요한 파일을 추출하는 첫 번째 빌드 스테이지를 설정합니다.
# 1단계: Java 런타임 환경이 포함된 베이스 이미지 사용
FROM openjdk:17 as build
# 유지보수자를 지정
LABEL maintainer="Illary Huaylupo <illaryhs@gmail.com>"
# 애플리케이션의 JAR 파일을 위한 변수 지정
ARG JAR_FILE=target/licensing-service-0.0.2-SNAPSHOT.jar
# JAR 파일을 컨테이너 내부로 복사
COPY ${JAR_FILE} app.jar
# JAR 파일 압축 해제
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf /app.jar)
✅ 1단계 주요 사항
- FROM openjdk:17 as build → OpenJDK 17을 기반 이미지로 사용하며, 이 스테이지를 build로 명명
- LABEL maintainer="Illary Huaylupo <illaryhs@gmail.com>" → 유지보수자 정보 추가
- ARG JAR_FILE → 빌드할 JAR 파일을 변수로 지정 (target/licensing-service-0.0.2-SNAPSHOT.jar)
- COPY ${JAR_FILE} app.jar → JAR 파일을 컨테이너 내부로 복사
- RUN mkdir -p target/dependency && (cd target/dependency; jar -xf /app.jar) → JAR 파일을 압축 해제하여 dependency 디렉토리에 저장
두 번째 스테이지에서는 실행에 필요한 파일만 복사하여 최적화된 컨테이너를 구성합니다.
# 2단계: 실행 환경을 위한 Java 런타임 이미지 사용
FROM openjdk:17
# /tmp 디렉토리를 볼륨으로 설정
VOLUME /tmp
# 의존성 라이브러리 및 클래스 파일을 복사
ARG DEPENDENCY=/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
# 애플리케이션 실행 명령어 설정
ENTRYPOINT ["java","-cp","app:app/lib/*","com.optimagrowth.license.LicenseServiceApplication"]
✅ 2단계 주요 사항
- FROM openjdk:17 → 실행 단계에서도 OpenJDK 17을 사용
- VOLUME /tmp → 애플리케이션 실행 중 생성될 임시 파일을 위한 볼륨 설정
- COPY --from=build ... → 첫 번째 빌드 스테이지에서 압축 해제한 파일을 가져와 /app 경로에 배치
- ENTRYPOINT → java 런처를 실행하여 Spring Boot 애플리케이션 기동
이 Dockerfile은 멀티 스테이지 빌드(Multi-Stage Build) 방식을 사용하여 최적화되었습니다. 이를 통해 얻을 수 있는 이점은 다음과 같습니다:
✅ 경량화된 컨테이너 이미지
- 빌드에 불필요한 파일들을 제외하고, 실행에 필요한 파일만 포함하여 이미지 크기를 줄입니다.
✅ 빌드 환경과 실행 환경 분리
- 첫 번째 스테이지에서 애플리케이션을 빌드하고, 두 번째 스테이지에서는 실행에 필요한 파일만 복사하여 유지보수성을 높입니다.
✅ 보안성 향상
- 실행 이미지에 Maven, Gradle 등 빌드 도구가 포함되지 않으므로 공격 표면이 줄어듭니다.
✅ 빌드 시간 단축
- JAR 파일을 추출하여 필요한 파일만 포함하므로 컨테이너 실행 속도가 향상됩니다.
🔹 멀티 스테이지 빌드를 활용하면 보다 효율적인 컨테이너 이미지를 생성할 수 있으며, 실행 환경을 최적화할 수 있습니다.
✨ 이를 기반으로 컨테이너화된 Spring Boot 애플리케이션을 효과적으로 배포해 보세요! 🚀
출처 : https://github.com/ihuaylupo/manning-smia/tree/master/chapter5/licensing-service
manning-smia/chapter5/licensing-service at master · ihuaylupo/manning-smia
Spring Microservices in Action - Second Edition - Code Examples - ihuaylupo/manning-smia
github.com
'Spring Microservice' 카테고리의 다른 글
docker compose 파일 분석 및 실행 (0) | 2025.03.13 |
---|---|
HashiCorp Vault 구동 (0) | 2025.03.13 |
Configserver 서버 프로젝트 (0) | 2025.03.13 |
Spring Microservices in Action Chapter05 프로젝트 (0) | 2025.03.13 |
Spring Cloud Config 서비스와 Vault 연동을 통한 보안 설정 관리 및 UI 활용 (0) | 2025.03.09 |