본문 바로가기
728x90
SMALL

MovieClip54

😁 이런 날이 오네?... MovieClip 회고 👏 일단 앱을 완성했다.영화와 티비 시리즈 정보를 받아오고, 리뷰를 작성할 수 있다.  다른 사람들 회고록 보면 4주도 안되서 상당한 퀄리티의 앱을 개발한 걸 여럿봤다.  그에 비해...  나느 이걸 만드는데 거짓 8주는 걸렸다..  주된 기능은 Swift + UIKit + Firebase정도이다.  구글 번역 API 를 통해 영화 정보를 번역했는데.. 이건 비용 문제로 중지 햇다.   여튼 이번 앱을 만들면서 제일 중요하다고 생각된건...   입력값, 출력값, 보내는 시점, 받는 시점, 보내는 양식, 받는 양식  대부분 오류가 발생한 경우가 데이터의 타입이 맞지 않거나, 시점이 잘못되었거나...  너무 많은 데이터를 효율적으로 처리하기 위한 관리 등..  아 그리고 기획서..   앱을 만들 때 큰 틀.. 2025. 3. 13.
❌ 리뷰 삭제 ... 왜 안되니? 🔍 GTMSessionFetcher 오류 분석GTMSessionFetcher 0x10141bab0 (https://firebasestorage.googleapis.com:443/v0/b/movieclip-6a2c3.firebasestorage.app/o?delimiter=/&prefix=users/L21gNu8OeIQa9BIDGuaoFwJmXD63/reviews/E6380A5A-CB53-490C-A044-D8E962791A2D/) was already running이 오류 메시지는 Firebase Storage에서 같은 URL에 대한 여러 개의 비동기 요청이 동시에 실행되고 있음을 의미합니다.  🤔 리뷰 삭제 1️⃣ Firebase Storage에서 이미지 삭제 2️⃣ Firestore 데이터 삭제?.. 2025. 3. 11.
❌ 컬렉션 뷰를 가로 스크롤할 때 다음 이미지가 살짝 보이는 문제 🔍 문제 원인layout.itemSize = CGSize(width: UIScreen.main.bounds.width, height: 300) UIScreen.main.bounds.width는 전체 화면 너비를 기준으로 설정됩니다.하지만, UICollectionView는 UITableViewCell 안에 있어서 contentView.bounds.width를 기반으로 설정해야 합니다.layoutSubviews()에서 collectionView.frame = contentView.bounds로 설정되어 있지만, layout.itemSize가 초기 생성 시점에 설정되므로 변경되지 않을 수 있음.따라서 컬렉션 뷰의 실제 크기보다 itemSize가 작거나 크면, 다음 아이템이 살짝 보이는 문제 발생. 🛠 해결.. 2025. 3. 11.
❌ 컴파일 오류 발생... // ❌ 컴파일 오류 발생snapshot.appendItems(viewModel.reviews, toSection: .myReviews)// Error: Cannot convert value of type '[ReviewItem]' to expected argument type '[ProfileItem]' 🔥 원인viewModel.reviews는 [ReviewItem] 타입이지만, Diffable DataSource는 ProfileItem을 다룬다.따라서 ReviewItem을 ProfileItem.review(ReviewItem)으로 변환해야 한다. 1. ProfileItem.review(ReviewItem)으로 변환하는 이유✅ Diffable DataSource는 ProfileItem 타입만 다룰 수.. 2025. 3. 9.
📌 회원탈퇴 구현 (Storage, Firestore Database) ✅ 회원 탈퇴 로직  Firebase Storage에서 프로필 이미지 삭제→ deleteProfilePhoto(for: userID)→ 이미지가 없으면 실패해도 계속 진행 (catch { _ in Just(()) })Firestore에서 유저 데이터 삭제→ collectionUsers(deleteUser: userID)Firebase Authentication에서 유저 계정 삭제→ deleteAccount()  1️⃣ Firebase Storage 에서 프로필 이미지 삭제final class StorageManager { // MARK: - Variable static let shared = StorageManager() let storage = Storage.sto.. 2025. 3. 4.
✅ 프로필 수정하기! (기존 프로필 입력 창 사용하기) ✅ ProfileCell의 editButton을 눌렀을 때, ProfileDataFormViewController로 이동 & Firebase 업데이트 흐름현재 구현하려는 기능을 정리하면:1️⃣ ProfileCell의 editButton을 누르면 ProfileDataFormViewController로 이동2️⃣ 이동한 ProfileDataFormViewController에서 기존 user 정보를 받아서 표시3️⃣ 유저가 정보 수정 후 "완료" 버튼을 누르면 Firebase에 업데이트4️⃣ ProfileViewController에서 UI를 업데이트하여 변경된 프로필 정보를 반영🔥 가장 좋은 방법은? ✔ Delegate 패턴을 활용해서 수정된 데이터를 ProfileViewController로 전달✔ Fire.. 2025. 3. 4.
❌ 문제 해결... ProfileItem이 Hashable 및 Equatable 프로토콜을 준수하지 않는다? ❓ ProfileItem 열거형에 기존의 MovieClipUser 라는 구조체를 데이터 타입으로 사용할 경우 에러 발생  📍데이터 모델 (Item) 만들기enum ProfileItem: Hashable { case profile(MovieClipUser) // 기존 모델 사용 ...} 📍기존 데이터 모델 struct MovieClipUser: Codable { let id: String var username: String = "" var createOn: Date = Date() var bio: String = "" var avatarPath: String = "" var clipMovies: [String] = [] var isUserOnboard.. 2025. 3. 1.
💾 Firestore 에 유저 정보 저장 및 이미지 업로드 🔷 StorageManager.swiftfinal class StorageManager { // MARK: - Variable static let shared = StorageManager() let storage = Storage.storage() // MARK: - Function /// 프로필 이미지를 firestore에 저장하는 메서드 func uploadProfilePhoto(with userID: String, image: Data, metaData: StorageMetadata) -> AnyPublisher { return storage .reference() .. 2025. 2. 28.
👤 Firebase에 로그인, 회원정보를 FireStore 저장, 회원정보 불러오기 ✅ 로그인 기능 구현 /// 이메일과 비밀번호로 로그인하는 함수func loginUser(email: String, password: String) -> AnyPublisher { return Auth.auth().signIn(withEmail: email, password: password) .map(\.user) // ✅ authResult?.user 만 추출 .eraseToAnyPublisher()}✅ 특징✔️ signIn(withEmail:password:)는 원래 Future 형태로 제공됨 → 따로 감쌀 필요 없음✔️ map(\.user)를 사용해 authResult?.user만 반환하여 더 간결한 코드✔️ .eraseToAnyPublisher()로.. 2025. 2. 27.
👤 Firebase에 이메일 & 비밀번호 회원가입 기능 (MVVM + Combine) 🔷 SceneDelegate.swift🏆 전체 흐름 정리1️⃣ 앱이 실행되면 scene(_:willConnectTo:options:)이 호출됨.2️⃣ setupWindow()를 통해 UIWindow를 초기화하고, 화면을 표시할 준비를 함.3️⃣ checkAuthentication()에서 현재 로그인 상태를 확인함.로그인이 안 되어 있다면 gotoController(with: OnboardingViewController()) 실행로그인이 되어 있다면 gotoController(with: MainTabBarController()) 실행4️⃣ gotoController(with:)가 rootViewController를 변경하고 애니메이션을 적용하여 화면을 전환함.class SceneDelegate: UIR.. 2025. 2. 26.
🌟 회원 가입 화면 뷰 관리? 순서?는 어떻게 해야하나? ✅ 현재 코드의 흐름📌 회원가입 성공 시RegisterViewController에서 회원가입이 성공하면 현재 띄워진 모든 모달을 닫음 (dismiss())NotificationCenter를 사용해 SceneDelegate에게 회원가입 성공을 알림 (CreateUserSuccess)SceneDelegate에서 checkAuthentication() 실행checkAuthentication()에서 window?.rootViewController를 MainTabBarController로 변경✔️ 지금 코드의 장점✅ 앱의 전체적인 화면 이동을 SceneDelegate에서 관리해서 중앙 집중식 제어 가능✅ NotificationCenter를 사용해 뷰 컨트롤러 간 의존성을 줄임class SceneDelegate:.. 2025. 2. 26.
❌ 문제 해결 - 검색 결과의 상세페이지 이동이 안됨.. ✅ NavigationController에서 push가 안 되는 이유 & 해결 방법🚀 현재 didSelectItemAt은 잘 호출되고 있지만, pushViewController가 동작하지 않는 문제👉 이유: SearchResultViewController는 UISearchController의 searchResultsController로 설정되어 있어서 네비게이션 스택에 포함되지 않음!👉 따라서 pushViewController가 동작하지 않음! 🔥 해결 방법: navigationController로 감싸서 Push 가능하게 만들기✅ 1. present()가 아니라 pushViewController()를 사용하고 싶다면?✔ DetailViewController를 UINavigationControll.. 2025. 2. 24.
🔨 검색결과 내의 영화, 티비의 장르 가져오는 부분 개선 📍 현재 장르 정보가 늦게 나옴... (좌측)  / 이제는 잘 나옴 (우측)✅ 검색 결과에서 장르 정보를 가져오는 코드 검토 & 개선점class SearchViewModel: ObservableObject { ... @Published var fetchedGenres: [Int: [String]] = [:] // 장르 저장 변수 // MARK: - 검색 실행 func search(query: String) { currentQuery = query resetState() Task { do { let results = try await searchServ.. 2025. 2. 24.
🔥 MVVM + Combine을 통한 검색기능 구현 2편 구현 순서? ✅ MVVM + Combine + UISearchViewController를 사용한 검색 기능 구현 순서 📍 Network 설정 TMDB API를 통해 데이터를 가져올 함수 구현// MARK: - APICallerclass NetworkManager { static let shared = NetworkManager() ... // MARK: - Search /// 검색어를 통해 영화 목록 결과 func searchMovie(with keyword: String, page: Int = 1) async throws -> SearchResultMedia { let url = URL(string: "\(Constants.base.. 2025. 2. 24.
🤔 createDataSource() 메서드에서 "검색 결과 전체보기" 버튼 동작 🔹 1. SearchFooterView의 역할📌 SearchFooterView는 UICollectionView의 각 섹션(영화, TV, 인물) 하단에 추가되는 버튼을 포함하는 뷰📌 "검색 결과 전체보기" 버튼을 누르면, 추가 데이터를 로드할 수 있도록 loadMoreAction 클로저를 실행하는 구조class SearchFooterView: UICollectionReusableView { // MARK: - Variable static let reuseIdentifier: String = "SearchFooterView" private var loadMoreAction: (() -> Void)? // MARK: - UI Component let.. 2025. 2. 24.
728x90
LIST