기존 UIKit 앱에 SwiftUI 통합하기: 기존 코드베이스에 SwiftUI gradual adoption.

작성일 :

기존 UIKit 앱에 SwiftUI 통합하기: 기존 코드베이스에 SwiftUI gradual adoption

SwiftUI는 Apple이 제공하는 최신 사용자 인터페이스 프레임워크로, 단순하고 선언적인 방식으로 UI를 구성할 수 있습니다. 많은 개발자가 SwiftUI의 잠재력을 실험하고 있지만, 기존에 대규모 UIKit 앱을 가지고 있는 경우 SwiftUI를 즉시 전환하기 어려울 수 있습니다. 이 글에서는 SwiftUI를 기존 UIKit 앱에 통합하는 법을 단계별로 안내드립니다.

SwiftUI와 UIKit 간의 브리징 이해하기

SwiftUI를 기존 UIKit 프로젝트에 통합하기 위한 첫 번째 단계는 두 프레임워크 간의 상호 작용을 이해하는 것입니다. SwiftUI 뷰를 UIKit에서 사용하거나, UIKit 뷰를 SwiftUI에서 사용하는 두 가지 방법이 있습니다.

SwiftUI를 UIKit에 포함하기

SwiftUI 뷰를 기존 UIKit 앱에 추가하려면 UIHostingController를 사용합니다. UIHostingController는 SwiftUI 뷰를 포함할 수 있는 UIKit 뷰 컨트롤러입니다. 아래 예제를 통해 간단히 설명하겠습니다.

swift
import SwiftUI
import UIKit

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // SwiftUI 뷰 생성
        let swiftUIView = Text("Hello, SwiftUI!")

        // UIHostingController로 SwiftUI 뷰 감싸기
        let hostingController = UIHostingController(rootView: swiftUIView)

        // UIHostingController를 자식 뷰 컨트롤러로 추가
        addChild(hostingController)
        view.addSubview(hostingController.view)

        // Auto Layout으로 위치 지정
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            hostingController.view.topAnchor.constraint(equalTo: view.topAnchor),
            hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])

        hostingController.didMove(toParent: self)
    }
}

위 예제에서는 SwiftUI의 Text 뷰를 생성하고 UIHostingController를 사용하여 이를 포함한 후, 자식 뷰 컨트롤러로 추가합니다. 이는 SwiftUI 뷰를 UIKit의 뷰 계층 구조에 통합할 수 있는 기본적인 방법입니다.

UIKit을 SwiftUI로 래핑하기

SwiftUI에서 기존 UIKit 뷰를 사용해야 할 경우, UIViewRepresentable 프로토콜을 준수하여 래핑할 수 있습니다. 예를 들어, UIImageView를 SwiftUI에서 사용하고자 하는 경우 다음과 같이 구현할 수 있습니다.

swift
import SwiftUI
import UIKit

struct UIKitImageView: UIViewRepresentable {
    let imageName: String

    func makeUIView(context: Context) -> UIImageView {
        return UIImageView()
    }

    func updateUIView(_ uiView: UIImageView, context: Context) {
        uiView.image = UIImage(named: imageName)
    }
}

위의 UIKitImageViewUIViewRepresentable 프로토콜을 구현하여, SwiftUI에서 UIImageView를 사용할 수 있게 합니다. 이후 SwiftUI 코드에서 다음과 같이 사용할 수 있습니다.

swift
struct ContentView: View {
    var body: some View {
        VStack {
            Text("UIKit 이미지 뷰 사용 예제")
            UIKitImageView(imageName: "example_image")
        }
    }
}

점진적 통합: 단계별 접근법

기존 UIKit 앱에 SwiftUI를 통합하는 가장 효율적인 방법은 점진적인 접근법을 취하는 것입니다. 이를 통해 처음부터 모든 코드를 변경하지 않고, 일부 화면이나 기능부터 시작하며 점진적으로 SwiftUI로 전환할 수 있습니다.

1단계: 개별 화면 또는 컴포넌트 변경

가장 간단한 방식은 앱의 특정 화면이나 컴포넌트를 SwiftUI로 변경하는 것입니다. 예를 들어, 앱의 설정 화면이나 메인 화면의 일부를 먼저 SwiftUI로 전환할 수 있습니다. 이를 통해 SwiftUI의 장점을 체감하면서도 앱의 기존 동작을 크게 변경하지 않습니다.

2단계: 뷰 컨트롤러 단위로 전환

한두 개의 화면이나 컴포넌트를 성공적으로 전환한 후에는 전체 뷰 컨트롤러를 SwiftUI로 전환할 수 있습니다. 이는 기존 UIKit 뷰 컨트롤러를 UIHostingController로 대체하는 방식으로 이루어질 수 있습니다. 이렇게 하면 해당 뷰 컨트롤러 내의 뷰 로직을 더 간단하게 만들 수 있습니다.

3단계: 앱 전반에 걸쳐 적용

마지막 단계에서는 앱의 대부분 또는 전체를 SwiftUI로 전환합니다. 이 과정에서는 다양한 SwiftUI 기능과 레이아웃을 사용하여 앱의 모든 화면을 재작성하게 됩니다. 이 단계에 도달하기 위해서는 충분한 테스트와 업데이트 과정이 필요합니다.

효율적인 테스트와 디버깅

SwiftUI와 UIKit을 함께 사용할 때 디버깅이나 테스트가 어려워질 수 있습니다. 이를 해결하기 위해 다음과 같은 방법을 활용할 수 있습니다.

프리뷰 사용하기

Xcode의 프리뷰 기능을 사용하면 SwiftUI 뷰를 실시간으로 확인하고 디버깅할 수 있습니다. 이를 통해 코드를 작성하면서 즉시 결과를 확인할 수 있습니다.

swift
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

테스트 코드 작성하기

SwiftUI와 UIKit 통합 후, 일반적인 유닛 테스트와 UI 테스트를 작성하여 주요 기능이 정상적으로 작동하는지 확인해야 합니다. 예를 들어, SwiftUI 뷰에서 특정 동작을 테스트하려면 별도의 유닛 테스트 케이스를 작성할 수 있습니다.

swift
import XCTest
@testable import YourApp

class ContentViewTests: XCTestCase {
    func testExample() {
        // Your test code here
    }
}

결론

SwiftUI는 효율적이고 모던한 사용자 인터페이스 개발 도구를 제공합니다. 기존 UIKit 앱에 SwiftUI를 점진적으로 통합함으로써 새로운 기능을 빠르게 도입하고, 코드베이스를 최신화할 수 있습니다. 점진적 접근법을 통해 앱의 안정성을 유지하면서도 SwiftUI의 이점을 최대한 활용할 수 있습니다.