이번 챕터에서는 영역 특화 언어(Domain-Specific Language)를 통해 관용적인 API를 디자인하는 법을 배울 것이다. 전통적인 API와 DSL 스타일 API의 차이점을 공부하고, DSL 스타일 API가 다양한 실용적인 문제, 즉 DB 접근, HTML 생성, 테스팅 등에 사용되는 방법을 배울 것이다.

코틀린 DSL 설계는 코틀린 언어의 여러 특성을 활용하는데 그중 하나가 5장에서 살펴본 수신 객체 지정 람다이다. 또다른 새로운 특징은 invoke로, 이는 10장에서 KFunction을 call이나 invoke로 호출할 수 있는 것에 관해서 11장에 설명할 것이라고 하고 넘어갔다. invoke를 사용하면 DSL 코드 안에서 람다와 프로퍼티 대입을 더 유연하게 조합할 수 있다.

#APIs에서 DSLs로

우리의 목표는 읽기 쉽고 유지보수하기 쉬운 코드를 만드는 것이다. 이 목표를 위해서는 각각의 클래스에만 집중하는 것이 아니라, 각 클래스가 서로 상호작용하는 바를 살펴봐야 한다. 즉, 클래스 APIs에 집중해야 한다.

이 책을 통해서 우리는 clean APIs를 만들기 위한 코틀린의 다양한 기능들을 살펴보았다. clean APIs란 무엇일까? 이는 다음과 같은 말을 뜻한다.

코틀린에서 깔끔한 API를 작성하기 위해 지원하는 기능들을 표로 살펴보자.

일반 구문 간결한 구문 사용한 언어 특성
StringUtil.capitalize(s) s.capitalize() 확장 함수
1.to(“one”) 1 to “one” 중위 호출
set.add(2) set += 2 연산자 오버로딩
map.get(“key”) map[“key”] get 메소드에 대한 관례
file.use({ f -> f.read() }) file.use{ it.read() } 람다를 괄호 밖으로 빼내는 관례
sb.append(“yes”) with(sb){append(“yes”)} 수신 객체 지정 람다

코틀린 DSL은 clean-syntax를 제공하는 기능과 그런 구문을 확장해 여러 메소드 호출을 조합함으로써 구조를 만들어내는 기능을 활용한다.

그 결과로 DSL은 각각의 메소드 호출만을 제공하는 API에 비해 더 표현력이 풍부해지고 사용하기 편해진다.

Kotlin DSLs는 코틀린과 마찬가지로, 컴파일 시점에 타입이 정해진다(fully statically typed). 따라서 DSL 패턴을 사용할 때도 코틀린의 장점을 누릴 수 있다.

다음은 DSLs이 할 수 있는 일의 예시이다.

val yesterday = 1.days.ago

//위의 코드는 다음과 같은 HTMl 코드를 생성함.
fun createSimpleTable() = createHTML().
	table {
		tr {
			td { +"cell" }
	}
}

자세한 이야기를 하기 전에, 먼저 DSLs이 무엇인지 살펴보자.

#영역 특화 언어(DSL)란?

영역 특화 언어라는 것 자체는 오래된 개념이다. 범용 프로그래밍 언어(general-purpose programming language)를 기반으로 하여 필요하지 않은 기능을 없앤 영역 특화 언어를 DSL이라고 부른다. 이는 특정 도메인과 그 도메인에 연관된 기능에만 집중하는 언어를 의미하며, SQL이나 정규식(regular expressions) 등이 여기에 속한다. 이 언어들은 특정 task를 처리하는 데는 휼륭하나, 이 언어만으로 전체 애플리케이션을 모두 작성할 수는 없다.

그러나 이 언어들은 제공하는 기능을 축소함으로써 자신의 목표를 효과적으로 성취할 수 있다. SQL을 사용하기 위해서는 함수나 클래스를 작성할 필요가 없다. 이와 같이 DSL은 범용 프로그래밍 언어와 달리 declarative(선언적)하다. 이와 달리 범용 프로그래밍 언어는 imperative하다.

declarative는 결과를 기술하기만 하고 그 결과를 달성하기 위한 세부 실행은 언어를 해석하는 엔진에 맡기는데, 실행 엔진이 결과를 얻는 과정을 최적화하기 때문에 declarative 언어가 더 효율적인 경우가 종종 있다.

그러나 DSLs은 또한 단점도 가지고 있는데, 그건 바로 범용 언어로 만든 애플리케이션과 조합하기 어렵다는 점이다. DSLs은 고유한 문법을 가지고 있어 다른 언어로 만든 프로그램과 통합하기 어렵다. 따라서 DSL로 작성한 프로그램을 다른 언어에서 호출하기 위해서는 DSL 프로그램을 별도의 파일이나 문자열 리터럴로 저장해야 한다.