[마이크로서비스] 스프링 부트 프로젝트 만들기 (2)

hyeokjin·2022년 8월 13일
0

microservice

목록 보기
2/13
post-thumbnail

스프링 부트 프로젝트 만들기

실습환경
🎄 inteliJ, JDK 11, Maven, Spring boot 2.2.3

실습에 들어가기 앞서, 기본적인 스프링 부트와 자바로 마이크로서비스를 시작해보겠다.

아래는 진행하고자하는 서비스들의 소스를 참고할 깃헙 주소이다.

https://github.com/hyeokjinON/microservice_study/tree/master/chapter3

licensing-service는 비용, 라이선스 타입, 라이선스 소유자, 라이선스 계약등 라이선스 정보가 있는 서비스를 말하며 해당 서비스를 이용해서 프로젝트를 구축해보고자 한다.

HATEOAS를 이용하여 링크를 지원하고, 다른언어를 사용하기 위해 프로퍼티에 국제화를 추가하여 적용했다.

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>2.2.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.optimagrowth</groupId>
	<artifactId>licensing-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>License Service</name>
	<description>Ostock Licensing Service</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
    	<dependency>
            <groupId>org.springframework.boot</groupId>
            <!-- 관련 링크를 표시하는 스프링 HATEOAS 구현 -->
            <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>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
                <!-- 스프링 부트 애플리케이션을 빌드하고 배포하도록 스프링용 메이븐 플러그 추가-->
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

LicenseServiceApplication.java

package com.optimagrowth.license;

import java.util.Locale;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

// 스프링 부트 프레임워크에 이 클래스를 부트스트랩 클래스로 지정
@SpringBootApplication
public class LicenseServiceApplication {

	//전체 스프링 부트 서비스를 시작한다.
	public static void main(String[] args) {
		SpringApplication.run(LicenseServiceApplication.class, args);
	}

	// 애플리케이션을 다른 언어에도 적응할 수 있도록 라이선싱 서비스에 국제화 추가한다
	@Bean
	public LocaleResolver localeResolver() {
		SessionLocaleResolver localeResolver = new SessionLocaleResolver();
		// 기본 로케일 US로 설정
		localeResolver.setDefaultLocale(Locale.US);
		return localeResolver;
	}
	@Bean
	public ResourceBundleMessageSource messageSource() {
		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
		// 메시지가 발견되지 않아도 에러를 던지지 않고 메시지 코드를 반환한다
		messageSource.setUseCodeAsDefaultMessage(true);
		// 언어 프로퍼티 파일의 기본 이름을 설정한다.
		messageSource.setBasenames("messages");
		return messageSource;
	}

}

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 java.util.Locale;

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.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.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

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

//스프링 부트에 이서비스는 REST기반 서비스이며 응답은 json으로 서비스요청 및 자동으로 직렬화 및 역직렬화할 것이라 지정한다
@RestController
// 이 클래스의 모든 HTTP 엔드포인트가 v1/organization/{organizationId}/license 에서 시작되도록 노출한다.
@RequestMapping(value="v1/organization/{organizationId}/license")
public class LicenseController {

	@Autowired
	private LicenseService licenseService;

	// organizationId, licenseId 를 매핑한다.
	@RequestMapping(value="/{licenseId}",method = RequestMethod.GET)
	public ResponseEntity<License> getLicense( @PathVariable("organizationId") String organizationId,
			@PathVariable("licenseId") String licenseId) {
		
		License license = licenseService.getLicense(licenseId, organizationId);
        // add()는 RepresentalionModel 클래스 메서드이며, linkTo() 메서드는 LicenseController 클래스를 검사해서 루트 매핑을 얻고
		// methodOn() 메서드는 대상 메서드에 더미 호출을 수행하여 메서드 매핑을 가져 온다.
		// HTTP get 호출을 하여 getLicense() 서비스의 응답 내용에 포함된 링크를 볼 수 있다.
		license.add( 
				linkTo(methodOn(LicenseController.class).getLicense(organizationId, license.getLicenseId())).withSelfRel(),
				linkTo(methodOn(LicenseController.class).createLicense(organizationId, license, null)).withRel("createLicense"),
				linkTo(methodOn(LicenseController.class).updateLicense(organizationId, license)).withRel("updateLicense"),
				linkTo(methodOn(LicenseController.class).deleteLicense(organizationId, license.getLicenseId())).withRel("deleteLicense")
		);
		
		// ResponseEntity로 전체 HTTP 응답을 표현한다.
		return ResponseEntity.ok(license);
	}

	// 업데이트
	@PutMapping
	// @RequestBody는 HTTP 요청 바디를 라이선스 객체로 매핑
	public ResponseEntity<String> updateLicense(@PathVariable("organizationId") String organizationId, @RequestBody License request) {
		return ResponseEntity.ok(licenseService.updateLicense(request, organizationId));
	}

	// 생성
	@PostMapping
	public ResponseEntity<String> createLicense(@PathVariable("organizationId") String organizationId, @RequestBody License request,
			@RequestHeader(value = "Accept-Language",required = false) Locale locale) {
		return ResponseEntity.ok(licenseService.createLicense(request, organizationId, locale));
	}

	//삭제
	@DeleteMapping(value="/{licenseId}")
	public ResponseEntity<String> deleteLicense(@PathVariable("organizationId") String organizationId, @PathVariable("licenseId") String licenseId) {
		return ResponseEntity.ok(licenseService.deleteLicense(licenseId, organizationId));
	}
}


License.Java

package com.optimagrowth.license.model;

import org.springframework.hateoas.RepresentationModel;

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

// 라이선스 정보를 보관하는 POJO
@Getter @Setter @ToString
public class License extends RepresentationModel<License> {

	private int id;
	private String licenseId;
	private String description;
	private String organizationId;
	private String productName;
	private String licenseType;

}

LicenseService.java

package com.optimagrowth.license.service;

import java.util.Locale;
import java.util.Random;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.optimagrowth.license.model.License;

@Service
public class LicenseService {
	
	@Autowired
	MessageSource messages;

	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;
	}

	// 메서드 매기변수로 locale을 전달받는다
	public String createLicense(License license, String organizationId, Locale locale){
		String responseMessage = null;
		if(!StringUtils.isEmpty(license)) {
			license.setOrganizationId(organizationId);
			// getMessage 3번째 인자 특정 메시지를 조회하기 위해 전달된 로케일로 설정
			responseMessage = String.format(messages.getMessage("license.create.message",null,locale), license.toString());
		}

		return responseMessage;
	}

	public String updateLicense(License license, String organizationId){
		String responseMessage = null;
		if(!StringUtils.isEmpty(license)) {
			license.setOrganizationId(organizationId);
			// getMessage 3번째 인자 특정 메시지를 조회하기 위해 NULL 로케일 전딜
			responseMessage = String.format(messages.getMessage("license.update.message", null, null), license.toString());
		}

		return responseMessage;
	}

	public String deleteLicense(String licenseId, String organizationId){
		String responseMessage = null;
		responseMessage = String.format(messages.getMessage("license.delete.message", null, null),licenseId, organizationId);
		return responseMessage;

	}
}

message_en.properties

license.create.message = License created %s
license.update.message = License %s updated
license.delete.message = Deleting license with id %s for the organization %s

message_es.properties

license.create.message = Licencia creada %s
license.update.message = Licencia %s creada
license.delete.message = Eliminando licencia con id %s para la organization %s

message_ko.properties

license.create.message = License created %s
license.update.message = License %s updated
license.delete.message = Deleting license with id %s for the organization %s

spring boot를 통한 간단한 서비스 구축을 했다. 위 코드에서 주석으로 설명을 달아놔서 따로 설명은 하지 않겠다

Postman을 이용한 테스트

postman을 이용하여 HTTP get 호출을 해보자

잘 된다.

createLicens() 경우 @RequestHeader에 보면 Accept-Language 요청 헤더 설정이 있을 것이다. postman 요청 헤더에 설정해주면 해당 로케일에 따른 메시지 프로퍼티에 셋팅을 찾아서 값을 호출한다.

Spring Actuator

또 여기서는 스프링 엑추에이터(Spring Actuator)을 maven에 같이 빌드 시켰다.
스프링 엑추에이터는 서비스 상태를 이해하고 관리할 수 있게 하며, 추가 설치 없이 바로 운영가능한 엔드 포인트를 제공한다.

http://localhost:8080/actuator/health 엔드포인트에 요청을 보내면 상태정보가 반환된다.

상태확인은 기본적으로 서비스 동작 여부와 가용 디스크 정보같은 기본 정보를 반환한다.

프로퍼티 파일에서 기본 구성을 변경할 수 도 있다.

application.properties

<!-- 스프링 엑추에이터를 사용할 경우, 예를 들어 다음과 같이 애플리케이션 프로퍼티 파일에서 기본 구성을 변경할 수 있다-->
management.endpoints.enabled-by-default=false
management.endpoint.health.enabled=true
management.endpoint.health.show-details=always
management.health.db.enabled=false
management.health.diskspace.enabled=true

🧨 이어서 다음 챕터는 본격적으로 도커를 이용할 것이다.
도커의 개념을 잡고, 프로젝트에 적용해보도록 한다.

profile
노옵스를향해

0개의 댓글