[트러블슈팅] 01. ModelContainer 충돌 문제

2026. 1. 5. 23:38·탭탭 - TapTap/트러블슈팅
728x90

문제

문제 1. SwiftData로 데이터를 처리(CRUD)하던 중 아래와 같은 Log와 함께 크래시가 발생

SwiftData error: Attempt to use ModelContext from a different container

 

문제 2. 메모리 초과, SQLite lock 관련 에러 발생

 

문제 3. 여러 곳에서 동시에 호출 시 동시성 문제 발생

 

분석

public enum AppGroupContainer {
  public static func createShareModelContainer() -> ModelContainer? {
    let appGroupID = "Team ID"
    let schema = Schema([LinkItem.self, HighlightItem.self, CategoryItem.self])
    
    guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID) else {
      print("Error: Failed to get container URL for App Group ID: \(appGroupID)")
      return nil
    }
    
    let storeURL = containerURL.appendingPathComponent("Nbs_store.sqlite")
    let configuration = ModelConfiguration(schema: schema, url: storeURL)
    
    do {
      return try ModelContainer(for: schema, configurations: [configuration])
    } catch {
      print("Error creating ModelContainer: \(error)")
      return nil
    }
  }
}


 우리 앱은 App Group을 통해 ShareExtension <-> SafariExtension <-> App 사이의 통신을 하고 있었습니다. 세 환경이 동일한 DB(Nbs_store.sqlite)를 공유해야 했기 때문에, App Group의 컨테이너 경로를 활용하는 AppGroupContainer를 별도로 생성해서 단일 DB 접근이 가능하도록 설계했습니다. 

 

 이때, AppGroupContainer 내부에서는 팩토리 메소드 패턴을 사용해 ModelContainer를 생성하도록 구현했습니다. 팩토리 메소드 패턴은 코드 구조를 단순하게 유지할 수 있었지만, 결과적으로 CRUD 처리를 수행할 때마다 새로운 ModelContainer 인스턴스가 생성되는 문제가 있었습니다.

 

 SwiftData에서 ModelContainer는 앱의 presistent store를 관리하는 핵심 객체로, 일반적으로 앱 전체에서 단일 인스턴스로 유지되어야 합니다. 그러나 여러 ModelContainer가 동일한 App Group 경로의 DB 파일을 동시에 참조하면서, 스토어 접근 충돌과 컨텍스트 간 불일치가 발생했습니다.

 

해결

  ModelContainer는 앱 전체에서 단 하나만 존재해야하는 객체이기 때문에, 매번 재생성하면 크래시가 발생하므로 싱글톤으로 수정해서 문제를 해결했습니다

 

 SwiftData 공식 가이드 문서에서는 명시적으로 "단일 인스턴스만 사용하라"고 문서화하지는 않았지만, 여러 문서에서 단일 인스턴스를 사용하는 것을 권장한다는 것을 내용을 바탕으로 문제를 해결했습니다. 

 

public enum AppGroupContainer {
  public static let shared: ModelContainer = {
    let appGroupID = "Team ID"
    let schema = Schema([ArticleItem.self, HighlightItem.self, CategoryItem.self])
    
    guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID) else {
      fatalError()
    }
    
    let storeURL = containerURL.appendingPathComponent("Nbs_store.sqlite")
    let configuration = ModelConfiguration(schema: schema, url: storeURL)
    
    do {
      return try ModelContainer(for: schema, configurations: [configuration])
    } catch {
      fatalError()
    }
  }()
}

 

참고자료

https://stackoverflow.com/questions/78052704/swiftdata-redundant-modelcontainer-instances-causing-data-constraint-loss

"While the documentation says that an app "must have at least one" container it also recommends that you use the .modelContainer modifier so that the container is shared for all your views.

 

https://medium.com/@mrdanteausonio/how-to-access-swift-data-model-context-outside-of-view-hirearchy-using-a-data-service-8508f7a94683

'탭탭 - TapTap > 트러블슈팅' 카테고리의 다른 글

[트러블슈팅] 02. LinkNavigator item 디코딩 시 Base64 문자열 처리 오류  (0) 2026.01.05
'탭탭 - TapTap/트러블슈팅' 카테고리의 다른 글
  • [트러블슈팅] 02. LinkNavigator item 디코딩 시 Base64 문자열 처리 오류
여성일
여성일
  • 여성일
    성일노트
    여성일
  • 전체
    오늘
    어제
    • 분류 전체보기 N
      • 탭탭 - TapTap N
        • 리팩토링 N
        • 트러블슈팅
        • 개발일지
      • 애플 디벨로퍼 아카데미
        • 챌린지 회고
        • 하루의 날씨
      • Swift Student Challenge 202..
      • AI를 잘쓰는 개발자가 될래요
      • 우리 같이 협업하자
      • ToyProject - 사카마카 (살까말까 고민 ..
      • ToyProject - Book2OnNon (모바..
      • ToyProject - 바꿔조 (환율 계산기)
      • iOS
        • iOS
        • Vapor
        • Design Pattern
        • CoreData
        • Tuist
        • RxSwift
        • ReactorKit
        • TCA
      • Swift
        • Swift 기본기
        • UIkit
        • SwiftUI
      • UX, 사용성
      • 원티드 프리온보딩 챌린지 iOS 과정
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      F
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.6
    여성일
    [트러블슈팅] 01. ModelContainer 충돌 문제
    상단으로

    티스토리툴바