10.1 도메인 전용 언어
DSL은 특정 도메인 문제를 해결하기 위해 만든 언어이다. 특정 비지니스 도메인을 인터페이스로 만든 API라고 생각할 수 있다.
DSL을 이용하면 사용자가 특정 도메인의 복잡성을 더 잘 다룰 수 있다. 저수준의 구현 세부 사항 메서드는 private로 만들어서 저수준 구현 세부 내용은 숨길 수 있다. 이런식으로 사용자 친화적인 DSL을 만들 수 있다.
10.1.1 DSL의 장점과 단점
DSL은 만병통치약이 아니다. DSL을 도메인에 이용하면 약이 될 수도 독이 될 수도 있다.
장점 | 단점 |
---|---|
간결함 | DSL설계의 어려움 |
가독성 | 개발 비용 |
유지보수 | 추가 우회 계층 |
집중 | 새로 배워야 하는 언어 |
관심사분리 | 호스팅 언어의 한계 |
10.1.2 JVM에서 이용할 수 있는 다른 DSL 해결책
내부 DSL
내부 DSL은 순수 자바 코드같은 기존 호스팅 언어를 기반으로 구현한다. 기존의 자바는 귀찮고 유연성이 떨어지기 때문에 DSL을 만드는데에 분명한 한계가 있었다.
하지만, 람다가 도입되면서 동작 파라미터화를 간단하게 만들 수 있게 되면서 신호 대비 잡음 비율을 줄일 수 있게 되었다. 여기서 잡음이란, 문법상 필요한 필요없는 코드들이다.
코드 추가 요망
위 코드에서 BOLD처리한 부분이 잡음이다.
장점은 아래와 같다.
- 기존 자바 언어를 이용해서 새로운 언어를 배울 노력이 줄어든다.
- 다른 코드와 함께 DSL을 컴파일할 수 있다. 따라서 다른 언어 컴파일러를 도입하지 않아도 되어 비용이 줄어든다.
- 개발 팀이 새로운 언어를 배우거나 IDE를 배울 필요가 없다.
- 기존의 IDE을 사용하여 자동 완성, 리팩토링을 그대로 사용할 수 있다.
- 새로운 DSL을 개발해야하는 상황에서 DSL을 쉽게 합칠 수 있다.
다중 DSL
JVM 위에서 돌아가는 여러 언어를 사용하여 구현하는 방법이다. 특히 스칼라는 꼬리 재귀등을 통해 최적화할 수 있고, 문법적 잡음이 전혀 없다.
단점은 아래와 같다.
- 새로운 언어를 배워야한다.
- 두 개 이상의 언어가 혼재하게 된다.
- 실제로는 자바와 100% 호환되지 않는다.
외부 DSL
자신만의 문법과 새 언어를 설계하여 구현하는 방법이다. 파싱과 파싱결과 분석, 외부 DSL을 실행할 코드까지 모두 만들어야한다. 생략하자.
10.2 최신 자바 API의 작은 DSL
10.2.1 스트림 API는 컬렉션을 조작하는 DSL
Stream
인터페이스는 네이티브 자바 API에 작은 내부 DSL을 적용한 좋은 예다. 컬렉션 도메인에 대해 필터, 정렬, 변환, 그룹화, 조작하는 작지만 강력한 DSL로 볼 수 있다.
코드 추가 요망
Stream
도입전의 코드인 위 코드를 보면, 문제가 분리되지 않아 가독성과 유지보수성 모두 저하되었다.
아래와 같이 같은 의무를 가진 코드가 여러 행에 분산되어 있다.
FileReader
가 만들어짐
- 파일이 종료되었는지 확인하는
while
루프의 두번째 조건
- 파일의 다음 행을 읽는
while
루프의 마지막 행
또한, 첫 40행을 수집하는 코드도 세 부분으로 흩어져 있다.
errorCount
변수를 초기화하는 코드
while
루프의 첫 번째 조건
- “
Error
”을 로그에서 발견하면 카운터를 증가시키는 행
코드 추가 요망
Stream
API를 통해 더 쉽고 간결하게 구현할 수 있다.
또한 Stream
API의 플루언트 형식은 잘 설계된 DSL의 또 다른 특징이다. 모든 중간 연산은 게으르며 다른 연산으로 파이프라인이 될 수 있는 스트림으로 변환한다. 또한 최종 연산은 적극적이며 전체 파이프라인의 계산을 일으킨다.
10.2.2 데이터를 수집하는 DSL인 Collectors
Collector
인터페이스는 데이터 수집을 수행하는 DSL로 간주할 수 있다.
코드 추가 요망
이렇게 플루언트 스타일로 코드를 구현할 수 있는 반면 Collectors API를 이용해 중첩함으로써 다중 수준 Collectors를 만들 수 있다.
코드 추가 요망
당연하게도 플루언트 스타일의 코드가 더 직관적으로 보인다. 그것 뿐만 아니라 한 가지 차이점이 더 존재하는데,
논리적으로는 최종 그룹화에 해당 되지만, 가장 안쪽의 Collectors가 첫 번째로 평가되어야 한다는 차이가 있다.
10.3 자바로 DSL을 만드는 패턴과 기법
너무 코드가 많음.. 코드 추가시 작업하여 추가예정
10.4 실생활의 자바 8 DSL
10.4.1 jOOQ
SQL을 Java 8 DSL로 실행할 수 있도록 작성된 라이브러리다. 내부 DSL의 대표 예이다.
코드 추가 요망
이런 SQL문을
코드 추가 요망
이런 JAVA 코드로 만들 수 있다. 게다가 Stream API와 호환되기 때문에 아래와 같은 코드도 작성이 가능하다.
코드 작성 요망
10.4.2 큐컴버
동작 주도 개발(BDD)는 TDD의 확장된 형태이다. 여기서는 시나리오를 구조적으로 서술하는 도메인 전용 스크립팅 언어를 사용한다. 큐컴버는 이 명령문들을 실행할 수 있는 테스트 케이스로 변환해준다.
코드는 생략하고, 외부 DSL의 대표 예로 볼 수 있다.
10.4.3 스프링 통합
스프링 통합은 엔터프라이즈 통합 패턴을 지원할 수 있도록 의존성 주입에 기반한 스프링 프로그래밍 모델을 확장한다. 역시 내부 DSL의 대표 예이다.
10.5 마치며
- DSL의 주요 기능은 개발자와 도메인 전문가 사이에 간격을 좁히는 것이다. 애플리케이션의 비지니스 로직을 구현하는 코드를 만든 사람이 프로그램이 사용될 비지니스 필드의 저문 지식을 갖추긴 어렵다. 개발자가 아닌 사람도 이해할 수 있는 언어로 이런 비지니스 로직을 구현할 수 있다고 해서 도메인 전문가가 프로그래머가 될 수 있는 것은 아니지만 적어도 로직을 읽고 검증하는 역할을 할 수는 있다.
- DSL을 크게 내부적(DSL이 사용될 애플리케이션을 개발한 언어를 그대로 활용) DSL과 외부적(직접 언어를 설계해 사용) DSL로 분류할 수 있다. 내부적 DSL은 개발 노력이 적게 드는 반면 호스팅 언어의 문법 제약을 받는다. 외부적 DSL은 높은 유연성을 제공하지만 구현하기가 어렵다.
- JVM에서 이용할 수 있는 스칼라, 그루비 등의 다른 언어로 다중 DSL을 개발할 수 있다. 이들 언어는 자바보다 유연하며 간결한 편이다. 하지만 이들을 자바와 통합하려면 빌드 과정이 복잡해지며 자바와의 상호 호환성 문제도 생길 수 있다.
- 자바의 장황함과 문법적 엄격함 때문에 보통 자바는 내부적 DSL을 개발하는 언어로는 적합하지 않다. 하지만 자바 8의 람다 표현식과 메서드 참조 덕분에 상황이 많이 개선되었다.
- 최신 자바는 자체 API에 작은 DSL을 제공한다.
Stream
,Collectors
클래스 등에서 제공하는 작은 DSL은 특히 컬렉션 데이터의 정렬, 필터링, 변환, 그룹화에 유용하다.
- 자바로 DSL을 구현할 때 보통 메서드 체인, 중첩 함수, 함수 시퀀싱 세 가지 패턴이 사용된다. 각각의 패턴은 장단점이 있지만 모든 기법을 한 개의 DSL에 합쳐 장점만을 누릴 수 있다.
- 많은 자바 프레임워크와 라이브러리를 DSL을 통해 이용할 수 있다. jOOQ, 큐컴버, 스프링 통합을 살펴보았다.
Uploaded by N2T
(23.05.31 23:44)에 작성된 글 입니다.