DSL은 Domain-Specific Language의 약자로 특정 도메인에 특화된 프로그래밍 언어를 의미합니다. 그렇다면 여기서 도메인(domain)이란 무엇일까요? 도메인은 특정 분야나 영역을 의미하는데, 예를 들면 웹 서버 구성, 데이터베이스 쿼리, 그래픽 디자인 등이 될 수 있습니다.
1. DSL의 특징
- 도메인 특화: DSL은 특정 분야나 문제 영역에 특화된 기능을 제공합니다.
- 직관적 표현: DSL을 사용하면 도메인의 문제나 로직을 간결하고 직관적으로 표현할 수 있습니다.
- 생산성 향상: 도메인에 특화된 기능을 제공하므로 개발 속도와 품질을 향상시킬 수 있습니다.
- 제한된 표현력: 일반적인 프로그래밍 언어(GPL: General-Purpose Language)에 비해 DSL은 제한된 표현력을 가집니다. 이는 DSL이 특정 도메인의 문제만을 해결하기 위해 설계되었기 때문입니다.
2. DSL의 종류
- 내부 DSL (Internal DSL):
- 기존의 일반적인 프로그래밍 언어(GPL)를 기반으로 만들어진 DSL입니다.
- 예: Kotlin의 Anko(안드로이드 UI DSL), ScalaTest(테스트 DSL)
- 외부 DSL (External DSL):
- 특정 도메인을 위해 처음부터 새로 만들어진 언어입니다.
- 예: SQL(데이터베이스 쿼리용), HTML(웹 페이지 마크업용)
3. DSL의 예
- SQL: 데이터베이스 질의를 위한 DSL.
SELECT
,INSERT
,UPDATE
등의 키워드를 사용해 데이터베이스로부터 데이터를 조회, 삽입, 수정할 수 있습니다. - Gradle: 빌드와 배포 자동화를 위한 도구에서 사용하는 Groovy 혹은 Kotlin DSL.
dependencies
,repositories
,tasks
등의 키워드로 프로젝트 구성을 선언적으로 기술합니다. - CSS: 웹 페이지 스타일링을 위한 DSL.
color
,margin
,font-size
등의 속성으로 웹 요소의 스타일을 지정합니다. - Regular Expression (Regex): 문자열 검색 및 조작을 위한 DSL. 다양한 패턴을 이용하여 문자열 내의 특정 패턴을 찾거나 대체합니다.
코틀린 DSL
Gradle은 초기에 Groovy 기반의 DSL을 주로 사용했으나, 최근에는 코틀린을 기반으로 한 DSL을 제공하고 있습니다. 코틀린 DSL은 코틀린의 강력한 타입 안전성과 간결한 문법 덕분에 점점 더 인기를 얻고 있습니다.
코틀린 DSL의 장점
- 타입 안전성: 컴파일 시점에 오류를 잡아낼 수 있습니다.
- 코드 자동완성: IDE의 지원을 받아 빌드 스크립트 작성 시 자동완성 기능을 활용할 수 있습니다.
- 읽기 쉬운 문법: 코틀린의 간결하고 명확한 문법으로 인해 빌드 스크립트가 읽기 쉬워집니다.
build.gradle.kts 예시
// plugins: 적용할 플러그인을 지정합니다. 여기서는 'kotlin' 플러그인을 적용하였습니다.
plugins {
kotlin("jvm") version "1.5.0"
}
// repositories: 의존성을 어디서 가져올지 지정합니다. mavenCentral을 사용하면 Maven 중앙 저장소에서 의존성을 가져옵니다.
repositories {
mavenCentral()
}
// dependencies: 프로젝트에 필요한 의존성을 지정합니다.
dependencies {
// 'implementation': 컴파일과 런타임 모두에 필요한 의존성입니다.
implementation(kotlin("stdlib-jdk8"))
// 'api': 모듈간 가시성을 제한하지 않으며, 컴파일과 런타임 모두에 필요한 의존성입니다.
api("org.some.library:library:1.0.0")
// 'runtimeOnly': 런타임시에만 필요한 의존성입니다. 컴파일에는 포함되지 않습니다.
runtimeOnly("org.another.library:runtime-lib:2.0.0")
}
// tasks: 특정 작업을 정의하거나 커스터마이징합니다.
tasks {
// 'register': 새로운 작업을 등록합니다.
register<Copy>("copyDocs") {
// from: 복사할 위치를 지정합니다.
from("src/docs")
// into: 복사할 대상 위치를 지정합니다.
into("$buildDir/docs")
}
val myTask = register("myTask") {
doLast {
println("Executing myTask")
}
}
// 'dependsOn()': 작업간 의존성을 나타냅니다. myTask가 실행되기 전에 copyDocs가 먼저 실행됩니다.
myTask {
dependsOn("copyDocs")
}
}
각 키워드 설명
plugins
: 프로젝트에 적용할 플러그인들을 지정합니다.repositories
: 의존성을 어디서 가져올 것인지 지정하는 곳입니다. Maven, JCenter, Google 등 다양한 저장소를 지정할 수 있습니다.dependencies
: 필요한 라이브러리의 의존성을 지정하는 곳입니다.implementation
: 프로젝트의 컴파일과 런타임 클래스 경로에 의존성을 추가합니다. 그러나 이 의존성은 해당 모듈을 사용하는 다른 모듈에는 전달되지 않습니다. 이는 캡슐화를 유지하기 위해 사용됩니다. 예를 들어, 모듈 A가implementation
을 사용하여 모듈 B에 의존하면, 모듈 C가 모듈 A에 의존할 때 모듈 B의 API를 사용할 수 없습니다.api
:implementation
과 비슷하게 의존성을 추가하지만, 이 의존성이 해당 모듈을 사용하는 다른 모듈에도 전달됩니다. 즉, 모듈간 가시성을 제한하지 않습니다.runtimeOnly
: 이 의존성은 런타임에만 필요하며, 컴파일 시점에는 필요하지 않습니다. 예를 들면, 로그 라이브러리나 JDBC 드라이버와 같이 실행 시에만 필요한 라이브러리에 주로 사용됩니다.tasks
: Gradle 빌드 작업을 커스터마이징하거나 새로운 작업을 정의할 때 사용합니다.register
:tasks
내에서 새로운 작업을 등록할 때 사용하는 함수입니다.dependsOn()
: 특정 작업이 다른 작업에 의존하게 만듭니다. 의존하는 작업이 먼저 실행되고, 그 다음에 해당 작업이 실행됩니다.
4. DSL 설계 시 고려사항
- 도메인 이해: DSL을 만들려는 도메인에 대한 깊은 이해가 필요합니다.
- 간결성: DSL 사용자가 문제를 간결하게 표현할 수 있도록 해야 합니다.
- 확장성: 도메인이 발전함에 따라 DSL도 쉽게 확장 가능해야 합니다.
- 유지 보수: 변경이나 확장이 용이하도록 DSL을 설계해야 합니다.
결론
DSL은 특정 도메인에 특화된 언어로, 해당 도메인의 문제나 요구 사항을 효과적으로 해결할 수 있습니다. 일반적인 프로그래밍 언어와 달리 제한된 표현력을 가지지만, 그 덕분에 도메인 문제를 더 간결하고 직관적으로 표현할 수 있습니다. 따라서 도메인의 특징과 요구 사항에 따라 적절한 DSL을 선택하거나 설계하는 것이 중요합니다.
728x90
'Language > Kotlin' 카테고리의 다른 글
[Kotlin Basic]let함수에 대해 (0) | 2023.09.15 |
---|---|
그래들 설정 settings.gradle.kts에 대해 (0) | 2023.09.13 |
Gradle, Spring, Kotlin 환경에서의 build.gradle.kts 내 tasks 이해하기 (0) | 2023.09.13 |
`reified`란? (0) | 2023.09.04 |
Inline 함수란? (0) | 2023.09.04 |