|| Jib → Buildpacks → GraalVM Native Image: 기동 시간 11배 단축기GraalVM Native Image를 도입하는 과정에서 가장 골치 아픈 건 기술 이해가 아니라 반복 작업이었어요. 에러가 나면 원인을 찾고, 메타데이터를 보완하고, 다시 빌드하고 — 이 사이클을 수십 번 돌아야 하거든요. 저희는 이 과정을 AI로 자동화했고, 덕분에 실제로 적용까지 이어질 수 있었어요. 기술 도입기와 AI 활용 노하우, 함께 담았습니다.안녕하세요, service platform 팀입니다
저희 service platform 팀은 광고 플랫폼에서 시작해, 현재는 플랫폼 전반의 푸시 알림, 채팅, 그리고 광고 업무까지 담당하고 있어요. 이번에는 컨테이너 빌드 방식을 개선하다가 자연스럽게 GraalVM Native Image까지 흘러들어가게 된 이야기를 공유하려고 해요.
결론부터 말씀드리면, 이벤트 기반 알림 서비스의 기동 시간이 4.784초 → 0.417초로 11.5배 빨라졌고, OCI 이미지 크기도 709MB → 458MB로 35% 줄었습니다. 그리고 이 결과는 처음부터 Native를 목표로 달려간 게 아니라, 다른 이유로 시작한 작업에서 길이 열렸던 거예요.
이런 분께 도움이 될 것 같아요
JVM 기동 시간이 답답하다고 느끼는 분
GraalVM Native Image, 들어본 적은 있는데 어떻게 쓰는 건지 막막한 분
Spring Boot + Kotlin/Java 서비스에 Native 적용을 검토 중인 분
Jib → Buildpacks 전환을 고민하고 있는 분
시작은 Jib였어요
저희 팀은 원래 Google Jib로 컨테이너 이미지를 빌드하고 있었어요. Jib의 가장 큰 장점은 Docker 데몬이 없어도 된다는 점이에요. Gradle 플러그인 하나만으로 이미지를 만들 수 있고, 레이어 캐시가 잘 맞으면 8초 안에도 빌드가 끝납니다. 꽤 쾌적했어요.
그런데 Gradle 버전을 올리면서 불편한 점들이 보이기 시작했어요.
Gradle Configuration Cache와 맞지 않았어요. Configuration Cache는 빌드 설정 단계를 캐싱해서 반복 빌드 속도를 크게 줄여주는 기능인데요, Jib 관련 Task 를 일일이 호환 불가로 선언해야 했어요.
또 한 가지, Spring Boot 3에서 bootBuildImage라는 표준 빌드 경로가 생겼어요. Spring Boot 공식 플러그인이 제공하는 이 기능은 Buildpacks 기반인데, Jib는 이 경로와 별개로 자체 방식을 쓰고 있었어요. 팀 표준을 맞추는 관점에서도 조금씩 맞지 않는 느낌이었습니다.
그래서 저희는 Buildpacks 기반으로 전환하기로 했어요.
Buildpacks로 넘어오면서
솔직히 말하면, 순수 빌드 속도만 보면 Jib가 더 빨라요. Buildpacks는 표준화된 단계를 더 거치다 보니, 저희 환경 로컬 기준으로는 캐시가 있어도 약 30~40초 수준이었어요. (CI/CD에서는 레지스트리 기반 캐시를 활용할 경우 빌드 시간이 점진적으로 줄어드는 구조입니다.) 그럼에도 전환을 결정한 건 저희 팀 우선순위가 달랐기 때문이에요.
Gradle Configuration Cache 호환
Spring Boot 기본 경로(bootBuildImage) 활용
빌드 표준화 — 팀 내 누가 돌려도 같은 방식
또한 Buildpacks가 단순 빌드 도구가 아니라는 점도 장점이에요. CNCF Cloud Native Buildpacks(CNB) 표준 기반이라 OS 레이어, JVM 런타임, 앱 레이어를 자동으로 구성해 표준화된 이미지를 만들어줘요. Kubernetes나 클라우드 환경과 궁합이 좋고, 동일한 설정을 유지하면 재현 가능한 빌드를 만들 수 있는 것도 강점이에요.
그리고 전환하고 나서 흥미로운 걸 발견했어요. Paketo Buildpacks 등 일부 buildpack에서는 네이티브 빌드를 지원하고 있어서, BP_NATIVE_IMAGE=true 설정을 통해 GraalVM Native Image 빌