본문 바로가기
감정일기(가칭)

🍋 Swift FileManager 설계 — private 유지하면서 안전하게 경로 노출하기

by 밤새는 탐험가89 2025. 10. 19.
728x90
SMALL

앱을 개발하다 보면 FileManager를 이용해 이미지를 저장하거나 불러오는 기능을 자주 구현하게 된다.


특히 나처럼 Core Data + FileManager를 함께 쓰는 경우라면,
이미지 저장 폴더 경로를 여러 곳에서 접근해야 하는 상황이 생긴다.

 

예를 들어 DiaryImageFileManager 클래스 내부에서
이런 코드가 있다고 해보자 👇

private func getDocumentsDirectory() -> URL {
    guard let doc = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
        LogManager.print(.error, "Document 디렉토리를 찾을 수 없습니다.")
        return URL(fileURLWithPath: "")
    }

    let folder = doc.appendingPathComponent(folderName)
    if !FileManager.default.fileExists(atPath: folder.path) {
        try? FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
    }
    return folder
}

 

여기서 문제가 생긴다.
이 함수가 private으로 선언되어 있어서, 다른 클래스에서는 이 경로를 직접 가져올 수 없다는 점.

그래서 단순히 이렇게 생각하게 된다.

 

“그럼 그냥 private을 빼면 되지 않나?” 🤔

 

하지만… 그건 좋은 방법이 아니다.


🚨 왜 private을 쉽게 풀면 안 될까?

private은 단순한 접근 제어가 아니라 “의도”를 담는 장치다.
즉, “이 메서드는 외부에서 직접 호출하지 말라”는 의미야.

 

FileManager에서 폴더 경로를 생성하거나 관리하는 로직은
앱 내부 데이터의 구조와 직접적으로 연결돼 있다.
이걸 외부에서 무분별하게 호출할 수 있게 두면,

 

1. 다른 클래스에서 중복 폴더 생성

2. 잘못된 시점의 파일 삭제

3. 데이터 정합성 깨짐


등의 문제가 발생할 수 있다.

 

즉, 정보 은닉(Encapsulation) 을 지켜야만
클래스 내부의 일관성과 안정성을 유지할 수 있다.


🧩 그렇다면 대안은?

✅ 방법 1. 읽기 전용(computed) 프로퍼티로 노출하기

가장 깔끔하고 많이 사용하는 패턴이다.
폴더 경로를 직접 계산해 반환하되, 외부에서는 읽기만 가능하게 한다.

final class DiaryImageFileManager {
    static let shared = DiaryImageFileManager()
    private init() {}

    private let folderName = "EmotionDiaryImages"

    /// 👍 외부에서 접근 가능한 읽기 전용 폴더 URL
    var folderURL: URL {
        getDocumentsDirectory()
    }

    private func getDocumentsDirectory() -> URL {
        guard let doc = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
            LogManager.print(.error, "Document 디렉토리를 찾을 수 없습니다.")
            return URL(fileURLWithPath: "")
        }

        let folder = doc.appendingPathComponent(folderName)
        if !FileManager.default.fileExists(atPath: folder.path) {
            do {
                try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
            } catch {
                LogManager.print(.error, "이미지 폴더 생성 실패: \(error.localizedDescription)")
            }
        }
        return folder
    }
}

 

💡 핵심 포인트

- getDocumentsDirectory()는 여전히 private 상태 유지

- 외부에서는 DiaryImageFileManager.shared.folderURL 로 접근

- 외부 클래스는 폴더 생성 로직을 전혀 알 필요가 없음

 

✅ 결과: “읽기만 가능하지만, 내부 구조는 보호됨”
즉, 정보 은닉(Encapsulation)재사용성(Reusability) 의 완벽한 균형!


✅ 방법 2. 경로 대신 결과만 반환하는 헬퍼 메서드 제공

만약 외부에서 “폴더 안의 파일 목록”처럼
결과물만 필요하다면, 경로 자체를 노출하지 않고
필요한 데이터만 반환하는 메서드를 만들어도 된다.

extension DiaryImageFileManager {
    /// 감정일기 폴더의 URL 리스트 반환
    func fetchDiaryFolderURLs() -> [URL] {
        let baseFolder = getDocumentsDirectory()
        return (try? FileManager.default.contentsOfDirectory(at: baseFolder, includingPropertiesForKeys: nil)) ?? []
    }
}

 

이렇게 하면 외부에서는 baseFolder에 직접 접근할 수 없고,
FileManager 내부의 경로 관리 로직도 완전히 캡슐화된다.


⚙️ 접근 제어자의 의도 다시 보기

접근 제어자 설명 사용 시점
private 오직 이 클래스 내부에서만 접근 가능 내부 로직, 헬퍼 함수
fileprivate 같은 파일 내 다른 타입에서 접근 가능 협력형 타입 있을 때
internal 같은 모듈 내 어디서든 접근 가능 (기본값) 앱 내부 공용
public 외부 모듈에서도 접근 가능 프레임워크 공개용
open 외부 모듈 + subclass/override 허용 SDK, 라이브러리 제작 시

✨ 결론

항목 결론
getDocumentsDirectory() 그대로 private 유지
folderURL ✅ 읽기 전용 computed property로 외부 접근 허용
외부에서 폴더 접근 folderURL 또는 별도 메서드로만 가능

 

728x90
LIST