Quarkus vs Spring Boot — 성능 벤치마크와 선택 기준
Spring Boot가 자바 프레임워크의 사실상 표준인 상황에서, 왜 굳이 다른 프레임워크를 고려해야 할까? 숫자로 보면 이야기가 달라진다.
한 줄 비교
- Spring Boot: 런타임에 유연하게 구성하는 "범용 프레임워크"
- Quarkus: 빌드 타임에 최적화를 끝내는 "클라우드 네이티브 프레임워크"
같은 자바이지만 철학이 다릅니다. 어떤 게 "더 좋다"가 아니라, ** 어떤 환경에 더 적합한가 **의 문제입니다.
벤치마크: 숫자로 보는 차이
동일한 REST API + JPA 기반 CRUD 애플리케이션으로 비교한 벤치마크 결과입니다. 환경이나 버전에 따라 수치는 달라질 수 있지만, 대체적인 경향은 일관됩니다.
시작 속도
| 모드 | Quarkus | Spring Boot | 차이 |
|---|---|---|---|
| JVM | 1.154s | 1.909s | Quarkus 40% 빠름 |
| Native | 0.049s | 0.104s | Quarkus 53% 빠름 |
JVM 모드에서도 Quarkus가 빠르지만, 네이티브 이미지에서는 49ms vs 104ms 로 차이가 더 극적입니다. 둘 다 밀리초 단위이긴 하지만, 서버리스 환경에서 콜드 스타트 50ms는 의미 있는 차이입니다.
메모리 사용량
| 모드 | Quarkus | Spring Boot | 차이 |
|---|---|---|---|
| JVM (RSS) | ~130MB | ~260MB | Quarkus 50% 수준 |
| Native (RSS) | ~35MB | ~70MB | Quarkus 50% 수준 |
Kubernetes에서 Pod 하나에 256MB 메모리 제한을 걸어두면, Spring Boot Native는 빠듯하지만 Quarkus Native는 여유가 있습니다.
TPS (Throughput)
| 시나리오 | Quarkus | Spring Boot | 차이 |
|---|---|---|---|
| 단순 JSON 응답 | 약 68,000 req/s | 약 25,000 req/s | Quarkus 2.7배 |
| DB 조회 포함 | 약 12,000 req/s | 약 8,500 req/s | Quarkus 1.4배 |
DB I/O가 포함되면 차이가 줄어드는 건 당연합니다. 병목이 프레임워크가 아니라 DB에 있기 때문입니다.
반직관적인 결과: 장기 실행에서는 Spring Boot가 앞선다
여기서 공부하다 보니 정말 흥미로운 지점을 발견했습니다.
30분 이상의 장기 부하 테스트에서는 Spring Boot(JVM)이 Quarkus(JVM)보다 약 12% 높은 처리량을 보였습니다.
이유는 JIT(Just-In-Time) 컴파일러 때문입니다.
[시간에 따른 TPS 변화]
TPS
│
│ Spring Boot (JVM)
│ ╱‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ ← JIT 최적화 후 역전
│ ╱
│───╱────────────────────── ← Quarkus (JVM)
│ ╱
│ ╱ Quarkus (Native)
│╱ ────────────────────── ← AOT, JIT 없음
└─────────────────────────── 시간
0 5min 15min 30min+
왜 이런 일이 벌어지는가
- JVM 모드의 Spring Boot: 시작은 느리지만, 실행 중에 JIT 컴파일러가 핫 패스를 최적화하여 점점 빨라짐
- JVM 모드의 Quarkus: 빌드 타임 최적화로 시작은 빠르지만, JIT 최적화 여지가 상대적으로 적음
- **Native 모드 **: AOT 컴파일이므로 JIT가 없음. 시작은 가장 빠르지만 피크 성능은 JVM 모드보다 낮음
이 결과가 말해주는 것은, "항상 Quarkus가 빠르다"는 단순한 결론이 틀렸다 는 것입니다. 워크로드의 성격에 따라 결론이 달라집니다.
선택 기준: 언제 무엇을 쓸 것인가
Quarkus가 유리한 경우
| 시나리오 | 이유 |
|---|---|
| 서버리스/FaaS (Lambda, Cloud Functions) | 콜드 스타트가 핵심. 49ms vs 1.9s |
| ** 마이크로서비스 (많은 인스턴스)** | 인스턴스당 메모리 절약이 비용에 직결 |
| Kubernetes 오토스케일링 | 스케일 아웃 시 빠른 시작이 중요 |
| ** 이벤트 드리븐 아키텍처** | 리액티브 스택이 기본 내장 |
| ** 리소스 제한 환경** | 엣지 컴퓨팅, IoT 게이트웨이 등 |
Spring Boot가 유리한 경우
| 시나리오 | 이유 |
|---|---|
| ** 장기 실행 모놀리스** | JIT 최적화의 이점을 최대한 활용 |
| ** 대규모 팀/기존 프로젝트** | Spring 생태계 경험과 인력 풀이 압도적 |
| ** 복잡한 엔터프라이즈 통합** | Spring Integration, Batch 등 성숙한 모듈 |
| ** 빠른 프로토타이핑** | Spring Initializr + 풍부한 스타터 |
| ** 리플렉션 의존 라이브러리** | Native 제약 없이 자유롭게 사용 |
결론적으로, "콜드 스타트가 중요하면 Quarkus, 장기 실행이 중요하면 Spring Boot"가 가장 단순한 판단 기준입니다.
개발 경험 비교
성능 외에 실제 개발 경험도 중요합니다.
프로젝트 구조
[Spring Boot] [Quarkus]
src/main/java/ src/main/java/
├── controller/ ├── resource/ (JAX-RS)
├── service/ ├── service/
├── repository/ ├── repository/
└── Application.java └── (메인 클래스 불필요)
src/main/resources/ src/main/resources/
└── application.yml └── application.properties
Quarkus는 메인 클래스가 필수가 아닙니다. 프레임워크가 엔트리포인트를 자동으로 생성합니다.
API 스타일 비교
// Spring Boot
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.findById(id));
}
@PostMapping
public ResponseEntity<UserDto> createUser(@RequestBody @Valid CreateUserRequest req) {
UserDto created = userService.create(req);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
}
// Quarkus (JAX-RS)
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
@Inject
UserService userService;
@GET
@Path("/{id}")
public Response getUser(@PathParam("id") Long id) {
return Response.ok(userService.findById(id)).build();
}
@POST
public Response createUser(@Valid CreateUserRequest req) {
UserDto created = userService.create(req);
return Response.status(Status.CREATED).entity(created).build();
}
}
표면적으로는 비슷하지만, Spring은 @RestController + @GetMapping 스타일이고 Quarkus는 JAX-RS 표준(@Path + @GET)을 사용합니다. 어노테이션 이름이 다를 뿐 구조는 거의 동일합니다.
DI 비교
// Spring
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) { // 생성자 주입
this.userRepository = userRepository;
}
}
// Quarkus (CDI)
@ApplicationScoped
public class UserService {
@Inject
UserRepository userRepository; // 필드 주입이 기본
// 생성자 주입도 가능
// UserService(UserRepository userRepository) { ... }
}
Spring 호환 레이어
Quarkus는 Spring에서 넘어오는 개발자를 위해 9개의 Spring 호환 확장 을 제공합니다.
| 확장 | 호환 대상 |
|---|---|
spring-di | @Autowired, @Component, @Service |
spring-web | @RestController, @GetMapping 등 |
spring-data-jpa | JpaRepository, 쿼리 메서드 |
spring-security | @Secured, @RolesAllowed |
spring-cloud-config-client | Spring Cloud Config 연동 |
spring-boot-properties | @ConfigurationProperties |
spring-cache | @Cacheable |
spring-scheduled | @Scheduled |
spring-data-rest | REST Repository |
// Quarkus에서 Spring 어노테이션 그대로 사용
@RestController
@RequestMapping("/api")
public class MyController {
@Autowired
private MyService myService;
@GetMapping("/hello")
public String hello() {
return myService.greet();
}
}
주의할 점이 있습니다. 이 호환 레이어는 **100% 호환이 아닙니다 **. Spring의 핵심 기능은 지원하지만, Spring Boot의 자동 설정(Auto-configuration)이나 Spring AOP의 모든 기능을 지원하지는 않습니다.
마이그레이션 경로
기존 Spring Boot 프로젝트를 Quarkus로 옮기고 싶다면, OpenRewrite 레시피를 활용할 수 있습니다.
<!-- pom.xml에 OpenRewrite 플러그인 추가 -->
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>5.x.x</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.quarkus.MigrateSpringBootToQuarkus</recipe>
</activeRecipes>
</configuration>
</plugin>
OpenRewrite는 코드를 자동으로 변환해주지만, 복잡한 프로젝트에서는 수동 작업이 필요한 부분이 있습니다.
마이그레이션 시 주의사항
- Auto-configuration: Spring Boot의 자동 설정은 Quarkus 확장으로 대체해야 함
- Spring AOP:
@Aspect는 CDI 인터셉터로 변환 필요 - Spring Profiles:
application-{profile}.properties→%{profile}.접두사 - ** 테스트 **:
@SpringBootTest→@QuarkusTest
# Spring Boot
# application-dev.properties
server.port=8080
# Quarkus
# application.properties
%dev.quarkus.http.port=8080
생태계 규모 비교
솔직하게 짚고 넘어가야 할 부분이 있습니다.
| 항목 | Spring Boot | Quarkus |
|---|---|---|
| GitHub 스타 | 78,000+ | 13,000+ |
| Stack Overflow 질문 | 20만+ | 1.5만+ |
| Maven 스타터/확장 수 | 200+ (공식) | 700+ (공식+커뮤니티) |
| 한국어 자료 | 풍부 | 부족 |
| 채용 공고 | 압도적 | 점차 증가 중 |
Spring의 생태계와 커뮤니티 규모는 여전히 압도적입니다. 공부하다 막히면 Spring은 한국어 자료만으로 해결되는 경우가 많지만, Quarkus는 영어 공식 문서에 의존해야 하는 경우가 많습니다. 이건 현실적으로 고려해야 할 부분입니다.
둘 다 써야 할 수도 있다
실무에서는 "하나만 고른다"는 접근이 항상 맞지는 않습니다.
[마이크로서비스 아키텍처 예시]
API Gateway (Spring Cloud Gateway)
│
├── User Service (Spring Boot) ← 복잡한 비즈니스 로직, 장기 실행
├── Auth Service (Quarkus Native) ← 빠른 응답 필요, 상태 없음
├── Notification (Quarkus + Kafka) ← 이벤트 드리븐, 경량
└── Batch Service (Spring Batch) ← Spring Batch 생태계 활용
서비스별로 적합한 기술을 선택하는 것이 마이크로서비스의 장점 중 하나입니다.
정리
최종 선택 기준을 체크리스트로 정리합니다.
Quarkus를 선택하라:
- 서버리스/FaaS 환경이다
- 시작 속도와 메모리가 비용에 직결된다
- Kubernetes 오토스케일링을 적극 활용한다
- 새 프로젝트를 시작한다
Spring Boot를 선택하라:
- 장기 실행 서버이고 피크 성능이 중요하다
- 팀이 Spring에 익숙하다
- Spring 생태계의 특정 기능에 깊이 의존한다
- 한국어 자료와 커뮤니티 지원이 필요하다
기억하기 좋은 한 줄 요약: Quarkus는 "시작이 빠른 자바", Spring Boot는 "달릴수록 빨라지는 자바"입니다.