카테고리 없음

[BalancingLibra] Application.persistentDataPath, Path.Combine()에 대해

mazayong 2026. 1. 18. 23:59

유저데이터를 저장할 때 해당 함수를 처음 이용해보아서 해당 함수의 작동 과정 및 궁금함을 해소한 과정을 기록해 두겠다.

 

 

1. Application.persistentDataPath

2. Path.Combine()

3. 후기

 

 

 

 

 

 

1. Application.persistentDataPath

해당 명령어는 OS 관계없이 저장 데이터를 저장할 기기 내 안전한 장소를 찾아준다.

나는 현재 맥북을 쓰고 있지만, 일반 PC나 안드로이드는 경우가 다르다.

PC의 경우, 일단 C드라이브에 저장하려고 하면 관리자 권한이 없다고 한다.

모바일은 앱마다 샌드박스라는, 각 OS만의 격리된 방이 아니면 파일 접근 자체가 금지되기 때문이다.

 

해당 명령어를 사용하면, 아래와 같이 이 앱이 자유롭게 파일을 다룰 수 있는 안전한 폴더 위치를 받아온다.

mac : ~/Library/Application Support/

window : %userprofile%\AppData\LocalLow\

android : /storage/emulated/0/Android/Data...

 

그러면 여기서 어떻게 안전한 장소를 찾을까?

그건 유니티의 cpp native가 OS의 API를 직접 호출해서 받아오는 값으로 결정된다. OS가 내어준 샌드박스 경로를 받아오는 것이다.

하지만 각 플랫폼의 구현 형식이 다르므로 유니티는 각 플랫폼별로 다르게 샌드박스를 경로를 받도록 구성되어 있다.

 

Win32 API : window의 SHGetKnownFolderPath같은 API를 호출해 FOLDERID_LocalAppData를 요청한다.

그러면 %APPDATA$\..\LocalLow\CompanyName\ProductName을 받아오는데, 윈도우 정책상 프로그램이 권한 없이 쓸 수 있는 표준 경로이기 때문이다.

android : 유니티의 cpp 백엔드가 JNI(Java Native Interface)를 통해 Android Java API인 Context.getExternalFilesDir(null)을 호출한다.

그 결과, /storage/emulated/0/Android/data/com.Company.Product/files를 받아오는데, android OS가 앱 삭제시 데이터를 날려주면서 앱 실행중에는 읽기쓰기 권한을 보장하는 경로이기 때문이다.

ios/mac : NSSearchPathForDirectoriesInDomains 함수를 호출해 NSDocumentDirectory 또는 NSLibraryDirectory를 찾는다.

그 결과, /var/mobile/Containers/Data/Application/<GUID>/Documents를 받아오는데, IOS 정책 안에서 유일하게 영구 데이터를 보존가능하도록 허용된 Documents 폴더이기 때문이다.

 

 

 

2. Path.Combine()

경고를 설정할 때, 현재 실행 중인 OS를 감지해서 알아서 맞는 구분 기호를 넣어준다.

window : \

mac/linux/mobile : /

 

그러면 Path.Combine이 실행될 때마다 OS를 스캔하는걸까? 그러면 자원 낭비이므로 해당 방법은 사용하지 않을 것이다.

.net 런타임(유니티 에디터에서 실행할 때는 Mono(mac/window 차이를 처리), 폰으로 빌드할 때는 IL2CPP)이 초기화될 때 이미 결정된 상수를 가져온다. (mac : /, window : \) 이 상수를 DirectorySeparatorChar로 정의한다.

Path.Combine은 문자열을 합칠 때, 이 DirectorySeparatorChar을 중간에 넣어 코드는 하나지만 실행되는 환경에 따라 문자열이 달라진다.

 

이후에 Path.Combine("A", "B")를 호출하면 다음 순서로 작동한다.

1) 유효성 검사 : 문자열이 null or 유효하지 않은 문자 포함되었는지 검사.

2) 루트 판별 : B가 절대 경로인지 확인한다. (B가 절대 경로라면 A를 무시하고 B만 반환한다. 이 부분은 추후 정리할 것이다.)

3) 구분자 처리 : A의 마지막 글자가 이미 구분자인지 아닌지 검사. (Yes : A + B, No : A + DirectorySeparatorChar + B) 수행.

 

위 3)처럼 +으로 문자열을 합치면 중간 단계의 가비지 메모리가 생성된다. 하지만 최신 .net은 Path.Combine은 내부적으로 StringBuilder과 유사한 버퍼 처리로 불필요한 메모리할당을 최소화하도록 최적화되어 있다. 

Update()에서 경로 계산을 매번 한다면, +보다 Path.Combine이 GC 부하가 적다.

 

그러나 두 번째 매개변수가 / or \로 시작하면 앞 경로가 사라진다. 

그 이유는 Path.combine은 두 번째 인자가 구분자로 시작하면 그건 루트부터 시작하는 절대 경로라고 판단하기 때문이다.

 

추가로 AssetDatabase.LoadAssetAtPath같은 유니티 에디터 전용 API는 윈도우에서도 반드시 /만 인식하는 경우가 있으므로 결과를 맹신하면 안된다.

 

즉, Path.Combine은 런타임이 미리 정해둔 상수를 끼워 넣는 방법이다.

 

 

 

 

3. 후기

처음에는 어떻게 사용하는지 가볍게 쓰려고 했는데 궁금한 게 많고, 글로 써야 정리가 잘 되어서 글로 많이 써버리게 되었다. 그래도 유니티 내 런타임도 알게 되고, Path.Combine팁도 알게 되어 좋았다. 꼭 프로젝트에서 써먹고 싶다.