Jacob(제이콥)
@jacobko
수강생
859
수강평
47
강의 평점
5.0
상시 학생 할인중
강의 금액이 부담되는 학생분들을 위해서 본인 학교 메일 계정으로 예) @***.ac.kr , @***.edu
이메일 보내주시면 전 강의 할인 쿠폰 (50%) 보내 드립니다.
👉jacobko@kakao.com
안녕하세요, iOS 개발자 Jacob 입니다.
현재 해외를 기반으로 iOS 앱 개발 및 강의를 병행하고 있습니다.
호주 · 뉴질랜드 스타트업에서 iOS 개발자로 실무 참여, App Store 앱 출시 경험
UIKit 기반 레거시 앱을 SwiftUI로 전환하는 마이그레이션 프로젝트 다수 진행
해외 팀과의 협업 경험을 바탕으로 실무 중심의 강의 커리큘럼 설계
국내가 아닌 해외 개발 환경에서 직접 부딪히며 쌓은 경험을 바탕으로, 이론에 머물지 않고 실제 프로젝트에서 바로 쓸 수 있는 SwiftUI 강의를 만들고 있습니다.
Lv.1부터 Lv.4까지 시리즈로 구성된 커리큘럼을 꾸준히 따라오시면, iOS 개발의 핵심을 체계적으로 익히실 수 있도록 책임감을 가지고 만들겠습니다.
강의
로드맵
전체 1수강평
- [Lv.2] SwiftUI 중급 - Core Data와 SwiftData로 앱 완성하기
- [Lv.3] 실전 네트워크 통신 - SwiftUI Combine, Async/Await
- [Lv.1] iOS 17 앱 개발 기초 - SwiftUI로 시작하기
- [Lv.3] 실전 네트워크 통신 - SwiftUI Combine, Async/Await
- [Lv.3] 실전 네트워크 통신 - SwiftUI Combine, Async/Await
게시글
질문&답변
소리가 ㅠㅠ
안녕하세요 김상관 님.먼저 질문 확인이 늦어서 늦게 답변드린점 죄송합니다.해당강의 영상 확인 하였습니다. 음성이 한쪽으로 나오는 현상 확인 하여서 삭제 후 동영상 재 인코딩 하여 업로드 하였습니다. 강의 오류 알려주셔서 감사합니다제이콥
- 0
- 2
- 46
질문&답변
index와 indexSet
Index와 IndexSet 차이점 설명안녕하세요 최수훈님, index와 IndexSet의 차이점에 대해 설명해드릴게요.Index vs IndexSet 차이점1. Index (Int 타입)// 하나의 숫자만 가리킴 let index = 2 // 배열의 2번째 요소 fruitArray.remove(at: index) // 하나만 삭제 2. IndexSet// 여러 개의 인덱스를 담을 수 있는 집합 let indexSet: IndexSet = [0, 2, 4] // 0번, 2번, 4번 요소들 fruitArray.remove(atOffsets: indexSet) // 여러 개 동시 삭제 왜 remove(atOffsets:)를 사용하나요?SwiftUI의 List에서 .onDelete를 사용할 때는 사용자가 여러 행을 선택해서 한 번에 삭제할 수 있기 때문입니다.List { ForEach(fruitArray, id: \.name) { fruit in Text("\(fruit.name) - \(fruit.count)개") } .onDelete(perform: deleteFruit) // 여기서 IndexSet이 자동으로 전달됨 } 실제 동작 예시사용자가 편집 모드에서:딸기(0번), 바나나(2번)를 동시 선택 → IndexSet [0, 2]가 전달한 번에 여러 개 삭제 가능정리구분 Index (Int) IndexSet 용도 단일 항목 삭제 다중 항목 삭제 메서드 remove(at:)remove(atOffsets:) 값 예시 2[0, 2, 4]감사합니다제이콥
- 0
- 2
- 52
질문&답변
@Published의 용도
안녕하세요 gaeun 님. @ObservationIgnored + @Published 사용 이유는 두개의 서로 다른 반응형 시스템을 함께 사용하기위해서 테스트 용으로 사용했습니다. 각각의 역할@Published (Combine)목적: Combine 연산자 사용 (.debounce(), .map() 등)용도: 비즈니스 로직 처리 (유효성 검사, 데이터 변환)@Observable (SwiftUI)목적: View 자동 업데이트용도: UI 반영왜 @ObservationIgnored가 필요한가?@ObservationIgnored @Published var textFieldID: String = "" var idValid: Bool = false❌ @ObservationIgnored 없으면textFieldID 변경 → 즉시 View 업데이트.debounce()가 무의미해짐불필요한 중복 업데이트 발생✅ @ObservationIgnored 사용하면textFieldID → Combine 파이프라인만 실행처리 결과 (idValid) → View 업데이트효율적인 업데이트 제어실제 동작 흐름사용자 입력 ↓textFieldID (@Published) ↓Combine 파이프라인(.debounce → .map → .sink) ↓idValid = true/false ↓View 자동 업데이트 (@Observable)결론: Combine의 강력한 연산자를 활용하면서, SwiftUI의 새로운 @Observable로 View를 효율적으로 업데이트하기 위한 하이브리드 방식 입니다. 감사합니다Jacob
- 0
- 1
- 44
질문&답변
Sorted, Filter, Map - UserViewModel 부분 오류 발생
안녕하세요. nonehour 님에러 발생 원인현재 코드에서 두 가지 주요 문제가 발생하고 있습니다:ObservableObject 프로토콜 준수 실패: UserViewModel 클래스가 ObservableObject를 채택했지만 필요한 요구사항을 충족하지 못하고 있습니다.Combine 프레임워크 누락: @Published 프로퍼티 래퍼와 관련된 초기화자를 찾을 수 없다는 에러가 발생합니다.해결 방법파일 상단의 import 구문을 다음과 같이 수정해주세요:import SwiftUI import Combine // 이 줄을 추가해주세요 왜 이런 차이가 발생하는가이런 현상이 발생하는 이유는 개발 환경의 차이 때문입니다:Xcode 버전 차이: 최신 Xcode에서는 SwiftUI를 import할 때 Combine도 함께 자동으로 가져오는 경우가 있지만, 구버전이나 특정 설정에서는 그렇지 않을 수 있습니다.iOS 배포 타겟: 프로젝트의 iOS 최소 지원 버전에 따라 자동 import 동작이 달라질 수 있습니다.프로젝트 설정: 프로젝트 생성 방식이나 빌드 설정에 따라 모듈 import 방식이 다를 수 있습니다.근본적인 이해@Published와 ObservableObject는 실제로 Combine 프레임워크의 핵심 구성 요소입니다. 따라서 이들을 사용할 때는 명시적으로 Combine을 import하는 것이 가장 안전하고 명확한 방법입니다.강의 예제와 달라서 에러가 발생된점 양해 드립니다.감사합니다.Jacob
- 0
- 1
- 91
질문&답변
NavigationBarItems Deprecated
안녕하세요 최수훈 님. 제가 질문 확인이 늦어서 답변이 이제 드립니다. .NavigationBarItems 는 Deprecated 되어서 .toolbar() 사용하게끔 권장되고 있습니다. 질문주신 ListBasic 부분에서 EditButton() 을 사용했는데, 변경된 코드는 다음과 같습니다. // 기존 코드 .navigationBarItems(leading: EditButton(), trailing: addButton) // 변경된 코드 .toolbar() 사용 .toolbar { ToolbarItem(placement: .navigationBarLeading) { EditButton() } ToolbarItem(placement: .navigationBarTrailing) { addButton } 다른 NavigationBarItems 사용한 강의 예제들도 .toolbar{} 를 사용하는 방식으로 변경하여 강의 제공 예제 파일에 업데이트 하도록 하겠습니다.감사합니다제이콥
- 0
- 3
- 82
질문&답변
ViewBuilder 강의 관련 질문입니다.
안녕하세요 ducduck 님. 질문 주셔서 감사합니다.정리하자면 제가 설명한 부분: LocalViewBuilder에 선언된 ViewType을 사용해서 조건부 뷰를 만들기 위해 @ViewBuilder가 필요하다.질문자님이 이해하신 부분: 각 조건에서 반환하는 뷰들이 서로 다른 타입(Text, VStack, Image)이라서 @ViewBuilder가 필요하다.실제로는 둘 다 연결된 같은 이유입니다!ViewType enum을 사용해서 → 조건부 분기를 만들고 → 각 분기에서 다른 타입의 뷰를 반환하기 때문에 → @ViewBuilder가 필요예시로 @ViewBuilder private var numberPlate: some View { if type == .one { Text("1") } // Text 타입 else if type == .two { VStack{} } // VStack 타입 else { Image("3") } // Image 타입 }SwiftUI에서 some View는 단일 타입만 반환할 수 있는데, @ViewBuilder가 이런 다른 타입들을 하나로 통합해주는 역할을 합니다.그렇게 때문에 ViewType 을 활용한 조건부 뷰 구현과 서로 다른 타입 처리, 둘 다 @ViewBuilder 가 필요한 이유입니다
- 0
- 1
- 75
질문&답변
CoreData Array의 변화에 따른 SwiftUI View 변화 적용(with @Observable Macro)
안녕하세요 ycc3819 님.질문해주신 @Observable을 활용한 코드에서, 클릭 시 ~가 View에 바로 반영되지 않고, 앱을 재시작하거나 새로운 데이터를 입력할 때 한 번에 업데이트되는 현상을 발견했습니다. 이 문제를 해결하기 위해 여러 시도를 해본 결과, @State와 @StateObject의 차이로 인해 Core Data와 View의 상태가 즉시 동기화되지 않았음을 알게 되었습니다. 1. Core Data에서 @StateObject를 사용하는 이유• Core Data의 상태는 View가 재생성되더라도 유지되어야 합니다. 따라서, @StateObject로 ViewModel을 관리하는 것이 적합합니다.• @StateObject는 ViewModel과 View의 생명주기를 연결하고, 상태가 변경될 때 View에 자동으로 반영되도록 보장합니다. 2. @State와 @StateObject의 선택 기준• @State:• 단순 상태(문자열, 정수, 부울 등)를 관리할 때 적합.• View 외부와의 상태 공유가 필요하지 않은 경우 사용.• @StateObject:• Core Data, 비동기 작업, ViewModel 등 객체 기반 상태 관리가 필요할 때 적합.• View와 ViewModel의 생명주기를 연결하고 상태를 유지. 3. 적용 예시• Core Data와 같은 외부 데이터베이스를 관리할 때는 ViewModel에서 Core Data를 초기화하고, @StateObject를 통해 ViewModel과 View를 연결해야 데이터가 재사용되고 상태가 유지됩니다. 저 역시 질문을 통해 새로운 관점을 배울 수 있었고, 여러 시도를 통해 이 문제를 해결할 수 있었습니다. 질문해주셔서 감사드리며, 앞으로도 궁금한 점이 있으면 언제든지 알려주세요.감사합니다.Jacobimport SwiftUI import CoreData import Observation @Observable // Observable 사용 class CoreDataInterViewModel2 { let container: NSPersistentContainer var savedEntities: [Fish] = [] // @Published 사용 안함 init() { container = NSPersistentContainer(name: "FishContainer") container.loadPersistentStores { (description, error) in if let error = error { print("ERROR LOADING CORE DATA. \(error)") } else { print("SUCCESSFULLY LOADED CORE DATA: \(description)") } } fetchFish() } func fetchFish() { let request = NSFetchRequest(entityName: "Fish") do { savedEntities = try container.viewContext.fetch(request) } catch { print("ERROR FETCHING CORE DATA: \(error)") } } func saveData() { do { try container.viewContext.save() fetchFish() } catch { print("ERROR SAVING CORE DATA: \(error)") } } func addFish(text: String) { let newFish = Fish(context: container.viewContext) newFish.name = text saveData() } func deleteFish(indexSet: IndexSet) { guard let index = indexSet.first else { return } let entity = savedEntities[index] container.viewContext.delete(entity) saveData() } func updateFish(fish: Fish) { let currentName = fish.name ?? "" fish.name = currentName + "~" saveData() } } // MARK: - VIEW struct CoreDataInter2: View { @State private var textFieldText: String = "" @StateObject var vm: CoreDataInterViewModel = .init() // 기존 @StateObject 를 사용 var body: some View { NavigationStack { VStack(spacing: 20) { TextField("새로운 생선을 입력하세요", text: $textFieldText) .textFieldStyle(.roundedBorder) Button { guard !textFieldText.isEmpty else { return } vm.addFish(text: textFieldText) textFieldText = "" } label: { Text("추가하기") .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } List { ForEach(vm.savedEntities, id: \.self) { fish in Text(fish.name ?? "이름 없음") .onTapGesture { vm.updateFish(fish: fish) } } .onDelete { indexSet in vm.deleteFish(indexSet: indexSet) } } .listStyle(.plain) } .padding() .navigationTitle("Fish Market") } } } #Preview { CoreDataInter2() }
- 0
- 2
- 195
질문&답변
Xcode version 문제
안녕하세요 miso lim 님.제공된 강의 code 파일을 xcode 16.2 버전 (iOS 18) 에서 실행해보니 이상 없이 잘 되었습니다.혹시 강의 중간마다 에러가 발생 되면 언제든지 질문 주시면 버전에 맞춰서 수정된 코드로 답변해 드리겠습니다.감사합니다Jacob
- 0
- 2
- 184
질문&답변
init-deinit의 무한루핑을 벗어나는 방법이 궁금합니다.
안녕하세요 ycc3819 님.저도 확인한 결과 @Observable 로 변경할 시 무한 루핑이 발생되는것을 확인하였습니다.발생한 원인을 보자면..1. 무한 루핑 발생 원인• @State는 값 타입 상태를 관리하기 위해 설계되었습니다. 하지만 @Observable은 클래스 타입(참조 타입)을 기반으로 동작하며, SwiftUI가 상태 변경을 감지할 때 ViewModel이 반복적으로 초기화되는 문제가 발생합니다.• 이로 인해 init과 deinit이 무한히 호출되며 루핑이 발생합니다.2. 그래서 강의 코드 처럼 @StateObject를 사용해야 하는 이유• 기존 강의 코드에서 @StateObject는 ObservableObject와 함께 사용되며, SwiftUI 뷰의 생명주기와 ViewModel의 초기화/해제를 안정적으로 관리합니다.• @StateObject는 뷰가 다시 렌더링되더라도 ViewModel을 재사용하기 때문에 무한 루핑 문제가 발생하지 않습니다.그래서 강의 내용 코드를 @Observable 로 바꿀때는 강의에서 다루지 않은 .onAppear, .onDisappear 를 사용해서 Weak self 약한 참조 내용을 확인 하실 수 있습니다. (@State, 와 init, deinit 을 사용하게 되면 라이프 사이클 충돌로 인해 무한 루핑이 되기 때문입니다. )예시 코드import SwiftUI import Observation // @Observable 사용을 위해 임포트 // MARK: - VIEWMODEL @Observable class WeakSelfInterViewModel { var data: String? = nil // 데이터를 관리하는 상태 변수 init() { getData() // 초기 데이터 로드 } func getData() { // 비동기로 데이터를 로드하며, 강한 참조 순환을 방지하기 위해 [weak self] 사용 DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [weak self] in self?.data = "NEW DATA!" // 데이터 업데이트 } } } // MARK: - SCREEN 1 struct WeakSelfInter: View { var body: some View { NavigationStack { // 2번 페이지로 이동하며 ViewModel은 2번 페이지에서만 생성 및 관리 NavigationLink("2번째 페이지로 이동") { WeakSelfInter2() } .navigationTitle("1번째 페이지") // 네비게이션 타이틀 설정 } } } // MARK: - SCREEN 2 struct WeakSelfInter2: View { @State private var vm = WeakSelfInterViewModel() // @State로 ViewModel 관리 var body: some View { VStack { Text("2번째 페이지") .font(.largeTitle) .foregroundColor(.red) // ViewModel의 데이터가 있으면 표시 if let data = vm.data { Text(data) } } .onAppear { // 뷰가 나타날 때 데이터 로드 확인 print("뷰가 나타남") } .onDisappear { // 뷰가 사라질 때 로그 출력 print("뷰가 사라짐") // ViewModel은 @State로 관리되므로 자동으로 메모리에서 해제됨 } } } // MARK: - PREVIEW #Preview { WeakSelfInter() // 미리보기에서 WeakSelfInter 화면 확인 }
- 1
- 2
- 162
질문&답변
Apple 공식 문서 보는법
안녕하세요 이재영 님.애플 공식문서 조회 하는 것에 대해서 몇가지 저의 팁을 드리자면 https://developer.apple.com/documentation/애플 문서는 주제별로 잘 정리되어 있지만, 특정 기능을 바로 찾기 위해서는 먼저 개념 파악이 중요합니다. 예를 들어, SwiftUI에서 뷰를 다룬다면 “View Protocol”로 검색을 시작하는 것이 유용합니다. 키워드 간결화: 문서 검색은 구체적인 용어를 간단하게 입력할수록 정확한 결과를 얻을 수 있습니다. 예를 들어 “SwiftUI delete text items in loop”보다는 “SwiftUI List delete”처럼 핵심 단어로 줄여서 검색해보세요.메서드 이름, 프로토콜 사용: Swift나 SwiftUI에서 제공하는 메서드나 프로토콜 이름을 직접 검색하면 유용합니다. 예를 들어, “ForEach”를 이용한 반복 처리를 문서에서 찾아보고, 그 예시를 통해 구현 방법을 이해할 수 있습니다.예를 들어, 반복문을 통해 텍스트를 한 번에 지우는 기능을 찾고자 한다면:• 먼저 “SwiftUI ForEach”를 검색합니다. 이 반복문 구조에 대한 문서를 찾고, ForEach 내부에서 데이터를 처리하는 방법을 이해할 수 있습니다.• 그다음으로 “SwiftUI delete items”나 “SwiftUI onDelete”와 같은 키워드로 검색합니다. SwiftUI의 List에서 항목을 삭제하는 기능은 주로 onDelete() 메서드로 처리합니다.• 이때 관련 문서를 찾으면, 다양한 예제와 함께 사용법을 배울 수 있습니다. 예를 들어, ForEach와 onDelete()를 결합하면 리스트의 여러 텍스트 항목을 반복적으로 삭제하는 기능을 구현할 수 있습니다.SwiftData 강의 부분은 오늘이나 내일 중으로 업데이트 예정입니다.감사합니다.Jacob
- 0
- 2
- 312





![Thumbnail image of the [Lv.2] SwiftUI 중급 - Core Data와 SwiftData로 앱 완성하기](https://cdn.inflearn.com/public/courses/331891/cover/3e85efeb-7057-485e-ab5b-8a600150fc5a/331891.png?w=148)
![Thumbnail image of the [Lv.1] iOS 17 앱 개발 기초 - SwiftUI로 시작하기](https://cdn.inflearn.com/public/courses/329815/cover/f0cadfc4-389a-4cef-a67b-72fed66ff4c5/Basic_Cover.png?w=148)