ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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
Designed by Tistory.