Building the doorway into the microservice: The Spring Boot controller

2025. 3. 2. 22:41Spring Microservice

🎯 1. Spring Boot 컨트롤러의 역할

Spring Boot에서 컨트롤러는 서비스 엔드포인트를 노출하고 HTTP 요청을 처리하는 핵심 역할을 합니다.

🔹 RESTful API의 핵심 원칙

HTTP/HTTPS 사용 → 서비스 호출은 HTTP 프로토콜을 통해 수행
표준 HTTP 메서드 활용GET, POST, PUT, DELETE
JSON 기반 데이터 직렬화 → JSON을 통해 데이터를 주고받음
HTTP 상태 코드 활용 → 200(성공), 404(찾을 수 없음), 500(서버 오류) 등

🏗 2. Spring Boot 컨트롤러 클래스 구현

📝 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.1-SNAPSHOT</version>
	<name>Licensing Service</name>
	<description>OStock Licensing Service</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<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>
		</dependency>
		
		<dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		    <scope>provided</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

 

컨트롤러는 @RestController@RequestMapping을 활용해 REST API 엔드포인트를 정의합니다.

📝 LicenseController.java

package com.optimagrowth.license.controller;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value="v1/organization/{organizationId}/license")
public class LicenseController {
}

🔹 @RestController → REST API를 제공하는 컨트롤러로 선언
🔹 @RequestMapping → 모든 엔드포인트의 기본 URL 설정

📄 3. License 모델 클래스

라이선스 정보를 저장하는 POJO(Plain Old Java Object) 클래스입니다.

📝 License.java

package com.optimagrowth.license.model;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter @Setter @ToString
public class License {
 private int id;
 private String licenseId;
 private String description;
 private String organizationId;
 private String productName;
 private String licenseType;
}

Lombok 활용 (@Getter, @Setter, @ToString) → 불필요한 코드 생략

4. LicenseService 클래스 (비즈니스 로직)

비즈니스 로직을 담당하며 CRUD(Create, Read, Update, Delete) 기능을 수행합니다.

📝 LicenseService.java

package com.optimagrowth.license.service;

import java.util.Random;
import org.springframework.stereotype.Service;
import com.optimagrowth.license.model.License;

@Service
public class LicenseService {
  public License getLicense(String licenseId, String organizationId){
    License license = new License();
    license.setId(new Random().nextInt(1000));
    license.setLicenseId(licenseId);
    license.setOrganizationId(organizationId);
    license.setDescription("Software product");
    license.setProductName("Ostock");
    license.setLicenseType("full");
    return license;
  }

  public String createLicense(License license, String organizationId){
    license.setOrganizationId(organizationId);
    return String.format("Created license: %s", license.toString());
  }

  public String updateLicense(License license, String organizationId){
    license.setOrganizationId(organizationId);
    return String.format("Updated license: %s", license.toString());
  }

  public String deleteLicense(String licenseId, String organizationId){
    return String.format("Deleting license with id %s for the organization %s", licenseId, organizationId);
  }
}

🔹 @Service → 서비스 계층을 정의하여 비즈니스 로직 분리
🔹 랜덤 값으로 ID를 생성해 샘플 데이터를 반환

🔗 5. REST API 엔드포인트 구현

컨트롤러에서 GET, POST, PUT, DELETE 요청을 처리하는 CRUD API를 구현합니다.

📝 LicenseController.java

package com.optimagrowth.license.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import com.optimagrowth.license.model.License;
import com.optimagrowth.license.service.LicenseService;

@RestController
@RequestMapping(value="v1/organization/{organizationId}/license")
public class LicenseController {

 @Autowired
 private LicenseService licenseService;

 // 🟢 GET: 라이선스 조회
 @GetMapping(value="/{licenseId}")
 public ResponseEntity<License> getLicense(
   @PathVariable("organizationId") String organizationId,
   @PathVariable("licenseId") String licenseId) {
   License license = licenseService.getLicense(licenseId, organizationId);
   return ResponseEntity.ok(license);
 }

 // 🟡 POST: 라이선스 생성
 @PostMapping
 public ResponseEntity<String> createLicense(
   @PathVariable("organizationId") String organizationId,
   @RequestBody License request) {
   return ResponseEntity.ok(licenseService.createLicense(request, organizationId));
 }

 // 🔵 PUT: 라이선스 업데이트
 @PutMapping
 public ResponseEntity<String> updateLicense(
   @PathVariable("organizationId") String organizationId,
   @RequestBody License request) {
   return ResponseEntity.ok(licenseService.updateLicense(request, organizationId));
 }

 // 🔴 DELETE: 라이선스 삭제
 @DeleteMapping(value="/{licenseId}")
 public ResponseEntity<String> deleteLicense(
   @PathVariable("organizationId") String organizationId,
   @PathVariable("licenseId") String licenseId) {
   return ResponseEntity.ok(licenseService.deleteLicense(licenseId, organizationId));
 }
}

🔹 @GetMapping, @PostMapping, @PutMapping, @DeleteMapping각 HTTP 메서드에 맞는 API 구현
🔹 @RequestBodyJSON 데이터를 객체로 변환

 

※ 실행 결과

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.4.3)

2025-03-03T11:11:06.358+09:00  INFO 35688 --- [Licensing Service] [           main] c.o.license.LicensingServiceApplication  : Starting LicensingServiceApplication using Java 17.0.9 with PID 35688 (C:\development\Workspace\codes\spring_boot3\springmsinaction\ch02\licensing-service\target\classes started by inthe in C:\development\Workspace\codes\spring_boot3\springmsinaction\ch02\licensing-service)
2025-03-03T11:11:06.362+09:00  INFO 35688 --- [Licensing Service] [           main] c.o.license.LicensingServiceApplication  : No active profile set, falling back to 1 default profile: "default"
2025-03-03T11:11:07.335+09:00  INFO 35688 --- [Licensing Service] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2025-03-03T11:11:07.345+09:00  INFO 35688 --- [Licensing Service] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2025-03-03T11:11:07.345+09:00  INFO 35688 --- [Licensing Service] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.36]
2025-03-03T11:11:07.385+09:00  INFO 35688 --- [Licensing Service] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2025-03-03T11:11:07.386+09:00  INFO 35688 --- [Licensing Service] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 976 ms
2025-03-03T11:11:07.876+09:00  INFO 35688 --- [Licensing Service] [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint beneath base path '/actuator'
2025-03-03T11:11:07.937+09:00  INFO 35688 --- [Licensing Service] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2025-03-03T11:11:07.949+09:00  INFO 35688 --- [Licensing Service] [           main] c.o.license.LicensingServiceApplication  : Started LicensingServiceApplication in 1.999 seconds (process running for 2.658)
2025-03-03T11:11:08.565+09:00  INFO 35688 --- [Licensing Service] [on(9)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-03-03T11:11:08.565+09:00  INFO 35688 --- [Licensing Service] [on(9)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2025-03-03T11:11:08.566+09:00  INFO 35688 --- [Licensing Service] [on(9)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms

🛠 6. API 테스트 (Postman 활용)

📌 Postman을 사용해 API를 테스트한 결과

🟢 1) GET 요청 (라이선스 조회)

GET http://localhost:8080/v1/organization/optimaGrowth/license/0235431845

 

 

🟡 2) POST 요청 (라이선스 생성)

POST http://localhost:8080/v1/organization/optimaGrowth/license

 

Body (JSON)

{
  "licenseId": "0235431845",  
  "organizationId": "optimaGrowth",
  "description": "Software product",
  "productName": "Ostock",
  "licenseType": "complete"
}

🔵 3) PUT 요청 (라이선스 업데이트)

PUT http://localhost:8080/v1/organization/optimaGrowth/license

 

 Body (JSON)

{
  "licenseId": "0235431845",  
  "organizationId": "optimaGrowth",
  "description": "Software product",
  "productName": "Ostock",
  "licenseType": "complete"
}

 

🔴 4) DELETE 요청 (라이선스 삭제)

DELETE http://localhost:8080/v1/organization/optimaGrowth/license/0235431845

 

🎉 7. 결론

✅ Spring Boot 기반 RESTful API 마이크로서비스 개발 완료!
@RestController, @RequestMapping, @PathVariable, @RequestBody 등을 활용해 CRUD API 구성
Postman을 활용한 API 테스트 성공 🚀

 

💡 다음 단계 → 다국어 지원(국제화) 기능 추가! 🌍🔥