Quarkus๊ฐ€ MicroProfile๊ณผ Spring์„ ๊ฒฐํ•ฉํ•˜๋Š” ๋ฐฉ๋ฒ•

์•ˆ๋…•ํ•˜์„ธ์š” ์—ฌ๋Ÿฌ๋ถ„, Quarkus ์‹œ๋ฆฌ์ฆˆ์˜ ์„ธ ๋ฒˆ์งธ ํฌ์ŠคํŒ…์ž…๋‹ˆ๋‹ค!

Quarkus๊ฐ€ MicroProfile๊ณผ Spring์„ ๊ฒฐํ•ฉํ•˜๋Š” ๋ฐฉ๋ฒ•

Java ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•  ๋•Œ ํ”ํžˆ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ๊ฐ๋ฉ๋‹ˆ๋‹ค. ์ดํด๋ฆฝ์Šค ๋งˆ์ดํฌ๋กœํ”„๋กœํŒŒ์ผ ะธ ๋ด„ ๋ถ€ํŒ… ๋ณ„๋„์˜ ๋…๋ฆฝ์ ์ธ API์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ์ƒˆ๋กœ์šด ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋Ÿฐํƒ€์ž„ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ฐฐ์šฐ๋Š” ๋ฐ ๋งŽ์€ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฏธ ์ต์ˆ™ํ•œ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋Š˜ ์šฐ๋ฆฌ๋Š” ๋ช‡ ๊ฐ€์ง€ ์ธ๊ธฐ ์žˆ๋Š” ๊ฐœ๋ฐœ์„ ๋‹จ์ˆœํ™”ํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. Spring ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ MicroProfile API Spring API์™€ ์ƒˆ๋กœ์šด ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์„ ๋™์‹œ์— ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ฟผ์ฟ ์Šค.

์ข€ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๋ฉด ๋จผ์ € Quarkus๊ฐ€ Spring API๋ฅผ ์ง€์›ํ•˜์—ฌ Spring ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ผ์ƒ ์ž‘์—…์—์„œ MicroProfile API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ฃผ๋Š” ๋ฒ”์œ„์™€ ์„ธ๋ถ€ ์‚ฌํ•ญ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ Spring ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์œ ์šฉํ•œ MicroProfile API๋ฅผ ๋‹ค๋ฃฐ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์™œ Quarkus์ธ๊ฐ€? ์ฒซ์งธ, ์ด๊ฒƒ์€ ๋ผ์ด๋ธŒ ์ฝ”๋”ฉ์ž…๋‹ˆ๋‹ค. ์ฆ‰, MicroProfile API, Spring API ๋ฐ ๊ธฐํƒ€ Java API์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ๋กœ๋“œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋‹จ ํ•˜๋‚˜์˜ ๋ช…๋ น(mvn quarkus:dev)์œผ๋กœ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ๋กœ, ์šฐ๋ฆฌ์˜ ์˜ˆ์—์„œ๋Š” Person ์„œ๋น„์Šค(๊ธฐ๋ณธ GraalVM ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Spring, MicroProfile ๋ฐ JPA API์—์„œ ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ์ปดํŒŒ์ผ)๋Š” ๋‹จ 0.055์ดˆ ๋งŒ์— ์‹œ์ž‘๋˜๋ฉฐ RESTful ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์—”๋“œํฌ์ธํŠธ์—์„œ ์•ฝ 90MB์˜ RAM(RSS)์„ ์ฐจ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ปดํŒŒ์ผ ์ž์ฒด๋Š” mvn package -Pnative๋ผ๋Š” ๋‹จ ํ•˜๋‚˜์˜ ๋ช…๋ น์œผ๋กœ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

Spring ๊ฐœ๋ฐœ์ž๊ฐ€ Quarkus์—์„œ MicroProfile API์™€ ํ•จ๊ป˜ Spring API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ดํ•ดํ•˜๋„๋ก ๋•๋Š” ๊ฒƒ ์™ธ์—๋Š” MicroProfile์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ปจํ…Œ์ด๋„ˆ์™€ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค

์ด ๋ฌธ์„œ๋ฅผ ๋‹จ์ˆœํ•˜๊ฒŒ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๊ธฐ์„œ๋Š” ์ง€์›์˜ ๋†’์€ ์ˆ˜์ค€์˜ ์ธก๋ฉด๋งŒ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. Kubernetes, ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. Quarkus๋Š” Kubernetes์šฉ Java ์Šคํƒ์œผ๋กœ ํฌ์ง€์…”๋‹๋˜์–ด Java ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐ ์„œ๋น„์Šค์˜ ๋ฉ”๋ชจ๋ฆฌ ์†Œ๋น„์™€ ์‹œ์ž‘ ์‹œ๊ฐ„์„ ์ตœ์†Œํ™”ํ•˜๊ณ  ๊ฒฐ๊ณผ์ ์œผ๋กœ ํ˜ธ์ŠคํŠธ์˜ ๋ฐ€๋„๋ฅผ ๋†’์ด๊ณ  ์ „์ฒด ๋น„์šฉ์„ ์ค„์ด๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ฟผ์ปค์Šค๋„ ์ž๋™ ์ƒ์„ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค Kubernetes ๋ฆฌ์†Œ์Šค ๋ฐ ์ œ์•ˆ ๊ฐ€์ด๋“œ Kubernetes ๋ฐ Red Hat OpenShift ํ”Œ๋žซํผ์— ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ Quarkus๋Š” ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ Dockerfile.jvm(JVM ํŒจํ‚ค์ง•) ๋ฐ Dockerfile.native(๋„ค์ดํ‹ฐ๋ธŒ ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒจํ‚ค์ง•) ํŒŒ์ผ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, Quarkus๋Š” Kubernetes๋ฅผ ๋Œ€์ƒ ๋ฐฐํฌ ํ™˜๊ฒฝ์œผ๋กœ ์ง‘์ค‘ํ•จ์œผ๋กœ์จ Kubernetes ํ”Œ๋žซํผ ์ž์ฒด ์ˆ˜์ค€์—์„œ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ์ด ๊ตฌํ˜„๋˜๋Š” ๊ฒฝ์šฐ Java ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ‘œ 1์€ Kubernetes์™€ Spring ๊ฐœ๋ฐœ์ž๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ Java ํ”„๋ ˆ์ž„์›Œํฌ ๊ฐ„์˜ ๊ธฐ๋Šฅ์  ๋Œ€์‘ ๋งต์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํ‘œ 1. Java ํ”„๋ ˆ์ž„์›Œํฌ์™€ Kubernetes ๊ฐ„์˜ ๊ธฐ๋Šฅ์  ๋Œ€์‘ ๋งต

๊ธฐ๋Šฅ
์ „ํ†ต์ ์ธ ์Šคํ”„๋ง ๋ถ€์ธ 
Kubernetes

์„œ๋น„์Šค ๋ฐœ๊ฒฌ
์œ ๋ ˆ์นด
DNS

๊ตฌ์„ฑ
์Šคํ”„๋ง ํด๋ผ์šฐ๋“œ ๊ตฌ์„ฑ
๊ตฌ์„ฑ ๋งต/๋น„๋ฐ€๋ฒˆํ˜ธ

๋กœ๋“œ ๊ท ํ˜• ์กฐ์ •
๋ฆฌ๋ณธ(ํด๋ผ์ด์–ธํŠธ ์ธก)
์„œ๋น„์Šค, โ€‹โ€‹๋ณต์ œ ์ปจํŠธ๋กค๋Ÿฌ(์„œ๋ฒ„ ์ธก)

์˜ˆ์ œ์˜ ์ฝ”๋“œ ์ปดํŒŒ์ผ ๋ฐ ์‹คํ–‰

์ด ๊ธฐ์‚ฌ์—์„œ๋Š” ๋‹ค์Œ์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์‹œ ํ”„๋กœ์ ํŠธ, Spring ๋ฐ MicroProfile API์™€ ๋™์ผํ•œ Java ํด๋ž˜์Šค๊ฐ€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋Š” ๋ช…๋ น์ค„์—์„œ ์ปดํŒŒ์ผํ•˜๊ณ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ README.md ํŒŒ์ผ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ API

์˜์กด์„ฑ ์ฃผ์ž…

Quarkus๋Š” ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ ๋ฐ ์ข…์†์„ฑ ์ฃผ์ž…(CDI) API ๋ฐ ์Šคํ”„๋ง ์ข…์†์„ฑ ์ฃผ์ž…(Spring DI) API. MicroProfile๋กœ ์ž‘์—…ํ•˜๋Š” ๊ฒฝ์šฐ ์ž๋ฐ” EE ๋ฐ ์ž์นด๋ฅดํƒ€ EE, ๊ทธ๋ ‡๋‹ค๋ฉด ๋‹น์‹ ์€ ์ด๋ฏธ CDI์— ๋Œ€ํ•ด ๋งค์šฐ ์ž˜ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์— Spring ๊ฐœ๋ฐœ์ž๋Š” Spring DI API์šฉ Quarkus Extension์„ ์‚ฌ์šฉํ•˜์—ฌ Spring DI์™€์˜ ํ˜ธํ™˜์„ฑ์„ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€์›๋˜๋Š” Spring DI API ์‚ฌ์šฉ ์˜ˆ๋Š” ํ‘œ 2์— ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

ะ’ ์šฐ๋ฆฌ ์˜ˆ์˜ ํ”„๋กœ์ ํŠธ CDI์™€ Spring ์ข…์†์„ฑ ์ฃผ์ž…์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฃผ์ œ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ๊ณผ ์˜ˆ์‹œ๋Š” Quarkus ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”. ์Šคํ”„๋ง DI ๊ฐ€์ด๋“œ.

ํ‘œ 2. ์ง€์›๋˜๋Š” Spring DI API ์‚ฌ์šฉ ์˜ˆ

์ง€์›๋˜๋Š” Spring DI ๊ธฐ๋Šฅ
์˜ˆ

์ƒ์„ฑ์ž ์ฃผ์ž…

public PersonSpringController(
   PersonSpringRepository personRepository,  // injected      
   PersonSpringMPService personService) {    // injected
      this.personRepository = personRepository;
      this.personService = personService;
}

ํ˜„์žฅ ์ฃผ์ž…
์ž๋™ ์œ ์„ 
๊ฐ€์น˜๊ด€

@Autowired
@RestClient
SalutationRestClient salutationRestClient;

@Value("${fallbackSalutation}")
String fallbackSalutation;

์ฝฉ
@ ๊ตฌ์„ฑ

@Configuration
public class AppConfiguration {
   @Bean(name = "capitalizeFunction")
   public StringFunction capitalizer() {
      return String::toUpperCase;
   }
}

๊ตฌ์„ฑ ์š”์†Œ

@Component("noopFunction")
public class NoOpSingleStringFunction implements StringFunction {
   @Override
   public String apply(String s) {
      return s;
   }
}

์˜ˆ๋ฐฐ

@Service
public class MessageProducer {
   @Value("${greeting.message}")
   String message;

   public String getPrefix() {
      return message;
   }
}

์›น ํ”„๋ ˆ์ž„์›Œํฌ

MicroProfile ์‚ฌ์šฉ์ž๋Š” Quarkus๊ฐ€ ๊ธฐ๋ณธ ์›น ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ๋กœ JAX-RS, MicroProfile Rest Client, JSON-P ๋ฐ JSON-B๋ฅผ ์ง€์›ํ•œ๋‹ค๋Š” ์ ์„ ์ข‹์•„ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. Spring ๊ฐœ๋ฐœ์ž๋Š” Spring Web API, ํŠนํžˆ REST ์ธํ„ฐํŽ˜์ด์Šค์— ๋Œ€ํ•œ Quarkus์˜ ์ตœ๊ทผ ์ง€์›์— ๋งŒ์กฑํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. Spring DI์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Spring Web API ์ง€์›์˜ ์ฃผ์š” ๋ชฉํ‘œ๋Š” Spring ๊ฐœ๋ฐœ์ž๊ฐ€ MicroProfile API์™€ ํ•จ๊ป˜ Spring Web API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ง€์›๋˜๋Š” Spring Web API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์˜ ์˜ˆ๋Š” ํ‘œ 3์— ์ œ๊ณต๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ด ์ฃผ์ œ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด์™€ ์˜ˆ๋Š” Quarkus ํŠœํ† ๋ฆฌ์–ผ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ด„ ์›น ๊ฐ€์ด๋“œ.

ํ‘œ 3. ์ง€์›๋˜๋Š” Spring Web API ์‚ฌ์šฉ ์˜ˆ

์ง€์›๋˜๋Š” Spring ์›น ๊ธฐ๋Šฅ
์˜ˆ

@RestController
@์š”์ฒญ ๋งคํ•‘

@RestController
@RequestMapping("/person")
public class PersonSpringController {
   ...
   ...
   ...
}

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
@RequestParam
@์š”์ฒญํ—ค๋”
@MatrixVariable
@๊ฒฝ๋กœ๋ณ€์ˆ˜
@CookieValue
@์š”์ฒญ ๋ณธ๋ฌธ
@์‘๋‹ต์ƒํƒœ
@ExceptionHandler
@RestControllerAdvice(์ผ๋ถ€)

@GetMapping(path = "/greet/{id}",
   produces = "text/plain")
   public String greetPerson(
   @PathVariable(name = "id") long id) {
   ...
   ...
   ...
}

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA

MicroProfile ์‚ฌ์šฉ์ž๋Š” Quarkus๊ฐ€ Hibernate ORM์„ ์‚ฌ์šฉํ•˜์—ฌ JPA๋ฅผ ์ง€์›ํ•œ๋‹ค๋Š” ์ ๋„ ๋†’์ด ํ‰๊ฐ€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. Spring ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์ข‹์€ ์†Œ์‹๋„ ์žˆ์Šต๋‹ˆ๋‹ค. Quarkus๋Š” ์ผ๋ฐ˜์ ์ธ Spring Data JPA ์ฃผ์„ ๋ฐ ์œ ํ˜•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ง€์›๋˜๋Š” Spring Data JPA API ์‚ฌ์šฉ ์˜ˆ๋Š” ํ‘œ 4์— ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.
ะ’ ์šฐ๋ฆฌ ์˜ˆ์˜ ํ”„๋กœ์ ํŠธ Spring Data JPA API๊ฐ€ ์‚ฌ์šฉ๋˜๋ฉฐ ๋” ๋งŽ์€ ์ •๋ณด๋Š” Quarkus ํŠœํ† ๋ฆฌ์–ผ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๊ฐ€์ด๋“œ.

ํ‘œ 4. ์ง€์›๋˜๋Š” Spring Data JPA API ์‚ฌ์šฉ ์˜ˆ

์ง€์›๋˜๋Š” Spring ๋ฐ์ดํ„ฐ JPA ๊ธฐ๋Šฅ
์˜ˆ

ํฌ๋Ÿฌ๋“œ ์ €์žฅ์†Œ

public interface PersonRepository
         extends JpaRepository,
                 PersonFragment {
   ...
}

์ €์žฅ์†Œ
Jpa๋ฆฌํฌ์ง€ํ† ๋ฆฌ
ํŽ˜์ด์ง• ๋ฐ ์ •๋ ฌ ์ €์žฅ์†Œ

public class PersonRepository extends 

    Repository {

    Person save(Person entity);

    Optional findById(Person entity);
}

์ €์žฅ์†Œ ์กฐ๊ฐ

public interface PersonRepository
         extends JpaRepository,
                 PersonFragment {
   ...
}

ํŒŒ์ƒ๋œ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ

public interface PersonRepository extends CrudRepository {

    List findByName(String name);
    
    Person findByNameBySsn(String ssn);
    
    Optional 
       findByNameBySsnIgnoreCase(String ssn);

    Boolean existsBookByYearOfBirthBetween(
            Integer start, Integer end);
}

์‚ฌ์šฉ์ž ์ •์˜ ์ฟผ๋ฆฌ

public interface MovieRepository
         extends CrudRepository {

    Movie findFirstByOrderByDurationDesc();

    @Query("select m from Movie m where m.rating = ?1")
    Iterator findByRating(String rating);

    @Query("from Movie where title = ?1")
    Movie findByTitle(String title);
}

๋งˆ์ดํฌ๋กœํ”„๋กœํŒŒ์ผ API

๊ฒฐํ•จ ํ—ˆ์šฉ

๋‚ด๊ฒฐํ•จ์„ฑ ๊ตฌ์„ฑ์€ ์—ฐ์‡„ ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ  ์•ˆ์ •์ ์ธ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. Spring ๊ฐœ๋ฐœ์ž๋Š” ์ˆ˜๋…„ ๋™์•ˆ ๋‚ด๊ฒฐํ•จ์„ฑ์„ ์œ„ํ•ด ํšŒ๋กœ ์ฐจ๋‹จ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•ด ์™”์Šต๋‹ˆ๋‹ค. ํžˆ์ŠคํŠธ๋ฆญ์Šค. ๊ทธ๋Ÿฌ๋‚˜ Hystrix๋Š” ์˜ค๋žซ๋™์•ˆ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์•˜์ง€๋งŒ MicroProfile์˜ Fault Tolerance๋Š” ํ˜„์žฌ ํ™œ๋ฐœํžˆ ๊ฐœ๋ฐœ ์ค‘์ด๋ฉฐ ์ˆ˜๋…„ ๋™์•ˆ ํ”„๋กœ๋•์…˜ ์šฉ๋„๋กœ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Quarkus์—์„œ ์„œ๋น„์Šค์˜ ์•ˆ์ •์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ‘œ 5์— ์‚ฌ์šฉ๋œ ์˜ˆ์‹œ์™€ ๊ฐ™์€ MicroProfile Fault Tolerance API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ Quarkus ๋งค๋‰ด์–ผ์„ ์ฐธ์กฐํ•˜์„ธ์š”. ๋‚ด๊ฒฐํ•จ์„ฑ ๊ฐ€์ด๋“œ.

ํ‘œ 5. ์ง€์›๋˜๋Š” MicroProfile Fault Tolerance API ์‚ฌ์šฉ ์˜ˆ.

MicroProfile ๊ฒฐํ•จ ํ—ˆ์šฉ ๊ธฐ๋Šฅ
๊ธฐ์ˆ 
์˜ˆ

@๋น„๋™๊ธฐ

๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋กœ์ง ์‹คํ–‰

@Asynchronous
@Retry
public Future<String> getSalutation() {
   ...
   return future;
}

@์นธ๋ง‰์ด

๋™์‹œ ์š”์ฒญ ์ˆ˜ ์ œํ•œ

@Bulkhead(5)
public void fiveConcurrent() {
   makeRemoteCall(); //...
}

@์„œํ‚ท๋ธŒ๋ ˆ์ด์ปค

์Šค๋งˆํŠธํ•œ ์žฅ์•  ์ฒ˜๋ฆฌ ๋ฐ ์žฅ์•  ๋ณต๊ตฌ

@CircuitBreaker(delay=500   // milliseconds
   failureRatio = .75,
   requestVolumeThreshold = 20,
   successThreshold = 5)
@Fallback(fallbackMethod = "fallback")
public String getSalutation() {
   makeRemoteCall(); //...
}

@๋Œ€์ฒด

์‹คํŒจ ์‹œ ๋Œ€์ฒด ๋กœ์ง ํ˜ธ์ถœ

@Timeout(500) // milliseconds
@Fallback(fallbackMethod = "fallback")
public String getSalutation() {
   makeRemoteCall(); //...
}

public String fallback() {
   return "hello";
}

์žฌ์‹œ๋„

์š”์ฒญ ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„

@Retry(maxRetries=3)
public String getSalutation() {
   makeRemoteCall(); //...
}

์‹œ๊ฐ„ ์ดˆ๊ณผ

์‹คํŒจ ์ œ์–ด ์‹œ๊ฐ„ ์ดˆ๊ณผ

@Timeout(value = 500 )   // milliseconds
@Fallback(fallbackMethod = "fallback")
public String getSalutation() {
   makeRemoteCall(); //...
}

์„œ๋น„์Šค ํ™•์ธ(์„œ๋น„์Šค ์ƒํƒœ)

Kubernetes ํ”Œ๋žซํผ์€ ํŠน์ˆ˜ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปจํ…Œ์ด๋„ˆ์˜ ์ƒํƒœ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ํ”Œ๋žซํผ์ด ์„œ๋น„์Šค๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด Spring ๊ฐœ๋ฐœ์ž๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ์ž ์ •์˜ HealthIndicator ๋ฐ Spring Boot Actuator๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. Quarkus์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑ ์ƒํƒœ ํ™•์ธ์„ ์ˆ˜ํ–‰ํ•˜์ง€๋งŒ ํ™œ์„ฑ ์ƒํƒœ์™€ ์ค€๋น„ ์ƒํƒœ๋ฅผ ๋™์‹œ์— ํ™•์ธํ•˜๋„๋ก ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” MicroProfile Health๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€์›๋˜๋Š” MicroProfile Health API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์˜ ์˜ˆ๋Š” ํ‘œ 6์— ์ œ๊ณต๋˜์–ด ์žˆ์œผ๋ฉฐ ์ถ”๊ฐ€ ์ •๋ณด๋Š” Quarkus ๋งค๋‰ด์–ผ์— ์ œ๊ณต๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฑด๊ฐ• ๊ฐ€์ด๋“œ.

ํ‘œ 6: ์ง€์›๋˜๋Š” MicroProfile Health API์˜ ์‚ฌ์šฉ ์˜ˆ.

MicroProfile ์ƒํƒœ ๊ธฐ๋Šฅ
๊ธฐ์ˆ 
์˜ˆ

@ํ™œ์„ฑํ™”

ํ”Œ๋žซํผ์€ ์‹คํŒจํ•œ ์ปจํ…Œ์ด๋„ˆํ™”๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ๋ถ€ํŒ…ํ•ฉ๋‹ˆ๋‹ค.
์ข…์  :
ํ˜ธ์ŠคํŠธ:8080/๊ฑด๊ฐ•/๋ผ์ด๋ธŒ

@Liveness
public class MyHC implements HealthCheck {
  public HealthCheckResponse call() {

   ...
   return HealthCheckResponse
     .named("myHCProbe")
     .status(ready ? true:false)
     .withData("mydata", data)
     .build();  
}

@์ค€๋น„

ํ”Œ๋žซํผ์€ ์ค€๋น„๊ฐ€ ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ปจํ…Œ์ด๋„ˆํ™”๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํŠธ๋ž˜ํ”ฝ์„ ๋ณด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
์ข…์  :
ํ˜ธ์ŠคํŠธ:8080/๊ฑด๊ฐ•/์ค€๋น„

@Readiness
public class MyHC implements HealthCheck {
  public HealthCheckResponse call() {

   ...
   return HealthCheckResponse
     .named("myHCProbe")
     .status(live ? true:false)
     .withData("mydata", data)
     .build();  
}

์ธก์ •ํ•ญ๋ชฉ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์šด์˜ ๋ชฉ์ (์„ฑ๋Šฅ SLA ๋ชจ๋‹ˆํ„ฐ๋ง) ๋˜๋Š” ๋น„์šด์˜ ๋ชฉ์ (๋น„์ฆˆ๋‹ˆ์Šค SLA)์— ๋Œ€ํ•œ ์ง€ํ‘œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Spring ๊ฐœ๋ฐœ์ž๋Š” Spring Boot Actuator ๋ฐ Micrometer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”ํŠธ๋ฆญ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ Quarkus๋Š” MicroProfile Metrics๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ ๋ฉ”ํŠธ๋ฆญ(JVM ๋ฐ ์šด์˜ ์ฒด์ œ), ๋ฒค๋” ๋ฉ”ํŠธ๋ฆญ(Quarkus) ๋ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฉ”ํŠธ๋ฆญ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. MicroProfile Metrics์—์„œ๋Š” ๊ตฌํ˜„ ์‹œ JSON ๋ฐ OpenMetrics(Prometheus) ์ถœ๋ ฅ ํ˜•์‹์„ ์ง€์›ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. MicroProfile Metrics API ์‚ฌ์šฉ ์˜ˆ๋Š” ํ‘œ 7์— ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

ะ’ ์šฐ๋ฆฌ ์˜ˆ์˜ ํ”„๋กœ์ ํŠธ MicroProfile ์ง€ํ‘œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ง€ํ‘œ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Quarkus ๋งค๋‰ด์–ผ์„ ์ฐธ์กฐํ•˜์„ธ์š”. ์ธก์ •ํ•ญ๋ชฉ ๊ฐ€์ด๋“œ.

ํ‘œ 7. MicroProfile Metrics API ์‚ฌ์šฉ ์˜ˆ.

MicroProfile ์ง€ํ‘œ ๊ธฐ๋Šฅ
๊ธฐ์ˆ 
์˜ˆ

@๊ณ„์‚ฐ๋จ

์ฃผ์„์ด ๋‹ฌ๋ฆฐ ๊ฐ์ฒด๊ฐ€ ํ˜ธ์ถœ๋œ ํšŸ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ์นด์šดํ„ฐ ์นด์šดํ„ฐ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

@Counted(name = "fallbackCounter", 
  displayName = "Fallback Counter", 
  description = "Fallback Counter")
public String salutationFallback() {
   return fallbackSalutation;
}

@ConcurrentGauge

์ฃผ์„์ด ๋‹ฌ๋ฆฐ ๊ฐ์ฒด์— ๋Œ€ํ•œ ๋™์‹œ ํ˜ธ์ถœ ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒŒ์ด์ง€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

@ConcurrentGuage(
  name = "fallbackConcurrentGauge", 
  displayName="Fallback Concurrent", 
  description="Fallback Concurrent")
public String salutationFallback() {
   return fallbackSalutation;
}

@๊ณ„๋Ÿ‰๊ธฐ

์ฃผ์„์ด ๋‹ฌ๋ฆฐ ๊ฐ์ฒด์˜ ๊ฐ’์„ ์ธก์ •ํ•˜๋Š” ๊ฒŒ์ด์ง€ ์„ผ์„œ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

@Metered(name = "FallbackGauge",
   displayName="Fallback Gauge",
   description="Fallback frequency")
public String salutationFallback() {
   return fallbackSalutation;
}

@์ธก์ •๋จ

์ฃผ์„์ด ๋‹ฌ๋ฆฐ ๊ฐœ์ฒด์˜ ํ˜ธ์ถœ ๋นˆ๋„๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ๋ฏธํ„ฐ ์„ผ์„œ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

@Metered(name = "MeteredFallback",
   displayName="Metered Fallback",
   description="Fallback frequency")
public String salutationFallback() {
   return fallbackSalutation;
}

๋ฉ”ํŠธ๋ฆญ

๋ฉ”ํŠธ๋ฆญ์„ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ์ƒ์„ฑํ•˜๋ผ๋Š” ์š”์ฒญ์ด ์ˆ˜์‹ ๋  ๋•Œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ ์ฃผ์„

@Metric
@Metered(name = "MeteredFallback",
   displayName="Metered Fallback",
   description="Fallback frequency")
public String salutationFallback() {
   return fallbackSalutation;
}

์‹œ๊ฐ„ ์ดˆ๊ณผ ๋œ

์ฃผ์„์ด ๋‹ฌ๋ฆฐ ๊ฐ์ฒด์˜ ์ง€์† ์‹œ๊ฐ„์„ ์ถ”์ ํ•˜๋Š” ํƒ€์ด๋จธ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

@Timed(name = "TimedFallback",
   displayName="Timed Fallback",
   description="Fallback delay")
public String salutationFallback() {
   return fallbackSalutation;
}

์ธก์ •ํ•ญ๋ชฉ ๋์ 

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธก์ •ํ•ญ๋ชฉ ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ:8080/๋ฉ”ํŠธ๋ฆญ/์‘์šฉ ํ”„๋กœ๊ทธ๋žจ
๊ธฐ๋ณธ ์ธก์ •ํ•ญ๋ชฉ ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ:8080/๋ฉ”ํŠธ๋ฆญ/๋ฒ ์ด์Šค
๊ณต๊ธ‰์—…์ฒด ์ง€ํ‘œ ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ:8080/๋ฉ”ํŠธ๋ฆญ/๊ณต๊ธ‰์—…์ฒด
๋ชจ๋“  ์ง€ํ‘œ ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ:8080/๋ฏธํ„ฐ

MicroProfile Rest ํด๋ผ์ด์–ธํŠธ

๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋Š” ์ข…์ข… ํ•ด๋‹น ํด๋ผ์ด์–ธํŠธ API๊ฐ€ ์ž‘๋™ํ•ด์•ผ ํ•˜๋Š” RESTful ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. RESTful ๋์ ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Spring ๊ฐœ๋ฐœ์ž๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ RestTemplate์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. Quarkus๋Š” ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด MicroProfile Rest Client API๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ์‚ฌ์šฉ ์˜ˆ๋Š” ํ‘œ 8์— ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

ะ’ ์šฐ๋ฆฌ ์˜ˆ์˜ ํ”„๋กœ์ ํŠธ RESTful ์—”๋“œํฌ์ธํŠธ ์‚ฌ์šฉ์€ MicroProfile Rest Client๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด ์ฃผ์ œ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด์™€ ์˜ˆ๋Š” Quarkus ๋งค๋‰ด์–ผ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋จธ์ง€ ํด๋ผ์ด์–ธํŠธ ๊ฐ€์ด๋“œ.

ํ‘œ 8. MicroProfile Rest ํด๋ผ์ด์–ธํŠธ API ์‚ฌ์šฉ ์˜ˆ

MicroProfile Rest ํด๋ผ์ด์–ธํŠธ ๊ธฐ๋Šฅ
๊ธฐ์ˆ 
์˜ˆ

@RegisterRestClient

ํ˜•์‹ํ™”๋œ Java ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ REST ํด๋ผ์ด์–ธํŠธ๋กœ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

@RegisterRestClient
@Path("/")
public interface MyRestClient {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getSalutation();
}

@RestClient

ํ˜•์‹ํ™”๋œ REST ํด๋ผ์ด์–ธํŠธ ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ธ์Šคํ„ด์Šค ๊ตฌํ˜„์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

@Autowired // or @Inject
@RestClient
MyRestClient restClient;

๊ธฐ๋„

REST ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

System.out.println(
   restClient.getSalutation());

mp-ํœด์‹/URL

REST ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

application.properties:
org.example.MyRestClient/mp-rest/url=
   http://localhost:8081/myendpoint

๊ฒฐ๊ณผ

์ฃผ๋กœ Spring ๊ฐœ๋ฐœ์ž๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•œ ์ด ๋ธ”๋กœ๊ทธ์—์„œ๋Š” Quarkus์˜ MicroProfile API์™€ ํ•จ๊ป˜ Spring API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Java ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•œ ๋‹ค์Œ ์ด๋ฅผ ์ˆ˜๋ฐฑ ๋ฉ”๊ฐ€๋ฐ”์ดํŠธ์˜ RAM์„ ์ ˆ์•ฝํ•˜๊ณ  ๋ช‡ ๋ฐ€๋ฆฌ์ดˆ์˜ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

์ด๋ฏธ ์ดํ•ดํ•˜์…จ๋“ฏ์ด Spring ๋ฐ MicroProfile API ์ง€์›์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด์™€ ๊ธฐํƒ€ ์œ ์šฉํ•œ ์ •๋ณด๋Š” ๋‹ค์Œ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Quarkus ๋งค๋‰ด์–ผ.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€