프레임워크를 선택할 때 성능만 보면 될까? 하루 8시간 이상 마주하는 개발 환경의 쾌적함이 생산성에 미치는 영향은 벤치마크 숫자보다 클 수도 있다.

Quarkus 개발자 경험의 핵심

Quarkus는 "Developer Joy"를 공식 목표로 내세웁니다. 빌드 타임 최적화만큼이나 개발 중의 경험 을 중요하게 여긴다는 뜻입니다.

핵심 기능 네 가지를 정리하면 다음과 같습니다.

  1. Live Reload — 코드 수정 즉시 반영
  2. DevServices — 외부 서비스(DB, Kafka 등) 자동 프로비저닝
  3. Dev UI — 브라우저에서 앱 상태 확인
  4. Continuous Testing — 코드 변경 시 관련 테스트 자동 실행

Live Reload: 서버 재시작 없는 코드 반영

작동 방식

Quarkus의 Dev Mode(quarkus dev)에서는 코드를 수정하면 ** 다음 HTTP 요청이 들어올 때** 변경사항이 자동으로 반영됩니다.

BASH
# Dev Mode 시작
quarkus dev
# 또는
./mvnw quarkus:dev

중요한 것은 ** 구현 방식의 차이 **입니다.

PLAINTEXT
[Spring Boot DevTools]
코드 수정 → 전체 ApplicationContext 재시작 → 빈 재생성 → 준비 완료
약 2~5초 (프로젝트 크기에 따라 더 걸림)

[Quarkus Dev Mode]
코드 수정 → 변경된 ClassLoader만 교체 → 변경된 부분만 재처리 → 준비 완료
약 0.5~2초 (대부분 1초 이내)

Spring Boot DevTools와의 차이

항목Spring Boot DevToolsQuarkus Dev Mode
방식전체 컨텍스트 재시작ClassLoader 교체
속도2~5초0.5~2초
상태 유지세션 등 일부 유지요청 시점에 반영
정적 리소스별도 처리동일 메커니즘
활성화의존성 추가 필요기본 내장

Spring Boot DevTools는 spring-boot-devtools 의존성을 추가해야 하고, Base ClassLoader와 Restart ClassLoader를 분리하여 Restart ClassLoader만 다시 로드하는 방식입니다. Quarkus는 Dev Mode 자체가 프레임워크에 내장되어 있어서 별도 설정 없이 바로 사용할 수 있습니다.

실제 체감

JAVA
// UserResource.java를 수정하고 저장
@Path("/users")
public class UserResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<UserDto> listUsers() {
        // 이 메서드를 수정하고 저장한 뒤
        // curl localhost:8080/users 를 치면 바로 반영됨
        return userService.findAll();
    }
}

공부하면서 직접 써보니, 수정 후 브라우저를 새로고침하면 바로 반영되는 경험이 프론트엔드의 HMR(Hot Module Replacement)과 비슷했습니다. 자바 백엔드에서 이 정도 속도의 피드백 루프는 꽤 신선합니다.


DevServices: 킬러 기능

DevServices는 Quarkus의 가장 차별화된 기능이라고 생각합니다. 공부하다 보니 이 기능 때문에 Quarkus를 선택하는 개발자도 있다는 걸 알게 되었습니다.

무엇을 해주는가

DB나 메시지 브로커 같은 외부 서비스의 의존성만 추가하면, Docker 컨테이너를 자동으로 시작하고 연결 설정까지 해줍니다.

XML
<!-- pom.xml에 PostgreSQL 의존성만 추가 -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>

이것만 추가하고 quarkus dev를 실행하면:

PLAINTEXT
[Dev Mode 시작 로그]

2026-03-28 10:00:01 INFO  [io.qua.dev.pos.dep] DevServices for PostgreSQL started
  - Container: testcontainers/ryuk:0.5.1
  - PostgreSQL: postgres:16
  - JDBC URL: jdbc:postgresql://localhost:55432/quarkus
  - Username: quarkus
  - Password: quarkus

Quarkus application started in 2.134s

**application.properties에 DB 연결 정보를 한 줄도 적지 않았는데 **, PostgreSQL 컨테이너가 자동으로 시작되고 연결까지 완료됩니다.

지원하는 DevServices

서비스확장자동 시작 컨테이너
PostgreSQLquarkus-jdbc-postgresqlpostgres
MySQLquarkus-jdbc-mysqlmysql
MongoDBquarkus-mongodb-clientmongo
Redisquarkus-redis-clientredis
Kafkaquarkus-smallrye-reactive-messaging-kafkaredpanda
RabbitMQquarkus-smallrye-reactive-messaging-rabbitmqrabbitmq
Elasticsearchquarkus-elasticsearch-rest-clientelasticsearch
Keycloakquarkus-oidckeycloak
Infinispanquarkus-infinispan-clientinfinispan
Apicurio Registryquarkus-apicurio-registry-avroapicurio

기존 방식과의 비교

PLAINTEXT
[기존: 로컬 개발 환경 세팅]
1. docker-compose.yml 작성
2. docker-compose up -d
3. application-local.properties에 연결 정보 작성
4. 프로파일 설정 (-Dspring.profiles.active=local)
5. 애플리케이션 시작
6. 개발 끝나면 docker-compose down

[Quarkus DevServices]
1. 의존성 추가
2. quarkus dev
3. 끝

docker-compose.yml을 작성할 필요도, 연결 정보를 설정할 필요도 없습니다. 팀원이 프로젝트를 클론받고 quarkus dev만 실행하면 모든 외부 서비스가 자동으로 준비됩니다.

DevServices 커스터마이징

기본 설정으로 충분하지 않을 때는 커스터마이징도 가능합니다.

PROPERTIES
# application.properties

# PostgreSQL 버전 지정
quarkus.datasource.devservices.image-name=postgres:15

# 초기 SQL 실행
quarkus.datasource.devservices.init-script-path=init.sql

# 포트 고정 (기본은 랜덤 포트)
quarkus.datasource.devservices.port=5432

# DevServices 비활성화 (이미 DB가 있을 때)
quarkus.datasource.devservices.enabled=false

# Kafka 설정
quarkus.kafka.devservices.image-name=vectorized/redpanda:latest
quarkus.kafka.devservices.topic-partitions.orders=3

팀 개발에서의 장점

DevServices는 개인 개발 환경의 편의성을 넘어서, ** 팀 개발에서의 "환경 차이" 문제를 해결 **합니다.

  • 신규 팀원 온보딩: git clonequarkus dev → 바로 개발 시작
  • 환경 일관성: 모든 팀원이 동일한 DB 버전, 동일한 초기 데이터
  • CI/CD: 테스트 시에도 동일한 메커니즘 사용

Dev UI: 브라우저에서 앱 상태 확인

Dev Mode에서 http://localhost:8080/q/dev-ui로 접속하면 Dev UI 를 볼 수 있습니다.

제공하는 정보

섹션내용
Extensions설치된 확장 목록과 상태
ArC Beans등록된 빈 목록, 스코프, 의존성 그래프
EndpointsREST 엔드포인트 목록
Configuration모든 설정값과 출처
Health헬스 체크 결과
Build Steps빌드 파이프라인 시각화
Continuous Testing테스트 결과와 실행 상태

Spring Actuator와의 비교

PLAINTEXT
[Spring Actuator]
- 프로덕션 모니터링 용도
- /actuator/health, /actuator/metrics 등
- 별도 의존성 추가 필요
- 보안 설정 필요 (프로덕션 노출 주의)

[Quarkus Dev UI]
- 개발 시 디버깅/탐색 용도
- 그래픽 UI로 직관적
- Dev Mode에서만 활성화 (프로덕션에선 자동 비활성화)
- 확장마다 커스텀 페이지 제공

Dev UI는 프로덕션 모니터링이 아니라 ** 개발 중 디버깅 도구 **입니다. 빈이 제대로 등록되었는지, 엔드포인트가 올바르게 매핑되었는지, 설정값이 의도한 대로 적용되었는지를 브라우저에서 바로 확인할 수 있습니다.

유용한 Dev UI 기능들

PLAINTEXT
[ArC Beans 페이지]
- 등록된 빈 수, 제거된 빈 수 확인
- 빈의 의존성 그래프 시각화
- 인터셉터 바인딩 확인

[Configuration 페이지]
- 모든 설정 키와 현재 값
- 설정 출처 (기본값 / application.properties / 환경변수 / 시스템 프로퍼티)
- 설정값 실시간 변경 (일부)

[Endpoints 페이지]
- 모든 REST 엔드포인트 목록
- HTTP 메서드, 경로, 파라미터
- 바로 테스트 호출 가능

Continuous Testing: 코드 변경 시 테스트 자동 실행

Quarkus의 Continuous Testing은 ** 코드를 수정하면 관련된 테스트가 자동으로 실행 **되는 기능입니다.

활성화 방법

Dev Mode에서 콘솔에 다음 메시지가 표시됩니다.

PLAINTEXT
Tests paused
Press [r] to resume testing, [o] Toggle test output, [h] for more options>

r을 누르면 Continuous Testing이 시작됩니다.

동작 방식

PLAINTEXT
[코드 수정 흐름]
UserService.java 수정


Quarkus가 변경된 클래스와 관련된 테스트를 식별

     ├── UserServiceTest.java ← UserService를 직접 테스트
     ├── UserResourceTest.java ← UserService를 간접 사용
     └── OrderServiceTest.java ← 변경과 무관, 실행 안 함


관련 테스트만 자동 실행, 결과를 콘솔과 Dev UI에 표시

** 모든 테스트를 실행하는 것이 아니라, 변경에 영향받는 테스트만 실행 **합니다. 이 점이 단순히 --watch 모드로 전체 테스트를 돌리는 것과 다릅니다.

실행 결과

PLAINTEXT
All 15 tests are passing (2 disabled), 3 tests were run in 1.2s.
Press [r] to re-run, [o] Toggle test output, [v] Toggle debug, [h] for more options>

Continuous Testing 설정

PROPERTIES
# application.properties

# 기본 활성화 (Dev Mode에서)
quarkus.test.continuous-testing=enabled

# 테스트 변경 감지 간격 (ms)
quarkus.test.poll-interval=500

# 특정 태그의 테스트만 실행
quarkus.test.include-tags=unit

# 특정 태그의 테스트 제외
quarkus.test.exclude-tags=integration,slow

# 테스트 실패 시 알림 (IDE 연동)
quarkus.test.display-test-output=true

기존 방식과의 비교

PLAINTEXT
[기존: 수동 테스트 실행]
1. 코드 수정
2. IDE에서 해당 테스트 클래스 찾기
3. 테스트 실행
4. 결과 확인
5. 관련된 다른 테스트도 실행해야 하나 고민
6. 전체 테스트 실행 (시간 오래 걸림)

[Quarkus Continuous Testing]
1. 코드 수정
2. 자동으로 관련 테스트 실행 + 결과 표시

Quarkus CLI

Quarkus는 전용 CLI 도구를 제공합니다.

설치

BASH
# macOS
brew install quarkusio/tap/quarkus

# Linux (SDKMAN)
sdk install quarkus

# Windows (Chocolatey)
choco install quarkus

주요 명령어

BASH
# 프로젝트 생성
quarkus create app com.example:my-app

# 개발 모드 실행
quarkus dev

# 확장 검색
quarkus extension list --installable --search="kafka"

# 확장 추가
quarkus extension add smallrye-reactive-messaging-kafka

# 확장 제거
quarkus extension remove smallrye-reactive-messaging-kafka

# 빌드
quarkus build

# 네이티브 빌드
quarkus build --native

# 정보 확인
quarkus info

Spring Boot CLI와의 비교

BASH
# Spring Boot (Initializr 웹에서 생성하는 경우가 많음)
curl https://start.spring.io/starter.tgz \
  -d dependencies=web,data-jpa,postgresql | tar -xz

# Quarkus CLI
quarkus create app --extension='resteasy-reactive,hibernate-orm-panache,jdbc-postgresql'

Quarkus CLI의 장점은 ** 개발 모드와 통합 **되어 있다는 점입니다. quarkus dev로 시작하면 Live Reload, DevServices, Dev UI, Continuous Testing이 모두 함께 동작합니다.


테스트 작성

Quarkus의 테스트는 @QuarkusTest로 시작합니다.

통합 테스트

JAVA
@QuarkusTest
public class UserResourceTest {

    @Test
    public void testGetUser() {
        given()
            .when().get("/users/1")
            .then()
            .statusCode(200)
            .body("name", is("홍길동"));
    }

    @Test
    public void testCreateUser() {
        given()
            .contentType(ContentType.JSON)
            .body("""
                {
                    "name": "김철수",
                    "email": "kim@example.com"
                }
                """)
            .when().post("/users")
            .then()
            .statusCode(201)
            .body("name", is("김철수"));
    }
}

@QuarkusTest는 실제 Quarkus 애플리케이션을 시작하고 테스트합니다. DevServices가 테스트에서도 동작하므로, ** 테스트용 DB를 별도로 준비할 필요가 없습니다 **.

네이티브 이미지 테스트

JAVA
@QuarkusIntegrationTest
public class UserResourceIT {

    // @QuarkusTest와 동일한 테스트 코드
    // 네이티브 바이너리를 대상으로 실행됨
    @Test
    public void testGetUser() {
        given()
            .when().get("/users/1")
            .then()
            .statusCode(200);
    }
}

@QuarkusIntegrationTest는 빌드된 아티팩트(JAR 또는 네이티브 바이너리)를 실행하고 테스트합니다. 네이티브 빌드에서 리플렉션 문제가 있는지 확인할 때 유용합니다.

모킹

JAVA
@QuarkusTest
public class UserServiceTest {

    @InjectMock
    UserRepository userRepository;  // 모킹된 빈이 주입됨

    @Inject
    UserService userService;  // 실제 빈, 위 모킹된 빈을 사용

    @Test
    public void testFindById() {
        // given
        when(userRepository.findById(1L))
            .thenReturn(Optional.of(new User("홍길동")));

        // when
        UserDto result = userService.findById(1L);

        // then
        assertEquals("홍길동", result.name());
    }
}

개발 워크플로 종합

모든 기능을 조합한 실제 개발 흐름을 그려보면 이렇습니다.

PLAINTEXT
[터미널]
$ quarkus dev

[자동으로 일어나는 일]
1. Quarkus 시작 (1~2초)
2. DevServices: PostgreSQL 컨테이너 자동 시작
3. DevServices: Redis 컨테이너 자동 시작
4. Dev UI 활성화 (localhost:8080/q/dev-ui)
5. Continuous Testing 대기

[개발 중]
1. UserResource.java 수정 + 저장
2. Live Reload: 0.5초 후 변경 반영
3. Continuous Testing: UserResourceTest 자동 실행
4. 브라우저: Dev UI에서 결과 확인
5. API 테스트: curl로 바로 확인

[개발 종료]
$ Ctrl+C
→ Quarkus 종료
→ DevServices 컨테이너 자동 정리

전체 과정에서 ** 수동으로 해야 할 일이 거의 없습니다 **. Docker Compose를 관리하거나, 테스트 DB를 세팅하거나, 서버를 재시작하는 번거로움이 사라집니다.


Spring Boot와의 개발 경험 비교 총정리

기능Spring BootQuarkus
핫 리로드DevTools (재시작)Dev Mode (ClassLoader 교체)
외부 서비스docker-compose 수동 관리DevServices 자동
개발 UIActuator (프로덕션 용도)Dev UI (개발 전용)
연속 테스트없음 (IDE 플러그인 의존)Continuous Testing 내장
CLISpring Initializr 웹Quarkus CLI
설정 변경재시작 필요일부 실시간 반영

Spring Boot도 개발 경험이 나쁜 것은 아닙니다. 하지만 Quarkus는 개발자 경험을 프레임워크의 핵심 기능으로 설계했다는 점에서 차이가 있습니다.


정리

Quarkus의 개발자 경험 기능을 정리하면 다음과 같습니다.

  • Live Reload: ClassLoader 교체 방식으로 Spring DevTools보다 빠른 반영
  • DevServices: DB, Kafka, Redis 등을 의존성 추가만으로 자동 프로비저닝 (가장 인상적인 기능)
  • Dev UI: 브라우저에서 빈, 엔드포인트, 설정, 테스트 결과를 한눈에 확인
  • Continuous Testing: 변경된 코드에 관련된 테스트만 자동 실행
  • Quarkus CLI: 프로젝트 생성부터 확장 관리, 빌드까지 통합

기억할 핵심: DevServices 하나만으로도 로컬 개발 환경 세팅에 들이는 시간을 크게 줄일 수 있습니다. docker-compose.yml 없이 quarkus dev 한 줄이면 모든 외부 서비스가 준비되는 경험은 한 번 써보면 돌아가기 어렵습니다.

댓글 로딩 중...