SwiftUI 데이터 흐름 & 단방향 아키텍처

발행: (2025년 12월 13일 오전 08:18 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

단방향 데이터 흐름이 의미하는 바

  • User Action → Event 가 계층 아래로 흐릅니다.
  • State 가 계층 위로 올라옵니다.
  • 지름길도 없고, 역채널도 없으며, 예상치 못한 변형도 없습니다.

데이터 흐름을 깨는 경우

  • 뷰가 전역 상태를 직접 변형
  • 서비스가 UI 상태를 스스로 업데이트
  • 여러 ViewModel이 동일한 데이터를 편집
  • 비즈니스 로직을 직접 뷰에 바인딩
  • 기능 경계를 넘어 바인딩을 전달

같은 상태를 두 개 이상의 객체가 변경할 수 있다면 버그는 보장됩니다.

4계층 아키텍처

LayerResponsibility
Views상태를 표시하고, 사용자 인텐트를 전송 (비즈니스 로직 없음)
ViewModel상태를 소유하고, 인텐트를 처리하며, 비동기 작업을 조정
Services부수 효과 수행 (네트워킹, 영속성, 분석)
Models순수 데이터, 로직 없음, 부수 효과 없음

각 계층은 하나의 책임만 가집니다.

뷰: 인텐트를 전송하고, 상태를 변형하지 않음

Button("Like") {
    viewModel.likeTapped()
}

이렇게 하면 안 됩니다:

viewModel.post.likes += 1   // ❌

인텐트 기반 API는 로직을 중앙 집중화하고 테스트 가능하게 합니다.

예시 ViewModel

@Observable
class FeedViewModel {
    var posts: [Post] = []
    var loading = false

    func load() async {  }
    func likeTapped(postID: String) {  }
}

상태 관리 규칙

  • Views는 상태를 읽습니다.
  • ViewModels는 상태를 변형합니다.
  • Services는 UI 상태를 절대 건드리지 않습니다.

비동기 작업은 뷰모델에 있어야 함

@MainActor
func load() async {
    loading = true
    defer { loading = false }

    do {
        posts = try await api.fetchFeed()
    } catch {
        errors.present(map(error))
    }
}

핵심 규칙

  • 항상 메인 액터에서 상태를 업데이트합니다.
  • 서비스가 뷰모델 속성을 변형하도록 하지 않습니다.
  • 비동기 오류는 뷰모델을 통해 다시 흐릅니다.

서비스 설계

좋은 서비스 프로토콜

protocol FeedService {
    func fetchFeed() async throws -> [Post]
}

나쁜 서비스 특성

  • UI 상태를 보유
  • 뷰모델을 변형
  • 네비게이션 트리거
  • 알림 표시

서비스는 오직 작업만 수행해야 합니다.

상태 기반 네비게이션

@Observable
class AppState {
    var route: AppRoute?
}
.onChange(of: appState.route) { route in
    navigate(route)
}

이렇게 하면 네비게이션이 예측 가능하고 테스트 가능해집니다.

위험한 패턴

ChildView(value: $viewModel.someState) // ⚠️

권장 패턴

ChildView(onChange: viewModel.childChanged)

바인딩은 UI 구성용이며, 아키텍처 경계를 넘는 용도로 사용하면 안 됩니다.

테스트가 간단해짐

func test_like_updates_state() async {
    let vm = FeedViewModel(service: MockService())
    await vm.load()
    vm.likeTapped(postID: "1")

    XCTAssertTrue(vm.posts.first!.liked)
}

UI가 필요하지 않습니다.

체크리스트

“이 파일을 읽었을 때, 누가 상태를 소유하고 있는지 알 수 있나요?”
답이 불분명하면 → 아키텍처가 잘못된 것입니다.

단방향 데이터 흐름의 장점

  • 예측 가능한 업데이트
  • 버그 감소
  • 비동기 처리 용이
  • 테스트 간소화
  • 확장 가능한 아키텍처
  • 관심사의 명확한 분리

SwiftUI는 이 모델을 위해 설계되었습니다 — 이를 받아들이면 모든 것이 자연스럽게 맞아떨어집니다.

Back to Blog

관련 글

더 보기 »

SwiftUI 성능 최적화 — 부드러운 UI, 재계산 감소

SwiftUI는 빠릅니다 — 하지만 올바르게 사용할 때만 그렇습니다. 시간이 지나면 다음과 같은 문제에 직면하게 됩니다: - 끊김이 있는 스크롤 성능 - 지연되는 애니메이션 - 느린 리스트 - 뷰가 새로 고침되는 t...

Single State 모델 아키텍처

문제 설명 현대 시스템 아키텍처는 종종 단순성과 일관성을 희생하면서 규모와 유연성을 우선시합니다. 마이크로서비스를 도입하려는 급박함 속에서…