이번 글에서는 제가 직접 만든 To-Do 앱의 Splash 화면과 Onboarding 화면을 소개해보려고 합니다.
제가 구성한 흐름은 다음과 같습니다:
- 🌱 앱의 진입 흐름 (RootView) – 앱의 시작 구조와 화면 전환 관리
- 🌊 SplashView – 심플하지만 강렬한 로딩 화면
- 📖 OnboardingTabView – 앱의 주요 기능을 소개하는 페이지 뷰
이 순서대로 각 화면의 구성과 구현 방식을 차근차근 리뷰해보겠습니다. 그럼 지금부터 함께 살펴보시죠! 🚀
🌱 앱의 진입 흐름(RootView)
앱의 진짜 시작점은 @main 속성을 가진 N_VoiceMemoApp_SwiftUIApp 구조체입니다.
SwiftUI 앱의 생명주기에서 가장 처음 실행되는 이 지점에서는 RootView()를 호출하여
UI 흐름을 시작하게 됩니다.
@main
struct N_VoiceMemoApp_SwiftUIApp: App {
var body: some Scene {
WindowGroup {
RootView()
}
}
}
네비게이션 흐름을 관리하기 위해 NavigationStack을 사용하였지만, 앱의 진입점인 N_VoiceMemoApp_SwiftUIApp에서는 사용하지 않고, 대신 RootView 내부에서 감싸도록 설계했습니다.
이렇게 구조를 나눈 이유는 앱 진입점에 과도한 책임을 지우지 않고,
화면 전환 및 상태 관리는 RootView에서 전담하도록 하여
코드의 역할 분리와 가독성을 높이기 위함입니다.
저는 앱 실행 시 Splash → Onboarding → Main 화면으로 자연스럽게 이어지도록 하기 위해
@State와 NavigationStack, 그리고 싱글톤 AppState를 활용했습니다.
struct RootView: View {
@State private var showOnboarding = false // 온보딩 표시 여부
@StateObject var appState = AppState.shared
var body: some View {
NavigationStack(path: $appState.navigationPath) {
Group {
if showOnboarding {
OnboardingTabView()
} else {
SplashView()
}
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
withAnimation {
showOnboarding = true
}
}
}
}
}
}
1. DispatchQueue
앱 시작 후 1.5초간 Splash 화면을 보여준 뒤, withAnimation을 통해 부드럽게 Onboarding 화면으로 전환되도록 구성한 점입니다.
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
withAnimation {
showOnboarding = true
}
}
}
2. AppState
전체 흐름을 NavigationStack으로 감싸고, 경로(navigationPath)는 @StateObject로 관리되는 AppState를 통해 전역 상태로 관리합니다.
class AppState: ObservableObject {
static let shared = AppState()
@Published var navigationPath = NavigationPath()
}
🌊 SplashView
별도의 로딩 애니메이션 없이, 로고 이미지를 깔끔하게 보여주도록 구성했습니다.
struct SplashView: View {
var body: some View {
Image("logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: Constants.ControlWidth * 250)
}
}
로고의 크기는 기기 너비에 따라 유동적으로 반응하도록 설정했으며, 저번에 포스팅했던 Figma 비율 그대로! 개발하는 Constants 구조체 만들기에 따라 설계된 Constants.ControlWidth 값을 활용하여 일관된 크기를 유지했습니다.
📖 OnboardingTabView
OnboardingView는 4단계 페이지형 화면으로 구현하였습니다.
이를 구현하기 위해 SwiftUI의 TabView와 PageTabViewStyle()을 활용했습니다.
TabView(selection: $currentPage) {
OnboardingFirstView().tag(0)
OnboardingSecondView().tag(1)
OnboardingThirdView().tag(2)
OnboardingLastView().tag(3)
}
.tabViewStyle(PageTabViewStyle())
1. TabView + .tag()
각 페이지를 구성하는 뷰(OnboardingFirstView 등)에 .tag()를 부여하고
TabView(selection:)과 연결함으로써 현재 어느 페이지에 있는지를 추적할 수 있게 했습니다.
@State private var currentPage = 0
currentPage는 사용자의 스와이프 동작에 따라 자동으로 바뀌며,
화면 하단의 페이지 인디케이터(동그라미 UI)의 상태도 함께 반응합니다.
ForEach(0..<4, id: \.self) { index in
Circle()
.fill(index == currentPage ? Color.key : Color.gray.opacity(0.5))
.frame(width: 8, height: 8)
}
2. PageTabViewStyle()
TabView의 기본 스타일은 수평 스크롤로 여러 뷰를 보여줄 수 있지만,
여기서는 페이지 스타일로 바꾸기 위해 PageTabViewStyle()을 적용했습니다.
.tabViewStyle(PageTabViewStyle())
3. 화면 이동
Onboarding의 마지막 페이지인 OnboardingLastView에서는 “시작하기” 버튼을 누르면 메인 화면으로 전환되도록 구성했습니다.
이 때 AppState.shared.navigationPath.append(...)를 활용하여 전체 앱 흐름과 자연스럽게 연결되도록 했습니다.
Button {
AppState.shared.navigationPath.append(onboardingType.main)
}
🎬 Splash & Onboarding 구현 영상
🧩 마무리
이번 포스팅에서는 제가 직접 만든 ToDo 앱의 시작 구조인
🌱 RootView, 🌊 SplashView, 📖 OnboardingView의 흐름과 구현 방식을 살펴보았습니다.
다음 포스팅에서는 SwiftUI에서 NavigationStack을 사용하는 경우와 사용하지 않는 경우의 차이점,
그리고 언제 어떤 방식이 더 적절한지에 대한 고민과 사례들을 정리해보려고 합니다.
앞으로도 꾸준히 기록하며 성장하는 개발자가 되기 위해 달려보겠습니다! 🚀
'iOS 개발 > SwiftUI' 카테고리의 다른 글
[SwiftUI] Todo 기능 리뷰 (Feat. 나만의 Todo) (3) | 2025.06.02 |
---|---|
[SwiftUI] @Published 썼는데도 View가 안 바뀐다? (0) | 2025.05.30 |
[SwiftUI] Figma 비율 그대로! 개발하는 Constants 구조체 만들기 (1) | 2025.05.24 |
뷰 상태변경 제 2장 (8) | 2024.11.13 |
뷰 상태변경 제 1장 (0) | 2024.11.13 |