
처음 코딩을 배우면 문법은 이해했는데 막상 무엇을 만들어야 할지 막막해지는 시기가 옵니다. 수업에서는 이 시기를 짧게 넘기도록 작은 게임 하나를 정해 기능을 …
메모리 속 데이터를 상자에 포장하는 일
게임이 돌아가는 동안 플레이어 정보는 메모리 안에 여러 조각으로 존재합니다. 이름, 레벨, 위치 좌표, 인벤토리 아이템 목록, 퀘스트 진행 상황이 서로 연결된 채로 흩어져 있습니다. 게임을 끄면 메모리는 비워지므로, 이 정보를 남기려면 파일로 옮겨야 합니다.
문제는 메모리 속 구조를 그대로 파일에 옮길 수 없다는 점입니다. 메모리에서는 '이 아이템 정보는 저 주소에 있다'처럼 서로를 가리키며 연결되어 있는데, 이 주소는 다음 실행 때 의미가 없어집니다. 그래서 연결된 정보를 풀어 순서대로 나열한 형태로 바꿔야 합니다.
이사할 때 가구를 분해해 상자에 넣는 것과 비슷합니다. 침대를 통째로 트럭에 실을 수 없으니 부품을 나누고, 어떤 부품인지 라벨을 붙여 차곡차곡 담습니다. 새집에서는 라벨을 보고 다시 조립합니다. 상자에 담는 과정이 직렬화, 다시 조립하는 과정이 역직렬화입니다.
게임에서 직렬화가 쓰이는 곳
- 세이브·로드: 진행 상황을 파일로 저장하고 다시 불러오기
- 설정 저장: 볼륨, 해상도, 키 배치 같은 옵션 기억하기
- 네트워크 통신: 내 캐릭터의 위치와 행동을 다른 플레이어나 서버로 보내기
- 에디터 데이터: 엔진에서 오브젝트에 입력한 값이 장면 파일에 저장되는 것
- 게임 데이터 테이블: 기획자가 만든 아이템·몬스터 수치를 파일에서 읽어 오기
특히 엔진 에디터의 동작을 이해하는 데 중요합니다. Unity에서 스크립트의 변수가 인스펙터 창에 보이고 값이 저장되는 것은 엔진이 그 변수를 직렬화하기 때문입니다. public 변수나 [SerializeField] 표시가 붙은 변수가 인스펙터에 나타나는 이유가 여기에 있습니다. Unreal Engine에서도 UPROPERTY로 표시한 변수가 에디터에 노출되고 저장됩니다.
사람이 읽는 형식과 기계가 읽는 형식
직렬화 결과물의 형식은 크게 텍스트와 바이너리로 나뉩니다. 무엇을 고를지는 용도에 따라 다릅니다.
| 형식 | 특징 | 어울리는 용도 |
|---|---|---|
| JSON 같은 텍스트 형식 | 사람이 열어 읽고 고칠 수 있음, 크기가 큰 편 | 설정 파일, 데이터 테이블, 개발 중 디버깅 |
| XML·YAML 같은 텍스트 형식 | 구조 표현이 자세함, 도구 호환이 좋음 | 에디터 데이터, 일부 엔진의 장면 파일 |
| 바이너리 형식 | 사람이 읽기 어려움, 작고 빠름 | 네트워크 전송, 큰 세이브 파일, 빠른 로딩 |
JSON은 처음 배우기에 가장 좋은 형식입니다. 괄호와 이름, 값으로 이루어져 메모장으로 열어도 내용이 보입니다. 저장이 제대로 되었는지 눈으로 확인할 수 있어서 직렬화 원리를 이해하는 데 큰 도움이 됩니다. 다만 텍스트 세이브 파일은 플레이어가 쉽게 고칠 수 있으니, 경쟁 요소가 있는 게임에서는 서버 검증이나 암호화 같은 대책을 함께 고민합니다.
직렬화할 것과 하지 않을 것 고르기
모든 데이터를 저장할 필요는 없습니다. 무엇을 저장할지 고르는 것이 세이브 시스템 설계의 핵심입니다. 기준은 '다시 계산하거나 다시 불러올 수 있는가'입니다.
예를 들어 플레이어의 레벨과 경험치는 저장해야 합니다. 하지만 레벨에서 계산되는 공격력은 굳이 저장하지 않고 불러올 때 다시 계산해도 됩니다. 캐릭터의 3D 모델이나 텍스처처럼 게임 파일에 이미 들어 있는 것도 저장하지 않습니다. 대신 '어떤 모델을 쓰는지' 가리키는 이름이나 번호만 저장합니다.
저장 대상을 줄이면 파일이 작아지고 불러오기가 빨라집니다. 무엇보다 나중에 게임을 고쳤을 때 세이브 파일과 어긋나는 부분이 줄어듭니다. 저장 항목을 정할 때는 기획 문서의 '이어하기' 조건을 먼저 확인하세요. 보스 체력이나 맵 위 상자의 열림 여부처럼 저장 여부가 게임 경험을 바꾸는 항목은 기획자와 함께 정해야 합니다.
처음 배울 때 부딪히는 문제
가장 흔한 문제는 저장한 변수가 왜 비어 있느냐는 것입니다. 엔진과 언어마다 직렬화할 수 있는 자료형에 제한이 있어서, 특정 형태의 데이터는 조용히 빠지기도 합니다. 예를 들어 Unity의 기본 직렬화는 일반적인 딕셔너리를 그대로 저장하지 못합니다. 저장되지 않는 형태라면 리스트 두 개로 나누는 식으로 바꿔 줍니다.
두 번째는 서로를 가리키는 데이터입니다. A가 B를 가리키고 B가 다시 A를 가리키면, 단순한 직렬화는 끝없이 따라가다 멈추거나 같은 데이터를 두 번 저장합니다. 이런 경우 객체를 직접 저장하지 않고 고유 번호로 참조하는 방식을 씁니다.
세 번째는 버전 문제입니다. 게임을 업데이트하며 저장 구조를 바꾸면, 예전 세이브 파일을 읽지 못하는 일이 생깁니다. 그래서 세이브 데이터에 버전 번호를 함께 넣어 두고, 예전 버전이면 새 구조로 변환하는 코드를 둡니다. 출시한 게임에서는 이 부분이 특히 중요합니다.
간단한 세이브 시스템으로 익혀 보기
플레이어 이름, 레벨, 위치, 아이템 세 개를 담는 데이터 클래스를 만들고, 이를 JSON으로 저장했다가 다시 불러오는 기능을 만들어 보세요. 저장된 파일을 메모장으로 열어 보고, 값을 직접 고친 뒤 불러와서 게임에 반영되는지 확인하면 직렬화와 역직렬화의 흐름이 손에 잡힙니다. 이어서 변수를 하나 추가한 뒤 예전 파일을 불러와 보면 버전 문제도 직접 경험할 수 있습니다.
세이브 시스템은 작은 게임에도 꼭 필요하지만 강의에서 대충 넘어가기 쉬운 주제입니다. 데이터 구조를 설계하는 힘까지 기르고 싶다면, SBS아카데미게임학원 게임프로그래밍 과정 상담에서 프로젝트 수업 중 어떤 기능을 직접 구현하게 되는지 구체적으로 물어보세요.
자주 묻는 질문
세이브 파일은 어디에 저장되나요?
운영체제와 엔진마다 정해진 사용자 데이터 폴더가 있습니다. 게임 설치 폴더에 직접 쓰면 권한 문제가 생길 수 있어, 엔진이 알려 주는 저장 경로를 쓰는 것이 일반적입니다.
직렬화는 프로그래머만 알면 되나요?
주로 프로그래머가 다루지만, 데이터 테이블을 만드는 기획자도 형식과 규칙을 알아 두면 협업이 수월해집니다.
가까운 캠퍼스에서 게임프로그래밍 알아보기
사는 곳과 가까운 캠퍼스를 고르면 수업 내용, 오시는 길, 상담 신청을 한 번에 볼 수 있습니다.