JVM 언어 생태계 개관

JVM은 자바만의 것이 아닙니다. Kotlin·Scala·Groovy·Clojure 등 여러 언어가 같은 바이트코드로 컴파일되어 하나의 JVM 위에서 동작합니다. JVM 언어가 존재하는 이유, 공유하는 자산, 그리고 대표 언어들의 성격을 조망합니다.

· 8 min read · PALDYN Team

지난 글에서 gRPC를 마지막으로 자바의 네트워킹 지형을 한 바퀴 돌았습니다. 이제 시야를 언어 자체로 옮겨 봅니다. 우리는 줄곧 “자바”를 이야기해 왔지만, 사실 자바가 동작하는 무대인 JVM은 자바만의 것이 아닙니다. 같은 무대 위에서 Kotlin, Scala, Groovy, Clojure 같은 여러 언어가 함께 뛰고 있습니다. 이번 글에서는 이 JVM 언어 생태계 전체를 조망하며, 왜 이런 언어들이 생겨났고 무엇을 공유하는지를 정리합니다.

JVM은 언어 중립적인 무대

핵심을 먼저 짚겠습니다. JVM이 실제로 실행하는 것은 자바 소스 코드가 아니라 .class 바이트코드 입니다. 자바 컴파일러는 자바 소스를 바이트코드로 번역할 뿐입니다. 그렇다면 다른 언어의 컴파일러가 자기 문법의 소스를 같은 바이트코드로 번역한다면 어떨까요? 그 언어도 JVM 위에서 똑같이 실행됩니다.

여러 언어, 하나의 바이트코드, 하나의 JVM

바로 이 지점이 JVM 언어의 출발점입니다. JVM은 특정 문법에 묶이지 않은, 잘 정의된 바이트코드를 실행하는 가상 기계 입니다. 바이트코드라는 공통 목표 지점만 맞추면 어떤 문법의 언어든 이 무대에 올라설 수 있습니다. 그 결과 수십 개의 언어가 JVM을 타깃으로 만들어졌습니다.

무엇을 공유하는가 — 막대한 자산의 상속

새 언어를 JVM 위에 만든다는 것은 단지 “또 하나의 언어”를 추가하는 일이 아닙니다. JVM이 수십 년간 갈고닦은 막대한 자산을 고스란히 물려받는 일입니다. 세계 최고 수준의 가비지 컬렉터, JIT 컴파일러의 런타임 최적화, 정교하게 정의된 메모리 모델과 스레드 모델 — 이 모든 것을 새 언어가 처음부터 다시 만들 필요가 없습니다.

그리고 어쩌면 더 중요한 것이 라이브러리 생태계 입니다. 자바가 25년 넘게 쌓아 온 방대한 라이브러리 — 로깅, 데이터베이스, 웹 프레임워크, 테스트 도구 — 를 JVM 언어들은 대부분 그대로 호출할 수 있습니다. 새 언어가 빠르게 실전에 투입될 수 있는 비결이 여기에 있습니다. 멋진 문법만 있고 쓸 라이브러리가 없는 언어는 살아남기 어렵지만, JVM 언어는 태어날 때부터 거대한 생태계를 등에 업고 시작합니다.

대표 언어들의 성격

JVM 언어들은 같은 무대를 공유하지만 지향점은 제각각입니다. 무엇을 더 중시하느냐에 따라 성격이 갈립니다.

JVM 언어들의 성격 — 무엇을 더 중시하는가

Java 는 안정성과 호환성을 가장 중시합니다. 새 기능을 신중하게 도입하고, 과거 코드와의 호환을 좀처럼 깨지 않으며, 가장 넓은 인력풀과 생태계를 가집니다. 다른 JVM 언어들이 비교 기준으로 삼는 중심 언어입니다.

Kotlin 은 자바의 장황함을 덜어낸 간결하고 실용적인 언어입니다. 널 안전성을 타입 시스템에 녹였고, 자바와의 호환이 매우 매끄러워 한 프로젝트에서 두 언어를 섞어 쓰기 좋습니다. 구글이 안드로이드 공식 언어로 채택하면서 폭넓게 퍼졌습니다.

Scala 는 표현력과 함수형 프로그래밍을 깊이 끌어안은 언어입니다. 강력한 타입 시스템 위에서 객체지향과 함수형을 융합하며, 데이터·빅데이터 분야(예: Spark)에서 널리 쓰입니다. 표현력이 큰 만큼 학습 곡선도 가파릅니다.

이 밖에 동적 타입의 유연함을 더한 Groovy(Gradle 빌드 스크립트로 친숙), 리스프 계열 함수형 언어인 Clojure 등도 같은 무대에서 각자의 자리를 차지하고 있습니다.

한 프로젝트에서 섞어 쓰기

JVM 언어들이 같은 바이트코드로 컴파일된다는 사실은 실용적으로 매우 강력한 결과를 낳습니다. 한 프로젝트 안에서 여러 언어를 섞어 쓸 수 있다 는 점입니다. 핵심 로직은 자바로, 새로 추가하는 모듈은 Kotlin으로, 데이터 처리는 Scala로 작성하고서도 서로의 클래스를 호출할 수 있습니다.

// Kotlin 코드에서 자바 라이브러리를 그대로 호출
import java.time.LocalDate

fun main() {
    val today = LocalDate.now()          // 자바 표준 라이브러리
    val users = listOf("홍길동", "이몽룡")  // Kotlin 컬렉션
    users.forEach { println("$it: $today") }
}

위 Kotlin 코드는 자바의 LocalDate를 아무런 변환 없이 쓰고 있습니다. 컴파일 결과가 같은 바이트코드이므로, 한 언어의 클래스가 다른 언어에게는 그저 또 하나의 클래스로 보일 뿐입니다. 이런 상호운용성 덕분에 기존 자바 프로젝트에 새 언어를 점진적으로 들여오는 일이 현실적으로 가능합니다.

정리

JVM이 실행하는 것은 자바 소스가 아니라 언어 중립적인 바이트코드이며, 그래서 Kotlin·Scala·Groovy·Clojure 같은 여러 언어가 같은 무대 위에서 동작합니다. 이들은 JVM의 GC·JIT·메모리 모델과 방대한 자바 라이브러리 생태계를 그대로 물려받아 빠르게 실전에 투입됩니다. 같은 바이트코드로 컴파일되므로 한 프로젝트에서 여러 언어를 섞어 쓰는 것도 가능합니다. 자바는 안정성, Kotlin은 간결함, Scala는 표현력처럼 각 언어는 서로 다른 가치를 추구합니다. 다음 글에서는 이 가운데 자바와 가장 자주 비교되는 Kotlin을 자바와 나란히 놓고 무엇이 다른지 구체적으로 들여다봅니다.


지난 글: gRPC — 고성능 RPC 프레임워크

다음 글: Java vs Kotlin — 무엇이 다른가


읽어주셔서 감사합니다. 😊