본문 바로가기
Combine

왜 Combine인가?

by thoonk: 2026. 3. 25.
반응형

Combine의 필요성에 대해 기록합니다.

 

Combine이란?

네트워크 요청, 사용자 입력 등 언제 올지 모르는 데이터(비동기로 발생하는 이벤트)를 다루기 위한 비동기 처리가 필요함.

Combine은 이 처리를 해결하기 위해 만든 프레임워크

→ 여러 비동기 패턴들을 하나의 일관된 파이프라인으로 통합하는 프레임워크

기존 방식의 한계

Combine 이전에는 여러가지 방식들(Completion Handler, Delegate, Notification Center, KVO 등)을 사용하여 처리했었음.

각각의 패턴에 장점이 있지만, 한 곳에 모이면 코드의 흐름을 따라가기가 어려워짐.

어떤 이벤트가 어디서 시작되어 어디로 흘러가는지, 에러는 어느 시점에서 처리되는지 이 모든걸 머릿속에서 추적해야 함.

콜백 중첩 문제

흔한 예를 들자면, 로그인 후 사용자 정보를 가져오고 그 정보로 프로필 이미지를 다운로드하는 3단계 비동기 작업이 있음.

loginAPI.login(email: email, password: pw) { result in
    switch result {
    case .success(let token):
        userAPI.fetchProfile(token: token) { result in
            switch result {
            case .success(let profile):
                imageLoader.download(url: profile.avatarURL) { result in
                    switch result {
                    case .success(let image):
                        DispatchQueue.main.async {
                            self.avatarImageView.image = image
                        }
                    case .failure(let error):
                        self.showError(error) // 에러 처리 3
                    }
                }
            case .failure(let error):
                self.showError(error) // 에러 처리 2
            }
        }
    case .failure(let error):
        self.showError(error) // 에러 처리 1
    }
}

이 코드의 문제점은 아래와 같이 많음.

  • 들여쓰기가 깊어질수록 가독성이 떨어지고 흐름을 파악하기 어려워 유지보수가 어려워짐.
  • 에러가 발생하면 각 레벨에서 개별 처리해야 함.
  • 실행 흐름 추적의 어려움
  • 메모리 누수 및 순환 참조 가능성

Combine을 통한 해결

loginAPI.login(email: email, password: pw)
    .flatMap { token in userAPI.fetchProfile(token: token) }
    .flatMap { profile in imageLoader.download(url: profile.avatarURL) }
    .receive(on: DispatchQueue.main)
    .sink(
        receiveCompletion: { completion in
            if case .failure(let error) = completion {
                self.showError(error)  // 에러 처리 딱 한 곳
            }
        },
        receiveValue: { image in
            self.avatarImageView.image = image
        }
    )
    .store(in: &cancellables)

컴바인을 이용한 코드에서는 아래와 같이 해결할 수 있음.

  • 중첩 없이 로그인 → 프로필 조회 → 이미지 다운로드 흐름이 그대로 드러나 가독성이 올라감.
  • 에러 처리를 한 곳에서 할 수 있어 어느 단계에서 실패하든 한번에 처리할 수 있음.
  • cancellables 을 이용하여 메모리가 자동으로 관리됨.

컴바인이 나오기 전에는 서드파티인 RxSwift 등을 사용하여 해결했지만 성능이 더 좋은 퍼스트파티 Combine을 사용하지 않을 이유가 없음.

또한, 선언형 UI인 SwiftUI와 함께 사용하기 좋아 필수로 사용해야 함.

비동기 처리의 필요성

UI는 메인 스레드에서 돌아가고, 시간이 걸리는 작업을 메인 스레드에서 하면 앱이 멈춤. 그래서 네트워크, 입출력, 복잡한 연산은 백그라운드에서 처리하고 결과만 메인 스레드로 돌려보내야 함.

하지만, 이러한 흐름을 명령형(imperative) 코드로 표현하면, 코드가 시간의 흐름을 따라가지 않음.

Combine은 해당 문제를 선언형(declarative)으로 해결함. 이 데이터가 오면 이렇게 변환하고, 어디로 보낼지 선언하면 프레임워크가 실행 순서와 스레드 관리를 대신 처리함.

반응형

댓글