[LG CNS AM CAMP 1기] 백엔드 II 15 | Spring

letthem·2025년 2월 17일
0

LG CNS AM CAMP 1기

목록 보기
31/31
post-thumbnail

P120 국제화 기능 추가

부트스트랩 클래스에 빈 추가 ⇒ LicenseServiceApplication

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();
    	localeResolver.setDefaultLocale(Locale.US);
    	return localeResolver;
	}
    
	@Bean
	public ResourceBundleMessageSource messageSource() {
    	ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    	messageSource.setUseCodeAsDefaultMessage(true);
    	messageSource.setBasename("messages");
    	return messageSource;
	}
}

resources/messages_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

resources/messages_ko.properties

license.create.message = 라이센스 %s가 생성되었습니다.
license.update.message = 라이센스 %s가 수정되었습니다.
license.delete.message = 라이센스 %s (%s 부서 소유)가 삭제되었습니다.

resources/messages.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

LicenseService 수정 ⇒ MessageSource에서 메시지를 검색해서 사용하도록 수정

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 com.optimagrowth.license.model.License;


// 각 메서드는 CRUD 테스트를 위해서 임의의 데이터와 메시지를 반환하는 코드를 추가

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

    public String createLicense(License license, String organizationId, Locale locale) {
        String responseMessage = null;
        if (license != null) {
            license.setOrganizationId(organizationId);
            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 (license != null) {
            license.setOrganizationId(organizationId);
            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.update.message", null, null),
                licenseId, organizationId);
        return responseMessage;
    }
}

LicenseController 수정 ⇒ Accept-Language 요청 헤더로 수신한 값을 서비스로 전달

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

테스트


P124 스프링 HATEOAS 구현

License 모델을 RepresentationModel 상속하도록 수정

package com.optimagrowth.license.model;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.hateoas.RepresentationModel;

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

LicenseController에 링크 추가

package com.optimagrowth.license.controller;

import java.util.Locale;

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

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;

	@GetMapping(value = "/{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).updateLicense(organizationId, license))
                	.withRel("updateLicense"),
            	linkTo(methodOn(LicenseController.class).createLicense(organizationId, license, null))
                	.withRel("createLicense"),
            	linkTo(methodOn(LicenseController.class).deleteLicense(organizationId, license.getLicenseId()))
                	.withRel("deleteLicense")
        	);
   	 
    	return ResponseEntity.ok(license);
	}
    
	@PutMapping
	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));
	}    
}

테스트





액츄에이터 의존을 추가(확인)

	<dependency>
        	<groupId>org.springframework.boot</groupId>
        	<artifactId>spring-boot-starter-actuator</artifactId>
    	</dependency>

상태 정보 확인

http://localhost:8080/actuator/

http://localhost:8080/actuator/health

application.yml 파일에 설정 정보를 추가 (properties 파일을 yml로 변경)

spring:
  application:
    name: license-service
management:
  endpoints:
    web:
      exposure:
        include: "*"
        exclude:
          - beans
  endpoint:
    health:
      show-details: always


4장. 도커

Dockerfile

프로젝트 최상단

FROM        openjdk:17.0.2
ARG         JAR_FILE=target/*.jar
COPY        ${JAR_FILE} /app.jar
ENTRYPOINT  ["java", "-jar", "/app.jar"]

docker image build -t license-service .

docker image ls

docker container run -d -p 8080 license-service

docker container ls

pom.xml ⇒ dockerfile-maven-plugin 플러그인 추가

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

mvnw clean package dockerfile:build
: dockerfile-maven-plugin를 사용해 도커 이미지를 빌드

com.spotify.docker.client.shaded.org.apache.http.conn.HttpHostConnectException: Connect to localhost:2375 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: getsockopt -> [Help 1]
이런 에러가 나면

docker image ls

REPOSITORY               TAG              IMAGE ID       CREATED             SIZE
ostock/license-service   0.0.1-SNAPSHOT   121ef222bbae   4 minutes ago       496MB

docker container run -d -p 8080 ostock/license-service:0.0.1-SNAPSHOT

926f0709ee459ec5f3adda7d7f5d13c03cb8d2a1077216afd64c21f303ffa1e0

docker container ls

CONTAINER ID   IMAGE                                   COMMAND                CREATED         STATUS         PORTS                     NAMES
926f0709ee45   ostock/license-service:0.0.1-SNAPSHOT   "java -jar /app.jar"   5 seconds ago   Up 4 seconds   0.0.0.0:52223->8080/tcp   beautiful_swirles

curl -X GET http://localhost:52223/v1/organization/optimaGrowth/license/1234567890

{"id":751,"licenseId":"1234567890","description":"Software product","organizationId":"optimaGrowth","productName":"Ostock","licenseType":"full","_links":{"self":{"href":"http://localhost:52223/v1/organization/optimaGrowth/license/1234567890"},"updateLicense":{"href":"http://localhost:52223/v1/organization/optimaGrowth/license"},"createLicense":{"href":"http://localhost:52223/v1/organization/optimaGrowth/license"},"deleteLicense":{"href":"http://localhost:52223/v1/organization/optimaGrowth/license/1234567890"}}}

P152 멀티스테이즈 빌드

FROM  openjdk:17.0.2 as build
ARG   JAR_FILE
COPY  ${JAR_FILE} /app.jar
RUN   mkdir -p /target/dependency && (cd /target/dependency; jar -xf /app.jar)


FROM  openjdk:17.0.2
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"]

docker image build --build-arg JAR_FILE= ./target/*.jar -t license-service:v2 .
docker image ls

REPOSITORY               TAG              IMAGE ID       CREATED          SIZE
license-service          v2               88290e102afb   53 seconds ago   496MB

docker container run -d -p 8080 license-service:v2

4d73a54a06d7b1db88fe29c006bd128cdb1bca36ce32fc0150907df5b5a74914

docker container ls

CONTAINER ID   IMAGE                                   COMMAND                   CREATED          STATUS          PORTS                     NAMES
4d73a54a06d7   license-service:v2                      "java -cp /app:/app/…"   5 seconds ago    Up 5 seconds    0.0.0.0:53004->8080/tcp   amazing_solomon
926f0709ee45   ostock/license-service:0.0.1-SNAPSHOT   "java -jar /app.jar"      41 minutes ago   Up 41 minutes   0.0.0.0:52223->8080/tcp   beautiful_swirles

mvnw spring-boot:build-image

P155 빌드팩을 이용한 도커 이미지 생성

mvnw spring-boot:build-image
docker image ls

REPOSITORY                                 TAG              IMAGE ID       CREATED          SIZE
license-service                            v2               88290e102afb   7 minutes ago    496MB
ostock/license-service                     0.0.1-SNAPSHOT   121ef222bbae   53 minutes ago   496MB
license-service                            latest           c486d4b2b076   2 hours ago      496MB
board-app                                  v3               6ce44b6fd022   3 days ago       192MB
mysql                                      latest           c013d3763e14   3 weeks ago      797MB
paketobuildpacks/run-jammy-tiny            latest           db978be19a6e   2 months ago     22.6MB
openjdk                                    17.0.2           5e28ba2b4cdb   2 years ago      471MB
paketobuildpacks/builder-jammy-java-tiny   latest           957e5a3be91c   45 years ago     672MB
license-service                            0.0.1-SNAPSHOT   383a8ce5d5d8   45 years ago     269MB
board                                      0.0.1-SNAPSHOT   14f122a78ecb   45 years ago     317MB

5장. 스프링 클라우드 컨피그 서버


구성 관리 서비스를 구축해보자 !

스프링 클라우드 컨피그 서버 생성

책처럼 bootstrap.yml 파일 생성하지 말자

컨피그 서비스의 application.properties 파일을 application.yml 파일로 변경

spring:
  application:
    name: config-server
  profiles:
    active:
    - native					⇐ 외부 저장소를 사용하지 않고, 
  cloud:					   로컬 파일 시스템에서 설정을 가져오는 방식
    config:
      server:
        native:
          search-locations: 
          - classpath:/config		⇐ 설정 파일이 저장된 경로
          # file:///c:\temp\config
server:
  port: 8071         
   

Config Server 활성화 ⇒ 부트스트랩 클래스에 @EnableConfigServer 어노테이션을 추가

package com.optimagrowth.configserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class ConfigurationServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConfigurationServerApplication.class, args);
	}

}

설정 파일을 추가

licensing-service.properties

example.property=I AM THE DEFAULT

spring.jpa.hibernate.ddl-auto=none
spring.jpa.database=POSTGRESQL
spring.datasource.platform=postgres
spring.jpa.show-sql=true
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.database.driverClassName=org.postgresql.Driver
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=SELECT 1

server.port=8080

spring.cloud.loadbalancer.ribbon.enabled=false
        
management.endpoints.web.exposure.include=*
management.endpoints.enabled-by-default=true

eureka.instance.preferIpAddress=true
eureka.client.registerWithEureka=true
eureka.client.fetchRegistry=true
eureka.client.serviceUrl.defaultZone=http://localhost:8070/eureka/

licensing-service-dev.properties

example.property=I AM DEV

spring.datasource.url=jdbc:postgresql://localhost:5432/ostock_dev
spring.datasource.username=postgres
spring.datasource.password={cipher}430c76ef665dae02ac7b751fec40adaf9b180cea2ad8e083e53fb1229cc597d3

licensing-service-prod.properties

example.property=I AM PROD

spring.datasource.url=jdbc:postgresql://database:5432/ostock_prod
spring.datasource.username=postgres
spring.datasource.password={cipher}f4609209a3e75d8ac79a5e3063ce151c2cd28aa431170bb06974b9421e807b6a

구성 파일(데이터) 명명 규칙 		⇒ applicationname-profile.properties
					   applicationname-profile.yml
컨피그 서버 엔드포인트 규칙 		⇒ http://localhost:8071/applicationname/profile/label
        				                                                 ~~~~~
                                  git 브랜치 또는 버전 (선택사항) 


=> licensing-service.properties 파일 내용


=> licensing-service-dev.properties 파일 내용 + licensing-service.properties 파일 내용


=> licensing-service-prod.properties 파일 내용 + licensing-service.properties 파일 내용


라이센싱 서비스에 spring-cloud-starter-config 의존을 추가

<?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.2</version>
		<relativePath/>
		<!-- lookup parent from repository -->
	</parent>
	<groupId>com.optimagrowth</groupId>
	<artifactId>license-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>license-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>
		<docker.image.prefix>ostock</docker.image.prefix>
		<spring-cloud.version>2024.0.0</spring-cloud.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-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-hateoas</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<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.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<annotationProcessorPaths>
						<path>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</path>
					</annotationProcessorPaths>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
			<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>

라이센싱 서비스의 application.yml 파일 수정

spring:
  application:
    name: license-service
  profiles:
    active: dev
  config:
    import: "optional:configserver:http://localhost:8071"
management:
  endpoints:
    web:
      exposure:
        include: "*"
        exclude:
        - beans
      base-path: /
  endpoint:
    health:
      show-details: always

컨피그 서버 구동 → 라이센싱 서버 구동

컨피그 서버를 구동
C:\Users\myanj\eclipse-workspace\configserver> mvnw spring-boot:run

라이센싱 서버 구동

2025-02-17T15:01:58.301+09:00[0;39m [32m INFO[0;39m [35m24900[0;39m [2m--- [licensing-service] [  restartedMain] [0;39m[36mo.s.o.j.p.SpringPersistenceUnitInfo     [0;39m [2m:[0;39m No LoadTimeWeaver setup: ignoring JPA class transformer
[2m2025-02-17T15:01:58.335+09:00[0;39m [32m INFO[0;39m [35m24900[0;39m [2m--- [licensing-service] [  restartedMain] [0;39m[36mcom.zaxxer.hikari.HikariDataSource      [0;39m [2m:[0;39m HikariPool-1 - Starting...
[2m2025-02-17T15:01:59.412+09:00[0;39m [33m WARN[0;39m [35m24900[0;39m [2m--- [licensing-service] [  restartedMain] [0;39m[36mo.h.engine.jdbc.spi.SqlExceptionHelper  [0;39m [2m:[0;39m SQL Error: 0, SQLState: 08001
[2m2025-02-17T15:01:59.412+09:00[0;39m [31mERROR[0;39m [35m24900[0;39m [2m--- [licensing-service] [  restartedMain] [0;39m[36mo.h.engine.jdbc.spi.SqlExceptionHelper  [0;39m [2m:[0;39m Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
[2m2025-02-17T15:01:59.413+09:00[0;39m [33m WARN[0;39m [35m24900[0;39m [2m--- [licensing-service] [  restartedMain] [0;39m[36mo.h.e.j.e.i.JdbcEnvironmentInitiator    [0;39m [2m:[0;39m HHH000342: Could not obtain connection to query metadata

컨피그 서버에서 가져온 정보로 데이터베이스 연결을 시도했으나, DB 서버가 존재하지 않기 때문에 오류가 발생


컨피그 서버 설정을 변경

spring:
  application:
    name: config-server
  profiles:
    active:
#   - native
    - git 
  cloud:
    config:
      server:
        native:
          search-locations: 
#         - classpath:/config
          - file:///c:\temp\config
        git:
          uri: https://github.com/letthem/license-service.git
          search-paths: config
server:
  port: 8071   
       

컨피그 서버 재시작 후 라이센스 서비스 시작
C:\lgcns\configserver> mvnw spring-boot:run

라이센스 서비스 ⇒ 이클립스를 이용해서 시작

P190 라이센싱 서비스 구현

License 모델을 엔티티 클래스로 변경

package com.optimagrowth.license.model;

import org.springframework.hateoas.RepresentationModel;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@Entity
@Table(name = "licenses")
public class License extends RepresentationModel<License> {
    // private int id;
    @Id
    @Column(name = "license_id", nullable = false)
    private String licenseId;
    
    private String description;
    
    @Column(name = "organization_id", nullable = false)
    private String organizationId;
    
    @Column(name = "product_name", nullable = false)
    private String productName;
    
    @Column(name = "license_type", nullable = false)
    private String licenseType;
    
    @Column(name = "comment")
    private String comment;
    
    public License withComment(String comment) {
        this.setComment(comment);
        return this;
    }    
}

Organization 모델 추가

package com.optimagrowth.license.model;

import org.springframework.hateoas.RepresentationModel;

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

@Getter
@Setter
@ToString
public class Organization extends RepresentationModel<Organization> {
    String id;
    String name;
    String contactName;
    String contactEmail;
    String contactPhone;
}

LicenseRepository 인터페이스 추가

package com.optimagrowth.license.repository;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

import com.optimagrowth.license.model.License;

public interface LicenseRepository extends CrudRepository<License, String> {
    public List<License> findByOrganizationId(String organizationId);
    public License findByOrganizationIdAndLicenseId(String organizationId, String licenseId);
}

LicenseService 수정 ⇒ LicenseRepository 연동

package com.optimagrowth.license.service;

import java.util.UUID;

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

import com.optimagrowth.license.config.ServiceConf;
import com.optimagrowth.license.model.License;
import com.optimagrowth.license.repository.LicenseRepository;

@Service
public class LicenseService {
    @Autowired
    MessageSource messages;
    
    @Autowired
    LicenseRepository licenseRepository;
    
    @Autowired
    ServiceConf config;
    
    public License getLicense(String licenseId, String organizationId) {
        License license = licenseRepository.findByOrganizationIdAndLicenseId(organizationId, licenseId);
        if (license == null) {
            throw new IllegalArgumentException(
                String.format(
                    messages.getMessage("license.search.error.message", null, null), 
                    licenseId, organizationId));
            
        }        
        return license.withComment(config.getProperty());
    }
    
    public License createLicense(License license) {
        license.setLicenseId(UUID.randomUUID().toString());
        licenseRepository.save(license);
        return license.withComment(config.getProperty());
    }
    
    public License updateLicense(License license) {
        licenseRepository.save(license);
        return license.withComment(config.getProperty());
    }
    
    public String deleteLicense(String licenseId) {
        License license = new License();
        license.setLicenseId(licenseId);
        licenseRepository.delete(license);
        
        String responseMessage = null;
        responseMessage 
            = String.format(messages.getMessage("license.delete.message", null, null), 
                licenseId);
        return responseMessage;
    }
}

ServiceConf 추가 ⇒ 설정 파일의 내용을 자바 객체로 변환

package com.optimagrowth.license.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import lombok.Getter;
import lombok.Setter;

@Configuration
@ConfigurationProperties(prefix = "example")
@Getter
@Setter
public class ServiceConf {
    private String property;
}

메시지 파일(messages*.properties)에 검색 실패 메시지를 추가

license.delete.message = Deleting license with id %s
license.search.error.message = Unable to find license with License id %s and Organization id %s

LicenseController

package com.optimagrowth.license.controller;

import java.util.Locale;

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

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;

    @GetMapping(value = "/{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).updateLicense(organizationId, license))
                    .withRel("updateLicense"),
                linkTo(methodOn(LicenseController.class).createLicense(organizationId, license, null))
                    .withRel("createLicense"),
                linkTo(methodOn(LicenseController.class).deleteLicense(organizationId, license.getLicenseId()))
                    .withRel("deleteLicense")
            );
        
        return ResponseEntity.ok(license);
    }
    
    @PutMapping
    public ResponseEntity<License> updateLicense(
            @PathVariable("organizationId") String organizationId, 
            @RequestBody License request) {
        return ResponseEntity.ok(licenseService.updateLicense(request));
    }
    
    @PostMapping
    public ResponseEntity<License> createLicense(
            @PathVariable("organizationId") String organizationId, 
            @RequestBody License request, 
            @RequestHeader(value = "Accept-Language", required = false) Locale locale) {
        return ResponseEntity.ok(licenseService.createLicense(request));
    }
    
    @DeleteMapping(value = "/{licenseId}")
    public ResponseEntity<String> deleteLicense(
            @PathVariable("organizationId") String organizationId, 
            @PathVariable("licenseId") String licenseId) {
        return ResponseEntity.ok(licenseService.deleteLicense(licenseId));
    }    
}

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

0개의 댓글

관련 채용 정보