정보/레벨 1

Swift의 접근 제어자(Access Control Levels)에 대해 설명해주세요.

밤새는 탐험가89 2024. 11. 11. 06:56

Swift에서 **접근 제어자(Access Control Levels)**는 코드의 접근 범위를 제어하여, 코드의 캡슐화와 보안을 강화하는 데 사용됩니다. 접근 제어를 통해 클래스, 구조체, 함수 등의 사용 범위를 제한할 수 있으며, 이를 통해 코드의 의도된 사용 범위를 명확히 하고, 외부 접근을 방지하여 안전성을 높일 수 있습니다.

Swift에서 제공하는 접근 제어자는 크게 다섯 가지로 나뉩니다.

 

1. open

  • 특징: 가장 개방적인 접근 수준으로, 모듈 외부에서도 접근 및 상속이 가능합니다.
  • 사용 예시: 클래스와 같은 타입에만 적용할 수 있으며, 주로 프레임워크나 라이브러리에서 외부 모듈이 상속과 재정의를 허용하도록 제공할 때 사용됩니다.
  • 사용 시기: 모듈 외부에서 해당 타입을 상속 및 재정의할 수 있도록 허용하고 싶을 때 사용합니다.
open class OpenClass {
    open func doSomething() {
        print("Open method in OpenClass")
    }
}

 

 

2. public

  • 특징: 모듈 외부에서도 접근할 수 있지만, 상속 및 재정의는 불가능합니다.
  • 사용 예시: 프레임워크나 라이브러리의 API에서 외부 모듈이 사용하도록 공개하지만, 상속과 재정의를 제한하고 싶을 때 사용합니다.
  • 사용 시기: 상속보다는 외부 사용만 허용하고 싶은 경우에 사용됩니다.
public class PublicClass {
    public func doSomething() {
        print("Public method in PublicClass")
    }
}

 

 

3. internal (기본 접근 수준)

  • 특징: 같은 모듈 내에서만 접근 가능합니다. 모듈 외부에서는 접근할 수 없습니다.
  • 사용 예시: Swift의 기본 접근 수준으로, 명시적으로 설정하지 않으면 기본적으로 internal로 설정됩니다.
  • 사용 시기: 주로 앱이나 라이브러리 내부에서만 사용하고, 외부 모듈에는 감추고 싶은 코드에 사용합니다.
class InternalClass {
    func doSomething() {
        print("Internal method in InternalClass")
    }
}

 

⭐️ 위의 InternalClass는 접근 제어자를 명시하지 않았지만, 기본적으로 internal 접근 수준이 적용됩니다.

 

 

4. fileprivate

  • 특징: 같은 파일 내에서만 접근이 가능합니다. 파일을 기준으로 접근을 제한하여, 다른 파일에서는 같은 모듈 내에서도 접근할 수 없습니다.
  • 사용 예시: 파일 내부의 특정 기능을 해당 파일 내에서만 사용하도록 제한하고 싶을 때 사용합니다.
  • 사용 시기: 같은 파일에서 여러 타입이 특정 속성이나 메서드를 공유해야 하지만, 외부 파일에서는 접근하지 못하도록 제한하고 싶은 경우에 사용합니다.
fileprivate class FilePrivateClass {
    fileprivate func doSomething() {
        print("File-private method in FilePrivateClass")
    }
}

 

 

5. private

  • 특징: 같은 파일 내에서도 같은 범위(스코프)에서만 접근이 가능하며, 해당 스코프(클래스, 구조체 등)를 벗어난 곳에서는 접근할 수 없습니다.
  • 사용 예시: 클래스나 구조체 내부에서만 사용하고, 외부에서는 절대 접근할 수 없도록 하는 가장 강력한 접근 제한 수준입니다.
  • 사용 시기: 타입 내부의 속성이나 메서드를 외부에 완전히 숨기고, 해당 타입의 내부에서만 사용해야 하는 경우에 사용합니다.
class PrivateClass {
    private func doSomething() {
        print("Private method in PrivateClass")
    }
}

 

위의 예시에서 doSomething() 메서드는 PrivateClass 내부에서만 접근 가능하며, 외부에서는 접근할 수 없습니다.

접근 제어자 요약 표

접근 제어자 모듈 외부 접근 상속 및 재정의 가능 사용 범위
open 가능 가능 모듈 외부에서 접근 및 상속/재정의가 모두 가능한 가장 개방적인 수준
public 가능 불가능 모듈 외부에서 접근 가능, 상속/재정의 불가능
internal 불가능 불가능 같은 모듈 내에서만 접근 가능 (기본 접근 수준)
fileprivate 불가능 불가능 같은 파일 내에서만 접근 가능
private 불가능 불가능 같은 타입 및 스코프 내에서만 접근 가능

 

 

예를 들어, 앱에서 모듈 외부와의 접근을 제한하고 특정 파일 내에서만 접근해야 하는 경우, 각 접근 제어자를 어떻게 적용할지 살펴보겠습니다.

public class Vehicle {
    public var type = "Car"               // 모듈 외부에서 접근 가능
    internal var speed = 120              // 같은 모듈 내에서만 접근 가능
    fileprivate var fuel = "Gasoline"     // 같은 파일 내에서만 접근 가능
    private var serialNumber = "12345"    // 같은 클래스 내에서만 접근 가능
    
    public func drive() {
        print("Driving a \(type) at \(speed) km/h")
        print("Fuel type: \(fuel)")
        print("Serial Number: \(serialNumber)") // 내부 접근 가능
    }
}

 

위 코드에서:

  • type: public이므로 모듈 외부에서 접근 가능.
  • speed: internal이므로 같은 모듈 내에서만 접근 가능.
  • fuel: fileprivate이므로 같은 파일 내에서만 접근 가능.
  • serialNumber: private이므로 Vehicle 클래스 내부에서만 접근 가능.

요약

  • openpublic: 외부 모듈에서 사용되며, open은 상속/재정의를 허용, public은 허용하지 않습니다.
  • internal: 같은 모듈 내에서만 접근 가능하며, 기본 접근 수준입니다.
  • fileprivateprivate: 같은 파일 내 또는 스코프 내부로 접근을 제한하여 코드의 캡슐화와 보안을 강화합니다.

Swift에서의 접근 제어자는 코드의 사용 범위를 명확히 정의하고, 보안과 캡슐화를 높이는 데 매우 중요한 역할을 합니다.

 

 

open과 public의 차이점은 무엇인가요?

 

open과 public은 Swift에서 가장 개방적인 접근 제어자로, 외부 모듈에서도 접근할 수 있습니다. 하지만 이 둘에는 중요한 차이점이 있습니다.

1. 상속 및 재정의 가능 여부

  • open: 외부 모듈에서도 상속과 재정의가 허용됩니다.
  • public: 외부 모듈에서 상속과 재정의가 불가능합니다.

즉, open 접근 수준은 클래스나 메서드를 상속하고 재정의할 수 있게 허용하는 반면, public 접근 수준은 외부에서 접근만 가능하고 상속과 재정의를 막습니다.

// open 클래스
open class OpenClass {
    open func greet() {
        print("Hello from OpenClass")
    }
}

// public 클래스
public class PublicClass {
    public func greet() {
        print("Hello from PublicClass")
    }
}​
 

 

외부 모듈에서 OpenClass를 상속하고 greet 메서드를 재정의할 수 있지만, PublicClass는 상속이나 메서드 재정의가 불가능합니다.

 

2. 사용 용도

  • open: 주로 레임워크나 라이브러리의 API에서 상속과 재정의를 허용할 때 사용합니다. 외부 모듈이 클래스나 메서드를 확장해 커스터마이징할 수 있도록 설계할 때 적합합니다.
  • public: 상속이 필요 없고, 외부에서 접근만 가능하게 하고 싶을 때 사용합니다. 외부 모듈이 API를 사용하되, 변경을 허용하지 않아야 할 때 사용합니다.

 

3. 기본 적용 대상

  • open은 클래스와 클래스 멤버에만 사용할 수 있습니다.
  • public은 모든 타입(클래스, 구조체, 열거형 등)과 속성, 메서드, 서브스크립트, 이니셜라이저에 사용할 수 있습니다.

 

다음은 open과 public의 차이를 보여주는 간단한 예시입니다:

// 모듈 A: Framework

open class Animal {
    open func sound() {
        print("Animal sound")
    }
}

public class Vehicle {
    public func run() {
        print("Vehicle is running")
    }
}

// 모듈 B: Application

class Dog: Animal {
    override func sound() {
        print("Woof!")  // 재정의 가능
    }
}

// Vehicle은 public이므로 상속 불가
// class Car: Vehicle { }  // 오류 발생: 상속 불가능

 

 

위의 예에서:

  • Animal 클래스와 sound() 메서드는 open으로 정의되었기 때문에 모듈 외부에서도 상속과 재정의가 가능합니다.
  • Vehicle 클래스와 run() 메서드는 public으로 정의되어 외부 모듈에서 접근은 가능하지만 상속과 재정의는 불가능합니다.

 

요약

  • open: 외부 모듈에서 상속과 재정의가 가능하며, 클래스와 클래스 멤버에만 적용할 수 있습니다.
  • public: 외부 모듈에서 접근만 허용하고, 상속과 재정의는 불가능하며, 모든 타입과 멤버에 사용할 수 있습니다.

 

 

internal, fileprivate, private의 사용 시기는 어떻게 결정하나요?

Swift에서 internal, fileprivate, private 접근 제어자는 각각 접근 범위를 제한하여 코드의 캡슐화와 보안을 강화합니다. 사용 시기는 코드의 사용 목적, 접근 범위, 보안 수준에 따라 결정할 수 있습니다. 각 접근 제어자는 특정 상황에서 코드의 접근을 제한하여 의도하지 않은 접근을 방지하고, 코드의 유지보수성을 높이는 역할을 합니다.

 

 

1. internal (기본 접근 수준)

  • 특징: internal은 같은 모듈 내에서만 접근을 허용하는 접근 제어자입니다. 모듈 외부에서는 접근할 수 없지만, 모듈 내에서는 어디서든 접근이 가능합니다.
  • 사용 시기: Swift의 기본 접근 수준이며, 외부 모듈에서는 접근할 필요가 없고 모듈 내 여러 파일에서 접근이 필요할 때 사용합니다. 예를 들어, 앱 전체에서 사용할 유틸리티 함수나 데이터 모델은 internal로 정의하면 좋습니다.
  • 예시: 애플리케이션의 여러 기능에서 사용해야 하는 도메인 모델, 공용 유틸리티 클래스 등을 정의할 때 적합합니다.
internal class NetworkManager {
    func fetchData() {
        print("Fetching data...")
    }
}
 
 

2. fileprivate

  • 특징: fileprivate는 같은 파일 내에서만 접근이 가능하도록 제한하는 접근 제어자입니다. 다른 파일에서는 같은 모듈 내에서도 접근할 수 없습니다.
  • 사용 시기: 특정 파일 내의 타입 간에 공유할 필요가 있을 때 사용합니다. 같은 파일 내의 다른 타입에서 접근해야 하지만, 파일 외부에서는 접근할 필요가 없는 경우에 유용합니다. 이를 통해 코드를 파일 단위로 분리하여 캡슐화할 수 있습니다.
  • 예시: 하나의 파일에 여러 관련 타입을 정의하고, 이들 타입 간에 접근이 필요한 속성이나 메서드를 공유할 때 적합합니다.
fileprivate class FileManagerHelper {
    fileprivate func loadFile() {
        print("File loaded")
    }
}

class FileController {
    func performAction() {
        let helper = FileManagerHelper()
        helper.loadFile()  // 같은 파일 내에서 접근 가능
    }
}

 

위의 FileManagerHelper와 FileController가 같은 파일에 정의되어 있는 경우, loadFile() 메서드를 fileprivate로 선언하여 이 파일 안에서만 사용할 수 있도록 제한할 수 있습니다.

 

 

3. private

  • 특징: private는 같은 스코프(클래스, 구조체, 확장 등) 내에서만 접근 가능하도록 제한하는 접근 제어자입니다. fileprivate보다 더 강력하게 접근을 제한하여, 정의된 스코프를 벗어난 다른 부분에서는 접근할 수 없습니다.
  • 사용 시기: 해당 스코프에서만 사용해야 하는 속성이나 메서드가 있을 때 사용합니다. 주로 외부에서 접근하지 못하도록 철저하게 숨기고 싶은 경우, 특정 타입의 내부 구현 세부 사항을 감추고자 할 때 사용합니다.
  • 예시: 클래스의 내부 상태를 감추기 위해 외부에서 절대 접근하지 못하도록 하여 코드의 안전성을 높입니다.
class AccountManager {
    private var balance: Double = 0.0  // 클래스 외부에서 접근 불가
    
    private func updateBalance(amount: Double) {
        balance += amount
    }
    
    func deposit(amount: Double) {
        updateBalance(amount: amount)  // 내부에서만 사용
        print("Deposited \(amount)")
    }
}

 

위의 balance 속성은 AccountManager 클래스 내부에서만 접근이 가능하도록 private으로 선언되어 있습니다. updateBalance() 메서드도 외부에서 호출할 수 없으며, 클래스 내부에서만 사용됩니다. 이를 통해 외부 접근을 제한하고 클래스 내부 상태를 보호할 수 있습니다.

 

요약

접근 제어자 사용 시기 
internal 모듈 내 어디서든 사용해야 하는 코드. 외부 접근은 불필요하지만 모듈 내에서 여러 파일에서 공유가 필요할 때.
fileprivate 파일 내에서 특정 타입 간 코드를 공유해야 하지만, 다른 파일에서는 접근할 필요가 없는 경우.
private 해당 스코프 내에서만 사용되어야 하는 코드. 외부 접근을 철저히 제한해 내부 구현을 감춰야 할 때.

 

 

 

접근 제어자를 사용하는 이유는 무엇인가요?

접근 제어자를 사용하는 이유는 코드의 안전성과 캡슐화를 강화하고 코드의 가독성과 유지보수성을 높이기 위해서입니다. Swift에서 접근 제어자는 코드의 접근 범위를 제한하여 불필요한 외부 접근을 방지하고, 코드가 의도된 대로 사용되도록 돕는 중요한 역할을 합니다.

 

  • 캡슐화와 정보 은닉
    • **캡슐화(encapsulation)**란 객체 지향 프로그래밍의 중요한 개념 중 하나로, 객체의 속성과 메서드를 외부에서 접근할 수 없도록 보호하여 내부 구현을 감추는 것입니다.
    • 접근 제어자를 통해 외부에 꼭 필요한 부분만 공개하고, 내부적인 세부 사항은 감출 수 있습니다. 이를 통해 코드가 의도치 않게 수정되거나 외부에서 접근되는 것을 방지할 수 있습니다.
    • 예를 들어, 클래스의 내부 상태를 private으로 설정하면, 외부에서 직접 수정할 수 없으므로 객체의 상태를 안전하게 보호할 수 있습니다.
class BankAccount {
    private var balance: Double = 0.0  // 외부에서 접근 불가

    func deposit(amount: Double) {
        balance += amount
    }
}
 

 

  • 위 코드에서 balance는 private으로 선언되어 외부에서 직접 접근할 수 없으며, 오직 deposit 메서드를 통해서만 수정됩니다. 이를 통해 외부 코드가 객체의 내부 상태를 직접 변경하지 못하도록 보호할 수 있습니다.

 

  • 코드의 안전성 및 무결성 유지
    • 접근 제어자를 통해 데이터의 접근을 제한하면 코드의 안전성과 무결성을 보장할 수 있습니다.
    • 예를 들어, 외부 모듈에서는 접근할 필요가 없는 특정 코드나 데이터가 외부에서 잘못 사용될 위험을 줄일 수 있습니다. 이를 통해 불필요한 접근을 막고 의도하지 않은 코드 사용이나 변경으로 인한 오류를 예방할 수 있습니다.

 

  • 코드의 가독성과 유지보수성 향상
    • 접근 제어자를 적절히 사용하면 코드의 의도를 명확하게 전달할 수 있습니다. 특정 클래스나 속성, 메서드가 외부에 공개되거나 사용되기를 의도하지 않았다는 사실을 명확하게 나타낼 수 있습니다.
    • 또한, 개발자는 코드가 외부에서 사용 가능한지, 같은 파일 내에서만 사용해야 하는지 쉽게 파악할 수 있어 유지보수가 용이해집니다.

 

  • 모듈 간 결합도 낮추기
    • 접근 제어자를 사용해 모듈 간 결합도를 낮추면 모듈의 독립성을 유지하고, 특정 모듈을 수정하더라도 다른 모듈에 영향을 미치지 않도록 할 수 있습니다. 예를 들어, 라이브러리나 프레임워크에서 public 또는 open으로 필요한 부분만 외부에 공개하고, 나머지 구현은 internal로 감추면, 외부 모듈과 결합도를 낮추어 코드의 안정성을 높일 수 있습니다.

 

  • 의도된 API 설계
    • 프레임워크나 라이브러리에서 외부 모듈에 제공하고자 하는 인터페이스(API)를 설계할 때, 접근 제어자를 통해 외부에 공개할 기능만 public 또는 open으로 설정할 수 있습니다.
    • 이로 인해 외부 모듈이 필요하지 않은 내부 구현에 접근하는 것을 방지하고, 사용자가 프레임워크나 라이브러리를 사용할 때 의도된 API만 사용하도록 제한할 수 있습니다.

 

요약: 접근 제어자를 사용하는 이유

 

  • 캡슐화와 정보 은닉: 내부 구현을 외부에 노출하지 않음으로써 코드의 안정성을 높입니다.
  • 안전성 및 무결성 유지: 의도치 않은 접근이나 수정으로 인한 오류를 예방합니다.
  • 가독성과 유지보수성 향상: 코드의 사용 의도를 명확히 하여, 유지보수가 용이해집니다.
  • 모듈 간 결합도 낮추기: 모듈 간 결합도를 줄여, 코드 수정이 다른 모듈에 미치는 영향을 최소화합니다.
  • API 설계의 명확성: 필요한 기능만 외부에 공개하여, 외부 모듈이 의도된 API만 사용할 수 있도록 제한합니다.