Ver3.0 ๐ Clojure์ ํธ๋์ญ์ ๋ฉ๋ชจ๋ฆฌ: ๋์์ฑ ์ ์ด์ ์๋ก์ด ํจ๋ฌ๋ค์

๐ Clojure์ ํธ๋์ญ์ ๋ฉ๋ชจ๋ฆฌ: ๋์์ฑ ์ ์ด์ ์๋ก์ด ํจ๋ฌ๋ค์
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ด ์ ์ฌํ๋ ๋์์ฑ์ ๋ง๋ฒ โจ
๐ฏ ์ ๋์์ฑ ์ ์ด๊ฐ ์ด๋ ๊ฒ ์ด๋ ค์ด ๊ฑธ๊น?
์ฌ๋ฌ๋ถ, ํ๋ก๊ทธ๋๋ฐ์ ํ๋ค ๋ณด๋ฉด ์ ๋ง ๊ณจ์น ์ํ ๋ฌธ์ ์ค ํ๋๊ฐ ๋ฐ๋ก ๋์์ฑ ์ ์ด์์. ๐ซ
์์ํด๋ณด์ธ์. ์ํ ๊ณ์ข์์ ๋์ ์ธ์ถํ๋ ํ๋ก๊ทธ๋จ์ ๋ง๋ค์๋๋ฐ, ๋ ๊ฐ์ ATM์์ ๋์์ ๊ฐ์ ๊ณ์ข์ ๋์ ๋นผ๋ด๋ ค๊ณ ํ๋ค๋ฉด? ์๋๋ฉด ์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ๋์์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ ค๊ณ ํ๋ค๋ฉด? ์ด๋ฐ ์ํฉ์์ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ์ ์งํ๋ ๊ฑด ์ ๋ง ๊น๋ค๋ก์ด ์ผ์ด์ฃ .
์ ํต์ ์ธ ๋ฐฉ์์์๋ ๋ฝ(Lock)์ด๋ ๋ฎคํ
์ค(Mutex)๋ฅผ ์ฌ์ฉํด์ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ด์. ํ์ง๋ง ์ด ๋ฐฉ๋ฒ๋ค์ ๋ฐ๋๋ฝ(Deadlock), ๋ ์ด์ค ์ปจ๋์
(Race Condition) ๊ฐ์ ์๋ก์ด ๋ฌธ์ ๋ค์ ๋ง๋ค์ด๋ด๊ณค ํ์ฃ . ๐
โข ์ฑ๋ฅ ์ ํ (๋ฝ ๋๊ธฐ ์๊ฐ)
โข ์ฝ๋ ๋ณต์ก๋ ์ฆ๊ฐ
โข ๋๋ฒ๊น ์ด ๋งค์ฐ ์ด๋ ค์
โข ํ์ฅ์ฑ ์ ํ
๊ทธ๋ฐ๋ฐ ์ฌ๊ธฐ, Clojure๋ผ๋ ์ธ์ด๊ฐ ์์ ํ ๋ค๋ฅธ ์ ๊ทผ ๋ฐฉ์์ ์ ์ํฉ๋๋ค. ๋ฐ๋ก Software Transactional Memory (STM)์ด๋ผ๋ ๊ฐ๋ ์ด์์! ๐
๐ Clojure์ STM: ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ๋์ญ์ ์ ๋ฉ๋ชจ๋ฆฌ๋ก!
Clojure์ STM์ ์ ๋ง ์ฒ์ฌ์ ์ธ ์์ด๋์ด์์ ์ถ๋ฐํ์ด์. ์ฌ๋ฌ๋ถ์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ ๋ ํธ๋์ญ์
(Transaction)์ด๋ผ๋ ๊ฐ๋
์ ์ฌ์ฉํ์์์? BEGIN, COMMIT, ROLLBACK ๊ฐ์ ๊ฒ๋ค ๋ง์ด์์. ๐พ
"์ด ๊ฐ๋
์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์๋ ์ ์ฉํ๋ฉด ์ด๋จ๊น?"
๋ฐ๋ก ์ด๊ฒ Clojure STM์ ํต์ฌ ์ฒ ํ์
๋๋ค! ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ๋์ญ์
์ฒ๋ผ, ๋ฉ๋ชจ๋ฆฌ ์์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ๋ ์์์ (Atomic)์ผ๋ก, ์ผ๊ด์ฑ(Consistent) ์๊ฒ, ๊ฒฉ๋ฆฌ(Isolated)๋์ด, ์ง์์ (Durable)์ผ๋ก ์ฒ๋ฆฌํ์๋ ๊ฑฐ์ฃ . ๋ค, ๋ง์์. ACID ์์ฑ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฉํ ๊ฑฐ์์! ๐ฏ
Clojure์์ STM์ผ๋ก ๊ด๋ฆฌ๋๋ ๋ณ๊ฒฝ ๊ฐ๋ฅํ ์ํ๋ฅผ ๋ด๋ ์ปจํ ์ด๋์์. ์ผ๋ฐ ๋ณ์์ ๋ฌ๋ฆฌ, Ref๋ ํธ๋์ญ์ ๋ด์์๋ง ๋ณ๊ฒฝํ ์ ์์ด์.
2. dosync (ํธ๋์ญ์ ๋ธ๋ก)
๋ชจ๋ Ref ๋ณ๊ฒฝ์ dosync ๋ธ๋ก ์์์ ์ผ์ด๋์ผ ํด์. ์ด ๋ธ๋ก์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ BEGIN...COMMIT๊ณผ ๊ฐ์ ์ญํ ์ ํ์ฃ .
3. ๋๊ด์ ๋์์ฑ ์ ์ด
๋ฝ์ ๊ฑธ์ง ์๊ณ ์ผ๋จ ์คํํ๊ณ , ์ถฉ๋์ด ๋ฐ์ํ๋ฉด ์๋์ผ๋ก ์ฌ์๋ํด์. ์ด๊ฒ STM์ ๋ง๋ฒ์ด์์! โจ
4. MVCC (Multi-Version Concurrency Control)
๊ฐ ํธ๋์ญ์ ์ ๋ฐ์ดํฐ์ ์ผ๊ด๋ ์ค๋ ์ท์ ๋ณด๊ฒ ๋๊ณ , ๋ณ๊ฒฝ์ฌํญ์ ์ปค๋ฐ ์์ ์ ์ ์ฉ๋ผ์.
์ด๋ฐ ๋ฐฉ์์ด ์ ํ๋ช
์ ์ด๋๊ณ ์? ์ ํต์ ์ธ ๋ฝ ๊ธฐ๋ฐ ๋ฐฉ์์์๋ ๊ฐ๋ฐ์๊ฐ ์ง์ ๋ฝ์ ๊ด๋ฆฌํด์ผ ํ์ด์. ์ด๋ค ์์๋ก ๋ฝ์ ๊ฑธ๊ณ , ์ธ์ ํด์ ํ ์ง, ๋ฐ๋๋ฝ์ ์ด๋ป๊ฒ ํผํ ์ง... ์ ๋ง ๋จธ๋ฆฌ ์ํ ์ผ์ด์์ฃ . ๐ต
ํ์ง๋ง STM์์๋? ๊ทธ๋ฅ dosync ๋ธ๋ก ์์ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๋! Clojure ๋ฐํ์์ด ์์์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ์ฒ๋ฆฌํด์ค์. ๋ง์น ์๋์ฐจ์ ์๋ ๋ณ์๊ธฐ์ฒ๋ผ, ๋ณต์กํ ๋ด๋ถ ๋์์ ์์คํ
์ด ์์์ ์ฒ๋ฆฌํ๊ณ ๊ฐ๋ฐ์๋ ๋น์ฆ๋์ค ๋ก์ง์๋ง ์ง์คํ ์ ์๊ฒ ๋๋ ๊ฑฐ์ฃ ! ๐
๐ป ์ค์ ์ฝ๋๋ก ์ดํดํ๋ Clojure STM
์ด๋ก ๋ง ๋ค์ผ๋ฉด ์ข ์ถ์์ ์ด์ฃ ? ์ค์ ์ฝ๋๋ฅผ ๋ณด๋ฉด์ ์ดํดํด๋ด์! ์ํ ๊ณ์ข ์ด์ฒด๋ผ๋ ํด๋์ํ ์์ ๋ก ์์ํด๋ณผ๊ฒ์. ๐ฆ
; ๋ ๊ฐ์ ์ํ ๊ณ์ข๋ฅผ Ref๋ก ์์ฑ
(def account-a (ref 1000)) ; A ๊ณ์ข: 1000์
(def account-b (ref 500)) ; B ๊ณ์ข: 500์
; ํ์ฌ ์์ก ํ์ธ (์ฝ๊ธฐ๋ ํธ๋์ญ์
์์ด๋ ๊ฐ๋ฅ)
@account-a ; => 1000
@account-b ; => 500
; ๋จ์ ์
๊ธ (ํธ๋์ญ์
ํ์)
(dosync
(alter account-a + 100)) ; A ๊ณ์ข์ 100์ ์
๊ธ
@account-a ; => 1100
์ฌ๊ธฐ์ @ ๊ธฐํธ๋ Ref์ ํ์ฌ ๊ฐ์ ์ฝ๋ ์ญํ ์ ํด์. alter ํจ์๋ Ref์ ๊ฐ์ ๋ณ๊ฒฝํ๋๋ฐ, ๋ฐ๋์ dosync ๋ธ๋ก ์์์๋ง ์ฌ์ฉํ ์ ์์ด์. ๐
; ๊ณ์ข ์ด์ฒด ํจ์
(defn transfer [from-account to-account amount]
(dosync
(alter from-account - amount) ; ์ถ๊ธ
(alter to-account + amount))) ; ์
๊ธ
; A ๊ณ์ข์์ B ๊ณ์ข๋ก 300์ ์ด์ฒด
(transfer account-a account-b 300)
@account-a ; => 800
@account-b ; => 800
์ด ์ฝ๋์ ์๋ฆ๋ค์์ ๋ณด์ธ์! ๐
๋ฝ์ ๊ฑธ๊ฑฐ๋ ํด์ ํ๋ ์ฝ๋๊ฐ ์ ํ ์์ด์. ๊ทธ๋ฅ ๋น์ฆ๋์ค ๋ก์ง๋ง ์์ฃ . ํ์ง๋ง ์ด ์ฝ๋๋ ์๋ฒฝํ๊ฒ ์ค๋ ๋ ์์ (Thread-safe)ํด์. ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ์ด ํจ์๋ฅผ ํธ์ถํด๋ ๋ฌธ์ ์์ด์!
์๋ํ๋ฉด dosync ๋ธ๋ก ์ ์ฒด๊ฐ ํ๋์ ์์์ ํธ๋์ญ์
์ผ๋ก ์คํ๋๊ธฐ ๋๋ฌธ์ด์์. ๋ง์ฝ ์ค๊ฐ์ ๋ค๋ฅธ ํธ๋์ญ์
์ด ๊ฐ์ ๊ณ์ข๋ฅผ ์์ ํ๋ค๋ฉด? Clojure๊ฐ ์๋์ผ๋ก ์ด ํธ๋์ญ์
์ ์ฌ์๋ํด์ค์. ๊ฐ๋ฐ์๋ ์ ๊ฒฝ ์ธ ํ์๊ฐ ์์ด์! ๐
; ์ฌ๋ฌ ๊ณ์ข ์์ฑ
(def accounts (vec (repeatedly 10 #(ref 1000))))
; ๋ฌด์์ ๊ณ์ข ๊ฐ ์ด์ฒด๋ฅผ ์ํํ๋ ํจ์
(defn random-transfer []
(let [from-idx (rand-int 10)
to-idx (rand-int 10)
amount (rand-int 100)]
(when (not= from-idx to-idx)
(transfer (accounts from-idx)
(accounts to-idx)
amount))))
; 100๊ฐ์ ์ค๋ ๋์์ ๋์์ 1000๋ฒ์ฉ ์ด์ฒด ์คํ
(defn stress-test []
(let [threads (repeatedly 100
#(Thread.
(fn []
(dotimes [_ 1000]
(random-transfer)))))]
(doseq [t threads] (.start t))
(doseq [t threads] (.join t))))
; ์ ์ฒด ์์ก ํ์ธ (ํญ์ 10000์ด์ด์ผ ํจ)
(defn total-balance []
(reduce + (map deref accounts)))
(stress-test)
(total-balance) ; => 10000 (ํญ์!)
์ด ์ฝ๋๋ ์ ๋ง ๊ฐ๋ ฅํ ํ
์คํธ์์! 100๊ฐ์ ์ค๋ ๋๊ฐ ๋์์ 10๋ง ๋ฒ์ ์ด์ฒด๋ฅผ ์ํํ๋๋ฐ, ์ ์ฒด ์์ก์ ํญ์ ์ ํํ๊ฒ 10000์์ ์ ์งํด์. ๐ช
์ ํต์ ์ธ ๋ฝ ๋ฐฉ์์ผ๋ก ์ด๊ฑธ ๊ตฌํํ๋ ค๋ฉด? ๋ฐ๋๋ฝ์ ํผํ๊ธฐ ์ํด ๊ณ์ข ๋ฒํธ ์์๋๋ก ๋ฝ์ ๊ฑธ์ด์ผ ํ๊ณ , ์์ธ ์ฒ๋ฆฌ๋ ์ ๊ฒฝ ์จ์ผ ํ๊ณ ... ์ฝ๋๊ฐ ๋ช ๋ฐฐ๋ ๋ณต์กํด์ง ๊ฑฐ์์. ํ์ง๋ง STM์์๋? ๊ทธ๋ฅ ์ด๋ ๊ฒ ๊ฐ๋จํด์! ๐
๐จ STM์ ๋ค์ํ ์ฐ์ฐ์๋ค
Clojure STM์ Ref๋ฅผ ์กฐ์ํ๊ธฐ ์ํ ์ฌ๋ฌ ๊ฐ์ง ํจ์๋ค์ ์ ๊ณตํด์. ๊ฐ๊ฐ์ ํน์ฑ์ ์ดํดํ๋ฉด ๋ ํจ์จ์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ด์! ๐ ๏ธ
(def counter (ref 0))
(dosync
(alter counter inc) ; counter๋ฅผ 1 ์ฆ๊ฐ
(alter counter + 10)) ; counter์ 10 ๋ํ๊ธฐ
@counter ; => 11
; alter๋ ํจ์๋ฅผ ๋ฐ์์ ํ์ฌ ๊ฐ์ ์ ์ฉ
(dosync
(alter counter (fn [current-value]
(* current-value 2))))
@counter ; => 22
alter๋ Ref์ ํ์ฌ ๊ฐ์ ์ฝ๊ณ , ํจ์๋ฅผ ์ ์ฉํ ๊ฒฐ๊ณผ๋ก ์
๋ฐ์ดํธํด์. ํธ๋์ญ์
์ด ์ฌ์๋๋ ๋๋ง๋ค ํจ์๊ฐ ๋ค์ ์คํ๋๊ธฐ ๋๋ฌธ์, ๋ถ์์ฉ์ด ์๋ ์์ ํจ์๋ฅผ ์ฌ์ฉํด์ผ ํด์! ๐ฏ
(def status (ref :idle))
(dosync
(ref-set status :running)) ; ์ง์ ๊ฐ์ ์ค์
@status ; => :running
; ์กฐ๊ฑด๋ถ ์ค์ ๋ ๊ฐ๋ฅ
(dosync
(when (= @status :running)
(ref-set status :completed)))
@status ; => :completed
ref-set์ ํ์ฌ ๊ฐ๊ณผ ์๊ด์์ด ์๋ก์ด ๊ฐ์ ์ง์ ์ค์ ํด์. ์ด์ ๊ฐ์ ์ฐธ์กฐํ ํ์๊ฐ ์์ ๋ ์ฌ์ฉํ๋ฉด ๋ผ์. ๐
(def total-requests (ref 0))
; ์ฌ๋ฌ ์ค๋ ๋์์ ๋์์ ์นด์ดํฐ ์ฆ๊ฐ
(defn handle-request []
(dosync
(commute total-requests inc)) ; alter ๋์ commute ์ฌ์ฉ
; ... ์์ฒญ ์ฒ๋ฆฌ ๋ก์ง ...
)
; commute๋ ๊ตํ๋ฒ์น์ด ์ฑ๋ฆฝํ๋ ์ฐ์ฐ์ ์ฌ์ฉ
; (+ 1 2 3) = (+ 3 2 1) ์ฒ๋ผ ์์๊ฐ ๋ฐ๋์ด๋ ๊ฒฐ๊ณผ๊ฐ ๊ฐ์ ๋
commute๋ STM์ ์จ๊ฒจ์ง ๋ณด์์ด์์! ๐
alter๋ ํธ๋์ญ์
์ปค๋ฐ ์์ ์ Ref ๊ฐ์ด ๋ณ๊ฒฝ๋์๋์ง ํ์ธํ๊ณ , ๋ณ๊ฒฝ๋์๋ค๋ฉด ์ฌ์๋ํด์. ํ์ง๋ง commute๋ ๋ ๊ด๋ํด์. ์ฐ์ฐ์ ์์๊ฐ ๊ฒฐ๊ณผ์ ์ํฅ์ ์ฃผ์ง ์๋๋ค๋ฉด (๊ตํ๋ฒ์น์ด ์ฑ๋ฆฝํ๋ค๋ฉด), ์ถฉ๋๋ก ๊ฐ์ฃผํ์ง ์๊ณ ๊ทธ๋ฅ ์งํํด์!
์๋ฅผ ๋ค์ด, ์นด์ดํฐ๋ฅผ ์ฆ๊ฐ์ํค๋ ๊ฒฝ์ฐ A ํธ๋์ญ์
์ด ๋จผ์ ์ฆ๊ฐ์ํค๋ B ํธ๋์ญ์
์ด ๋จผ์ ์ฆ๊ฐ์ํค๋ ์ต์ข
๊ฒฐ๊ณผ๋ ๊ฐ์์์? ์ด๋ฐ ๊ฒฝ์ฐ commute๋ฅผ ์ฌ์ฉํ๋ฉด ๋ถํ์ํ ์ฌ์๋๋ฅผ ์ค์ฌ์ ์ฑ๋ฅ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ด์! ๐
โข ํธ๋์ญ์ ์์ ์ Ref ๊ฐ ๊ธฐ๋ก
โข ์ปค๋ฐ ์ ๊ฐ์ด ๋ณ๊ฒฝ๋์๋์ง ํ์ธ
โข ๋ณ๊ฒฝ๋์๋ค๋ฉด ์ ์ฒด ํธ๋์ญ์ ์ฌ์๋
โข ์ ํํ์ง๋ง ์ถฉ๋์ด ๋ง์ผ๋ฉด ๋๋ ค์ง
commute ์ฌ์ฉ์:
โข ํธ๋์ญ์ ์์ ์ ๊ฐ ํ์ธ ์ ํจ
โข ์ปค๋ฐ ์ ๋ง์ง๋ง์ ์ฐ์ฐ ์ ์ฉ
โข ์์๋ง ๋ฐ๋ ๋ฟ ์ฌ์๋ ๋ถํ์
โข ๊ตํ๋ฒ์น์ด ์ฑ๋ฆฝํ๋ ์ฐ์ฐ์์ ํจ์ฌ ๋น ๋ฆ
(def inventory (ref 100))
(def reserved (ref 0))
(defn reserve-items [amount]
(dosync
(ensure inventory) ; inventory ๊ฐ์ด ๋ณ๊ฒฝ๋์ง ์์์ ๋ณด์ฅ
(when (>= @inventory amount)
(alter reserved + amount)
true)))
; ensure๋ ์ฝ๊ธฐ๋ง ํ๋ Ref๋ ํธ๋์ญ์
๊ฒ์ฆ์ ํฌํจ์ํด
ensure๋ ํน๋ณํ ํจ์์์. Ref๋ฅผ ์ฝ๊ธฐ๋ง ํ๊ณ ๋ณ๊ฒฝํ์ง ์์ง๋ง, ํธ๋์ญ์
๋์ ๊ทธ ๊ฐ์ด ๋ณ๊ฒฝ๋์ง ์์์์ ๋ณด์ฅํ๊ณ ์ถ์ ๋ ์ฌ์ฉํด์. ๐
์ ์์ ์์ inventory๋ฅผ ์ฝ์ด์ ์ถฉ๋ถํ์ง ํ์ธํ์ง๋ง ๋ณ๊ฒฝํ์ง๋ ์์์. ํ์ง๋ง ensure๋ฅผ ์ฌ์ฉํจ์ผ๋ก์จ, ํธ๋์ญ์
์ด ์ปค๋ฐ๋ ๋ inventory ๊ฐ์ด ์ค๊ฐ์ ๋ณ๊ฒฝ๋์ง ์์์์ ๋ณด์ฅ๋ฐ์ ์ ์์ด์!
๐๏ธ ์ค์ ์์ : ์จ๋ผ์ธ ์ผํ๋ชฐ ์ฌ๊ณ ๊ด๋ฆฌ ์์คํ
์ด๋ก ๊ณผ ๊ฐ๋จํ ์์ ๋ ์ถฉ๋ถํ ๋ดค์ผ๋, ์ด์ ์ค์ ๊ฐ์ ์์ ๋ฅผ ๋ง๋ค์ด๋ณผ๊น์? ์จ๋ผ์ธ ์ผํ๋ชฐ์ ์ฌ๊ณ ๊ด๋ฆฌ ์์คํ ์ STM์ผ๋ก ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค! ๐
; ์ํ ์ ๋ณด ๊ตฌ์กฐ
(defrecord Product [id name price stock])
; ์ฃผ๋ฌธ ์ ๋ณด ๊ตฌ์กฐ
(defrecord Order [id customer-id items total status])
; ์ํ ์ฌ๊ณ ๋ฅผ Ref๋ก ๊ด๋ฆฌ
(def products
(ref {1 (->Product 1 "๋
ธํธ๋ถ" 1500000 10)
2 (->Product 2 "๋ง์ฐ์ค" 30000 50)
3 (->Product 3 "ํค๋ณด๋" 80000 30)}))
; ์ฃผ๋ฌธ ๋ชฉ๋ก
(def orders (ref []))
; ์ฃผ๋ฌธ ID ์์ฑ๊ธฐ
(def order-id-counter (ref 0))
; ์ํ ์ฃผ๋ฌธ ํจ์
(defn place-order [customer-id items-to-order]
(dosync
(let [order-id (alter order-id-counter inc)
current-products @products
; ์ฌ๊ณ ํ์ธ
stock-check (every?
(fn [[product-id quantity]]
(>= (get-in current-products
[product-id :stock])
quantity))
items-to-order)]
(if stock-check
; ์ฌ๊ณ ๊ฐ ์ถฉ๋ถํ๋ฉด ์ฃผ๋ฌธ ์ฒ๋ฆฌ
(do
; ์ฌ๊ณ ๊ฐ์
(doseq [[product-id quantity] items-to-order]
(alter products
update-in [product-id :stock]
- quantity))
; ์ด์ก ๊ณ์ฐ
(let [total (reduce
(fn [sum [product-id quantity]]
(+ sum
(* quantity
(get-in current-products
[product-id :price]))))
0
items-to-order)
new-order (->Order order-id
customer-id
items-to-order
total
:confirmed)]
; ์ฃผ๋ฌธ ๋ชฉ๋ก์ ์ถ๊ฐ
(alter orders conj new-order)
{:success true
:order new-order
:message "์ฃผ๋ฌธ์ด ์๋ฃ๋์์ต๋๋ค!"}))
; ์ฌ๊ณ ๋ถ์กฑ
{:success false
:message "์ฌ๊ณ ๊ฐ ๋ถ์กฑํฉ๋๋ค."}))))
; ์ฃผ๋ฌธ ์ทจ์ ํจ์
(defn cancel-order [order-id]
(dosync
(let [order (first (filter #(= (:id %) order-id) @orders))]
(if (and order (= (:status order) :confirmed))
(do
; ์ฌ๊ณ ๋ณต๊ตฌ
(doseq [[product-id quantity] (:items order)]
(alter products
update-in [product-id :stock]
+ quantity))
; ์ฃผ๋ฌธ ์ํ ๋ณ๊ฒฝ
(alter orders
(fn [order-list]
(mapv #(if (= (:id %) order-id)
(assoc % :status :cancelled)
%)
order-list)))
{:success true
:message "์ฃผ๋ฌธ์ด ์ทจ์๋์์ต๋๋ค."})
{:success false
:message "์ทจ์ํ ์ ์๋ ์ฃผ๋ฌธ์
๋๋ค."}))))
; ์ฌ๊ณ ์กฐํ
(defn get-stock [product-id]
(get-in @products [product-id :stock]))
; ์ฌ์ฉ ์์
(place-order 1001 {1 2, 2 3})
; => {:success true, :order {...}, :message "์ฃผ๋ฌธ์ด ์๋ฃ๋์์ต๋๋ค!"}
(get-stock 1) ; => 8 (10์์ 2๊ฐ ๊ฐ์)
(get-stock 2) ; => 47 (50์์ 3๊ฐ ๊ฐ์)
; ๋์ ์ฃผ๋ฌธ ์๋ฎฌ๋ ์ด์
(defn stress-test-orders []
(let [threads (repeatedly 50
#(Thread.
(fn []
(dotimes [_ 10]
(place-order
(rand-int 1000)
{(inc (rand-int 3)) 1})))))]
(doseq [t threads] (.start t))
(doseq [t threads] (.join t))))
(stress-test-orders)
; 500๊ฐ์ ๋์ ์ฃผ๋ฌธ์ด ์๋ฒฝํ๊ฒ ์ฒ๋ฆฌ๋จ!
์ด ์ฝ๋์ ๋ฉ์ง ์ ์ ๋ณผ๊น์? ๐
1. ์์์ฑ ๋ณด์ฅ
์ฃผ๋ฌธ ์ฒ๋ฆฌ ๊ณผ์ ์์ ์ฌ๊ณ ํ์ธ, ์ฌ๊ณ ๊ฐ์, ์ฃผ๋ฌธ ์์ฑ์ด ๋ชจ๋ ํ๋์ ํธ๋์ญ์
์ผ๋ก ์ฒ๋ฆฌ๋ผ์. ์ค๊ฐ์ ์คํจํ๋ฉด ๋ชจ๋ ๋ณ๊ฒฝ์ด ๋กค๋ฐฑ๋์ฃ !
2. ๋์์ฑ ์์
50๊ฐ์ ์ค๋ ๋๊ฐ ๋์์ ์ฃผ๋ฌธ์ ๋ฃ์ด๋ ์ฌ๊ณ ๊ฐ ๋ง์ด๋์ค๊ฐ ๋๊ฑฐ๋ ์ค๋ณต ์ฃผ๋ฌธ์ด ๋ฐ์ํ์ง ์์์. STM์ด ์์์ ์ฒ๋ฆฌํด์ค์!
3. ์ฝ๋ ๊ฐ๊ฒฐ์ฑ
๋ฝ ๊ด๋ฆฌ ์ฝ๋๊ฐ ์ ํ ์์ด์. ๋น์ฆ๋์ค ๋ก์ง๋ง ๊น๋ํ๊ฒ ํํ๋์ด ์์ฃ . ์ด๊ฒ ๋ฐ๋ก STM์ ํ์ด์์! ๐ช
4. ๋ฐ๋๋ฝ ๋ถ๊ฐ๋ฅ
์ฌ๋ฌ ์ํ์ ๋์์ ์ฃผ๋ฌธํด๋ ๋ฐ๋๋ฝ์ด ๋ฐ์ํ ์ ์์ด์. STM์ ๋ฝ์ ์ฌ์ฉํ์ง ์์ผ๋๊น์!
ํธ๋์ญ์ ์ ๊ฐ๋ฅํ ํ ์๊ณ ๋น ๋ฅด๊ฒ ์ ์งํ๋ ๊ฒ ์ข์์. ํธ๋์ญ์ ์ด ๊ธธ์ด์ง์๋ก ์ถฉ๋ ๊ฐ๋ฅ์ฑ์ด ๋์์ง๊ณ , ์ฌ์๋ ๋น์ฉ๋ ์ปค์ง๊ฑฐ๋ ์. ๋ณต์กํ ๊ณ์ฐ์ด๋ I/O ์์ ์ ํธ๋์ญ์ ๋ฐ์์ ์ํํ๊ณ , ๊ผญ ํ์ํ ์ํ ๋ณ๊ฒฝ๋ง dosync ์์ ๋ฃ์ผ์ธ์!
โก STM์ ์ฑ๋ฅ ํน์ฑ๊ณผ ์ต์ ํ
STM์ด ํธ๋ฆฌํ๊ธด ํ์ง๋ง, ์ฑ๋ฅ์ ์ด๋จ๊น์? ๋ชจ๋ ๊ธฐ์ ์๋ ํธ๋ ์ด๋์คํ๊ฐ ์์ผ๋๊น์. STM์ ์ฑ๋ฅ ํน์ฑ์ ์ดํดํ๊ณ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ์์๋ด์! ๐
์ค๋ ๋๋ค์ด ๋ฝ์ ๊ธฐ๋ค๋ฆฌ๋ฉฐ ๋ธ๋ก๋์ง ์์์. ๋ชจ๋๊ฐ ๋์์ ์์ ์ ์งํํ ์ ์์ฃ .
โข ๋ฐ๋๋ฝ ๋ถ๊ฐ๋ฅ
๋ฝ์ ์ฌ์ฉํ์ง ์์ผ๋ ๋ฐ๋๋ฝ์ด ์์ฒ์ ์ผ๋ก ๋ถ๊ฐ๋ฅํด์.
โข ์ปดํฌ์ ๋น๋ฆฌํฐ(Composability)
์ฌ๋ฌ ํธ๋์ญ์ ์ ์กฐํฉํด์ ๋ ํฐ ํธ๋์ญ์ ์ ๋ง๋ค๊ธฐ ์ฌ์์. ๋ฝ ๊ธฐ๋ฐ์์๋ ์ด๊ฒ ๋งค์ฐ ์ด๋ ค์์.
โข ์ฝ๊ธฐ ์ฑ๋ฅ ์ฐ์
์ฝ๊ธฐ ์์ ์ ๋ฝ ์์ด ๋น ๋ฅด๊ฒ ์ํ๋ผ์.
์ถฉ๋์ด ๋ง์ผ๋ฉด ํธ๋์ญ์ ์ด ์ฌ๋ฌ ๋ฒ ์ฌ์๋๋์ด ์ค๋ฒํค๋๊ฐ ๋ฐ์ํด์.
โข ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋
MVCC๋ฅผ ์ํด ์ฌ๋ฌ ๋ฒ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ ์งํด์ผ ํด์.
โข ๊ธด ํธ๋์ญ์ ์ ๋ถ๋ฆฌ
ํธ๋์ญ์ ์ด ๊ธธ์๋ก ์ถฉ๋ ํ๋ฅ ์ด ๋์์ ธ์.
โข ๋ถ์์ฉ ์ ํ
ํธ๋์ญ์ ๋ด์์ I/O ๊ฐ์ ๋ถ์์ฉ์ ์ํํ๋ฉด ์ ๋ผ์.
; โ ๋์ ์: ํธ๋์ญ์
์ด ๋๋ฌด ๊น
(defn bad-update [data-ref]
(dosync
(Thread/sleep 1000) ; ๊ธด ์์
(alter data-ref inc)
(println "Updated!") ; ๋ถ์์ฉ
(Thread/sleep 1000))) ; ๋ ๊ธด ์์
; โ
์ข์ ์: ํธ๋์ญ์
์ ์งง๊ฒ, ๋ถ์์ฉ์ ๋ฐ์์
(defn good-update [data-ref]
(let [result (dosync
(alter data-ref inc)
@data-ref)]
(println "Updated to:" result) ; ๋ถ์์ฉ์ ๋ฐ์์
result))
; โ ๋์ ์: ๋ถํ์ํ๊ฒ alter ์ฌ์ฉ
(def counter (ref 0))
(defn bad-increment []
(dosync
(dotimes [_ 1000]
(alter counter inc)))) ; 1000๋ฒ์ alter
; โ
์ข์ ์: ํ ๋ฒ์ ์ฒ๋ฆฌ
(defn good-increment []
(dosync
(alter counter + 1000))) ; ํ ๋ฒ์ alter
; โ
๋ ์ข์ ์: commute ์ฌ์ฉ (์์ ๋ฌด๊ดํ ๊ฒฝ์ฐ)
(defn best-increment []
(dosync
(commute counter + 1000))) ; ์ถฉ๋ ๊ฐ๋ฅ์ฑ ๊ฐ์
2. commute ํ์ฉ: ๊ตํ๋ฒ์น์ด ์ฑ๋ฆฝํ๋ ์ฐ์ฐ์ commute ์ฌ์ฉ
3. ๋ถ์์ฉ ์ ๊ฑฐ: I/O, ์ถ๋ ฅ ๋ฑ์ ํธ๋์ญ์ ๋ฐ์์
4. ์ฝ๊ธฐ ์ ์ฉ์ ํธ๋์ญ์ ๋ฐ์์: ๋จ์ ์กฐํ๋ @ ์ฐ์ฐ์๋ง ์ฌ์ฉ
5. ๋ฐฐ์น ์ฒ๋ฆฌ: ์ฌ๋ฌ ๊ฐ์ ์์ ๋ณ๊ฒฝ์ ํ๋๋ก ๋ฌถ๊ธฐ
6. ํซ์คํ ํํผ: ๋ชจ๋ ํธ๋์ญ์ ์ด ๊ฐ์ Ref๋ฅผ ์์ ํ์ง ์๋๋ก
7. ์ ์ ํ ๋ฐ์ดํฐ ๊ตฌ์กฐ: Ref์ ๊ฐ์์ ํฌ๊ธฐ ์ต์ ํ
; STM ๋ฒ์
(def stm-counter (ref 0))
(defn stm-benchmark []
(time
(let [threads (repeatedly 10
#(Thread.
(fn []
(dotimes [_ 10000]
(dosync
(commute stm-counter inc))))))]
(doseq [t threads] (.start t))
(doseq [t threads] (.join t)))))
; ์ ํต์ ๋ฝ ๋ฒ์
(def lock-counter (atom 0))
(def lock (Object.))
(defn lock-benchmark []
(time
(let [threads (repeatedly 10
#(Thread.
(fn []
(dotimes [_ 10000]
(locking lock
(swap! lock-counter inc))))))]
(doseq [t threads] (.start t))
(doseq [t threads] (.join t)))))
; ์คํ ๊ฒฐ๊ณผ (์์)
; STM: "Elapsed time: 245.3 msecs"
; Lock: "Elapsed time: 312.7 msecs"
; ์ถฉ๋์ด ์ ์ ๊ฒฝ์ฐ STM์ด ๋ ๋น ๋ฆ!
๋ฒค์น๋งํฌ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ํฅ๋ฏธ๋ก์์! ๐ค
์ถฉ๋์ด ์ ์ ์ํฉ์์๋ STM์ด ์ ํต์ ์ธ ๋ฝ๋ณด๋ค ๋น ๋ฅด๊ฑฐ๋ ๋น์ทํด์. ์๋ํ๋ฉด ๋ฝ ๋๊ธฐ ์๊ฐ์ด ์๊ณ , commute๋ฅผ ์ฌ์ฉํ๋ฉด ๋ถํ์ํ ์ถฉ๋์ ํผํ ์ ์๊ฑฐ๋ ์.
ํ์ง๋ง ๋ชจ๋ ํธ๋์ญ์
์ด ๊ฐ์ Ref๋ฅผ ์์ ํ๋ ๋ฑ ์ถฉ๋์ด ๋ง์ ์ํฉ์์๋ ์ฌ์๋ ๋น์ฉ ๋๋ฌธ์ ์ฑ๋ฅ์ด ์ ํ๋ ์ ์์ด์. ์ด๋ด ๋๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ฌ์ค๊ณํ๊ฑฐ๋, ๊ฒฝ์ฐ์ ๋ฐ๋ผ atom์ด๋ agent ๊ฐ์ ๋ค๋ฅธ ๋์์ฑ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ๊ณ ๋ คํด๋ณผ ์ ์์ด์.
๐ Clojure์ ๋ค๋ฅธ ๋์์ฑ ํ๋ฆฌ๋ฏธํฐ๋ธ์ ๋น๊ต
Clojure๋ STM ์ธ์๋ ๋ค์ํ ๋์์ฑ ๋๊ตฌ๋ฅผ ์ ๊ณตํด์. ๊ฐ๊ฐ์ ํน์ฑ์ ์ดํดํ๊ณ ์ ์ฌ์ ์์ ์ฌ์ฉํ๋ ๊ฒ ์ค์ํด์! ๐จ
; Atom์ ๋จ์ผ ๊ฐ์ ๋๊ธฐ์ ๋ณ๊ฒฝ์ ์ต์ ํ
(def counter (atom 0))
; swap!์ผ๋ก ๊ฐ ๋ณ๊ฒฝ (CAS ๊ธฐ๋ฐ)
(swap! counter inc) ; => 1
; reset!์ผ๋ก ์ง์ ์ค์
(reset! counter 100) ; => 100
; Atom์ ํธ๋์ญ์
์์ด ์ฌ์ฉ
; ์ฌ๋ฌ Atom์ ์กฐ์จํ ์ ์์
(def account-a (atom 1000))
(def account-b (atom 500))
; โ ์ด๋ ๊ฒ ํ๋ฉด ์์์ฑ ๋ณด์ฅ ์ ๋จ
(defn bad-transfer [amount]
(swap! account-a - amount)
(swap! account-b + amount))
; ์ค๊ฐ์ ์คํจํ๋ฉด ์ผ๊ด์ฑ ๊นจ์ง!
; โ
์ฌ๋ฌ ์ํ๋ฅผ ์กฐ์จํ๋ ค๋ฉด Ref ์ฌ์ฉ
Atom์ ์ฌ์ฉํด์ผ ํ ๋:
โข ๋จ์ผ ๊ฐ๋ง ๋ณ๊ฒฝํ ๋
โข ํธ๋์ญ์
์ด ํ์ ์์ ๋
โข ์ต๋ํ ๋น ๋ฅธ ์ฑ๋ฅ์ด ํ์ํ ๋
โข ๋
๋ฆฝ์ ์ธ ์ํ ๊ด๋ฆฌ
Atom์ Compare-And-Swap(CAS) ์ฐ์ฐ์ ์ฌ์ฉํด์ STM๋ณด๋ค ์ค๋ฒํค๋๊ฐ ์ ์ด์. ํ์ง๋ง ์ฌ๋ฌ Atom์ ์กฐ์จํ ์ ์๋ค๋ ์ ์ฝ์ด ์์ฃ . ๐
; Agent๋ ๋น๋๊ธฐ์ ์ผ๋ก ์ํ ๋ณ๊ฒฝ
(def logger (agent []))
; send๋ก ๋น๋๊ธฐ ์์
์ ์ก
(send logger conj "์ฒซ ๋ฒ์งธ ๋ก๊ทธ")
(send logger conj "๋ ๋ฒ์งธ ๋ก๊ทธ")
; ์ฆ์ ๋ฐํ๋๊ณ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํ
@logger ; => [] (์์ง ์คํ ์ ๋จ)
; ์ ์ ํ...
(Thread/sleep 100)
@logger ; => ["์ฒซ ๋ฒ์งธ ๋ก๊ทธ" "๋ ๋ฒ์งธ ๋ก๊ทธ"]
; send-off๋ ๋ธ๋กํน ์์
์ฉ (๋ณ๋ ์ค๋ ๋ ํ)
(def file-writer (agent nil))
(send-off file-writer
(fn [_]
(spit "log.txt" "๋ก๊ทธ ๋ด์ฉ")
nil))
; Agent๋ ์๋ฌ ์ฒ๋ฆฌ๋ ์ง์
(set-error-handler! logger
(fn [agent exception]
(println "์๋ฌ ๋ฐ์:" exception)))
Agent๋ฅผ ์ฌ์ฉํด์ผ ํ ๋:
โข ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๋
โข I/O ์์
์ ์ํํ ๋
โข ์์๊ฐ ๋ณด์ฅ๋์ด์ผ ํ ๋
โข ๋ฐฑ๊ทธ๋ผ์ด๋ ์์
Agent๋ ์กํฐ ๋ชจ๋ธ๊ณผ ๋น์ทํ ๊ฐ๋
์ด์์. ๊ฐ Agent๋ ์์ ๋ง์ ์์
ํ๋ฅผ ๊ฐ์ง๊ณ ์๊ณ , ์์ฐจ์ ์ผ๋ก ์์
์ ์ฒ๋ฆฌํด์. STM๊ณผ ๋ฌ๋ฆฌ ๋ถ์์ฉ์ด ์๋ ์์
๋ ์์ ํ๊ฒ ์ํํ ์ ์์ด์! ๐ฌ
; Var๋ ๋์ ๋ฐ์ธ๋ฉ ์ง์
(def ^:dynamic *config* {:debug false})
; binding์ผ๋ก ์ค๋ ๋ ๋ก์ปฌ ๊ฐ ์ค์
(binding [*config* {:debug true}]
(println *config*)) ; => {:debug true}
(println *config*) ; => {:debug false} (์๋ ๊ฐ)
; ์ค๋ ๋๋ง๋ค ๋ค๋ฅธ ๊ฐ ๊ฐ์ง ์ ์์
(defn worker [id]
(binding [*config* {:worker-id id}]
(println "Worker" id "config:" *config*)))
(.start (Thread. #(worker 1)))
(.start (Thread. #(worker 2)))
; ๊ฐ ์ค๋ ๋๊ฐ ๋
๋ฆฝ์ ์ธ *config* ๊ฐ ๊ฐ์ง
Var๋ฅผ ์ฌ์ฉํด์ผ ํ ๋:
โข ์ค๋ ๋๋ณ๋ก ๋ค๋ฅธ ์ค์ ์ด ํ์ํ ๋
โข ์ปจํ
์คํธ ์ ๋ณด ์ ๋ฌ
โข ํ
์คํธ์์ ๋ชจํน
โข ์ ์ญ ์ค์ ์ ์์ ๋ณ๊ฒฝ
| ์ํฉ | ์ถ์ฒ ๋๊ตฌ | ์ด์ |
|---|---|---|
| ์ฌ๋ฌ ์ํ๋ฅผ ์กฐ์จ | Ref (STM) | ํธ๋์ญ์ ์ผ๋ก ์์์ฑ ๋ณด์ฅ |
| ๋จ์ผ ๊ฐ ๋น ๋ฅธ ๋ณ๊ฒฝ | Atom | CAS ๊ธฐ๋ฐ์ผ๋ก ๋น ๋ฆ |
| ๋น๋๊ธฐ ์์ | Agent | ๋ฐฑ๊ทธ๋ผ์ด๋ ์ฒ๋ฆฌ |
| I/O ์์ | Agent | ๋ถ์์ฉ ํ์ฉ |
| ์ค๋ ๋๋ณ ์ค์ | Var | ๋์ ๋ฐ์ธ๋ฉ |
| ๋ถ๋ณ ๋ฐ์ดํฐ ๊ณต์ | ์ผ๋ฐ ๊ฐ | ๋๊ธฐํ ๋ถํ์ |
๐ STM์ ์ค์ ์ฌ์ฉ ์ฌ๋ก์ ์ ์ฉ ๋ถ์ผ
์ด๋ก ๊ณผ ์์ ๋ ์ถฉ๋ถํ ๋ดค์ผ๋, ์ค์ ๋ก STM์ด ์ด๋์ ์ฌ์ฉ๋๋์ง ์์๋ณผ๊น์? ๐
STM์ ๊ฐ์ฅ ์์ฐ์ค๋ฌ์ด ์ ์ฉ ๋ถ์ผ๋ ๊ธ์ต ์์คํ
์ด์์. ๊ณ์ข ์ด์ฒด, ๊ฑฐ๋ ์ฒ๋ฆฌ, ์์ก ๊ด๋ฆฌ ๋ฑ ์ฌ๋ฌ ์ํ๋ฅผ ์์์ ์ผ๋ก ๋ณ๊ฒฝํด์ผ ํ๋ ์์
์ด ๋ง๊ฑฐ๋ ์. ๐ฐ
์ ํต์ ์ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ๋์ญ์
๊ณผ ์ ์ฌํ ๊ฐ๋
์ด๋ผ ๊ธ์ต ๊ฐ๋ฐ์๋ค์ด ์ดํดํ๊ธฐ ์ฝ๊ณ , ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ๋ณด์ฅํ๋ ๊ฒ ์ค์ํ ๋ถ์ผ๋ผ STM์ ์ฅ์ ์ด ๋น์ ๋ฐํด์!
; ์ค์๊ฐ ๊ฑฐ๋ ์์คํ
์์
(def trading-accounts (ref {}))
(def order-book (ref {:buy [] :sell []}))
(def trade-history (ref []))
(defn execute-trade [buyer-id seller-id stock amount price]
(dosync
; ๊ตฌ๋งค์ ์์ก ํ์ธ ๋ฐ ์ฐจ๊ฐ
(let [buyer-balance (get-in @trading-accounts
[buyer-id :balance])]
(when (>= buyer-balance (* amount price))
; ๊ฑฐ๋ ์คํ
(alter trading-accounts
update-in [buyer-id :balance]
- (* amount price))
(alter trading-accounts
update-in [buyer-id :stocks stock]
(fnil + 0) amount)
(alter trading-accounts
update-in [seller-id :balance]
+ (* amount price))
(alter trading-accounts
update-in [seller-id :stocks stock]
- amount)
; ๊ฑฐ๋ ๊ธฐ๋ก
(alter trade-history
conj {:buyer buyer-id
:seller seller-id
:stock stock
:amount amount
:price price
:timestamp (System/currentTimeMillis)})
{:success true :message "๊ฑฐ๋ ์๋ฃ"}))))
๋ฉํฐํ๋ ์ด์ด ๊ฒ์์์๋ STM์ด ์ ์ฉํด์! ์ฌ๋ฌ ํ๋ ์ด์ด๊ฐ ๋์์ ๊ฒ์ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ์ํฉ์์ ์ผ๊ด์ฑ์ ์ ์งํ๊ธฐ ์ด๋ ค์ด๋ฐ, STM์ ์ฌ์ฉํ๋ฉด ๊น๋ํ๊ฒ ํด๊ฒฐํ ์ ์์ด์. ๐ฎ
; ๊ฒ์ ์๋ ์ํ ๊ด๋ฆฌ
(def game-world
(ref {:players {}
:items {}
:monsters {}}))
(defn pickup-item [player-id item-id]
(dosync
(when (contains? (:items @game-world) item-id)
; ์์ดํ
์ ์๋์์ ์ ๊ฑฐํ๊ณ ํ๋ ์ด์ด์๊ฒ ์ถ๊ฐ
(alter game-world
update :items
dissoc item-id)
(alter game-world
update-in [:players player-id :inventory]
conj item-id)
{:success true})))
(defn player-attack [attacker-id target-id damage]
(dosync
(let [target-hp (get-in @game-world
[:players target-id :hp])]
(when (and target-hp (> target-hp 0))
(let [new-hp (max 0 (- target-hp damage))]
(alter game-world
assoc-in [:players target-id :hp]
new-hp)
; ์ฌ๋ง ์ฒ๋ฆฌ
(when (= new-hp 0)
(alter game-world
update-in [:players target-id]
assoc :status :dead))
{:success true
:new-hp new-hp})))))
์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ๋์์ ๊ฐ์ ๋ฌธ์๋ฅผ ํธ์งํ๋ ํ์ ๋๊ตฌ์์๋ STM์ด ๋น์ ๋ฐํด์. Google Docs ๊ฐ์ ์๋น์ค๋ฅผ ๋ง๋ ๋ค๊ณ ์๊ฐํด๋ณด์ธ์! ๐
; ํ์
๋ฌธ์ ํธ์ง ์์คํ
(def document (ref {:content ""
:version 0
:editors #{}}))
(defn edit-document [user-id edit-fn]
(dosync
; ์ฌ์ฉ์๋ฅผ ํธ์ง์ ๋ชฉ๋ก์ ์ถ๊ฐ
(alter document update :editors conj user-id)
; ๋ฌธ์ ๋ด์ฉ ๋ณ๊ฒฝ
(alter document update :content edit-fn)
; ๋ฒ์ ์ฆ๊ฐ
(alter document update :version inc)
{:version (:version @document)
:content (:content @document)}))
(defn insert-text [user-id position text]
(edit-document user-id
(fn [content]
(str (subs content 0 position)
text
(subs content position)))))
; ์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ๋์์ ํธ์งํด๋ ์์ !
์ฌ๋ฅ๋ท(https://www.jaenung.net) ๊ฐ์ ์ฌ๋ฅ ๊ฑฐ๋ ํ๋ซํผ์์๋ STM์ด ์ ์ฉํ๊ฒ ์ฐ์ผ ์ ์์ด์! ๐
์๋ฅผ ๋ค์ด, ์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ๋์์ ๊ฐ์ ์ฌ๋ฅ ์ํ์ ๊ตฌ๋งคํ๋ ค๊ณ ํ ๋, ์ฌ๊ณ ๊ด๋ฆฌ์ ๊ฒฐ์ ์ฒ๋ฆฌ๋ฅผ ์์์ ์ผ๋ก ์ํํด์ผ ํ์์์? ๋๋ ํฌ์ธํธ ์ ๋ฆฝ, ์ฟ ํฐ ์ฌ์ฉ, ๋ฑ๊ธ ์
๋ฐ์ดํธ ๊ฐ์ ์ฌ๋ฌ ์ํ ๋ณ๊ฒฝ์ ํ๋์ ํธ๋์ญ์
์ผ๋ก ๋ฌถ์ด์ผ ํ ๋๋ ์๊ณ ์.
; ์ฌ๋ฅ๋ท ์คํ์ผ์ ๊ฑฐ๋ ์์คํ
(def users (ref {}))
(def talents (ref {}))
(def transactions (ref []))
(defn purchase-talent [buyer-id talent-id]
(dosync
(let [buyer (get @users buyer-id)
talent (get @talents talent-id)
price (:price talent)]
(when (and buyer talent
(>= (:points buyer) price)
(> (:available talent) 0))
; ํฌ์ธํธ ์ฐจ๊ฐ
(alter users
update-in [buyer-id :points]
- price)
; ์ฌ๋ฅ ์ฌ๊ณ ๊ฐ์
(alter talents
update-in [talent-id :available]
dec)
; ํ๋งค์์๊ฒ ํฌ์ธํธ ์ง๊ธ
(alter users
update-in [(:seller-id talent) :points]
+ (* price 0.9)) ; 10% ์์๋ฃ
; ๊ฑฐ๋ ๊ธฐ๋ก
(alter transactions
conj {:buyer buyer-id
:talent talent-id
:price price
:timestamp (System/currentTimeMillis)})
; ๊ตฌ๋งค ํ์ ์ฆ๊ฐ๋ก ๋ฑ๊ธ ์
๋ฐ์ดํธ
(alter users
update-in [buyer-id :purchase-count]
inc)
{:success true
:message "์ฌ๋ฅ ๊ตฌ๋งค ์๋ฃ!"}))))
์ด๋ฐ ์์ผ๋ก ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง์ ๊ฐ๊ฒฐํ๊ณ ์์ ํ๊ฒ ๊ตฌํํ ์ ์์ด์. ๋ฝ์ ์ง์ ๊ด๋ฆฌํ์ง ์์๋ ๋๋ ์ฝ๋๊ฐ ํจ์ฌ ๊น๋ํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฝ์ฃ ! ๐
๐ STM ํ์ต ๋ก๋๋งต๊ณผ ์ค์ ํ
STM์ ์ ๋๋ก ํ์ฉํ๋ ค๋ฉด ์ด๋ป๊ฒ ๊ณต๋ถํด์ผ ํ ๊น์? ๋จ๊ณ๋ณ ํ์ต ๋ก๋๋งต์ ์ ์ํด๋๋ฆด๊ฒ์! ๐
โข Clojure ๊ธฐ๋ณธ ๋ฌธ๋ฒ ํ์ต
โข ๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ ์ดํด
โข ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ฐ๋
โข ๊ธฐ๋ณธ ๋์์ฑ ๋ฌธ์ ์ดํด
2๋จ๊ณ: STM ๊ธฐ๋ณธ (2-3์ฃผ)
โข Ref, dosync ์ฌ์ฉ๋ฒ
โข alter, ref-set ์ฐจ์ด์
โข ๊ฐ๋จํ ์์ ๊ตฌํ (์นด์ดํฐ, ๊ณ์ข ์ด์ฒด)
โข ํธ๋์ญ์ ๊ฐ๋ ์ดํด
3๋จ๊ณ: ๊ณ ๊ธ ๊ธฐ๋ฅ (3-4์ฃผ)
โข commute ํ์ฉ๋ฒ
โข ensure ์ฌ์ฉ ์๋๋ฆฌ์ค
โข ์ฑ๋ฅ ์ต์ ํ ๊ธฐ๋ฒ
โข ๋ค๋ฅธ ๋์์ฑ ํ๋ฆฌ๋ฏธํฐ๋ธ์ ๋น๊ต
4๋จ๊ณ: ์ค์ ํ๋ก์ ํธ (4์ฃผ ์ด์)
โข ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌํ
โข ์ฑ๋ฅ ์ธก์ ๋ฐ ํ๋
โข ์๋ฌ ์ฒ๋ฆฌ ๋ฐ ๋๋ฒ๊น
โข ํ๋ก๋์ ๋ฐฐํฌ ๊ฒฝํ
์ฒ์๋ถํฐ ๋ณต์กํ ์์คํ ์ STM์ ์ ์ฉํ์ง ๋ง์ธ์. ์์ ๋ชจ๋๋ถํฐ ์์ํด์ ๊ฒฝํ์ ์์ผ์ธ์.
2. ๋ก๊น ๊ณผ ๋ชจ๋ํฐ๋ง
ํธ๋์ญ์ ์ฌ์๋ ํ์๋ฅผ ๋ชจ๋ํฐ๋งํ์ธ์. ์ฌ์๋๊ฐ ๋ง๋ค๋ฉด ์ค๊ณ๋ฅผ ์ฌ๊ฒํ ํด์ผ ํด์.
3. ํ ์คํธ ์์ฑ
๋์์ฑ ๋ฒ๊ทธ๋ ์ฐพ๊ธฐ ์ด๋ ค์์. ์คํธ๋ ์ค ํ ์คํธ๋ฅผ ์์ฑํด์ ์ถฉ๋ถํ ๊ฒ์ฆํ์ธ์.
4. ๋ฌธ์ํ
์ด๋ค Ref๊ฐ ์ด๋ค ํธ๋์ญ์ ์์ ์ฌ์ฉ๋๋์ง ๋ช ํํ ๋ฌธ์ํํ์ธ์.
5. ์ปค๋ฎค๋ํฐ ํ์ฉ
Clojure ์ปค๋ฎค๋ํฐ๋ ๋งค์ฐ ํ๋ฐํด์. ๋งํ๋ ๋ถ๋ถ์ด ์์ผ๋ฉด ์ง๋ฌธํ์ธ์!
โ ๋ฌธ์ : ํ์ผ ์ฐ๊ธฐ, DB ์ฟผ๋ฆฌ ๋ฑ์ dosync ์์์ ์ํ
โ ํด๊ฒฐ: I/O๋ ํธ๋์ญ์ ๋ฐ์์ ์ํํ๊ฑฐ๋ Agent ์ฌ์ฉ
์ค์ 2: ๋๋ฌด ๊ธด ํธ๋์ญ์
โ ๋ฌธ์ : ๋ณต์กํ ๊ณ์ฐ์ ํธ๋์ญ์ ์์ ํฌํจ
โ ํด๊ฒฐ: ๊ณ์ฐ์ ๋ฐ์์ ํ๊ณ ๊ฒฐ๊ณผ๋ง ํธ๋์ญ์ ์์ ์ ์ฉ
์ค์ 3: ๋ชจ๋ ๊ณณ์ STM ์ฌ์ฉ
โ ๋ฌธ์ : ๋จ์ํ ๊ฒฝ์ฐ์๋ ๋ฌด์กฐ๊ฑด Ref ์ฌ์ฉ
โ ํด๊ฒฐ: ์ํฉ์ ๋ง๋ ๋๊ตฌ ์ ํ (Atom, Agent ๋ฑ)
์ค์ 4: ์ฌ์๋ ๋ถ์์ฉ ๋ฌด์
โ ๋ฌธ์ : ์ฌ์๋๋ ์ ์๋ค๋ ๊ฑธ ๊ณ ๋ คํ์ง ์์
โ ํด๊ฒฐ: ์์ ํจ์๋ง ์ฌ์ฉํ๊ณ ๋ถ์์ฉ ์ ๊ฑฐ
๐ฎ STM์ ๋ฏธ๋์ ๋ค๋ฅธ ์ธ์ด๋ก์ ํ์ฐ
Clojure์ STM์ ์ ๋ง ํ์ ์ ์ธ ๊ฐ๋ ์ด์์ด์. ๊ทธ๋์ ๋ค๋ฅธ ์ธ์ด๋ค๋ ์ด ์์ด๋์ด๋ฅผ ๋ฐ์๋ค์ด๊ธฐ ์์ํ์ฃ ! ๐
Haskell
Haskell์ STM์ ํ์
์์คํ
๊ณผ ๊ฒฐํฉํด์ ๋์ฑ ๊ฐ๋ ฅํ๊ฒ ๋ง๋ค์์ด์. STM ๋ชจ๋๋๋ฅผ ํตํด ํธ๋์ญ์
์ฝ๋๋ฅผ ํ์
๋ ๋ฒจ์์ ๊ตฌ๋ถํ ์ ์์ฃ .
Scala
ScalaSTM ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด Clojure์ ์ ์ฌํ STM ๊ธฐ๋ฅ์ ์ ๊ณตํด์. Scala์ ๊ฐ์ฒด์งํฅ๊ณผ ํจ์ํ ํจ๋ฌ๋ค์์ ๋ชจ๋ ํ์ฉํ ์ ์์ด์.
Python
python-stm ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ์์ง๋ง, ๋์ ํ์
์ธ์ด์ ํน์ฑ์ ์ฑ๋ฅ์ด ์ ํ์ ์ด์์.
Java
Multiverse, Akka STM ๋ฑ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ด์. ํ์ง๋ง Java์ ๋ฌธ๋ฒ์ ์ ์ฝ ๋๋ฌธ์ Clojure๋งํผ ์ฐ์ํ์ง๋ ์์์.
STM์ด ์๋ฒฝํ ํด๊ฒฐ์ฑ
์ ์๋์์. ๋ช ๊ฐ์ง ํ๊ณ๊ฐ ์์ฃ :
1. ๋ถ์ฐ ์์คํ
์์์ ์ ์ฝ
STM์ ๋จ์ผ ํ๋ก์ธ์ค ๋ด์์๋ง ์๋ํด์. ์ฌ๋ฌ ์๋ฒ์ ๊ฑธ์น ๋ถ์ฐ ํธ๋์ญ์
์ ์ง์ํ์ง ์์์. ์ด๋ฐ ๊ฒฝ์ฐ ๋ค๋ฅธ ์ ๊ทผ์ด ํ์ํด์.
2. ์ค์๊ฐ ์์คํ
์ ์ด๋ ค์
์ฌ์๋ ๋ฉ์ปค๋์ฆ ๋๋ฌธ์ ์คํ ์๊ฐ์ ์์ธกํ๊ธฐ ์ด๋ ค์์. ํ๋ ๋ฆฌ์ผํ์ ์์คํ
์๋ ์ ํฉํ์ง ์์ ์ ์์ด์.
3. ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋
MVCC๋ฅผ ์ํด ์ฌ๋ฌ ๋ฒ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ ์งํด์ผ ํ๋ฏ๋ก ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ฆ๊ฐํด์.
4. ํ์ต ๊ณก์
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ๊ณผ ๋ถ๋ณ์ฑ ๊ฐ๋
์ ์ต์ํ์ง ์์ ๊ฐ๋ฐ์์๊ฒ๋ ์ง์
์ฅ๋ฒฝ์ด ์์ด์.
์ฅ์ : ์ดํดํ๊ธฐ ์ฌ์, ์์ธก ๊ฐ๋ฅํ ์ฑ๋ฅ
๋จ์ : ๋ฐ๋๋ฝ, ๋ณต์กํ ์ฝ๋, ํ์ฅ์ฑ ์ ํ
STM (Clojure)
์ฅ์ : ์ปดํฌ์ ๋ธ, ๋ฐ๋๋ฝ ์์, ๊ฐ๊ฒฐํ ์ฝ๋
๋จ์ : ์ฌ์๋ ์ค๋ฒํค๋, ๋จ์ผ ํ๋ก์ธ์ค ์ ํ
์กํฐ ๋ชจ๋ธ (Erlang, Akka)
์ฅ์ : ๋ถ์ฐ ์์คํ ์ง์, ๊ฒฉ๋ฆฌ์ฑ
๋จ์ : ๋ฉ์์ง ํจ์ฑ ์ค๋ฒํค๋, ๋ณต์กํ ์ํ ๊ด๋ฆฌ
CSP (Go, Clojure core.async)
์ฅ์ : ๊ฐ๋จํ ๋์์ฑ ๋ชจ๋ธ, ์ฑ๋ ๊ธฐ๋ฐ ํต์
๋จ์ : ๋ฐฑํ๋ ์ ๊ด๋ฆฌ, ๋ณต์กํ ์กฐ์จ
๋ฆฌ์กํฐ๋ธ ํ๋ก๊ทธ๋๋ฐ (RxJava, Reactor)
์ฅ์ : ๋น๋๊ธฐ ์คํธ๋ฆผ ์ฒ๋ฆฌ, ๋ฐฑํ๋ ์
๋จ์ : ํ์ต ๊ณก์ , ๋๋ฒ๊น ์ด๋ ค์
๊ฐ ํจ๋ฌ๋ค์์ ์ฅ๋จ์ ์ด ์์ด์. ์ค์ํ ๊ฑด ์ํฉ์ ๋ง๋ ๋๊ตฌ๋ฅผ ์ ํํ๋ ๊ฑฐ์์! STM์ ์ฌ๋ฌ ์ํ๋ฅผ ์กฐ์จํด์ผ ํ๋ ๋ณต์กํ ๋์์ฑ ๋ฌธ์ ์ ํนํ ๊ฐ๋ ฅํ์ง๋ง, ๋ชจ๋ ์ํฉ์ ๋ง๋ฅ ํด๊ฒฐ์ฑ ์ ์๋์์. ๐ฏ
๐ผ ์ค๋ฌด์์ STM ๋์ ํ๊ธฐ
์ด๋ก ๊ณผ ์์ ๋ ์ถฉ๋ถํ ๋ดค์ผ๋, ์ค์ ํ๋ก์ ํธ์ STM์ ๋์ ํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊น์? ๐
โข ๋ณต์กํ ๋์์ฑ ๋ก์ง์ด ํ์ํ ๋
โข ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ด ๋งค์ฐ ์ค์ํ ๋
โข ๋ฝ ๊ธฐ๋ฐ ์ฝ๋๊ฐ ๋๋ฌด ๋ณต์กํด์ง ๋
โข ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์์ ์ ํธํ ๋
โข ํ์ด Clojure์ ์ต์ํ๊ฑฐ๋ ๋ฐฐ์ธ ์์ง๊ฐ ์์ ๋
โข ๋ถ์ฐ ์์คํ (๋ค๋ฅธ ์๋ฃจ์ ํ์)
โข ์ค์๊ฐ ์์คํ (์์ธก ๊ฐ๋ฅํ ์ฑ๋ฅ ํ์)
โข ํ์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ฒฝํ ๋ถ์กฑ
โข ๋ ๊ฑฐ์ ์์คํ ๊ณผ์ ํตํฉ์ด ๋ณต์กํ ๋
โข I/O ์ค์ฌ์ ์ ํ๋ฆฌ์ผ์ด์
1๋จ๊ณ: ํ์ผ๋ฟ ํ๋ก์ ํธ
์์ ๋ชจ๋์ด๋ ์๋ก์ด ๊ธฐ๋ฅ์ ๋จผ์ ์ ์ฉํด๋ณด์ธ์. ์ ์ฒด ์์คํ
์ ํ ๋ฒ์ ๋ฐ๊พธ๋ ค๊ณ ํ์ง ๋ง์ธ์!
2๋จ๊ณ: ํ ๊ต์ก
STM๊ณผ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋ํ ๊ต์ก์ ์งํํ์ธ์. ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๊ด๋ จ ๊ฐ์๋ฅผ ์ฐพ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์์!
3๋จ๊ณ: ์ฝ๋ ๋ฆฌ๋ทฐ ํ๋ก์ธ์ค
STM ์ฝ๋์ ๋ํ ๋ฆฌ๋ทฐ ๊ฐ์ด๋๋ผ์ธ์ ๋ง๋ค๊ณ , ๋ฒ ์คํธ ํ๋ํฐ์ค๋ฅผ ๊ณต์ ํ์ธ์.
4๋จ๊ณ: ๋ชจ๋ํฐ๋ง ๊ตฌ์ถ
ํธ๋์ญ์
์ฌ์๋ ํ์, ์ฑ๋ฅ ๋ฉํธ๋ฆญ ๋ฑ์ ๋ชจ๋ํฐ๋งํ๋ ์์คํ
์ ๊ตฌ์ถํ์ธ์.
5๋จ๊ณ: ์ ์ง์ ํ๋
ํ์ผ๋ฟ ํ๋ก์ ํธ๊ฐ ์ฑ๊ณตํ๋ฉด ๋ค๋ฅธ ์์ญ์ผ๋ก ์ ์ง์ ์ผ๋ก ํ๋ํ์ธ์.
; ์ค๋ฌด ์์ : ๋ชจ๋ํฐ๋ง์ด ํฌํจ๋ STM ๋ํผ
(def transaction-stats (atom {:total 0
:retries 0
:failures 0}))
(defn monitored-dosync [& body]
(swap! transaction-stats update :total inc)
(let [start-time (System/nanoTime)
retry-count (atom 0)]
(try
(dosync
(swap! retry-count inc)
(when (> @retry-count 1)
(swap! transaction-stats update :retries inc))
(apply (first body) (rest body)))
(catch Exception e
(swap! transaction-stats update :failures inc)
(throw e))
(finally
(let [duration (/ (- (System/nanoTime) start-time) 1000000.0)]
(when (> duration 100)
(println "Warning: Long transaction" duration "ms")))))))
; ์ฌ์ฉ ์
(monitored-dosync
(alter some-ref inc))
; ํต๊ณ ํ์ธ
@transaction-stats
; => {:total 1000, :retries 23, :failures 2}
ํธ๋์ญ์ ์์/์ข ๋ฃ, ์ฌ์๋ ๋ฐ์์ ๋ก๊น ํ์ธ์. ํ์ง๋ง ์ฑ๋ฅ์ ์ํฅ์ ์ฃผ์ง ์๋๋ก ์ฃผ์ํ์ธ์.
์ฑ๋ฅ ํ ์คํธ
์ค์ ๋ถํ์ ์ ์ฌํ ์กฐ๊ฑด์์ ์ถฉ๋ถํ ํ ์คํธํ์ธ์. ํนํ ํผํฌ ์๊ฐ๋๋ฅผ ์๋ฎฌ๋ ์ด์ ํ์ธ์.
๋กค๋ฐฑ ๊ณํ
๋ฌธ์ ๋ฐ์ ์ ๋น ๋ฅด๊ฒ ์ด์ ๋ฒ์ ์ผ๋ก ๋์๊ฐ ์ ์๋ ๊ณํ์ ์ธ์ฐ์ธ์.
๋ฌธ์ํ
STM์ ์ฌ์ฉํ๋ ์ด์ , ์ฃผ์์ฌํญ, ๋ฒ ์คํธ ํ๋ํฐ์ค๋ฅผ ๋ฌธ์ํํ์ธ์.
์ ์ง์ ๋ง์ด๊ทธ๋ ์ด์
๊ธฐ์กด ๋ฝ ๊ธฐ๋ฐ ์ฝ๋๋ฅผ ํ ๋ฒ์ ๋ฐ๊พธ์ง ๋ง๊ณ , ์ ์ง์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ์ธ์.
๐จ STM ๋์์ธ ํจํด
STM์ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ๋์์ธ ํจํด์ ์๊ฐํ ๊ฒ์! ๐ฏ
; โ ๋์ ์: ํธ๋์ญ์
์์์ ๋ณต์กํ ๊ณ์ฐ
(defn bad-update [data-ref]
(dosync
(let [current @data-ref
; ๋ณต์กํ ๊ณ์ฐ (ํธ๋์ญ์
์์์!)
result (expensive-calculation current)]
(ref-set data-ref result))))
; โ
์ข์ ์: ๊ณ์ฐ์ ๋ฐ์์, ํธ๋์ญ์
์ ์งง๊ฒ
(defn good-update [data-ref]
(let [current @data-ref
; ๋ณต์กํ ๊ณ์ฐ (ํธ๋์ญ์
๋ฐ์์!)
result (expensive-calculation current)]
(dosync
; ๋น ๋ฅธ ๊ฒ์ฆ ํ ์
๋ฐ์ดํธ
(when (valid? @data-ref result)
(ref-set data-ref result)))))
; ์ฌ๊ณ ๊ฐ ์ถฉ๋ถํ ๋๋ง ์ฃผ๋ฌธ ์ฒ๋ฆฌ
(defn conditional-order [inventory-ref order-ref item-id quantity]
(dosync
(let [current-stock (get @inventory-ref item-id 0)]
(if (>= current-stock quantity)
(do
(alter inventory-ref update item-id - quantity)
(alter order-ref conj {:item item-id :qty quantity})
{:success true})
{:success false :reason "์ฌ๊ณ ๋ถ์กฑ"}))))
; ์ฌ๋ฌ ๊ฐ์ ์์ ์
๋ฐ์ดํธ๋ฅผ ํ๋๋ก ๋ฌถ๊ธฐ
(defn batch-update [data-ref updates]
(dosync
(doseq [[key value] updates]
(alter data-ref assoc key value))))
; ์ฌ์ฉ ์
(batch-update my-ref {:a 1 :b 2 :c 3})
; 3๋ฒ์ ํธ๋์ญ์
๋์ 1๋ฒ์ผ๋ก ์ฒ๋ฆฌ!
; ๋ฒ์ ๋ฒํธ๋ฅผ ์ฌ์ฉํ ๋๊ด์ ๋ฝ
(def document (ref {:content "" :version 0}))
(defn update-document [expected-version new-content]
(dosync
(let [current @document]
(if (= (:version current) expected-version)
(do
(ref-set document
{:content new-content
:version (inc expected-version)})
{:success true})
{:success false
:reason "๋ฒ์ ์ถฉ๋"
:current-version (:version current)}))))
; ์ํ ๋ณ๊ฒฝ์ ์ด๋ฒคํธ๋ก ๊ธฐ๋ก
(def state (ref {:balance 1000}))
(def events (ref []))
(defn apply-event [event]
(dosync
(case (:type event)
:deposit (alter state update :balance + (:amount event))
:withdraw (alter state update :balance - (:amount event)))
(alter events conj (assoc event :timestamp (System/currentTimeMillis)))))
; ์ฌ์ฉ ์
(apply-event {:type :deposit :amount 500})
(apply-event {:type :withdraw :amount 200})
; ์ด๋ฒคํธ ํ์คํ ๋ฆฌ๋ก ์ํ ์ฌ๊ตฌ์ฑ ๊ฐ๋ฅ!
๐ฌ ๋ง๋ฌด๋ฆฌ: STM์ด ๊ฐ์ ธ์จ ํจ๋ฌ๋ค์์ ๋ณํ
์ฌ๊ธฐ๊น์ง Clojure์ STM์ ๋ํด ๊น์ด ์๊ฒ ์์๋ดค์ด์! ์ ๋ง ๊ธด ์ฌ์ ์ด์์ฃ ? ๐
STM์ ๋จ์ํ ์๋ก์ด ๊ธฐ์ ์ด ์๋๋ผ, ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๋ํ ์ฌ๊ณ ๋ฐฉ์์ ์ ํ์ ๊ฐ์ ธ์์ด์. "์ด๋ป๊ฒ ๋ฝ์ ๊ด๋ฆฌํ ๊น?"์์ "์ด๋ป๊ฒ ์ํ ๋ณ๊ฒฝ์ ์กฐ์จํ ๊น?"๋ก ์ง๋ฌธ์ด ๋ฐ๋ ๊ฑฐ์ฃ .
์ ํต์ ์ธ ๋ฐฉ์์์๋ ๊ฐ๋ฐ์๊ฐ ๋ฝ์ ์์, ๋ฐ๋๋ฝ ํํผ, ๊ฒฝ์ ์กฐ๊ฑด ๋ฑ์ ๋ชจ๋ ์ ๊ฒฝ ์จ์ผ ํ์ด์. ํ์ง๋ง STM์์๋? ๊ทธ๋ฅ dosync ๋ธ๋ก ์์ ๋น์ฆ๋์ค ๋ก์ง์ ์์ฑํ๋ฉด ๋์ด์์. ๋๋จธ์ง๋ ๋ฐํ์์ด ์์์ ์ฒ๋ฆฌํด์ฃผ์ฃ ! ๐
๋ณต์กํ ๋ฝ ๊ด๋ฆฌ ์ฝ๋๊ฐ ์ฌ๋ผ์ง๊ณ , ๋น์ฆ๋์ค ๋ก์ง์ ์ง์คํ ์ ์์ด์.
2. ์์ ์ฑ
๋ฐ๋๋ฝ, ๋ ์ด์ค ์ปจ๋์ ๊ฐ์ ๊ณ ์ง์ ์ธ ๋ฌธ์ ๋ค์ด ์์ฒ์ ์ผ๋ก ์ฐจ๋จ๋ผ์.
3. ์ปดํฌ์ ๋น๋ฆฌํฐ
์์ ํธ๋์ญ์ ๋ค์ ์กฐํฉํด์ ํฐ ํธ๋์ญ์ ์ ๋ง๋ค๊ธฐ ์ฌ์์.
4. ์ ์ง๋ณด์์ฑ
์ฝ๋๊ฐ ๊ฐ๊ฒฐํ๊ณ ์ดํดํ๊ธฐ ์ฌ์์ ์ ์ง๋ณด์๊ฐ ํธํด์.
5. ํ์ฅ์ฑ
๋ฝ ๋๊ธฐ ์๊ฐ์ด ์์ด์ ๋ ๋ง์ ๋์ ์์ ์ ์ฒ๋ฆฌํ ์ ์์ด์.
๋ฌผ๋ก STM์ด ๋ชจ๋ ๋ฌธ์ ์ ํด๋ต์ ์๋์์. ๋ถ์ฐ ์์คํ
, ์ค์๊ฐ ์์คํ
, I/O ์ค์ฌ ์ ํ๋ฆฌ์ผ์ด์
๋ฑ์์๋ ๋ค๋ฅธ ์ ๊ทผ์ด ํ์ํ ์ ์์ด์. ํ์ง๋ง ์ ์ ํ ์ํฉ์์ ์ฌ์ฉํ๋ค๋ฉด, STM์ ์ ๋ง ๊ฐ๋ ฅํ ๋๊ตฌ๊ฐ ๋ ์ ์์ด์! ๐ช
ํนํ ๊ธ์ต, ๊ฒ์, ํ์
๋๊ตฌ ๊ฐ์ด ๋ณต์กํ ์ํ ๊ด๋ฆฌ๊ฐ ํ์ํ ๋ถ์ผ์์ STM์ ์ง๊ฐ๊ฐ ๋ฐํ๋ผ์. ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ๊ฑฐ๋ ์ฒ๋ฆฌ, ํฌ์ธํธ ๊ด๋ฆฌ, ์ฌ๊ณ ๊ด๋ฆฌ ๋ฑ์ ํ์ฉํ ์ ์์ ๊ฑฐ์์!
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ๊ณผ ๋ถ๋ณ์ฑ, ๊ทธ๋ฆฌ๊ณ STM. ์ด ์ธ ๊ฐ์ง๊ฐ ๋ง๋๋ฉด ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ด ์ ๋ง ์ฐ์ํด์ ธ์. ๋ง์น ๋ณต์กํ ์ค์ผ์คํธ๋ผ๋ฅผ ์งํํ๋ ๊ฒ์ฒ๋ผ, ์ฌ๋ฌ ์ค๋ ๋๊ฐ ์กฐํ๋กญ๊ฒ ํ๋ ฅํ๋ ๋ชจ์ต์ ๋ณผ ์ ์์ฃ . ๐ผ
1. ์ง์ ์ฝ๋ ์์ฑํ๊ธฐ
์ด๋ก ๋ง ์๋ ๊ฒ๊ณผ ์ง์ ๊ตฌํํ๋ ๊ฑด ์์ ํ ๋ฌ๋ผ์. ์์ ํ๋ก์ ํธ๋ถํฐ ์์ํด๋ณด์ธ์!
2. ์คํ์์ค ํ๋ก์ ํธ ๋ถ์
GitHub์์ Clojure ํ๋ก์ ํธ๋ฅผ ์ฐพ์๋ณด๊ณ , STM์ด ์ด๋ป๊ฒ ์ฌ์ฉ๋๋์ง ์ดํด๋ณด์ธ์.
3. ์ปค๋ฎค๋ํฐ ์ฐธ์ฌ
ClojureVerse, Reddit์ r/Clojure ๋ฑ์์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ์ํตํ์ธ์.
4. ์ค์ ํ๋ก์ ํธ ๋์
์ค์ ๋ฌธ์ ๋ฅผ STM์ผ๋ก ํด๊ฒฐํด๋ณด์ธ์. ์คํจํด๋ ๊ด์ฐฎ์์. ๊ทธ๊ฒ ์ต๊ณ ์ ํ์ต์ด์์!
5. ์ง์ ๊ณต์
๋ฐฐ์ด ๋ด์ฉ์ ๋ธ๋ก๊ทธ์ ์ ๋ฆฌํ๊ฑฐ๋, ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๋ค๋ฅธ ์ฌ๋๋ค๊ณผ ๊ณต์ ํด๋ณด์ธ์!
๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ์ด๋ ค์์. ํ์ง๋ง STM ๊ฐ์ ๋๊ตฌ๊ฐ ์๋ค๋ฉด ํจ์ฌ ์ฌ์์ง ์ ์์ด์. ์ค์ํ ๊ฑด ์ฌ๋ฐ๋ฅธ ๋๊ตฌ๋ฅผ ์ฌ๋ฐ๋ฅธ ์ํฉ์ ์ฌ์ฉํ๋ ๊ฒ์ด์์. ๐ฏ
์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ์ STM์ด ํ์ํ์ง ๊ณ ๋ฏผํด๋ณด์ธ์. ๋ง์ฝ ์ฌ๋ฌ ์ํ๋ฅผ ์กฐ์จํด์ผ ํ๋ ๋ณต์กํ ๋์์ฑ ๋ฌธ์ ๊ฐ ์๋ค๋ฉด, Clojure์ STM์ด ํ๋ฅญํ ํด๊ฒฐ์ฑ
์ด ๋ ์ ์์ด์!
์ด ๊ธ์ด ์ฌ๋ฌ๋ถ์ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ ์ฌ์ ์ ๋์์ด ๋์๊ธฐ๋ฅผ ๋ฐ๋ผ์. ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ธ๊ณ๋ ๋๊ณ ๊น์ด์. STM์ ๊ทธ ์ค ํ๋์ ์๋ฆ๋ค์ด ์กฐ๊ฐ์ผ ๋ฟ์ด์์. ๊ณ์ ํํํ๊ณ , ๋ฐฐ์ฐ๊ณ , ์ฑ์ฅํ์ธ์! ๐ฑ
Happy coding! ๊ทธ๋ฆฌ๊ณ ๋์์ฑ ๋ฒ๊ทธ ์๋ ์ฝ๋๋ฅผ ์์ฑํ์ธ์! ๐โจ
๐ ์ด ๊ธ์ด ๋์์ด ๋์ จ๋์?
๋ ๋ง์ ํ๋ก๊ทธ๋๋ฐ ์ง์๊ณผ ์ค์ ๋
ธํ์ฐ๊ฐ ํ์ํ์๋ค๋ฉด,
์ฌ๋ฅ๋ท(https://www.jaenung.net)์ '์ง์์ธ์ ์ฒ'์์
๋ค์ํ ๊ฐ๋ฐ ๊ด๋ จ ์ปจํ
์ธ ๋ฅผ ๋ง๋๋ณด์ธ์! ๐
์ฌ๋ฌ๋ถ์ ์ฌ๋ฅ์ ๊ณต์ ํ๊ณ , ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ํจ๊ป ์ฑ์ฅํ์ธ์! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.