ClojureScript 1.12.145: Lisped Web에 Async/Await 도입
ClojureScript 팀은 생태계에서 가장 요청이 많았던 기능 중 하나인 async 및 await에 대한 네이티브 지원을 도입하는 1.12.145 버전을 출시했다고 발표했습니다. 이번 업데이트는 ClojureScript 개발자가 현대적인 JavaScript API와 상호작용하는 방식에 있어 중요한 변화를 의미하며, ECMAScript 2016 표준과의 더욱 원활한 통합을 향해 나아갑니다.
수년 동안 ClojureScript 사용자들은 비동기 작업을 처리하기 위해 외부 라이브러리나 복잡한 패턴에 의존해 왔습니다. 컴파일러가 네이티브 JavaScript async 함수를 생성할 수 있도록 함으로써, ClojureScript는 이제 추가적인 의존성 오버헤드 없이 Promise와 비동기 흐름을 처리할 수 있는 일급 객체 방식을 제공합니다.
ClojureScript에서 Async 함수 구현하기
새로운 기능은 함수에 ^:async 힌트를 주는 것으로 트리거됩니다. 컴파일러가 이 힌트를 만나면 JavaScript async 함수를 생성합니다. 이러한 함수 내에서 await 특수 형태(special form)를 사용하여 Promise가 해결될 때까지 실행을 일시 중지할 수 있습니다.
다음 구현 사례를 살펴보십시오:
(refer-global :only '[Promise])
(defn ^:async foo [n]
(let [x (await (Promise/resolve 10))
y (let [y (await (Promise/resolve 20)]
(inc y))
;; not async
f (fn [] 20)]
(+ n x y (f))))
이 구문은 테스트 프레임워크에도 확장되어, 개발자가 비동기 테스트를 더욱 자연스럽게 작성할 수 있게 합니다:
(deftest ^:async defn-test
(try
(let [v (await (foo 10))]
(is (= 61 v)))
(let [v (await (apply foo [10]))]
(is (= 61 v)))
(catch :default _ (is false))))
상호운용성 트레이드오프: Native vs. CSP
async/await의 도입은 Clojure의 비동기 프로그래밍 철학에 관한 커뮤니티 내의 건강한 논쟁을 불러일으켰습니다. 오랫동안 ClojureScript의 동시성 표준은 Communicating Sequential Processes (CSP)를 구현하는 core.async였습니다.
일부 커뮤니티 구성원들은 복잡한 상태 관리와 프로세스 간 통신에는 CSP 모델이 더 우수하다고 주장합니다. 한 사용자가 언급했듯이, async 함수를 CSP로 감싸는 것이 종종 "더 나은 처리 방식"으로 여겨졌는데, 이는 Clojure가 이미 이를 위한 강력한 패턴을 가지고 있었기 때문입니다. 다른 이들은 JS 스타일의 async 키워드를 도입하는 것이 진정한 업그레이드인지, 아니면 로직을 채널로 밀어넣는 Clojure의 핵심 철학에서 너무 멀어지는 것인지 의문을 제기합니다.
하지만, 이 변화의 주요 동력은 상호운용성입니다. Clojure 설문조사 결과, async 지원은 JS interop을 위한 희망 사항 목록에서 상위권을 차지했습니다. 네이티브 async/await를 지원함으로써, ClojureScript는 거의 전적으로 Promise 기반인 현대적인 Browser API 및 인기 있는 JavaScript 라이브러리와의 일반적인 상호작용을 위해 Promesa와 같은 라이브러리가 필요하지 않게 되었습니다.
기술적 난관 극복하기
이 기능의 구현 경로는 즉각적이지 않았습니다. 커뮤니티의 통찰에 따르면, 이전의 async/await 구현 시도는 다음과 같은 이유로 어려움을 겪었습니다:
- 컴파일러 복잡성: 이를 구현하려면 ClojureScript 컴파일러 전반에 걸친 깊은 변화가 필요합니다.
shadow-cljs의 제작자를 포함한 저명한 커뮤니티 구성원들의 이전 시도들은 이것이 매우 벅찬 작업임을 결론지었습니다. - 언어적 차이: Clojure(JVM 버전)와의 일관성을 유지하는 것과 JS 런타임에 필요한 도구를 제공하는 것 사이에는 긴장 관계가 존재합니다.
await가 이미clojure.core의 키워드이기 때문에, ClojureScript에서 이 특수 형태를 도입하는 것은 웹 환경의 특정 요구 사항을 충족하기 위해 계산된 차이(divergence)를 나타냅니다.
결론
ClojureScript 1.12.145는 단순한 구문 업데이트 그 이상입니다. 이는 현대적인 웹 개발 요구 사항에 대한 실용적인 응답입니다. core.async의 CSP 모델이 복잡한 동시성을 위한 강력한 도구로 남아 있는 한편, 네이티브 async/await는 JavaScript 생태계와 상호작용하기 위한 가볍고 마찰 없는 경로를 제공합니다. 현대적인 프론트엔드를 구축하는 개발자들에게, 대상 플랫폼에 네이티브이게 느껴지는 비동기 코드를 작성할 수 있는 능력은 생산성과 상호운용성을 위한 거대한 승리입니다.