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

06. 할 일 완료 처리 기능 구현하기

by daco2020 2025. 10. 31.

지난 글(새로운 할 일 추가 기능 구현)에서 우리는 할 일을 생성(Create), 목록으로 읽고(Read), 삭제(Delete)하는 것까지 구현했습니다.

 

하지만 투두리스트에서 가장 중요한 기능인 수정(Update) 기능, 즉 완료 상태를 변경하는 기능이 빠져있습니다. 이번 글에서는 할 일을 완료 처리하고, 한 번 더 선택하면 미완료 처리로 되돌리는 기능을 구현해보겠습니다.

 


 

 

 

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

ContentView 부분을 아래와 같이 수정하겠습니다. 새롭게 추가되고 변경된 부분을 중심으로 코드를 살펴보세요. (AddItemView나 TodoDetailView 등 다른 부분은 그대로 두시면 됩니다)

struct ContentView: View {
    @State private var todoItems: [TodoItem] = []
    @State private var isShowingAddItemView: Bool = false

    var body: some View {
        NavigationStack {
            List {
                ForEach(todoItems) { item in
                    NavigationLink(destination: TodoDetailView(item: item)) {
                        HStack {
                            // 1. Image에 탭 제스처를 추가합니다.
                            Image(systemName: item.isCompleted ? "checkmark.circle.fill" : "circle")
                                .foregroundStyle(item.isCompleted ? .green : .gray)
                                .onTapGesture {
                                    // 2. 이미지를 탭하면 이 코드가 실행됩니다.
                                    toggleCompletion(for: item)
                                }

                            Text(item.title)
                                // 3. 완료된 항목은 취소선을 그어줍니다.
                                .strikethrough(item.isCompleted, color: .gray)
                                .foregroundStyle(item.isCompleted ? .gray : .primary)
                        }
                    }
                }
                .onDelete(perform: deleteItem)

            }
            .navigationTitle("나의 할 일 목록")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        isShowingAddItemView = true
                    }) {
                        Image(systemName: "plus")
                    }
                }
            }
            .sheet(isPresented: $isShowingAddItemView) {
                AddItemView { newItem in
                    todoItems.insert(newItem, at: 0)
                }
            }
        }
    }

    func deleteItem(offsets: IndexSet) {
        todoItems.remove(atOffsets: offsets)
    }
    // 4. 할 일의 완료 상태를 변경하는 함수 (새로 추가!)
    func toggleCompletion(for item: TodoItem) {
        // `todoItems` 배열에서 내가 탭한 `item`과 동일한 id를 가진 항목이
        // 몇 번째에 있는지 찾아냅니다.
        if let index = todoItems.firstIndex(where: { $0.id == item.id }) {
            // 해당 위치(index)에 있는 항목의 isCompleted 값을 반대로 뒤집습니다(toggle).
            // (false -> true, true -> false)
            todoItems[index].isCompleted.toggle()
        }
    }
}

 

 

핵심 코드에 대한 설명

 

1. .onTapGesture { ... }

 

Button처럼 UI 요소를 탭할 수 있게 만들어주는 기능입니다. 여기서는 circle Image를 탭했을 때 특정 코드를 실행시키고 싶어서 사용했습니다.

 

 

 

2. toggleCompletion(for: item)

 

onTapGesture 안에서 우리가 방금 탭한 item 을 알려주면서 toggleCompletion 함수를 호출합니다.

 

 

 

3. strikethrough(...) 와 .foregroundStyle(...)

 

strikethrough는 isCompleted가 true이면 텍스트에 취소선을 그어줍니다.
foregroundStyle는 isCompleted가 true이면 글자색을 회색으로 변경합니다.

 

이 부분은 없어도 되는 부분이지만 사용자 경험을 향상시키기 위한 디테일입니다.

 

 

 

4. toggleCompletion 함수의 작동 원리

 

if let index = todoItems.firstIndex(where: { $0.id == item.id }) 이 코드는 todoItems 배열 전체를 빠르게 스캔하여 id를 기준으로 아이템의 index(순서)를 변수에 저장합니다.

 

todoItems[index] 이 코드는 위에서 찾은 index를 사용해 @State 변수인 todoItems 배열의 원본 데이터에 직접 접근합니다. 그리고 .isCompleted.toggle()을 통해 Bool 값을 true는 false로, false는 true로 뒤집어 줍니다.

 

isCompleted 값이 변경되면 @State 변수의 내용이 변경되었기 때문에, SwiftUI는 이 변화를 감지하고 List 전체를 자동으로 새로 그려서 화면을 업데이트합니다.

 

 

 

 

여기서 이런 의문이 들수도 있습니다.

 

"왜 그냥 item.isCompleted.toggle() 이라고 하지 않고 index를 찾아 변경하나요?"

 

그 이유는 ForEach 안에서 사용하는 item은 todoItems 배열에 있는 원본 데이터를 복사(copy)해서 가져온 let(상수) 이기 때문입니다. 따라서 item의 값을 직접 바꾸려고 하면 "상수(let)는 바꿀 수 없다"는 에러가 발생합니다.

 

실제 데이터의 값을 바꾸려면 원본 데이터에 직접 접근해야 합니다. toggleCompletion 함수는 바로 이 동작을 수행합니다.

 

 


 

 

 

투두리스트 앱을 완성했습니다!

 

이제 시뮬레이터를 다시 실행(Command + R)하고, 할 일 목록의 동그라미 아이콘이나 체크 표시된 아이콘을 직접 탭해 보세요.

 

- 동그라미가 체크 표시로 바뀌고, 글자에 취소선이 그어지며 회색으로 변하는 것을 볼 수 있습니다.

- 다시 탭하면 원래 상태로 돌아옵니다.

 

 

 

여기까지 SwiftUI로 데이터를 생성(C), 조회(R), 수정(U), 삭제(D)하는 모든 기능을 갖춘 앱을 만들어 보았습니다.