KoreaMango 나무

[WWDC2021] Building DSLs in Swift 본문

WWDC

[WWDC2021] Building DSLs in Swift

KoreaMango 2022. 10. 19. 14:38

서론

저번에 AppClip하면서 봤던 Fruta 라는 애플 앱이 있는데 코드를 보니 신기한 부분이 많아서 조금 찾아봤다.

 

특히 이 부분이었다.

resultBuilder에 대해서 찾아보니까 DSL 을 만들기 위한 프로퍼티 래퍼인 것 같다.

WWDC 영상에 DSL을 만드는 방법도 있길래 한번 보려고 한다.

[Write a DSL in Swift using result builders - WWDC21 - Videos - Apple Developer](https://developer.apple.com/wwdc21/10253)

Write a DSL in Swift using result builders

일부 문제들은 “커스텀된 프로그래밍 언어” 또는 “DSL”을 만듦으로써 쉽게 해결된다.

DSL를 만드려면 자체 컴파일러를 작성해야 하지만, resultBuilder가 포함된 Swift 5.4를 사용해서 코드를 쉽게 읽고 유지 관리할 수 있다. Swift용 커스텀 언어를 설계하기 위한 모범 사례를 알려주겠다.

result builders와 후행 클로져 전달인자에 대해 알아보고, modifier-style methods와 왜 잘 작동하는지 탐구해보자. Swift 기본 문법을 확장하여 DSL로 바꿀 수 있는지 알아보자.

이 세션을 활용하려면, SwiftUI를 작성해본 경험이 조금 필요할 것입니다. 그리고 당신은 parser나 컴파일러 구현에 대해 알 필요가 없다.

DSL이란 무엇인가

DSL은 “도메인”이라는 특정 영역에서 작동하는 프로그램을 위해서 설계된 일종의 소형 프로그래밍 언어이다.

언어는 특정 종류의 작업을 염두에 두고 설계되었기 때문에 이러한 종류의 작업을 더 쉽게 수행할 수 있는 특별한 기능을 가질 수 있다.

따라서 범용 언어 대신 DSL용 코드를 작성할 때 정확한 문제에 특정한 것만 작성하면 됩니다.

많은 DSL은 선언적이다.

임베디드 DSL은 현대적인 대안이다. 임베디드 DSL은 Swift와 같은 호스트 언어의 내장 기능을 사용하여 DSL의 암시적 동작을 코드의 일부에 추가하여 호스트 언어를 도메인에 맞게 효과적으로 수정한다.

독립 실행형 DSL을 원한다면 직접 작성해야 한다. 그리고 호스트 언어에서 시작하기 때문에 해당 언어를 이미 알고 있는 클라이언트는 배울 것이 훨씬 적다.

Swift는 임베디드 DSL을 지원하도록 설계되었다. 그리고 SwiftUI를 사용한 적이 있다면 이미 사용했다.

SwiftUI가 내부적으로 어떻게 돌아가는진 모르지만 지루한 세부사항을 모두 암시한다. View를 설명하는 건 사용자의 몫이고, 설명하고 있는 View를 수집하고 이를 표시하는 방법을 파악하는 것은 DSL의 일이다.

그럼 언제 DSL을 사용하나…

엄격한 규칙은 없지만 몇 가지 징후가 있다.

1. 바닐라 스위프트를 사용하는 메커니즘이 코드의 의미를 모호하게 하는 곳을 찾자.

2. 무언가를 어떻게 할지에 대한 지침을 직접 작성하는 대신 코드의 다른 부분에 무언가를 작성하는 것이 가장 좋은 방법 인 상황을 찾자

3. 주요 작업이 프로그래밍이 아닌 사람이 유지하는 코드 부분을 찾자.

4. DSL에서 많은 마일리지를 받을 수 있는 상황을 찾자.

뛰어난 DSL은 프로젝트 내에서 자주 정의하거나 자주 읽고 업데이트해야 하며 가능한 한 쉽게 만들고 싶은 것을 처리할 수 있습니다. DSL을 만드는 이유가 무엇이든, 설계와 구현에 약간의 노력이 걸릴 뿐만 아니라, 클라이언트가 학습하기 위해 약간의 노력이 필요합니다.

SwiftUI DSL의 이점

1. Property wrappers

이를 통해서 클라이언트는 DSL 동작과 관련된 변수를 선언할 수 있다.

2. Trailing closure arguments

DSL은 언어에 추가된 사용자 지정 구문처럼 읽을 함수나 이니셜라이저를 제공할 수 있다.

3. Result builders

이를 통해 DSL 코드에서 계산된 값을 반환값으로 수집하여 처리할 수 있다.

4. Modifier-Style methods

이것들은 기본적으로, 불려 간 값의 랩 된 버젼 또는 변경된 버젼을 돌려주는 메소드에 지나지 않는다.

Result buliders 는 코드에 의해 계산된 값을 수집하므로 이 패턴은 매우 잘 작동한다.

Property wrappers는 2019년 후반부에서 이미 다뤄졌기 때문에 덜 설명한다.

(WWDC 2019 Property wrapper 다음은 너다.)

특히 Result Builder가 이 세션의 주요 주제가 된다. Trailing closure와 Modifier-Style method는 많은 프로그래머가 익숙한 것이지만, Result Builder는 봐 무대 뒤에 기능이다.

Result Builder

Result bulider는 DSL로 작성된 값을 수집하고 언어에 필요한 데이터 구조에 연결하는 데 사용된다.

특별한 형식을 선언하고 해당 형식을 다음과 같이 사용할 수 있다는 점에서 Property wrapper와 약간 비슷하다.

프로퍼티, 함수, 메소드, 연산 프로퍼티의 게터, 클로저 등 반환 값을 가지는 거의 모든 함수 본체에 Result Builder를 적용시킬 수 있다.

SwiftUI에서 어떻게 DSL이 작동하는가?

가장 먼저 알아야 할 점은 최상위 레벨에서 새로운 구문처럼 보이는 블록을 포함하는 이 VStack은 실제로 후행 클로저 인수라는 것이다. VStack을 살펴보면 SwiftUI의 구조체임을 알 수 있습니다. 따라서 후행 클로저 인수는 그, 구조체의 이니셜 라이저에 건네 진다.

클로저가 전달되는 매개 변수를 보면 ViewBuilder 특성이 있음을 알 수 있다. 이 속성은 ViewBuilder라는 결과 빌더를 클로저에 적용해야 함을 컴파일러에게 알린다.

그럼 ViewBuilder란 무엇인가?

컴파일러는 @ViewBuilder라는 이름으로 type을 찾고, SwiftUI에서 이 type을 찾습니다.

 

Swift는 resultBuilder인 것을 찾고 클로저에 적용하기 시작한다.

 

Result Builder안에 있는 코드들에서 먼저, 결과를 생성하는 모든 명령문의 변수를 만든다.

 

이렇게 let v0, v1이 생긴다.

이러한 변수를 만든 후에는 ViewBuilder의 buildBlock 메서드에 대한 호출을 쓰고 해당 변수를 모두 전달한다.

 

이렇게 변수를 return 한다.

buildBlock 작업은 모든 매개 변수를 처리하거나 결합하여 단일 값으로 만들고 반환하는 것이다.

그런 다음 컴파일러는 클로저에서 buildBlock 결과를 반환하는 return문을 쓴다.

다시 말해서, 기본적으로 컴파일러는 코드를 가져오고 코드를 노란색처럼 추가하고 ViewBuilder에서 만든 모든 값으로 VStack이 콘텐츠로 사용하는 단일 값으로 조립할 수 있다.

이제 Modifier-style methods에 집중해보자.

Modifier style methods는 self의 변경된 copy, 또는 다른 타입으로 감싸진 self의 copy를 return 한다.

이것은 result Builder가 값을 확인하기 전에 값이 변경된다.

result Builder 이야기

Result Builder를 만들며 걱정한 건 Swift가 크게 변하면 DSL이 신뢰할 수 없게 된다.

또한 Result Builder를 사용하는 경우, Catch, Break, 와 같이 결과를 캡처하고 제어 흐름을 중단하는 기능을 사용할 수 없다. 또한 if, switch, for-in 문과 같은 일부 기능은 Result Builder가 이를 구현하는 데 사용되는 추가 메서드를 제공하지 않는 한 사용할 수 없다.

Swift DSL과 Swift API

Swift DSL 디자인은 실제로 Swift API 디자인과 매우 유사하다.

DSL은 API가 일반적으로 사용하지 않는 추가 기능을 사용하고 있다. Swift API와 마찬가지로 Swift DSL은 여러 가지 방법으로 설계할 수 있으며 이 모든 것이 문제를 해결한다.

DSL은 클라이언트가 언어를 배우기 위해 조금 미리 투자한다고 가정하기 때문에 이전에 본 적이 없는 사람들에게 매우 명확한 것은 그다지 우선하지 않는다. 따라서 이전에 API를 설계한 적이 있다면 DSL을 설계하기 위한 좋은 출발점을 얻을 수 있다. 내가 DSL에 사용하는 제안과 기법 중 일부는 API 설계에 매우 잘 적용된다.

Fruta

얼마나 간단하게 바뀌는지 설명해주는 게 포인트다.

 

참 보기 불편하다. 반복되는 객체, 보기 어려운 매개변수를 DSL로 바꿔보자.

 

일단 이렇게 modifier-style-method를 사용해 저걸 다 뒤로 빼준다. 그럼 중복도 없고 깔끔하게 보인다.

 

Smoothie 구조체 안에 static 변수에서 이렇게 빌더를 사용해서 all로 모든 리스트를 묶어준다.

그다음

 

이렇게 더 깔끔하게 바꿀 수 있다. SwiftUI 다워졌다.

 

그리고 DSL을 이용해서 코드 블럭과 UI의 구성 요소를 순서대로 배치해줄 수 있다.

DSL은 프로그래밍 언어이며, 개인의 취향과 주관적인 트레이드 오프는 프로그래밍 언어를 설계하는 데 큰 부분을 차지한다. 그래서 그 언어에 무엇을 요구하는지 명확하게 생각하는 것으로 시작해야합니다.

구현

이것 외에 다양한 메소드를 이용해서 구현을 해주고 있으며, 자세한 내용은 영상을 참고하면 좋을 것 같다.

마무리

DSL 구현에 대해 기억해야 할 가장 중요한 것은 모든 것이 클라이언트의 경험을 향상시키는 것이다. DSL은 클라이언트가 정의를 구성하는 메커니즘을 신경 쓰지 않고 사물을 정의할 수 있게 함으로써 매우 복잡하고 반복적인 코드를 보다 정리할 수 있다. 결과 빌더는 DSL이 정의된 값을 수집할 수 있는 강력한 도구다. 또한 수정자 스타일 메서드를 사용하면 결과 빌더가 값을 캡처하기 전에 값을 변경할 수 있는 구성 가능한 방법이 제공된다. 그러나 DSL을 만드는 경우 클라이언트는 사용법을 배워야 한다. 시간과 노력을 소비할 가치가 있는 경우에만 DSL을 제공하라.

'WWDC' 카테고리의 다른 글

[WWDC23] Meet SwiftData  (0) 2023.08.01
[WWDC 2022] Swift Student Challenge 후기  (1) 2022.06.27
[WWDC 2020] Vision  (0) 2022.05.13