Notice
Recent Posts
Recent Comments
Link
ยซ   2025/03   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Archives
Today
Total
๊ด€๋ฆฌ ๋ฉ”๋‰ด

J

[SwiftUI] SwiftUI + TCA : Redux ํŒจํ„ด ์˜ํ–ฅ? ๋‹จ๋ฐฉํ–ฅ? ๋ณธ๋ฌธ

SwiftUI

[SwiftUI] SwiftUI + TCA : Redux ํŒจํ„ด ์˜ํ–ฅ? ๋‹จ๋ฐฉํ–ฅ?

yujaehui 2024. 6. 30. 20:02

๐Ÿง˜ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์—…

TCA๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ์ž์ฃผ ์–ธ๊ธ‰๋˜๋Š” ๊ฐœ๋…๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.

Redux ํŒจํ„ด์˜ ์˜ํ–ฅ์„ ๋ฐ›์•˜๋‹ค, ๋‹จ๋ฐฉํ–ฅ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋”ฐ๋ฅธ๋‹ค ๋“ฑ์ธ๋ฐ, ์ฒ˜์Œ ์ ‘ํ•˜๋ฉด ์ด๋Ÿฐ ์šฉ์–ด๋“ค์ด ์˜คํžˆ๋ ค ์ดํ•ด๋ฅผ ๋ฐฉํ•ดํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋จผ์ € ์ด ๊ฐœ๋…๋“ค์„ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•œ ํ›„, ๋ณธ๊ฒฉ์ ์œผ๋กœ TCA์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!


๐Ÿ” TCA๊ฐ€ Redux ํŒจํ„ด์—์„œ ์˜ํ–ฅ์„ ๋ฐ›์€ ํ•ต์‹ฌ ์›์น™

TCA๋Š” Redux ํŒจํ„ด์—์„œ ์˜ํ–ฅ์„ ๋ฐ›์•„ "๋‹จ์ผ ์ƒํƒœ(Single State)์™€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜ ๊ธฐ๋ฐ˜์˜ ์•ก์…˜ ์ฒ˜๋ฆฌ(Pure Function Action Handling)" ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

์ง€๊ธˆ๋ถ€ํ„ฐ ์ด๊ฒŒ ๋ฌด์Šจ ์˜๋ฏธ์ธ์ง€ ํ•˜๋‚˜์”ฉ ์‰ฝ๊ฒŒ ํ’€์–ด์„œ ์„ค๋ช…ํ•ด๋ณผ๊ฒŒ์š”.


1๏ธโƒฃ ๋‹จ์ผ ์ƒํƒœ(Single State)๋ž€?

๐Ÿ‘‰ ์•ฑ์˜ ๋ชจ๋“  ์ƒํƒœ(State)๋ฅผ ํ•˜๋‚˜์˜ struct ์•ˆ์—์„œ ๊ด€๋ฆฌํ•œ๋‹ค.

์ด ๋ง์€ ์•ฑ ์ „์ฒด์˜ ๋ฐ์ดํ„ฐ(์ƒํƒœ)๊ฐ€ ๋‹จ ํ•˜๋‚˜์˜ ๊ตฌ์กฐ์ฒด๋กœ ํ‘œํ˜„๋œ๋‹ค๋Š” ๊ฒƒ ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, "์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ทฐ(View)๋‚˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ์ž ์ƒํƒœ๋ฅผ ๊ฐ€์ง€์ง€ ์•Š๊ณ , ํ•˜๋‚˜์˜ ์ค‘์•™ ์ƒํƒœ์—์„œ ๊ด€๋ฆฌ๋œ๋‹ค" ๋ผ๋Š” ๊ฐœ๋…์ด์—์š”.

๐Ÿ”น ๊ธฐ์กด ๋ฐฉ์‹ (SwiftUI @State)

๊ธฐ์กด SwiftUI์—์„œ๋Š” ๋ทฐ๋งˆ๋‹ค @State ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜์—ฌ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

struct CounterView: View {
    @State private var count: Int = 0

    var body: some View {
        VStack {
            Text("\\(count)")
            Button("์ฆ๊ฐ€") { count += 1 }
        }
    }
}
  • CounterView ๋‚ด๋ถ€์—์„œ count ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•จ.
  • ํ•˜์ง€๋งŒ, ๋งŒ์•ฝ ๋‹ค๋ฅธ ํ™”๋ฉด์—์„œ๋„ count๋ฅผ ๊ณต์œ ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ, @State๋ฅผ @StateObject๋‚˜ @EnvironmentObject๋กœ ๋ฐ”๊ฟ”์•ผ ํ•˜๋Š” ๋ถˆํŽธํ•จ.

๐Ÿ”น TCA ๋ฐฉ์‹ (Single State)

TCA์—์„œ๋Š” ์•ฑ์˜ ๋ชจ๋“  ์ƒํƒœ๋ฅผ State ๋ผ๋Š” ๋‹จ์ผ ๊ตฌ์กฐ์ฒด์—์„œ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

struct CounterFeature: Reducer {
    struct State: Equatable {
        var count: Int = 0
    }
}
  • count ์ƒํƒœ๋Š” ๋” ์ด์ƒ View ์•ˆ์— ์กด์žฌํ•˜์ง€ ์•Š๊ณ , State ๊ตฌ์กฐ์ฒด ์•ˆ์—์„œ ๊ด€๋ฆฌ๋จ.
  • ์ฆ‰, ์•ฑ ์ „์ฒด๊ฐ€ ๋‹จ ํ•˜๋‚˜์˜ ์ƒํƒœ(State)๋กœ ํ‘œํ˜„๋จ.
  • ๋•๋ถ„์— ์—ฌ๋Ÿฌ ํ™”๋ฉด์—์„œ ๊ฐ™์€ ์ƒํƒœ๋ฅผ ์‰ฝ๊ฒŒ ๊ณต์œ ํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ.

โžก "์•ฑ์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋Š” ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌ๋œ๋‹ค!"

โžก "์–ด๋””์„œ๋“  ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ์ฐธ์กฐํ•˜๊ณ  ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค!"


2๏ธโƒฃ ์ˆœ์ˆ˜ ํ•จ์ˆ˜(Pure Function) ๊ธฐ๋ฐ˜์˜ ์•ก์…˜ ์ฒ˜๋ฆฌ

๐Ÿ‘‰ ๋ชจ๋“  ์ƒํƒœ(State) ๋ณ€๊ฒฝ์€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜(Pure Function)์ธ Reducer์—์„œ๋งŒ ์ฒ˜๋ฆฌํ•œ๋‹ค.

๐Ÿ‘‰ ์ฆ‰, Reducer ๋‚ด๋ถ€์—์„œ๋งŒ State๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ณ , ๋‹ค๋ฅธ ๊ณณ์—์„œ๋Š” ์ƒํƒœ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋‹ค.

๐Ÿ’ก ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋ž€?

๊ฐ™์€ ์ž…๋ ฅ(State + Action)์ด ๋“ค์–ด์˜ค๋ฉด ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ(State)์ด ๋‚˜์˜ค๋Š” ํ•จ์ˆ˜๋ถ€์ˆ˜ํšจ๊ณผ(Side Effect)๊ฐ€ ์—†์Œ
⇒ ๋„คํŠธ์›Œํฌ ์š”์ฒญ, DB ์ €์žฅ ๊ฐ™์€ ์ž‘์—…์„ ํ•˜์ง€ ์•Š์Œ

๐Ÿ”น SwiftUI์—์„œ ์ง์ ‘ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ์‹

๊ธฐ๋ณธ SwiftUI์—์„œ๋Š” ์ƒํƒœ๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

struct CounterView: View {
    @State private var count: Int = 0

    var body: some View {
        VStack {
            Text("\\(count)")
            Button("์ฆ๊ฐ€") {
                count += 1  // ์ง์ ‘ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ
            }
        }
    }
}
  • Button์„ ๋ˆ„๋ฅด๋ฉด count += 1 ์ด ์‹คํ–‰๋˜๋ฉด์„œ ์ƒํƒœ๊ฐ€ ์ฆ‰์‹œ ๋ณ€๊ฒฝ๋จ.
  • ํ•˜์ง€๋งŒ ์ƒํƒœ๊ฐ€ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์ˆ˜์ •๋  ๊ฒฝ์šฐ ์˜ˆ์ธกํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง€๋ฉฐ, ๋””๋ฒ„๊น…์ด ์–ด๋ ต๊ณ , ์ƒํƒœ ๋ณ€๊ฒฝ ๋กœ์ง์ด ๋ถ„์‚ฐ๋จ.

๐Ÿ”น TCA ๋ฐฉ์‹ (Reducer์—์„œ๋งŒ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ)

TCA์—์„œ๋Š” ์ƒํƒœ๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ , Reducer๋ฅผ ํ†ตํ•ด์„œ๋งŒ ๋ณ€๊ฒฝ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

struct CounterFeature: Reducer {
    struct State: Equatable {
        var count: Int = 0
    }
    
    enum Action: Equatable {
        case increment
    }
    
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .increment:
                state.count += 1  // ์ƒํƒœ ๋ณ€๊ฒฝ
                return .none
            }
        }
    }
}

๐Ÿ’ก Reducer์˜ ์›์น™

  1. ์ž…๋ ฅ์ด ๊ฐ™์œผ๋ฉด ํ•ญ์ƒ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.
    • state.count += 1 ์€ ๊ฐ™์€ Action.increment ๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ํ•ญ์ƒ ๊ฐ™์€ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•จ.
  2. Reducer ๋‚ด๋ถ€์—์„œ๋งŒ State๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.
    • View์—์„œ๋Š” state.count += 1 ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Œ.
  3. Side Effect(๋ถ€์ˆ˜ํšจ๊ณผ) ์—†์ด ์ƒํƒœ ๋ณ€๊ฒฝ๋งŒ ๋‹ด๋‹นํ•œ๋‹ค.
    • API ์š”์ฒญ ๊ฐ™์€ ์ž‘์—…์€ Effect๋ฅผ ํ†ตํ•ด ๋ถ„๋ฆฌํ•ด์•ผ ํ•จ.

โžก "Reducer์—์„œ๋งŒ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค!"

โžก "Reducer๋Š” ๊ฐ™์€ ์ž…๋ ฅ(Action + State)์— ๋Œ€ํ•ด ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ์„ ๋‚ด์•ผ ํ•œ๋‹ค!"


3๏ธโƒฃ ์•ก์…˜ ๊ธฐ๋ฐ˜ ์ƒํƒœ ๋ณ€๊ฒฝ (Action-Driven State Management)

TCA์—์„œ๋Š” Action์„ ํ†ตํ•ด ์ƒํƒœ ๋ณ€๊ฒฝ์ด ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ƒํƒœ ๋ณ€๊ฒฝ์ด ๋ช…ํ™•ํ•˜๊ฒŒ ์ถ”์  ๊ฐ€๋Šฅ ํ•ด์ง€๊ณ , ์–ด๋–ค ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€๋ฅผ ์‰ฝ๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

enum Action: Equatable {
    case increment
}
  • ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ Action์œผ๋กœ ์ •์˜ํ•˜๊ณ ,
  • Reducer์—์„œ Action์„ ๋ฐ›์•„ State๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

โžก "๋ชจ๋“  ์ƒํƒœ ๋ณ€๊ฒฝ์€ ๋ช…ํ™•ํ•œ Action์„ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง„๋‹ค!"


๐Ÿ“ ์ •๋ฆฌ: TCA๊ฐ€ Redux ํŒจํ„ด์—์„œ ์˜ํ–ฅ์„ ๋ฐ›์€ ์ด์œ 

โœ… ํ•ต์‹ฌ ๊ฐœ๋…  โœ… ์„ค๋ช…
๋‹จ์ผ ์ƒํƒœ (Single State) ์•ฑ์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์˜ State ๊ตฌ์กฐ์ฒด์—์„œ ๊ด€๋ฆฌ
์ˆœ์ˆ˜ ํ•จ์ˆ˜ ๊ธฐ๋ฐ˜ ์ƒํƒœ ๋ณ€๊ฒฝ (Pure Function Reducer) ์ƒํƒœ ๋ณ€๊ฒฝ์€ ๋ฐ˜๋“œ์‹œ Reducer์—์„œ๋งŒ ์ฒ˜๋ฆฌ (View์—์„œ ์ง์ ‘ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€)
์•ก์…˜ ๊ธฐ๋ฐ˜ ์ƒํƒœ ๋ณ€๊ฒฝ (Action-Driven State Management) Action์„ ํ†ตํ•ด ๋ช…ํ™•ํ•˜๊ฒŒ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ

๐Ÿ” TCA๋Š” ๋‹จ๋ฐฉํ–ฅ(Unidirectional) ์•„ํ‚คํ…์ฒ˜์ด๋‹ค.

TCA(The Composable Architecture)๋Š” Redux ํŒจํ„ด์—์„œ ์˜ํ–ฅ์„ ๋ฐ›์•„ ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„(Unidirectional Data Flow, UDF)์„ ๋”ฐ๋ฅด๋Š” ๊ตฌ์กฐ์ด๋‹ค.

์ฆ‰, ๋ฐ์ดํ„ฐ๊ฐ€ ํ•œ ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ํ๋ฅด๋ฉฐ, ์ƒํƒœ ๋ณ€๊ฒฝ์ด ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๊ฒŒ ๊ด€๋ฆฌ๋œ๋‹ค.


1๏ธโƒฃ ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด๋ž€?

๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด๋ž€ ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ(์•ก์…˜)์ด ์ƒํƒœ(State)๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์œ ์ผํ•œ ๊ฒฝ๋กœ๊ฐ€ ์ •ํ•ด์ ธ ์žˆ๋Š” ๊ตฌ์กฐ ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

โœ… ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„ (TCA์—์„œ์˜ ํ๋ฆ„)

  1. ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด(Action)
  2. Reducer๊ฐ€ Action์„ ๋ฐ›์•„ State๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ 
  3. View๊ฐ€ ๋ณ€๊ฒฝ๋œ State๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•œ๋‹ค.

๐Ÿ“Œ ๋‹จ ํ•˜๋‚˜์˜ ๋ฐฉํ–ฅ๋งŒ ์กด์žฌํ•œ๋‹ค: [์‚ฌ์šฉ์ž ์ž…๋ ฅ] → [Action ๋ฐœ์ƒ] → [Reducer] → [State ๋ณ€๊ฒฝ] → [View ์—…๋ฐ์ดํŠธ]


2๏ธโƒฃ TCA์˜ ๋ฐ์ดํ„ฐ ํ๋ฆ„ (Unidirectional Flow)

TCA์—์„œ ๋ฐ์ดํ„ฐ๋Š” ์•„๋ž˜์˜ ๊ฒฝ๋กœ๋กœ๋งŒ ์ด๋™ํ•œ๋‹ค.

(1) View → Store (Action ์ „์†ก)

  • ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ(๋ฒ„ํŠผ ํด๋ฆญ ๋“ฑ)์ด ๋ฐœ์ƒํ•˜๋ฉด Action์„ Store.send(action)์„ ํ†ตํ•ด ์ „๋‹ฌํ•œ๋‹ค.
struct CounterView: View {
    let store: StoreOf<CounterFeature>

    var body: some View {
        WithViewStore(store, observe: { $0 }) { viewStore in
            VStack {
                Text("\\(viewStore.count)")
                Button("+") {
                    viewStore.send(.increment)  // Action ๋ฐœ์ƒ
                }
            }
        }
    }
}
  • ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด Action.increment๊ฐ€ Reducer๋กœ ์ „๋‹ฌ๋จ.

(2) Store → Reducer (์ƒํƒœ ๋ณ€๊ฒฝ)

  • Reducer์—์„œ Action์„ ๋ฐ›์•„ State๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.
struct CounterFeature: Reducer {
    struct State: Equatable {
        var count: Int = 0
    }
    
    enum Action: Equatable {
        case increment
    }
    
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .increment:
                state.count += 1  // ์ƒํƒœ ๋ณ€๊ฒฝ
                return .none
            }
        }
    }
}
  • Reducer๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜(Pure Function)๋กœ, Action์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ƒˆ๋กœ์šด State๋ฅผ ๋ฐ˜ํ™˜ํ•จ.

(3) Store → View (์ƒํƒœ ์ „๋‹ฌ)

  • Store๊ฐ€ ๋ณ€๊ฒฝ๋œ State๋ฅผ View์— ์ „๋‹ฌํ•˜๋ฉด, SwiftUI๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋งํ•œ๋‹ค.
WithViewStore(store, observe: { $0 }) { viewStore in
    Text("\\(viewStore.count)")
}
  • WithViewStore๋ฅผ ํ†ตํ•ด State๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ count ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ํ™”๋ฉด์„ ๋‹ค์‹œ ๊ทธ๋ฆผ.

3๏ธโƒฃ ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์˜ ์žฅ์ 

โœ… ์˜ˆ์ธก ๊ฐ€๋Šฅํ•จ → ์ƒํƒœ(State)๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ์œ ์ผํ•œ ๊ฒฝ๋กœ๊ฐ€ ์กด์žฌํ•˜๋ฏ€๋กœ, ๋””๋ฒ„๊น…์ด ์‰ฌ์›€.

โœ… ํ…Œ์ŠคํŠธ๊ฐ€ ์šฉ์ดํ•จ → Reducer๊ฐ€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜์ด๋ฏ€๋กœ, ๊ฐ™์€ ์ž…๋ ฅ(Action)์ด ๋“ค์–ด์˜ค๋ฉด ๊ฐ™์€ ๊ฒฐ๊ณผ(State)๊ฐ€ ๋‚˜์™€์„œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅ.

โœ… ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๋†’์Œ → ์ƒํƒœ(State), ๋กœ์ง(Reducer), UI(View)๊ฐ€ ๋ถ„๋ฆฌ๋˜์–ด ์ฝ”๋“œ๊ฐ€ ๋ช…ํ™•ํ•จ.


4๏ธโƒฃ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„๊ณผ์˜ ๋น„๊ต

โœ… ์–‘๋ฐฉํ–ฅ(Bidirectional) ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด๋ž€?

  • ์ƒํƒœ(State)๋ฅผ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์ง์ ‘ ์ˆ˜์ • ํ•˜๊ฑฐ๋‚˜,
  • ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ ๋‚ด๋ ค๊ฐˆ ์ˆ˜๋„ ์žˆ๊ณ , ๋ฐ˜๋Œ€๋กœ ์˜ฌ๋ผ๊ฐˆ ์ˆ˜๋„ ์žˆ๋Š” ๊ตฌ์กฐ.

๐Ÿ’ก ์˜ˆ์‹œ: SwiftUI์˜ @Binding์„ ์‚ฌ์šฉํ•œ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„

struct ParentView: View {
    @State private var count = 0

    var body: some View {
        ChildView(count: $count)  // ์–‘๋ฐฉํ–ฅ ๋ฐ”์ธ๋”ฉ
    }
}

struct ChildView: View {
    @Binding var count: Int  // ์ƒํƒœ๋ฅผ ์ง์ ‘ ์ˆ˜์ • ๊ฐ€๋Šฅ

    var body: some View {
        Button("+") { count += 1 }  // ์ƒํƒœ๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝ
    }
}
  • @Binding์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•˜์œ„ ๋ทฐ์—์„œ ์ƒ์œ„ ๋ทฐ์˜ ์ƒํƒœ๋ฅผ ์ง์ ‘ ์ˆ˜์ • ๊ฐ€๋Šฅ.
  • count += 1์ด ์ง์ ‘ ์‹คํ–‰๋˜์–ด State๊ฐ€ ๋ณ€๊ฒฝ๋จ.
  • TCA์™€ ๋‹ฌ๋ฆฌ, ์ƒํƒœ ๋ณ€๊ฒฝ์ด ์–ด๋””์„œ๋“  ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์„œ ์˜ˆ์ธกํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ.

๐Ÿง˜ ๋งˆ์น˜๋ฉฐ…

๋‹ค์Œ ๊ธ€์—์„œ๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ์ตœ์‹  ๋ฒ„์ „์˜ TCA์˜ ํ•ต์‹ฌ ๊ฐœ๋…๊ณผ ๋™์ž‘ ๊ณผ์ •์„ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.