차트에 표시할 항목 하나를 설정 화면에 추가했다. 체크박스를 바꾸면 화면도 바로 반응했고 앱을 닫기 전까지는 아무 문제가 없었다. 그런데 다시 실행하면 새 옵션만 기본값으로 돌아왔다. 분명 저장 버튼을 눌렀고 기존 설정은 그대로 남아 있어서 처음에는 UI 초기화 문제라고 생각했다.
저장 파일을 직접 열어 보니 새 필드가 없었다. 설정 클래스에는 값을 추가했지만 실제 저장용 객체를 만드는 코드에는 옮기지 않았던 것이다. 읽는 쪽과 쓰는 쪽이 서로 다른 모델을 쓰고 있었다.
화면 상태와 저장 상태의 차이
체크박스의 값이 바뀌었다는 건 메모리 안의 현재 상태가 바뀌었다는 뜻이었다. 파일에 기록됐다는 뜻은 아니었다. 흐름을 나누어 적으니 빠진 단계가 보였다.
클릭 -> 화면 상태 변경 -> 저장 모델 생성 -> JSON 직렬화 -> 파일 기록
실행 -> 파일 읽기 -> JSON 역직렬화 -> 기본값 보정 -> 화면 반영
저장과 복원은 서로 반대 방향처럼 보이지만 실제 코드는 별도로 관리되고 있었다.
신규 필드의 이전 파일 호환성
새 버전에서 필드를 추가해도 사용자의 기존 JSON에는 값이 없다. bool은 값이 없으면 false가 되기 때문에 의도한 기본값이 true라면 구분하기 어려웠다. 설정 버전이나 nullable 값을 활용해 이전 데이터와 사용자가 직접 끈 값을 나눌 필요가 있었다.
[Serializable]
public class ChartSettings
{
public int version = 2;
public bool? showGuideLine;
}
bool showGuide = saved.showGuideLine ?? true;
실제 구조에서는 사용 중인 JSON 라이브러리가 nullable을 어떻게 처리하는지도 확인해야 했다.
저장 직후 재조회 검증
버튼을 눌렀다는 사실만으로 성공을 판단하지 않고 저장된 파일의 내용과 재실행 결과를 확인했다. 새 파일, 이전 버전 파일, 일부 필드가 빠진 파일을 각각 준비해 보니 기본값 처리가 어디에서 필요한지도 분명해졌다.
JSON이 깨졌을 때 전체 설정을 초기화할지, 정상인 필드는 살릴지도 별도 결정이었다. 무조건 새 객체를 만들면 사용자의 다른 설정까지 사라질 수 있었다.
필드 추가에 따른 데이터 변경
처음에는 설정 옵션 추가를 체크박스 하나를 만드는 UI 작업으로 봤다. 실제로는 저장 모델, 직렬화, 이전 버전과의 호환, 복원 시점까지 이어지는 데이터 변경이었다.
그 뒤로 저장되는 값을 수정할 때는 쓰는 코드와 읽는 코드를 함께 찾게 됐다. 화면에서 지금 잘 보이는지만 확인하지 않고 앱을 껐다 켜도 남는지, 이전 파일을 읽어도 안전한지를 체크했다. 작은 설정 하나 덕분에 상태의 수명은 화면보다 길다는 걸 배웠다.
공식 문서 기반의 추가 정리
작업 당시에는 눈앞의 오류를 먼저 고쳤지만, 이후 기술 문서와 맞춰 보며 확인 순서를 조금 더 구체화했다.
Unity의 JsonUtility 문서를 다시 읽어 보니, JSON을 자유로운 key-value 저장소처럼 다루기보다 직렬화 가능한 클래스와 필드에 대응시키는 도구라는 점이 더 분명했다. 모르는 필드는 무시되고 빠진 필드는 생성된 객체의 기본값으로 남을 수 있다. 따라서 파싱에 성공했다는 사실과 지금 화면에서 써도 되는 데이터라는 판단은 같지 않다.
RFC 8259도 객체 멤버의 순서를 상호운용 가능한 계약으로 기대하지 않는다. 배열 순서가 의미를 가져도 객체 필드가 적힌 순서까지 로직에 사용하면 다른 생성기나 마이그레이션에서 쉽게 깨진다.
다음에 JSON 설정 저장 누락 같은 문제를 보면 아래 순서부터 확인하려고 한다.
- schema version과 마이그레이션 경로
- 필수 필드·기본값·범위 검증
- 깨진 항목만 격리하는 단위
- 이전 버전으로 되돌릴 수 있는지
형식이 올바른 JSON이어도 필수 식별자, 범위, 버전이 맞지 않으면 화면 모델로 넘기지 않는 편이 안전하다.
공식 참고 자료
- Unity 2021.3 JSON 직렬화 문서 — 필드 기반 직렬화와 누락·알 수 없는 필드 처리
- RFC 8259 JSON 표준 — JSON 값·숫자·객체 순서와 상호운용성