본문 바로가기
iOS/Swift

[Swift] borrowing, consuming

by Bokoo14 2026. 3. 15.

Swift 5.9(SE-0377)에서 소유권 제어(Ownership Control) 키워드가 공식 도입되었다.

borrowing, consuming, copy를 이해하면 값 타입의 메모리 흐름을 훨씬 정밀하게 제어할 수 있다.

 

왜 필요한가?

Swift에서 값 타입(struct, enum)을 함수에 전달하면 기본적으로 복사(copy)가 발생한다.

struct Packet {
    var payload: [UInt8] = Array(repeating: 0, count: 65536) // 64KB
}

func validate(_ packet: Packet) -> Bool {
    return packet.payload.first == 0xFF
    // 읽기만 하는데도 64KB를 통째로 복사
}

단순히 읽기만 하는데 매번 64KB를 복사하는 건 낭비다. 이때 borrowing과 consuming이 해결책이 된다.

 

borrowing

함수가 인자를 복사하지 않고 읽기 전용으로 빌려쓴다는 선언이다.

소유권은 호출자(caller)가 그대로 유지하며, 함수 내부에서는 값을 소비하거나 저장할 수 없다.

func validate(_ packet: borrowing Packet) -> Bool {
    return packet.payload.first == 0xFF  // ✅ 읽기 가능
}

제약 사항

var stored: Packet?

func validate(_ packet: borrowing Packet) -> Bool {
    stored = packet         // ❌ 컴파일 에러: 저장 불가
    let copy = packet       // ❌ 컴파일 에러: 암묵적 복사 불가
    return true
}

명시적 복사가 필요하다면

내부에서 꼭 복사가 필요하면 copy 키워드를 명시한다.

func validate(_ packet: borrowing Packet) -> Bool {
    let myCopy = copy packet   // ✅ 명시적 복사 허용
    // myCopy로 작업...
    return true
}

호출 후 원본은 그대로

var packet = Packet()
validate(packet)
print(packet.payload.count)  // ✅ 여전히 사용 가능 — 소유권 유지

 

 

consuming

함수가 인자의 소유권을 완전히 가져간다는 선언이다.

호출자는 함수 호출 이후 해당 변수를 더 이상 사용할 수 없다.

 

borrowing과 consuming의 차이는 도서관 책 대출에 비유하면 직관적이다.

  • borrowing → 열람실에서 읽기: 책을 그 자리에서만 읽고 반납한다. 책은 여전히 도서관 소유
  • consuming → 책 구매: 책의 소유권이 완전히 내게 넘어온다. 도서관엔 더 이상 그 책이 없다.
func read(_ packet: borrowing Packet) { ... }   // 열람만, 원본 유지
func store(_ packet: consuming Packet) { ... }  // 소유권 이전, 원본 소멸
func store(_ packet: consuming Packet) {
    cache = packet   // ✅ 소유권이 있으므로 저장 가능
}

var packet = Packet()
store(packet)
print(packet.payload.count)  // ❌ 컴파일 에러: 소유권이 넘어감

함수 내부에서 할 수 있는 것

소유권을 가져왔으므로 수정, 저장, 반환이 모두 자유롭다.

func transform(_ packet: consuming Packet) -> Packet {
    var p = packet         // ✅ var로 받아 수정 가능
    p.payload.append(0xFF)
    return p               // ✅ 반환도 가능
}

 

consume 연산자

함수 파라미터 외에도, 변수 수준에서 명시적으로 소유권을 이전할 수 있다.

var packet = Packet()

let transferred = consume packet  // packet → transferred로 소유권 이전
print(packet.payload.count)       // ❌ 컴파일 에러
print(transferred.payload.count)  // ✅

 

왜 원본이 사라지는가?

Swift의 값 타입은 기본적으로 변수가 값의 유일한 소유자다.

consuming으로 함수를 호출하는 순간, 컴파일러는 그 변수의 소유권을 함수로 이동(move) 시키고, 원래 변수를 uninitialized 상태로 만든다.

복사가 일어나는 게 아니다. 값 자체가 호출자 쪽에서 사라진다.

[호출 전]  packet ──owns──▶ Packet { payload: [...] }

store(packet) 호출 시점:

[호출 후]  packet ──✖ (uninitialized)
           함수 내부 ──owns──▶ Packet { payload: [...] }

그래서 컴파일러가 호출 이후 packet에 접근하는 코드를 컴파일 타임에 에러로 잡아낸다.

런타임 크래시가 아니라 컴파일 에러라는 점이 중요하다.

 

Swift가 소유권을 정적으로 추적하고 있기 때문이다.

var packet = Packet()
store(packet)
print(packet.payload.count)  // ❌ 컴파일 에러: 'packet' used after consume

 

비교

  기본 (copy) borrowing consuming
복사 발생
호출 후 원본 사용
함수 내 수정·저장
명시적 복사 불필요 copy 키워드 불필요
주요 용도 일반 전달 읽기 전용, 대용량 값 Builder, 소유권 이전
// borrowing: 읽고 돌려줌 → 호출 후 원본 그대로
func validate(_ packet: borrowing Packet) -> Bool {
    return packet.payload.first == 0xFF
}

var packet = Packet()
validate(packet)
print(packet.payload.count)  // ✅ 원본 살아있음


// consuming: 소유권 가져감 → 호출 후 원본 소멸
func store(_ packet: consuming Packet) {
    cache = packet
}

var packet2 = Packet()
store(packet2)
print(packet2.payload.count)  // ❌ 컴파일 에러

 

언제 써야 할까?

borrowing

  • 함수 내에서 읽기만 하고 값을 변경하거나 저장하지 않을 때
  • 크기가 큰 struct를 자주 전달해서 복사 비용이 부담될 때
  • 성능에 민감한 데이터 파이프라인

consuming

  • 값을 전달한 후 원본이 더 이상 필요 없을 때
  • Builder 패턴처럼 단계적으로 값을 변환해 나갈 때
  • 소유권 이전을 명시적으로 코드에 드러내고 싶을 때

그냥 기본(copy)으로 두는 상황

  • 값이 작고 복사 비용이 미미할 때
  • 호출 후에도 원본을 계속 써야 할 때
  • 컴파일러 최적화(COW 등)에 맡겨도 충분할 때

 

요약

borrowing과 consuming로 Swift가 더 명확한 소유권 모델로 나아가고 있다

ARC 오버헤드를 줄이고 개발자가 값의 수명을 코드 레벨에서 표현할 수 있게 해준다.

일반적인 앱 개발에서는 컴파일러 최적화(COW, Copy-on-Write)에 맡겨도 충분하지만, 성능에 민감한 코드나 의도를 명확히 표현해야 하는 상황에서 이 키워드들을 사용할 수 있다.

 

 

[참고]

https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020

 

SE-0377: borrow and take parameter ownership modifiers

Hi Swift community, The review of SE-0377: borrow and take parameter ownership modifiers begins now and runs through November 8, 2022. Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread o

forums.swift.org

https://github.com/swiftlang/swift-evolution/blob/main/proposals/0377-parameter-ownership-modifiers.md

 

swift-evolution/proposals/0377-parameter-ownership-modifiers.md at main · swiftlang/swift-evolution

This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swiftlang/swift-evolution

github.com

 

SE-0366: consume operator to end the lifetime of a variable binding

 

swift-evolution/proposals/0366-move-function.md at main · swiftlang/swift-evolution

This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swiftlang/swift-evolution

github.com