Tong's Blog

[Swift] Combine Filtering Operator 정리 + RxSwift 비교해보기 본문

iOS/Combine

[Swift] Combine Filtering Operator 정리 + RxSwift 비교해보기

통스 2026. 1. 27. 00:30
반응형

안녕하세요.

지난 3편에서는 Combine의 변환(Transforming) Operator를 중심으로
map, flatMap, compactMap, scan 등을 RxSwift와 비교하며 살펴봤습니다.

이번 4편에서는 그 다음 단계로, 값을 걸러내고 흐름을 제어하는 데 핵심이 되는 필터링(Filtering) Operator를 정리해보려고 합니다.

 

필터링(Filtering) Operator란?

필터링 Operator는 Publisher가 방출한 값 중 일부만 통과시키는 역할을 합니다.

 

실무에서는 다음과 같은 상황에서 거의 필수적으로 사용됩니다.

  • 검색어가 바뀌지 않았는데 API가 다시 호출되는 문제 방지
  • 특정 조건을 만족할 때만 UI 업데이트
  • 첫 이벤트 / 마지막 이벤트만 사용
  • 너무 잦은 입력 이벤트 제어

1. filter – 조건을 만족하는 값만 통과

가장 기본적인 필터링 Operator입니다.
조건이 true 인 값만 downstream으로 전달합니다.

 

Combine 예제

let numbers = [1, 2, 3, 4, 5].publisher

numbers
    .filter { $0 % 2 == 0 }
    .sink { value in
        print("값:", value)
    }
    .store(in: &bag)
// 값: 2
// 값: 4

 

RxSwift

Observable.from([1, 2, 3, 4, 5])
    .filter { $0 % 2 == 0 }
    .subscribe(onNext: { value in
        print("값:", value)
    })
    .disposed(by: disposeBag)

 

2. removeDuplicates – 이전 값과 같으면 무시

이전 값과 동일한 값이면 방출하지 않음
UI 중복 업데이트 방지에 매우 중요합니다.

Combine 예제

let inputs = ["a", "a", "b", "b", "c"].publisher

inputs
    .removeDuplicates()
    .sink { value in
        print("값:", value)
    }
    .store(in: &bag)
// 값: a
// 값: b
// 값: c

 

RxSwift

distinctUntilChanged 사용

Observable.from(["a", "a", "b", "b", "c"])
    .distinctUntilChanged()
    .subscribe(onNext: { value in
        print("값:", value)
    })
    .disposed(by: disposeBag)

 

3. first / last – 처음 또는 마지막 값만 사용,  prefix – 앞에서 N개만 사용

 

  • first() : 가장 처음 값 하나만 받고 종료
  • last() : 모든 스트림이 끝난 후 마지막 값 하나만 전달

Combine 예제

[1, 2, 3, 4].publisher
    .first()
    .sink { print("첫 값:", $0) }
    .store(in: &bag)
// 첫 값: 1

[1, 2, 3, 4].publisher
    .last()
    .sink { print("마지막 값:", $0) }
    .store(in: &bag)
// 마지막 값: 4

[1, 2, 3, 4].publisher
    .prefix(2)
    .sink { print("값:", $0) }
    .store(in: &bag)
// 값: 1, 2

 

 

RxSwift

take, takeLast 으로 사용합니다.

Observable.from([1, 2, 3, 4])
    .take(1)
    .subscribe(onNext: { value in
        print("첫 값:", value)
    })
    .disposed(by: disposeBag)

Observable.from([1, 2, 3, 4])
    .takeLast(1)
    .subscribe(onNext: { value in
        print("마지막 값:", value)
    })
    .disposed(by: disposeBag)

 

4. dropFirst / drop(while:) – 앞부분 무시

초기 이벤트를 무시하고 싶을 때 사용합니다.
초기 상태값, 최초 로딩 이벤트 무시 등에 자주 사용됩니다.

 

Combine 예제

[0, 1, 2, 3].publisher
    .dropFirst()
    .sink { print($0) }
    .store(in: &bag)
// 1, 2, 3

[0, 0, 1, 2].publisher
    .drop(while: { $0 == 0 })
    .sink { print($0) }
    .store(in: &bag)
// 1, 2

RxSwift

skip 으로 대응합니다.

Observable.from([0, 1, 2, 3])
    .skip(1)
    .subscribe(onNext: { value in
        print(value)
    })
    .disposed(by: disposeBag)

Observable.from([0, 0, 1, 2])
    .skip(while: { $0 == 0 })
    .subscribe(onNext: { value in
        print(value)
    })
    .disposed(by: disposeBag)

 

실무 예제 – 중복 요청 방지 패턴

// TextField 입력 상태 체크 및 검색 API 요청

inputPublisher
    .removeDuplicates()
    .filter { !$0.isEmpty }
    .map { $0.trimmingCharacters(in: .whitespaces) }
    .sink { query in
        print("요청:", query)
    }
    .store(in: &bag)

 

기능 Combine RxSwift
조건 필터 filter filter
중복 제거 removeDuplicates distinctUntilChanged
첫 값 first / take(1) take(1)
마지막 값 last takeLast(1)
앞부분 제거 drop / dropWhile skip / skipWhile
앞부분 선택 prefix take

 

이번 4편에서는 Combine의 필터링 Operator를 중심으로,
값 기반 필터링과 스트림 정제 방법을 정리해봤습니다.

필터링을 잘 활용하면 코드가 단순해지고,
불필요한 연산과 네트워크 비용을 크게 줄일 수 있습니다.

시간 기반 제어(debounce, throttle)는 다음 편에서 별도로 자세히 다룰 예정입니다.

 

읽어주셔서 감사합니다

반응형
Comments