Skip to content

Conversation

@dudwntjs
Copy link
Collaborator

@dudwntjs dudwntjs commented Dec 17, 2025

🎬 구현화면

일별 박스오피스 가져왓더용

🍿 MovieViewModel 설명

1) @Published 프로퍼티

@Published private(set)var movies: [MovieInfo]= []
@Published private(set)var errorMessage:String?=nil

@Published는 값이 변경될 때마다 새로운 값을 방출하는 Publisher입니다
ViewController는 해당 프로퍼티를 구독(subscribe) 하여 값 변경 시 자동으로 UI를 갱신합니다.

상태 변경 책임을 ViewModel 내부로 한정하고 싶어서,
private(set)을 사용하여 ViewModel 외부에서는 값을 읽기만 가능하도록 제한했어용

  • movies: 화면에 표시할 영화 목록
  • errorMessage: 네트워크 실패 시 전달할 에러 메시지

2)fetch(targetDate:) — ViewModel의 입력(Input)

funcfetch(targetDate:String)

ViewController에서 호출하는 메서드로,
Combine 파이프라인을 시작하는 트리거 역할을 합니다.

화면 진입 또는 특정 이벤트 발생 시 네트워크 요청을 수행합니다.

3) Future — 비동기 작업을 Combine으로 래핑

Future<[MovieInfo],Error> { promisein

Future는 어떤 작업을 수행한 뒤, 단 한 번의 결과(success 또는 failure)를 비동기적으로 방출하는 Publisher입니다.
Apple 공식 문서

여러 값을 지속적으로 방출하는 일반적인 Publisher와 달리,
한 번의 결과만 필요한 비동기 작업에 적합하다고 하더라구요‼️

근데 영화진흥위원회 Open API 요청은 요청 → 응답 → 종료 형태의 1회성 네트워크 작업이니까요,,,
네트워크 요청을 Combine 스트림에 연결하기 위해 Future를 사용해봤습니당

사실 async/await 로도 충분히 구현 가능할 것 같긴해요 함 써보고 시펏서여

4) Task — async/await와 Combine 연결

Task {
    let dto = try await service.fetchDailyBoxOffice(...)
}

서비스 레이어는 async/await 기반으로 구현되어 있습니다.
그러나 Future의 클로저 내부에서는 async 함수를 직접 호출할 수 없기 때문에,

Task를 사용하여 Swift Concurrency와 Combine을 연결했습니다.

5) DTO → Entity 변환

이후에는 네트워크 응답으로 받은 DTO를 앱 내부에서 사용하는 Entity로 변환

6) 메인 스레드로 전달

.receive(on:DispatchQueue.main)

Published 프로퍼티는 UI와 직접 연결되기 때문에 값 변경은 반드시 메인 스레드에서 이루어져야 합니다. receive(on:)을 통해 이후 연산과 구독이 메인 스레드에서 실행되도록 합니다.

7) sink — 결과 구독 및 상태 업데이트

.sink { completionin... } receiveValue: { valuein... }

sink는 Publisher를 실제로 실행시키는 구독자(Subscriber) 입니다.

  • completion: 스트림의 성공 또는 실패를 처리
  • receiveValue: 성공적으로 전달된 값을 처리

실패 시 에러 메시지를 업데이트하고,
성공 시 영화 목록을 @Published 프로퍼티에 할당

8) store(in:) — 구독 생명주기 관리

.store(in:&cancellables)

Combine의 구독은 참조가 유지되지 않으면 즉시 취소됩니다.
AnyCancellable을 저장하여 ViewModel이 살아 있는 동안 Combine 스트림이 유지되도록 합니다.

ViewController
↓    fetch()

ViewModel
↓    @Published를 통해 상태 변경을 방출
↓     Future를 사용해 비동기 네트워크 요청을 Combine 스트림으로 변환
↓    sink로 결과를 구독하고 상태를 업데이트
↓    store(in:)으로 구독 생명주기를 관리

ViewController
↓    자동으로 UI 갱신

🎥 Input/Output이 잘 안보이는 것 같은데요?!!🤔

Input/Output 패턴을 명시적인 구조체로 분리하지는 않아서,, 제대로 안보일 수도 있다고 생각해용

Input이 메서드 호출로 표현

funcfetch(targetDate:String)
  • 이 메서드가 Input 역할이라 보면 됩니당
  • 다만 Input struct / transform(input:)처럼 명시적으로 감싸지 않았을 뿐이죠!

Output은 @Published로 바로 노출됨

@Publishedvar movies
@Publishedvar errorMessage
  • Output을 struct로 묶지 않고
  • Combine의 Publisher(@Published)를 직접 Output으로 사용햇습니다

그래도 Combine의 핵심 개념은 다 사용했어요!

Input/Output을 명시적으로 추상화하지 않았을 뿐, Combine의 핵심 개념은 모두 포함되어 있습니당

  • Publisher (@Published)
  • Subscriber (sink)
  • Cancellable 관리 (store(in:))

그래도 Input/Output을 구조적으로 분리하는게 책임 분리 측면에서 더 좋은 것 같아유...흠냥

image 컴바인 스터디 그동안 감사햇더용 다들 행복하새우

@dudwntjs dudwntjs self-assigned this Dec 17, 2025
Copy link
Collaborator

@y-eonee y-eonee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅠㅠ영주언니도 고생많아따

Comment on lines +13 to +14
@Published private(set) var movies: [MovieInfo] = []
@Published private(set) var errorMessage: String? = nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너무너무좋은데 유킷에서 @published 사용보다 Publisher나 Subject를 만들어주는건 어떨까여........? 사실저도잘모르겟어요

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants