Spring Boot가 자바 프레임워크의 사실상 표준인 상황에서, 왜 굳이 다른 프레임워크를 고려해야 할까? 숫자로 보면 이야기가 달라진다.

한 줄 비교

  • Spring Boot: 런타임에 유연하게 구성하는 "범용 프레임워크"
  • Quarkus: 빌드 타임에 최적화를 끝내는 "클라우드 네이티브 프레임워크"

같은 자바이지만 철학이 다릅니다. 어떤 게 "더 좋다"가 아니라, ** 어떤 환경에 더 적합한가 **의 문제입니다.


벤치마크: 숫자로 보는 차이

동일한 REST API + JPA 기반 CRUD 애플리케이션으로 비교한 벤치마크 결과입니다. 환경이나 버전에 따라 수치는 달라질 수 있지만, 대체적인 경향은 일관됩니다.

시작 속도

모드QuarkusSpring Boot차이
JVM1.154s1.909sQuarkus 40% 빠름
Native0.049s0.104sQuarkus 53% 빠름

JVM 모드에서도 Quarkus가 빠르지만, 네이티브 이미지에서는 49ms vs 104ms 로 차이가 더 극적입니다. 둘 다 밀리초 단위이긴 하지만, 서버리스 환경에서 콜드 스타트 50ms는 의미 있는 차이입니다.

메모리 사용량

모드QuarkusSpring Boot차이
JVM (RSS)~130MB~260MBQuarkus 50% 수준
Native (RSS)~35MB~70MBQuarkus 50% 수준

Kubernetes에서 Pod 하나에 256MB 메모리 제한을 걸어두면, Spring Boot Native는 빠듯하지만 Quarkus Native는 여유가 있습니다.

TPS (Throughput)

시나리오QuarkusSpring Boot차이
단순 JSON 응답약 68,000 req/s약 25,000 req/sQuarkus 2.7배
DB 조회 포함약 12,000 req/s약 8,500 req/sQuarkus 1.4배

DB I/O가 포함되면 차이가 줄어드는 건 당연합니다. 병목이 프레임워크가 아니라 DB에 있기 때문입니다.


반직관적인 결과: 장기 실행에서는 Spring Boot가 앞선다

여기서 공부하다 보니 정말 흥미로운 지점을 발견했습니다.

30분 이상의 장기 부하 테스트에서는 Spring Boot(JVM)이 Quarkus(JVM)보다 약 12% 높은 처리량을 보였습니다.

이유는 JIT(Just-In-Time) 컴파일러 때문입니다.

PLAINTEXT
[시간에 따른 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"가 가장 단순한 판단 기준입니다.


개발 경험 비교

성능 외에 실제 개발 경험도 중요합니다.

프로젝트 구조

PLAINTEXT
[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 스타일 비교

JAVA
// 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);
    }
}
JAVA
// 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 비교

JAVA
// 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-jpaJpaRepository, 쿼리 메서드
spring-security@Secured, @RolesAllowed
spring-cloud-config-clientSpring Cloud Config 연동
spring-boot-properties@ConfigurationProperties
spring-cache@Cacheable
spring-scheduled@Scheduled
spring-data-restREST Repository
JAVA
// 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 레시피를 활용할 수 있습니다.

XML
<!-- 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
PROPERTIES
# Spring Boot
# application-dev.properties
server.port=8080

# Quarkus
# application.properties
%dev.quarkus.http.port=8080

생태계 규모 비교

솔직하게 짚고 넘어가야 할 부분이 있습니다.

항목Spring BootQuarkus
GitHub 스타78,000+13,000+
Stack Overflow 질문20만+1.5만+
Maven 스타터/확장 수200+ (공식)700+ (공식+커뮤니티)
한국어 자료풍부부족
채용 공고압도적점차 증가 중

Spring의 생태계와 커뮤니티 규모는 여전히 압도적입니다. 공부하다 막히면 Spring은 한국어 자료만으로 해결되는 경우가 많지만, Quarkus는 영어 공식 문서에 의존해야 하는 경우가 많습니다. 이건 현실적으로 고려해야 할 부분입니다.


둘 다 써야 할 수도 있다

실무에서는 "하나만 고른다"는 접근이 항상 맞지는 않습니다.

PLAINTEXT
[마이크로서비스 아키텍처 예시]

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는 "달릴수록 빨라지는 자바"입니다.

댓글 로딩 중...