Quarkus(쿼커스) 소개: 나는 클라우드 네이티브를 위해 태어났다

클라우드 네이티브와 마이크로 서비스 아키텍처(MSA)가 주류가 된 지금, 자바는 여전히 ‘무겁고 느리다’는 편견이 있는 것은 사실이다. Quarkus는 이러한 편견을 정면으로 돌파하며 “Supersonic Subatomic Java“라는 슬로건과 함께 등장했다. 이번 포스팅에서는 Quarkus가 무엇인지 어떻게 사용할 수 있는지에 대해서 간단히 소개하고자 한다.

Quarkus가 뭐에요?

Quarkus는 Red Hat에서 주도하여 개발한 오픈 소스 자바 프레임워크로 쿠버네티스 혹은 서버리스 환경에 최적화된 스택을 지향한다.
Spring Boot와 같은 전통적인 자바 프레임워크는 애플리케이션이 실행되는 런타임 시점에 클래스를 스캔하고 빈(Bean)을 생성하며 설정을 적용한다. 이 과정은 메모리를 많이 소모하고 부팅 속도를 늦추는 원인이 된다. 우리가 간단한 REST 서버를 기동할 때도 최소 몇초의 기동 시간이 소요되는 것은 초기 런타임에 이러한 과정을 거치기 때문이다.

Quarkus는 이 패러다임을 완전히 바꿧다. “Build-time First” 전략을 사용하여 런타임에 하던 작업의 80%이상을 빌드 시점에 미리 처리를 한다. 그 결과 우리는 자바로도 Go 언어나 Node.js에 버금가는 가벼운 애플리케이션을 만들 수 있다.

quarkus 빌드 타임
출처: https://quarkus.io/performance/

그림에서 보듯이 Quarkus의 기본 컨셉은 ‘빌드 타임에 할 수 있는 것은 미리 다 해버리자’다. 런타임에 수행 하는 것들을 빌드 타임에 미리 수행해 놓음으로써 런타임 초기에 발생하는 소모 시간을 대폭 줄이도록 한 것이다.

JAX-RS: Quarkus의 핵심 커뮤니케이션 도구

Quarkus에서 RESTful API를 개발할 때 가장 먼저 마주하게 되는 것이 바로 JAX-RS(Jakarta RESTful Web Service) 인데 Spring의 @RestController에 익숙하겠지만, Quarkus는 자바 표준 규격인 JAX-RS를 기본으로 사용한다.
JAX-RS에 대한 상세 내용은 https://jakarta.ee/specifications/ 에서 확인하면 도움이 될 것 같다.
JAX-RS 표준을 따르기 때문에 특정 밴더에 종속되지 않고 오랜 시간 검증된 안정적인 API 구성을 제공한다는데 장점이 있다.

예를 들어 REST API를 위한 기본적인 어노테이션은 다음과 같다.

  • 경로 및 메서드 정의
    • @Path: 리소스의 URI를 결정한다.
      • 클래스 상단에 @Path(“/items”)라고 선언하면 해당 클래스 내부의 모든 메서드는 /items 로 시작하는 경로를 가진다.
        Spring의 클래스에 @RequestMapping(“/items”)를 지정한 것과 동일하다.
    • @GET, @POST, @PUT, @DELETE, @PATCH: HTTP 메서드를 명시한다.
      • 클라이언트가 어떤 의도로 요청을 보내는지 (조회, 생성, 수정, 삭제)를 명시한다.
  • 데이터 형식 지정
    • @Produces: 서버가 클라이언트에게 응답할 데이터 타입을 정의한다.
      • @Produces(MediaType.APPLICATION_JSON)과 같이 application/json 응답 형식을 지정하는 역할을 한다.
    • @Consumes: 서버가 클라이언트로부터 받을 데이터 타입을 정의한다.
      • @Consumes(MediaType.APPLICATION_JSON)과 같이 요청 payload를 application/json으로 받겠다는 형식을 지정한다.
  • 파라미터 핸들링 (Data Injection)
    • @PathParam: URL 경로에 포함된 변수값을 가져온다. Spring의 @PathVariable과 동일
      • 경로: /users/{id} -> 메서드: public User get(@PathParam(“id”) Long id)
    • @QueryParam: URL뒤의 쿼리 스트링을 가져온다.
      • 경로: /search?name=kim -> 메서드 public List<User> search(@QueryParam(“name”) String name)
    • @HeaderParam: HTTP 요청 헤더에 담긴 특정 값을 가져온다.
    • @Context: HTTP 요청 자체에 대한 정보(UriInfo, HttpHeaders등)를 주입받을 때 사용한다.

Quarkus Dev Mode

자바 개발자들의 가장 큰 고충 중 하나는 ‘수정 후 재시작 기다리기‘다. 코드 한 줄 고치고 10~20초씩 기다리는 것은 지루한 일이 아닐 수 없다. Quarkus의 Dev Mode는 이러한 불편함을 완전히 해결한다. (물론 개발 환경에서만 사용한다.)

Dev Mode란?

./mvnw quarkus:dev
Plaintext

명령어로 실행되는 이 모드는 애플리케이션을 실행 중인 상태에서 코드를 수정하면 서버 재시작 없이 즉시 반영되는 기능을 제공한다.

작동 원리

단순히 코드를 교체하는 수준이 아닌 Quarkus Dev Mode의 내부 동작은 다음과 같은 메커니즘을 따른다.

  1. 요청 가로채키 (Request Interception)
    • 사용자가 코드를 수정하고 브라우저에서 ‘새로고침’을 누르면 Quarkus는 요청을 잠시 멈춘다. (Pause)
  2. 변경 사항 스캔
    • 마지막 요청 이후 소스 파일 (.java, .properties 등)에 변화가 있는지 확인한다.
  3. 증분 컴파일: 변경된 파일만 골라내서 컴파일을 수행한다.
  4. 격리된 클래스로더 재시작
    • Quarkus는 애플리케이션의 상태를 관리하는 부분과 프레임워크 코어를 분리해 두었다. 변경된 클래스를 포함한 사용자 영역의 클래스로더만 순식간에 재시작한다.
  5. 요청 재개
    • 모든 준비가 끝나면 멈췄던 HTTP 요청을 다시 진행한다.

Dev Mode를 통해 개발 환경에서 빠르게 수정된 내용을 반영할 수 있는 이점이 있다.

Quarkus의 차별점

  1. Static Analysis vs Dynamic Reflection
    • 전통적인 프레임워크는 실행 시점에 리플렉션을 통해 클래스를 분석한다. 이는 CPU와 메모리를 많이 사용하게 되는데 Quarkus는 빌드 시점에 클래스 계층 구조를 미리 분석하고 필요한 프록시 객체를 미리 생성해 둔다. 덕분에 런타임에는 아무런 부하 없이 바로 로직을 수행할 수 있다.
  2. Native Image (GraalVM)
    • Quarkus는 GraalVM과 완벽히 호환된다. 자바 코드를 JVM위에서 돌아가는 바이트코드가 아닌 OS에서 직접 실행되는 네이티브 실행 파일(Binary)로 컴파일 할 수 있다.

Quarkus Extension

Spring Boot에는 모듈별 디펜던시를 관리하는 starter가 제공되는데 Quarkus도 Extension을 통해서 이와같은 기능을 제공한다.
Quarkus의 Extension은 Spring Boot Starter와 마찬가지로 특정 기능을 구현하는데 필요한 모든 라이브러리와 설정을 하나로 묶어놓은 단위다. 하지만 단순히 디펜던시의 묶음을 넘어서서 중요한 역할을 수행한다.

Spring Boot Starter vs Quarkus Extension

Sring Boot StarterQuarkus Extension
명칭StarterExtension
주요 역할의존성 관리 + 자동 설정 (Auto Config)의존성 관리 + 빌드 타임 최적화
실행 시점애플리케이션 실행시 스캔애플리케이션 빌드시 분석
설정 방식application.properties / ymlapplication.properties / yml

가장 큰 차이점은 언제 일을 하는가 이다. Spring Boot Starter는 서버가 실행될 때 설정을 읽고 빈을 생성하지만, Quarkus Extesion은 코드를 빌드하는 시점에 이미 설정을 다 읽어서 최적화된 바이트코드를 생성한다.

자주 사용하는 Extension 매핑 (Spring vs Quarkus)

Spring Boot에서 대표적으로 사용했던 starter와 매핑되는 Quarkus Extension을 정리해 보았다.

Spring Boot StarterQuarkus Extension
REST APIspring-boot-starter-webquarkus-reateasy-jackson
데이터베이스 (JPA)spring-boot-starter-data-jpaquarkus-hibernate-orm-panache
보안 (security)spring-boot-starter-securityquarkus-security / quarkus-oidc
유효성 검증spring-boot-starter-validationquarkus-hibernate-validator
메시징 (kafka)spring-cloud-starter-stream-kafkaquarkus-messaging-kafka
모니터링spring-boot-starter-actuatorquarkus-smallrye-health / metrics

Extension을 찾고 추가하는 방법

Quarkus는 개발자가 Extension을 쉽고 직관적으로 관리할 수 있도록 다양한 도구를 제공한다.

  • 웹 UI 활용
    • Spring의 start.spring.io와 같이 code.quarkus.io를 제공한다. 원하는 기능을 검색해서 체크만 하면 프로젝트 구조와 필요한 Extension이 포함된 압축 파일을 생성해 준다.
quarkus 프로젝트 생성 화면
code.quarkus.io 프로젝트 생성 화면
  • CLI 명령 활용
    • Quarkus의 강력한 도구인 quarkus CLI를 활용하면 개발 도중에 바로 Extension을 추가할 수 있다.
$> quarkus extension add hibernate-orm-panache jdbc-postgresql
Bash

위 명령어를 입력하면 pom.xml 이나 build.gradle에 자동으로 의존성이 추가된다. 수동으로 버전을 맞출 필요가 없어서 매우 편리하다.
quarkus CLI는 SKDMAN을 이용하여 쉽게 설치할 수 있다.
SDKMAN 관련 내용은 SDKMAN으로 개발도구 버전 쉽게 관리하자 포스팅을 참고하면 도움이 될 것 같다.

# 사용 가능한 버전을 확인
$> sdk list quarkus

# quarkus 3.30.4 설치
$> sdk install quarkus 3.30.4
Downloading: quarkus 3.30.4

In progress...

#################################################################################################################################### 100.0%

Installing: quarkus 3.30.4
Done installing!


Setting quarkus 3.30.4 as default.
Bash
  • Maven/Gradle 명령 활용
    • CLI를 설치하지 않았더라도 Maven 명령어로 사용 가능한 목록을 확인할 수 있다.
$> ./mvnw quarkus:list-extensions
Bash
$> ./gradlew listExtensions
Bash

목록 리스트에 보면 별표(*)가 있는 것과 없는 것들이 있는데 별표가 있는 것은 Quarkus 커뮤니티나 Red Hat에서 공식적으로 권장하거나 안정적으로 관리하고 있는 Extension이다. 운영 환경에서 사용하기 적합하며 문서화가 잘 되어 있고 Quarkus의 최신 버전과도 호환성이 높다. 별표가 없는 경우는 비교적 최신이거나 실험적인 단계 또는 서드파티에서 제공하는 기능일 수 있다.

Quarkus Extension 공식 문서는 어디에 있나?

Quarkus의 장점 중 하나는 Spring과 마찬가지로 문서화가 매우 잘 되어 있다는 점이다. 하지만 확장이 워낙 많다 보니 “내가 지금 설치한 이 확장의 사용법은 어디에 있지” 라고 헤맬 수 있으니 문서를 찾는 방법을 살펴보자.

  • 가장 직관적인 방법: code.quarkus.io
    • 프로젝트를 처음 만들 때 사용하는 code.quarkus.io에서 쉽게 찾아볼 수 있다.
    • 검색 창에 원하는 extension을 검색 후 우측의 아래 화살표 모양을 클릭하면 ‘See the extension guide’를 볼 수 있다.
code.quarkus.io 에서 quarkus extension guide 문서 찾기
code.quarkus.io에서 Extension 문서 찾기
  • Quarkus 공식 가이드 페이지
  • 개발 중에 확인 (Dev UI)
    • Dev Mode가 실행 중이라면 웹 브라우저에서 해당 프로젝트에서 사용하고 있는 확장의 문서 링크를 바로 확인할 수 있다.
      • http://localhost:8080/q/dev/ 접속 (포트는 해당 애플리케이션의 포트 정보를 입력한다)
Dev Mode에서 애플리케이션이 사용중인 Extension 확인 및 가이드 문서 찾기
Dev UI에서 사용중인 Extension 확인


Quarkus Extension은 단순히 라이브러리만 가져오는게 아니다. 각 Extension에는 Deployment 모듈이 포함되어 있어 빌드 단계에서 다음과 같은 작업을 수행한다.

  • 리플렉션 제거: 런타임에 클래스를 동적으로 찾는 대신 빌드 시점에 미리 연결을 끝낸다.
  • 프록시 생성: 하이버네이트나 의존성 주입을 위한 프록시 객체를 빌드 시점에 미리 구워낸다.
  • 불필요한 코드 제거: 설정에서 사용하지 않는 것으로 판단된 기능은 최종 바이너리에서 제외하여 크기를 줄인다.

Quarkus도 자동 구성을 제공하나?

Spring Boot의 가장 큰 매력은 spring boot starter 의존성 추가로 관련 모듈에 대한 빈 생성이나 properties를 통한 여러가지 구성들을 자동으로 해주는 Auto Configuration 제공에 있다. Quarkus 역시 개발자가 별도의 복잡한 설정을 하지 않아도 확장을 추가하는 것만으로 자동 구성 기능이 동작한다. 하지만 내부 동작 방식은 Spring Boot와는 다른 점을 보이는데 그 차이를 짚어보자.

Spring Boot vs Quarkus Auto Configuration

Spring Boot와 Quarkus의 자동 구성 동작 방식을 살펴보자.

Spring Boot: 런타임 자동 설정

Spring Boot는 애플리케이션이 실행되는 시점에 다음과 같은 일을 수행한다.

  1. 클래스패스 스캔: 현재 로드된 라이브러리 중에 무엇이 있는지 확인한다.
  2. 조건부 평가(@Conditional): 클래스패스에 MySQL 드라이버가 있나? DataSource 빈이 아직 등록 안됐나? 등을 체크한다.
  3. 빈(Bean) 등록: 조건이 맞으면 그제야 관련된 설정 클래스를 실행하여 빈을 생성하고 의존성을 주입한다.

이 모든 과정이 애플리케이션이 구동되는 시점에 일어나기 때문에 라이브러리가 많아질수록 부팅되는 속도가 느려지고 메모리 사용량이 늘어난다.

Quarkus: 빌드 타임 확장

Quarkus는 Spring의 @EnableAutoConfiguration 같은 개념이 런타임에 존재하지 않는다. 대신 Augmentation(증강) 이라는 빌드 단계에서 모든 자동 설정이 끝난다.

  1. 빌드 시점 분석: 프로젝트를 빌드(Maven/Gradle)할 때 Quarkus Extension은 이미 어떤 설정이 필요한지 분석을 시작한다.
  2. 배포 기록: 런타임에 실행되어야 할 설정 로직(예: DB 커넥션 풀 생성 코드)을 분석하여 이를 최적화된 바이트코드로 미리 생성한다.
  3. 데드 코드 제거: 사용되지 않는 설정이나 경로는 빌드 단계에서 아예 삭제하여 최종 바이너리에 포함하지 않는다.

결과적으로 서버가 구동 될 때 “어떤 설정을 해야 하지?”라고 물어보지 않는다. 이미 빌드 때 이런 고민을 해결했기 때문이다. “최적화된 실행 계획”을 그대로 수행하면 되기 때문에 부팅이 순식간에 끝난다.

설정의 이원화: Build-time vs Runtime properties

동작 방식이 다르다 보니 설정 파일(application.properties (yml))을 다루는 방식에도 차이가 있다. Quarkus는 설정을 두 가지로 엄격하게 구분한다.

  1. 빌드 타임 고정 설정 (Build-time fixed properties)
    • 빌드 할 때만 유효하며, 한 번 빌드되면 런타임에 바꿀 수 없다.
    • 예시: 사용할 DB 종류, 바이트코드 최적화 옵션 등
  2. 런타임 오버라이드 설정 (Runtime Override properties)
    • 서버를 실행할 때 환경 변수나 커맨드 라인 인자로 바꿀 수 있다.
    • 예시: DB 접속 주소 (JDBC URL), 비밀번호, 로그레벨 등

Spring Boot는 모든 설정을 실행 시점에 바꿀 수 있지만, Quarkus는 성능 최적화를 위해 일부 설정을 빌드 시점에 고정한다는 점이 가장 큰 차이점이라고 할 수 있겠다.

Spring BootQuarkus
핵심 메커니즘리플렉션과 조건부 어노테이션 스캔빌드 타임 바이트코드 생성
실행 시점애플리케이션 구동 시애플리케이션 빌드 시
유연성매우 높음 (실행 중 모든 설정 변경 가능)높음(단, 빌드시 고정되는 설정 존재)
성능 영향starter가 많을 수록 부팅 지연extension이 많아도 부팅 속도에 영향 미미

Spring Boot나 Quarkus에서 제공하는 자동 설정에 대한 편리함을 두 프레임워크가 비슷하다. 하지만 운영 환경에서의 차이는 무시할 수 없다. 클라우드 네이티브, 특히 요청이 올 때만 서버를 띄우는 서버리스(Serverless) 환경에서 자바 애플리케이션이 갖는 Code Start 문제를 해결하는데 있어서 Quarkus의 이러한 방식이 압도적인 우위를 차지한다.


최근에 Quarkus에 대한 관심을 갖게 되면서 MSA 환경이나 AWS 람다와 같은 Serverless 환경에서 사용하면 좋을 것 같다는 생각을 하게 되었다. 이번 포스팅은 Quarkus에 대한 간단한 소개와 함께 몇가지 특징들을 살펴봤는데 앞으로 차근차근 학습한 내용들을 토대로 포스팅을 이어나갈 예정이다.

Quarkus 맛보기를 원한다면 아래 링크를 참고하기 바란다.

https://quarkus.io/get-started
https://quarkus.io/guides/cli-tooling

참고하면 좋을 링크

https://quarkus.io/guides/
https://code.quarkus.io/