Library, Framework, Swift package

Park Jong Ho·2022년 12월 17일
1

서론

빠른 개발을 위해서 우리는 SPM 혹은 코코아팟으로 써드 파티 라이브러리들을 프로젝트에 추가하고 개발한다.

우리는 써드파티 라이브러리들을 추가할 때 framework, library, Swift package란 용어들을 접한다. 그러나 이 세 가지의 차이점에 대해선 잘 알지 못했는데, 이 글을 통해 한 번 정리해보려고 한다.

기초 & 용어들

Swift는 Module을 기반으로 접근제어와 같은 기능들을 제공한다. 초반엔 iOS 앱을 개발하면, 보통 하나의 타겟 내에서 모든 코드들 (Data Layer라던가, Utility 코드 등등) 을 작성하곤 했는데, 이런 경우는 모두 같은 모듈에 들어가기 때문에 internal 접근 수준으로도 모든 코드에 접근 가능했다.

모듈이란 코드 배포의 단위로 사용되며, Framework, Library, Swift Package, 또한 Xcode의 target 들이 각각 모듈로 취급된다. 모듈은 보통 부분적인 문제들을 해결하며, 재사용 가능하기 때문에 하나의 모듈을 잘 만들어 놓는다면 프로젝트 전반적으로 계속해서 코드를 재사용할 수 있다.

그리고 앱의 각 기능을 모듈로 분리한다면, 많은 장점을 가져올 수 있다. 예를 들어 이미 컴파일한 모듈의 경우 업데이트되지 않는다면 재컴파일이 불필요하므로, 컴파일 시간도 줄일 수 있고, 또한 각 기능이 명확히 나뉘기 때문에 작업 능률도 향상된다.

번들이란 여러 개의 서브디렉토리들로 이루어진 파일 디렉토리이며, 관련있는 모든 파일들을 하나의 패키지 형태로 나타낸다. 예를 들어, 실행에 필요한 image, nib 파일들 혹은 컴파일된 코드들 까지, 하나의 번들로 묶이게 되고, 시스템은 번들을 하나의 파일로 취급하기 때문에 번들의 내부 구조를 모르고도 우리는 번들 내부에 존재하는 여러 가지 자원들에 접근할 수 있다.

소스 파일이란 말 그대로 swift 소스코드가 담긴 모듈 내부의 파일이다.

Executable이란 실행 가능한 파일이다.

Object file 이란 Shared library(모든 실행파일 혹은 다른 Object file들에서 참조 가능한 라이브러리, dynamic library 라고 이해하면 될 듯?)와 비슷하게 동작하며, 모든 곳에서 참조 가능한 기계어로 된 파일이다. Object file은 실행할 수는 없다.

Library

컴퓨터 과학에서, Library란 자원들과 컴파일된 코드들의 집합을 의미한다. iOS 앱을 개발하면서 우리는 static library 와 dynamic library를 사용하게 된다.

Static Library

img

static library를 사용하면, Static linker라는 녀석이 우리가 작성한 소스 코드들과 라이브러리 코드들을 연결하고, 최종적으론 source 코드와 static library가 결합된 executable file이 생성된다. 즉 우리가 Static library를 사용한다면, 해당 라이브러리 코드들은 모두 우리 프로그램 내부에 포함된다.

Static library는 다음과 같은 특징을 가지고 있다.

  • 실행 파일의 크기가 커진다. (라이브러리 코드가 실행 파일 내부에 포함되므로)
  • Static Library가 업데이트 된다면, 다시 link 작업을 해야 한다. 결론적으론 다시 컴파일 하는 과정이 필요함
  • image, asset, nib, string file과 같은 파일들을 포함할 수 없다.

Dynamic Library (*.dylib)

Dynamic Library는 static 과 다르게, 실행 파일 내부에 라이브러리 코드들이 포함되지 않는다. 대신 static linker는 library에 대한 참조를 실행 파일 내부에 저장시키며, 프로그램을 로드하거나 실행할 때 동적으로 메모리 내에 상주하고 있는 라이브러리 파일들과 연결된다.

System iOS와 macOS 라이브러리는 모두 dynamic library다.

그도 그럴것이 system library는 모든 앱에서 사용하게 되는데, 만약 system library가 static 이면 모든 앱에 중복된 코드들이 들어가게 될 것이다. 그러면 모든 앱들의 용량이 지금보다 훨씬 커질 것이다.

또한 dynamic library는 다음과 같은 특징을 가지고 있다.

  • 실행 파일 내부엔 dynamic library에 대한 참조만 저장하고 있으므로, 만약 이후에 dynamic library가 업데이트 된다고 해도 다시 컴파일 할 필요가 없이 업데이트 된 사항들을 모두 적용할 수 있다.

Frameworks

framework는 dynamic library, header files, 그리고 storyboard, nib, image 파일들과 같은 파일들이 포함된 디렉토리 계층 구조를 하나의 package 형식으로 encapsulate 한다. framework는 static and dynamic library와 똑같은 문제를 해결하기 위해 나온 개념이다.

그러나 library와는 다른 점이 있는데, 바로

  • Library와는 다르게 image, asset, 그리고 다른 모든 파일들을 포함할 수 있다.
  • 오로지 하나의 framework가 로드되서 메모리에 상주하게 된다. 따라서 많은 iOS App과 extension이 하나의 프레임워크를 공유할 수 있으며, 총 메모리 사용량이 줄어들게 된다.

또한 제어 역전의 관점에서 Library와 Framework의 차이점을 기술하는 내용도 있는데, 다음과 같다.

  • Library에 존재하는 모든 함수들, 그리고 코드들은, 사용하는 클라이언트 쪽에서 호출해야 한다. 이 말인 즉, 우리가 작성하는 코드들 (Client) 에서 Library의 코드를 호출하고 제어한다는 의미가 된다.
  • 그러나 Framework는 Library와 다르게, 우리가 제어하는 것이 아니다. 우리는 Framework에 존재하는 인터페이스, 클래스들을 상속 혹은 구현함으로써 원하는 행동을 주입할 수 있다. 그러고 나면, Framework가 우리가 주입한 행동들을 호출한다.

    즉 Framework가 우리가 작성한 코드들을 호출하고, 흐름을 제어한다는 의미이다.

  • Framework에서 호출하는 이벤트들을 interface 형식으로 정의하거나 (Button의 클릭 이벤트, delegate 등등...) 아니면 상속 가능한 class 형태로 제공한다. 또한 Notification 과 같은 패턴을 사용해서 우리가 원하는 행동을 주입하기도 한다.

    예를 들어 Keyboard가 올라오는 이벤트를 Notification Center에서 전달해준다.우리가 만약 Keyboard가 올라올 때 특정한 행동을 하고 싶으면 그런 코드를 Notification center에 넘겨주면 된다. 그러면 Notification center가 키보드가 올라올 때마다 우리가 주입한 행동을 실행하게 된다.

이렇게 Framework 는 제어 역전을 제공함으로써, 객체의 라이프 사이클과 흐름을 모두 Framework가 결정하게 된다. 단지 우리는 Framework에 원하는 행동 (비즈니스 로직)만 주입하면 되는 것이다.

Swift Package

Swift package는 Source 파일들과 Package.swift 라는 manifest file을 포함하고 있다. Swift package는 Swift Package Manager와 함께 사용되며, Swift 3.0 이상부터 사용가능 하다.

Swift package는 framework와 마찬가지로 각종 image, asset 그리고 Storyboard 등등의 파일을 포함할 수 있다.

Swift Package는 Library, Framework이 binary code 형식으로 배포되는 것과 다르게 소스코드 형태로 배포된다. 따라서 binrary compatability를 더 이상 유지할 필요가 없다. 따라서 Swift가 지원하는 모든 플랫폼에서 사용 가능하다.

그러나 소스코드 형태로 배포되기 때문에, 만약 소스 코드를 공개하길 원하지 않는다면 Package 형태로 배포하는 것을 추천하지 않는다.

Swift package는 다음 3가지 요소로 구성되며, 이 모든 설정은 Package.swift 파일에서 수정할 수 있다.

// swift-tools-version: 5.4
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "KeyChainWrapper",
    platforms: [
        .iOS(.v14),
        .macOS(.v11),
    ],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "KeyChainWrapper",
            targets: ["KeyChainWrapper"]),

    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "KeyChainWrapper",
            dependencies: []),
    ]
)

Package.swift 파일에선, package가 어떤 형태로 배포될지, 또 어떤 외부 라이브러리에 의존할지, 그리고 어떤 타겟들을 배포에 포함할지, 그리고 어떤 플랫폼에서 사용 가능한 패키지인지도 결정할 수 있다.

  • Dependencies - swift package는 다른 package를 사용할 수 있으며, 의존하는 다른 package들은 Package.swift 파일에 작성된다.
  • Target - Swift Package가 빌드할 타겟을 나타낸다.
  • Products - Swift Package가 최종적으로 어떤 형식으로 배포될 것인지를 결정한다. 위에선 library 형식으로 배포된다고 적혀있는데, dynamic 인지 static인지도 결정할 수 있고, library말고 plugin, executable 형태로도 배포가 가능하다.

Swift Package의 장점과 단점

Swift Package의 장점

  • 패키지와 패키지의 의존성이 Xcode에 의해 관리된다.
  • 패키지의 버전또한 Xcode에 의해 관리된다.
  • 크로스 플랫폼이 가능하다. 즉 binary compatability가 필요 없다는 뜻이다.
  • 소스 코드 형태로 배포되기 때문에, 사용하는 클라이언트 측에서 소스 코드를 살펴보며 디버깅 할 수 있다.

Swift Package의 단점

  • 아무래도 소스 코드 형태로 배포된다면, 비공개 하길 원하는 제공자에겐 적합하지 않은 배포 방식이다.

References

https://medium.com/@zippicoder/libraries-frameworks-swift-packages-whats-the-difference-764f371444cd

https://martinfowler.com/bliki/InversionOfControl.html

profile
iOS 개발자입니다.

1개의 댓글

comment-user-thumbnail
2023년 11월 19일

잘 읽었습니다!

답글 달기