iOS Development Guide

TCA 개념 + 샘플앱 구현 전달 문서

TCA를 처음 보는 개발자가 왜/어떻게/어디를 먼저 봐야 하는지를 한 화면에서 이해할 수 있게 정리한 문서다.

학습 날짜

2026-05-12

Why This Work Exists

질문 포인트가 “TCA가 무엇인지” + “현재 샘플앱에서 어떻게 적용됐는지”였기 때문에, 개념 설명과 코드 매핑을 함께 제공한다.

Scope / Non-scope

  • Scope: Mini TCA 기준 State/Action/Reducer/Effect/Store 흐름
  • Scope: 샘플 파일별 역할, 읽기 순서, 취소/비동기 처리
  • Non-scope: 실제 Point-Free 라이브러리 전체 API 심화

초간단 버전 (먼저 이것만 이해)

아래 4줄만 먼저 기억하면 된다.

정말 최소 코드 (Effect 명시)
enum Action { case load, loaded(String) }

struct Effect {
  let run: () async -> Action
}

func reducer(action: Action) -> Effect? {
  switch action {
  case .load:
    return Effect {
      let msg = (try? await fetchMessage()) ?? "fail"
      return .loaded(msg)
    }
  case .loaded:
    return nil
  }
}

func fetchMessage() async throws -> String {
  try await Task.sleep(nanoseconds: 300_000_000)
  return "ok"
}
실전용 async/await 최소 코드
struct State {
  var count = 0
  var isLoading = false
  var message = ""
}

enum Action {
  case plus, minus
  case loadMessageTapped
  case messageLoaded(String)
}

// 핵심: reducer는 Effect(비동기 작업)를 반환
func reducer(state: inout State, action: Action) -> Effect<Action> {
  switch action {
  case .plus:
    state.count += 1
    return .none

  case .minus:
    state.count -= 1
    return .none

  case .loadMessageTapped:
    state.isLoading = true
    return .run { send in
      Task {
        let text = await fetchMessage()      // async/await 사용
        send(.messageLoaded(text))           // 결과를 Action으로 환원
      }
    }

  case .messageLoaded(let text):
    state.isLoading = false
    state.message = text
    return .none
  }
}

func fetchMessage() async -> String {
  try? await Task.sleep(nanoseconds: 1_000_000_000)
  return "loaded"
}

TCA란 무엇인가

데이터 흐름(실행 순서 요약)

의미: 버튼 탭 이후 상태가 실제로 바뀌고 화면이 다시 그려질 때까지의 실행 순서

As-is / To-be

  • As-is: 기본 템플릿 ViewController
  • To-be: TCAExample 폴더로 코어/피처/뷰 분리

What The Developer Must Do Next

  • 1) MiniTCA.swift로 send/effect/cancel 개념 이해
  • 2) CounterFeature.swift에서 reducer 분기 확인
  • 3) CounterViewController.swift 바인딩 확인
  • 4) 필요 시 실제 ComposableArchitecture 패키지로 이관

API / Data Contract

이 샘플은 외부 실 API 대신 학습용 비동기 환경 함수를 사용한다.

Class Diagram

classDiagram direction LR class Store~State,Action~ { +state +send(action) +observe(observer) } class Effect~Action~ { +operation(send) +cancellableID +isCancellation } class CounterState { +count:Int +isLoading:Bool +message:String +lastLoadedAt:Date? } class CounterAction class CounterViewController Store --> Effect : returns Store --> CounterState : owns CounterViewController --> Store : send/observe CounterAction <.. CounterViewController : emits

Sequence Diagram

sequenceDiagram participant U as User participant V as ViewController participant S as Store participant R as Reducer participant E as EffectTask U->>V: Tap "Load Message" V->>S: send(.loadMessageTapped) S->>R: reduce(state, action) R-->>S: state.isLoading=true + Effect.run(id) S->>E: start task E-->>S: send(.messageResponse/.messageFailed) S->>R: reduce(state, response) R-->>S: state update S-->>V: observe(newState) V-->>U: render U->>V: Tap "Cancel Load" V->>S: send(.cancelLoadTapped) S->>R: reduce(state, cancel) R-->>S: Effect.cancel(id) S->>E: cancel task

Flowchart

flowchart TD A[User Event] --> B[ViewController send Action] B --> C[Store] C --> D[Reducer] D --> E{Effect needed?} E -->|No| F[State Updated] E -->|Yes| G[Run Effect Task] G --> H[Emit Response Action] H --> C F --> I[Observers Notified] I --> J[UI Render]

QA Checklist

  • 증가/감소/리셋 시 즉시 상태 반영
  • Load 중 버튼 비활성 처리
  • Cancel 시 로딩 중단 및 문구 반영
  • 성공/실패 메시지 정상 표시

Operations / Rollout Checklist

  • 실 라이브러리 도입 시 패키지 버전 고정
  • Effect 취소 ID 정책 기능별 표준화
  • State 접근은 Store 단일 경로 유지

샘플 파일 경로

/Users/hankyulee/Desktop/Xcodes/test/tca/tca/TCAExample/MiniTCA.swift
/Users/hankyulee/Desktop/Xcodes/test/tca/tca/TCAExample/CounterFeature.swift
/Users/hankyulee/Desktop/Xcodes/test/tca/tca/TCAExample/CounterViewController.swift
/Users/hankyulee/Desktop/Xcodes/test/tca/tca/SceneDelegate.swift

Q

Q. "데이터 흐름(실행 순서 요약)"은 무슨 뜻?

A. 버튼 탭 이후 Action 전달, Reducer 처리, Effect 재전달, 렌더까지 실제 실행 순서를 짧게 묶어 표현한 것.

Q. 왜 취소 ID가 필요한가?

A. 동일 목적의 오래된 비동기 작업을 취소하고 최신 요청만 유지하기 위해서다.

Q. View가 상태를 직접 바꿔도 되나?

A. 권장하지 않는다. TCA 원칙은 Action을 통해서만 상태가 변경된다.

Q. 지금 코드는 진짜 TCA 라이브러리인가?

A. 아니고 학습용 Mini TCA다. 개념은 동일하고 실제 라이브러리 문법은 더 강력하다.