빌드 메시지 분석해서 빌드 시간 개선하기
프로젝트 빌드 메시지를 분석해서 빌드 시간 개선해보고자 함.
예제 파일
목차
- 빌드 메시지 확인하는 방법
- 프로젝트 구조 및 Home Package 확인하기
- 1차 문제 분석 및 개선 방향성 잡기
- Player Package 확인하기
- Player Package 개선하기
- 1차 개선 결과
- 2차 문제 분석 및 개선 방향성 잡기
- 문제 코드 영역 확인하기
- HomeDetail을 HomeDetailInterface 모듈로 분리하기
- 개선한 Home Package
- 2차 개선 결과
- 실행 가능한 앱(데모 앱) 구성하기
- 실행 가능한 앱(데모 앱) 빌드 결과
- 결과 한눈에 확인하기
- 1차 개선
- 2차 개선
- 앱 빌드
빌드 메시지 확인하는 방법
좌측 인스펙터에 빨간색 이미지 영역 클릭
빌드 이미지를 보면 순차대로 어떤 것들이 먼저 처리되는지 확인할 수 있음
- 아래 그래프는 Home을 빌드했을 때의 결과
프로젝트 구조 및 Home Package 확인하기
좌측은 Package 구조, 우측은 Home에 Packge에 작성된 코드
1차 문제 분석 및 개선 방향성 잡기
문제 인식
- Home에는 Player를 사용하고 있지 않지만, VideoPlayer, MusicPlayer가 함께 빌드되어서 불필요한 빌드시간이 존재
개선 방향성
- Player에서 VideoPlayer의 인터페이스에 의존하게 변경
- HomeDetail에서 MusicPlayer의 빌드를 직접하지 않을 수 있음
Player Package 확인하기
Player 패키지는 아래처럼 되어있음
- VideoPlayer가 MusicPlayer에 의존
// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Player",
products: [
.library(
name: "MusicPlayer",
targets: ["MusicPlayer"]
),
.library(
name: "VideoPlayer",
targets: ["VideoPlayer"]
),
],
targets: [
.target(
name: "MusicPlayer"
),
.target(
name: "VideoPlayer",
dependencies: [
"MusicPlayer"
]
)
]
)
Player Package 개선하기
인터페이스와 실제 구현체를 분리하고자 함
- 인터페이스를 별도로 분리해 실제 구현 객체에 의존하지 않도록 변경
- 이에 따라 VideoPlayer에서 다른 의존성 또한 빌드되지 않도록 하여 빌드 속도 개선
Package 개선한 코드
- 인터페이스에 의존하는 형태로 수정
// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Player",
products: [
.library(
name: "MusicPlayer",
targets: ["MusicPlayer"]
),
.library(
name: "VideoPlayerInterface",
targets: ["VideoPlayerInterface"]
),
.library(
name: "VideoPlayer",
targets: ["VideoPlayer"]
),
],
targets: [
.target(
name: "MusicPlayer"
),
.target(
name: "VideoPlayerInterface"
),
.target(
name: "VideoPlayer",
dependencies: [
"MusicPlayer",
"VideoPlayerInterface"
]
)
]
)
1차 개선 결과
- 인터페이스를 분리함으로써 불필요하게 빌드되던 MusicPlayer를 제거할 수 있었음.
- 따라서 약간의 빌드 시간 감소
2차 문제 분석 및 개선 방향성 잡기
문제 인식
- Home에는 Utils Package에 대한 의존성이 없지만 HomeDetail로 인해서 불필요한 빌드시간이 추가됨
개선 방향성
- HomeDetail도 인터페이스로 분리
- 이점: Utils의 빌드를 피할 수 있음
Home Package 확인하기
Home이 HomeDetail에 의존하고 있음
- HomeDetail은 다양한 모듈들에 의존하는 상황
// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Home",
platforms: [.iOS(.v15)],
products: [
.library(
name: "Home",
targets: ["Home"]
),
.library(
name: "HomeDetail",
targets: ["HomeDetail"]
),
],
dependencies: [
.package(path: "../Player"),
.package(path: "../Utils")
],
targets: [
.target(
name: "Home",
dependencies: [
"Model",
"HomeDetail"
]
),
.target(
name: "HomeDetail",
dependencies: [
"Model",
.product(name: "VideoPlayerInterface", package: "Player"),
.product(name: "KeyboardReadable", package: "Utils"),
.product(name: "SceneDelegateReadable", package: "Utils"),
.product(name: "AppDelegateReadable", package: "Utils")
]
),
.target(
name: "Model"
)
]
)
문제 코드 영역 확인하기
예제는 VIPER를 사용하고 있고, HomeRouter 코드 확인
- HomeDetail을 import하여 Builder를 직접 호출하고 있음
- 따라서 HomeDetail에 필요한 의존성도 모두 함께 빌드가 됨.
import UIKit
import HomeDetail
protocol HomeRouterInterface {
var navigationContoller: UINavigationController? { get }
func pushToDetail()
}
class HomeRouter: HomeRouterInterface {
var navigationContoller: UINavigationController?
func pushToDetail() {
let view = HomeDetailBuilder().build()
navigationContoller?.pushViewController(view, animated: true)
}
}
HomeDetail을 HomeDetailInterface 모듈로 분리하기
HomeInterface 추가
- 외부에 노출할 필요한 인터페이스를 분리
import UIKit
public protocol HomeDetailInterface {
func build() -> UIViewController
}
HomeDetail 코드 수정
- HomeDetail이 HomeDetailInterface에 의존하도록 변경
import UIKit
import HomeDetailInterface
public struct HomeDetailBuilder: HomeDetailInterface {
public init() {}
public func build() -> UIViewController {
let router = HomeDetailRouter()
let interactor = HomeDetailInteractor()
let presenter = HomeDetailPresenter(
interactor: interactor,
router: router
)
interactor.output = presenter
let view = HomeDetailViewController(presenter: presenter)
router.viewContoller = view
return view
}
}
개선한 Home Package
Home도 동일하게 Interface로 분리
- 외부에 노출할 필요한 인터페이스를 분리
// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Home",
platforms: [.iOS(.v15)],
products: [
.library(
name: "Home",
targets: ["Home"]
),
.library(
name: "HomeInterface",
targets: ["HomeInterface"]
),
.library(
name: "HomeDetail",
targets: ["HomeDetail"]
),
.library(
name: "HomeDetailInterface",
targets: ["HomeDetailInterface"]
),
],
dependencies: [
.package(path: "../Player"),
.package(path: "../Utils")
],
targets: [
.target(
name: "HomeInterface"
),
.target(
name: "Home",
dependencies: [
"Model",
"HomeDetailInterface",
"HomeInterface"
]
),
.target(
name: "HomeDetailInterface"
),
.target(
name: "HomeDetail",
dependencies: [
"Model",
"HomeDetailInterface",
.product(name: "VideoPlayerInterface", package: "Player"),
.product(name: "KeyboardReadable", package: "Utils"),
.product(name: "SceneDelegateReadable", package: "Utils"),
.product(name: "AppDelegateReadable", package: "Utils")
]
),
.target(
name: "Model"
)
]
)
2차 개선 결과
인터페이스를 분리함으로써 HomeDetail에 걸린 의존성을 빌드하지 않음
- 따라서 약간의 빌드 시간 감소
실행 가능한 앱(데모 앱) 구성하기
실행 가능한 앱을 구성하기 위해서는 세부 모듈은 Interface에 의존하고 있더라도 앱을 실행하기 위해서는 구현체를 필요로 함
- 앱을 실행하기 위해서는 구현체는 반드시 한번은 빌드가 되어야 해서 구현체를 가져도 상관없음
- 즉, 작은 모듈에서는 인터페이스에 의존하여 개발하고, 데모 앱을 만들때는 구현체를 모두 포함시켜 빌드시킴
import UIKit
import VideoPlayerInterface
import VideoPlayer
import Home
import HomeInterface
import HomeDetailInterface
import HomeDetail
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.backgroundColor = .systemBackground
let components = RootComponents()
let builder: HomeBuilderInterface = HomeBuilder(
dependency: components
)
let rootViewController = builder.build()
window?.rootViewController = rootViewController
window?.makeKeyAndVisible()
}
}
final class RootComponents: HomeBuilderDepedency {
lazy var homeDetail: HomeDetailInterface = {
HomeDetailBuilder(dependency: HomeDetailDependency())
}()
}
final class HomeDetailDependency: HomeDetailBuilderDepedency {
lazy var videoPlayer: VideoPlayerInterface = {
VideoPlayer()
}()
}
실행 가능한 앱(데모 앱) 빌드 결과
구현체를 포함하여 필요한 모듈을 모두 빌드함
- 따라서 필요한 모든 모듈을 빌드하기에 시간이 더 걸림
결과 한눈에 비교하기
1차 개선
- Player패키지를 Interface와 분리하여 HomeDetail 개선
2차 개선
- HomeDetail도 Interface에 의존하게 만들어 Home 수정
- 해당 이미지엔 영향을 주진 않지만 Home도 외부 제공을 위해 인터페이스로 분리
앱 빌드
- 데모 앱 혹은 실행가능한 앱을 위한 빌드
'project > 개발 업무' 카테고리의 다른 글
Combine을 활용해 로그인 상태 관리 기능 구현 (0) | 2024.11.27 |
---|---|
멀티캐스트 딜리게이트 패턴 활용한 로그인 상태 관리 구현 (0) | 2024.11.27 |
UICollectionView Crashes on iOS 18 with Xcode 16: Troubleshooting Guide (0) | 2024.11.22 |
Combine ReadOnly Publisher (0) | 2024.11.20 |
SwiftUI로 Placeholder가 존재하는 TextField 설계 팁 (UIKit호환) (1) | 2024.09.29 |