Core Data 모델 로딩 실패 문제

jeongmuyamette·2025년 2월 7일

TIL

목록 보기
40/72
post-thumbnail

1. MapViewController

  • 지도 화면의 메인 컨트롤러입니다.
  • 주요 기능:
    • 지도 표시 및 위치 권한 관리
    • 로또 판매점 데이터 로드
    • 마커 관리
    // MARK: - Properties
    private let mapView = NMFMapView()
    private let locationManager = CLLocationManager()
    private let lottoAPIManager = LottoAPIManager.shared
    private var stores: [LottoStore] = []
    private var visibleMarkers: [String: NMFMarker] = [:]
    private lazy var markerManager = MarkerManager(mapView: mapView)
    private let geocodingService = GeocodingService()
    private let notificationCenter = UNUserNotificationCenter.current()
    private let monitoringRadius: CLLocationDistance = 1000 // 1km 반경
    private var monitoredRegions: [CLCircularRegion] = []
    

2. LottoAPIManager

  • 로또 판매점 데이터 관리를 담당합니다.
  • 주요 기능:
    • 전체 판매점 조회 (fetchAllLottoStores)
    • 주변 판매점 조회 (fetchNearbyLottoStores)
    • 거리 기반 필터링
    func fetchAllLottoStores(completion: @escaping (Result<[LottoStore], Error>) -> Void) {
        // 모든 매장을 불러오는 로직 구현
        let allStores = CoreDataManager.shared.fetchStores()
        completion(.success(allStores))
    }
        
//    // 현재 위치 기반 로또 판매점 조회 메서드 추가
//    func fetchNearbyLottoStores(latitude: Double,
//                                longitude: Double,
//                                radius: Int = 3000,
//                                completion: @escaping (Result<[LottoStore], Error>) -> Void) {
            //        print("📡 검색 반경: \(radius)m")
//        // 이전 작업 취소
//        currentTask?.cancel()
            do {
        currentTask = Task {
            do {
                // 1. CoreData에서 전체 데이터 로드
                let allStores = CoreDataManager.shared.fetchStores()
                let currentLocation = CLLocation(latitude: latitude, longitude: longitude)
                // 2. 현재 위치 기준으로 반경 내 판매점 필터링
                let currentLocation = CLLocation(latitude: latitude, longitude: longitude)
                let nearbyStores = allStores.filter { store in
                let nearbyStores = filterStoresByDistance(allStores, currentLocation: currentLocation, radius: Double(radius))
                          let storeLng = Double(store.longitude ?? "") else {
                if Task.isCancelled { return }
                    }
                DispatchQueue.main.async {
                    completion(.success(nearbyStores))
                }
                    return distance <= Double(radius)
            } catch {
                if !Task.isCancelled {
                    DispatchQueue.main.async {
                        completion(.failure(error))
                    }
                }
//                }
        }
    }

3. CoreDataManager

  • 로컬 데이터베이스 관리를 담당합니다.
  • 주요 기능:
    • 판매점 데이터 저장 (saveStores)
    • 저장된 판매점 조회 (fetchStores)
class CoreDataManager {
    static let shared = CoreDataManager()
    
    private init() {}
    
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "LottoStore")
        container.loadPersistentStores { _, error in
            if let error = error {
                fatalError("CoreData 로드 실패: \(error)")
            }
        }
        return container
    }()
    
    var context: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    
    // 판매점 저장
    func saveStores(_ stores: [LottoStore]) {
        context.perform { [weak self] in
            guard let self = self else { return }
            
            do {
                // 기존 데이터 삭제 전에 백업
                let oldStores = self.fetchStores()
                
                // 기존 데이터 삭제
                let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "LottoStore")
                let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
                // 새로운 데이터 저장
                try self.context.execute(deleteRequest)
                    let storeEntity = NSEntityDescription.insertNewObject(forEntityName: "LottoStore", into: self.context)
                // 새로운 데이터 저장
                for store in stores {
                    let storeEntity = NSEntityDescription.insertNewObject(forEntityName: "LottoStore", into: self.context)
                    storeEntity.setValue(String(store.number), forKey: "id")
                    storeEntity.setValue(store.name, forKey: "name")
                    storeEntity.setValue(store.address, forKey: "address")
                    storeEntity.setValue(store.roadAddress, forKey: "roadAddress")
                    storeEntity.setValue(store.latitude, forKey: "latitude")
                    storeEntity.setValue(store.longitude, forKey: "longitude")
                    storeEntity.setValue(store.number, forKey: "number")
                    storeEntity.setValue(Date(), forKey: "lastUpdated")
                }
                
                try self.context.save()
                print("✅ 판매점 데이터 저장 완료: \(stores.count)개")
            } catch {
                print("❌ CoreData 저장 실패: \(error.localizedDescription)")
                // 에러 발생 시 이전 데이터 복구 로직 추가 필요
            }
        }
    }

4. MarkerManager

  • 지도 마커 관리를 담당합니다.
  • 주요 기능:
    • 마커 생성 및 표시
    • 마커 클러스터링
class MarkerManager: NSObject {
    // MARK: - Properties
    private let mapView: NMFMapView
    private var markers: [NMFMarker] = []
    private var clusters: [NMFMarker] = []
    
    // MARK: - Constants
    private enum ClusterSize {
        static let small = 10
        static let medium = 100
        static let large = 500
        static let clusteringDistance: Double = 100.0 // 클러스터링 거리 (미터)
    }
    
    // MARK: - Initialization
    init(mapView: NMFMapView) {
        self.mapView = mapView
        super.init()
        setupMapView()
        setupMapViewDelegate()
    }
    
    private func setupMapView() {
        mapView.minZoomLevel = 6
        mapView.maxZoomLevel = 18
    }
    
    private func setupMapViewDelegate() {
        // 지도 줌 레벨 변경 시 클러스터링 업데이트
        mapView.addCameraDelegate(delegate: self)
    }
    
    // MARK: - Public Methods
    
    func clearMarkers() {
        markers.forEach { $0.mapView = nil }
        markers.removeAll()
        clusters.forEach { $0.mapView = nil }
        clusters.removeAll()
    }
    
    func removeAllMarkers() {
        clearMarkers()
    }
    
    // MARK: - Private Methods
    func createMarkers(for stores: [LottoStore]) {
        clearMarkers()
        
        // 마커 생성 및 즉시 지도에 표시
        stores.forEach { store in
            if let marker = createMarker(for: store) {
                marker.mapView = mapView  // 즉시 지도에 표시
                markers.append(marker)
            }
        }
    }

5. LocationManager

  • 위치 관련 기능을 관리합니다.
  • 주요 기능:
    • 위치 권한 요청
    • 판매점 모니터링
    func requestLocationAuthorization() {
        locationManager.requestAlwaysAuthorization()
    }
    
    func startMonitoringStores(_ stores: [LottoStore]) {
        print("🔍 LocationManager: 판매점 모니터링 시작...")
        
        // 기존 모니터링 중지
        monitoredRegions.forEach { locationManager.stopMonitoring(for: $0) }
        monitoredRegions.removeAll()
        
        for store in stores {
            guard let latitude = Double(store.latitude ?? ""),
                  let longitude = Double(store.longitude ?? ""),
                  let storeId = store.id else {
                print("⚠️ 판매점 좌표 오류: \(store.name)")
                continue
            }
            
            let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)

데이터 흐름

  1. 앱 실행 시:

    • 위치 권한 확인 및 요청
    • Core Data에서 저장된 판매점 데이터 로드
  2. 판매점 데이터 처리:

    • LottoAPIManager가 Core Data에서 데이터 로드
    • 위치 기반 필터링 수행
    • 필터링된 데이터를 MarkerManager에 전달
  3. 마커 표시:

    • MarkerManager가 받은 데이터로 마커 생성
    • 지도에 마커 표시
    • 필요시 클러스터링 수행

주요 이슈

현재 Core Data 모델 로딩 실패 문제가 있습니다:

  • Failed to load model named LottoStore 오류 발생
  • Core Data 모델 파일 (.xcdatamodeld)이 없거나 잘못 설정되어 있을 수 있습니다.

이 문제를 해결하기 위해서는 Core Data 모델을 올바르게 생성하고 설정해야 합니다.

0개의 댓글