[Modern Java In Action] 10. 도메인 전용 언어(DSL)


도메인 전용 언어 (Domain-Specific Language)

  • 작은, 범용이 아니라 특정 도메인을 대상으로 만들어진 특수 프로그래밍 언어
    • 예) 메이븐, 앤트 → 빌드 과정을 표현하는 DSL
    • 예) HTML → 웹페이지의 구조를 정의하도록 특화된 언어
  • 특정 비즈니스 도메인을 인터페이스로 만든 언어
  • 특정 비즈니스 도메인을 인터페이스로 만든 API
  • 도메인을 표현할 수 있는 클래스와 메서드 집합이 필요하다.

플루언트 스타일

  • 스트림의 API의 특성인 메서드 체인을 보통 자바의 루프의 복잡함 제어와 비교해 유창함을 의미
  • 메소드 체이닝에 상당 부분 기반한 객체 지향 API 설계 메소드이며, 소스 코드의 가독성을 산문과 유사하게 만드는 것이 목적
  • Fluent API
    • 함수들을 작성하고 나면, 마치 그 문장이 영어 문장처럼 읽히는 API

DSL의 장점과 단점

DSL의 장점

  • 간결함
    • API는 비즈니스 로직을 간편하게 캡슐화하므로 반복을 피할수 있고 코드를 간결하게 만들수 있다.
  • 가독성
    • 도메인 영역의 용어를 사용하므로 비 도메인 전문가도 코드를 쉽게 이해할 수 있다.
    • 다양한 조직 구성원 간에 코드와 도메인 영역이 공유될 수 있다.
  • 유지보수
    • 잘 설계된 DSL로 구현한 코드는 쉽게 유지보수하고 바꿀수 있다.
  • 높은 수준의 추상화
    • DSL은 도메인과 같은 추상화 수준에서 동작하므로 도메인의 문제와 직접적으로 관련되지 않은 세부사항을 숨긴다.
  • 집중
    • 비즈니스 도메인의 규칙을 표현할 목적으로 설계된 언어이므로 프로그래머가 특정 코드에 집중할 수 있다.
  • 관심사분리
    • 지정된 언어로 비즈니스 로직을 표현함으로 애플리케이션의 인프라구조와 관련된 문제와 독립적으로 비즈니스 관련된 코드에서 집중하기가 용이하다.

DSL의 단점

  • DSL 설계의 어려움
    • 간결하게 제한적인 언어에 도메인 지식을 담는 것이 쉬운 작업은 아니다.
  • 개발비용
    • 코드에 DSL을 추가하는 작업은 초기 프로젝트에 많은 비용과 시간이 소모된다.
    • DSL 유지보수와 변경은 프로젝트에 부담을 주는 요소이다.
  • 추가 우회 계층
    • DSL은 추가적인 계층으로 도메인 모델을 감싸며 이 때 계층을 최대한 작게 만들어 성능 문제를 회피한다.
  • 새로 배워야 하는 언어
    • DSL을 프로젝트에 추가하면서 팀이 배워야 하는 언어가 한 개 더 늘어난다는 부담이 있다.
  • 호스팅 언어 한계
    • 일부 자바 같은 범용 프로그래밍 언어는 장황하고 엄격한 문법을 가졌다.
    • 이런 언어로는 사용자 친화적 DSL을 만들기가 힘들다.

JVM에서 이용할 수 있는 다른 DSL 해결책

내부 DSL

순수 자바 코드 같은 기존 호스팅 언어를 기반으로 구현

(이 책은 자바 언어 책이므로) 자바로 구현한 DSL을 의미한다. 람다 표현식이 등장하면서 쉽고 간단하고, 표현력 있는 DSL을 만들 수 있게 되었다.

익명 내부 클래스를 사용해 DSL을 구현하는 것보다 장황함을 크게 줄여 신호 대비 잡음 비율을 적정 수준으로 유지하는 DSL을 만들수 있다.

List<String> numbers = Arrays.asList("one", "two", "three");
// 코드의 잡음 : numbers.forEach, accept, System.out.println(s);
numbers.forEach( new Consumer<String>() {
    @Override
    public void accept( String s ){
        System.out.println(s);
    }
});
 
// 잡음 제거
numbers.forEach(s -> System.out.println(s));
// 메서드 참조
numbers.forEach(System.out::println);
  • 외부 DSL에 비해 새로운 패턴과 기술을 배워 DSL을 구현하는 노력이 현저하게 줄어든다.
  • 순수 자바로 DSL을 구현하면 나머지 코드와 함께 DSL을 컴파일 할 수 있다.
    • 다른 언어의 컴파일러를 이용하거나 외부 DSL을 만드는 도구를 사용할 필요가 없으므로 추가로 비용이 들지 않는다.
  • 새로운 언어를 배우거나 익숙하지 않고 복잡한 외부 도구를 배울 필요가 없다.
  • 기존의 자바 IDE를 이용해 자동 완성, 자동 리팩터링 같은 기능을 그대로 사용할수 있다.
  • 한 개의 언어로 한개의 도메인 또는 여러 도메인을 대응하지 못해 추가로 DSL을 개발해야 하는 상황에서 자바를 이용한다면 추가 DSL을 쉽게 합칠수 있다.

다중 DSL

같은 자바 바이트코드를 사용하여 JVM 기반 프로그래밍 언어를 이용함으로 DSL 합칠 수 있다.

def times(i: Int, f: => Unit): Unit = {
    f
    if ( i > 1 ) times(i - 1, f)
}
times(3, println("Hello World"))
 
// times 함수를 커링하거나 두 그룹으로 인수를 놓을수 있다.
def times(i: Int)(f: => Unit): Unit = { f
    if (i > 1 times(i - 1)(f)
}
 
// 여러 번 실행할 명령을 중괄호 안에 넣어 같은 결과를 얻을 수 있다.
times(3) {
    println("Hello World")
}
 
// 함수가 반복할 인수를 받는 한 함수를 가지면서 Int를 익명 클래스로 암묵적 변환하도록 정의
implicit def intToTimes(i: Int) = new {
    def times(f: => Unit): Unit = {
        def times(i: Int, f: => Unit): Unit = {
            f
            if( i > 1 ) times(i - 1, f)
        }
        times(i, f)
    }
}
 
//
3 times {
    println("Hello World")
}
  • 문법적 잡음이 없으며 개발자가 아닌 사람도 코드를 쉽게 이해할 수 있다.
  • 자바 언어가 가지는 한계를 넘을 수 있다.
  • 누군가가 이미 해당 언어에 대한 고급 기술을 사용할 수 있을 정도의 충분한 지식을 가지고 있어야 한다.
  • 두 개 이상의 언어가 혼재하므로 여러 컴파일러로 소스를 빌드하도록 빌드 과정을 개선해야한다.
  • 호환성 문제를 고려해야 한다.

외부 DSL

자신만의 문법과 구문으로 새 언어를 설계해야 한다.

새 언어를 파싱하고, 파서의 결과를 분석하고, 외부 DSL을 실할 코드를 만들어야한다.

  • 가장 큰 장점은 무한한 유연성이다.
  • 필요한 특성을 완벽하게 제공하는 언어를 설계할 수 있다.
  • 제대로 언어를 설계하면 우리의 비즈니스 문제를 묘사하고 해결하는 가독성 좋은 언어를 얻을수 있다.
  • 자바로 개발된 인프라 구조 코드와 외부 DSL로 구현한 비즈니스 코드를 명확하게 분리할수 있다.

요약

  • DSL은 개발자 아닌 일반인(ex.도메인전문가, 기획자)도 이해할수잇도록하는 언어라고 보면됨
  • 내부적DSL(개발언어 그대로 사용) / 외부적DSL(직접 언어설계) / 다중DSL(여러언어 섞어서사용)
  • 사실, 직접 언어설계까지는 굳이..?라는 느낌이다.
  • 어떤 도메인에서는 DSL이 필요할수도 있을듯..^^
  • 기획자한테 코드를 보여준다는 니즈라기보다는, DSL 의 철학을 가져와서 기존언어를 잘 설계해서 개발하면 이후 코드를 접할 개발자들한테는 무척 좋을듯 !





© 2020. by berrrrr

Powered by berrrrr