본문 바로가기

iOS

Access Control - Basic to Advanced

728x90

앞의 글에서 접근제어에 대한 .. 첫인사(say ㅎ2)를 나눴다면 이번 글에서는 보다 자세하게 접근제어에 대해 알아보자.

 

접근 제어자

  • 소스파일 및 모듈의 코드 중 일부에 대한 접근을 제한하고자 할 때 사용한다.
  • 접근 제어자를 통해서 코드의 상세 내용을 숨길 수 있고 코드의 불필요한 외부 노출을 명시적으로 제한함으로써 은닉화의 특성을 구현할 수 있다.
    • 외부에서 보거나 접근하면 안되는 코드에 접근하게 된다면 의도하지 않은 오류가 발생할 수 있다.
  • 접근 제어자는 모듈소스 코드 기준으로 접근 수준을 판단하게 된다.
    • 모듈
      • import를 통해서 프로젝트 내에서 사용할 수 있는 형태의 코드 묶음 단위를 말한다.
      • 프로젝트를 하나의 모듈로 볼 수 있으며 프레임워크 하나도 하나의 모듈이라고 생각할 수 있다.
      • open, public 접근 제어자를 통해서 외부에서 프로젝트에 접근할 수 있다.
    • 소스 파일
      • 하나의 프로젝트 내에서 코드를 제어할 경우, internal / fileprivate / private 으로 소스파일에 대한 코드 제어를 할 수 있다. 
  • 접근 제어자의 규칙 중 하나는 바깥의 접근 레벨보다 내부의 접근 레벨이 더 높을 수 없다는 것이다.
    • class가 internal 이라면, 그 내부에서 구현한 메서드의 경우 open / public으로 선언될 수 없다.

 

Access Level (접근 레벨)

모듈/파일 내-외부에 대한 접근을 기반으로 접근 제어의 수준을 나눌 수 있다.

 

키워드 접근 제어 수준
open 개방 접근 수준
public  공개 접근 수준
internal 내부 접근 수준 
fileprivate 파일 내부 접근 수준 
private  비공개 접근 수준 

 

키워드를 하나씩 살펴보자.

 

open

  • 내/외부 모두 접근 가능하다.
    • open으로 선언한 외부 모듈 코드는 내 프로젝트 내에서 사용할 수 있다.
  • 클래스에서만 사용 가능하며 open으로 클래스를 선언할 경우, 다른 모듈에서 해당 클래스를 오버라이딩 할 수 있다.

 

public

  • 내/외부 모두 접근 가능하다.
    • open으로 선언한 외부 모듈의 코드는 내 프로젝트 내에서 사용할 수 있다.
    • public으로 선언한 경우 상속받을 수도 없고 오버라이딩을 할 수도 없다

 

internal

  • 별도로 접근 제어를 선언하지 않은 경우에 default로 설정되는 접근 레벨이다.
  • 같은 모듈 내에서는 internal로 선언한 경우 어떤 파일에서도 코드에 대한 접근이 가능하며 클래스 상속도 받을 수 있다. 

 

fileprivate

  • 하나의 스위프트 파일 내부에서만 접근 가능한 접근 레벨이다.
  • 다른 스위프트 파일에서 fileprivate 로 설정한 파일의 코드를 변경하려고 하거나 호출하려고 할 때 오류가 발생한다.

 

private 

  • private로 설정한 요소가 들어 있는 블록 내에서만 접근이 가능하다.

 

🤔 fileprivate VS private 

이 둘의 차이가 무엇인지 코드를 통해서 살펴보자. (내가 모르겠으니까.)

 

위와 같이 Device라는 구조체를 만들고 그 안에 fileprivate 프로퍼티와 private 프로퍼티를 만들었을 때,

 

fileprivate로 선언한 name의 경우 같은 파일 내라면 접근할 수 있다.

(AppleDevice라는 구조체를 만들고 그 안의 메서드에서 인스턴스 생성 후 name에 접근할 수 있다.) 

 

그러나 private로 선언한 price의 경우 name과 다르게 접근할 수 없는 것을 확인할 수 있다.

Device 를 확장한 뒤 작성한 example 메서드에서는 접근할 수 있지만 같은 코드블럭이 아니라면 접근에 제한이 있다. 

 


여기서부터는 공식문서를 바탕으로 정리한 글이다. 

접근 제어에 대한 정의는 위에서 했으니 몇가지 새롭게 알게된 점이나 .. 내 기준 중요한 점을 위주로 정리하겠습니다 .. 

 

Swift에서는 기본 접근레벨을 제공해 접근 레벨의 처리를 쉽게할 수 있도록 돕는다.

그러므로 단일 타겟의 앱에서는 특별히 접근 레벨을 명시하지 않아도 된다. 

 

모듈과 소스파일

Swift의 접근제어는 모듈과 소스파일을 기반으로 두고 있다.

✔️ 모듈 : 코드를 배포하는 단일 단위, 프레임워크 또는 앱이 모듈 단위로 배포

✔️ 소스파일 : 모듈 안에 있는 소스파일을 의미 (각 소스파일에 여러 특정 타입을 선언하여 사용할 수 있다.)

 

접근레벨 가이드 원칙

Swift에서 접근 레벨은 더 낮은 레벨이 갖고 있는 다른 엔티티를 특정 엔티티에 선언해서 사용할 수 없다.

  • public으로 선언된 변수는 internal / fileprivate / private 타입에서 정의될 수 없다. 
    왜냐하면 그 타입은 public 변수가 사용되는 모든 곳에서 사용할 수 없기 때문이다.
  • 함수는 그 함수의 파라미터 타입 / 리턴 값 타입보다 높은 접근 레벨을 가질 수 없다.
    함수에는 접근 가능하지만 파라미터에 접근이 불가 or 반환 값 타입보다 접근 레벨이 낮아 함수를 사용하는 관련 코드에서 이용할 수 없을 수 있기 때문이다. 

 

기본 접근 레벨

아무런 접근 레벨을 명시하지 않은 경우 internal을 갖게 된다.

 

단일 타겟 앱을 위한 접근 레벨

위에서 말한 것과 같이 단일 타겟 앱에서는 특별히 접근 레벨을 명시할 필요가 없지만 필요에 따라서 fileprivate / private 등을 사용해서 앱 내에서 세부 사항을 숨길 수 있다.

 

프레임워크를 위한 접근 레벨 

프레임워크를 개발한다면, public 혹은 open 으로 지정하여 다른 모듈에서 볼 수 있고 접근 가능하도록 해야 한다.

그러나 만약 프레임워크의 구체적인 구현을 숨기고 싶은 부분이 있다면 그 부분을 inteneral로 선언하면 된다. (보여주고 싶은 부분만 public or open으로 지정한다.) 

 

유닛테스트 타겟을 위한 접근 레벨 

기본적으로 open or public으로 지정한 엔티티만 다른 모듈에서 접근 가능하지만 

유닛테스트틀 하는 경우 모듈을 import 할 때 import 앞에 @testable 속성을 부여하면 해당 모듈을 테스트 가능한 모듈로 컴파일해서 사용할 수 있다. 

 


커스텀 타입

커스텀 클래스에 특정 접근 레벨을 지정할 수 있다.

레벨을 지정하면 해당 클래스 안의 프로퍼티와 리턴 타입의 접근 레벨은 클래스 레벨의 접근 권한만 사용할 수 있다.

 

예를 들어서 클래스를 fileprivate으로 선언하면

그 안의 프로퍼티와 함수 파라미터 그리고 반환 타입 모두 fileprivate 접근 권한을 갖는다.

 

특정 타입의 접근레벨 지정은 그 타입의 멤버(프로퍼티, 메서드 초기자, 서브 스크립트)에 기본접근 레벨을 결정, 영향을 미친다.

 

또 다른 중요한 점은 public으로 지정할 경우이다.

public 타입은 기본적으로 internal 멤버를 갖고 public을 갖고 있지 않는다. 

 

왜냐하면 public API를 만들 때 외부에서 보이지 말아야 할 API가 실수로 노출되는 것을 막기 위해서이다. 그러므로 외부에서 접근할 수 있게 하려면 명시적으로 public 접근 제어자를 작성해야 한다. 

 

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
}

 


함수 타입

함수 타입의 접근 레벨은 함수의 파라미터 타입과 리턴 타입의 접근 레벨 중 최소의 접근 레벨로 계산되어 사용한다.

그래서 그것에 맞는 접근 레벨을 함수 앞에 명시해야 한다.

 

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

위의 코드는 컴파일 시 오류가 난다.

그 이유는 반환 값 중에서 접근 레벨이 private 인 SomePrivateClass가 존재하기 때문이다. 그래서 someFunction() 은 internal 접근 레벨로 선언될 수 없다. 

 

해당 함수를 적절하게 선언하기 위해서는 아래와 같이 함수 앞에 private 접근 레벨을 명시해야 한다.

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

 


열거형 타입

열거형에서 각 case는 enum의 접근 레벨을 따르고 개별적으로 다른 접근 레벨을 지정할 수 없다.

 

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

위의 코드에서 CompassPoint 열거형은 public 접근 레벨을 갖고 있으므로 각 케이스는 모두 public 접근 레벨을 갖는다. 

 


익스텐션

클래스, 구조체, 열거형 등에서 익스텐션으로 새로운 멤버를 추가하면 새로 추가된 것은 기존에 타입이 선언된 접근 레벨과 동일한 레벨을 갖는다. 

 

그리고 추가적으로 명시하여 접근 레벨을 지정할 수도 있다. 

그러나 익스텐션을 프로토콜로 사용하는 경우 명시적인 접근 레벨 변경이 불가능하다.

 

private members in extensions

익스텐션이 클래스, 구조체, 혹은 열거형과 같은 파일에 선언된 경우 다음이 가능하다.

  • 원본 선언에서 private 멤버로 선언한 것을 익스텐션에서 멤버로 접근할 수 있다.
  • 하나의 익스텐션에서 private 멤버로 선언한 것을 같은 파일의 다른 익스텐션에서 접근할 수 있다.
  • 하나의 익스텐션에서 private 멤버로 선언한 것을 원본 선언에서 멤버로 접근할 수 있다

 


타입 별칭 (Type Aliases)

타입 별칭은 별칭을 붙인 타입보다 같거나 낮은 접근 레벨을 갖는다.

예를 들어서 private 타입의 별칭은 private, fileprivate, internal, public, open 타입의 접근 권한을 가질 수 있다. 

 

'iOS' 카테고리의 다른 글

App Sandbox 그리고 Files  (2) 2022.08.27
백업/복구 (+ 백업 리스트 UI 및 Custom Progress View)  (3) 2022.08.26
YPImagePicker  (0) 2022.08.13
0812 Q&A 정리  (0) 2022.08.12
required init VS override init  (0) 2022.08.10