iOS

회사 게임의 iOS 버전 포팅을 마무리하다.

Unity 2017.1 로 만들어진 회사 게임 ( RPG 클리커)은 1년이 넘었고 나름대로 흥행 성공했지만 ,

iOS 버전 출시를 하지 못해왔다고 한다. 그래서 내가 이 프로젝트에 대한 어느정도 숙지가 끝나자마자

iOS 를 굽는 임무에 투입되었다 !

다양한 이슈들을 만나고 ( 블로그 포스팅을 올리지 못했던 일주일 동안 ㅎㅎ 힘들었다 ..)

Unity iOS 포팅은 처음은 아니고 전 회사와 개인 작도 경험했지만,

개인 작은 출시를 하지 못했고 , 전 회사 플젝은 어드벤쳐 게임이고 인앱이 1개고 클라우드 데이터도 단순해서 큰 문제가 되지 않았었다 .

가장 먼저 맞닥뜨린 iOS에서만 등장하는 버그

게임은 전진하며 몬스터를 사냥하고 새로운 몬스터를 만나는 방식으로 게임 진행이 이루어지는데 ,

iOS 버전과 MAC , PC 버전에서만 한마리를 잡고나면 다음 몬스터 리젠이 안되는 크리티컬한 버그가 발생했다.

1시간 정도 헤메고

로그도 좀 찍어본 결과 원인이 게임 내 로컬라이징 관련 정보들을 파싱해올 때 경로 문제인 것을 파악했다.

그래서 보니, 기존 코드에서 해당 경로를 플랫폼에 따라 분기를 나누는 과정에서 오류가 있었다. ( 아마 예제코드 부터 잘못되었던 것 같다 . )

그래서 해결 !

날로 먹은 게임 센터, 별거 아닌 줄 알았는데 헤멘 아이 클라우드 저장

게임 센터는 예상과 달리 유니티 내장 Social 라이브러리로 쉽게 해결 가능했다 .

허나 iCloud는 조금 고생했다 .

처음에 깃 허브에서 줏은 오픈소스 라이브러리를 사용했는데 , 정상적으로 잘 작동했지만

GPGS와 달리 세이브와 로드할 때 게임이 멎었다 .

( 내 예상으로는 GPGS는 다른 쓰레드에서 처리하도록 하는 것 같은데 , 이 소스는 그게 안되어 있던 것 같다. )

나름 유저 경험에 크리티컬한 이슈라

회사에 말해서 Prime 31 iCloud Plugin 이라는 유니티 에셋을 구입하여 사용했다.

마법처럼 될 것 같았지만..

유저 정보 -> JSON -> Byte Array -> Key-Value 로 배열 사이즈와 배열내용을 하나씩 저장 (Data_1 , Data_2 하는 식으로 )

저장은 했지만 로드를 하지 못하는 상태로 하루를 꼬박 소비했다.

에셋 설명을 아무리 읽어도 해결책을 못찾다가 .

Apple 공식 문서인 iCloud Fundamental의 Key-Value 설명을 보고 답을 알아냈다.

Key-Value Store 는 총 1MB 의 키 - 밸류 쌍을 저장할 수 있으며 ,

그 쌍은 총 1024개만 허용된다.

1MB가 작아보이지만, 왠만한 게임의 유저 데이터는 저 안에서 다 저장 가능하다 . ( 보통 큰 게임도 30KB 정도에서 끝나는 듯 )

그래서 기존의 바이트 어레이로 (11235 개) ( 이 프로젝트의 유저 정보 자체를 한번 정리 할 필요가 있을 것 같기도하다 .. ) 하던 걸

그냥 JSON 하나로 키 - 밸류로 저장했다 .

얻은 교훈은

에셋을 과신하지 말자 .

해당 Native에 가까운 문서를 가장 열심히 보고 그 후에야 에셋이나 플러그인의 해당 문서를 보는 것이 좋을 것 같다.

된통 깨진 경험 ㅎㅎ.

그밖에 잡스러운 iOS 대응들

노치 디자인이라던가 , 아직 iOS 용 서버가 개발이 끝나지는 않아서 랭킹 부분을 막아두는 등의 자잘한 업데이트도 진행했다 .

In APP Localized Price 할당 ( 국가별 인앱 상품 가격 텍스트 설정 )

Unity IAP 의 내장 API 중에 , 구글 스토어나 애플스토어에 이 게임의 상품 ID 를 던지면

결제 유저의 통화에 맞게 가격 스트링을 던져주는 기능이 있다. (MetaData.LocalizedPriceString? )

이 것을 이용해서 유저에 따라서 스트링을 뿌려주는 것을 만들어야 하는데 ,

기왕 하는 김에 좀 더 생산성이 좋고 쾌적한 방식이 없을까 고민하다가 다음과 같은 방식으로 구현했다.

가격 텍스트에는 “InAppCostText” 라는 Tag 를 설정해준다 . ( 커스텀 클래스를 짜넣어서 컴포넌트를 써도 되지만, 굳이 낭비하기 싫었다 )

그리고 InAppCostManager 라는 MonoBehaviour 를 상속 받는 ( 컴포넌트 ) 객체를 하나 만들고 ,

이 클래스의 Custom Edior (Inspector) 도 하나 만들어준다 .

이 에디터에는 버튼이 하나 달려있고 , 그걸 누르면 씬내의 모든 Tag == “InAppCostText” 인 Text 를 검출하여

딕셔너리로 들고 있는다. Key 는 Text의 GameObject.name 으로 하고 , Value 는 Text 컴포넌트 로 넣는다 .

그리고 그 딕셔너리 갯수와 키값들도 아래로 죽 늘어놓아 쉽게 확인 가능하게 한다 .

그리고 게임이 시작되면 IAP Store Controller != Null 일때 까지 대기하다가 IAP 초기화에 성공한 후에

현지화된 가격을 Text 에 뿌려준다 .

이 간단한 구현에도 두가지 이슈가 발생했다 . ( 홓 )

첫째로는 씬 내의 해당 태그를 가진 오브젝트를 검출하는 함수가 InActive 상태인 오브젝트는 검출하지 못했다 .

그래서 Resources.FindObjectWithType? 였나 하는 ( 왜 Resources에 있는지 외국 유니티 개발자들도 전부 의문인듯 했다 . )

함수로 Text 컴포넌트를 다 긁은 뒤에 거기서 Tag 를 비교해 인앱 텍스트만 가려서 딕셔너리에 저장했다 .

두번째로는 딕셔너리 자체가 시리얼라이즈 되지 않기 때문에 씬의 컴포넌트 상태로 제대로 저장되지 않는다는 점이었다. (SetDirty 를 해줘도 안됨 )

그래서 해결책으로 배열 두개를 만들어 딕셔너리를 흉내내서 박아놓고 게임 시작하면 이 배열로 다시 딕셔너리를 구축하는 방식을 사용했다.

AI 개발과 인지부채

AI를 이용한 개발에서 인지부채를 마주하고, 같은 패턴으로 파티가 전멸하지 않으려면 Continue reading

카페에서 우연히

Published on May 31, 2026

근황

Published on May 17, 2026