프로그래밍모바일 개발자

스위프트의 기능적 reduce는 어떻게 작동하며, 사용의 장점과 특징은 무엇인가요? 컬렉션에서 reduce를 사용할 때의 오류와 함정은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

reduce 메커니즘은 데이터 컬렉션에 대한 기능적 작업에 해당하며, 스위프트는 이것을 기능적 언어(map-reduce, fold)에서 가져왔습니다. 역사적으로 이 기능은 모든 컬렉션을 단일 집계 값(예: 합계, 곱셈, 문자열 결합 등)으로 변환할 수 있게 해주며, 모든 요소를 순회하며 결과를 누적합니다. 여기서 해결하는 기본 문제는 수동 루프 대신 데이터 집계를 간결하고 읽기 쉽게, 그리고 버그 없이 처리하는 것입니다.

스위프트에서 reduce는 컬렉션 메서드로 정의되어 있습니다:

func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

이는 초기(시작) 값을 지정하고, 이후 각 요소와 현재의 누산기에서 새로운 집계 값을 반환하는 함수를 작성해야 함을 의미합니다.

코드 예:

let numbers = [1, 2, 3, 4] let sum = numbers.reduce(0) { $0 + $1 } // 10 let joined = numbers.reduce("") { $0 + String($1) } // "1234"

주요 특징:

  • 컬렉션 집계를 한 줄의 코드로 작성할 수 있음
  • 부작용(side-effects)이 없도록 보장 — 컬렉션을 변경하지 않고 기능적으로 작동
  • 오류 발생(throws)이 있는 것부터 숫자 이외의 다양한 타입으로 사용할 수 있음

함정이 있는 질문.

빈 컬렉션에서 reduce는 어떻게 작동하나요?

Reduce는 컬렉션의 각 요소에 적용됩니다. 컬렉션이 비어 있으면 초기(시작) 값이 반환되며, 클로저 호출은 발생하지 않습니다.

코드 예:

let empty: [Int] = [] let sum = empty.reduce(100) { $0 + $1 } // 100

reduce를 사용하여 초기 컬렉션을 변경할 수 있나요?

아니요, reduce는 순수 함수(pure function)로, 초기 컬렉션을 변경하지 않습니다. 모든 변경은 오직 누산기에 대해서만 발생합니다.

reduce와 reduce(into:)의 차이는 무엇인가요?

reduce(into:)는 각 반복 시 누산기를 변형할 수 있게 하며, 값 타입(value type)에 대해 더 효율적으로 작동합니다. 값 타입에서 새 복사본(copy-on-write)의 생성이 비용이 많이 들기 때문입니다.

코드 예:

let nums = [1, 2, 3] let squares = nums.reduce(into: []) { (result: inout [Int], item) in result.append(item * item) } // squares == [1, 4, 9]

전형적인 오류 및 안티 패턴

  • 잘못된 초기 값 선택 (예: 곱셈 집계 시 0 대신 1 사용)
  • reduce 클로저 내의 부작용 (예: 외부 변수 변경)
  • 다른 함수(예: filter, map)로 더 잘 해결할 수 있는 문제에 reduce 사용

실제 예

부정적인 사례

개발자는 products 배열에 저장된 제품 가격을 합산하고 싶어 했습니다. 초기 값 0.0으로 reduce를 사용했지만, 클로저가 할인으로 인해 때때로 음수의 합계를 반환하여 부정확한 최종 결과와 세금 계산 문제를 초래했습니다.

장점:

  • 간결한 코드
  • 수동 루프 없음

단점:

  • 오류는 프로덕션에서만 발견됨
  • 최종 합계의 비즈니스 가치가 부정확함

긍정적인 사례

id에 따라 엔티티 배열에서 캐시를 생성하기 위해 reduce(into:)를 사용했습니다:

let objects: [Entity] = ... let cache = objects.reduce(into: [:]) { $0[$1.id] = $1 }

장점:

  • 빠르고 효율적이며 배열 복사가 없음
  • 캐시의 명확한 타입 지정

단점:

  • reduce(into:) 코드는 초보자에게는 다소 읽기 어려울 수 있음
  • 중복된 키로 인해 우연히 값을 덮어쓸 수 있음