보통 프로젝트를 새로 만들면 하나의 프로젝트에 모든 코드와 리소스를 때려박아서 개발을 진행한다.

(내가 그럼 ㅇㅇ)

 

하지만 프로젝트 내에 서브 프로젝트를 만들어 소스 코드를 추가하고,

오직 상위 프로젝트에서만 서브 프로젝트의 코드를 알도록 Build Phase -> Dependencies를 이용할 수 있다.

 

프로젝트, 서브 프로젝트

프로젝트내에 서브 프로젝트를 만들어서 관리할 수 있다.

이는 해당 프로젝트의 코드가 많아지면 서브 프로젝트를 만들어 관리하는 것이다.

 

예제 : http://minsone.github.io/ios/mac/ios-framework-part-2-project-subproject-dependencies

 

[iOS][Xcode] Framework Part 2 : 프로젝트, 서브 프로젝트, Dependencies, 그리고 Static, Dynamic Framework

서론 프로젝트를 만들면 해당 프로젝트 내에 서브 프로젝트를 만드는 것에 이야기를 들어본적이 없었습니다. 프로젝트 하나에 모든 코드와 리소스가 다 들어가도록 개발을 했기 때문입니다. 하

minsone.github.io

 

  1. SampleApp 프로젝트를 생성한다.
  2. SampleApp 프로젝트 내에 Service 서브 프로젝트를 생성한다.
  3. SampleApp's General -> Frameworks, Libraries and Embedded Content에 Service.framework 추가

Framework를 만들면 기본으로 Dynamic Framework로 지정된다. 즉, 위의 방식으로 서브 프로젝트들을 많이 만들게되면 SampleApp 프로젝트에는 많은 Dynamic Framework가 임베딩된다.

 

그것이 맞는 방법일까?

그렇다면 Dynamic Framework 프로젝트를 만들고, 해당 서브 프로젝트로 Static Framework를 만들면 어떻게 될까?

-> Dynamic Library에 Static Library의 코드가 복사되기 때문에, 서브 프로젝트는 많아질 수 있어도, Dynamic Framework는 적은 숫자로 유지된다.

 

  1. Service 서브 프로젝트 내에AppLogService 서브 프로젝트 생성
  2. AppLogService -> Build Settings -> Linking -> Mach-O Type을 Static Library로 변경
  3. Service 서브 프로젝트의 Build Phases -> Dependencies 에서 AppLogService Framework 추가

Service 프로젝트는 Dynamic Framework이며, AppLogService 프로젝트는 Static Framework이다.

그러므로 Service 프로젝트가 빌드되면서 Service Dynamic Library에 AppLogService Static Library가 복사될 것이다.

 

코드를 작성해주자

 

AppLogService

 

Service

SampleApp

 

이후 빌드하면, 생성된 Service Framework의 Dynamic Library를 확인하자.

  1. Service.project의 Product -> Service.framework 파일 선택후, 오른쪽에서 Pull Path를 얻는다.
  2. 터미널에서 수행
  3. Service library의 Symbol 목록을 확인하다보면 AppLogService의 코드가 있는 것을 확인할 수 있다.
    이는 Static Linker가 AppLogService Static Library를 Service Dynamic Library에 복사함을 확인할 수 있다.


 

그렇다면, Service 프로젝트는 서브 프로젝트만 관리하는 프로젝트인 경우

만약 Service 프로젝트는 서브 프로젝트들만 관리하는 프로젝트이고, 사용할 때는 AppLogService 프로젝트의 Service를 호출하여 사용한다면 어떻게 될까?

즉, Service 프로젝트에서 AppLogService 프로젝트를 호출하는 코드가 하나도 없고, SampleApp 프로젝트에서만 호출한다면?

 

  1. Service 프로젝트의 Service.swift 코드 제거
  2. SampleAPp의 AppDelegate에서 다음과 같이 코드 작성


  3. SampleApp의 Product->SampleApp.app 파일 선택 후, 오른쪽에서 Pull Path 확인



  4. 터미널로 명령 실행
    SampleApp의 바이너리의 Symbol 목록에서 AppLogService Static Library 코드가 복사된 것을 확인할 수 있다.따라서 Linker가 Static Library를 사용하는 곳에 코드를 복사함을 알 수 있다.

    이야기를 확장해보면, AppLogService의 Service 클래스의 Bundle 위치는 Service 프레임워크가 아니라, SampleApp이 된다.
    만약에 해당 클래스가 Storyboard나 nib를 사용하는 ViewController의 경우, 코드가 있는 Bundle 위치와 Storyboard나 nib가 있는 Bundle의 위치가 달라진다.
    개발자가 예상하지 못한 곳에 코드가 있으면 안되기 때문에, Static Library를 강제로 우리가 원하는 곳에 복사되도록 해야 한다.
  5. 즉, 상위 Dynamic Framework에서 더미로 코드를 사용해줘야 한다.


AppLogService를 SampleAppdㅔ서 import시, Service.AppLogService로 사용할 수 없을까?

SampleApp에서 AppLogService를 import Service.AppLogService와 같은 방법으로 AppLogService 프레임워크를 호출하고 싶을 수 있다.

왜냐하면 AppLogService 프로젝트는 AppLog로 바꾸어서 Service 프로젝트의 AppLog를 담당하는 서비스라고 생각하고 작업할 수 있다.

SampleApp에서 import Service.AppLogService로 import하면, No such module 'Service.AppLogService' 에러 발생

하지만 UIKit의 UIViewController는 import UIKit.UIViewController로 사용 가능하다.

 

따라서 modulemap을 사용하여 원하는 기능을 구현해보자.

 

  1. Service 프로젝트 내에 빈 module.modulemap 을 생성
  2. module.modulemap 파일 내에 코드 추가


  3. Service 프로젝트의 Build Settings -> Packaging -> Module Map File에 코드 추가


  4. Service 프로젝트의 Service.swift 파일에 코드 추가


  5. module.modulemap 파일에 코드 수정


  6. SampleApp의 AppDelegate에 코드 작성


 

 

Framework

  • 코드(클래스, 프로토콜, 컴포넌트)/리소스들을 모듈화한 모음
  • 사용하는 주체와 IoC(제어의 역전) 관계
  • 특정 개념들의 추상화를 제공하는 여러 클래스나 컴포넌트로 구성되어 있다.
  • 컴포넌트들은 재사용 가능
  • 고수준에서 조작 가능
  • iOS에서 제공하는 Cocoa Touch 프레임워크가 존재한다.

Library

  • Application이 연결할 수 있는, 패키징된 객체 파일들의 모음
  • 사용하는 주체가 기능을 요청하며 사용한다.
  • 즉, 개발자가 만든 클래스에서 직접 호출해서 사용

 

차이점

제어의 흐름에 대한 주도성이 누구에게 있는가에 달렸다.

즉, Application의 Flow를 누가 쥐고 있는가?

  • 프레임워크는 전체적인 흐름을 스스로가 쥐고 있고, 사용자는 그 안에서 코드를 넣는다.
  • 라이브러리는 사용자가 전체적인 흐름을 만들고, 라이브러리를 필요한 곳에서 가져다 쓴다.

다시 말해, "라이브러리는 라이브러리를 사용하는 측에 주도성이 있고, 프레임워크는 그 틀안에 주도성을 갖고 있다" 고 볼 수 있다.

 

 

제어의 역전(IoC)

어떠한 일을 하도록 만들어진 프레임워크에 제어의 권한을 넘김으로써, 클라이언트 코드가 신경쓰는 것을 줄이는 전략

내가 라이브러리의 메소드를 사용하는 것은 쉽게 이해할 수 있다.

결국, 프레임워크의 메소드가 사용자의 코드를 호출한다... 인데

어떻게 프레임워크가 내 메소드를 호출하는가?

  1. 쉬운 방법 : 프레임워크의 event, delegate에 나의 메소드를 등록시키는 것
  2. DI : 프레임워크에 정의된 protocol 을 나의 코드에서 구현, 상속한 후에 프레임워크에 넘겨준다.
    1. 이는 객체를 프레임워크에 주입하는 것이다.

 

 

 


모듈화

하나의 프로젝트에 모든 기능들을 넣어서 개발하다보면 어느 순간 단점들이 생긴다.

  1. 컴파일 속도
  2. 하나의 코드를 고쳤을 때, 발생하는 사이드 이펙트

따라서 모듈화를 통해 결합도를 낮추고, 응집도를 높히는 작업

 

출처 : http://minsone.github.io/ios/mac/ios-enterprise-app-configuration-1

  • Module : 라이브러리를 가진 프로젝트로, 특정 역할(네트워크, Custom UI 등)을 수행, 외부에는 정의한 protocol을 통해 호출가능하게 한다. 
  • Module Package : 모듈들을 관리, 모듈들의 결합으로 기능을 확장
  • Service : 특정 도메인, 서비스를 관리하는 프로젝트
  • Common Service : 인증, 보안 등 다른 서비스에서 공통으로 사용되는 서비스
  • Main Service : 각 서비스들을 호출 및 연결
  • Application : UIApplication에서 제공하는 기능을 받아서 처리

 

장점

  1. 메인 프로젝트에서의 라이브러리 의존성이 사라짐
  2. Widget, Watch 등을 개발할 때, 모듈을 쉽게 사용할 수 있음
  3. Clean Build시에는 빌드가 느리지만, Rebuild시에는 해당 수정 부분만 컴파일되므로 훨씬 빨라짐

 

 

 

 

 

 

 

 

 

 

 

 

 

+ Recent posts