🔥 Extensions

513자
7분

Swift에서는 기존에 정의된 클래스, 구조체, 열거형 등의 타입을 확장하여 새로운 기능을 추가할 수 있습니다. 이를 Extension이라고 하는데요. Extension을 사용하면 기존 타입의 소스 코드에 접근하지 않고도 새로운 기능을 추가할 수 있어 매우 유용하답니다.

Extension을 정의할 때는 확장하려는 타입이 정의된 접근 수준과 같거나 더 낮은 접근 수준에서 정의해야 해요. 예를 들어, public 타입을 확장할 때는 public 또는 internal 접근 수준에서 확장할 수 있고, internal 타입은 internal 또는 그 이하의 접근 수준에서 확장할 수 있죠.

Extension에서 추가한 멤버의 기본 접근 수준은 Extension이 정의된 접근 수준을 따르게 됩니다. 예를 들어, public 타입을 internal 접근 수준에서 확장하면 추가된 멤버의 기본 접근 수준은 internal이 되는 거예요.

하지만 Extension에 명시적으로 접근 수준을 지정하면 그 접근 수준이 추가된 멤버의 기본 접근 수준으로 설정됩니다. 아래 코드를 볼까요?

public struct SomeType {
    // 구조체 정의...
}
 
private extension SomeType {
    func somePrivateMethod() {
        // private 메서드 구현...
    }
}
swift

위 코드에서 SomeType 구조체는 public으로 선언되었지만, Extension에는 private 접근 수준이 지정되었어요. 따라서 Extension에 추가된 somePrivateMethod() 메서드는 SomeType 내부에서만 접근 가능한 private 메서드가 되는 거랍니다.

한편, Extension을 사용해서 프로토콜 준수성을 추가할 때는 Extension에 명시적인 접근 수준을 지정할 수 없어요. 대신 프로토콜 자체의 접근 수준이 Extension에서 구현한 프로토콜 요구 사항의 기본 접근 수준으로 사용된답니다. 예를 들어 볼게요.

// internal 접근 수준의 프로토콜 선언
internal protocol SomeProtocol {
    func doSomething()
}
 
// public 접근 수준의 구조체 선언
public struct SomeStruct {
    private var privateVariable = 12
}
 
// SomeProtocol 준수성 추가
extension SomeStruct: SomeProtocol {
    func doSomething() {
        print(privateVariable)
    }
}
swift

위 코드에서 SomeProtocol은 internal 접근 수준으로 선언되었어요. 그리고 SomeStruct 구조체는 public으로 선언되었죠.

SomeStruct를 확장하는 Extension에서는 SomeProtocol 프로토콜을 준수하도록 했습니다. 이때 Extension에는 명시적인 접근 수준을 지정하지 않았어요.

Extension에서 구현한 doSomething() 메서드는 SomeProtocol에서 요구하는 메서드예요. 이 메서드의 접근 수준은 프로토콜의 접근 수준인 internal을 따르게 됩니다.

즉, SomeStruct가 public으로 선언되었더라도 SomeProtocol을 준수하기 위해 구현한 doSomething() 메서드는 internal 접근 수준을 갖게 되는 거죠.

이렇게 프로토콜 준수성을 추가할 때는 프로토콜의 접근 수준이 준수 타입의 접근 수준보다 우선시됩니다.

Private 멤버와 Extension

같은 파일 내에서 클래스, 구조체, 열거형을 확장하는 Extension은 마치 원래 타입의 선언에 포함된 것처럼 동작해요. 그래서 다음과 같은 일들이 가능하죠.

  • 원래 타입에 private 멤버를 선언하고, 같은 파일의 Extension에서 그 멤버에 접근할 수 있어요.
  • 한 Extension에 private 멤버를 선언하고, 같은 파일의 다른 Extension에서 그 멤버에 접근할 수 있죠.
  • Extension에 private 멤버를 선언하고, 같은 파일의 원래 타입 선언에서 그 멤버에 접근할 수도 있답니다.

이런 특성 덕분에 Extension을 사용해서 코드를 체계적으로 구성할 수 있게 되는 거예요. 타입에 private 멤버가 있는지 여부와 상관없이 말이죠.

예를 들어 다음과 같은 간단한 프로토콜이 있다고 해 보겠습니다.

protocol SomeProtocol {
    func doSomething()
}
swift

Extension을 사용해서 이 프로토콜을 준수하도록 만들 수 있어요.

struct SomeStruct {
    private var privateVariable = 12
}
 
extension SomeStruct: SomeProtocol {
    func doSomething() {
        print(privateVariable)
    }
}
swift

SomeStruct 구조체는 privateVariable이라는 private 저장 속성을 가지고 있죠. 그리고 Extension을 사용해서 SomeProtocol 프로토콜을 준수하도록 했어요. Extension에서 doSomething() 메서드를 구현하면서 privateVariable에 접근하고 있는 걸 볼 수 있답니다. Extension이 원래 타입 선언과 같은 파일에 있기 때문에 privateVariable에 접근하는 게 가능한 거죠.

이렇게 Extension을 활용하면 코드를 깔끔하게 분리하면서도 타입의 기능을 확장할 수 있게 됩니다. 접근 제어와 함께 사용하면 코드의 캡슐화와 모듈화를 향상시킬 수 있어 매우 강력한 도구가 된답니다!