์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ Clojure: LISP ๊ธฐ๋ฐ˜ ํ•จ์ˆ˜ํ˜• ์–ธ์–ด์™€๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์™„์ „ ์ •๋ณต ๊ฐ€์ด๋“œ

๐Ÿš€ Clojure: LISP ๊ธฐ๋ฐ˜ ํ•จ์ˆ˜ํ˜• ์–ธ์–ด์™€
๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์™„์ „ ์ •๋ณต ๊ฐ€์ด๋“œ

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ์ฒ ํ•™๋ถ€ํ„ฐ ์‹ค์ „ ํ™œ์šฉ๊นŒ์ง€, ์ง„์งœ ์“ธ๋ชจ์žˆ๋Š” ์ง€์‹๋งŒ ๋‹ด์•˜์–ด์š” โœจ

Clojure ํ•จ์ˆ˜ํ˜• ์–ธ์–ด์˜ ์„ธ๊ณ„ LISP์˜ ์ฒ ํ•™ ร— JVM์˜ ํŒŒ์›Œ ร— ๋ถˆ๋ณ€์„ฑ์˜ ์•ˆ์ •๊ฐ ๐Ÿง  LISP ์ฒ ํ•™ ์ฝ”๋“œ = ๋ฐ์ดํ„ฐ ํ˜ธ๋ชจ์ด์ฝ”๋‹‰์„ฑ(Homoiconicity) ๋งคํฌ๋กœ ์‹œ์Šคํ…œ S-ํ‘œํ˜„์‹ ๊ธฐ๋ฐ˜ ๋ฌธ๋ฒ• 1958๋…„ John McCarthy ์ฐฝ์‹œ โšก Clojure ํŠน์ง• JVM ์œ„์—์„œ ๋™์ž‘ Rich Hickey๊ฐ€ 2007๋…„ ์„ค๊ณ„ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ฐ•์  Java ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์™„์ „ ํ˜ธํ™˜ ClojureScript๋กœ JS ํ™˜๊ฒฝ๋„ ์ง€์› ๐Ÿ”’ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ์˜์†์  ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๊ตฌ์กฐ์  ๊ณต์œ (Structural Sharing) ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ ๋ณด์žฅ ๋ถ€์ž‘์šฉ(Side Effect) ์ตœ์†Œํ™” Phil Bagwell์˜ HAMT ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ธฐ๋ฐ˜ + + #ํ•จ์ˆ˜ํ˜•ํ”„๋กœ๊ทธ๋ž˜๋ฐ #๋ถˆ๋ณ€์„ฑ #๋™์‹œ์„ฑ #JVM #LISP๋ฐฉ์–ธ
๐ŸŽฏ ๋“ค์–ด๊ฐ€๊ธฐ ์ „์— - ์™œ Clojure์•ผ?

์†”์งํžˆ ๋งํ•˜๋ฉด... ์ฒ˜์Œ์— Clojure ๋“ค์—ˆ์„ ๋•Œ "์ด๊ฒŒ ๋ญ์•ผ ใ…‹ใ…‹ ๊ด„ํ˜ธ ๋ฉ์–ด๋ฆฌ ์•„๋‹ˆ์•ผ?" ์‹ถ์—ˆ๋˜ ์‚ฌ๋žŒ ์† ๐Ÿ™‹

๊ทผ๋ฐ ํŒŒ๊ณ ๋“ค๋ฉด ํŒŒ๊ณ ๋“ค์ˆ˜๋ก ์ด ์–ธ์–ด๊ฐ€ ์ง„์งœ ๋ฏธ์ณค๋‹ค๋Š” ๊ฑธ ์•Œ๊ฒŒ ๋จใ…‹ใ…‹
2007๋…„์— Rich Hickey๋ผ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ, ์ด ์‚ฌ๋žŒ์ด ๊ทธ๋ƒฅ ๋งŒ๋“  ๊ฒŒ ์•„๋‹ˆ๋ผ 10๋…„ ๋„˜๊ฒŒ ์‹ค๋ฌด์—์„œ ๊ฒช์€ ๊ณ ํ†ต์„ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ์„ค๊ณ„ํ•œ ์–ธ์–ด์•ผ.

ํŠนํžˆ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๊ณต์œ  ์ƒํƒœ(shared mutable state) ๋•Œ๋ฌธ์— ์ƒ๊ธฐ๋Š” ๋ฒ„๊ทธ๋“ค... ์ด๊ฑฐ ์ง„์งœ ๊ฐœ๋ฐœ์ž๋“ค ๋ฐค์ƒˆ๊ฒŒ ๋งŒ๋“œ๋Š” ์ฃผ๋ฒ”์ด์ž–์•„?
Clojure๋Š” ์ด ๋ฌธ์ œ๋ฅผ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ๊ทผ๋ณธ์ ์œผ๋กœ ํ•ด๊ฒฐํ•ด๋ฒ„๋ฆผ.

๊ทธ๋ฆฌ๊ณ  JVM ์œ„์—์„œ ๋Œ์•„๊ฐ€๋‹ˆ๊นŒ Java ์ƒํƒœ๊ณ„ ๊ทธ๋Œ€๋กœ ์“ธ ์ˆ˜ ์žˆ๊ณ , ์„ฑ๋Šฅ๋„ ๋ณด์žฅ๋จ.
์ด๊ฒŒ ์™œ ์ค‘์š”ํ•˜๋ƒ๋ฉด... ์‹ค๋ฌด์—์„œ "์–ธ์–ด๋Š” ์ข‹์€๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—†์–ด์„œ ๋ชป ์“ฐ๊ฒ ๋‹ค"๋Š” ์ƒํ™ฉ์ด ์–ผ๋งˆ๋‚˜ ๋งŽ์€์ง€ ์•Œ์ž–์•„ ๐Ÿ˜…

๐Ÿ’ก Clojure ํ•œ ์ค„ ์š”์•ฝ: LISP์˜ ์ฒ ํ•™ + JVM์˜ ์‹ค์šฉ์„ฑ + ๋ถˆ๋ณ€์„ฑ์˜ ์•ˆ์ •๊ฐ = ๋™์‹œ์„ฑ ์‹œ๋Œ€์— ์ตœ์ ํ™”๋œ ์–ธ์–ด

๐Ÿ“š LISP์˜ ์—ญ์‚ฌ์™€ Clojure์˜ ํƒ„์ƒ ๋ฐฐ๊ฒฝ
๐Ÿ›๏ธ LISP - 60๋…„์ด ๋„˜์€ ์–ธ์–ด๊ฐ€ ์•„์ง๋„ ์‚ด์•„์žˆ๋Š” ์ด์œ 

LISP(LISt Processing)์€ 1958๋…„ MIT์˜ John McCarthy๊ฐ€ ๋งŒ๋“  ์–ธ์–ด์•ผ.
C์–ธ์–ด๋ณด๋‹ค๋„ ์˜ค๋ž˜๋๊ณ , ์‹ฌ์ง€์–ด FORTRAN ๋‹ค์Œ์œผ๋กœ ์˜ค๋ž˜๋œ ๊ณ ์ˆ˜์ค€ ์–ธ์–ด์ž„.

๊ทผ๋ฐ ์ด๊ฒŒ ์•„์ง๋„ ์“ฐ์ด๊ณ , ์‹ฌ์ง€์–ด Clojure ๊ฐ™์€ ํ˜„๋Œ€์ ์ธ ์–ธ์–ด์˜ ๊ธฐ๋ฐ˜์ด ๋œ๋‹ค๋Š” ๊ฒŒ ์‹ ๊ธฐํ•˜์ง€ ์•Š์•„? ๐Ÿค”

LISP์ด ์‚ด์•„๋‚จ์€ ์ด์œ ๋Š” ๋”ฑ ํ•˜๋‚˜์•ผ: ํ•ต์‹ฌ ์•„์ด๋””์–ด๊ฐ€ ๋„ˆ๋ฌด ๊ฐ•๋ ฅํ•ด์„œ.

LISP์˜ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค์„ ๋ณด๋ฉด:

1 ํ˜ธ๋ชจ์ด์ฝ”๋‹‰์„ฑ(Homoiconicity) - ์ฝ”๋“œ์™€ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ™์€ ํ˜•ํƒœ(S-ํ‘œํ˜„์‹)๋กœ ํ‘œํ˜„๋จ
2 ์ผ๊ธ‰ ํ•จ์ˆ˜(First-class Functions) - ํ•จ์ˆ˜๋ฅผ ๊ฐ’์ฒ˜๋Ÿผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Œ
3 ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ - LISP์ด ์ตœ์ดˆ๋กœ ๋„์ž…ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๋ฐฉ์‹
4 ๋™์  ํƒ€์ดํ•‘ - ๋Ÿฐํƒ€์ž„์— ํƒ€์ž… ๊ฒฐ์ •
5 ์žฌ๊ท€(Recursion) - ๋ฐ˜๋ณต๋ณด๋‹ค ์žฌ๊ท€๋ฅผ ํ†ตํ•œ ๋ฌธ์ œ ํ•ด๊ฒฐ
6 ๋งคํฌ๋กœ ์‹œ์Šคํ…œ - ์–ธ์–ด ์ž์ฒด๋ฅผ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ

์ด ์ค‘์—์„œ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์€ ์ง€๊ธˆ Java, Python, JavaScript ๋‹ค ์“ฐ๊ณ  ์žˆ๊ณ ,
์ผ๊ธ‰ ํ•จ์ˆ˜๋Š” JavaScript ES6+, Python, Kotlin ๋‹ค ์ฑ„ํƒํ–ˆ๊ณ ,
๋งคํฌ๋กœ๋Š”... ์ด๊ฑด ์•„์ง๋„ LISP ๊ณ„์—ด ์–ธ์–ด๋“ค๋งŒ ์ œ๋Œ€๋กœ ์ง€์›ํ•จ ใ…‹ใ…‹

์ฆ‰, ํ˜„๋Œ€ ์–ธ์–ด๋“ค์ด LISP์—์„œ ์•„์ด๋””์–ด๋ฅผ ๋นŒ๋ ค๊ฐ„ ๊ฑฐ์ง€, LISP์ด ํ˜„๋Œ€ ์–ธ์–ด๋ฅผ ๋”ฐ๋ผ๊ฐ„ ๊ฒŒ ์•„๋‹˜. ๋ ˆ์ „๋“œ ใ„นใ…‡

๐ŸŒฑ LISP ๋ฐฉ์–ธ๋“ค์˜ ๊ณ„๋ณด
LISP (1958) John McCarthy / MIT Common Lisp 1984 / ANSI ํ‘œ์ค€ Scheme 1975 / ๋ฏธ๋‹ˆ๋ฉ€๋ฆฌ์ฆ˜ Emacs Lisp 1985 / ์—๋””ํ„ฐ ํ™•์žฅ Racket 1995 / ๊ต์œก์šฉ Clojure โญ 2007 / JVM ๊ธฐ๋ฐ˜ ClojureScript JS ํ™˜๊ฒฝ Clojure CLR .NET ํ™˜๊ฒฝ Babashka ์Šคํฌ๋ฆฝํŒ… Clojure๋Š” LISP ๊ณ„๋ณด์—์„œ ๊ฐ€์žฅ ํ˜„๋Œ€์ ์ด๊ณ  ์‹ค์šฉ์ ์ธ ๋ฐฉ์–ธ์œผ๋กœ ํ‰๊ฐ€๋ฐ›๊ณ  ์žˆ์–ด์š” ๐ŸŽฏ
๐Ÿ’ก Rich Hickey๊ฐ€ Clojure๋ฅผ ๋งŒ๋“  ์ด์œ 

Rich Hickey๋Š” Clojure๋ฅผ ๋งŒ๋“ค๊ธฐ ์ „์— Java์™€ C++๋กœ ์˜ค๋žซ๋™์•ˆ ์‹ค๋ฌด ๊ฐœ๋ฐœ์„ ํ–ˆ์–ด.
๊ทธ๊ฐ€ ๊ฐ€์žฅ ํฌ๊ฒŒ ๋А๋‚€ ๋ฌธ์ œ๋Š” "๊ฐ€๋ณ€ ์ƒํƒœ(mutable state)์™€ ๋™์‹œ์„ฑ(concurrency)์˜ ์กฐํ•ฉ์ด ๋„ˆ๋ฌด ์œ„ํ—˜ํ•˜๋‹ค"๋Š” ๊ฑฐ์˜€์Œ.

์ƒ๊ฐํ•ด๋ด. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋™์‹œ์— ์ˆ˜์ •ํ•˜๋ฉด?
โ†’ ๋ ˆ์ด์Šค ์ปจ๋””์…˜(Race Condition) ๋ฐœ์ƒ
โ†’ ๋ฐ๋“œ๋ฝ(Deadlock) ๋ฐœ์ƒ
โ†’ ๋””๋ฒ„๊น… ์ง€์˜ฅ ์‹œ์ž‘ ๐Ÿ˜ฑ

Java์—์„œ๋Š” ์ด๊ฑธ synchronized, volatile, Lock ๊ฐ™์€ ๊ฑธ๋กœ ํ•ด๊ฒฐํ•˜๋ ค ํ•˜๋Š”๋ฐ,
์ด๊ฒŒ ์ฝ”๋“œ๋ฅผ ์—„์ฒญ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ , ์‹ค์ˆ˜ํ•˜๋ฉด ๋” ํฐ ๋ฒ„๊ทธ๊ฐ€ ์ƒ๊น€.

Rich Hickey์˜ ํ•ด๊ฒฐ์ฑ…์€ ๋‹จ์ˆœํ–ˆ์–ด: "์• ์ดˆ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝ ๋ชป ํ•˜๊ฒŒ ํ•˜๋ฉด ๋˜์ž–์•„?"

์ด๊ฒŒ Clojure์˜ ํ•ต์‹ฌ ์ฒ ํ•™์ด์•ผ. ๋ถˆ๋ณ€์„ฑ(Immutability)์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋งŒ๋“ค์–ด๋ฒ„๋ฆฐ ๊ฑฐ์ง€.
๊ทธ๋ฆฌ๊ณ  LISP์˜ ๊ฐ„๊ฒฐํ•œ ๋ฌธ๋ฒ•๊ณผ ๋งคํฌ๋กœ ์‹œ์Šคํ…œ์„ ๊ฐ€์ ธ์™€์„œ, JVM ์œ„์—์„œ ์‹คํ–‰๋˜๋Š” ์‹ค์šฉ์ ์ธ ์–ธ์–ด๋ฅผ ๋งŒ๋“  ๊ฑฐ์ž„.


๐Ÿ”ค Clojure ๋ฌธ๋ฒ• ๊ธฐ์ดˆ - ๊ด„ํ˜ธ๊ฐ€ ๋ฌด์„œ์›Œ? ๊ฑฑ์ • ใ„ดใ„ด
๐Ÿ“ S-ํ‘œํ˜„์‹(S-expression) ์ดํ•ดํ•˜๊ธฐ

Clojure ์ฝ”๋“œ๋ฅผ ์ฒ˜์Œ ๋ณด๋ฉด ์ด๋Ÿฐ ๋А๋‚Œ์ž„:

(+ 1 2 3) โ†’ ์ด๊ฒŒ ๋ญ์•ผ ใ…‹ใ…‹

๊ทผ๋ฐ ์ด๊ฒŒ ์‚ฌ์‹ค ์—„์ฒญ ๋…ผ๋ฆฌ์ ์ธ ๊ตฌ์กฐ์•ผ.
์ผ๋ฐ˜ ์–ธ์–ด์—์„œ 1 + 2 + 3 ์ด๋ผ๊ณ  ์“ฐ๋Š” ๊ฑธ,
Clojure์—์„œ๋Š” (ํ•จ์ˆ˜ ์ธ์ž1 ์ธ์ž2 ์ธ์ž3) ํ˜•ํƒœ๋กœ ์”€.

์ฆ‰, ๋ชจ๋“  ๊ฒƒ์ด ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด์•ผ. ์—ฐ์‚ฐ์ž๋„ ํ•จ์ˆ˜๊ณ , ์กฐ๊ฑด๋ฌธ๋„ ํ•จ์ˆ˜๊ณ , ๋ฐ˜๋ณต๋„ ํ•จ์ˆ˜์ž„.
์ด๊ฒŒ ์ฒ˜์Œ์—” ์–ด์ƒ‰ํ•œ๋ฐ, ์ต์ˆ™ํ•ด์ง€๋ฉด ์˜คํžˆ๋ ค ์ผ๊ด€์„ฑ์ด ์žˆ์–ด์„œ ํŽธํ•จ.

๐Ÿ’ป ๊ธฐ๋ณธ ๋ฌธ๋ฒ• ์˜ˆ์ œ๋“ค
; ์ฃผ์„์€ ์„ธ๋ฏธ์ฝœ๋ก ์œผ๋กœ ์‹œ์ž‘ํ•ด
; ๊ธฐ๋ณธ ์—ฐ์‚ฐ
(+ 1 2 3)        ; => 6
(* 2 3 4)        ; => 24
(- 10 3)         ; => 7
(/ 10 2)         ; => 5

; ๋ฌธ์ž์—ด
(str "์•ˆ๋…•" " " "ํด๋กœ์ €!")  ; => "์•ˆ๋…• ํด๋กœ์ €!"
(count "hello")              ; => 5

; ๋ณ€์ˆ˜ ์ •์˜ (def)
(def my-name "ํด๋กœ์ € ๊ฐœ๋ฐœ์ž")
(def pi 3.14159)

; ํ•จ์ˆ˜ ์ •์˜ (defn)
(defn greet [name]
  (str "์•ˆ๋…•ํ•˜์„ธ์š”, " name "๋‹˜!"))

(greet "์žฌ๋Šฅ๋„ท ์œ ์ €")  ; => "์•ˆ๋…•ํ•˜์„ธ์š”, ์žฌ๋Šฅ๋„ท ์œ ์ €๋‹˜!"

; ์กฐ๊ฑด๋ฌธ (if)
(if (> 10 5)
  "10์ด ๋” ํฌ๋‹ค"
  "5๊ฐ€ ๋” ํฌ๋‹ค")
; => "10์ด ๋” ํฌ๋‹ค"

; let์œผ๋กœ ์ง€์—ญ ๋ณ€์ˆ˜ ๋ฐ”์ธ๋”ฉ
(let [x 10
      y 20
      sum (+ x y)]
  (str "ํ•ฉ๊ณ„: " sum))
; => "ํ•ฉ๊ณ„: 30"
๐Ÿ’ก ๊ฟ€ํŒ: Clojure์—์„œ def๋Š” ์ „์—ญ ๋ณ€์ˆ˜, let์€ ์ง€์—ญ ๋ณ€์ˆ˜ ๋ฐ”์ธ๋”ฉ์ด์•ผ. ๊ทธ๋ฆฌ๊ณ  ํ•œ๋ฒˆ ๋ฐ”์ธ๋”ฉ๋œ ๊ฐ’์€ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€! ์ด๊ฒŒ ๋ถˆ๋ณ€์„ฑ์˜ ์‹œ์ž‘์ž„.
๐ŸŽฏ Clojure์˜ ํ•ต์‹ฌ ๋ฐ์ดํ„ฐ ํƒ€์ž…๋“ค
ํƒ€์ž…์˜ˆ์‹œ์„ค๋ช…
์ˆซ์ž42, 3.14, 22/7์ •์ˆ˜, ์‹ค์ˆ˜, ์œ ๋ฆฌ์ˆ˜ ์ง€์›
๋ฌธ์ž์—ด"hello"Java String๊ณผ ๋™์ผ
ํ‚ค์›Œ๋“œ:name, :age์ฝœ๋ก ์œผ๋กœ ์‹œ์ž‘, ์ž๊ธฐ ์ž์‹ ์ด ๊ฐ’
์‹ฌ๋ณผfoo, my-var๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜๋ฅผ ์ฐธ์กฐ
๋ถˆ๋ฆฌ์–ธtrue, falsenil๋„ false๋กœ ์ทจ๊ธ‰
nilnilJava์˜ null๊ณผ ๋™์ผ
๋ฌธ์ž\a, \newline๋ฐฑ์Šฌ๋ž˜์‹œ๋กœ ์‹œ์ž‘
๐Ÿ—‚๏ธ ์ปฌ๋ ‰์…˜ ํƒ€์ž… - ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ํ•ต์‹ฌ
; ๋ฒกํ„ฐ (Vector) - ์ˆœ์„œ ์žˆ๋Š” ์ปฌ๋ ‰์…˜, ์ธ๋ฑ์Šค ์ ‘๊ทผ O(1)
(def my-vec [1 2 3 4 5])
(get my-vec 0)    ; => 1
(nth my-vec 2)    ; => 3
(count my-vec)    ; => 5

; ๋ฆฌ์ŠคํŠธ (List) - ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ, ์•ž์—์„œ ์ถ”๊ฐ€ O(1)
(def my-list '(1 2 3 4 5))
(first my-list)   ; => 1
(rest my-list)    ; => (2 3 4 5)

; ๋งต (Map) - ํ‚ค-๊ฐ’ ์Œ
(def person {:name "๊น€ํด๋กœ์ €"
             :age 30
             :job "๊ฐœ๋ฐœ์ž"})
(get person :name)   ; => "๊น€ํด๋กœ์ €"
(:age person)        ; => 30  (ํ‚ค์›Œ๋“œ๋ฅผ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ์‚ฌ์šฉ!)

; ์…‹ (Set) - ์ค‘๋ณต ์—†๋Š” ์ปฌ๋ ‰์…˜
(def my-set #{1 2 3 4 5})
(contains? my-set 3)  ; => true
(contains? my-set 6)  ; => false
โš ๏ธ ์ค‘์š”! Clojure์˜ ๋ชจ๋“  ์ปฌ๋ ‰์…˜์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ถˆ๋ณ€(Immutable)์ด์•ผ. ๊ฐ’์„ "๋ณ€๊ฒฝ"ํ•˜๋ฉด ์ƒˆ๋กœ์šด ์ปฌ๋ ‰์…˜์ด ๋ฐ˜ํ™˜๋˜๊ณ , ์›๋ณธ์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€๋จ. ์ด๊ฒŒ ํ•ต์‹ฌ ๊ฐœ๋…์ด๋‹ˆ๊นŒ ๊ผญ ๊ธฐ์–ตํ•ด!

๐Ÿ”’ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ - Clojure์˜ ์ง„์งœ ๊ฐ•์ 
๐Ÿค” ๋ถˆ๋ณ€์„ฑ์ด ์™œ ์ค‘์š”ํ•œ๊ฐ€?

๋ถˆ๋ณ€์„ฑ(Immutability)... ์ด๊ฑฐ ์ฒ˜์Œ ๋“ค์œผ๋ฉด "๊ทธ๋Ÿผ ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ๋ฐ”๊ฟ”?" ์‹ถ์ž–์•„ ใ…‹ใ…‹

๊ทผ๋ฐ ์ƒ๊ฐํ•ด๋ด. ์šฐ๋ฆฌ๊ฐ€ ์ผ์ƒ์—์„œ ์“ฐ๋Š” ์ˆซ์ž 5๋Š” ๋ณ€ํ•˜์ง€ ์•Š์ž–์•„?
5 + 3 = 8์ด๋ผ๊ณ  ํ•ด์„œ 5๊ฐ€ 8์ด ๋˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์ƒˆ๋กœ์šด ๊ฐ’ 8์ด ์ƒ๊ธฐ๋Š” ๊ฑฐ์ž–์•„.

Clojure๋Š” ์ด ๊ฐœ๋…์„ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์— ์ ์šฉํ•œ ๊ฑฐ์•ผ.
๋ฒกํ„ฐ์— ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด? โ†’ ์ƒˆ๋กœ์šด ๋ฒกํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜๋จ. ์›๋ณธ์€ ๊ทธ๋Œ€๋กœ.
๋งต์—์„œ ๊ฐ’์„ ๋ฐ”๊พธ๋ฉด? โ†’ ์ƒˆ๋กœ์šด ๋งต์ด ๋ฐ˜ํ™˜๋จ. ์›๋ณธ์€ ๊ทธ๋Œ€๋กœ.

์ด๊ฒŒ ์™œ ์ข‹๋ƒ๋ฉด:

1 ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ - ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด๋„ ์ ˆ๋Œ€ ์ถฉ๋Œ ์—†์Œ
2 ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ - ํ•จ์ˆ˜์— ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ฒจ๋„ ์›๋ณธ์ด ๋ฐ”๋€” ๊ฑฑ์ • ์—†์Œ
3 ๋””๋ฒ„๊น… ์šฉ์ด - ๋ฐ์ดํ„ฐ๊ฐ€ ์–ธ์ œ ์–ด๋””์„œ ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ์ถ”์  ๋ถˆํ•„์š”
4 ์‹œ๊ฐ„ ์—ฌํ–‰ ๋””๋ฒ„๊น… - ๊ณผ๊ฑฐ ์ƒํƒœ๋ฅผ ์–ธ์ œ๋“  ์ฐธ์กฐ ๊ฐ€๋Šฅ
5 ์บ์‹ฑ ์ตœ์ ํ™” - ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ๋Š” ์•ˆ์ „ํ•˜๊ฒŒ ์บ์‹œ ๊ฐ€๋Šฅ

โšก ๊ตฌ์กฐ์  ๊ณต์œ (Structural Sharing) - ์„ฑ๋Šฅ ๋ฌธ์ œ ํ•ด๊ฒฐ์˜ ๋น„๋ฐ€

"๋ถˆ๋ณ€์ด๋ฉด ๋ฐ์ดํ„ฐ ๋ฐ”๊ฟ€ ๋•Œ๋งˆ๋‹ค ์ „์ฒด๋ฅผ ๋ณต์‚ฌํ•ด์•ผ ํ•˜๋Š” ๊ฑฐ ์•„๋‹ˆ์•ผ? ๊ทธ๋Ÿผ ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„ ์‹ฌํ•˜์ง€ ์•Š์•„?" ๐Ÿค”

์ด ์งˆ๋ฌธ ์ง„์งœ ์ข‹์€ ์งˆ๋ฌธ์ž„! ๊ทธ๋ฆฌ๊ณ  Clojure๋Š” ์ด ๋ฌธ์ œ๋ฅผ ๊ตฌ์กฐ์  ๊ณต์œ (Structural Sharing)๋กœ ํ•ด๊ฒฐํ•ด.

ํ•ต์‹ฌ ์•„์ด๋””์–ด: ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ์ƒˆ๋กœ ๋งŒ๋“ค๊ณ , ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๋ถ€๋ถ„์€ ๊ณต์œ ํ•จ.

Clojure์˜ ์˜์†์  ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ(Persistent Data Structures)๋Š” HAMT(Hash Array Mapped Trie) ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด.
Phil Bagwell์ด 2001๋…„์— ๋ฐœํ‘œํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ธ๋ฐ, Rich Hickey๊ฐ€ ์ด๊ฑธ Clojure์— ์ ์šฉํ•œ ๊ฑฐ์•ผ.

HAMT์˜ ํŠน์ง•:

โ€ข ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ
โ€ข ์ˆ˜์ • ์‹œ ๋ณ€๊ฒฝ๋œ ๊ฒฝ๋กœ์˜ ๋…ธ๋“œ๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑ
โ€ข ๋‚˜๋จธ์ง€ ๋…ธ๋“œ๋Š” ์ด์ „ ๋ฒ„์ „๊ณผ ๊ณต์œ 
โ€ข ๋Œ€๋ถ€๋ถ„์˜ ์—ฐ์‚ฐ์ด O(logโ‚ƒโ‚‚ n) ์‹œ๊ฐ„ ๋ณต์žก๋„ (์‚ฌ์‹ค์ƒ O(1)์— ๊ฐ€๊นŒ์›€)
โ€ข 32-way branching factor๋กœ ํŠธ๋ฆฌ ๊นŠ์ด ์ตœ์†Œํ™”

๊ตฌ์กฐ์  ๊ณต์œ (Structural Sharing) ์‹œ๊ฐํ™” ๋ฒกํ„ฐ [1, 2, 3, 4] ์—์„œ ์ธ๋ฑ์Šค 2๋ฅผ 99๋กœ ๋ณ€๊ฒฝํ•  ๋•Œ ์›๋ณธ: [1, 2, 3, 4] ๋ฃจํŠธ ๋…ธ๋“œ ๋…ธ๋“œ A ๋…ธ๋“œ B 1 2 3 4 โ†’ assoc idx 2 ๊ฐ’ 99๋กœ ๋ณ€๊ฒฝ ์ƒˆ ๋ฒ„์ „: [1, 2, 99, 4] ์ƒˆ ๋ฃจํŠธ โœจ ๋…ธ๋“œ A ๊ณต์œ ๋จ โ™ป๏ธ ์ƒˆ ๋…ธ๋“œ B' โœจ ์ƒˆ๋กœ ์ƒ์„ฑ 1 2 99 โœจ 4 ํ•ต์‹ฌ ํฌ์ธํŠธ ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ๋…ธ๋“œ (๋ฃจํŠธ, ๋…ธ๋“œB', ๊ฐ’99) ๊ณต์œ ๋œ ๋…ธ๋“œ (๋…ธ๋“œA, ๊ฐ’1, ๊ฐ’2, ๊ฐ’4) - ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ! ์ „์ฒด ๋ณต์‚ฌ ์—†์ด ๋ณ€๊ฒฝ๋œ ๊ฒฝ๋กœ๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑ โ†’ ์‹œ๊ฐ„/๊ณต๊ฐ„ ํšจ์œจ์ !
๐Ÿ› ๏ธ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ์กฐ์ž‘ ํ•จ์ˆ˜๋“ค
; ๋ฒกํ„ฐ ์กฐ์ž‘
(def v [1 2 3 4 5])

(conj v 6)          ; => [1 2 3 4 5 6]  (๋์— ์ถ”๊ฐ€)
(assoc v 2 99)      ; => [1 2 99 4 5]   (์ธ๋ฑ์Šค 2๋ฅผ 99๋กœ)
(dissoc v 2)        ; ๋ฒกํ„ฐ์—๋Š” dissoc ์•ˆ ์”€
(subvec v 1 3)      ; => [2 3]          (์Šฌ๋ผ์ด์‹ฑ)
(into [] (rest v))  ; => [2 3 4 5]      (์ฒซ ์š”์†Œ ์ œ๊ฑฐ)

; ์›๋ณธ์€ ๊ทธ๋Œ€๋กœ!
v  ; => [1 2 3 4 5]  ๋ณ€ํ•˜์ง€ ์•Š์Œ!

; ๋งต ์กฐ์ž‘
(def m {:a 1 :b 2 :c 3})

(assoc m :d 4)      ; => {:a 1, :b 2, :c 3, :d 4}
(dissoc m :b)       ; => {:a 1, :c 3}
(update m :a inc)   ; => {:a 2, :b 2, :c 3}
(merge m {:d 4 :e 5}) ; => {:a 1, :b 2, :c 3, :d 4, :e 5}

; ์ค‘์ฒฉ ๋งต ์กฐ์ž‘
(def nested {:user {:name "๊น€ํด๋กœ์ €" :age 30}})
(assoc-in nested [:user :age] 31)
; => {:user {:name "๊น€ํด๋กœ์ €", :age 31}}

(get-in nested [:user :name])
; => "๊น€ํด๋กœ์ €"

(update-in nested [:user :age] inc)
; => {:user {:name "๊น€ํด๋กœ์ €", :age 31}}
๐Ÿ’ก ๊ฟ€ํŒ: assoc-in, get-in, update-in์€ ์ค‘์ฒฉ๋œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ์ง„์งœ ์œ ์šฉํ•ด. ๊ฒฝ๋กœ๋ฅผ ๋ฒกํ„ฐ๋กœ ์ง€์ •ํ•˜๋ฉด ๊นŠ์€ ๊ณณ๊นŒ์ง€ ์ ‘๊ทผ ๊ฐ€๋Šฅ!

๐ŸŽจ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค
๐Ÿ”„ ๊ณ ์ฐจ ํ•จ์ˆ˜(Higher-Order Functions)

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ฝƒ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ ์ฐจ ํ•จ์ˆ˜!
ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ฑฐ๋‚˜, ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งํ•ด.

Clojure์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ๋Š” ๊ณ ์ฐจ ํ•จ์ˆ˜๋“ค:

; map - ์ปฌ๋ ‰์…˜์˜ ๊ฐ ์š”์†Œ์— ํ•จ์ˆ˜ ์ ์šฉ
(map inc [1 2 3 4 5])
; => (2 3 4 5 6)

(map #(* % 2) [1 2 3 4 5])
; => (2 4 6 8 10)

(map str [1 2 3])
; => ("1" "2" "3")

; filter - ์กฐ๊ฑด์— ๋งž๋Š” ์š”์†Œ๋งŒ ์ถ”์ถœ
(filter even? [1 2 3 4 5 6])
; => (2 4 6)

(filter #(> % 3) [1 2 3 4 5])
; => (4 5)

; reduce - ์ปฌ๋ ‰์…˜์„ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ์ถ•์•ฝ
(reduce + [1 2 3 4 5])
; => 15

(reduce * [1 2 3 4 5])
; => 120

(reduce (fn [acc x] (+ acc (* x x))) 0 [1 2 3 4 5])
; => 55  (1ยฒ + 2ยฒ + 3ยฒ + 4ยฒ + 5ยฒ)

; ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ!
(->> [1 2 3 4 5 6 7 8 9 10]
     (filter even?)
     (map #(* % %))
     (reduce +))
; => 220  (2ยฒ + 4ยฒ + 6ยฒ + 8ยฒ + 10ยฒ)
๐Ÿ”— ์Šค๋ ˆ๋”ฉ ๋งคํฌ๋กœ - ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์˜ ํ˜๋ช…

Clojure์—์„œ ์ง„์งœ ํŽธํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๊ฐ€ ์Šค๋ ˆ๋”ฉ ๋งคํฌ๋กœ์•ผ.
๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด.

-> (์Šค๋ ˆ๋“œ ํผ์ŠคํŠธ): ์ฒซ ๋ฒˆ์งธ ์ธ์ž ์œ„์น˜์— ์‚ฝ์ž…
->> (์Šค๋ ˆ๋“œ ๋ผ์ŠคํŠธ): ๋งˆ์ง€๋ง‰ ์ธ์ž ์œ„์น˜์— ์‚ฝ์ž…

; ์Šค๋ ˆ๋”ฉ ๋งคํฌ๋กœ ์—†์ด (์ฝ๊ธฐ ์–ด๋ ค์›€ ใ… ใ… )
(str (clojure.string/upper-case
       (clojure.string/trim
         "  hello world  ")))
; => "HELLO WORLD"

; -> ์Šค๋ ˆ๋”ฉ ๋งคํฌ๋กœ ์‚ฌ์šฉ (ํ›จ์”ฌ ์ฝ๊ธฐ ์‰ฌ์›€!)
(-> "  hello world  "
    clojure.string/trim
    clojure.string/upper-case
    str)
; => "HELLO WORLD"

; ->> ์Šค๋ ˆ๋”ฉ ๋งคํฌ๋กœ (์ปฌ๋ ‰์…˜ ์ฒ˜๋ฆฌ์— ์œ ์šฉ)
(->> (range 1 11)
     (filter odd?)
     (map #(* % %))
     (reduce +))
; => 165  (1ยฒ + 3ยฒ + 5ยฒ + 7ยฒ + 9ยฒ)

; as-> ์Šค๋ ˆ๋”ฉ ๋งคํฌ๋กœ (์œ„์น˜๋ฅผ ์ง์ ‘ ์ง€์ •)
(as-> 5 x
  (* x x)
  (+ x 10)
  (str "๊ฒฐ๊ณผ: " x))
; => "๊ฒฐ๊ณผ: 35"
๐ŸŽญ ํด๋กœ์ €(Closure) - ์ด๋ฆ„์˜ ์œ ๋ž˜

Clojure๋ผ๋Š” ์ด๋ฆ„์ด ์‚ฌ์‹ค ํด๋กœ์ €(Closure)์—์„œ ์™”์–ด.
(C, L, J๋ฅผ ํฌํ•จํ•ด์„œ C-L-J-ure... Java, Lisp, Clojure์˜ ์•ž๊ธ€์ž ์กฐํ•ฉ์ด๊ธฐ๋„ ํ•จ ใ…‹ใ…‹)

ํด๋กœ์ €(Closure)๋Š” ํ•จ์ˆ˜๊ฐ€ ์ž์‹ ์ด ์ •์˜๋œ ํ™˜๊ฒฝ์˜ ๋ณ€์ˆ˜๋ฅผ "์บก์ฒ˜"ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•ด.

; ํด๋กœ์ € ์˜ˆ์ œ
(defn make-adder [n]
  (fn [x] (+ x n)))  ; n์„ ์บก์ฒ˜ํ•˜๋Š” ํด๋กœ์ €

(def add5 (make-adder 5))
(def add10 (make-adder 10))

(add5 3)   ; => 8
(add10 3)  ; => 13

; ๋” ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ
(defn make-counter []
  (let [count (atom 0)]  ; atom์€ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์ฐธ์กฐ (๋‚˜์ค‘์— ์„ค๋ช…!)
    {:increment #(swap! count inc)
     :decrement #(swap! count dec)
     :value     #(deref count)}))

(def counter (make-counter))
((:increment counter))
((:increment counter))
((:increment counter))
((:value counter))  ; => 3
๐Ÿ”€ ๋ถ€๋ถ„ ์ ์šฉ(Partial Application)๊ณผ ์ปค๋ง(Currying)
; partial - ํ•จ์ˆ˜์˜ ์ผ๋ถ€ ์ธ์ž๋ฅผ ๋ฏธ๋ฆฌ ์ ์šฉ
(def multiply-by-3 (partial * 3))
(multiply-by-3 5)   ; => 15
(multiply-by-3 10)  ; => 30

; ์‹ค์šฉ์ ์ธ ์˜ˆ
(def add-tax (partial * 1.1))
(add-tax 1000)  ; => 1100.0
(add-tax 5000)  ; => 5500.0

; comp - ํ•จ์ˆ˜ ํ•ฉ์„ฑ
(def process-name
  (comp clojure.string/upper-case
        clojure.string/trim))

(process-name "  hello  ")  ; => "HELLO"

; juxt - ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ๋™์‹œ์— ์ ์šฉ
(def stats (juxt min max #(/ (reduce + %) (count %))))
(stats [3 1 4 1 5 9 2 6])
; => [1 9 31/8]  (์ตœ์†Ÿ๊ฐ’, ์ตœ๋Œ“๊ฐ’, ํ‰๊ท )

โšก ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ - Clojure์˜ ํ‚ฌ๋Ÿฌ ๊ธฐ๋Šฅ
๐Ÿงฉ Clojure์˜ ๋™์‹œ์„ฑ ๋ชจ๋ธ

Clojure๋Š” ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด 4๊ฐ€์ง€ ์ฐธ์กฐ ํƒ€์ž…์„ ์ œ๊ณตํ•ด.
๊ฐ๊ฐ ๋‹ค๋ฅธ ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์•ผ. ์ด๊ฒŒ ์ง„์งœ ์ž˜ ์„ค๊ณ„๋œ ๋ถ€๋ถ„์ž„ ๐Ÿ‘

๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ + ์ด 4๊ฐ€์ง€ ์ฐธ์กฐ ํƒ€์ž… = ๋™์‹œ์„ฑ ๋ฒ„๊ทธ ์—†๋Š” ์ฝ”๋“œ!

Clojure 4๊ฐ€์ง€ ์ฐธ์กฐ ํƒ€์ž… ๊ฐ๊ฐ ๋‹ค๋ฅธ ๋™์‹œ์„ฑ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์œ„ํ•ด ์„ค๊ณ„๋จ โš›๏ธ Atom ๋‹จ์ผ ๊ฐ’์˜ ๋™๊ธฐ์  ๋ณ€๊ฒฝ โ€ข CAS ๊ธฐ๋ฐ˜ ์›์ž์  ์—…๋ฐ์ดํŠธ โ€ข swap!, reset! ํ•จ์ˆ˜ ์‚ฌ์šฉ โ€ข ๋…๋ฆฝ์ ์ธ ์ƒํƒœ ๊ด€๋ฆฌ โ€ข ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๋Š” ํƒ€์ž… ์นด์šดํ„ฐ, ์บ์‹œ, ์„ค์ •๊ฐ’ ๋™๊ธฐ / ๋น„์กฐ์œจ ๋…๋ฆฝ์  ๋ณ€๊ฒฝ ๐Ÿ”— Ref STM ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋ฐ˜ โ€ข ์†Œํ”„ํŠธ์›จ์–ด ํŠธ๋žœ์žญ์…˜ ๋ฉ”๋ชจ๋ฆฌ โ€ข dosync ๋ธ”๋ก ๋‚ด์—์„œ ๋ณ€๊ฒฝ โ€ข ์—ฌ๋Ÿฌ Ref ๋™์‹œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ โ€ข ACID ์†์„ฑ ๋ณด์žฅ ์€ํ–‰ ๊ณ„์ขŒ, ์žฌ๊ณ  ๊ด€๋ฆฌ ๋™๊ธฐ / ์กฐ์œจ๋จ ํŠธ๋žœ์žญ์…˜ ๋ณ€๊ฒฝ ๐Ÿค– Agent ๋น„๋™๊ธฐ ๋…๋ฆฝ์  ๋ณ€๊ฒฝ โ€ข ์Šค๋ ˆ๋“œ ํ’€์—์„œ ๋น„๋™๊ธฐ ์‹คํ–‰ โ€ข send, send-off ํ•จ์ˆ˜ ์‚ฌ์šฉ โ€ข ์ˆœ์„œ ๋ณด์žฅ (์ง๋ ฌํ™”) โ€ข ์—๋Ÿฌ ํ•ธ๋“ค๋ง ๋‚ด์žฅ ๋กœ๊ทธ, ํŒŒ์ผ I/O, ์ด๋ฒคํŠธ ๋น„๋™๊ธฐ / ๋น„์กฐ์œจ ๋…๋ฆฝ์  ๋ณ€๊ฒฝ ๐Ÿ“Œ Var ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ๋ฐ”์ธ๋”ฉ โ€ข def๋กœ ์ƒ์„ฑ๋˜๋Š” ๊ธฐ๋ณธ ํƒ€์ž… โ€ข binding์œผ๋กœ ๋™์  ๋ฐ”์ธ๋”ฉ โ€ข ์Šค๋ ˆ๋“œ๋ณ„ ๋…๋ฆฝ์  ๊ฐ’ โ€ข ์ฃผ๋กœ ์„ค์ •/์ปจํ…์ŠคํŠธ์šฉ DB ์—ฐ๊ฒฐ, ์„ค์ •, ์ปจํ…์ŠคํŠธ ๋™๊ธฐ / ์Šค๋ ˆ๋“œ ๋กœ์ปฌ ๋™์  ๋ฐ”์ธ๋”ฉ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ + 4๊ฐ€์ง€ ์ฐธ์กฐ ํƒ€์ž… = ์•ˆ์ „ํ•œ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๐ŸŽฏ
๐Ÿ’ป Atom ์‹ค์ „ ์˜ˆ์ œ
; Atom ์ƒ์„ฑ
(def counter (atom 0))
(def user-db (atom {}))

; ๊ฐ’ ์ฝ๊ธฐ
@counter        ; => 0  (deref์™€ ๋™์ผ)
(deref counter) ; => 0

; ๊ฐ’ ๋ณ€๊ฒฝ (swap! - ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•ด์„œ ๋ณ€๊ฒฝ)
(swap! counter inc)     ; => 1
(swap! counter + 5)     ; => 6
(swap! counter * 2)     ; => 12

; ๊ฐ’ ๊ต์ฒด (reset! - ์ƒˆ ๊ฐ’์œผ๋กœ ์ง์ ‘ ๊ต์ฒด)
(reset! counter 0)      ; => 0

; ์‹ค์šฉ์ ์ธ ์˜ˆ: ์‚ฌ์šฉ์ž DB ๊ด€๋ฆฌ
(swap! user-db assoc :user1 {:name "๊น€ํด๋กœ์ €" :age 30})
(swap! user-db assoc :user2 {:name "์ดํ•จ์ˆ˜ํ˜•" :age 25})
(swap! user-db update-in [:user1 :age] inc)

@user-db
; => {:user1 {:name "๊น€ํด๋กœ์ €", :age 31},
;     :user2 {:name "์ดํ•จ์ˆ˜ํ˜•", :age 25}}

; ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋„ ์•ˆ์ „!
(def safe-counter (atom 0))
(doall
  (pmap (fn [_] (swap! safe-counter inc))
        (range 1000)))
@safe-counter  ; => 1000  (ํ•ญ์ƒ ์ •ํ™•ํ•œ ๊ฐ’!)
๐Ÿฆ Ref์™€ STM - ํŠธ๋žœ์žญ์…˜ ์˜ˆ์ œ
; ์€ํ–‰ ๊ณ„์ขŒ ์˜ˆ์ œ (STM์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ!)
(def account-a (ref 1000))
(def account-b (ref 500))

; ์ด์ฒด ํ•จ์ˆ˜
(defn transfer! [from to amount]
  (dosync  ; ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
    (when (>= @from amount)
      (alter from - amount)
      (alter to + amount)
      true)))

; ์ด์ฒด ์‹คํ–‰
(transfer! account-a account-b 300)
; => true

@account-a  ; => 700
@account-b  ; => 800

; ๋™์‹œ์— ์—ฌ๋Ÿฌ ์ด์ฒด๊ฐ€ ์ผ์–ด๋‚˜๋„ ์•ˆ์ „!
; STM์ด ์ถฉ๋Œ์„ ๊ฐ์ง€ํ•˜๊ณ  ์ž๋™์œผ๋กœ ์žฌ์‹œ๋„ํ•จ
(future (transfer! account-a account-b 100))
(future (transfer! account-b account-a 50))

; ์ž ์‹œ ํ›„...
(+ @account-a @account-b)  ; => 1500  (ํ•ญ์ƒ ์ดํ•ฉ ๋ณด์กด!)

๐Ÿ”ฎ Clojure ๋งคํฌ๋กœ ์‹œ์Šคํ…œ - ์–ธ์–ด๋ฅผ ํ™•์žฅํ•˜๋Š” ๋Šฅ๋ ฅ
๐Ÿคฏ ๋งคํฌ๋กœ๊ฐ€ ๋ญ”๋ฐ?

๋งคํฌ๋กœ๋Š” Clojure(LISP)์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์•ผ.
์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ.

์ผ๋ฐ˜ ํ•จ์ˆ˜๋Š” ๋Ÿฐํƒ€์ž„์— ์‹คํ–‰๋˜์ง€๋งŒ, ๋งคํฌ๋กœ๋Š” ์ปดํŒŒ์ผ ํƒ€์ž„์— ์ฝ”๋“œ๋ฅผ ๋ณ€ํ™˜ํ•ด.
์ด๊ฒŒ ๊ฐ€๋Šฅํ•œ ์ด์œ ๊ฐ€ ๋ฐ”๋กœ ํ˜ธ๋ชจ์ด์ฝ”๋‹‰์„ฑ(Homoiconicity)์ด์•ผ.
Clojure์—์„œ ์ฝ”๋“œ ์ž์ฒด๊ฐ€ ๋ฐ์ดํ„ฐ(๋ฆฌ์ŠคํŠธ)์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ฝ”๋“œ๋ฅผ ๋ฐ์ดํ„ฐ์ฒ˜๋Ÿผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ์–ด.

์ด๊ฒŒ ์™œ ๊ฐ•๋ ฅํ•˜๋ƒ๋ฉด... ์–ธ์–ด์— ์—†๋Š” ๊ธฐ๋Šฅ์„ ์ง์ ‘ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ !
์˜ˆ๋ฅผ ๋“ค์–ด if, when, and, or ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์‚ฌ์‹ค Clojure์—์„œ ๋งคํฌ๋กœ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์–ด ใ„ทใ„ท

; ๋งคํฌ๋กœ ์ •์˜ ์˜ˆ์ œ
; when ๋งคํฌ๋กœ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ
(defmacro my-when [condition & body]
  `(if ~condition
     (do ~@body)
     nil))

; ์‚ฌ์šฉ
(my-when (> 10 5)
  (println "10์ด ๋” ํฌ๋‹ค!")
  (println "๋งž์•„!"))
; => 10์ด ๋” ํฌ๋‹ค!
; => ๋งž์•„!

; ๋งคํฌ๋กœ ํ™•์žฅ ํ™•์ธ (macroexpand)
(macroexpand '(my-when (> 10 5) (println "hello")))
; => (if (> 10 5) (do (println "hello")) nil)

; ์‹ค์šฉ์ ์ธ ๋งคํฌ๋กœ ์˜ˆ์ œ: ํƒ€์ด๋ฐ ์ธก์ •
(defmacro time-it [& body]
  `(let [start# (System/nanoTime)
         result# (do ~@body)
         end# (System/nanoTime)]
     (println (str "์‹คํ–‰ ์‹œ๊ฐ„: "
                   (/ (- end# start#) 1000000.0)
                   "ms"))
     result#))

(time-it
  (Thread/sleep 100)
  (+ 1 2 3))
; => ์‹คํ–‰ ์‹œ๊ฐ„: 100.xxx ms
; => 6

; ๋กœ๊น… ๋งคํฌ๋กœ
(defmacro log-call [fn-name & args]
  `(do
     (println (str "[LOG] " ~fn-name " ํ˜ธ์ถœ: " ~@args))
     (~(symbol fn-name) ~@args)))
๐Ÿ’ก ๋งคํฌ๋กœ ์‚ฌ์šฉ ํŒ: ๋งคํฌ๋กœ๋Š” ๊ฐ•๋ ฅํ•˜์ง€๋งŒ ๋‚จ์šฉํ•˜๋ฉด ์ฝ”๋“œ ์ดํ•ด๊ฐ€ ์–ด๋ ค์›Œ์ ธ. "ํ•จ์ˆ˜๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ํ•จ์ˆ˜๋ฅผ ์จ๋ผ, ๋งคํฌ๋กœ๋Š” ์ •๋ง ํ•„์š”ํ•  ๋•Œ๋งŒ"์ด Clojure ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ์›์น™์ด์•ผ!

๐ŸŒŠ ์‹œํ€€์Šค ์ถ”์ƒํ™” - Clojure์˜ ํ†ต์ผ๋œ ์ธํ„ฐํŽ˜์ด์Šค
๐ŸŽฏ ๋ชจ๋“  ์ปฌ๋ ‰์…˜์„ ํ•˜๋‚˜์˜ ์ธํ„ฐํŽ˜์ด์Šค๋กœ

Clojure์˜ ์ง„์งœ ์šฐ์•„ํ•œ ๋ถ€๋ถ„ ์ค‘ ํ•˜๋‚˜๊ฐ€ ์‹œํ€€์Šค ์ถ”์ƒํ™”(Sequence Abstraction)์•ผ.

๋ฒกํ„ฐ๋“ , ๋ฆฌ์ŠคํŠธ๋“ , ๋งต์ด๋“ , ์…‹์ด๋“ , ์‹ฌ์ง€์–ด ํŒŒ์ผ์ด๋‚˜ ๋„คํŠธ์›Œํฌ ์ŠคํŠธ๋ฆผ์ด๋“ ...
๋ชจ๋‘ ๋™์ผํ•œ ์‹œํ€€์Šค ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด.

first, rest, next, cons ๊ฐ™์€ ํ•จ์ˆ˜๋“ค์ด ๋ชจ๋“  ์‹œํ€€์Šค์— ๋™์ž‘ํ•จ.
๊ทธ๋ฆฌ๊ณ  map, filter, reduce ๊ฐ™์€ ๊ณ ์ฐจ ํ•จ์ˆ˜๋“ค๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์•ผ.

์ด๊ฒŒ ์™œ ์ข‹๋ƒ๋ฉด... ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๋งŒ๋“ค์–ด๋„ ์‹œํ€€์Šค ํ”„๋กœํ† ์ฝœ๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด
๊ธฐ์กด์˜ ๋ชจ๋“  ํ•จ์ˆ˜๋“ค์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

; ๋‹ค์–‘ํ•œ ์ปฌ๋ ‰์…˜์— ๋™์ผํ•œ ํ•จ์ˆ˜ ์ ์šฉ
(first [1 2 3])        ; => 1  (๋ฒกํ„ฐ)
(first '(1 2 3))       ; => 1  (๋ฆฌ์ŠคํŠธ)
(first #{1 2 3})       ; => 1  (์…‹, ์ˆœ์„œ ๋ถˆํ™•์ •)
(first {:a 1 :b 2})    ; => [:a 1]  (๋งต์€ ํ‚ค-๊ฐ’ ์Œ)

; ์ง€์—ฐ ์‹œํ€€์Šค (Lazy Sequence)
; ๋ฌดํ•œ ์‹œํ€€์Šค๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด!
(def natural-numbers (range))  ; 0๋ถ€ํ„ฐ ๋ฌดํ•œ๋Œ€๊นŒ์ง€
(take 10 natural-numbers)
; => (0 1 2 3 4 5 6 7 8 9)

; ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜์—ด (์ง€์—ฐ ์‹œํ€€์Šค)
(def fibs
  (lazy-cat [0 1]
            (map + fibs (rest fibs))))

(take 10 fibs)
; => (0 1 1 2 3 5 8 13 21 34)

; ๋ฌดํ•œ ์‹œํ€€์Šค์—์„œ ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ
(->> (range)
     (filter even?)
     (map #(* % %))
     (take 5))
; => (0 4 16 36 64)

; iterate - ํ•จ์ˆ˜๋ฅผ ๋ฐ˜๋ณต ์ ์šฉ
(take 8 (iterate #(* 2 %) 1))
; => (1 2 4 8 16 32 64 128)

; cycle - ๋ฌดํ•œ ๋ฐ˜๋ณต
(take 7 (cycle [1 2 3]))
; => (1 2 3 1 2 3 1)

; repeat - ๊ฐ’ ๋ฐ˜๋ณต
(take 5 (repeat "hello"))
; => ("hello" "hello" "hello" "hello" "hello")
๐Ÿ”ง ํŠธ๋žœ์Šค๋“€์„œ(Transducers) - ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ๋ํŒ์™•

ํŠธ๋žœ์Šค๋“€์„œ๋Š” Clojure 1.7์—์„œ ๋„์ž…๋œ ๊ฐœ๋…์œผ๋กœ, Rich Hickey๊ฐ€ ์ง์ ‘ ์„ค๊ณ„ํ•œ ๊ฑฐ์•ผ.

๊ธฐ์กด์˜ map, filter ๊ฐ™์€ ํ•จ์ˆ˜๋“ค์€ ์ค‘๊ฐ„ ์ปฌ๋ ‰์…˜์„ ์ƒ์„ฑํ•ด.
์˜ˆ๋ฅผ ๋“ค์–ด (map f (filter g coll))์€ filter ๊ฒฐ๊ณผ๋ฅผ ๋‹ด๋Š” ์ค‘๊ฐ„ ์ปฌ๋ ‰์…˜์ด ์ƒ๊น€.

ํŠธ๋žœ์Šค๋“€์„œ๋Š” ์ด ์ค‘๊ฐ„ ์ปฌ๋ ‰์…˜ ์—†์ด ๋ณ€ํ™˜์„ ํ•ฉ์„ฑํ•  ์ˆ˜ ์žˆ์–ด.
โ†’ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ ํ–ฅ์ƒ
โ†’ ์„ฑ๋Šฅ ํ–ฅ์ƒ
โ†’ ์ปฌ๋ ‰์…˜, ์ฑ„๋„, ์ŠคํŠธ๋ฆผ ๋“ฑ ์–ด๋””์„œ๋“  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ

; ์ผ๋ฐ˜ ๋ฐฉ์‹ (์ค‘๊ฐ„ ์ปฌ๋ ‰์…˜ ์ƒ์„ฑ)
(->> [1 2 3 4 5 6 7 8 9 10]
     (filter even?)
     (map #(* % %))
     (into []))
; ์ค‘๊ฐ„์— (2 4 6 8 10) ์ปฌ๋ ‰์…˜์ด ์ƒ์„ฑ๋จ

; ํŠธ๋žœ์Šค๋“€์„œ ๋ฐฉ์‹ (์ค‘๊ฐ„ ์ปฌ๋ ‰์…˜ ์—†์Œ!)
(def xf
  (comp
    (filter even?)
    (map #(* % %))))

(into [] xf [1 2 3 4 5 6 7 8 9 10])
; => [4 16 36 64 100]

; transduce๋กœ ๋ฐ”๋กœ reduce
(transduce xf + [1 2 3 4 5 6 7 8 9 10])
; => 220

; ์ฑ„๋„์—๋„ ์ ์šฉ ๊ฐ€๋Šฅ (core.async)
; (chan 10 xf)  ; ์ฑ„๋„์— ํŠธ๋žœ์Šค๋“€์„œ ์ ์šฉ

๐Ÿ—๏ธ ์‹ค์ „ ํ”„๋กœ์ ํŠธ - Clojure๋กœ ๋ญ˜ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด?
๐ŸŒ ์›น ๊ฐœ๋ฐœ - Ring๊ณผ Compojure

Clojure๋กœ ์›น ๊ฐœ๋ฐœ๋„ ์ถฉ๋ถ„ํžˆ ํ•  ์ˆ˜ ์žˆ์–ด!
Ring์€ Clojure์˜ HTTP ์ถ”์ƒํ™” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ณ ,
Compojure๋Š” ๋ผ์šฐํŒ… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ.
Python์˜ WSGI + Flask ์กฐํ•ฉ์ด๋ž‘ ๋น„์Šทํ•œ ๋А๋‚Œ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋จ.

; project.clj (Leiningen ํ”„๋กœ์ ํŠธ ์„ค์ •)
(defproject my-web-app "0.1.0"
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [ring/ring-core "1.9.6"]
                 [ring/ring-jetty-adapter "1.9.6"]
                 [compojure "1.7.0"]
                 [ring/ring-json "0.5.1"]])

; src/my_web_app/core.clj
(ns my-web-app.core
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.adapter.jetty :as jetty]
            [ring.middleware.json :as json]
            [ring.util.response :as response]))

; ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ (Atom ์‚ฌ์šฉ!)
(def users (atom [{:id 1 :name "๊น€ํด๋กœ์ €" :age 30}
                  {:id 2 :name "์ดํ•จ์ˆ˜ํ˜•" :age 25}]))

; ๋ผ์šฐํŠธ ์ •์˜
(defroutes app-routes
  (GET "/" []
    (response/response "Clojure ์›น ์„œ๋ฒ„์— ์˜ค์‹  ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!"))

  (GET "/users" []
    (response/response @users))

  (GET "/users/:id" [id]
    (let [user-id (Integer/parseInt id)
          user (first (filter #(= (:id %) user-id) @users))]
      (if user
        (response/response user)
        (response/not-found "์‚ฌ์šฉ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"))))

  (POST "/users" {body :body}
    (let [new-user (assoc body :id (inc (count @users)))]
      (swap! users conj new-user)
      (response/created "/users" new-user)))

  (route/not-found "ํŽ˜์ด์ง€๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"))

; ๋ฏธ๋“ค์›จ์–ด ์ ์šฉ
(def app
  (-> app-routes
      json/wrap-json-response
      json/wrap-json-body))

; ์„œ๋ฒ„ ์‹œ์ž‘
(defn -main []
  (jetty/run-jetty app {:port 3000 :join? false})
  (println "์„œ๋ฒ„ ์‹œ์ž‘! http://localhost:3000"))
๐Ÿ“Š ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ - Clojure์˜ ์ง„์งœ ๊ฐ•์ 
; CSV ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์˜ˆ์ œ
(require '[clojure.string :as str])
(require '[clojure.java.io :as io])

; ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ
(def raw-data
  "์ด๋ฆ„,๋‚˜์ด,์ ์ˆ˜,๋ถ€์„œ
  ๊น€ํด๋กœ์ €,30,95,๊ฐœ๋ฐœ
  ์ดํ•จ์ˆ˜ํ˜•,25,88,๊ฐœ๋ฐœ
  ๋ฐ•๋ถˆ๋ณ€,35,92,๊ธฐํš
  ์ตœ๋™์‹œ์„ฑ,28,78,๊ฐœ๋ฐœ
  ์ •๋งคํฌ๋กœ,32,85,๊ธฐํš")

; ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ ํ•จ์ˆ˜
(defn parse-csv [csv-str]
  (let [lines (str/split-lines (str/trim csv-str))
        headers (map keyword (str/split (first lines) #","))
        rows (rest lines)]
    (map (fn [row]
           (zipmap headers
                   (str/split (str/trim row) #",")))
         rows)))

(def employees (parse-csv raw-data))

; ๋ฐ์ดํ„ฐ ๋ถ„์„
; ๊ฐœ๋ฐœํŒ€ ํ‰๊ท  ์ ์ˆ˜
(->> employees
     (filter #(= (:๋ถ€์„œ %) "๊ฐœ๋ฐœ"))
     (map #(Integer/parseInt (:์ ์ˆ˜ %)))
     (fn [scores] (/ (reduce + scores) (count scores))))
; => 87  (๊ฐœ๋ฐœํŒ€ ํ‰๊ท )

; ๋ถ€์„œ๋ณ„ ๊ทธ๋ฃนํ•‘
(group-by :๋ถ€์„œ employees)
; => {"๊ฐœ๋ฐœ" [...], "๊ธฐํš" [...]}

; ์ ์ˆ˜ ๊ธฐ์ค€ ์ •๋ ฌ
(sort-by #(Integer/parseInt (:์ ์ˆ˜ %)) > employees)
; ์ ์ˆ˜ ๋†’์€ ์ˆœ์œผ๋กœ ์ •๋ ฌ
๐Ÿ”„ core.async - Go ์Šคํƒ€์ผ ๋™์‹œ์„ฑ
; core.async ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ
(require '[clojure.core.async :as async
           :refer [chan go go-loop >! <! >!! <!! close!]])

; ์ฑ„๋„ ์ƒ์„ฑ
(def ch (chan 10))

; ์ƒ์‚ฐ์ž (Producer)
(go
  (doseq [i (range 5)]
    (>! ch (* i i))
    (println (str "์ „์†ก: " (* i i))))
  (close! ch))

; ์†Œ๋น„์ž (Consumer)
(go-loop []
  (when-let [val (<! ch)]
    (println (str "์ˆ˜์‹ : " val))
    (recur)))

; ํŒŒ์ดํ”„๋ผ์ธ ์ฒ˜๋ฆฌ
(def input-ch (chan 10))
(def output-ch (chan 10))

; ๋ณ€ํ™˜ ํŒŒ์ดํ”„๋ผ์ธ
(async/pipeline
  4  ; ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์ˆ˜
  output-ch
  (comp (filter even?) (map #(* % %)))
  input-ch)

; ๋ฐ์ดํ„ฐ ์ „์†ก
(go
  (doseq [i (range 10)]
    (>! input-ch i))
  (close! input-ch))

; ๊ฒฐ๊ณผ ์ˆ˜์ง‘
(go-loop [results []]
  (if-let [val (<! output-ch)]
    (recur (conj results val))
    (println "๊ฒฐ๊ณผ:" results)))

๐Ÿ› ๏ธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ • - ์‹œ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ•
โš™๏ธ ์„ค์น˜ ๋ฐ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

Clojure ์‹œ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€์•ผ:
Leiningen (์ „ํ†ต์ ์ธ ๋ฐฉ๋ฒ•) ๋˜๋Š” Clojure CLI + deps.edn (์ตœ์‹  ๋ฐฉ๋ฒ•)

์š”์ฆ˜์€ Clojure CLI ๋ฐฉ์‹์„ ๋” ๋งŽ์ด ์ถ”์ฒœํ•˜๋Š” ์ถ”์„ธ์•ผ.

; 1. Java ์„ค์น˜ (JDK 11 ์ด์ƒ ๊ถŒ์žฅ)
; https://adoptium.net/ ์—์„œ ๋‹ค์šด๋กœ๋“œ

; 2. Clojure CLI ์„ค์น˜ (macOS)
; brew install clojure/tools/clojure

; 3. Leiningen ์„ค์น˜ (์„ ํƒ)
; brew install leiningen

; 4. ์ƒˆ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
; lein new app my-clojure-app
; ๋˜๋Š”
; clojure -Tnew app :name myname/myapp

; 5. REPL ์‹œ์ž‘ (์ง„์งœ ์ค‘์š”! Clojure ๊ฐœ๋ฐœ์˜ ํ•ต์‹ฌ)
; lein repl
; ๋˜๋Š”
; clj

; 6. ์ฝ”๋“œ ์‹คํ–‰
; lein run
; ๋˜๋Š”
; clj -M -m my-app.core
๐ŸŽฎ ์ถ”์ฒœ ์—๋””ํ„ฐ์™€ ํ”Œ๋Ÿฌ๊ทธ์ธ
์—๋””ํ„ฐํ”Œ๋Ÿฌ๊ทธ์ธํŠน์ง•
VS CodeCalva๊ฐ€์žฅ ์‰ฝ๊ฒŒ ์‹œ์ž‘ ๊ฐ€๋Šฅ, REPL ํ†ตํ•ฉ
IntelliJ IDEACursive๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ IDE ์ง€์›, ์œ ๋ฃŒ
EmacsCIDER์ „ํ†ต์ ์ธ Clojure ๊ฐœ๋ฐœ ํ™˜๊ฒฝ
Vim/NeovimConjurevim ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•œ ์„ ํƒ
Sublime TextSublimeREPL๊ฐ€๋ฒผ์šด ์—๋””ํ„ฐ ์„ ํ˜ธ์‹œ
๐Ÿ’ก REPL ์ฃผ๋„ ๊ฐœ๋ฐœ(REPL-Driven Development): Clojure ๊ฐœ๋ฐœ์˜ ํ•ต์‹ฌ์€ REPL์ด์•ผ. ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ์ฆ‰์‹œ ์‹คํ–‰ํ•ด๋ณด๊ณ , ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜๋ฉด์„œ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ์‹. ์ด๊ฒŒ ์ต์ˆ™ํ•ด์ง€๋ฉด ๋‹ค๋ฅธ ์–ธ์–ด๋กœ ๋Œ์•„๊ฐ€๊ธฐ ์‹ซ์–ด์ง ใ…‹ใ…‹
๐Ÿ“ฆ ์ฃผ์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ƒํƒœ๊ณ„
๐ŸŒ ์›น ๊ฐœ๋ฐœ

RingCompojure
PedestalLuminus
ReititBidi
๐Ÿ—„๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

next.jdbcHoneySQL
DatomicCrux
MongerCarmine
๐Ÿงช ํ…Œ์ŠคํŒ…

clojure.testMidje
Expectationstest.check
Kaocha
๐Ÿ“Š ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

core.asyncManifold
Incantertech.ml
Tablecloth

๐Ÿ†š ๋‹ค๋ฅธ ์–ธ์–ด์™€์˜ ๋น„๊ต
๐Ÿ“Š Clojure vs ๋‹ค๋ฅธ ํ•จ์ˆ˜ํ˜• ์–ธ์–ด๋“ค
ํŠน์ง•ClojureHaskellScalaElixir
ํƒ€์ž… ์‹œ์Šคํ…œ๋™์  ํƒ€์ž…์ •์  ํƒ€์ž…์ •์  ํƒ€์ž…๋™์  ํƒ€์ž…
์‹คํ–‰ ํ™˜๊ฒฝJVM๋„ค์ดํ‹ฐ๋ธŒJVMBEAM VM
๋ถˆ๋ณ€์„ฑ๊ธฐ๋ณธ๊ฐ’๊ธฐ๋ณธ๊ฐ’์„ ํƒ์ ๊ธฐ๋ณธ๊ฐ’
ํ•™์Šต ๊ณก์„ ์ค‘๊ฐ„๋งค์šฐ ๋†’์Œ๋†’์Œ์ค‘๊ฐ„
Java ํ˜ธํ™˜์™„์ „ ํ˜ธํ™˜์—†์Œ์™„์ „ ํ˜ธํ™˜์—†์Œ
๋™์‹œ์„ฑSTM, AtomSTMAkkaActor ๋ชจ๋ธ
๋งคํฌ๋กœ๊ฐ•๋ ฅํ•จ์ œํ•œ์ ์ œํ•œ์ ์žˆ์Œ
์ƒํƒœ๊ณ„Java ํ™œ์šฉ๋…์ž์ Java ํ™œ์šฉErlang ํ™œ์šฉ
๐Ÿค” Clojure๊ฐ€ ์ ํ•ฉํ•œ ์ƒํ™ฉ

Clojure๊ฐ€ ํŠนํžˆ ๋น›๋‚˜๋Š” ์ƒํ™ฉ๋“ค:

1 ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ ํŒŒ์ดํ”„๋ผ์ธ - ETL, ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ, ๋ถ„์„
2 ๋™์‹œ์„ฑ์ด ์ค‘์š”ํ•œ ์„œ๋ฒ„ - ๋†’์€ ์ฒ˜๋ฆฌ๋Ÿ‰, ์‹ค์‹œ๊ฐ„ ์‹œ์Šคํ…œ
3 ๊ธฐ์กด Java ์‹œ์Šคํ…œ๊ณผ ํ†ตํ•ฉ - Java ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ทธ๋Œ€๋กœ ํ™œ์šฉ
4 ๋„๋ฉ”์ธ ํŠนํ™” ์–ธ์–ด(DSL) ๊ฐœ๋ฐœ - ๋งคํฌ๋กœ๋กœ ์–ธ์–ด ํ™•์žฅ
5 REPL ๊ธฐ๋ฐ˜ ํƒ์ƒ‰์  ๊ฐœ๋ฐœ - ๋ฐ์ดํ„ฐ ๊ณผํ•™, ํ”„๋กœํ† ํƒ€์ดํ•‘
6 ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค - ์ž‘๊ณ  ๋…๋ฆฝ์ ์ธ ์„œ๋น„์Šค ๊ตฌ์„ฑ


๋ฐ˜๋ฉด Clojure๊ฐ€ ๋œ ์ ํ•ฉํ•œ ์ƒํ™ฉ:
โ€ข ํŒ€ ์ „์ฒด๊ฐ€ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ์ต์ˆ™ํ•˜์ง€ ์•Š์„ ๋•Œ
โ€ข ๊ฐ•ํ•œ ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ํ•„์š”ํ•  ๋•Œ (Haskell์ด๋‚˜ Scala ๊ณ ๋ ค)
โ€ข ๋ชจ๋ฐ”์ผ ์•ฑ ๊ฐœ๋ฐœ (ClojureScript๋กœ React Native๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋ณต์žกํ•จ)
โ€ข ๊ฒŒ์ž„ ๊ฐœ๋ฐœ (์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ)


๐ŸŽ“ Clojure ํ•™์Šต ๋กœ๋“œ๋งต
Clojure ํ•™์Šต ๋กœ๋“œ๋งต ๐Ÿ—บ๏ธ 1๋‹จ๊ณ„: ๊ธฐ์ดˆ (1~2์ฃผ) โ€ข REPL ํ™˜๊ฒฝ ์„ค์ • ๋ฐ ๊ธฐ๋ณธ ๋ฌธ๋ฒ• ์ตํžˆ๊ธฐ โ€ข ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ํƒ€์ž…, ์ปฌ๋ ‰์…˜, ํ•จ์ˆ˜ ์ •์˜ (def, defn, let) โ€ข ์ถ”์ฒœ: "Clojure for the Brave and True" (๋ฌด๋ฃŒ ์˜จ๋ผ์ธ) โ€ข ์ถ”์ฒœ: 4Clojure ๋ฌธ์ œ ํ’€๊ธฐ (์—ฐ์Šต ์‚ฌ์ดํŠธ) 2๋‹จ๊ณ„: ํ•จ์ˆ˜ํ˜• ์‚ฌ๊ณ  (2~4์ฃผ) โ€ข ๊ณ ์ฐจ ํ•จ์ˆ˜ (map, filter, reduce) ๋งˆ์Šคํ„ฐ โ€ข ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์™€ ๊ตฌ์กฐ์  ๊ณต์œ  ์ดํ•ด โ€ข ์Šค๋ ˆ๋”ฉ ๋งคํฌ๋กœ (->, ->>) ํ™œ์šฉ โ€ข ํด๋กœ์ €, ๋ถ€๋ถ„ ์ ์šฉ, ํ•จ์ˆ˜ ํ•ฉ์„ฑ 3๋‹จ๊ณ„: ์ค‘๊ธ‰ ๊ฐœ๋… (1~2๊ฐœ์›”) โ€ข ๋™์‹œ์„ฑ (Atom, Ref, Agent, Var) โ€ข ๋งคํฌ๋กœ ์‹œ์Šคํ…œ ์ดํ•ด ๋ฐ ์ž‘์„ฑ โ€ข ์‹œํ€€์Šค ์ถ”์ƒํ™”, ์ง€์—ฐ ์‹œํ€€์Šค โ€ข ํ”„๋กœํ† ์ฝœ(Protocol)๊ณผ ๋ฉ€ํ‹ฐ๋ฉ”์„œ๋“œ 4๋‹จ๊ณ„: ์‹ค์ „ (2~3๊ฐœ์›”) โ€ข ์›น ๊ฐœ๋ฐœ (Ring, Compojure, Reitit) โ€ข core.async๋กœ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ โ€ข ํŠธ๋žœ์Šค๋“€์„œ, ์ŠคํŽ™(clojure.spec) โ€ข ์‹ค์ œ ํ”„๋กœ์ ํŠธ ์™„์„ฑ ๋ฐ ๋ฐฐํฌ ๐Ÿ’ก ํ•ต์‹ฌ: ๋งค์ผ REPL์—์„œ ์ฝ”๋“œ ์‹คํ—˜ํ•˜๊ธฐ! ์ด๋ก ๋ณด๋‹ค ์‹ค์Šต์ด ํ›จ์”ฌ ์ค‘์š”ํ•ด์š”
๐Ÿ“š ์ถ”์ฒœ ํ•™์Šต ์ž๋ฃŒ

๐Ÿ“– ์ฑ… โ€ข Clojure for the Brave and True - Daniel Higginbotham (๋ฌด๋ฃŒ ์˜จ๋ผ์ธ, ์ดˆ๋ณด์ž ์ตœ๊ณ )
โ€ข Programming Clojure - Alex Miller, Stuart Halloway
โ€ข The Joy of Clojure - Michael Fogus, Chris Houser (์ค‘๊ธ‰~๊ณ ๊ธ‰)
โ€ข Clojure Applied - Ben Vandgrift, Alex Miller (์‹ค์ „ ํŒจํ„ด)

๐ŸŒ ์˜จ๋ผ์ธ ์ž๋ฃŒ
โ€ข clojure.org - ๊ณต์‹ ๋ฌธ์„œ (์ง„์งœ ์ž˜ ๋˜์–ด์žˆ์Œ)
โ€ข clojuredocs.org - ํ•จ์ˆ˜๋ณ„ ์˜ˆ์ œ ํ’๋ถ€
โ€ข 4clojure.com - ๋ฌธ์ œ ํ’€๊ธฐ ์—ฐ์Šต
โ€ข exercism.io/tracks/clojure - ์ฒด๊ณ„์ ์ธ ์—ฐ์Šต ๋ฌธ์ œ

๐ŸŽฅ ์˜์ƒ
โ€ข Rich Hickey์˜ ๊ฐ•์—ฐ๋“ค (Simple Made Easy, The Value of Values ๋“ฑ) - ์œ ํŠœ๋ธŒ์—์„œ ๋ฌด๋ฃŒ
โ€ข ClojureTV ์œ ํŠœ๋ธŒ ์ฑ„๋„

๐Ÿ’ฌ ์ปค๋ฎค๋‹ˆํ‹ฐ
โ€ข Clojurians Slack - ๊ฐ€์žฅ ํ™œ๋ฐœํ•œ ์ปค๋ฎค๋‹ˆํ‹ฐ
โ€ข r/Clojure ์„œ๋ธŒ๋ ˆ๋”ง
โ€ข ์žฌ๋Šฅ๋„ท์—์„œ๋„ Clojure ๊ด€๋ จ ์žฌ๋Šฅ์„ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์–ด! ๋ฉ˜ํ† ๋ง์ด๋‚˜ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ์„œ๋น„์Šค๋ฅผ ํ™œ์šฉํ•˜๋ฉด ํ•™์Šต ์†๋„๊ฐ€ ํ›จ์”ฌ ๋นจ๋ผ์ง ๐Ÿš€


๐ŸŒŸ Clojure์˜ ์ฒ ํ•™ - Rich Hickey์˜ ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€
๐Ÿ’ญ Simple Made Easy

Rich Hickey์˜ ๊ฐ€์žฅ ์œ ๋ช…ํ•œ ๊ฐ•์—ฐ ์ œ๋ชฉ์ด "Simple Made Easy"์•ผ.
์—ฌ๊ธฐ์„œ ๊ทธ๋Š” ๋‹จ์ˆœํ•จ(Simple)๊ณผ ์‰ฌ์›€(Easy)์„ ๊ตฌ๋ถ„ํ•ด.

Easy(์‰ฌ์›€): ์šฐ๋ฆฌ์—๊ฒŒ ์นœ์ˆ™ํ•˜๊ณ , ๊ฐ€๊นŒ์ด ์žˆ๊ณ , ๋ฐฐ์šฐ๊ธฐ ์‰ฌ์šด ๊ฒƒ
Simple(๋‹จ์ˆœํ•จ): ๋ณต์žกํ•˜๊ฒŒ ์–ฝํ˜€์žˆ์ง€ ์•Š์€ ๊ฒƒ, ํ•˜๋‚˜์˜ ์—ญํ• ๋งŒ ํ•˜๋Š” ๊ฒƒ

์˜ˆ๋ฅผ ๋“ค์–ด Java์˜ ๊ฐ€๋ณ€ ๊ฐ์ฒด๋Š” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‹จ์ˆœํ•˜์ง€ ์•Š์•„.
์™œ๋ƒํ•˜๋ฉด ๊ฐ์ฒด์˜ ์ƒํƒœ๊ฐ€ ์–ธ์ œ ์–ด๋””์„œ ๋ฐ”๋€”์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋ณต์žก์„ฑ์ด ์ฆ๊ฐ€ํ•˜๊ฑฐ๋“ .

๋ฐ˜๋ฉด Clojure์˜ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ๋Š” ์ฒ˜์Œ์—” ์–ด์ƒ‰ํ•˜๊ฒŒ ๋А๊ปด์ง€์ง€๋งŒ, ๋‹จ์ˆœํ•ด.
๋ฐ์ดํ„ฐ๊ฐ€ ์ ˆ๋Œ€ ๋ฐ”๋€Œ์ง€ ์•Š์œผ๋‹ˆ๊นŒ ์ถ”๋ก ํ•˜๊ธฐ ์‰ฝ๊ณ , ๋ฒ„๊ทธ๊ฐ€ ์ค„์–ด๋“ค์–ด.

์ด ์ฒ ํ•™์ด Clojure ์ „์ฒด ์„ค๊ณ„์— ๋…น์•„์žˆ์–ด. ๊ทธ๋ž˜์„œ ์ฒ˜์Œ์—” ์–ด์ƒ‰ํ•ด๋„ ์ต์ˆ™ํ•ด์ง€๋ฉด ์ง„์งœ ํŽธํ•จ ใ…‹ใ…‹

๐ŸŽฏ Clojure์˜ ํ•ต์‹ฌ ์ฒ ํ•™ ํ•œ ์ค„ ์š”์•ฝ: "๋ณต์žก์„ฑ(Complexity)์€ ์†Œํ”„ํŠธ์›จ์–ด์˜ ๊ฐ€์žฅ ํฐ ์ ์ด๋‹ค. ๋ถˆ๋ณ€์„ฑ๊ณผ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ๋ณต์žก์„ฑ์„ ๊ทผ๋ณธ์ ์œผ๋กœ ์ค„์—ฌ๋ผ."
๐Ÿข ์‹ค์ œ ์‚ฌ์šฉ ์‚ฌ๋ก€

Clojure๊ฐ€ ์‹ค์ œ๋กœ ์–ด๋””์„œ ์“ฐ์ด๋ƒ๊ณ ? ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์€ ๊ณณ์—์„œ ์“ฐ์—ฌ!

๐Ÿฆ ๊ธˆ์œต/ํ•€ํ…Œํฌ
โ€ข Nubank (๋ธŒ๋ผ์งˆ ์ตœ๋Œ€ ๋””์ง€ํ„ธ ๋ฑ…ํฌ) - Clojure๋ฅผ ์ฃผ๋ ฅ ์–ธ์–ด๋กœ ์‚ฌ์šฉ
โ€ข Funding Circle - ๋Œ€์ถœ ํ”Œ๋žซํผ
โ€ข Stripe - ์ผ๋ถ€ ๋ฐฑ์—”๋“œ ์„œ๋น„์Šค

๐ŸŽฎ ๊ฒŒ์ž„/๋ฏธ๋””์–ด
โ€ข Puppet Labs - ์ธํ”„๋ผ ์ž๋™ํ™”
โ€ข Netflix - ์ผ๋ถ€ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ

๐Ÿ”ฌ ๋ฐ์ดํ„ฐ/AI
โ€ข Cognitect (Datomic ๊ฐœ๋ฐœ์‚ฌ) - Rich Hickey์˜ ํšŒ์‚ฌ
โ€ข ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ๊ณผํ•™ ํ”„๋กœ์ ํŠธ๋“ค

ํŠนํžˆ Nubank์˜ ์‚ฌ๋ก€๊ฐ€ ์œ ๋ช…ํ•œ๋ฐ, ์ˆ˜๋ฐฑ๋งŒ ๋ช…์˜ ์‚ฌ์šฉ์ž๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธˆ์œต ์‹œ์Šคํ…œ์„
Clojure๋กœ ๊ตฌ์ถ•ํ•ด์„œ ์•ˆ์ •์„ฑ๊ณผ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ ๋ชจ๋‘ ๋†’์˜€๋‹ค๊ณ  ํ•ด. ๋ ˆ์ „๋“œ ใ„นใ…‡


๐ŸŽฏ ๋งˆ๋ฌด๋ฆฌ - Clojure๋ฅผ ๋ฐฐ์›Œ์•ผ ํ•˜๋Š” ์ด์œ 

์†”์งํžˆ ๋งํ• ๊ฒŒ. Clojure๊ฐ€ ๋‹น์žฅ ์ทจ์—…์— ๊ฐ€์žฅ ์œ ๋ฆฌํ•œ ์–ธ์–ด๋Š” ์•„๋‹ˆ์•ผ.
Python, JavaScript, Java์— ๋น„ํ•˜๋ฉด ์ฑ„์šฉ ๊ณต๊ณ  ์ˆ˜๊ฐ€ ์ ์€ ๊ฑด ์‚ฌ์‹ค์ด์•ผ.

๊ทผ๋ฐ Clojure๋ฅผ ๋ฐฐ์šฐ๋ฉด ์–ป๋Š” ๊ฒŒ ์žˆ์–ด:

1 ํ•จ์ˆ˜ํ˜• ์‚ฌ๊ณ ๋ฐฉ์‹ - ์ด๊ฒŒ ๋‹ค๋ฅธ ์–ธ์–ด์—๋„ ์ ์šฉ๋จ. JavaScript์—์„œ ํ•จ์ˆ˜ํ˜• ํŒจํ„ด ์“ธ ๋•Œ, Python์—์„œ map/filter ์“ธ ๋•Œ ํ›จ์”ฌ ์ž˜ ์ดํ•ดํ•˜๊ฒŒ ๋จ

2 ๋™์‹œ์„ฑ์— ๋Œ€ํ•œ ๊นŠ์€ ์ดํ•ด - ๋ฉ€ํ‹ฐ์ฝ”์–ด ์‹œ๋Œ€์— ํ•„์ˆ˜์ ์ธ ์ง€์‹

3 ๋ถˆ๋ณ€์„ฑ์˜ ๊ฐ€์น˜ - React์˜ ์ƒํƒœ ๊ด€๋ฆฌ, Redux ํŒจํ„ด ๋“ฑ์ด ๋‹ค Clojure์˜ ์˜ํ–ฅ์„ ๋ฐ›์Œ

4 REPL ์ฃผ๋„ ๊ฐœ๋ฐœ - ํƒ์ƒ‰์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋Šฅ๋ ฅ ํ–ฅ์ƒ

5 ์–ธ์–ด ์„ค๊ณ„์— ๋Œ€ํ•œ ํ†ต์ฐฐ - ์™œ ์–ธ์–ด๊ฐ€ ์ด๋ ‡๊ฒŒ ์„ค๊ณ„๋˜์—ˆ๋Š”์ง€ ์ดํ•ดํ•˜๊ฒŒ ๋จ

์žฌ๋Šฅ๋„ท์—์„œ๋„ Clojure๋‚˜ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ด€๋ จ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ๋ฐฐ์šธ ์ˆ˜ ์žˆ๋Š” ๊ธฐํšŒ๊ฐ€ ์žˆ์œผ๋‹ˆ, ๊ด€์‹ฌ ์žˆ์œผ๋ฉด ์ฐพ์•„๋ด! ๐ŸŽฏ

Clojure๋Š” "์ƒ์‚ฐ์„ฑ์„ ๋†’์ด๋Š” ์–ธ์–ด"๋ผ๊ธฐ๋ณด๋‹ค "๋” ๋‚˜์€ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๋˜๊ฒŒ ํ•ด์ฃผ๋Š” ์–ธ์–ด"์•ผ.
ํ•œ๋ฒˆ ์ œ๋Œ€๋กœ ๋ฐฐ์›Œ๋‘๋ฉด ํ‰์ƒ ์จ๋จน์„ ์ˆ˜ ์žˆ๋Š” ์‚ฌ๊ณ ๋ฐฉ์‹์„ ์–ป๊ฒŒ ๋จ ใ…‹ใ…‹

๐Ÿš€ ์ง€๊ธˆ ๋‹น์žฅ ์‹œ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ•: 1. brew install clojure (macOS) ๋˜๋Š” ๊ณต์‹ ์‚ฌ์ดํŠธ์—์„œ ์„ค์น˜
2. clj ๋ช…๋ น์–ด๋กœ REPL ์‹œ์ž‘
3. (println "Hello, Clojure!") ์ž…๋ ฅ
4. ์ด ๊ธ€์—์„œ ๋ฐฐ์šด ์˜ˆ์ œ๋“ค ์ง์ ‘ ์‹คํ–‰ํ•ด๋ณด๊ธฐ
5. "Clojure for the Brave and True" ๋ฌด๋ฃŒ ์˜จ๋ผ์ธ ์ฑ… ์ฝ๊ธฐ

์‹œ์ž‘์ด ๋ฐ˜์ด์•ผ! ์ง€๊ธˆ ๋ฐ”๋กœ ํ•ด๋ด ๐Ÿ˜„
ํ•ต์‹ฌ ์ •๋ฆฌ โœจ ๐Ÿง  LISP ์ฒ ํ•™ ๊ณ„์Šน ํ˜ธ๋ชจ์ด์ฝ”๋‹‰์„ฑ, ๋งคํฌ๋กœ, S-ํ‘œํ˜„์‹ ๊ธฐ๋ฐ˜ ๋ฌธ๋ฒ• ๐Ÿ”’ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๊ตฌ์กฐ์  ๊ณต์œ , HAMT, ์˜์†์  ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ โšก JVM ์œ„์˜ ์‹ค์šฉ์„ฑ Java ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜ธํ™˜, ClojureScript ์ง€์› ๐Ÿ”„ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ณ ์ฐจ ํ•จ์ˆ˜, ํด๋กœ์ €, ๋ถ€๋ถ„ ์ ์šฉ, ํ•จ์ˆ˜ ํ•ฉ์„ฑ ๐Ÿงฉ ๋™์‹œ์„ฑ ๋ชจ๋ธ Atom, Ref, Agent, Var, STM, core.async ๐ŸŒŠ ์‹œํ€€์Šค ์ถ”์ƒํ™” ์ง€์—ฐ ์‹œํ€€์Šค, ํŠธ๋žœ์Šค๋“€์„œ, ํ†ต์ผ๋œ ์ปฌ๋ ‰์…˜ ์ธํ„ฐํŽ˜์ด์Šค Clojure = ๋ณต์žก์„ฑ์„ ์ค„์ด๋Š” ์–ธ์–ด ๐ŸŽฏ ๋ถˆ๋ณ€์„ฑ + ํ•จ์ˆ˜ํ˜• + LISP ์ฒ ํ•™ = ๋” ๋‚˜์€ ์†Œํ”„ํŠธ์›จ์–ด Rich Hickey, 2007 ~
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.