GN⁺: 왜 Clojure인가?
(gaiwan.co)- Clojure는 주류 프로그래밍 언어 중 하나가 아니며, 일부 사람들에게는 생소할 수 있음
- Clojure의 주요 장점
- 개발자 생산성 : Clojure는 인터랙티브하고, 반복 작업이 적으며, 효율적인 개발 환경을 제공. 개발자들이 만족하면서 빠르게 제품을 출시할 수 있음
- 장기적인 유지보수성 : Clojure 언어와 생태계는 성숙하고 안정적임. 높은 품질의 시스템을 구축하면서 유지보수 비용을 절감할 수 있음
- 아이디어 중심의 문화 : Clojure 커뮤니티는 과거와 현재, 학계와 산업의 아이디어를 탐구하여 더 나은 소프트웨어 개발 방식을 모색. 개발자들에게 새로운 도전과 학습 기회를 제공
(hello 'clojure)
- Clojure는 1950년대에 만들어진 Lisp 계열 언어 중 하나
- Lisp는 이론적 모델로 시작했지만, 실제 프로그래밍에서도 높은 개념적 우아함을 제공
- 코드의 구문이 곧 데이터 구조와 일치하여 기존의 문법이 복잡한 언어들보다 여러 장점이 있음
- 과거 AI 붐 당시 주요 언어로 사용됨
- Lisp 계열 언어는 주기적으로 인기를 얻었다가 사라졌으나, 최근 10년 동안 Clojure가 주목받음
- Clojure는 Java의 JVM 위에서 동작하며, 최신 프로그래밍 개념을 반영
- 강력한 불변 데이터 구조 지원
- 동시성을 고려한 설계
- 주요 대기업의 지원 없이도 성장한 작은 커뮤니티지만, 강력한 언어적 특성을 가짐
- Clojure는 다양한 환경에서 실행 가능
- ClojureScript: JavaScript로 변환하여 실행
- ClojureCLR: .NET 환경에서 실행
- Babashka: GraalVM을 활용한 빠른 스크립팅 인터프리터
- Jank: 네이티브 컴파일 지원
- Clojure를 배우면 여러 환경에서 동일한 지식을 활용할 수 있는 장점이 있음
인터랙티브 개발 방식
- 프로그래밍은 코드 작성과 검증의 반복 과정
- 피드백이 없다면, 코드가 정상 동작하는지 확신하기 어려움
- 일반적인 피드백 방식:
- 스크립트 실행 반복
- UI 상호작용 및 로그 출력
- 단위 테스트 활용
- 컴파일러 및 정적 분석 도구 사용
- 빠른 피드백이 개발자의 생산성에 큰 영향을 미침
- 피드백이 느려질수록 디버깅 시간이 증가하고, 코드 작성 시간이 감소
- Clojure는 기존의 피드백 방식 외에도 인터랙티브 개발(interactive development) 방식을 제공
- REPL (Read-Eval-Print Loop) 기반 개발이 핵심
- 개발자는 코드를 작성하기 전, Clojure 런타임을 실행하여 에디터와 연결
- 코드 조각을 하나씩 실행하며 즉각적인 피드백을 받을 수 있음
- Lisp의 일관된 구문 구조 덕분에 작은 코드 조각부터 큰 코드 블록까지 자유롭게 실행 가능
- 일반적인 REPL과 달리, Clojure는 실행 중인 시스템에 연결된 상태에서 코드 조각을 실행할 수 있음
- 이 방식은 기존의 "코드를 작성 → 실행 → 디버깅" 방식보다 훨씬 강력한 피드백 루프 제공
- 실시간으로 프로그램을 수정하고 디버깅 가능
- 단순한 REPL이 아닌, 운영 환경에서도 활용 가능한 강력한 인터랙션 도구
안정성을 중시하는 문화
- Clojure를 선택하면 단순히 강력한 기술을 얻는 것이 아니라, 독특한 철학과 원칙을 가진 커뮤니티의 일원이 됨
- 기술만을 채택하고 커뮤니티와 소통하지 않는다면 Clojure의 진정한 가치를 경험하기 어려움
- Clojure 커뮤니티는 안정성과 하위 호환성을 중요한 가치로 삼음
- 핵심 언어는 거의 절대적으로 깨지는 변경 사항 없이 지속적으로 개선되고 확장됨
- Clojure의 오픈 소스 생태계도 같은 철학을 따르며, 불필요한 변화(churn)를 최소화함
- 이는 다른 대부분의 현대 프로그래밍 언어 생태계와 대조됨
- 불필요한 변화로 인한 리소스 낭비는 전 세계적으로 수십억 달러 규모에 달함
- Clojure에서는 최신 버전의 언어와 라이브러리를 업그레이드하는 것이 매우 자연스러움
- 버그 수정, 보안 업데이트, 성능 개선을 누리면서도 코드베이스를 재작성할 필요 없음
- 다른 언어에서는 새로운 버전이 나오면 깨질 수도 있는 기존 코드가, Clojure에서는 그대로 유지됨
- 일부 개발자는 "변화 없이 어떻게 발전할 수 있는가?"라는 의문을 가질 수도 있음
- 그러나 안정성과 정체(stagnation)는 다름
- Clojure는 기존 기능을 깨지 않으면서도 새로운 기능을 추가하고 개선하는 방식으로 발전
- 개발자들은 불필요한 코드 수정 없이도 지속적으로 더 나은 도구를 사용할 수 있음
정보 시스템과 지식 표현
- 웹 및 비즈니스 애플리케이션 개발에서는 정보 수집, 접근, 처리가 핵심
- 그러나 주류 프로그래밍 언어들은 정보 표현과 조작 측면에서 비효율적인 설계를 보이는 경우가 많음
- 저수준 데이터 구조를 강요하여 불필요한 복잡성을 초래함
- 정적 타입 시스템이 지나치게 경직되어, 유연한 데이터 조작이 어려움
- Clojure는 함수형 데이터 구조와 강력한 데이터 조작 기능을 기본적으로 제공
- 동적 타입 언어로서 "열린 세계 가정(Open World Assumption)" 을 따름
- 이는 데이터 확장성과 유연성을 극대화하는 방식
- **RDF(Semantic Web을 위한 데이터 모델링 프레임워크)**의 영향을 많이 받음
- 대표적인 예로 Datomic 같은 그래프 데이터베이스와의 시너지가 큼
- 네임스페이스가 포함된 키워드를 활용하여 문맥 독립적인 의미 부여가 가능
- 동적 타입 언어로서 "열린 세계 가정(Open World Assumption)" 을 따름
- 네임스페이스 키워드를 활용한 Clojure 맵(Map) 구조는 단순한 JSON보다 더 정교한 의미 표현이 가능
- 추가적인 데이터 확장이 쉽고, 이름 충돌을 피하면서도 직관적인 데이터 표현이 가능
작은 조합 가능한 함수와 불변 데이터
- Clojure는 순수 함수(pure functions)와 불변 데이터(immutable data) 중심으로 프로그래밍하는 것이 일반적
- Java, Ruby, C 스타일의 명령형 코드도 작성할 수 있지만, Clojure다운 코드(idiomatic Clojure)는 매우 다름
- 순수 함수: 입력 값만을 기반으로 결과를 반환하며, 외부 상태를 변경하지 않음
- 불변 데이터: 참조(reference)나 객체(identity)가 아닌 값(value) 기반의 의미를 가짐
- 외부 상태에 영향을 받지 않기 때문에 코드의 예측 가능성이 높음
- 전역 변수 수정이나 예상치 못한 부작용(side effects)이 없음
- 데이터가 변경될 위험이 없으므로 병렬 처리 및 동시성 문제 해결이 용이
동시성 처리
- 현대 컴퓨팅은 멀티코어 프로세서를 기반으로 동작하며, 동시성은 필수적인 요소
- Moore의 법칙이 한계에 도달함에 따라, 병렬성을 활용하는 것이 성능 향상의 핵심
- 그러나 **가변 상태(mutable state)**를 다루면 동기화 문제와 복잡한 타이밍 제어가 필요
- Clojure는 **불변성(immutability)**을 강조하여 동시성 문제를 근본적으로 해결
- 가변 메모리를 조작하면 타이밍과 순서에 대한 의존성이 발생
- 순수한 데이터 변환(data-in, data-out) 방식은 언제나 안전하게 병렬 실행 가능
- Clojure는 JVM의 기존 동시성 기능(
java.util.concurrent
)을 활용하되, 더 높은 수준의 추상화된 도구를 제공- Atoms: CAS(Compare-and-Set)와 자동 재시도를 활용한 원자적 연산 지원
- Refs: 소프트웨어 트랜잭셔널 메모리(Software Transactional Memory, STM) 제공
- Agents: 비동기적으로 업데이트를 적용하는 방식
- Futures: 스레드 풀 기반의 fork-and-join 인터페이스 제공
-
core.async 라이브러리 제공
- Go의 goroutine과 유사한 CSP(Communicating Sequential Processes) 패턴 지원
- Erlang/Elixir의 Actor 모델, Scala의 Akka와 비교 가능
- Clojure의 높은 수준의 추상화 기능을 사용하지 않고, 더 낮은 수준의 동시성 제어 기법도 활용 가능
- 동기화된 큐, 원자적 참조, 락(lock), 세마포어(semaphore), 다양한 스레드 풀, 수동 스레드 관리 등 지원
- 필요한 경우 세밀한 동시성 제어도 가능하지만, 대부분의 경우 추상화된 도구를 활용하는 것이 더 안전하고 효율적
지역적 추론(Local Reasoning)
- 한 번에 고려할 수 있는 코드의 복잡성에는 한계가 있음
- 프로그램의 상태와 변경이 여러 곳에서 발생할 경우, 코드의 이해와 유지보수가 어려워짐
- Clojure가 지역적 추론을 쉽게 만드는 이유
-
순수 함수(pure function) 중심의 코드
- 입력값만으로 함수의 동작을 완전히 이해할 수 있음
- 함수 외부의 상태를 고려할 필요 없음
-
객체 지향 언어와의 차이점
- Clojure는 다형성(polymorphism)을 최소화하여 코드가 어디서 실행되는지 쉽게 파악 가능
- 객체 지향 프로그래밍(OOP)에서는 "모든 것이 다른 곳에서 발생"하지만,
Clojure에서는 네임스페이스에 정의된 함수만 추적하면 됨
-
순수 함수(pure function) 중심의 코드
- Clojure의 일관된 구문 구조 덕분에 코드 리팩토링이 간편
- 불변 데이터와 순수 함수 사용으로 인해 코드 변경 시 예기치 않은 부작용(side effect)이 적음
- 최소한의 명령형 코드만 별도로 분리하여, 명령형 코드와 함수형 코드를 조화롭게 구성
쉬운 테스트
- Clojure의 순수 함수 기반 코드에서는 입력 값을 넣고 출력 값을 확인하는 것만으로 테스트 가능
- 테스트를 위한 복잡한 상태 초기화, 목(mock) 객체 설정, 타이밍 제어 등이 불필요
- 따라서 테스트의 신뢰성이 높아지고, "플레이키(flakiness, 일관성 없는 테스트 실패)"가 적음
- 고급 테스트 기법인 Property Based Testing(Generative Testing) 지원
- 랜덤한 입력값을 생성하여 특정 속성이나 불변식을 위반하는 경우를 탐색
- 최소한의 실패 사례를 찾아주는 shrinking 기법 지원
- 이 개념은 Haskell에서 시작되었으며, 다양한 언어에서 QuickCheck 기반 테스트 프레임워크로 구현됨
- Clojure의 불변 데이터 구조와 REPL 기반 개발 방식과 시너지를 이루어 더욱 효과적
Clojure 개발자 채용의 장점
- 일반적으로 JavaScript, Python 등의 대중적인 언어에 비해 Clojure 개발자는 상대적으로 적음
- 하지만 Clojure 개발자도 적은 만큼, Clojure를 사용하는 기업도 많지 않음
- 따라서 전체적인 수급 균형은 유지되고 있음
- 실제로 Clojure 개발자를 원하는 기업과 개발자는 서로 적절하게 매칭되는 경우가 많음
-
Clojure 개발자는 높은 수준의 문제 해결 능력을 가진 경우가 많음
- 단순히 인기 있는 기술을 배우기보다, 새로운 사고방식과 아이디어를 탐구하는 개발자들이 많음
- 실제로 Clojure를 사용하는 회사들은 지원자의 수는 적지만, 품질이 높은 개발자가 많다고 평가
- 예시: Nubank는 브라질에서 수백 명의 개발자를 직접 Clojure로 교육하여 성공적인 사례를 남김
- Clojure 개발자를 찾는 것은 어렵지만, 적합한 인재를 찾으면 뛰어난 개발자를 확보할 가능성이 높음
- 단순히 언어 경험이 있는 개발자를 찾기보다, 학습 능력이 뛰어난 개발자를 교육하는 것도 좋은 선택
- Clojure 커뮤니티 자체가 문제를 깊이 고민하고 해결하는 개발자를 끌어들이는 특성을 가짐
트레이드오프 및 추상화 수준 조절
- Clojure는 고수준(high-level) 언어이며, 간결하고 표현력이 뛰어난 코드 작성을 지향
- 함수형(불변) 데이터 구조와 강력한 데이터 조작 API 덕분에 불필요한 복잡성이 줄어듦
- Clojure의 데이터 구조는 불변성 보장을 위해 내부적으로 트리 구조(Hash Array Mapped Trie) 사용
- 업데이트 시 경로 복사(path copying)가 발생하여 GC(가비지 컬렉션) 부담이 증가할 수 있음
- Java와의 상호운용(interop) 과정에서 런타임 리플렉션(reflection), 박싱/언박싱이 발생할 수도 있음
- 일반적인 애플리케이션에서는 이 비용이 거의 무시할 수준이며, 개발 생산성이 증가하는 장점이 큼
- 실시간 그래픽 엔진, 신호 처리, 수치 연산 등 고성능이 필요한 경우 저수준 최적화 가능
- Clojure에서 타입 힌트(type hint) 제공으로 리플렉션 제거 및 원시 타입 연산 최적화 가능
- 연속적인 원시 타입 배열 사용으로 CPU 캐시 활용 가능
- GPU 가속 라이브러리 활용 가능
- 대부분의 고수준 언어는 성능이 중요한 경우 C/Rust 등의 네이티브 확장을 사용해야 하지만,
Clojure는 JVM의 최적화(JIT 컴파일)를 활용하여 대부분의 성능 문제를 해결 가능- 프로파일링과 약간의 최적화를 통해 이벤트 루프 등의 성능을 극대화할 수 있음
메타프로그래밍과 데이터 중심 API
- Clojure는 Lisp 계열 언어로서 코드를 데이터처럼 다룰 수 있음
- JSON과 유사하지만, 보다 읽기 쉬운 구조로 프로그램을 표현 가능
- **EDN(Extensible Data Notation)**이라는 데이터 포맷을 활용하여 JSON과 유사한 데이터 표현 제공
- Clojure는 매크로를 이용해 코드 자체를 변환하는 기능 제공
- 그러나 Clojure 커뮤니티는 매크로 사용을 신중하게 제한하는 문화를 가짐
- 매크로는 디버깅이 어렵고 정적 분석 도구와의 호환성이 떨어질 수 있음
- 대신, 데이터 중심(data-driven) API 설계 방식이 선호됨
- 예: HTTP 라우팅, HTML/CSS 생성, 데이터 검증 등
- 특정 함수를 직접 호출하는 대신, 데이터 구조(맵, 벡터)를 이용하여 동작을 지정
- 데이터 기반 API는 동적으로 조작 가능하며, 사용자 설정을 쉽게 저장하고 수정 가능
- 데이터 중심 API 덕분에 런타임에서 시스템을 동적으로 재구성 가능
- 이를 통해 고도로 유연한 시뮬레이션 시스템, 동적 설정 관리, 메타프로그래밍을 쉽게 구현 가능
Java 상호운용(Interop) 및 생태계 활용
- 현대 애플리케이션 개발은 수많은 오픈 소스 라이브러리와 API를 조합하는 과정
- Clojure는 JVM에서 실행되므로, Maven Central에 있는 수백만 개의 Java 패키지 활용 가능
- 리플렉션 없이도 간단하게 Java 라이브러리를 호출 가능
- Clojure는 Java보다 코드가 훨씬 간결하며, REPL을 이용한 실험적 프로그래밍이 용이
- Java보다 훨씬 빠르게 API를 탐색하고 조합 가능
- ClojureScript를 이용하면 같은 방식으로 JavaScript와 NPM 생태계 활용 가능
아이디어 중심의 문화
- 어떤 언어를 사용해도 성공적인 프로젝트는 가능하며, 반대로 잘못된 방식으로 사용하면 어느 언어에서도 실패 가능
- 좋은 도구가 좋은 개발자를 만드는 것은 아님
- Clojure는 입문 난이도가 높고, 학습 과정에서 시행착오가 필요하지만, 이를 통해 깊은 사고가 가능
- 일부 Clojure 프로젝트는 Java나 Python 스타일의 코드 작성 방식이 남아 있어 제대로 활용되지 못함
- 그러나 Clojure의 철학과 아이디어를 받아들인 팀은 더 나은 소프트웨어 설계 역량을 갖추게 됨
- Clojure 커뮤니티는 기존의 개발 방식에 대한 끊임없는 의문을 던지고, 더 나은 방법을 탐구
- Rich Hickey(클로저 창시자)의 강연은 단순한 기술 소개가 아니라 근본적인 소프트웨어 설계 원칙을 탐구
- Clojure 컨퍼런스에서는 라이브러리 소개보다 아이디어, 논문 분석, 경험 공유가 중심
결론
- Clojure는 단순한 프로그래밍 언어가 아니라, 더 나은 소프트웨어 개발 방식을 고민하는 사람들의 커뮤니티
- 이 커뮤니티에서는 자신의 한계를 넓히고, 새로운 가능성을 탐색하는 것이 핵심 가치
- 이런 문화 속에서 성장하는 개발자는 단순히 Clojure를 잘 다루는 것을 넘어, 더 나은 문제 해결 능력을 갖춘 소프트웨어 엔지니어가 됨
개인적으로 클로저를 쓰고 있는데, 본문에 내용에 공감이 많이 갑니다.
업무로는 파이썬과 자바(타입)스크립트를 주로 사용해 왔는데 조금만 관리를 안해도 언어 자체와 라이브러리들의 변화를 따라가지 못해 레거시 코드가 되기 일쑤 였는데, 클로저는 한번 작성해놓은 코드를 일년 뒤에 봐도 바로 수정개발이 용이한 부분이 매우 만족스러웠습니다.
이후 개인용도로는 특정한 라이브러리의 제약이 아니면 클로저를 애용하고 있네요.
Hacker News 의견
-
어떤 프로그래밍을 즐겼냐고 묻는다면, 쉘에서 데이터를 처리하는 파이프라인을 구축하고, 지난 5년 동안 Clojure와 ClojureScript를 작성하는 것이었음
- 현재 4명이 함께 Clojure를 작성하고 있으며, 총 30년 이상의 Clojure 경험을 보유하고 있음
- 프라하에서 Clojure를 선호하는 컨설팅 회사를 시작했으며, 다양한 시스템에 적합한 선택이 될 수 있다고 생각함
-
Clojure를 12년 동안 사용해왔으며, 그 이전에는 12년 이상 Java를 사용했음
- Clojure와 Java로 훌륭한 앱과 라이브러리를 만들었음
- Clojure를 "가장 덜 나쁜 프로그래밍 언어"라고 설명하며, 핵심이 강력하고 안정적임
- 도구를 마스터하면 작업 속도와 정밀도가 향상됨
- Clojure의 REPL 지향 워크플로우는 중요한 가치 제안임
-
Clojure를 작성하는 것을 사랑하며, 다른 언어들과 비교해도 Clojure에 대한 깊은 애정을 설명할 필요가 없음을 깨달았음
- 다른 언어들에 대한 개인적인 문제에 관심이 없으며, Clojure에 대한 사랑은 이론적, 실용적, 감정적, 재정적 이유 때문임
- Clojure는 완벽하지 않지만, 일반적인 프로그래밍 언어 중 가장 만족스러움
-
공동 창업자는 최소한의 회사로 최대한의 제품을 만드는 것을 목표로 함
- 5년 동안 부트스트랩하여 100만 달러 이상의 ARR을 달성했으며, Clojure가 큰 역할을 했음
- Clojure는 프로그래머로서의 행복에도 기여했으며, 앞으로 Clojure 핵심 제품 팀을 성장시킬 계획임
-
Clojure를 사용하여 10년 동안 SaaS 비즈니스를 운영했으며, Clojure 없이는 불가능했을 것임
- 언어의 안정성이 매우 유용하며, 다른 생태계에서는 자주 소프트웨어를 다시 작성해야 하는 경우가 많음
- 피상적인 비판보다는 실제로 언어를 사용한 사람들의 통찰력 있는 게시물을 읽을 것을 권장함
-
Clojure를 사용하는 사람들에게 <a href="https://www.flow-storm.org/">Flow Storm</a>을 추천함
- 좋은 테스트 스위트와 함께 사용하면 다양한 시나리오를 트리거할 수 있음
-
Rich Hickey로부터 많은 것을 배웠으며, Clojure와 FP에 대한 열정이 있었음
- 그러나 비즈니스가 성장하면서 동적 타이핑이 부담이 되었고, 주요 인력이 떠난 후 Clojure 개발자를 찾는 데 어려움을 겪었음
- 현재는 Python과 Go로 대부분의 스택이 구성되어 있음
-
ClojureDocs의 문서가 오래되었다는 지적이 있었으며, 답변에 투표할 수 있는 기능을 추가하고 싶었음
- Google 알고리즘 덕분에 ClojureDocs가 주로 사용되고 있음
-
Clojure의 안정성에 대한 부분이 놀라웠으며, 매년 시도할 때마다 모든 것이 달라졌다고 느낌
- "Hello World" 실행이 여전히 느린지 궁금하며, Clojure를 읽는 것은 즐겁지만 작성하는 것은 항상 장애물이었음
-
공통 Lisp로 시작한 후 Go와 Rust로 이동했지만, 최근 Clojure를 다시 보고 있음
- REPL과 인터랙티브 프로그래밍 덕분에 빠르게 작업을 수행할 수 있음
- JVM은 훌륭한 런타임임