2. 접근 제한자 (Access Modifier)

SteadySlower·2023년 10월 22일
0

iOS Development

목록 보기
35/38

안녕하세요. 이번 포스팅에서는 접근 제한자 (Access Modifier)에 대해서 알아봅니다.

접근 제한 (Access Control)

접근 제한의 정의는 “다른 소스파일 혹은 모듈에서 현재 코드에 대한 엑세스를 제한”하는 것입니다. 즉 현재의 모듈 안에서의 코드는 기본적으로 현재 모듈에서만 접근할 수 있습니다.

여기서 소스파일은 .swift로 정의되는 파일 하나를 의미하고요. 모듈은 import “모듈명”으로 불러올 수 있는 하나의 라이브러리, 프레임워크 혹은 애플리케이션을 의미합니다.

우리가 만드는 패키지는 라이브러리 혹은 프레임워크라고 볼 수 있는데요. 여기서 접근 제한의 역할은 구체적인 구현 사항은 숨기고 외부에서 액세스할 수 있는 인터페이스만을 지정할 수 있도록 하는 것입니다.

은닉화

접근 제한자를 쓰는 이유는 기본적으로 “은닉화”를 하기 위함입니다. 은닉화란 객체 (혹은 모듈) 내부의 작동 방식을 숨기고 직접적으로 데이터를 수정, 조작할 수 없도록 하는 것입니다. 그리고 사용자들이 필요한 정보만을 public한 인터페이스를 통해서 제공해주는 것이죠.

하나의 모듈이 하나의 어플리케이션이라고 생각해봅시다. 예를 들어 카톡이라고 해보죠. 사용자들은 메시지를 보내고 메시지를 받습니다. 아주 단순한 동장 2가지로 메시지 앱이 작동하죠.

하지만 앱에서는 사용자에게 입력을 받고 서버에 보내고 서버에서 데이터를 받고 그 데이터를 파싱해서 사용자에게 보여주는 훨씬 복잡한 과정을 거칩니다.

하지만 사용자 입장에서 이 모든 원리를 알아야만 카톡을 사용할 수 있다면 너무 복잡하겠죠? 따라서 카톡앱은 이 모든 상세 과정을 숨기고 메시지 보내기, 메시지 받기라는 단순한 2가지의 인터페이스만 제공합니다.

우리가 만들려고 하는 모듈 (라이브러리)도 동일합니다. 내부가 어떻게 돌아가는지 지나치게 구체적으로 알아야 한다면 모듈을 사용하는 개발자들이 차라리 직접 구현하는 것이 낫다고 생각할 수도 있겠죠? 따라서 접근 제한자를 통해서 사용자에게 반드시 필요한 인터페이스만 공개하고 나머지는 숨깁니다.

접근 제한자 (Access Modifier)

Swift에서 접근 제한자의 종류는 5가지 존재합니다. 접근 제한자는 전역에 선언된 변수 (혹은 상수), 특정 타입 (클래스, 구조체, 열거형) 외에도 타입에 소속된 property, method, init에도 선언이 가능합니다.

참고로 protocol에도 접근 제한자를 선언할 수 있는데요. 타입과는 다르게 protocol 안에 있는 property, method에는 별도의 접근 제한자를 선언할 수는 없습니다.

이제 접근 제한자를 하나하나 알아보겠습니다.

open

가장 낮은 수준의 은닉을 가진 접근 제한자입니다. 그리고 open의 경우 class와 class에 소속된 멤버들 (property와 method)에게만 적용할 수 있습니다. open이 클래스에만 적용할 수 있는 이유는 “상속”에 관한 정의를 하는 접근 제한자이기 때문입니다.

open으로 정의된 클래스는 “외부에서” 상속하여 다른 subclass를 만들 수 있습니다. 또한 open으로 정의된 클래스의 멤버들은 override를 통해서 재정의될 수 있습니다.

❗️ 여기서 중요하게 알아두고 갈 사항은 타입과 타입의 멤버들의 접근제한자는 별개라는 것입니다. open으로 정의된 클래스의 멤버라고 해서 모두 override할 수 있는 것은 아닙니다. open으로 정의되었더라도 내부의 멤버들의 default 접근 제한 상태는 internal입니다.

public

open 보다는 한단계 높은 은닉 수준을 가진 접근제한자입니다. public으로 선언된 코드는 다른 모듈에서도 접근이 가능합니다.

하지만 open과는 다르게 public이 적용된 클래스와 그 멤버들은 모듈 내부에서만 상속 및 override가 될 수 있고 외부 모듈에서는 상속 및 override할 수 없습니다.

internal

internal은 같은 모듈 내에서는 어디서든 접근이 가능하도록 하는 접근 제한자입니다. 기본적으로 아무런 접근 제한자가 선언되지 않는다면 적용되는 디폴트 값입니다.

외부 모듈에서는 접근할 수 없습니다.

fileprivate

이제부터는 같은 모듈 안에서도 접근을 제한할 수 있도록 해주는 접근 제한자들입니다. 아마 패키지를 만들어보지 않으셨어도 앞으로 나오는 fileprivate과 private은 일반 앱에서도 많이 사용하는 것들입니다.

fileprivate으로 선언된 코드는 해당 “파일” 안에서만 접근할 수 있는 코드입니다. 참고로 하나의 전역에서 private으로 선언이 된 코드는 fileprivate과 같은 은닉 수준을 가집니다.

private

가장 은닉도가 높은 접근 제한자로 같은 코드 블록 {} 내에서만 접근이 가능하도록 하는 접근 제한자입니다. private(set)으로 선언할 경우

접근 제한자 사용할 때 주의할 점!

접근 수준 위계

접근 제한자는 은닉 수준이 높은 순으로 private > fileprivate > internal > public > open입니다. 타입에 접근 제한자를 선언할 때는 이 은닉 수준을 고려해야 하는데요. 가장 중요한 원칙은 “하위 멤버는 상위 멤버보다 더 낮은 은닉 수준을 가질 수 없다는 점입니다.”

예를 들면 private인 class 안에 public인 property는 있을 수 없습니다.

//🚫 컴파일 에러가 나지는 않지만 외부 모듈에서 SomeClass를 접근할 수 없으므로 내부 멤버에도 접근할 수 없음.
private class SomeClass {
    public var someString: String = "Hi"
    public class AnotherClass {}
}
private class SomeClass {
    public var someString: String = "Hi"
    public class AnotherClass {}
}

//🚫 private으로 선언된 타입을 property로 가지면 컴파일 에러
public class TheOtherClass {
    var someProperty: SomeClass
}

//🚫 열거형의 연관값의 경우도 마찬가지로 컴파일 에러
public enum SomeEnum {
    case someCase(SomeClass)
}

setter 접근 제한자

객체지향을 공부하시고 앱을 만들어보신 분들은 private(set)을 많이 사용 해보셨을텐데요. 다른 접근 제한자 뒤에도 (set)을 붙이게 되면 setter만 접근제한자를 설정할 수 있습니다.

이를 활용하면 아래처럼 getter와 setter에 각각 다른 접근 제한자를 설정할 수도 있습니다.

// public getter: 외부 모듈에서 get할 수 있음
// private setter: 같은 scope에서만 set할 수 있음.
public class SomeClass {
    public private(set) var someString: String = "Hi"
}

public initializer를 만들자!

제가 이번에 회사 아키텍쳐를 만들면서 가장 많이 했던 실수 중에 하나가 class나 struct를 public으로 만들고 public init을 만들지 않았던 것입니다.

내부에 지정해주어야 할 property가 없거나 모두 기본값이 있는 경우 internal이라면 굳이 initializer를 명시하지 않고 사용하는 경우가 많을텐데요.

public의 경우는 명시적으로 public initializer를 만들지 않으면 외부 모듈에서 해당 타입의 인스턴스를 얻을 수 없습니다.

// 타입이 public이라도
public class SomeClass {
    // 별로의 initializer를 만들어주어야 함
    public init() {}
}
profile
백과사전 보다 항해일지(혹은 표류일지)를 지향합니다.

0개의 댓글