-
[iOS, macOS] StoryBoard 없이 코드 베이스 UI 구성하기Apple🍎 2025. 4. 22. 20:54
📱 iOS vs 🖥 macOS – 코드베이스 UI를 위한 프로젝트 세팅 비교
UIKit과 AppKit, 두 플랫폼 모두 코드로 UI를 작성하고 싶을 때가 있습니다.
그런데 둘 다 Xcode에서는 Storyboard나 SwiftUI를 기본으로 요구하죠.
오늘은 iOS와 macOS에서 스토리보드 없이 코드만으로 UI를 구성하기 위한 초기 세팅 방법을 비교해봅니다.🧭 목차
- 왜 스토리보드를 쓰지 않나?
- iOS (UIKit) 프로젝트 세팅
- macOS (AppKit) 프로젝트 세팅
- 주요 차이점 요약
- 마무리
1. 왜 스토리보드를 쓰지 않나?
- 유지보수가 어렵고 충돌 위험이 큼
- 협업 시 Git conflict 빈번
- 복잡한 UI는 코드가 더 명확할 수 있음
- 재사용성과 확장성이 낮음
그래서 많은 개발자들이 코드로 UI를 직접 작성하고 싶어하죠.
2. 📱 iOS: UIKit 기반 프로젝트 세팅
✅ 기본 흐름
- Xcode에서 iOS 프로젝트 생성 (Interface는 Storyboard 선택해도 OK)
- Main.storyboard 삭제
- Info.plist 수정 → Main storyboard file base name 항목 삭제
- 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 기반 프로젝트 세팅
✅ 기본 흐름
- Xcode에서 macOS App 생성 (Interface: Storyboard 선택해도 OK)
- Main.storyboard 삭제
- Info.plist에서 Main storyboard와 Application Scene Manifest 제거
- AppDelegate.swift에서 @main 제거
- 새 파일 main.swift 추가 → 진입점 지정
- 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) 설정이 필요합니다.'Apple🍎' 카테고리의 다른 글
🎧 AVFoundation 오디오 분석을 위한 데이터 다루기 (0) 2025.04.24 iOS 앱 개발에서 버전과 빌드의 이해하기 (0) 2025.04.12 Xcode가 저장 공간 다 잡아 먹는다. (0) 2025.03.01 SwiftUI와 UIKit 뷰 구성하는 법 비교하기 ( 선언형, 절차형 패러다임 비교) (0) 2024.09.19 String Catalog를 이용한 Localization (0) 2024.08.26