[iOS/Swift] 새로 등장한 접근제어자, package

 

접근 제어(Access Control)는 다른 소스 파일과 모듈 등에서 코드 접근에 대해 제한하는 기능이다.

접근 제어를 통해 코드의 세부 구현을 숨길수도 있으며, 어디에서까지 해당 코드에 접근할지 범위를 설정할 수 있다.

 

우리가 흔히 알고있는 Swift의 접근제어자는 5가지가 있다.

 

  • `private` : 선언된 파일 내에서만 접근 가능
  • `fileprivate` : 같은 파일 내 모든 스코프에서 접근 가능
  • `internal` : 같은 모듈(타겟) 내에서만 접근 가능
  • `public` : 다른 모듈에서도 접근 가능
  • `open` : public과 같으면서, 서브클래싱오버라이드까지 허용

 

그런데 Swift5.9에서 `package`라는 새로운 접근제어자가 등장했다.

 

이번 글에서는 이 package 접근제어자에 대해 알아보도록 하겠다.

 


package 접근제어자란?

package의 접근 제어 범위는 SPM으로 구성된 하나의 패키지 내부에서만 접근을 허용한다.

즉, package 접근제어자는 패키지 안에 여러 모듈이 있을 때, 이 모듈 사이에서만 접근이 가능하게 하는 것으로,

패키지 외부에 코드를 공유하지 않는 다는 특징이 있다.

 

그럼, package 접근제어자가 등장한 이유에 대해 알아보도록 하자.

 

package 접근제어를 사용할 수 없었을때는, 패키지 내의 다른 모듈에서 코드에 접근하기 위해 public 접근제어자를 사용했을 것이다.

// gamePkg 내의 Engine 모듈

public struct MainEngine {
    public init() { ...  }
    // public으로 선언
    public var stats: String { ...  }
    // Game 모듈에서만 접근 가능하도록 public으로 선언된 헬퍼 펑션
    public func run() { ...  }
}
// gamePkg 내의 Game 모듈

import Engine

public func play() {
    MainEngine().run() // 같은 gamePkg 내부이기 때문에 run 펑션에 접근 가능
}

 

그러나, 이렇게 public으로 선언된 코드는 패키지 외부에서도 접근 가능하다는 문제가 있었다.

// appPkg 내의 클라이언트 App

import Game
import Engine

let engine = MainEngine()
engine.run() // App 에서도 run 펑션에 접근 가능
Game.play()
print(engine.stats) // state에 접근 가능

 

 

이렇게 됐을때의 문제점은 다음과 같다.

클라이언트 App 에서 패키지 내부의 코드에 접근 할 수 있다는 것은, 이 SPM을 쓰는 모든 클라이언트 App 에서 내부 코드에 접근할 수 있다는 뜻이고, 이는 곧 의도치 않게 패키지 내부의 API등이 노출될 수 있다는 위험이 존재한다는 것이다.

 

내부 API가 노출된다는건, SPM을 사용하는 사용자가 내부 구현에 의존하게 되고, 패키지를 만드는 쪽에서 마음대로 구조를 바꾸거나 삭제할 수 없는 등 유연성이 떨어질 수 있다.

 

Swift5.9에서 package 접근제어자가 등장한 이유가 바로 이러한 문제를 해결하기 위해서이다.

 


package를 사용한 개선

위의 public으로 선언된 코드들을, package로 다음과 같이 변경해볼 수 있다.

 

state 프로퍼티와 run 펑션을 package로 선언하면 다음과 같이 같은 패키지 내의 다른 모듈에서 접근해서 사용할 수 있다.

// gamePkg 내의 Engine 모듈

package struct MainEngine {
    // package 접근제어자로 초기화도 패키지 내부 전용으로 지정
    package init() { ... }

    // 패키지 내부에서만 읽을 수 있는 stats 프로퍼티
    package var stats: String {
        // ...
        return "Engine 상태 정보"
    }

    // Game 모듈에서도 접근 가능한 run 함수
    package func run() {
        print("Engine 가동 중…")
    }
}
// gamePkg 내의 Game 모듈

import Engine

// App 쪽에 노출할 public API
public func play() {
    let engine = MainEngine() // 같은 패키지 내이므로 접근 가능
    engine.run() // ✅ package 멤버 호출 가능
    print(engine.stats) // ✅ package 멤버 호출 가능
}

 

그리고, 외부 클라이언트 앱에서는 public으로 선언된 play 펑션에는 접근할 수 있으나, 

package로 선언된 MainEngine이나, run, state에는 접근 할 수 없다.

// appPkg 내의 클라이언트 App

import Game
import Engine

play() // ✅ Game.play()는 public이므로 호출 가능

let engine = MainEngine() // ❌ 오류: 'MainEngine' initializer is inaccessible due to 'package' protection level
engine.run() // ❌ 오류: 'run()' is inaccessible due to 'package' protection level
print(engine.stats) // ❌ 오류: 'stats' is inaccessible due to 'package' protection level

 

 

이렇게 package를 도입하면 위에서 언급한 public으로 선언했을 때의 문제를 해결할 수 있다.

  • 배포 단위 경계: 모든 클라이언트에 노출 -> 패키지 내부에만 공개하고 외부에는 숨김
  • API 유지보수: 의도치 않은 의존성 발생 -> 외부에 영향을 받지 않고, 내부에서 리펙토링 가능

 


*참고자료

https://github.com/swiftlang/swift-evolution/blob/main/proposals/0386-package-access-modifier.md

 

swift-evolution/proposals/0386-package-access-modifier.md at main · swiftlang/swift-evolution

This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swiftlang/swift-evolution

github.com

 

https://green1229.tistory.com/453

 

New access modifier - package

안녕하세요. 그린입니다 🍏 이번 포스팅에서는 Swift 5.9에서 새로 나온 접근 제어자인 package에 대해 알아보겠습니다 🙋🏻 나온 배경 원래 기존에 접근제어자라고 하면 흔히 알고 있는 5가지가

green1229.tistory.com

 

https://youngkdevlog.tistory.com/70

 

Swift 6.0 접근제어자 Access Control

우선 우리가 잘 알고 있듯이 swift의 접근제어자는 다음과 같이 6개가 있습니다. 5개라고 알고 있는 사람도 많겠지만, Swift 5.9 부터 package 접근제어자가 추가되어 6개가 되었습니다.Swift는 코드 내

youngkdevlog.tistory.com

 

https://blog.eidinger.info/a-new-access-modifier-in-swift-package

 

A new access modifier in Swift: package

Detailed explanation about Swift's new package access modifier introduced in Swift 5.9

blog.eidinger.info