Spring Boot 기반 Licensing Service 프로젝트 분석

2025. 3. 13. 14:54Spring 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 디렉토리에 저장
🔹 Stage 2: 경량 컨테이너 생성 및 실행

두 번째 스테이지에서는 실행에 필요한 파일만 복사하여 최적화된 컨테이너를 구성합니다.

# 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 기반 Licensing Service의 Dockerfile을 분석하고, 멀티 스테이지 빌드 방식의 장점컨테이너 빌드 및 실행 방법을 정리했습니다. 🎯

🔹 멀티 스테이지 빌드를 활용하면 보다 효율적인 컨테이너 이미지를 생성할 수 있으며, 실행 환경을 최적화할 수 있습니다.

 

✨ 이를 기반으로 컨테이너화된 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