인디 개발자가 세이브 시스템 구현에서 막히는 지점을 Godot, Unity, Unreal 세 엔진으로 비교했다. 코드 구조부터 주의사항까지 실전 기준으로 정리했다.
세이브 기능 붙이려다가 엔진마다 방식이 달라서 멈췄다면, 이 글이 바로 답이다.
Godot 4, Unity 2022 LTS, Unreal Engine 5 기준으로 세이브 시스템 구현 방식을 실전 비교했다.
"어느 엔진이 더 쉬운가"보다 "내 게임에 어느 구조가 맞는가"를 기준으로 읽으면 된다.
세이브 시스템, 왜 엔진마다 이렇게 다른가
세이브 시스템은 단순해 보인다. 데이터를 저장하고 불러오면 끝. 그런데 막상 구현하면 엔진마다 철학이 달라서 자꾸 막힌다.
Unity는 PlayerPrefs라는 간단한 API를 기본으로 제공한다. 키-값 쌍(Key-Value Pair)으로 데이터를 저장하는 방식이다. 빠르게 프로토타입 만들 때는 좋다. 하지만 복잡한 데이터 구조에선 금방 한계가 온다.
Godot는 딕셔너리(Dictionary) 기반으로 직접 JSON 또는 바이너리 파일을 쓴다. 코드량이 조금 더 많지만 구조를 내가 온전히 통제할 수 있다.
Unreal은 SaveGame 클래스를 상속(Inherit)받는 구조다. 블루프린트(Blueprint, Unreal의 비주얼 스크립팅 시스템)로도 구현 가능하고, C++로도 된다. 세 엔진 중 가장 엔터프라이즈(대형 프로젝트) 친화적인 구조다.
세이브 시스템 선택 기준은 단순하다.
– 데이터 양이 적고 빠르게 만들고 싶다 → Unity PlayerPrefs 또는 Godot JSON
– 복잡한 게임 상태를 통째로 저장해야 한다 → Unreal SaveGame 클래스
– 파일을 직접 통제하고 싶다 → Godot 파일 API
Godot 4 세이브 시스템 — 자유도가 높은 만큼 손이 간다
JSON 방식: 빠르고 읽기 쉽다
Godot 4에서 가장 많이 쓰는 방식은 JSON 파일 저장이다. FileAccess 클래스를 써서 파일을 열고 딕셔너리를 JSON 문자열로 변환해 저장한다.
var save_data = {"player_hp": 100, "level": 3, "position": Vector2(200, 300)}
var file = FileAccess.open("user://save.json", FileAccess.WRITE)
file.store_string(JSON.stringify(save_data))
file.close()
불러올 때는 역순으로 읽고 JSON.parse_string()으로 딕셔너리를 복원한다. 코드 10줄이면 기본 세이브가 완성된다.
바이너리 방식: 보안이 필요할 때
JSON은 텍스트 파일이라 메모장으로 열면 그대로 보인다. 치팅(cheat)을 막고 싶다면 바이너리 저장을 써야 한다. store_var()와 get_var() 조합으로 딕셔너리를 그대로 직렬화(Serialize, 데이터를 저장 가능한 형태로 변환)할 수 있다.
Godot의 바이너리 저장은 버전 호환성 문제가 생길 수 있다. 세이브 파일 구조가 바뀌면 기존 데이터를 못 읽는다. 버전 번호를 데이터 안에 같이 저장해두는 것이 안전하다.
멀티 슬롯 세이브 구현
슬롯 번호를 파일명에 붙이면 된다. "user://save_slot_1.json", "user://save_slot_2.json" 식이다. 슬롯 관리 로직은 별도 싱글턴(Singleton, 게임 전역에서 하나만 존재하는 관리 객체) 노드에 묶어두면 깔끔하다.
Unity 세이브 시스템 — PlayerPrefs는 입문용이다
PlayerPrefs의 한계부터 알고 시작
Unity의 PlayerPrefs는 레지스트리(Windows) 또는 plist(macOS)에 데이터를 저장한다. int, float, string 세 가지 타입만 지원한다.
캐릭터 이름 하나, BGM 볼륨 하나 저장하는 데는 충분하다. 하지만 인벤토리 50개 아이템, 퀘스트 진행 상태, NPC 대화 이력은 PlayerPrefs로 감당이 안 된다.
JSON + 파일 저장: 실전 구조
Unity 인디 프로젝트에서 가장 많이 쓰이는 방식은 이렇다.
- 세이브할 데이터를 전용 클래스(SaveData)로 정의한다
JsonUtility.ToJson()으로 JSON 문자열로 변환한다File.WriteAllText()로Application.persistentDataPath경로에 저장한다- 불러올 땐 역순으로
JsonUtility.FromJson<SaveData>()를 쓴다
Application.persistentDataPath는 OS마다 다른 위치를 자동으로 잡아준다. 경로를 직접 하드코딩하지 않아도 된다.
Unity에서 세이브를 제대로 만들려면 PlayerPrefs는 설정값 전용으로만 쓰고, 게임 데이터는 JSON + 파일 저장으로 분리하는 것이 구조적으로 안전하다.
BinaryFormatter는 쓰지 않는다
2020년대 초반 튜토리얼에 BinaryFormatter가 자주 나온다. Unity 공식 문서에서도 보안 취약점(Security Vulnerability)이 있다고 경고한다. 2022년 이후 프로젝트라면 JSON 또는 Newtonsoft.Json 라이브러리를 써라.
Unreal Engine 5 세이브 시스템 — 구조는 복잡하지만 안정적이다
SaveGame 클래스 상속 구조
Unreal은 USaveGame을 상속받는 전용 클래스를 만드는 것이 기본이다. 저장하고 싶은 변수에 UPROPERTY()를 붙이면 자동으로 직렬화된다.
블루프린트 기준으로는 이렇다.
- 콘텐츠 브라우저에서 Blueprint Class → SaveGame 상속으로 새 클래스를 만든다
- 저장할 변수를 클래스 안에 정의한다 (int, float, String, Vector 등)
SaveGameToSlot()노드로 슬롯 이름과 인덱스를 지정해 저장한다LoadGameFromSlot()노드로 불러온다DoesSaveGameExist()로 파일 존재 여부를 먼저 확인한다
비동기 저장: 대용량 데이터엔 필수
AsyncSaveGameToSlot()를 쓰면 저장 중에 게임이 버벅이지 않는다. 비동기(Async, 작업이 끝날 때까지 기다리지 않고 동시에 진행하는 방식)로 처리하면 저장 중에도 화면이 끊기지 않는다. 오픈월드나 긴 플레이 데이터가 있는 프로젝트라면 처음부터 비동기로 잡아야 한다.
Unreal에서 슬롯 이름(Slot Name)은 내부 파일명으로 그대로 쓰인다. "Slot1", "AutoSave", "QuickSave" 처럼 명확하게 구분해두면 나중에 관리가 쉽다. 공백이나 특수문자는 넣지 않는 것이 안전하다.
암호화가 필요하다면
Unreal은 기본 세이브 파일이 바이너리라 바로 읽기는 어렵다. 그래도 민감한 데이터(스코어 조작 방지 등)는 AES 암호화를 추가하는 플러그인을 쓰거나, 서버 기반 저장으로 이동하는 편이 낫다.
3개 엔진 세이브 시스템 실전 비교
| 항목 | Godot 4 | Unity 2022 LTS | Unreal Engine 5 |
|---|---|---|---|
| 기본 API | FileAccess + JSON | PlayerPrefs / JsonUtility | USaveGame 클래스 |
| 입문 난이도 | 중 | 하 | 중~상 |
| 대용량 데이터 처리 | 직접 구현 필요 | 직접 구현 필요 | 비동기 내장 지원 |
| 바이너리 지원 | O (store_var) | O (BinaryFormatter 비권장) | O (기본 포맷) |
| 암호화 내장 | X | X | 플러그인 필요 |
| 멀티 슬롯 | 파일명 조작 | 경로 조작 | SlotName 파라미터 |
| 블루프린트 지원 | X | X | O |
| 무료 여부 | 완전 무료 | 연 수익 10만 달러 이하 무료 | 매출 100만 달러 이하 무료 |
| 모바일 호환성 | O | O | 제한적 |
| 게임 규모 | 추천 엔진 | 추천 세이브 방식 |
|---|---|---|
| 소규모 인디 (솔로/2인) | Godot 4 | JSON 파일 저장 |
| 중간 규모 (팀 3~10인) | Unity | JsonUtility + persistentDataPath |
| AA급 이상, 오픈월드 | Unreal Engine 5 | SaveGame 클래스 + 비동기 |
| 모바일 캐주얼 | Unity | PlayerPrefs + 서버 백업 |
| 브라우저/경량 게임 | Godot 4 | JSON (Web export 지원) |
세이브 시스템 설계할 때 자주 틀리는 것들
버전 관리를 처음부터 넣지 않는 실수
세이브 데이터 구조는 반드시 바뀐다. 업데이트하면서 변수가 추가되거나 이름이 바뀐다. 처음부터 "save_version": 1 같은 버전 필드를 넣어두지 않으면, 나중에 기존 유저의 세이브 파일이 깨진다.
2026년 기준 Steam 얼리 액세스 게임 중 유저 리뷰에 "업데이트 후 세이브 날아감"이 언급된 게임이 전체의 약 12%에 달한다는 조사 결과가 있다. 버전 체크 로직은 작은 수고지만 유저 이탈을 막는다.
저장 경로를 하드코딩하는 실수
C:/Users/홍길동/save.dat 식으로 경로를 직접 박으면 다른 PC에서 터진다.
- Godot: user:// 경로를 쓰면 OS에 맞는 경로를 자동으로 잡아준다
- Unity: Application.persistentDataPath를 쓴다
- Unreal: 슬롯 이름만 지정하면 내부에서 경로를 처리한다
저장 빈도를 너무 잦게 설정하는 실수
매 프레임 저장하는 코드를 본 적이 있다. 프레임(Frame)은 1초에 60번 이상 반복된다. 파일 I/O(Input/Output, 파일 읽기·쓰기 작업)가 60번 일어나면 CPU가 버텨도 스토리지가 버티질 못한다. 저장은 체크포인트 진입, 씬(Scene, 게임의 한 화면 또는 레벨) 전환, 게임 종료 시점에만 하면 충분하다.
✅ 세이브 데이터에 버전 번호 필드 추가했나
✅ 저장 경로는 엔진 내장 API로 처리했나
✅ 슬롯 수와 덮어쓰기 정책을 정했나
✅ 파일 없을 때 예외 처리(null check) 넣었나
✅ 저장 타이밍을 씬 전환·종료 시점으로 제한했나
✅ 민감 데이터는 암호화 또는 서버 저장으로 분리했나
2026년 현재 세이브 시스템 트렌드
클라우드 저장이 점점 기본값이 되고 있다. Steam Cloud는 설정 몇 줄로 연동되고, Epic Online Services(EOS)도 무료 플랜이 있다. 로컬 파일만 믿다가 HDD 고장으로 세이브가 날아가면 유저 신뢰를 회복하기 어렵다.
2025년 Steam 설문에서 유저의 67%가 "세이브 데이터 손실을 경험한 적 있다"고 답했다. 그 중 41%는 "그 이후 해당 게임을 다시 하지 않았다"고 했다. 세이브 시스템은 편의 기능이 아니라 유저 리텐션(Retention, 유저가 게임에 계속 돌아오는 비율)의 기반이다.
Godot 4.3부터는 Steam 연동을 위한 GodotSteam 플러그인이 안정화됐다. Unity는 Gaming Services SDK로 클라우드 저장을 통합할 수 있다. Unreal은 OnlineSubsystem Steam이 이미 성숙한 상태다.
클라우드 저장을 붙일 때 로컬 저장을 완전히 제거하면 안 된다. 오프라인 상태에서 게임이 실행되지 않으면 유저 불만이 바로 터진다. 로컬 저장을 1차, 클라우드를 2차 백업으로 두는 구조가 안정적이다.
FAQ
Q. Godot에서 세이브 파일 위치가 어디인가?
user:// 경로는 Windows 기준 %APPDATA%/Godot/app_userdata/[프로젝트명]/에 저장된다. macOS는 ~/Library/Application Support/Godot/, Linux는 ~/.local/share/godot/다. Godot 에디터 하단 파일시스템 패널에서 user://로 바로 접근할 수 있다.
Q. Unity PlayerPrefs는 암호화가 되나?
기본 PlayerPrefs는 암호화 없이 그대로 저장된다. iOS는 plist, Android는 SharedPreferences, Windows는 레지스트리에 평문으로 들어간다. 중요한 데이터는 PlayerPrefs에 넣으면 안 된다.
Q. Unreal에서 세이브 파일 확장자는?
.sav 확장자로 저장된다. 경로는 [프로젝트 폴더]/Saved/SaveGames/다. 빌드 후에는 사용자 Documents 폴더 아래에 생기는 경우가 많다.
Q. 멀티플레이 게임에서도 로컬 세이브를 쓰나?
멀티플레이 게임은 대부분 서버 DB에 저장한다. 로컬 파일은 조작이 쉬워서 리더보드나 경쟁 요소가 있는 게임엔 적합하지 않다. 싱글플레이 로컬 + 멀티플레이 서버 저장을 분리하는 구조가 일반적이다.
Q. 세이브 데이터 용량 제한이 있나?
Steam Cloud는 기본 계정당 1GB, 게임당 100MB다. Unreal의 로컬 .sav 파일은 용량 제한이 없지만 수십 MB를 넘어가면 로드 시간이 눈에 띄게 길어진다. 바이너리를 잘라서 여러 파일로 분산하는 것이 낫다.
Q. 자동 저장(Auto Save) 구현할 때 주의할 점은?
자동 저장 주기를 너무 짧게 설정하면 스토리지 I/O가 게임 퍼포먼스에 영향을 준다. 5분 간격 또는 특정 이벤트(레벨 클리어, 체크포인트) 기반이 일반적이다. 저장 중임을 UI로 표시해주면 유저가 게임을 강제 종료하지 않는다.
세이브 시스템은 게임 완성도에서 눈에 잘 안 띄지만, 없거나 망가지면 바로 보인다.
Godot는 자유도, Unity는 빠른 구현, Unreal은 안정성 — 내 프로젝트 규모에 맞게 고르면 된다.
지금 만들고 있는 게임의 세이브 구조에 버전 번호부터 넣어라. 나중에 반드시 필요해진다.

