본문 바로가기
SwiftUI 독학으로 기본기 익히기

04. 투두리스트 화면과 내비게이션 구현하기

by daco2020 2025. 10. 29.

이번에는 단일 화면을 넘어 여러 화면을 가진, 조금 더 앱다운 앱을 만들어 보겠습니다.

 

사용자에게 할 일 목록을 보여주고, 새로운 할 일을 추가하며, 각 항목을 눌렀을 때 상세 내용을 볼 수 있는 "나만의 투두리스트(To-Do List)" 를 구현해 보겠습니다.. 그 과정에서 List, NavigationStack, 데이터 모델링(struct)의 개념을 알아보겠습니다.

 

 

내용이 조금 많으므로 세 파트로 나누어 진행하겠습니다.

 

Part 1 : 데이터 목록을 만들고, 리스트 화면에 보여주며, 상세 화면으로 넘어가는 뼈대 만들기.

Part 2 : 새로운 할 일을 추가하는 기능 구현하기.

Part 3 : 할 일 완료 처리 기능 구현하기.

 


 

 

Part 1 : 투두리스트 화면과 내비게이션 구현하기

지난 글에 작성했던 ContentView 파일의 코드를 모두 지우고, 아래의 코드를 다시 한번 직접 타이핑하여 입력해 주세요. 새로운 개념들이 등장하므로 주석을 꼼꼼히 읽어보면서 타이핑해 주세요.

import SwiftUI

// 1. '할 일' 데이터의 설계도를 만듭니다. (데이터 모델링)
// id는 각 항목을 구분하기 위한 고유 값입니다.
// title은 할 일의 내용, isCompleted는 완료 여부를 저장합니다.
struct TodoItem: Identifiable {
    let id = UUID()
    var title: String
    var isCompleted: Bool = false
}

// 3. 상세 내용을 보여줄 화면을 미리 만듭니다.
struct TodoDetailView: View {
    let item: TodoItem // 어떤 할 일에 대한 상세 화면인지 알아야 하므로, 해당 아이템 정보를 받습니다.

    var body: some View {
        VStack {
            Text(item.title)
                .font(.largeTitle)
            Text(item.isCompleted ? "완료됨" : "미완료") // 삼항연산자: 조건 ? 참일 때 값 : 거짓일 때 값
                .foregroundStyle(item.isCompleted ? .green : .red)
        }
        .navigationTitle("상세 보기") // 내비게이션 바의 제목
    }
}


struct ContentView: View {
    // 2. '할 일' 데이터를 @State 변수로 만듭니다.
    // 여러 개의 TodoItem을 담을 수 있는 배열입니다.
    @State private var todoItems: [TodoItem] = [
        TodoItem(title: "SwiftUI 공부하기"),
        TodoItem(title: "운동하기"),
        TodoItem(title: "저녁 장보기", isCompleted: true)
    ]

    var body: some View {
        // 4. 내비게이션 기능을 사용하기 위해 전체를 NavigationStack으로 감쌉니다.
        NavigationStack {
            // 5. todoItems 배열에 있는 모든 항목을 화면에 보여주기 위해 List를 사용합니다.
            List(todoItems) { item in
                // 6. 각 목록의 항목을 누르면, 지정된 화면(TodoDetailView)으로 이동시켜주는 링크를 만듭니다.
                NavigationLink(destination: TodoDetailView(item: item)) {
                    // 여기에 목록에 보여질 UI를 디자인합니다.
                    HStack {
                        // 완료 여부에 따라 다른 아이콘을 보여줍니다.
                        Image(systemName: item.isCompleted ? "checkmark.circle.fill" : "circle")
                            .foregroundStyle(item.isCompleted ? .green : .gray)
                        Text(item.title)
                    }
                }
            }
            .navigationTitle("나의 할 일 목록") // 내비게이션 바의 제목
        }
    }
}

#Preview {
    ContentView()
}

 

 

1. struct TodoItem: Identifiable (데이터 모델링)

 

지금까지는 Text나 Int 같은 단순한 데이터만 다뤘습니다. 하지만 '할 일'은 '내용(String)'과 '완료여부(Bool)'를 한 덩어리로 가져야 합니다. 이렇게 우리가 원하는 모양의 데이터 타입을 직접 설계하는 것을 '모델링'이라고 하며, struct(구조체)를 사용합니다.

 

Identifiable은 "각 항목이 고유하게 식별 가능하다"는 약속입니다. List가 여러 항목을 구분하고 효율적으로 처리하기 위해 필요하며, let id = UUID() 코드를 통해 식별자를 구분합니다.

더 배우기
데이터 모델링과 관련한 '다양한 자료형'과 '기본값 할당' 방법을 알아보세요.

 

 

 

2. @State private var todoItems: [TodoItem] = [...]

 

이제 @State 변수는 단순한 숫자 배열이 아니라, 우리가 직접 만든 TodoItem 구조체의 배열이 되었습니다. 지난 글에서 배운 것처럼 이 배열의 내용이 바뀌거나 추가/삭제되면 List가 자동으로 업데이트됩니다.

 

 

3. NavigationStack { ... }

 

화면 이동 기능을 제공하는 가장 중요한 컨테이너입니다. "이 안에 있는 것들은 화면 이동이 가능하게 관리할게!" 라고 선언하는 것과 같습니다. 추가로 화면 상단에 "나의 할 일 목록"이라는 제목(네비게이션 타이틀)을 표시해 주는 역할도 합니다.

더 배우기
'NavigationStack'와 'NavigationTitle' 관계를 알아보세요.

 

 

 

4. List(todoItems) { item in ... }

 

todoItems 배열에 들어있는 항목의 개수만큼 자동으로 행(row)을 만들어주는 UI 요소입니다. item이라는 변수에는 각 순서에 해당하는 TodoItem 데이터(예: "SwiftUI 공부하기")가 하나씩 들어오게 됩니다.

 

 

 

5. NavigationLink(destination: ...) { ... }

 

이 코드는 List의 각 행을 누를 수 있는 링크로 만들어 줍니다.

 

destination: TodoDetailView(item: item)은 "이 링크를 누르면 TodoDetailView라는 화면으로 이동할 거야. 이동할 때 방금 누른 item 데이터를 가지고 가!" 라는 의미입니다. 이렇게 다음 화면으로 데이터를 전달합니다.

 

나머지 { ... } 부분은 해당 링크가 사용자 눈에 어떻게 보일 지를 디자인하는 부분입니다. 우리는 아이콘과 할 일 텍스트를 HStack을 이용해 가로로 배치했습니다.

 

 


 

 

'나의 할 일 목록'과 '상세 보기'

 

코드를 작성하고 프리뷰를 확인해보면 아래처럼 동작하는 앱을 확인할 수 있습니다.

 

시뮬레이터(Command + R)를 실행하고, 목록에 있는 "SwiftUI 공부하기", "운동하기" 등을 직접 눌러보세요.

 

각 항목을 누를 때마다 해당 항목의 제목과 완료 상태를 보여주는 "상세 보기" 화면으로 이동하는 것을 볼 수 있습니다. 왼쪽 상단에는 자동으로 "뒤로 가기" 버튼이 생성됩니다.

 

 

이렇게 여러 화면을 가진 앱의 기본 구조를 만들고, 화면 간에 데이터를 전달하는 방법을 알게 되었습니다.

 

다음 글에서는 이 앱을 정말 사용 할 수 있도록 사용자가 직접 새로운 할 일을 추가하는 기능을 만들어 보겠습니다.