애플 아카데미의 두 번째 챌린지:
CRUD 기능이 있는 App을 만들면서 디자인, 테크 스킬을 기르자!
챌린지2 과정에서 본격 앱 구현에 들어가기 전
간단한 투두 앱을 만들어보는 시간이 있었습니다.
(근데 이제 전혀 간단하지 않았던,...)
개발 생전 처음, 스위프트, Xcode 다 처음이고 swift 병아리반에서 박수만 뻑뻑 치다 온 저였는데요...
그래도 투두 앱을 빠르게 한번 경험해 본 덕에 두 번째 챌린지를 잘(?) 마무리 짓고 있는 것 같습니다.
그래서 제가 어떻게 앱 개발에 감히! 도전해 볼 용기가 생겼는지 공유 겸 복습해 보고자~
투두 앱을 만들어본 과정을 적어보게 되었습니다.
‼️학습 목표
- 할 일 항목을 리스트에 추가할 수 있다
- 할 일을 삭제할 수 있다
- 할 일을 수정할 수 있다
1. Swift 프로젝트 만들기
일단 Xcode를 켜줍니다. 그럼 아래 같은 화면이 나오는데 iOS - App을 선택하고 Next~
인터페이스는 Swift UI를 선택해 줍니다!
그러고 나면
짜잔~ 뭐가 생겨있습니다.
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
대충 읽어보면 VStack이란 거 안에 이미지와 텍스트가 있죠?
오른쪽에 보이는 아이콘과 글자가 저것일 거라는 예감이 듭니다.
그리고 디자이너에게 익숙한 단어 padding.
음~ Stack은 '뭔가 피그마의 프레임 같다'라는 느낌이 들고 '이미지와 텍스트는 오토레이아웃을 적용한 것 같다'라는 느낌이 옵니다.
(프레이머를 써보신 분은 Stack도 친근한 느낌이 들 것 같아요.)
그럼 이제 안에 투두 리스트 화면을 그려줘야겠죠?
2. 기본 UI 만들기
⬇️ 투두 리스트의 최소 사양
- 할 일을 작성할 텍스트 입력 필드
- 할 일을 추가해 주는 버튼
- 할 일 리스트
텍스트 필드를 넣는 법은 간단합니다!
TextField를 사용해서 그 안에 placeholder에 들어갈 내용, 필드 안에 들어갈 값을 써주면 됩니다.
그런데~ 입력되는 값은 매번 달라지니까 고정적인 값이 아닌
변수(var)를 사용해서 입력받을 값을 비워줍니다.
그리고 그 변수를 필드 값으로 연결($)해서 입력될 수 있도록 해줍니다.
struct ContentView: View {
@State var todoTitle: String = ""
var body: some View {
VStack {
TextField("입력해 주세요", text: $todoTitle)
.padding()
}
}
}
버튼도 넣어봅니다. 기능은 비워두어도 label만 있으면 프리뷰에서 볼 수 있어요.
Button{ //여기에는 버튼을 누르면 동작할 기능을 넣습니다.
} label: {
//여기는 버튼 안에 들어가는 내용을 넣습니다.
Text("추가")
}
버튼 레이블 안에는 Image를 활용해서 애플의 기본 아이콘(SF Symbols)도 넣을 수 있습니다.
리스트도 넣어봅니다. 간단하게 텍스트만 넣어봤어요.
이렇게만 넣어도 아이폰 기본 UI로 리스트가 만들어집니다!
List{ Text("안녕하세요")
Text("Swift")
Text("왕초보")
}
오~.~ 벌써 그럴싸한 느낌이 나요. 하지만 아무것도 동작하지 않겠죠?
3. 간단한 기능 넣기
버튼을 눌렀을 때 리스트에 추가되게 하려면 .append를 활용할 수 있습니당.
그리고 텍스트 필드와 같이 리스트에도 입력된 값을 넣을 수 있게 해 주려면 변수를 써야 합니다.
이렇게 고쳐봅니다.
struct ContentView: View {
@State var todoTitle: String = ""
@State var todos: [String] = []
var body: some View {
VStack {
TextField("입력해 주세요", text: $todoTitle)
.padding()
Button{
todos.append(todoTitle)
} label: {
Text("추가")
}
List( todos, id: \.self) { todo in
Text(todo)
}
}
}
}
todos는 리스트에 쭉- 생길 값이니까 [ ] Array로 선언해 줍니다.
(Array와 관련된 자세한 내용은 공식 문서 참고!)
그리고 버튼이 눌릴 때 필드에 입력된 값(todoTitle)이 배열에 추가(append)되도록,
리스트 안에는 늘어난 배열의 값들이 갈 수 있도록 합니다.
4. 삭제 기능 추가하기
그럼 이제 생겨난 리스트를 지우는 마법을 부려볼 수 있습니다.
그전에 리스트 안에 있는 값을 ForEach로 변경합니다. 왜냐! 삭제 기능을 사용하려면 그래야 하거든요..
//수정 전
List(todos, id: \.self) { todo in
Text(todo)
}
//수정 후
List{
ForEach(todos, id: \.self) { todo in
Text(todo)
}
}
그러면 이제 .onDelete를 붙일 수 있습니다. (공식 문서 또 참고해 보기)
이걸 사용하면 아주 간단하게 스와이프로 삭제하기 기능을 붙일 수 있어요.
List{
ForEach(todos, id: \.self) { todo in
Text(todo)
}
.onDelete { indexSet in
todos.remove(atOffsets: indexSet)
}
}
벌써 학습 목표 두 가지를 해치웠습니다!
이제 남은 건 수정하기 뿐이네요.
But..!
리스트가 글자만 있으니 좀 심심하고, 타입을 구분해 보면 좋겠고 하니
구조체(struct)를 사용해서 리스트에 들어가는 내용을 하나의 덩어리로 만들어봅니다.
마치 피그마에서 컴포넌트를 만드는 것과 비슷하달까..?
5. 리스트를 아이템으로 구조화하기
구조체를 만들고, 리스트의 유형을 구분하는 ItemType을 정의해 줍니다.
struct TodoItem: Identifiable {
let id = UUID()
var text: String
var type: TodoType
}
enum TodoType: String, CaseIterable {
case work
case personal
case hobby
}
이런 식으로 넣을 수 있습니다! 이 친구들은 contentView 밖에 넣어줍니다.
TodoItem은 id, text, type을 갖고 type은 일, 개인적인 것, 취미 이런 식으로 case로 구분해 줄 수 있습니다.
그리고 타입마다 화면에서 보일 텍스트와 이모지를 정의해 줄 수도 있어요.
struct TodoItem: Identifiable {
let id = UUID()
var text: String
var type: TodoType
}
enum TodoType: String, CaseIterable, Identifiable {
case work
case personal
case hobby
var id: String { self.rawValue }
var icon: String {
switch self {
case .work: return "💼"
case .personal: return "👤"
case .hobby: return "🛹"
}
}
var displayName: String {
switch self {
case .work: return "업무"
case .personal: return "개인"
case .hobby: return "취미"
}
}
}
그러면 이제 Picker를 넣고 할 일 타입들을 불러올 수 있어요.
//body 밖에 타입을 불러오는 변수를 선언해 줍니다.
@State var newItemType: TodoType = .personal
//body 안에 picker를 넣고 할 일 타입들을 넣어줍니다.
Picker("할일타입", selection: $newItemType) {
ForEach(TodoType.allCases) { type in
Text("\(type.displayName)")
.tag(type)
}
}
또 리스트에 구조체를 넣어서 이모지, 텍스트를 같이 보여줄 수 있습니다.
//리스트 배열 안에 String을 구조체 이름으로 변경(TodoItem)
@State var todos: [TodoItem] = []
//새로운 타입 변수를 선언해주고 기본 값을 개인으로 넣습니다.
@State var newItemType: TodoType = .personal
//body 안의 리스트
List {
ForEach(todos) { item in
HStack {
Text(item.type.icon)
Text(item.text)
}
}
.onDelete { indexSet in
todos.remove(atOffsets: indexSet)
}
}
//리스트 내용을 위와 같이 수정합니다.
조금 귀여워졌을지도..
이제 남은 건 진짜 수정뿐이야!
6. 할 일 수정 기능 넣기
리스트에서 할 일 항목을 터치하면 상세화면으로 넘어가고
거기서 내용을 수정할 수 있도록 할 것입니다.
그러면 넘어갈 상세 화면이 필요하겠죠? 새로운 SwiftUI 파일을 cmd+n을 눌러서 만들어 줍니다.
똑같이 텍스트 필드와 피커를 만들어주는데 이번에는 원래 입력된 값을 가져와야 합니다.
@Binding을 활용해서요!
import SwiftUI
struct EditItemView: View {
//TodoItem 정보를 item으로 가져오기.
@Binding var item: TodoItem
//상세화면을 닫게하려면 아래 환경 변수를 넣어줍니다.
@Environment(\.dismiss) var dismiss
var body: some View {
HStack {
//텍스트 필드에 아이템 텍스트(할 일 타이틀)을 가져옵니다.
TextField("아이템", text: $item.text)
.padding()
//피커에 아이템 타입(할일 타입)을 가져옵니다.
Picker("투두타입", selection: $item.type) {
ForEach(TodoType.allCases) { type in
Text("\(type.displayName)")
.tag(type)
}
}
}
Button {
//창닫힘 버튼 액션을 넣어줍니다.
dismiss()
} label: {
Text("저장")
}
}
}
이렇게 만들어진 화면을 리스트에서 눌렀을 때 넘어갈 수 있도록 해봅니다.
NavigationView로 감싸서 Link로 넘어가도록요~
var body: some View {
//전체 화면이 이동할 수 있게 NavigationView로 감싸주고
NavigationView{
VStack {
HStack {
TextField("입력해 주세요", text: $todoTitle)
.padding()
Picker("투두타입", selection: $newItemType) {
ForEach(TodoType.allCases) { type in
Text("\(type.displayName)")
.tag(type)
}
}
}
Button{
let newItem = TodoItem(text: todoTitle, type: newItemType)
todos.append(newItem)
} label: {
Text("추가")
}
List{
ForEach($todos) { $item in
//Link를 넣어서 넘어갈 수 있게
NavigationLink {
//수정화면으로 넘어가게 하자
EditItemView(item: $item)
} label:{
HStack{
Text(item.type.icon)
Text(item.text)
}
}
}
.onDelete { indexSet in
todos.remove(atOffsets: indexSet)
}
}
}
}
}
와~~ 세 가지 학습 목표를 전부 달성했습니다.
🥳
여기서 하나 추가하면 좋을 것이 있는데요.
텍스트 필드에서 추가 버튼을 누르면 입력된 내용을 초기화시키는 코드입니다.
Button {
let newItem = TodoItem(text: todoTitle, type: newItemType)
todos.append(newItem)
todoTitle = "" // <- 초기화!
} label: {
Text("추가")
}
버튼 안에 추가해 보세요!
이렇게 self.앱 구현에 들어가기 전에 Swift와 SwiftUI를 찍어먹어 보았습니다.
아직도 코딩은 너어어어어어무 어렵고 대부분의 문법들은 GPT 도움 없이 쓰기 힘들지만
투두 앱을 만들면서 앱의 핵심 기능 CRUD에 대한 이해를 가져갈 수 있었어요.
(데이터 흐름이 대충 이렇고 저렇고~ 어떤 느낌~?을 받을 수 있었습니다.)
그리고 '어라..? 내가 만들려고 하는 앱이랑 구조가 비슷하네?'라는 생각이 들어서
GPT와 함께 투두 앱을 업그레이드해보면서 한 발 더 나아가보기! 시도해 보았습니다.
그리하여 이어지는...스포일러
📝 왕초보 투두 리스트 앱 만들기 2탄
- 리스트 안에 디자인 살짝 가미하기
- 할 일에 버튼을 넣어서 누르면 완료 항목으로 보이게 하기
2탄 보러가기
[Swift] 왕초보 투두 리스트 앱 만들기(feat. SwiftUI) 2탄
다시 돌아온 투두 리스트 만들기 입니다~.~ 지난 번에 이어 이번에 해볼 것은!📝 목표- 리스트 안에 디자인 살짝 가미하기- 할 일에 버튼을 넣어서 누르면 완료 항목으로 보이게 하기 그럼 바로
designer-murphy.tistory.com
'개발 뚝딱이 > Swift' 카테고리의 다른 글
[Swift] 왕초보 투두 리스트 앱 만들기(feat. SwiftUI) 2탄 (0) | 2025.05.02 |
---|