-
[Swift] Access Control🍏Swift 2023. 2. 6. 13:38
안녕하세요 오늘은 평소 개발하면서 놓치기 쉬운 접근 제어에 대한 부분을 공부해보려고 합니다!
먼저, 접근 제어를 사용하는 이유는 뭘까요?
- 외부로부터 데이터를 보호하기 위해서
- 외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해서
입니다!
그럼 어떻게 사용하면 될까요?
개념부터 차근차근 알아보겠습니다!
개념
- 접근 수준
💡 모듈(module): 프레임워크, 라이브러리, 앱 등 import해서 사용할 수 있는 외부의 코드
💡 타입(클래스, 구조체, 열거형 등), 변수/속성, 함수/메서드(생성자, 서브스크립트), 프로토콜 등 접근 제어 사용 가능
- 기본 원칙
타입은 타입을 사용하는 변수(속성)나, 함수(메서드)보다 높은 수준으로 선언되어야함
// 변수 // public variable에 속한 타입은 더 낮은 접근 수준을 가지지 못함 // (public/internal/fileprivate/private) var some: String = "접근가능" // 함수 // 파라미터, 리턴 타입이 더 낮은 접근 수준을 가지지 못함 // (internal/fileprivate/private) internal func someFunction(a: Int) -> Bool { print(a) // Int 타입 print("hello") // String 타입 return true // Bool 타입 } // 자신보다 내부에서 더 낮은 타입을 사용하면 접근을 못해서, 사용하지 못할 수 있음- 기본 문법
//타입 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() {} //아무것도 붙이지 않으면? class SomeInternalClass1 {} //암시적인 internal 선언 let someInternalConstant1 = 0- 실무에서의 관습적 패턴
//실제 프로젝트에서 많이 사용하는 관습적인 패턴 //속성(변수)를 선언시 private으로 외부에 감추려는 속성은 _(언더바)를 사용해서 이름 지음 class SomeOtherClass { private var _name = "이름" //쓰기 - private var name: String { //읽기 - internal return _name } } var abc = SomeOtherClass() abc.name //가능 abc.name = "스위프트" //불가능 //저장속성의 (외부에서) 쓰기를 제한하기 //읽기만 가능하게 만듦 class SomeAnotherClass { // 읽기 - internal / 쓰기 - private //internal이 앞에 생략됨 private(set) var name = "이름" }커스텀 타입의 접근제어
- 사용자 정의 타입의 접근 제어
1. 타입의 내부 멤버는 타입자체의 접근 수준을 넘을 수 없음
2. 내부 멤버가 명시적선언을 하지 않는다면 internal로 유지//타입의 접근 제어 수준은 (해당 타입)멤버의 접근 제어 수준에 영향을 미침 //ex)타입이 internal 로 선언 되면 내부의 멤버는 internal이하(internal / fileprivate / private)로 설정됨 public class SomePublicClass { //명시적인 public 선언 open var someOpenProperty = "SomeOpen" //open 이라고 설정해도 public으로 작동 public var somePublicProperty = "SomePublic" var someInternalProperty = "SomeInternal" //원래의 기본 수준 fileprivate var someFilePrivateProperty = "SomeFilePrivate" private var somePrivateProperty = "SomePrivate" } let somePublic = SomePublicClass() somePublic.someOpenProperty somePublic.somePublicProperty somePublic.someInternalProperty somePublic.someFilePrivateProperty //같은 파일 안이기 때문에 접근 가능 //somePublic.somePrivateProperty class SomeInternalClass { //암시적인 internal 선언 open var someOpenProperty = "SomeOpen" //open 이라고 설정해도 internal로 작동 public var somePublicProperty = "SomePublic" //public 이라고 설정해도 internal로 작동 var someInternalProperty = "SomeInternal" fileprivate var someFilePrivateProperty = "SomeFilePrivate" private var somePrivateProperty = "SomePrivate" } let someInternal = SomeInternalClass() someInternal.someOpenProperty someInternal.somePublicProperty someInternal.someInternalProperty someInternal.someFilePrivateProperty // 같은 파일 안이기 때문에 접근 가능 //someInternal.somePrivateProperty fileprivate class SomeFilePrivateClass { // 명시적인 fileprivate 선언 open var someOpenProperty = "SomeOpen" public var somePublicProperty = "SomePublic" var someInternalProperty = "SomeInternal" var someFilePrivateProperty = "SomeFilePrivate" private var somePrivateProperty = "SomePrivate" } //변수선언(internal) -> 타입선언(fileprivate)은 불가능 (fileprivate / private 선언가능) //internal let someFilePrivate = SomeFilePrivateClass() fileprivate let someFilePrivate = SomeFilePrivateClass() someFilePrivate.someOpenProperty // fileprivate someFilePrivate.somePublicProperty // fileprivate someFilePrivate.someInternalProperty // fileprivate someFilePrivate.someFilePrivateProperty // 같은 파일 안이기 때문에 접근 가능 //someFilePrivate.somePrivateProperty private let someFilePrivate2 = SomeFilePrivateClass() // 현재의 scope에서 private //타입 자체를 private으로 선언하는 것은 아무 의미가 없어짐 -> fileprivate으로 동작함 private class SomePrivateClass { // 명시적인 private 선언 open var someOpenProperty = "SomeOpen" public var somePublicProperty = "SomePublic" var someInternalProperty = "SomeInternal" var someFilePrivateProperty = "SomeFilePrivate" // 실제 fileprivate 처럼 동작(공식문서 오류) private var somePrivateProperty = "SomePrivate" } fileprivate let somePrivate = SomePrivateClass() somePrivate.someOpenProperty somePrivate.somePublicProperty somePrivate.someInternalProperty somePrivate.someFilePrivateProperty // 같은 파일 안이기 때문에 접근 가능 //somePrivate.somePrivateProperty // 타입의 접근 수준이 높다고, 내부 멤버의 접근 수준이 무조건 따라서 높아지는 것 아님 ****open class SomeClass { var someProperty = "SomeInternal" // internal 임 ➞ 클래스와 동일한 수준을 유지하려면 명시적으로 open선언 필요 }- 상속과 확장의 접근 제어
1) 타입 관련: 상속해서 만든 서브클래스는 상위클래스보다 더 높은 접근 수준을 가질 수는 없음
2) 멤버 관련: 동일 모듈에서 정의한 클래스의 상위 멤버에 접근가능하면 (접근 수준 올려서) 재정의(override)도 가능//상속의 접근 제어 public class A { fileprivate func someMethod() {} } // public이하의 접근 수준만 가능(public/internal/fileprivate) internal class B: A { override internal func someMethod() { //접근 수준 올려서 재정의 가능 super.someMethod() //(더 낮아도) 모듈에서 접근가능하기 때문에 호출가능 } } //확장의 접근 제어 //기본법칙: 원래 본체와 동일한 접근 수준을 유지하고 본체의 멤버에는 기본적인 접근 가능 public class SomeClass { private var somePrivateProperty = "somePrivate" } extension SomeClass { // public으로 선언한 것과 같음 func somePrivateControlFunction() { somePrivateProperty = "접근가능" } }- 속성과 접근 제어
속성의 읽기(getter)와 쓰기(setter)의 접근 제어 수준을 구분해서 구현하는 것
// 일반적으로 밖에서 쓰는 것(setter)은 불가능하도록 구현하는 경우가 많음 struct TrackedString { //var numberOfEdits = 0 //외부에서도 변경가능 //private var numberOfEdits = 0 //이렇게 선언하면, 읽기도 불가능해짐 //앞에 internal이 생략 private(set) var numberOfEdits = 0 //setter에 대해서만 private 선언 //internal private(set) var numberOfEdits = 0 // 속성 관찰자 var value: String = "시작" { didSet { numberOfEdits += 1 } } } var stringToEdit = TrackedString() stringToEdit.value = "첫설정" stringToEdit.value += " 추가하기1" stringToEdit.value += " 추가하기2" stringToEdit.value += " 추가하기3" print("수정한 횟수: \\(stringToEdit.numberOfEdits)") print(stringToEdit.value) //아래와 같이 외부에서 직접설정하는 것은 불가능(읽는 것은 가능) //stringToEdit.numberOfEdits = 3💡 아래처럼 읽기설정(getter)과 쓰기설정(setter)을 명시적으로 표현가능
💡 internal private(set) var numberOfEdits = 0 이라고 선언하면
속성의 읽기설정(getter)은 - internal
속성의 쓰기설정(setter)은 - private(set)
저장속성/계산속성 모두에 설정 가능혹시 잘못된 내용이 있다면 피드백 주시면 정말 감사합니다! 😊
'🍏Swift' 카테고리의 다른 글
[Swift] Optional (0) 2023.02.19 [Swift] Enumeration (0) 2023.02.09 [Swift] map, filter, reduce 등 고차함수 (0) 2023.02.03 [Swift] 메서드의 종류 (0) 2022.10.13 [Swift] 프로토콜 (0) 2022.08.09