
「마계공단: 던전사업부」는 던전 편에 서서 몬스터 직원을 배치하고, 쳐들어오는 모험가를 막는 모바일 던전 경영 게임이다. 혼자 만들고 있고, 지금은 App Store에 나와 있다. 이번 편은 게임 세이브 오류 얘기다. 화려한 기능은 하나도 없고, 저장이 꼬여서 골드가 돌아오거나 장비가 사라지던 버그들을 하나씩 잡은 기록이다.
10월 2일부터 14일까지, 저장이랑 관련된 커밋만 골라 보니 14개였다. 그중 절반은 10월 7일에 직접 끝까지 해 보면서 적은 플레이테스트 목록에서 나왔다. 커밋 설명에 "playtest 2026-10-07 #01" 같은 번호가 붙어 있어서 어떤 버그가 어디서 나왔는지 따라가기 쉬웠다.
이번 편엔 버그 화면 캡처가 거의 없다. 그래서 그때 저장소에 남긴 테스트 화면 두 장과, 커밋 설명을 옮겨 그린 도식 두 장을 넣었다. 도식은 도식이라고 표시해 뒀다.
저장하면 돈이 돌아오는 버그
처음 잡은 건 10월 2일 밤이다. 스토리 스테이지를 처음 깨면 첫 클리어 골드를 주는데, 저장하고 나면 그 골드가 없어졌다.
원인은 저장 구조에 있었다. 이 게임 세이브에는 예전 프로토타입 시절의 진행 데이터 형식이 아직 남아 있다. 새 저장 형식으로 옮기면서 옛 형식을 한 번에 다 지우지 못해서, 저장할 때 옛 형식 사본을 만들고 그걸 다시 새 데이터에 적용하는 단계가 끼어 있었다. 정산 중에 첫 클리어 골드를 지갑에 넣었는데, 옛 사본 쪽 골드는 그 전 값이었다. 저장하는 순간 옛 값으로 덮인 거다.
그날은 옛 사본의 골드를 정산이 끝날 때 지갑 값으로 맞추는 한 줄로 고쳤다. 검사도 붙였다. 첫 클리어를 하고 저장했을 때 골드가 남는지, 같은 정산을 두 번 불렀을 때 두 번 주지 않는지, 골드 상한 근처에서 첫 클리어를 해도 상한을 넘지 않는지. 같은 스테이지를 다시 깨면 일반 보상만 저장되는지, 지면 전리품과 돌파당해서 잃은 골드만 반영되는지도 같이 본다.
사흘 뒤 10월 5일엔 비슷한 게 또 나왔다. 전투 정산을 하면 유물 조각이 사라졌다. 이번엔 지갑을 복사하는 함수가 골드, 보석, 강화석, 속성별 재화는 다 옮기는데 유물 조각만 빠뜨리고 있었다. 유물 조각은 9월 말에 발굴 콘텐츠를 넣으면서 새로 생긴 재화라, 복사 함수를 고칠 때 같이 안 고친 거다. 이건 정말 한 단어짜리 수정이었다.
10월 4일, 사라진 영업 보고서 살리기
영업은 플레이어가 자리를 비운 동안 던전이 알아서 손님을 받고, 돌아오면 보고서를 결재해서 보상을 받는 방치형 모드다. 이 보고서가 사라지는 경우가 있었다.
Unity의 기본 JSON 저장 방식은 비어 있는 객체 필드를 null로 저장하지 않고 빈 객체로 써 버린다. 그래서 코드에 "ID가 없는 보고서는 보고서가 없는 것"이라는 규칙을 넣어 뒀다. 그런데 정산은 이미 있는 보고서에 이어서 보상을 쌓는다. 저장 파일에 빈 껍데기가 남아 있으면 다음 정산이 그 껍데기에 골드랑 장비를 쌓았고, ID가 없으니 나중에 불러올 때 '없는 보고서'로 지워졌다.
그래서 보고서를 정리하는 함수를 따로 만들었다. 골드, 장비, 알, 방어 횟수 중 하나라도 기록이 있으면 새 ID를 붙여서 살리고, 결재함에 그 보고서를 가리키는 공문이 없으면 '영업 실적' 공문을 새로 넣는다. 진짜 빈 껍데기면 그때만 지운다.
이건 웹 빌드로 확인했다. 브라우저 저장소에 옛 형식 보고서(527골드짜리)를 직접 심어 두고, 게임을 불러온 다음 저장 파일을 다시 읽어서 결과를 봤다.

개발자 노트옛 형식 사본이 남아 있는 세이브
이 게임은 처음에 단순한 진행 데이터 하나로 시작했다가, 2편에서 정산과 저장 구조를 새로 짰다. 그런데 화면 코드 일부가 여전히 옛 진행 데이터를 읽고 써서, 새 세이브에서 옛 형식을 투영한 사본을 만들어 쓰고 저장 직전에 다시 새 세이브에 적용하는 어댑터가 있었다.
문제는 사본을 만든 시점과 저장 시점 사이에 새 세이브가 직접 바뀌는 경우다. 정산이 지갑에 골드를 넣거나, 화면이 지갑에서 바로 돈을 빼면 사본은 옛 값을 들고 있다. 10월 2일엔 정산 끝에서 사본 골드만 맞췄고, 10월 8일에는 저장할 때마다 사본을 새로 만들도록 바꿔서 이 종류의 버그를 한꺼번에 막았다. 회귀 검사는 "옛 적용 경로는 예외를 던진다"는 것까지 확인한다. 나중에 누가 옛 경로를 다시 쓰면 바로 걸리게.
10월 7일 플레이테스트에서 나온 것들
10월 8일 저녁 8시부터 10시 사이에 커밋 5개로 플레이테스트 번호 #01, #02, #04, #05, #06을 차례로 닫았다.
#01 집무실이 벽돌 배경만 남는다
영업으로 처리한 방어마다 요청 ID를 붙이는데, ID를 계산하는 반복문이 반복 횟수를 방어 수에 더하고, 방어 수도 반복하면서 같이 늘렸다. 그래서 ID가 0, 2, 4처럼 건너뛰었고, 방어 2개짜리 묶음 다음 묶음은 앞 묶음의 마지막 ID를 다시 썼다. 정산은 "이미 정산한 방어"라며 예외를 던졌고, 그게 로비 화면을 그리는 도중이라 화면에 벽돌 배경만 남았다.
요청 ID는 방어가 일어난 20분 단위 시간 칸에서 만들게 바꿨다. 같은 칸은 두 번 정산될 일이 없다. 방어 하나가 실패해도 묶음 전체를 멈추지 않고 그 방어만 무효로 표시한다. 정산이 실패해도 로그만 남기고 화면은 그려지게 했다.
#04 대장간을 올렸는데 돈이 그대로
대장간을 Lv2로 올리면 20,000골드가 나가야 하는데, 잔액이 1,036,175골드에서 안 바뀌었다. 위에서 말한 옛 사본 문제가 또 나온 거다. 시설 강화는 지갑에서 바로 돈을 빼고 저장을 요청하는데, 저장이 옛 사본을 다시 적용하면서 지불 전 골드로 돌아갔다. 장비 강화는 다른 경로를 타서 멀쩡했다.

#02 결과 화면 버튼이 안 눌린다
같은 원인이 더 나쁜 모양으로 나온 경우다. 직원 연수로 레벨을 올린 다음 저장하면, 옛 사본에는 예전 레벨이 들어 있어서 "잘못된 성장 갱신"으로 예외가 났다. 저장을 부른 화면 처리가 그 자리에서 멈췄다. 장비 화면은 갱신 도중에 멈췄고, 전투 결과 확인 버튼은 결과를 지운 다음 예외가 나서 눌러도 아무 일도 안 일어나는 버튼이 됐다.
#04를 고치고 30분 뒤에 이 커밋을 했다. 아까 넣은 골드만 맞추는 수정은 지우고, 저장할 때마다 사본을 새로 만들게 했다. 전투에서 나갈 때 저장이 실패하면 결과 화면에 다시 시도 버튼이 뜨고, 정산 중 예외가 나면 끝없는 전투 화면 대신 재시도 창이 뜬다. 화면을 그리다 실패하면 집무실로 돌아가는 길을 보여 준다. 테스터 기기 로그에서 볼 수 있게 화면 전환과 정산 시간을 찍는 로그도 넣었다.
#05 우편함이 꽉 차면 장비가 사라진다
이게 제일 아팠다. 우편함이 100칸을 넘으면 가장 오래된 우편을 등급에 상관없이 자동으로 분해하고 있었다. 게다가 영업 결산 장비가 보관함과 우편함을 거쳐 보고서로 옮겨지는 순서라, 우편함이 거의 찬 상태에서 결산하면 받은 건 없는데 우편이 98개에서 95개로 줄었다.
자동 분해는 아예 없앴다. 전투 한 판 분량만큼은 우편함이 넘쳐도 받게 두고, 꽉 찬 상태에서는 전투와 발굴에 들어가기 전에 "장비 정리 필요"를 띄우고 분해 화면으로 가는 바로가기를 준다. 영업 장비는 보관함을 거치지 않고 보고서로 바로 모은다. 결재는 보관함과 우편함에 자리가 날 때까지 기다리고, 몇 칸이 모자라는지 알려 준다.

검사는 보관함 499/500, 우편함 98·99·100칸 조합을 다 만들어서 장비 ID가 하나도 안 사라지는지, 결재가 막혔다가 자리가 나면 한 번만 지급되는지 본다.
#06 뒤로가기가 발굴과 공방 사이를 맴돈다
세이브 버그는 아니지만 같은 날 고쳤다. 발굴 화면과 유물 공방은 서로 바로가기가 있는데, 누를 때마다 화면 기록에 새로 쌓였다. 뒤로가기를 누르면 둘 사이를 왔다 갔다 하고, 집무실까지 가려면 한참 눌러야 했다. 이미 기록에 있는 화면으로 갈 땐 쌓지 않고 그 자리로 돌아가게 바꿨다. 이제 몇 번을 오가도 뒤로가기 두 번이면 집무실이다.
10월 10일, 프레임 손보기
10일 밤엔 성능을 봤다. 모바일 Unity는 따로 정하지 않으면 30프레임이라, 주사율 높은 폰에서도 스크롤이 30프레임으로 돌았다. 60프레임으로 올리고 안드로이드 프레임 페이싱 옵션을 켰다. 그리고 매 프레임 화면 안전 영역(노치나 홈 바를 피해서 UI를 놓는 영역)을 다시 계산하던 걸 값이 바뀔 때만 하게 바꿨고, 전투 중 캐릭터와 이펙트 그리기 순서를 매 프레임 새로 정렬하던 것도 바뀐 게 없으면 손대지 않게 했다. 검사로 200번 무작위 배치에서 순서가 예전과 같은지, 바뀐 게 없을 때 메모리 할당이 0바이트인지 확인한다.
클라우드 백업: 진행은 기기에, 사본만 서버에
10월 12일엔 세이브를 서버에 백업할지 정했다. 결제 서버를 Firebase로 정한 날이라, 같이 정해야 했다.
정한 규칙은 이렇다.
- 게임 진행은 기기에 저장하는 게 기본이다. 서버에는 복구용 사본만 하나 둔다. 이전 버전은 쌓지 않고 덮어쓴다.
- 앱을 내릴 때만 자동으로 올린다. 마지막 업로드에서 최소 1시간이 지나야 하고, 바뀐 게 없으면 건너뛰고, 하루 10번까지다. '지금 백업' 버튼은 없다.
- 하루 10번은 앱 쪽 제한과 별개로 서버 보안 규칙에서도 막는다. 날짜는 기기 시계가 아니라 서버 시각의 한국 자정 기준이다.
- 같은 OS 기기로 바꿀 때 복구하는 용도다. 안드로이드와 iOS 사이 이동은 지원하지 않는다.
- 결제로 산 것(구매 권리, 패스, 보급 수령 기록)은 세이브 사본보다 서버 구매 기록을 먼저 믿는다.
새 기기에 이미 진행이 있는데 서버에도 백업이 있으면 어느 쪽을 이을지 고르는 창이 뜬다. 두 진행을 합치지는 않는다. 게임 계정에 연결되지 않은 경우엔 이 기기에만 저장되고, 앱을 지우면 복구할 수 없다고 먼저 알려 준다.
이 규칙엔 알려진 한계도 적어 뒀다. 하루 10번을 다 쓰면 그 뒤 진행은 다음 날 첫 업로드 전까지 백업에 안 들어간다. 기기에는 계속 저장되니까, 영향이 있는 건 그 사이에 기기를 바꾸는 경우뿐이다.
로그인은 따로 화면을 두지 않았다. 익명으로 시작해서 안드로이드는 Google Play 게임즈, iOS는 Game Center에 자동으로 연결한다. 익명 계정만 쓰면 앱을 지웠다 깔거나 기기를 바꿀 때 계정이 새로 만들어져서, 백업도 구매 기록도 사라진 것처럼 보인다. 그래서 설계 문서에 "익명 로그인만으로 출시 금지"라고 적어 놨다.
비용도 대충 계산해 봤다. 세이브 50KB, 하루 10번이라고 크게 잡아도 하루 1,000명이 하는 동안은 무료 범위 안이고, 하루 2만 명 정도가 돼야 월 1만 원 안팎이다. 실제로는 앱을 하루에 두세 번 내리니까 더 적을 거라고 봤다. 설계 문서에 호출 줄이기, 재시도 제한 같은 비용 방어 항목을 따로 길게 적어 둔 것도 혼자 운영하는 게임이라서다.

10월 14일, 오래된 백업이 결제를 막았다
이틀 뒤 내부 테스트 빌드에서 결제가 아예 안 열렸다. 스토어 연결도, 서버 호출도 안 했다.
따라가 보니 백업이 원인이었다. 앱을 켤 때 백업을 풀면서, 백업 내용의 지문을 지금 빌드의 세이브 구조로 다시 계산해 비교하고 있었다. 그 사이 구매 관련 필드가 세이브에 추가됐으니, 그 전에 올린 백업은 전부 지문이 안 맞는다고 거부됐다. 백업 상태가 '오류'가 됐고, 결제는 백업이 정상일 때만 열리게 해 놨으니 같이 닫혔다.
무결성은 백업 내용 자체의 해시로 이미 확인하고 있었다. 구조에 따라 달라지는 재확인은 지웠고, 옛 구조 백업을 푸는 회귀 검사를 넣었다. 3분 뒤엔 결제가 열리는 조건도 바꿨다. 백업을 읽거나 올리다 실패해도 계정이 확인됐으면 결제는 열린다. 연결 중이거나, 어느 진행을 쓸지 고르는 중이거나, 계정 자체가 실패한 경우만 막는다. 그날 내부 테스트 빌드 1.0 (5)로 기기에서 테스트 구매 3건이 검증되고 지급되는 걸 확인했다.
그날 밤엔 옛 백업을 복원했을 때 패스 보상을 두 번 받을 수 없는지도 검사에 넣었다. 이미 받은 단계는 한 번만 인정하고, 새 단계만 열린다.
개발자 노트세이브 백업과 구매 기록을 나눈 이유
세이브 사본은 클라이언트가 자기 문서에 직접 쓰는 복구용 데이터라서 서버가 내용을 검증하지 않는다. 그래서 돈이 걸린 건 여기에 맡기지 않았다. 구매 지급은 서버 원장에 순번을 두고, 클라이언트는 마지막으로 적용한 순번 이후만 다시 받는다. 옛 세이브를 복원해서 순번이 뒤로 가도, 서버 기준으로 다시 맞추니 지급이 사라지거나 두 번 들어가지 않는다.
10월 14일 버그는 이 두 기능을 너무 단단히 묶어 둬서 생겼다. 백업은 실패해도 되는 기능인데, 결제 입구가 백업 상태를 보고 있었다. 결제 입구는 이제 '계정이 확정됐는가'만 본다.
2026.10.02~10.14 세이브 관련 커밋
- 저장할 때 첫 클리어 골드 유지
- 옛 빌드의 영업 보고서 복구
- 전투 정산에서 유물 조각 유지
- 영업 방어마다 고유 요청 ID (#01)
- 시설 강화 비용이 저장 후에도 빠지게 (#04)
- 저장할 때마다 사본을 새로, 멈춘 화면 복구 (#02)
- 발굴과 공방 사이 뒤로가기 기록 (#06)
- 우편함이 차도 장비를 분해하지 않게 (#05)
- 모바일 60프레임, 화면 갱신 줄이기
- 자동 로그인과 백업 상한 결정
- 클라우드 백업, 안드로이드 로그인 연결
- 옛 구조 백업 해독 수정
- 백업 실패가 결제를 막지 않게
- 복원 후 패스·보급 수령 검사
2주를 돌아보면
버그 대부분이 "새로 만든 것과 옛날 것이 같이 살아 있는 자리"에서 나왔다. 옛 형식 사본, 새로 생긴 재화를 모르는 복사 함수, 구조가 바뀐 줄 모르는 백업 지문. 고칠 때마다 검사를 하나씩 붙였더니, 같은 종류가 다시 나오면 바로 걸리게 됐다. 그래도 #05처럼 플레이어 장비가 사라지는 건 검사보다 직접 해 본 플레이테스트에서 먼저 나왔다.
다음 편은 상점과 광고, 그리고 결제 서버를 어떤 기준으로 골랐는지다. 플레이테스트를 처음 해 본 얘기는 4편에, 성장과 장비 시스템을 처음 만든 얘기는 3편에 있다. 게임이 궁금하면 게임 소개 페이지부터 보면 된다.

NOW AVAILABLE
마계공단: 던전사업부
용사는 침입자, 몬스터는 직원. 폐쇄 위기의 제7던전을 살려 주세요.
스토어로 이동합니다. Android(Google Play) 버전은 준비 중이에요.