Java vs Scala — 함수형의 무게
Scala는 객체지향과 함수형을 강력한 타입 시스템 위에서 융합한 JVM 언어입니다. 표현식 중심 스타일, 불변성, 패턴 매칭, 트레이트 같은 핵심 특징을 자바와 비교하고, 표현력과 학습 곡선의 무게를 함께 따져 선택 기준을 정리합니다.
지난 글에서 간결함과 실용성을 앞세운 Kotlin을 자바와 비교했습니다. 같은 JVM 무대에는 또 다른 성격의 강력한 언어가 있습니다. 함수형 프로그래밍을 깊이 끌어안고, 강력한 타입 시스템으로 표현력을 극한까지 밀어붙인 Scala 입니다. Kotlin이 “자바를 더 편하게”를 지향한다면, Scala는 “프로그래밍 패러다임 자체를 다시”에 가깝습니다. 이번 글에서는 Scala를 자바와 비교하며, 그 표현력의 매력과 함께 따라오는 무게를 함께 살펴봅니다.
두 패러다임의 융합
Scala라는 이름은 “scalable language(확장 가능한 언어)“에서 왔지만, 그 정체성을 한마디로 요약하면 객체지향과 함수형의 융합 입니다. 자바가 객체지향을 기반으로 람다·스트림을 통해 함수형을 부분적으로 받아들인 반면, Scala는 두 패러다임을 처음부터 동등하게 설계했습니다.
Scala에서는 모든 값이 객체이면서(순수 객체지향), 동시에 모든 함수가 값입니다(순수 함수형). 클래스와 상속을 쓰다가도 자연스럽게 불변 데이터와 함수 합성으로 넘어갈 수 있습니다. 이 융합 위에 자바보다 훨씬 강력한 타입 시스템이 얹혀 있어, 타입으로 많은 것을 표현하고 강제할 수 있습니다.
표현식 중심 — 모든 것이 값을 낳는다
자바와 Scala의 사고방식 차이를 가장 잘 드러내는 것은 표현식 중심(expression-oriented) 스타일입니다. 자바의 for, if는 값을 만들지 않는 문장(statement)이고, 우리는 가변 변수를 두고 그것을 갱신하며 결과를 쌓아 갑니다. Scala에서는 거의 모든 것이 값을 만들어 내는 표현식입니다.
val sum = nums.filter(_ % 2 == 0)
.map(n => n * n)
.sum
자바 스트림을 써 본 사람이라면 이 모습이 낯설지 않을 것입니다. 실제로 자바 스트림은 Scala 같은 함수형 언어에서 영향을 받았습니다. 차이는 이런 스타일이 Scala에서는 기본값 이라는 점입니다. 가변 변수와 반복문이 예외적이고, 불변 값(val)과 변환의 연쇄가 일상적입니다. if조차 값을 반환하므로 val x = if (cond) a else b처럼 쓸 수 있습니다.
패턴 매칭 — switch를 넘어서
Scala가 자랑하는 또 하나의 무기는 패턴 매칭 입니다. 자바도 최근 switch 패턴 매칭으로 비슷한 방향으로 발전했지만, Scala의 패턴 매칭은 훨씬 오래되었고 강력합니다. 값의 구조를 분해하면서 동시에 분기할 수 있습니다.
def describe(x: Any): String = x match {
case 0 => "영"
case n: Int if n < 0 => "음수"
case s: String => s"문자열: $s"
case (a, b) => s"튜플: $a, $b"
case _ => "기타"
}
타입, 값, 조건(if 가드), 구조 분해를 한 자리에서 표현합니다. 특히 케이스 클래스(case class)와 결합하면 데이터의 형태에 따라 코드를 분기하는 일이 매우 간결해집니다. 이런 패턴 매칭은 함수형 프로그래밍에서 데이터를 다루는 핵심 도구입니다.
트레이트 — 더 유연한 믹스인
자바의 인터페이스가 디폴트 메서드로 일부 구현을 가질 수 있게 되었듯, Scala의 트레이트(trait) 는 그보다 한발 앞서 풍부한 기능을 제공해 왔습니다. 트레이트는 메서드 구현은 물론 상태(필드)까지 가질 수 있고, 한 클래스에 여러 트레이트를 섞어 넣는(mix-in) 다중 상속에 가까운 조합이 가능합니다. 작은 기능 단위를 트레이트로 쪼개고 필요에 따라 조립하는 설계가 Scala에서는 자연스럽습니다.
표현력의 대가 — 무게
여기까지 보면 Scala가 자바보다 모든 면에서 강력해 보입니다. 하지만 그 강력함에는 대가 가 따릅니다. 표현 방법이 많다는 것은 곧 배워야 할 것이 많고, 한 가지 일을 하는 방식이 여러 가지라는 뜻입니다. 강력한 타입 시스템은 때로 난해한 타입 에러 메시지로 돌아오고, 암묵적 변환(implicit) 같은 기능은 잘못 쓰면 코드의 흐름을 추적하기 어렵게 만듭니다.
그래서 Scala는 학습 곡선이 가파른 언어로 꼽힙니다. 팀 전체가 함수형 사고와 Scala의 관용구에 익숙해지기까지 시간이 걸리고, 같은 코드를 두고도 사람마다 스타일이 크게 갈릴 수 있습니다. 표현력이라는 매력과 복잡성이라는 무게는 동전의 양면입니다.
어디서 빛나는가
Scala가 특히 강한 영역은 데이터 처리와 빅데이터 입니다. 분산 데이터 처리 엔진 Apache Spark가 Scala로 작성되었고, 함수형 스타일과 불변성이 대규모 병렬 데이터 변환에 잘 맞기 때문입니다. 복잡한 도메인을 타입으로 정밀하게 모델링하고 싶은 경우, 함수형 프로그래밍을 본격적으로 활용하고 싶은 경우에도 Scala가 매력적입니다. 반대로 팀의 진입 장벽을 낮추고 빠르게 합류시키는 것이 중요하거나, 단순함과 일관성을 우선한다면 자바나 Kotlin이 더 나은 선택일 수 있습니다.
정리
Scala는 객체지향과 함수형을 강력한 타입 시스템 위에서 융합한 JVM 언어로, 표현식 중심 스타일·불변성·패턴 매칭·트레이트를 통해 자바보다 높은 표현력을 제공합니다. 자바 스트림이 받아들인 함수형 아이디어가 Scala에서는 언어의 기본값이며, 패턴 매칭과 케이스 클래스로 데이터를 우아하게 다룹니다. 다만 그 표현력은 가파른 학습 곡선과 복잡성이라는 무게를 동반하므로, 데이터·빅데이터처럼 강점이 분명한 영역에서 특히 빛납니다. 다음 글에서는 다시 Kotlin으로 돌아와, 자바와 Kotlin이 한 프로젝트에서 어떻게 매끄럽게 상호운용되는지를 실제 코드로 살펴봅니다.
지난 글: Java vs Kotlin — 무엇이 다른가
다음 글: Java와 Kotlin 상호운용
읽어주셔서 감사합니다. 😊