🔥 타입별 접근 제어

788자
9분

사용자 정의 타입

사용자 정의 타입에 대해 명시적인 접근 수준을 지정하려면 타입을 정의하는 시점에 그렇게 해야 합니다. 그러면 새로운 타입은 접근 수준이 허용하는 곳에서 사용될 수 있어요. 예를 들어, file-private 클래스를 정의하면 해당 클래스는 file-private 클래스가 정의된 소스 파일 내에서만 속성의 타입이나 함수 매개변수 또는 반환 타입으로 사용될 수 있죠.

타입의 접근 제어 수준은 또한 해당 타입의 멤버(속성, 메서드, 이니셜라이저 및 서브스크립트)의 기본 접근 수준에도 영향을 미칩니다. 타입의 접근 수준을 private 또는 file private으로 정의하면 해당 멤버의 기본 접근 수준도 private 또는 file private이 됩니다. 반면에 타입의 접근 수준을 internal 또는 public으로 정의하거나 (명시적으로 접근 수준을 지정하지 않고 기본 접근 수준인 internal을 사용하는 경우), 해당 타입의 멤버에 대한 기본 접근 수준은 internal이 되죠.

public class SomePublicClass {                  // 명시적으로 public 클래스
    public var somePublicProperty = 0            // 명시적으로 public 클래스 멤버
    var someInternalProperty = 0                 // 암묵적으로 internal 클래스 멤버
    fileprivate func someFilePrivateMethod() {}  // 명시적으로 file-private 클래스 멤버
    private func somePrivateMethod() {}          // 명시적으로 private 클래스 멤버
}
 
 
class SomeInternalClass {                       // 암묵적으로 internal 클래스
    var someInternalProperty = 0                 // 암묵적으로 internal 클래스 멤버
    fileprivate func someFilePrivateMethod() {}  // 명시적으로 file-private 클래스 멤버
    private func somePrivateMethod() {}          // 명시적으로 private 클래스 멤버
}
 
 
fileprivate class SomeFilePrivateClass {        // 명시적으로 file-private 클래스
    func someFilePrivateMethod() {}              // 암묵적으로 file-private 클래스 멤버
    private func somePrivateMethod() {}          // 명시적으로 private 클래스 멤버
}
 
 
private class SomePrivateClass {                // 명시적으로 private 클래스
    func somePrivateMethod() {}                  // 암묵적으로 private 클래스 멤버
}
swift

위의 예제 코드를 보면, 클래스의 접근 수준에 따라 해당 클래스의 멤버들이 어떤 기본 접근 수준을 갖게 되는지 잘 알 수 있어요. 물론 필요에 따라 개별 멤버에 대해 명시적으로 다른 접근 수준을 지정할 수도 있죠.

튜플 타입

튜플 타입의 접근 수준은 해당 튜플에 사용된 모든 타입 중 가장 제한적인 접근 수준이 됩니다. 예를 들어, internal 접근 수준의 타입과 private 접근 수준의 타입 두 가지로 튜플을 구성하면 해당 복합 튜플 타입의 접근 수준은 private이 되는 거예요.

let internalTuple = (SomeInternalClass(), SomeInternalClass())  // internal 튜플
let privateTuple = (SomeInternalClass(), SomePrivateClass())    // private 튜플
swift

함수 타입

함수 타입의 접근 수준은 함수의 매개변수 타입과 반환 타입 중 가장 제한적인 접근 수준으로 계산됩니다. 함수의 계산된 접근 수준이 컨텍스트의 기본값과 일치하지 않는 경우, 함수 정의의 일부로 접근 수준을 명시적으로 지정해야 해요.

아래 예제는 함수 자체에 대한 특정 접근 수준 수정자를 제공하지 않고 someFunction()이라는 전역 함수를 정의합니다. 이 함수가 "internal"의 기본 접근 수준을 갖게 될 것으로 예상할 수 있지만, 실제로는 그렇지 않아요. 사실 아래와 같이 작성된 someFunction()은 컴파일되지 않습니다:

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 함수 구현부
}
swift

함수의 반환 타입은 위의 Custom Types에서 정의한 두 개의 사용자 정의 클래스로 구성된 튜플 타입이에요. 이 클래스 중 하나는 internal로 정의되고 다른 하나는 private으로 정의되었죠. 따라서 복합 튜플 타입의 전체 접근 수준은 private입니다(튜플을 구성하는 타입의 최소 접근 수준).

함수의 반환 타입이 private이기 때문에 함수 선언이 유효하려면 함수의 전체 접근 수준을 private 수정자로 표시해야 합니다:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 함수 구현부
}
swift

someFunction()의 정의를 public 또는 internal 수정자로 표시하거나 internal의 기본 설정을 사용하는 것은 유효하지 않아요. 왜냐하면 함수의 public 또는 internal 사용자는 함수의 반환 타입에 사용되는 private 클래스에 대한 적절한 접근 권한이 없을 수 있기 때문이죠.

열거형 타입

열거형의 개별 case는 자동으로 해당 열거형과 동일한 접근 수준을 받습니다. 개별 열거형 case에 대해 다른 접근 수준을 지정할 수는 없어요.

아래 예제에서 CompassPoint 열거형은 public의 명시적 접근 수준을 갖습니다. 따라서 열거형 case인 north, south, eastwest도 public 접근 수준을 갖게 되죠:

public enum CompassPoint {
    case north
    case south
    case east
    case west
}
swift

원시 값과 연관 값

열거형 정의에서 원시 값이나 연관 값에 사용되는 타입은 열거형의 접근 수준 이상의 접근 수준을 가져야 합니다. 예를 들어, internal 접근 수준의 열거형에서 private 타입을 원시 값 타입으로 사용할 수 없어요.

internal enum Size: String {  // 열거형은 internal 접근 수준
    case small = "S"  // 원시 값의 타입인 String은 internal 이상의 접근 수준
    case medium = "M"
    case large = "L"
}
swift

중첩 타입

중첩 타입의 접근 수준은 포함하는 타입과 동일하지만, 포함하는 타입이 public인 경우는 예외입니다. public 타입 내에 정의된 중첩 타입은 자동으로 internal 접근 수준을 갖게 되죠. public 타입 내의 중첩 타입을 공개적으로 사용할 수 있게 하려면 해당 중첩 타입을 명시적으로 public으로 선언해야 해요.

public class OuterClass {
    class InnerClass {  // 암묵적으로 internal 접근 수준
        // ...
    }
 
    public class PublicInnerClass {  // 명시적으로 public 접근 수준
        // ...
    }
}
swift

이렇게 Swift의 접근 제어는 사용자 정의 타입과 그 멤버들에도 세밀하게 적용될 수 있답니다. 타입과 멤버의 접근 수준을 적절히 설정하면 코드의 캡슐화와 모듈화를 향상시킬 수 있어요. 불필요하게 세부 구현을 노출하지 않으면서도 외부에서 사용할 수 있는 공용 인터페이스를 명확히 정의할 수 있게 되죠.

이제 여러분도 사용자 정의 타입을 설계할 때 접근 제어를 효과적으로 활용할 수 있게 되었길 바랍니다. 코드의 가독성과 유지보수성을 높이는 데 큰 도움이 될 거예요!