파일 입출력 트러블 슈팅2(URL 접근 문제)

문인범·2025년 4월 13일

Macro 챌린지

목록 보기
6/6
post-thumbnail

Reazy앱에서 파일을 읽는 과정에서 문제가 발생했습니다.
PDF파일을 앱에 업로드 하고 바로 URL을 바로 사용하는 과정에서는 문제가 없었습니다.

그런데 하루가 지나면 파일이 열리지 않는다는 버그가 있다고 합니다.
하루가 지나면 파일이 안열린다…?? 뭔가 좀 이상한 버그인것 같아 경위를 조사해보니 기기가 꺼졌다 켜지면 파일에 접근에 문제가 생기는 것이었습니다

이 문제를 해결하기 위해 파일을 여는 코드를 봤습니다.

기존 코드

// in PaperListView.swift
private func navigateToPaper(_ id: UUID) {
    guard let selectedPaper = homeViewModel.paperInfos.first(where: { $0.id == id }) else {
        return
    }
    // 일부분 생략
    ...
    
    if url.startAccessingSecurityScopedResource() {
        navigationCoordinator.push(.mainPDF(paperInfo: selectedPaper))
        url.stopAccessingSecurityScopedResource()
    }
}
// in PDFSharedData.swift
public func makeDocument(from paperInfo: PaperInfo) {
    var isStale: Bool = false
    
    if let url = try? URL.init(resolvingBookmarkData: paperInfo.url, bookmarkDataIsStale: &isStale),
       url.startAccessingSecurityScopedResource() {
        
        defer {
            url.stopAccessingSecurityScopedResource()
        }
        
        let document = PDFDocument(url: url)
        self.document = document
        
        if let originalPaper = self.paperInfo, originalPaper.id == paperInfo.id {
            return
        }
        self.paperInfo = paperInfo
    }
}

해당 부분에서 코드만 보면 문제가 없습니다.
엔티티에서 저장된 URL의 BookmarkData를 URL로 변환 시켜 사용을 하게 됩니다.

하지만 여기서 문제가 발생하게 됩니다.

문제점

마지막에 코드를 보게 되면 .startAccessingSecurityScopedResource() 라는 메소드가 사용됩니다.
이 메소드는 url이 앱 외부에 있는 데이터일 때 접근을 위해 퍼미션을 받는 작업입니다.
이것이 왜 문제가 되느냐 하면

저희 앱의 기존 업로드 로직은 아래와 같았습니다.

  • 파일을 업로드 하면 해당 파일에 대한 참조를 CoreData에 저장(즉 원본 파일의 URL을 저장)

그래서 해당 파일은 무조건 앱 외부에 있기 때문에 해당 URL을 사용하기 전 퍼미션을 받기 위해 .startAccessingSecurityScopedResource() 메소드를 사용하게 됩니다.

하지만 프로젝트 QA 과정에서 논문을 수정하고 저장을 할 시 원본을 참조하고 있기 때문에 원본이 변형되는 문제가 발생해 다음과 같이 로직을 변경했습니다.

  • 파일을 업로드 하면 URL에 있는 파일을 앱 내 샌드박스에 Document Container에 저장하고 해당 URL을 CoreData로 저장

이 과정에서 이제 앱 내부에서 파일을 불러오기 때문에 .startAccessingSecurityScopedResource() 메소드는 필요가 없게 되었습니다.

여기서 문제가 발생하게 되었습니다.

해당 메소드는 URL이 퍼미션을 받을 필요가 없을 경우(앱 내부의 URL이라 접근 허락을 받을 필요가 없을 경우) 항상 false를 반환하기 때문에 정상적으로 논문이 열리지 않았습니다.
그래서 해당 문제를 해결하기 위해 논문을 처음 업로드 할 때만(앱 외부에서 파일을 불러올 때만) 퍼미션을 받는 형식으로 바꾸었습니다.

해결 방안

// in PaperListView.swift
private func navigateToPaper(_ id: UUID) {
    guard let selectedPaper = homeViewModel.paperInfos.first(where: { $0.id == id }) else {
        return
    }
    
    var isStale = false
    let data = selectedPaper.url
    
    guard let url = try? URL.init(resolvingBookmarkData: data, bookmarkDataIsStale: &isStale) else {
        print("bookmarkdata to url failed")
        return
    }
    
    if isStale {
        print("Bookmark(\(url.lastPathComponent)) is stale")
        guard let newURL = try? url.bookmarkData(options: .suitableForBookmarkFile) else {
            print("Unable to create bookmark")
            return
        }
        
        let idx = homeViewModel.paperInfos.firstIndex { $0.id == id }!
        homeViewModel.paperInfos[idx].url = newURL
    }
    
    navigationCoordinator.push(.mainPDF(paperInfo: selectedPaper))
}
// in PDFSharedData.swift
public func makeDocument(from paperInfo: PaperInfo) {
    var isStale: Bool = false
    
    do {
        let url = try URL(resolvingBookmarkData: paperInfo.url, bookmarkDataIsStale: &isStale)
        let document = PDFDocument(url: url)
        self.document = document
        
        if let originalPaper = self.paperInfo, originalPaper.id == paperInfo.id {
            return
        }
        self.paperInfo = paperInfo
        
    }
    ...
}

이렇게 CoreData에서 받아온 데이터를 사용하는 부분에서는 퍼미션 코드를 완전 빼서 해결을 했습니다.

의문점

다만 의문점이 든게 그러면 앱이 foreground 상태일 때에는 앱 내부에 있는 url에 대한 .startAccessingSecurityScopedResource() 메소드를 사용해도 통과를 했는데
기기를 종료했다 다시 실행하면 이것이 막히는 것인지 해결을 못하긴 했습니다.

이 부분에 대해선 좀 더 공부를 해 볼 필요성을 느꼈습니다!

profile
월클 개발자를 향한 도전일지

0개의 댓글