[Swift] Dynamic Libraries or Static Libraries / 동적 라이브러리, 정적라이브러리

2024. 2. 8. 04:51🍏/Swift

라이브러리란?

라이브러리는 재사용 가능한 코드의 집합입니다. 이 코드들은 특정 작업을 수행하는 함수, 클래스, 서비스, 또는 프레임워크로 구성되어 있습니다. 개발자들은 이러한 라이브러리를 사용하여 애플리케이션을 더 빠르고 효율적으로 개발할 수 있습니다. 라이브러리는 개발자가 직접 일반적인 문제들을 해결하기 위해 코드를 작성하는 대신, 이미 검증된 코드를 재사용함으로써 개발 시간을 단축시키고 애플리케이션의 안정성을 높일 수 있게 도와줍니다.

라이브러리는 크게 두 가지 유형으로 나뉩니다:

  1. 정적 라이브러리(Static Libraries): 이들은 애플리케이션의 실행 파일에 컴파일 시점에 포함됩니다. 애플리케이션과 함께 하나의 실행 가능한 파일로 묶여 배포되기 때문에, 애플리케이션이 실행될 때 별도의 로딩이 필요 없습니다. 그러나 이 방식은 애플리케이션의 크기를 증가시키고, 라이브러리를 업데이트할 때마다 애플리케이션을 재컴파일해야 하는 단점이 있습니다.
  2. 동적 라이브러리(Dynamic Libraries): 동적 라이브러리는 애플리케이션의 실행 파일과 분리되어 있으며, 애플리케이션이 실행될 때나 필요할 때에만 메모리에 로드됩니다. 이러한 방식은 애플리케이션의 초기 메모리 사용량을 줄이고, 라이브러리를 애플리케이션과 독립적으로 업데이트할 수 있게 해줍니다. 동적 라이브러리는 시스템 리소스를 효율적으로 사용하고, 여러 애플리케이션이 동일한 라이브러리의 인스턴스를 공유할 수 있게 해줍니다.

 

이 글에서 다뤄 볼 것

앱의 성능을 결정하는 두 가지 주요 요소는 실행 시간과 메모리 사용량입니다. 이 두 가지를 최적화하기 위한 효과적인 방법 중 하나는 동적 라이브러리의 사용입니다.

이 문서에서는 동적 라이브러리를 소개하고 정적 라이브러리 대신 동적 라이브러리를 사용하면 이를 사용하는 앱의 파일 크기와 초기 메모리 공간을 모두 줄일 수 있는 방법을 설명합니다. 또한 앱이 런타임에 동적 라이브러리와 함께 작동하기 위해 사용하는 동적 로더 호환성 기능에 대한 개요도 제공합니다.

 

동적 라이브러리란?

동적 라이브러리는 앱의 실행 파일에 포함되지 않고, 앱이 실행될 때나 필요할 때에만 로드되는 외부 라이브러리입니다. 이는 앱의 실행 파일 크기를 줄이고, 초기 메모리 사용량을 감소시키는데 도움이 됩니다. 동적 라이브러리는 앱 실행 시점이 아닌 필요한 시점에만 특정 기능을 로딩하여 메모리를 효율적으로 관리할 수 있게 해줍니다. 이러한 특성으로 인해 동적 라이브러리는 앱의 성능을 향상시키고 업데이트 관리를 용이하게 합니다.

 

동적 라이브러리 vs 정적 라이브러리

정적 라이브러리는 앱이 컴파일될 때 앱의 실행 파일에 포함되어, 런타임에 전체가 메모리에 로드됩니다. 이는 앱의 실행 파일을 무겁게 만들고, 메모리 사용량을 증가시킵니다. 반면, 동적 라이브러리는 앱이 시작될 때나 필요할 때에만 로드되어, 앱의 파일 크기와 초기 메모리 사용량을 줄여줍니다.

 

동적 라이브러리의 장점

  • 파일 크기 및 메모리 사용량 감소: 앱이 필요할 때만 동적 라이브러리를 로드하므로, 앱의 초기 메모리 점유율이 낮아집니다.
  • 업데이트 용이성: 동적 라이브러리는 앱의 실행 파일과 독립적이므로, 라이브러리를 업데이트해도 앱을 재컴파일할 필요가 없습니다.
  • 라이브러리 공유: 여러 앱이 동일한 동적 라이브러리를 공유할 수 있어, 시스템의 전체 메모리 사용량이 감소합니다.

 

정적 라이브러리의 장점

  • 실행 속도: 정적 라이브러리는 애플리케이션과 같이 컴파일되어 하나의 실행 파일로 묶입니다. 이로 인해 런타임에 추가적인 로딩 시간이 필요 없어, 애플리케이션이 동적 라이브러리를 로드하고 링크하는 데 필요한 시간을 절약할 수 있습니다. 결과적으로 애플리케이션의 실행 속도가 빨라질 수 있습니다.
  • 배포의 단순성: 정적 라이브러리를 사용하는 애플리케이션은 모든 필요한 코드가 하나의 실행 파일 내에 포함되기 때문에 배포가 간단합니다. 별도의 라이브러리 파일을 관리하거나 사용자가 추가적으로 설치해야 할 필요가 없습니다. 이는 특히 애플리케이션을 다양한 환경에 배포할 때 유리할 수 있습니다.
  • 의존성 관리: 정적 라이브러리는 컴파일 시점에 애플리케이션에 통합되므로, 런타임에 외부 라이브러리가 누락되거나 호환되지 않는 버전의 문제를 방지할 수 있습니다. 이는 애플리케이션이 예상대로 동작할 것임을 보장합니다.
  • 호환성: 정적 라이브러리를 사용함으로써 개발된 애플리케이션은 해당 라이브러리를 사용하는 다른 애플리케이션과의 라이브러리 버전 충돌 문제에서 자유롭습니다. 각 애플리케이션은 자신만의 라이브러리 버전을 가지고 있으므로, 시스템 레벨에서 다른 애플리케이션과의 의존성 문제를 피할 수 있습니다.
  • 보안: 정적 라이브러리는 애플리케이션 실행 파일 내에 포함되므로, 외부에서 라이브러리를 교체하거나 수정하는 것이 더 어렵습니다. 이는 애플리케이션의 보안을 강화하는 데 도움이 될 수 있습니다.

이러한 장점들로 인해, 정적 라이브러리는 성능 최적화가 중요하거나, 배포 및 실행 환경이 제한적인 경우, 또는 애플리케이션의 안정성과 호환성을 우선시할 때 유용하게 사용됩니다.

 

실행

앱의 기능 대부분은 실행 코드 라이브러리에서 구현됩니다. 정적 링커를 사용하여 앱이 라이브러리와 연결되면 앱이 사용하는 코드가 생성된 실행 파일에 복사됩니다. 정적 링커는 컴파일된 소스 코드(객체 코드)와 라이브러리 코드를 하나의 실행 파일로 수집하여 런타임에 전체가 메모리에 로드됩니다. 앱 실행 파일의 일부가 되는 라이브러리의 종류를 정적 라이브러리라고 합니다. 정적 라이브러리는 객체 파일의 컬렉션 또는 아카이브입니다.


참고: 정적 라이브러리는 정적 아카이브 라이브러리 및 정적 링크 공유 라이브러리라고도 합니다.

앱이 실행되면 앱이 링크된 정적 라이브러리의 코드가 포함된 앱 코드가 앱의 주소 공간에 로드됩니다. 앱에 많은 정적 라이브러리를 연결하면 앱 실행 파일이 대용량으로 생성됩니다. 그림 1은 정적 라이브러리로 구현된 기능을 사용하는 앱의 메모리 사용량을 보여줍니다. 실행 파일이 큰 애플리케이션은 실행 시간이 느려지고 메모리 사용량이 많아집니다. 또한 정적 라이브러리가 업데이트되면 해당 클라이언트 앱은 개선된 기능의 혜택을 받지 못합니다. 개선된 기능에 액세스하려면 앱 개발자가 앱의 객체 파일을 새 버전의 라이브러리와 연결해야 합니다. 그리고 앱 사용자는 앱 사본을 최신 버전으로 교체해야 합니다. 따라서 정적 라이브러리가 제공하는 최신 기능으로 앱을 최신 상태로 유지하려면 개발자와 최종 사용자 모두의 작업이 필요합니다.

더 나은 접근 방식은 앱이 실행 시 또는 런타임에 실제로 필요할 때 주소 공간에 코드를 로드하는 것입니다. 이러한 유연성을 제공하는 라이브러리 유형을 동적 라이브러리라고 합니다. 동적 라이브러리는 클라이언트 앱에 정적으로 링크되지 않으며 실행 파일의 일부가 되지 않습니다. 대신 동적 라이브러리는 앱이 시작될 때 또는 앱이 실행될 때 앱에 로드(및 링크)될 수 있습니다.

참고: 동적 라이브러리는 동적 공유 라이브러리, 공유 개체 또는 동적 링크 라이브러리라고도 합니다.

그림 2는 일부 기능을 정적 라이브러리 대신 동적 라이브러리로 구현하면 앱 실행 후 앱에서 사용하는 메모리가 어떻게 줄어드는지 보여줍니다.

동적 라이브러리를 사용하면 라이브러리에 대한 링크가 정적이 아닌 동적이므로 프로그램이 사용하는 라이브러리가 자동으로 개선되는 이점을 누릴 수 있습니다. 즉, 앱 개발자가 앱을 다시 컴파일할 필요 없이 클라이언트 앱의 기능을 개선하고 확장할 수 있습니다. OS X의 모든 시스템 라이브러리는 동적 라이브러리이므로 OS X용으로 작성된 앱은 이 기능의 이점을 누릴 수 있습니다. 이것이 바로 Carbon 또는 Cocoa 기술을 사용하는 앱이 OS X의 개선된 기능을 활용하는 방법입니다.

동적 라이브러리가 제공하는 또 다른 장점은 로드될 때 초기화할 수 있고 클라이언트 앱이 정상적으로 종료될 때 정리 작업을 수행할 수 있다는 점입니다. 정적 라이브러리에는 이 기능이 없습니다. 자세한 내용은 모듈 이니셜라이저 및 파이널라이저를 참조하세요.

동적 라이브러리를 개발할 때 개발자가 염두에 두어야 할 한 가지 문제는 라이브러리가 업데이트될 때 클라이언트 앱과의 호환성을 유지하는 것입니다. 라이브러리는 클라이언트 앱 개발자가 모르는 사이에 업데이트될 수 있으므로 앱은 코드를 변경하지 않고도 새 버전의 라이브러리를 사용할 수 있어야 합니다. 이를 위해 라이브러리의 API는 변경되지 않아야 합니다. 그러나 개선 사항으로 인해 API 변경이 필요한 경우가 있습니다. 이 경우 클라이언트 앱이 제대로 실행되려면 라이브러리의 이전 버전이 사용자의 컴퓨터에 남아 있어야 합니다. 동적 라이브러리 설계 가이드라인에서는 동적 라이브러리가 발전함에 따라 클라이언트 앱과의 호환성을 관리하는 주제를 살펴봅니다.

동적 라이브러리 사용 방법

동적 라이브러리를 사용하는 과정을 요약하자면 다음과 같습니다:

  1. 라이브러리 로드: 앱이 실행될 때 OS X 커널은 동적 로더를 사용하여 필요한 동적 라이브러리를 로드합니다.
  2. 심볼 확인: 앱이 실제로 사용하는 함수나 변수 등의 심볼만 로더가 확인하고, 나머지는 필요할 때까지 로드하지 않습니다.
  3. 라이브러리와의 상호 작용: 앱은 라이브러리 내의 함수나 변수를 사용할 수 있습니다. 필요한 경우, 앱은 런타임에 추가적인 라이브러리를 로드할 수도 있습니다.


1. 라이브러리 로드: 앱이 실행되면 OS X 커널은 앱의 코드와 데이터를 새 프로세스의 주소 공간에 로드합니다. 또한 커널은 동적 로더( /usr/lib/dyld )를 프로세스에 로드하고 제어권을 프로세스에 전달합니다. 그런 다음 동적 로더는 앱의 종속 라이브러리를 로드합니다. 앱이 링크된 동적 라이브러리입니다. 정적 링커는 앱이 링크될 때 각 종속 라이브러리의 파일명을 기록합니다. 이 파일 이름을 동적 라이브러리의 설치 이름이라고 합니다. 동적 로더는 앱의 종속 라이브러리 설치 이름을 사용하여 파일 시스템에서 해당 라이브러리를 찾습니다. 동적 로더가 실행 시 앱의 모든 종속 라이브러리를 찾지 못하거나 라이브러리 중 앱과 호환되지 않는 라이브러리가 있으면 실행 프로세스가 중단됩니다.


2. 심볼 확인: 동적 로더는 실행 프로세스 중에 앱이 실제로 사용하는 정의되지 않은 외부 심볼만 확인합니다. 다른 심볼은 앱이 사용할 때까지 해결되지 않은 상태로 유지됩니다. 앱이 실행될 때 동적 로더가 진행하는 프로세스에 대한 자세한 내용은 Mach-O 프로그래밍 주제에서 "Mach-O 파일 실행하기"를 참조하세요.

3. 상호작용: 동적 로더는 실행 시 동적 라이브러리를 자동으로 로드하는 것 외에도 앱의 요청에 따라 런타임에 동적 라이브러리를 로드합니다. 즉, 앱이 실행될 때 동적 라이브러리를 로드할 필요가 없는 경우 개발자는 앱의 객체 파일을 동적 라이브러리와 연결하지 않고 대신 동적 라이브러리가 필요한 부분에만 동적 라이브러리를 로드하도록 선택할 수 있습니다. 이러한 방식으로 동적 라이브러리를 사용하면 실행 프로세스가 빨라집니다. 런타임에 로드되는 동적 라이브러리를 동적으로 로드된 라이브러리라고 합니다. 런타임에 라이브러리를 로드하기 위해 앱은 실행 중인 플랫폼의 동적 로더와 상호 작용하는 함수를 사용할 수 있습니다.



참고: 클라이언트와 동적 라이브러리의 대상 아키텍처가 동일해야 합니다. 그렇지 않으면 동적 로더가 라이브러리를 로드하지 않습니다.

플랫폼마다 동적 로더를 구현하는 방식이 다릅니다. 또한 플랫폼 간에 코드를 포팅하기 어렵게 만드는 사용자 지정 동적 코드 로딩 인터페이스가 있을 수도 있습니다. 예를 들어, 앱을 UNIX에서 Linux로 쉽게 포팅하기 위해 Jorge Acereda와 Peter O'Gorman은 동적 로더 호환성(DLC) 기능을 개발했습니다. 이 기능은 개발자가 앱에서 동적 라이브러리를 사용할 수 있는 표준적이고 이식 가능한 방법을 제공합니다.

DLC 함수는 /usr/include/dlfcn.h에 선언되어 있으며 5가지가 있습니다:

dlopen(3) OS X 개발자 도구 매뉴얼 페이지: 동적 라이브러리를 엽니다. 앱은 라이브러리의 내보낸 심볼을 사용하기 전에 이 함수를 호출합니다. 현재 프로세스에서 동적 라이브러리를 열지 않은 경우 라이브러리가 프로세스의 주소 공간에 로드됩니다. 이 함수는 dlsym 및 dlclose 호출에서 열린 라이브러리를 참조하는 데 사용되는 핸들을 반환합니다. 이 핸들을 동적 라이브러리 핸들이라고 합니다. 이 함수는 현재 프로세스가 특정 동적 라이브러리를 열기 위해 dlopen을 사용한 횟수를 나타내는 참조 횟수를 유지합니다.
dlsym(3) OS X 개발자 도구 매뉴얼 페이지: 동적으로 로드된 라이브러리에서 내보낸 심볼의 주소를 반환합니다. 앱은 dlopen 호출을 통해 라이브러리에 대한 핸들을 얻은 후 이 함수를 호출합니다. dlsym 함수는 dlopen에서 반환한 핸들 또는 심볼 검색 범위와 심볼 이름을 지정하는 상수를 매개변수로 받습니다.
dladdr(3) OS X 개발자 도구 매뉴얼 페이지: 제공된 주소에 대한 정보를 반환합니다. 주소가 앱의 주소 공간 내에 동적으로 로드된 라이브러리에 해당하는 경우 이 함수는 주소에 대한 정보를 반환합니다. 이 정보는 동적 라이브러리의 경로명, 라이브러리의 기본 주소, 제공된 주소에서 가장 가까운 심볼의 주소 및 값을 캡슐화하는 Dl_info 구조로 반환됩니다. 제공된 주소에서 동적 라이브러리를 찾을 수 없으면 함수는 아무 정보도 반환하지 않습니다.
dlclose(3) OS X 개발자 도구 매뉴얼 페이지: 동적으로 로드된 라이브러리를 닫습니다. 이 함수는 dlopen이 반환한 핸들을 매개변수로 받습니다. 해당 핸들의 참조 횟수가 0에 도달하면 라이브러리는 현재 프로세스의 주소 공간에서 언로드됩니다.
dlerror(3) OS X 개발자 도구 매뉴얼 페이지: dlopen, dlsym 또는 dlclose에 대한 마지막 호출에서 발생한 오류 조건을 설명하는 문자열을 반환합니다.
DLC 함수에 대한 자세한 내용은 OS X ABI 동적 로더 참조를 참조하십시오.

 

 

Overview of Dynamic Libraries

Overview of Dynamic Libraries Two important factors that determine the performance of apps are their launch times and their memory footprints. Reducing the size of an app’s executable file and minimizing its use of memory once it’s launched make the ap

developer.apple.com