2010, Android 사용자들이 iPhone 을 사용하지 않는 이유 중 이런 이유를 들은 적이 있습니다.
안드로이드는 그냥 USB 꼽아서 바로 파일 관리하면되는데, iPhone 은 안되니까 불편해
글쓰는 시점인 2022년에는 애플 제품에 "File(파일)" 이라는 앱을 통해서 관리할 수 있어서 편리해지긴 했습니다만, 과거에는 위 불편사항이 있었습니다.
iOS(기타 Apple 제품) 의 파일 저장구조 혹은 앱 저장구조가 어떠하길래, 이런 불편함이 있을까 조사하다가, Sandbox 개념에 대해서 정리하게 되었습니다.
(출처: www.sandboxie.com)
Programs 에 보면, 친근한 친구들이 몇몇 보이죠. 아이콘 상으로는 없으나, 우리가 만든 App 들도 이곳에 위치합니다. 위에 그림은 샌드박스가 없는 하드디스크이고, 아래 있는 그림은 샌드박스가 적용된 하드디스크 입니다.
빨간색 박스가 새롭게 들어가는 데이터 입니다. 샌드박스가 없는 경우, 정해진 위치에 저장되는 것이 아니라, 무작위로(혹은 우리가 규칙을 알 수 없는) 저장되고 있습니다. 이에 반해, 샌드박스가 적용된 하드디스크는, 노랑색 상자 안에만 저장됩니다.
이렇게 특정 지역에서만 '안전하게' 저장하도록 하는 정책을 "샌드박스(SandBox)" 라고 칭합니다.
어원
현실 세계에서의 SandBox 는 위 그림입니다. 아이들이 놀 곳을 정해놓은 모래상자이죠. 우리나라로 적용해보자면, 모래가 있는 놀이터 내부에만 놀이기구를 추가하니, 그곳을 샌드박스라고 칭할 수 있겠네요.
SandBox 가 적용되지 않은 하드디스크는, 데이터를 추가하는 과정에서 '직접적인' 접근이 가능합니다. 이 때, OS를 손상시킬 목적으로 잘못된 데이터를 추가할 우려가 발생하죠.
반면에,
SandBox 가 적용된 하드디스크의 경우, 해당 범위가 한정됩니다. 그러므로 설사 피해가 가더라도 정해진 범위로 축소되겠죠.
그러면, iOS App 에서는 어떤 방식으로 SandBox 를 적용하고 있을까요?
(출처: Exploring iOS's Sandbox)
class FileManager: NSObject
이 클래스를 통해서 데이터를 지지고 볶을 수 있습니다.
해당 위치는 "Document" 에 위치한 데이터 입니다.
App 이 설치된 경로
/Users/kimwoosung/Library/Developer/CoreSimulator/Devices/242C41B9-01E6-4A9A-AC57-087610FF5848/data/Containers/Data/Application/A58B3A4C-BBCD-4EF6-9B2C-A1C027A2BB4D
(Simulator 로 확인한 App 위치)
print(NSHomeDirectory)
앱에서 저장된 데이터는 "Application/{app id}/Documents" 에 저장됩니다.
let filemgr = FileManager.default
let dirPaths = filemgr.urls(for: .documentDirectory, in: .userDomainMask)
let docsDir = dirPaths[0].path
print("DEBUG: " + docsDir)
.documentDirectory
는 앱의 home directory 에 해당합니다./Users/kimwoosung/Library/Developer/CoreSimulator/Devices/242C41B9-01E6-4A9A-AC57-087610FF5848/data/Containers/Data/Application/E09202B4-28E2-481D-98D4-A883E70B4123/Documents
(위 코드로 실행한 Documents Path)
애플이 정해둔 Sandbox 내에서는 자유롭게 데이터를 추가할 수 있습니다.
let docsURL = dirPaths[0]
let newDirectory = docsURL.appending(path: "newPath").path
print("DEBUG: " + newDirectory)
/Users/kimwoosung/Library/Developer/XCPGDevices/35CF0D01-2CC4-4886-891A-8135F6F42211/data/Containers/Data/Application/30FD8D27-2D8C-441A-BB8E-268F523C595F/Documents/newPath
폴더 생성 및 파일 생성 전체 코드입니다.
import Foundation
protocol FileHandling {
func createFolder() throws -> FileHandler
func createFile(_ fileName: String,
content: String
) throws -> FileHandler
}
class FileHandler: FileHandling {
// 에러 정의
enum FileHandlingError: Error {
case failedCreateFolder
case filedCreateFile
}
let fileManager: FileManager
var directoryURL: URL!
init() {
fileManager = FileManager.default
}
// 폴더 생성
func createFolder() throws -> FileHandler {
guard let documentsURL = fileManager
.urls(for: .documentDirectory,
in: .userDomainMask).first else {
throw FileHandlingError.failedCreateFolder
}
directoryURL = documentsURL.appending(path: "TestFolor")
do {
try fileManager.createDirectory(
at: directoryURL,
withIntermediateDirectories: false,
attributes: nil
)
return self
} catch {
print("\(#function): " + error.localizedDescription)
throw FileHandlingError.failedCreateFolder
}
}
// 파일 생성
func createFile(
_ fileName: String,
content: String) throws -> FileHandler {
let filePath = directoryURL.appending(
path: fileName)
do {
try content.write(
to: filePath,
atomically: false,
encoding: .utf8
)
return self
} catch {
throw FileHandlingError.filedCreateFile
}
}
}
이 코드는 다음 코드로 실행했습니다.
struct ContentView: View {
let fileHandler: FileHandler
init() {
self.fileHandler = FileHandler()
}
var body: some View {
VStack {
Button(
action: {
do {
try _ = fileHandler
.createFolder()
.createFile(
"TestingFolder",
content: "Test 22.11.23"
)
} catch {
print("DEBUG: ERROR")
}
let filemgr = FileManager.default
let dirPaths = filemgr.urls(for: .documentDirectory, in: .userDomainMask)
let docsDir = dirPaths[0].path
print("DEBUG: " + docsDir)
},
label: {
Text("파일저장합니다.")
.foregroundColor(.white)
.padding()
.background(Color.blue)
.cornerRadius(8)
})
}
.padding()
}
}