iOS 책 검색 앱 개발기

호씨·2025년 1월 6일
0

iOS 책 검색 앱 개발기 (트러블 슈팅 포함) 📚

프로젝트 소개 💡

카카오 도서 검색 API를 활용하여 책을 검색하고 관리할 수 있는 iOS 앱을 개발하였다. MVVM 패턴을 적용하고 CoreData를 활용한 데이터 관리, 무한 스크롤 등 다양한 기능을 구현하였다.

개발 환경 🛠

  • iOS 17.0+
  • Xcode 15
  • Swift 5.9
  • SnapKit
  • CoreData

주요 기능 구현 💻

1. 검색 기능 구현

class BookSearchViewModel {
    private let apiKey = "YOUR_API_KEY"
    private(set) var books: [Book] = []
    var onBooksUpdated: (() -> Void)?
    
    func searchBooks(query: String) {
        guard let encodedQuery = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
        // API 호출 및 데이터 처리
    }
}

2. 무한 스크롤 구현

extension BookSearchViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let position = scrollView.contentOffset.y
        let contentHeight = scrollView.contentSize.height
        let screenHeight = scrollView.bounds.height
        
        if position > contentHeight - screenHeight - 100 {
            viewModel.loadNextPageIfNeeded()
        }
    }
}

3. CoreData 구현

class CoreDataManager {
    static let shared = CoreDataManager()
    
    func saveBook(_ book: Book) {
        let context = persistentContainer.viewContext
        let bookEntity = BookEntity(context: context)
        // 데이터 저장 로직
    }
}

트러블 슈팅 🔨

1. 탭 리스트 구현 문제

문제 상황

  • TabBarController 구현 시 화면 전환에서 데이터 초기화 발생
  • 탭 간 이동 시 메모리 누수 발생
  • 북마크 탭에서 데이터 갱신이 실시간으로 되지 않는 문제

해결 방법

class ViewController: UITabBarController {
    private func setupTabBar() {
        let searchVC = BookSearchViewController()
        let bookmarkVC = BookmarkViewController()
        
        // Delegate 패턴을 통한 데이터 전달 구현
        searchVC.bookmarkDelegate = bookmarkVC
        
        let searchNav = UINavigationController(rootViewController: searchVC)
        let bookmarkNav = UINavigationController(rootViewController: bookmarkVC)
        
        searchNav.tabBarItem = UITabBarItem(
            title: "검색",
            image: UIImage(systemName: "magnifyingglass"),
            selectedImage: UIImage(systemName: "magnifyingglass.fill")
        )
        
        bookmarkNav.tabBarItem = UITabBarItem(
            title: "저장된 책",
            image: UIImage(systemName: "bookmark"),
            selectedImage: UIImage(systemName: "bookmark.fill")
        )
        
        self.viewControllers = [searchNav, bookmarkNav]
    }
}

// BookmarkViewController에 viewWillAppear 추가로 데이터 갱신 문제 해결
extension BookmarkViewController {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        bookmarkTableView.reloadData()
    }
}

2. 최근 본 책 기능 구현 문제

문제 상황

  • CoreData에 최근 본 책 저장 시 중복 데이터 발생
  • 최근 본 책 정렬 순서가 맞지 않는 문제
  • 앱 재실행 시 데이터가 초기화되는 현상

해결 방법

extension CoreDataManager {
    func saveRecentBook(_ book: Book) {
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "RecentBook")
        fetchRequest.predicate = NSPredicate(format: "isbn == %@", book.isbn)
        
        do {
            let results = try context.fetch(fetchRequest)
            if let existing = results.first {
                // 이미 존재하는 경우 날짜만 업데이트
                existing.setValue(Date(), forKey: "viewedAt")
                existing.setValue(book.thumbnail, forKey: "thumbnail")
                existing.setValue(book.title, forKey: "title")
            } else {
                // 새로운 책 추가
                let entity = NSEntityDescription.entity(forEntityName: "RecentBook", in: context)!
                let recentBook = NSManagedObject(entity: entity, insertInto: context)
                recentBook.setValue(book.isbn, forKey: "isbn")
                recentBook.setValue(Date(), forKey: "viewedAt")
                recentBook.setValue(book.thumbnail, forKey: "thumbnail")
                recentBook.setValue(book.title, forKey: "title")
            }
            saveContext()
            
            // 10개 제한 유지
            cleanupOldRecentBooks()
        } catch {
            print("Error saving recent book: \(error)")
        }
    }

    private func cleanupOldRecentBooks() {
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "RecentBook")
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "viewedAt", ascending: false)]
        
        do {
            let results = try context.fetch(fetchRequest)
            if results.count > 10 {
                for index in 10..<results.count {
                    context.delete(results[index])
                }
                saveContext()
            }
        } catch {
            print("Error cleaning up old recent books: \(error)")
        }
    }
}
profile
이것저것 많이 해보고싶은 사람

0개의 댓글

관련 채용 정보