[최종 수정일 : 2018.09.12]

원문 : 애플 개발 문서 Swift 4.2 Language Guide - Access Control

접근 제어(Access Control)

접근 제어(Access control)은 다른 소스파일이나 모듈에 있는 코드에서 여러분의 코드에 접근하는 것을 제한합니다. 이 기능은 코드의 세부 구현을 숨기는게 가능하고 해당 코드를 접근하고 사용할 수 있는 인터페이스 지정해야 합니다.

개별 타입(클래스, 구조체, 열거형) 뿐만 아니라 프로퍼티, 메소드, 초기화, 서브스크립트에 대해서 특정 접근 레벨(level)을 설정할 수 있습니다. 프로토콜은 전역 상수, 변수, 함수처럼 특정 상황(context)으로 제한할 수 있습니다.

다양한 접근 제어 레벨을 제공하는 것외에도, Swift는 일반적인 시나리오에 대한 기본 접근 레벨을 제공함으로써 명시적으로 접근 제어 레벨을 지정해야 하는 필요성을 줄여줍니다. 확실히, 단일 대상(single-target) 앱을 작성하는 경우, 명시적으로 접근 제어 레벨을 지정하는 것이 필요 없을 수 있습니다.

주의
코드의 다양한 측면에서 접근 제어를 적용할 수 있는 것들(프로퍼티, 타입, 함수, 등등)을 간략하게, 아래 섹션에서 있는 요소들(entities)이라고 합니다.

모듈과 소스파일(Modules and Source Files)

Swift의 접근 제어 모델은 모듈과 소스파일의 개념을 기반으로 합니다.

모듈(module)은 단일 코드로 배포됩니다 - 프레임워크 또는 앱은 단일 단위로 만들고(built) 전달되고(shipped), Swift의 import키워드로 다른 모듈로부터 가져올수 있습니다.

Xcode에서 각 빌드 타겟(앱 번들이나 프레임워크)은 Swift에서 구분된 모듈로 취급됩니다(treated). 독립 실행형 프레임워크(여러 앱에서 해당 코드를 캡슐화하고 재사용)로 앱의 코드를 그룹화 하는 경우에 프레임워크에서 정의한 모든 곳에서 앱에 포함(imported)되고 사용되거나 다른 프레임워크에서 사용될때 별도의 모듈이 될것입니다.

소스 파일(source file)은 모듈로 된 하나의 Swift 소스 파일입니다(실제로 앱이나 프레임워크에 있는 단일 파일). 별도의 소스파일에 개별 타입을 정의하는 것이 일반적이지만, 하나의 소스 파일은 여러 타입, 함수, 등등에 대한 정의를 포함할 수 있습니다.

접근 레벨(Access Levels)

Swift는 코드에서 요소들(entities)에 대한 5개의 다른 접근 레벨(access levels)을 제공합니다. 이러한 접근 레벨은 요소(entity)가 정의된 소스 파일과 관련있고, 또한, 소스 파일이 속한 모듈과 관련 있습니다.

  • Open access와 public access는 요소들이 정의된 모듈에 있는 모든 소스파일에서 사용되는게 가능하고, 그것들을 정의하는 모듈을 가져오는 다른 모듈의 소스파일에서도 사용가능합니다. 일반적으로 프레임워크에 public 인터페이스를 지정할때, open과 public 접근을 사용합니다. open과 public 접근 간의 차이점은 아래에 설명되어 있습니다.
  • internal access은 요소들이 그것들을 정의하는 모듈에 있는 모든 소스파일에서 사용하는것이 가능하지만, 모듈 바깥쪽의 소스파일에서는 그렇지 않습니다. 일반적으로 앱이나 플레임워크의 내부 구조를 정의할때 internal 접근을 사용합니다.
  • File-private access는 자체적으로 정의하는 소스파일에서만 요소를 사용할 수 있도록 제한합니다. file-private 접근은 세부 사항이 전체 파일에서 사용될때, 함수의 특정 부분의 상세 구현을 숨겨줍니다.
  • Private access는 선언된 영역(enclosing)에서만 요소를 사용할 수 있도록 제한하고, 동일한 파일에 있는 해당 선언의 확장에도 적용됩니다. private 접근은 하나의 선언에서 세부 사항이 사용할때 함수의 특정 부분의 상세 구현을 숨기기 위해 사용합니다.

Open 접근이 가장 높은(가장 적게 제한하는) 접근 레벨이고, private 접근은 가장 낮은(가장 많이 제한하는) 접근 레벨입니다.

Open 접근은 클래스와 클래스 멤버에서만 적용하고, 다음과 같이 public 접근과 다릅니다.

  • public 접근이나 더 제한적인 접근레벨의 클래스는 모듈이 정의된 곳에서만 하위클래스화(subclassed) 할 수 있습니다.
  • public 멤버나 더 제한적인 접근 레벨의 클래스 멤버는, 모듈이 정의된 곳에서만 하위클래스에 의해 오버라이드(overridden) 될 수 있습니다.
  • open클래스는 모듈이 정의된 곳과 정의된 모듈을 가져오는 모듈에서 하위클래스화(subclassed) 될수 있습니다.
  • open 클래스 멤버는 모듈이 정의된 곳과 정의된 모듈을 가져오는 모듈에서 하위클래스에 의해 오버라이드(overridden) 될수 있습니다.

클래스를 명시적으로 open으로 표시하면 다른모듈에서 그 클래스를 상위클래스로 사용하는 코드의 영향을 고려하고, 클래스의 코드가 적절하게 설계되었는지를 나타냅니다.

접근 레벨의 가이드 원칙(Guiding Principle of Access Levels)

Swift에서 접근 레벨은 전반적인으로 다음 가이드 원칙을 따릅니다: 요소(entity)는 더 낮은(더 제한적인) 접근 레벨을 가지는 다른 요소(entity)의 조건으로 정의될 수 없습니다.“

예를 들어:
public 변수는 public변수로 사용되는 곳 어디에서든 해당 타입을 사용하지 못할 수가 있기 때문에, internal, file-private, private타입으로 정의될 수 없습니다. 
* 함수는 해당 구성 타입이 주변 코드에서 사용할수 없는 상황에서 사용될수 있기 때문에, 함수는 매개변수 타입과 반환타입보다 더 높은 접근 레벨을 가질수 없습니다.

다른 언어적인 측면에서 가이드 원칙(guiding principle)의 세부적인 의미는 아래에 자세히 설명되어 있습니다.

기본 접근 레벨(Default Access Levels)

코드에 있는 모든 요소들(entities)은 명시적으로 접근 레벨을 지정하지 않는 경우에, 내부적으로 기본 접근 레벨을 가지고 있습니다. 결과적으로, 대부분의 경우 코드에서 명시적인 접근 레벨을 지정하지 않아도 됩니다.

단일 대상 앱에 대한 접근 레벨(Access Levels for Single-Target Apps)

프레임워크(framework)를 개발할때, 프레임 워크를 open또는 public으로 공용영역(public-facing) 인터페이스로 표시해서 프레임워크를 가져오는 앱처럼, 다른 모듈에서 보여지고 접근할 수 있도록 합니다. 공용영역(public-facing) 인터페이스는 프레임워크에 대한 API(application programming interface) 입니다.

주의
프레임워크의 모든 internal 상세 구현은 여전히 internal 기본 접근 레벨로 사용할수 있거나, 프레임워크듸 내부 코드를 다른 부분으로부터 숨기기 위해 private 또는 file private로 표시 될 수 있습니다. 프레임워크의 API가 되게 하는 경우에만 요소를 open 또는 public으로 표시하는 것이 필요합니다.

단위 테스트 대상에 대한 접근 레벨(Access Levels for Unit Test Targets)

단위 테스트 대상 앱을 작성할때, 앱의 코드는 테스트하기 위해 모듈을 사용할수 있게 해줘야 합니다.기본적으로, open이나 public으로 표시된 요소들만 다른 모듈에 접근가능합니다. 하지만, 단위 테스트 대상은 제품(product) 모듈에 대한 가져오기 선언을 @testable 속성으로 표시하고 테스트 가능한 제품 모듈을 컴파일 하는 경우에, 모든 internal 요소에 접근할 수 있습니다.

접근 제어 문법(Access Control Syntax)

open, public, internal, fileprivate, private 수식어 중의 하나를 요소(entity)의 앞에 배치해서 요소(entity)에 대한 접근 레벨을 정의합니다.

public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFilePrivateClass {}
private class SomePrivateClass {}

public var somePublicVariable = 0
internal let someInternalConstant = 0
fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}

기본 접근 레벨(Default Access Levels)에서 설명된 것처럼, 별도의 규정이 없으면, 기본 접근 레벨은 internal이 됩니다. SomeInternalClass와 someInternalConstant는 명시적인 접근레벨 수식어가 없고, 여전히 internal 접근레벨을 가지는 것을 의미합니다.

class SomeInternalClass {}              // implicitly internal
let someInternalConstant = 0            // implicitly internal

사용자정의 타입(Custom Types)

사용자정의 타입에 대한 명시적인 접근 레벨을 지정하길 원하는 경우, 타입을 정의하는 시점에 해야 합니다. 새로운 타입은 접근 레벨이 허용되는 모든 곳에서 사용될 수 있습니다. 예를들어, file-private클래스로 정의된 경우에, 그 클래스는 file-private클래스가 정의된 소스파일 내에서 프로퍼티의 타입 또는 함수 매개변수나 반환타입 처럼 사용될 수 있습니다.

또한 타입의 접근 레벨은 타입의 멤버(members)(프로퍼티, 메소드, 초기화, 서브스크립트)의 기본 접근레벨에 영향을 줍니다. 타입의 접근 레벨을 private또는 file private로 정의하는 경우, 멤버의 기본 접근 레벨은 private또는 file private가 될것 입니다. 타입의 접근 레벨을 internal 또는 public(또는 명시적인 접근레벨 지정하지 않고 internal 기본 접근 레벨이 사용)으로 정의하는 경우, 타입의 멤버의 기본 접근 레벨은 internal이 될것입니다.

중요
public 타입은 기본적으로 public 멤버가 아닌 internal멤버를 가지고 있습니다. 타입 멤버가 public이 되는것을 원하는 경우, 반드시 명시적으로 표시해줘야 합니다. 이러한 요구사항은 타입에 대한 공용영역(public-facing) API가 공개되도록 선택하고, 실수로 internal타입을 public API로 작업하는 것을 막아줍니다.

public class SomePublicClass {                  // explicitly public class
    public var somePublicProperty = 0            // explicitly public class member
    var someInternalProperty = 0                 // implicitly internal class member
    fileprivate func someFilePrivateMethod() {}  // explicitly file-private class member
    private func somePrivateMethod() {}          // explicitly private class member
}

class SomeInternalClass {                       // implicitly internal class
    var someInternalProperty = 0                 // implicitly internal class member
    fileprivate func someFilePrivateMethod() {}  // explicitly file-private class member
    private func somePrivateMethod() {}          // explicitly private class member
}

fileprivate class SomeFilePrivateClass {        // explicitly file-private class
    func someFilePrivateMethod() {}              // implicitly file-private class member
    private func somePrivateMethod() {}          // explicitly private class member
}

private class SomePrivateClass {                // explicitly private class
    func somePrivateMethod() {}                  // implicitly private class member
}

튜플 타입(Tuple Types)

튜플 타입에 대한 접근 레벨은 튜플에서 사용되는 모든 타입 중에 가장 제한적인 접근 레벨입니다. 예를들어, 두가지 다른 타입으로 구성된 튜플에서, 하나는 internal 접근이고 하나는 private 접근인 경우에, 복합적인 튜플에 대한 접근 레벨은 private가 될 것입니다.

주의
튜플 타입은 클래스, 구조체, 열거형과 함수 방식에서 독립적으로 정의할 수 없습니다. 튜플 타입의 접근 레벨은 튜플 타입이 사용되고 명시적으로 지정될수 없을때, 자동으로 추론됩니다.

함수 타입(Function types)

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

아래 예제는 함수 자체에 특정 접근 레벨 수식어를 제공하지 않고 전역 함수 someFunction()을 정의합니다. 함수가 "internal" 기본 접근 레벨이 될것이라 예상할지 모르지만, 이 경우에는 그렇지 않습니다. 사실상 someFunction()은 아래와 같이 컴파일되지 않을 것입니다.

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // function implementation goes here
}

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

함수의 반환 타입이 private이기 때문에, 함수 선언이 유효하도록 함수의 전체 접근 레벨은 반드시 private수정자로 표시해야 합니다.

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // function implementation goes here
}

someFunction()의 정의를 public이나 internal 수식어 또는 기본 설정되는 internal을 사용해서 표시하는 것은 함수의 public이나 internal 사용자가 함수의 반환 타입에 사용되는 private클래스에 대한 적절한 접근을 가지고 있지 않기 때문에, 유효하지 않습니다.

열거형 타입(Enumeration Types)

열거형의 개별 케이스(cases)는 자동으로 열거형과 같은 레벨이 됩니다. 개개의 열거형 케이스(cases)에 대해 다른 접근 레벨을 지정할 수 없습니다.

아래 예제에서, CompassPoint 열거형은 public 명시적인 접근 레벨을 가집니다. 따라서 north, south, east, west 열거형 케이스(cases)는public 접근 레벨을 가집니다.

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

원시 값과 연관된 값(Raw Values and Associated Values)

열거형 정의에서 원시 값(raw values)이나 연관된 값(associated values)에 사용되는 타입은 반드시 열거형의 접근 레벨 보다 높은 접근 레벨을 가집니다. 예를 들어, internal접근 레벨로 된 열거형의 원시값(raw-value) 타입을 private타입을 사용할 수 없습니다.

중첩된 타입(Nested Types)

private 타입으로 정의된 중첩된 타입(nested types)은 자동으로 private 접근 레벨을 가집니다. file-private 타입으로 정의된 중첩된 타입은 자동으로 fileprivate접근 레벨을 가집니다. public 타입 또는 internal타입으로 정의된 중첩된 타입은 자동으로 internal접근 레벨을 가집니다. 공용으로 사용할수 있도록 public 타입의 중첩된 타입을 원하는 경우에, 반드시 명시적으로 중첩된 타입을 public으로 선언해야 합니다.

하위클래스 만들기(Subclassing)

현재 접근 컨텍스트(context)에서 접근할 수 있는 모든 클래스를 하위클래스로(subclass) 만들 수 있습니다. 하위 클래스는 상위클래스보다 더 높은 접근 레벨을 가질수 없습니다. - 예를 들어, internal 상위클래스의 하위클래스를 public로 작성할 수 없습니다.

추가적으로, 특정 접근 컨텍스트(context)에서 볼수 있는, 모든 클래스 멤버(메소드, 프로퍼티, 초기화, 서브스크립트)를 오버라이드(override)할 수 있습니다.

오버라이드(override)는 상속된 클래스 멤버를 상위클래스 버젼 보다 더 접근성 있게 만들 수 있습니다. 아래 예제에서, 클래스 A는 file-private메소드 someMethod()를 가진 public 클래스입니다. 클래스 B는 "internal"로 접근 레벨을 줄인 A의 하위 클래스입니다. 그럼에도 불구하고, 클래스 B는 someMethod()의 원래 구현보다 더 높은(higher)"internal"접근 레벨로 된 someMethod()의 오버라이드(override)를 제공합니다.

public class A {
    fileprivate func someMethod() {}
}

internal class B: A {
    override internal func someMethod() {}
}

심지어는 허용된 접근 레벨 컨텍스트(context)에서 상위클래스의 멤버를 가져오는 한, 하위클래스 멤버가 하위클래스 멤버보다 더 낮은 접근 권한을 가진 상위클래스 멤버를 호출하는 것이 유효합니다. (이는 file-private 멤버를 호출하는 상위클래스와 같은 파일에서에 있거나, internal 멤버를 호출하는 상위클래스와 같은 모듈에 있는 경우)

public class A {
    fileprivate func someMethod() {}
}

internal class B: A {
    override internal func someMethod() {
        super.someMethod()
    }
}

상위 클래스 A와 하위 클래스 B는 동일한 소스파일안에서 정의되기 때문에, B의 someMethod() 구현에서 super.someMethod()를 호출하는 것인 유효합니다.

상수, 변수, 프로퍼티, 서브스크립트(Constants, Variables, Properties, and Subscripts)

상수, 변수, 프로퍼티는 타입보다 더 public일 수는 없습니다. 예를 들어, private 타입으로 public프로퍼티를 작성하는 것은 유효하지 않습니다. 비슷하게, 서브스크립트는 인덱스 타입이나 반환 타입중 하나보다 더 public이 될 수 없습니다.

상수, 변수, 프로퍼티, 서브스크립트가 private 타입을 사용하는 경우, 상수, 변수, 프로퍼티, 서브스크립트는 반드시 private로 표시되어야 합니다.

private var privateInstance = SomePrivateClass()

Getters와 Setters(Getters and Setters)

상수, 변수, 프로퍼티, 서브스크립트에 대한 getters와 setters는 상수, 변수, 프로퍼티, 서브스크립트가 속해있던 것처럼, 자동으로 동일한 접근 레벨이 됩니다.

변수, 프로퍼티, 서브스크립트의 읽기-쓰기(read-write) 범위을 제한하기 위해, setter에 해당 getter보다도 더 낮은(lower) 접근 레벨을 줄수가 있습니다. var이나 subscript 앞에 fileprivate(set), private(set), internal(set)을 작성해서 더 낮은 접근 레벨을 할당합니다.

주의
이 규칙은 저장 프로퍼티 뿐만 아니라 계산 프로퍼티에도 적용합니다. 심지어는 저장 프로퍼티에 대해 명시적인 getter와 setter를 작성하지 않아도, Swift는 여전히 저장 프로퍼티의 백업 저장소에 접근을 제공하는 암시적인 getter와 setter를 통합합니다. fileprivate(set), private(set), internal(set)은 게산 프로퍼티에서 명시적인 setter를 설정하는 것과 정확하게 같은 방법으로, 함성된 setter의 접근 레벨을 변경하기 위해 사용합니다.

아래 예제는 문자열 프로퍼티가 수정되는 회수를 기록하는 구조체 TrackedString를 정의 합니다.

struct TrackedString {
    private(set) var numberOfEdits = 0
    var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
}

TrackedString 구조체는 초기 값 ""(빈 문자열)인 문자열 저장 프로퍼티 value정의합니다. 또한, 구조체는 value가 수정되는 회수를 기록하는데 사용하는 정수형 저장 프로퍼티 numberOfEdits를 정의합니다. 수정되는 것을 기록하는 것은 value 프로퍼티의 didSet프로퍼티 옵져버로 구현되어 있으며, value프로퍼티가 새 값이 설정될때마다 numberOfEdits 증가시킵니다.

TrackedString구조체와 value프로퍼티는 명시적인 접근 레벨 수식어를 제공하지 않고, 둘 다 기본 접근 레벨이 internal이 됩니다. 하지만, numberOfEdits 프로퍼티에 대한 접근 레벨은 프로퍼티의 getter가 여전히 기본 접근 레벨 internel을 가지는 것을 나타내기 위해private(set)수정자로 표시되지만, 그 프로피트는 TrackedString구조체의 코드에서만 설정할 수 있습니다. TrackedString은 numberOfEdits프로퍼티를 내부적으로 수정하는게 가능하지만, 구조체의 정의 바깥쪽에서 사용될때 , 읽기 전용(read-only) 프로퍼티가 되도록 표시합니다.

TrackedString인스턴스를 생성하고 몇차례 문자열 값을 수정하는 경우, 수정한 횟수와 일치하도록 numberOfEdits프로퍼티 값을 업데이트 하는 것을 볼수 있습니다.

var stringToEdit = TrackedString()
stringToEdit.value = "This string will be tracked."
stringToEdit.value += " This edit will increment numberOfEdits."
stringToEdit.value += " So will this one."
print("The number of edits is \(stringToEdit.numberOfEdits)")
// Prints "The number of edits is 3"

비록 다른 소스파일에서 numberOfEdits프로퍼티의 현재 값을 조회할 수 있지만, 다른 소스파일의 프로퍼티를 수정(modify) 할 수는 없습니다. 이 제한사항은 기능적인 측면에서 편리하게 접근을 제공하는면서, TrackedString 편집을 추적하는 기능의 상세 구현을 보호합니다.

필요한 경우에 getter와 setter에 대해 명시적으로 접근 레벨을 할당할 수 있는 것을 주의합니다. 아래 예제는 명시적으로 public 접근 레벨로 정의된 TrackedString구조체 버젼을 보여줍니다. 이 구조체의 멤버들은 기본적으로 internal 접근 레벨을 가집니다. public과 private(set) 접근 레벨 수식어를 결합해서 구조체의 numberOfEdits 프로퍼티를 getter publicsetter private로 만들수 있습니다.

public struct TrackedString {
    public private(set) var numberOfEdits = 0
    public var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
    public init() {}
}

초기화(Initializers)

사용자정의 초기화는 초기화하는 타입보다 더 낮거나 같은 접근 레벨을 할당할 수 있습니다. 유일한 예외는 필수 초기화(required initializers)입니다(필수 초기화(Required Initializers)에서 정의된것 처럼). 필수 초기화는 반드시 속해있는 클래스와 같은 접근 레벨을 가집니다.

함수와 메소드 매개변수 처럼, 타입의 초기화 매개변수는 초기화의 자체 접근 레벨보다 더 private 할수 없습니다.

기본 초기화(Default Initializers)

기본 초기화(Default Initializers)에서 설명된것 처럼, Swift는 자동으로 모든 구조체에 대해 인자 없이 기본 초기화(default initializer)를 제공하거나 기본 클래스에서 모든 프로퍼티에 기본 값을 제공하고, 자체적으로 초기화를 제공하지 않습니다.

타입이 publc으로 정의되지 않는한, 기본 초기화는 타입의 초기화와 같은 접근 레벨을 가집니다. 타입이 public으로 정의된 경우에, 기본 초기화는 internal로 구성됩니다. 다른 모듈에서 사용될때 인자 없는 초기화로 초기화하기위해 public타입을 원하는 경우에, 타입 정의에서 명시적으로 public으로 된 인자없는 초기화를 제공해야 합니다.

구조체 타입에 대한 기본 멤버단위 초기화(Default Memberwise Initializers for Structure Types)

구조체의 모든 저장 프로퍼티들이 private인 경우에, 구조체 타입에서 기본 멤버단위 초기화는 private로 구성됩니다. 비슷하게, 구조체의 모든 저장 프로퍼티가 file private인 경우에, 초기화는 file private입니다. 그렇지 않으면, 그 초기화는 internal 접근 레벨을 가집니다.

위의 기본 초기화 처럼, 다른 모듈에서 사용될때, 멤버단위 초기화로 public 구조체 타입을 초기화 하기 위해, 타입의 정의에서 반드시 public 멤버단위 초기화를 제공해야 합니다.

프로토콜(Protocols)

프로토콜 타입에 명시적인 접근 레벨을 할당하길 원하는 경우에, 프로토콜을 정의하는 시점에 할 수 있습니다. 특정 접근 컨텍스트(context)에서만 채택할수 있는 프로토콜을 만드는게 가능합니다.

프로토콜 정의에서 각 요구사항에 대한 접근 레벨은 자동으로 프로토콜과 같은 접근 레벨로 설정합니다. 프로토콜 요구사항을 지원하는 프로토콜과 다른 접근 레벨을 설정 할 수 없습니다. 이렇게 하면 프로토콜의 모든 요구사항들이 프로토콜을 채택한 모든 타입에서 볼수 있습니다.

주의
public프로토콜 정의하는 경우, 프로토콜의 요구사항은 이러한 요구사항이 구현될때 public 접근 레벨이 되야 합니다. 이 동작은 다른 타입과는 다르며, public 타입 정의는 해당 타입의 멤버에 대해서 internal 접근 레벨을 의미합니다.

프로토콜 상속(Protocol Inheritance)

기존 프로토콜을 상속받아 새로운 프로토콜을 정의하는 경우, 새로운 프로토콜은 상속받은 프로토콜과 거의 같은 접근 레벨을 가질수 있습니다. 예를 들어, internal프로토콜을 상속받아 public 프로토콜로 작성할 수 없습니다.

프로토콜 적합성(Protocol Conformance)

타입은 타입 자체보다 낮은 접근 레벨을 가지는 프로토콜을 준수(conform)할 수 있습니다. 예를들어, 다른 모듈에서 사용할수 있도록 public 타입으로 정의 할 수 있지만, internal 프로토토콜을 준수하는 internal 프로토콜을 정의하는 모듈에서만 사용될 수 있습니다.

타입이 특정 프로토콜을 준수하는 상황(context)은 타입의 접근 레벨과 프로토콜의 접근 레벨 중 최소 값이 됩니다. 타입이 public인 경우에, 프로토콜이 internal을 준수하지만, 해당 프로토콜을 준수하는 타입 또한 internal입니다.

프로토콜을 준수하는 타입을 작성하거나 확장할때, 타입의 각 프로토콜 요구사항 구현은 적어도 프로토콜을 준수하는 타입과 같은 접근 레벨인 것을 보장해야 합니다. 예를 들어, public타입이 internal 프로토콜을 준수하는 경우에, 타입의 각 프로토콜 요구사항 구현은 적어도 "internal이 되야 합니다.

주의
Swift에서는 Objective-C 처럼, 프로토콜 적합성(conformance)은 전역(global)적입니다. - 같은 프로그램에서 타입이 2가지 다른 방법으로 한 프로토콜을 준수하는 것은 불가능 합니다.

확장(Extensions)

클래스, 구조체, 열거형을 사용할수 있는 모든 접근 상황(context)에서, 클래스, 구조체, 열거형을 확장할 수 있습니다. 확장에 추가된 모든 타입 멤버는 원래 타입에서 선언된 타입 멤버와 같은 기본 접근 수준을 가집니다. public이나 internal 타입을 확장하는 경우, 새로 추가하는 타입의 모든 멤버는 internal 기본 접근 레벨을 가집니다. file-private 타입을 확장하는 경우, 새로 추가하는 타입의 모든 멤버는 file private 기본 접근 레벨을 가집니다. private 타입을 확장하는 경우, 새로 추가하는 타입의 모든 멤버는 private 기본 접근 레벨을 가집니다.

또는, 확장에서 정의된 모든 멤버에 대해 새로운 기본 접근 레벨을 설정하기 위해 명시적인 접근 레벨 수식어(예를들어 private extension)로 확장을 표시할 수 있습니다. 새로운 기본값은 각각 타입 멤버에 대해 확장에서 오버라이드(overridden) 할 수 있습니다.

확장에서 프로토콜을 준수하도록 추가하는 경우에, 확장에 대한 명시적인 접근레벨(access-level) 수정자를 제공할 수 없습니다. 대신에, 확장에서 각 프로토콜 요구사항 구현에 대한 기본 접근레벨을 제공하기 위해서 프로토콜의 자체 접근 레벨이 사용됩니다.

확장에서의 Private 멤버(Private Members in Extensions)

확장되는 클래스, 구조체, 열거형과 같은 파일에 있는 확장은, 확장하는 코드가 원래 타입의 선언으로 작성된 것처럼 동작합니다. 결과적으로, 다음을 할 수 있습니다.

  • 원래 선언에서 private 멤버를 선언하고, 같은 파일에 있는 확장에서 멤버를 접근할 수 있습니다.
  • 하나의 확장에서 private 멤버를 선언하고, 같은 파일에 있는 또다른 확장에서 멤버를 접근할 수 있습니다.
  • 확장에서 private 멤버를 선언하고, 같은 파일에 있는 원래 선언에 있는 멤버를 접근할 수 있습니다.

이러한 동작은 타입에 private 요소들이 있는지 여부에 대한 코드를 구성하는 것과 같은 방법으로 확장을 사용할 수 있는 것을 의미합니다. 예를들어, 다음과 같이 간단한 프로토톨을 사용합니다.

protocol SomeProtocol {
    func doSomething()
}
```. 

다음과 같이 프로토콜 적합성(conformance)을 추가하기 위해, 확장을 사용할 수 있습니다.

struct SomeStruct {
private var privateVariable = 12
}

extension SomeStruct: SomeProtocol {
func doSomething() {
print(privateVariable)
}
}
”`

제네릭(Generics)

제네릭 타입이나 제네릭 타입에 대한 접근 레벨은 제네릭 타입이나 함수 자체의 접근레벨과 타입 매개변수에 있는 모든 타입 제약사항의 접근레벨 중에 최소값입니다.

타입 별칭(Type Aliases)

정의한 모든 타입 별칭은 접근 제어를 위해 별개의 타입으로 처리됩니다. 타입 별칭은 타입의 접근레벨보다 낮거나 같은 접근레벨을 가질 수 있습니다. 예를들어, private 타입 별칭은 private, file-private, internal, public, open타입이 될수 있지만, public 타입 별칭은 internal, file-private, private 타입이 될수 없습니다.

주의
이 규칙은 프로토콜 적합성을 만족시키는데 사용되는 연관된 타입에 대해서도 적용됩니다.


'Swift > Language Guide' 카테고리의 다른 글

Advanced Operators  (0) 2018.09.18
Access Control  (0) 2018.09.18
Memory Safety  (0) 2018.09.18
Automatic Reference Counting  (0) 2018.09.18
Generics  (0) 2018.09.18
Protocols  (0) 2018.09.18
Posted by 까칠코더