개발/Swift

Swift에서 append(contentsOf:) vs 반복 append

까칠코더 2025. 11. 11. 12:18
반응형

Swift에서 append(contentsOf:) vs 반복 append

 

Swift에서 배열에 여러 요소를 추가할 때 두 가지 주요 방식이 있습니다.

  • append(contentsOf:) — 한 번에 여러 요소를 추가 (배열·시퀀스 통째로)
  • 반복 append — 루프 내에서 하나씩 추가

두 방법 모두 동일한 결과를 얻을 수 있지만, 성능·메모리 효율·코드 의도 측면에서 큰 차이가 있습니다.

 

1. 핵심 결론

항목 append(contentsOf:) 반복 append
호출 횟수 1회 N회
메모리 재할당 1회(예상 용량 기반) 여러 번 발생 가능
속도 빠름 (벡터화·버퍼 복사 최적화) 느림 (COW + 재할당)
가독성 “배열 병합” 의도 명확 “요소 단위 추가”로 장황
안전성 성능·COW 최적화 내장 루프 내부에서 동작 제어 필요

요약: 여러 요소를 한 번에 추가할 수 있다면 항상 append(contentsOf:)를 사용하는 것이 좋습니다.

 

2. 기본 예제


2.1 append(contentsOf:)

var a = [1, 2, 3]
let b = [4, 5, 6]
a.append(contentsOf: b)  // [1, 2, 3, 4, 5, 6]

2.2 반복 append

var a = [1, 2, 3]
let b = [4, 5, 6]
for x in b {
    a.append(x)
}

결과는 같지만, 내부 동작은 다릅니다.

 

3. 성능 차이의 이유


3.1 append(contentsOf:)는 한 번의 capacity 계산

Swift 표준 라이브러리는 내부적으로 다음과 같이 최적화되어 있습니다:

  • 추가할 요소 수(rhs.count)를 알고 있을 경우,
    필요한 용량(capacity) 을 미리 확보(reserveCapacity)
  • 한 번의 메모리 복사로 전체 요소를 추가

3.2 반복 append는 루프마다 capacity 체크 + 복사

  • 매번 append 시 배열의 용량(capacity)을 확인
  • 꽉 차면 새 버퍼 할당 + 전체 복사 발생
  • 큰 배열일수록 복사 비용이 누적되어 기하급수적으로 느려짐

예를 들어 1만 개 요소를 추가할 때, append(contentsOf:)는 1회 재할당으로 끝날 수 있지만, 반복 append는 수십 번의 버퍼 확장을 수행합니다.

 

4) 실제 벤치마크 예시

import Foundation

let src = Array(0..<1_000_000)

func measure(_ name: String, _ block: () -> Void) {
    let t1 = CFAbsoluteTimeGetCurrent()
    block()
    let t2 = CFAbsoluteTimeGetCurrent()
    print(name, ":", t2 - t1, "초")
}

// append(contentsOf:)
measure("append(contentsOf:)") {
    var dest = [Int]()
    dest.append(contentsOf: src)
}

// 반복 append
measure("반복 append") {
    var dest = [Int]()
    for n in src { dest.append(n) }
}

일반적으로 append(contentsOf:)가 3~6배 이상 빠름,

대량의 요소를 다룰수록 차이가 커집니다.

 

5. 내부 동작 (표준 라이브러리 수준)

Swift 표준 라이브러리의 Array.append(contentsOf:)는 내부적으로 다음 단계를 수행합니다:

  1. 추가할 컬렉션의 count를 확인
  2. self.reserveCapacity(currentCount + newCount)로 용량 확보
  3. 새 요소를 한 번의 메모리 블록 복사로 추가
extension Array {
    public mutating func append<S>(contentsOf newElements: S) where S: Sequence, S.Element == Element {
        let additionalCount = newElements.underestimatedCount
        reserveCapacity(count + additionalCount)
        for x in newElements { self.append(x) } // 내부 루프도 있지만 이미 capacity 확보됨
    }
}

즉, 반복 append보다 capacity 재조정 비용이 현저히 적습니다.

 

6. 성능 최적화 팁


6.1 reserveCapacity(_:)로 사전 확보

var result = [Int]()
result.reserveCapacity(10_000)
for i in 0..<10_000 {
    result.append(i)
}

append(contentsOf:) 내부에서도 자동 호출하지만, 반복 append를 써야 한다면 반드시 사전 예약을 해주는 것이 좋습니다.

6.2 Lazy + append(contentsOf:)

let doubled = (0..<10_000).lazy.map { $0 * 2 }
var result = [Int]()
result.append(contentsOf: doubled)

Lazy 시퀀스도 지원하므로, 중간 배열 생성 없이 효율적으로 처리됩니다.

 

7. 문자열에서도 동일 원리

var text = "Hello"
let add = [" ", "Swift", " ", "World"]
text.append(contentsOf: add)  // "Hello Swift World"

문자열 역시 String.append(contentsOf:)는 내부적으로 buffer reserve + 단일 복사로 최적화됩니다.

 

8. 복합 예제: 데이터 병합 파이프라인

struct Log { let id: Int; let message: String }

let logs1 = (0..<10_000).map { Log(id: $0, message: "A\($0)") }
let logs2 = (10_000..<20_000).map { Log(id: $0, message: "B\($0)") }

// 권장
var merged = [Log]()
merged.append(contentsOf: logs1)
merged.append(contentsOf: logs2)

// 비권장
var mergedSlow = [Log]()
for l in logs1 { mergedSlow.append(l) }
for l in logs2 { mergedSlow.append(l) }

위와 같은 구조에서 append(contentsOf:)는 메모리 복사 최소화로 훨씬 빠르고 안정적입니다.

 

9. 언제 반복 append를 써야 하나?

  • 요소별 조건 검사나 변환 후 추가가 필요한 경우만 예외입니다.
var result: [Int] = []
for n in 0..<100 {
    if n.isMultiple(of: 2) {
        result.append(n * 10) // 조건적 추가
    }
}

이 경우 append(contentsOf:)를 직접 쓸 수 없으므로 filter + map + append(contentsOf:) 패턴이 대체됩니다.

var result: [Int] = []
let evenMultiplied = (0..<100).filter { $0.isMultiple(of: 2) }.map { $0 * 10 }
result.append(contentsOf: evenMultiplied)

 

10. 참조 링크

 

11. 결론

  • 여러 요소를 추가해야 한다면 항상 append(contentsOf:) 가 권장됩니다.
    → 내부에서 용량을 계산해 한 번에 복사
  • 반복 append는 조건적 추가에만 사용하세요.
    → 불필요한 capacity 증가와 복사로 성능 저하

요약:

“전체 추가 → append(contentsOf:)”

“조건적 추가 → 반복 append + filter/map 결합”

반응형