💻 프로그래밍

Clojure

JVM 기반 현대적 Lisp 방언

📖 상세 설명

Clojure는 2007년 Rich Hickey가 만든 현대적인 Lisp 방언으로, JVM(Java Virtual Machine) 위에서 실행됩니다. 60년 역사의 Lisp 패밀리 언어 중 가장 성공적인 현대 방언으로, 함수형 프로그래밍과 불변 데이터 구조를 핵심 철학으로 삼고 있습니다.

Clojure의 가장 큰 특징은 불변성(Immutability)입니다. 모든 데이터 구조가 기본적으로 불변이며, 이를 통해 동시성 프로그래밍에서 발생하는 복잡한 문제들을 원천적으로 방지합니다. Persistent Data Structure를 사용하여 효율적인 불변 컬렉션을 제공합니다.

Clojure는 JVM 생태계와 완벽하게 호환되어 Java 라이브러리를 직접 호출할 수 있습니다. 또한 ClojureScript를 통해 JavaScript로 컴파일되어 브라우저에서도 실행 가능하며, Babashka는 빠른 스크립팅 환경을 제공합니다.

실무에서 Clojure는 대용량 데이터 처리, 동시성이 중요한 시스템, 금융 서비스 등에서 활용됩니다. Nubank(세계 최대 디지털 은행), Walmart, Netflix 등에서 핵심 시스템에 사용하고 있으며, REPL 기반 개발로 빠른 피드백 루프와 인터랙티브한 개발이 가능합니다.

💻 코드 예제

;; 1. 기본 데이터 구조와 불변성
(def user {:name "홍길동" :age 25 :email "hong@example.com"})

;; assoc은 새로운 맵을 반환 (원본 변경 없음)
(def updated-user (assoc user :age 26))
(println (:age user))         ; 25 (원본 유지)
(println (:age updated-user)) ; 26 (새 맵)

;; 2. 함수형 프로그래밍
(defn process-numbers [numbers]
  (->> numbers
       (filter even?)           ; 짝수만 필터링
       (map #(* % %))           ; 제곱
       (reduce +)))             ; 합계

(println (process-numbers [1 2 3 4 5 6 7 8 9 10])) ; 220

;; 3. 동시성 - Atom을 사용한 안전한 상태 관리
(def counter (atom 0))

(defn increment-counter []
  (swap! counter inc))

;; 여러 스레드에서 안전하게 호출 가능
(doall (pmap (fn [_] (increment-counter)) (range 1000)))
(println @counter) ; 1000

;; 4. Java 상호운용
(import '[java.time LocalDateTime]
        '[java.time.format DateTimeFormatter])

(defn current-time-formatted []
  (let [now (LocalDateTime/now)
        formatter (DateTimeFormatter/ofPattern "yyyy-MM-dd HH:mm:ss")]
    (.format now formatter)))

(println (current-time-formatted)) ; "2025-01-25 14:30:00"

;; 5. 매크로 - 코드 생성
(defmacro when-valid [condition & body]
  `(when ~condition
     (try
       ~@body
       (catch Exception e#
         (println "오류 발생:" (.getMessage e#))))))

🗣️ 실무에서 이렇게 말하세요

💬 회의에서
"이 서비스는 동시성 처리가 핵심인데, Clojure의 불변 데이터 구조와 STM을 사용하면 락 없이도 안전한 상태 관리가 가능합니다. 기존 Java 라이브러리도 그대로 활용할 수 있어요."
💬 면접에서
"Clojure의 Persistent Data Structure는 구조적 공유를 통해 불변 데이터를 효율적으로 다룹니다. 예를 들어 백만 개의 요소가 있는 벡터에 하나를 추가해도 O(log n) 시간에 새 벡터를 생성하고, 메모리는 대부분 공유됩니다."
💬 코드 리뷰에서
"여기서 atom 대신 ref를 쓰는 게 좋겠어요. 여러 상태를 한 트랜잭션에서 조정해야 하는데, atom은 개별 업데이트만 보장하거든요. dosync 블록 안에서 ref를 사용하면 ACID 트랜잭션이 가능합니다."

⚠️ 흔한 실수 & 주의사항

JVM 시작 시간 고려하기

Clojure는 JVM 위에서 동작하므로 콜드 스타트 시간이 깁니다. CLI 도구나 스크립트용으로는 Babashka를 사용하거나, GraalVM 네이티브 이미지로 컴파일하세요.

지연 시퀀스(Lazy Sequence) 주의

map, filter 등은 지연 평가됩니다. 부작용이 필요하면 doall이나 dorun으로 강제 평가하세요. 그렇지 않으면 예상치 못한 타이밍에 실행될 수 있습니다.

REPL 주도 개발 활용하기

Clojure의 가장 큰 장점은 REPL입니다. 에디터와 REPL을 연결하여 코드를 즉시 평가하고, 상태를 검사하며 개발하세요. 이는 TDD보다 더 빠른 피드백 루프를 제공합니다.

🔗 관련 용어

📚 더 배우기