ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [iOS, macOS] StoryBoard 없이 코드 베이스 UI 구성하기
    Apple🍎 2025. 4. 22. 20:54

     

    📱 iOS vs 🖥 macOS – 코드베이스 UI를 위한 프로젝트 세팅 비교

    UIKit과 AppKit, 두 플랫폼 모두 코드로 UI를 작성하고 싶을 때가 있습니다.
    그런데 둘 다 Xcode에서는 Storyboard나 SwiftUI를 기본으로 요구하죠.
    오늘은 iOS와 macOS에서 스토리보드 없이 코드만으로 UI를 구성하기 위한 초기 세팅 방법을 비교해봅니다.

    🧭 목차

    1. 왜 스토리보드를 쓰지 않나?
    2. iOS (UIKit) 프로젝트 세팅
    3. macOS (AppKit) 프로젝트 세팅
    4. 주요 차이점 요약
    5. 마무리

    1. 왜 스토리보드를 쓰지 않나?

    • 유지보수가 어렵고 충돌 위험이 큼
    • 협업 시 Git conflict 빈번
    • 복잡한 UI는 코드가 더 명확할 수 있음
    • 재사용성과 확장성이 낮음

    그래서 많은 개발자들이 코드로 UI를 직접 작성하고 싶어하죠.

    2. 📱 iOS: UIKit 기반 프로젝트 세팅

    ✅ 기본 흐름

    1. Xcode에서 iOS 프로젝트 생성 (Interface는 Storyboard 선택해도 OK)
    2. Main.storyboard 삭제
    3. Info.plist 수정 → Main storyboard file base name 항목 삭제
    4. SceneDelegate.swift 수정

    ✅ 코드 예시

    SceneDelegate.swift

    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
        var window: UIWindow?
    
        func scene(_ scene: UIScene, willConnectTo session: ..., options: ...) {
            guard let windowScene = scene as? UIWindowScene else { return }
    
            window = UIWindow(windowScene: windowScene)
            window?.rootViewController = ViewController() // 코드로 만든 VC
            window?.makeKeyAndVisible()
        }
    }
    

    ViewController.swift

    class ViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .white
    
            let label = UILabel()
            label.text = "Hello UIKit"
            label.textAlignment = .center
            label.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
            view.addSubview(label)
        }
    }
    

     

    3. 🖥 macOS: AppKit 기반 프로젝트 세팅

    ✅ 기본 흐름

    1. Xcode에서 macOS App 생성 (Interface: Storyboard 선택해도 OK)
    2. Main.storyboard 삭제
    3. Info.plist에서 Main storyboard와 Application Scene Manifest 제거
    4. AppDelegate.swift에서 @main 제거
    5. 새 파일 main.swift 추가 → 진입점 지정
    6. NSWindow, NSViewController를 코드로 생성

    ✅ 코드 예시

    main.swift

    import Cocoa
    
    let app = NSApplication.shared
    let delegate = AppDelegate()
    app.delegate = delegate
    
    _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

    AppDelegate.swift

    class AppDelegate: NSObject, NSApplicationDelegate {
    
        var window: NSWindow!
    
        func applicationDidFinishLaunching(_ notification: Notification) {
            window = NSWindow(
                contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
                styleMask: [.titled, .closable, .resizable],
                backing: .buffered, defer: false)
            window.title = "AppKit Window"
            window.center()
    
            window.contentViewController = MainViewController()
            window.makeKeyAndOrderFront(nil)
        }
    }
    

     

    MainViewController.swift

    class MainViewController: NSViewController {
        override func loadView() {
            self.view = NSView()
            self.view.wantsLayer = true
            self.view.layer?.backgroundColor = NSColor.white.cgColor
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let label = NSTextField(labelWithString: "Hello AppKit")
            label.frame = NSRect(x: 100, y: 120, width: 200, height: 30)
            view.addSubview(label)
        }
    }
    

    🧠 이 세 줄의 코드, 뭐 하는 거야?

    let app = NSApplication.shared
    let delegate = AppDelegate()
    app.delegate = delegate
    
    _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
    

     

    1️⃣ NSApplication.shared

    💬 뭐야 이거?

    • macOS 앱에서 애플리케이션의 중심 객체.
    • UIKit의 UIApplication.shared와 유사한 역할.
    • 앱의 생명 주기, 이벤트 루프, 윈도우 관리 등을 처리.

    ✅ 예시

    let app = NSApplication.shared
    

    이렇게 하면 앱 인스턴스를 가져와서 설정할 수 있게 됩니다.

    2️⃣ let delegate = AppDelegate()

    💬 뭐야 이거?

    • 우리가 만든 AppDelegate 클래스는 NSApplicationDelegate 프로토콜을 따르며,
    • 앱의 시작, 종료, 상태 변경 이벤트를 처리합니다.
    • UIKit에서는 자동으로 연결되지만, macOS에서 storyboard 없이 만들면 수동으로 인스턴스를 만들어 연결해야 합니다.

    3️⃣ app.delegate = delegate

    💬 뭐야 이거?

    • 앞서 만든 AppDelegate 인스턴스를 NSApplication에 직접 연결합니다.
    • 이렇게 연결해줘야 AppDelegate의 applicationDidFinishLaunching 같은 메서드가 호출됩니다.

    4️⃣ _ = NSApplicationMain(...)

    💬 뭐야 이거?

    • 이게 macOS 앱의 진짜 실행 시작점이에요.
    • 내부적으로 이벤트 루프를 시작하고, 사용자 이벤트(마우스 클릭, 키보드 입력 등)를 처리합니다.
    • UIKit의 UIApplicationMain(...)과 같아요.
    _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
    
    • CommandLine.argc와 CommandLine.unsafeArgv는 C 스타일의 main 함수에서 인자를 넘겨주는 역할을 해요.
    • 즉, main.swift에서 이걸 호출하면 macOS 앱이 정식으로 시작됩니다.

    🔁 전체 흐름 정리

    NSApplication.shared macOS 앱 인스턴스를 생성 또는 반환
    AppDelegate() 우리가 만든 앱 이벤트 처리기 인스턴스 생성
    app.delegate = delegate 이벤트 처리기를 앱에 연결
    _ = NSApplicationMain(...) 앱의 메인 루프 시작 (실제 실행 지점)

    💡 왜 우리가 이걸 수동으로 해야 할까?

    Xcode 템플릿에서 Storyboard를 쓰면,
    Info.plist에 설정된 "NSMainStoryboardFile" 값이 이 모든 걸 자동으로 연결해줘요.

    하지만 우리가 스토리보드를 제거했기 때문에
    AppDelegate를 직접 연결하고,
    앱 실행 루프도 수동으로 시작해야 하는 상황이 된 거예요.

    ✍️ 예시로 쉽게 비유해볼게요:

    // macOS에서 앱을 직접 부팅하는 느낌
    let app = NSApplication.shared           // 앱 본체 가져오기
    let delegate = AppDelegate()             // 내 앱 설정 관리자 만들기
    app.delegate = delegate                  // 앱에 내 설정 관리자 붙이기
    _ = NSApplicationMain(...)               // 드디어 앱 부팅!
    

     

    4. 🔍 차이점 요약

    항목 iOS (UIKit) macOS (AppKit)

    스토리보드 삭제 후 처리 SceneDelegate에서 UIWindow 설정 NSApplication에 AppDelegate 직접 등록
    main.swift 필요 여부 ❌ 필요 없음 ✅ 필요함
    진입점 제어 방식 SceneDelegate 이용 main.swift에서 직접 제어
    UI 구성 방식 UIViewController + UIView NSViewController + NSView
    자동 설정 제거 Info.plist에서 Main 설정 삭제 Info.plist에서 Main + Scene Manifest 삭제

     

    5. ✅ 마무리

    스토리보드 없이 코드로 UI를 구성하면 더 많은 유연성과 제어권을 가질 수 있습니다.
    UIKit과 AppKit은 비슷해 보이지만, 앱 생명주기나 진입점 설정 방식에서 구조적인 차이가 있죠.

    특히 AppKit은 SceneDelegate 같은 구조가 없기 때문에,
    코드로 모든 걸 세팅하려면 더 명시적인 진입점(main.swift) 설정이 필요합니다.

     

     

    댓글

Designed by Tistory.