목차
UI 프레임 워크에 대한 잡설
- 항상 모든 프로젝트들이 그러하듯 초반에 제작되는 UI 프레임워크에 따라 전체적인 개발 방향이나 업무 분담과 효율이 정해진다.
- 프로젝트의 기획적 의도와 UI 구조를 복잡하게 가져가는지 아닌지에 따라 천차만별로 달라지는게 UI 프레임워크이다.
- 단순하게 Normal, Popup, Modal, Error 등등 기본적인 레이어를 나누고 에러, 팝업 & 모달, 토스트, 뷰어로만 구성되는 구조가 있는 반면
- 우리 프로젝트의 경우 제페토나 인스타그램의 요소가 들어가있기 때문에 어쩔 수 없이 복잡한 구조를 가져가게 되는 구조도 존재한다.
- 다양한 디자인 패턴들을 적용하고 룰들을 지정해서 개발하면 가독성도 좋고 효율적인 작업을 이루어낼 수 있다. 예를 들어 MVC, MVP, MVVC ..
- 하지만 개발 도중 불가피하게 이러한 규칙들을 벗어나게 되는 예외 케이스들이 존재하기 마련이다.
- 특히 개발자의 편의성과 빠른 TDD를 위해 혹은 애초에 구조적으로 디자인 패턴을 깨야만 하는 때가 찾아온다. 여기서 정말 많은 고민과 고뇌에 빠져서 그것을 탈피하기 위한 갖가지 방법과 수단을 동원해보지만
- 결국 끝에가서는 깨버리니깐 수긍하고 빨리 넘어가자. 이건 어쩔 수 없다고 생각한다.
- 절대적인 완벽함을 추구하는 것도 좋지만, 결국 이회사에서 배운 점은 우리는 서비스를 만들어내는 개발자들이라는것. 그 서비스를 만들어 내기 위해서는 때로는 빠르게 타협하고 넘어가야만 하는 경우도 존재한다. (물론 처음부터 완벽하게 만들면 좋겠지만..)
토이버스의 UI 프레임워크
- 토이버스는 제페토, 인스타그램 과 같은 구조를 가져가기 때문에 UI구조가 정말 복잡해져버렸다.
- 유저들이 주로 사용하는 씬은 홈, 월드, 디자인 툴 이렇게 크게 3개인데, 주로 홈에서 프로필, 인벤토리, 모멘트(유저들이 투고한 게시물을 보는 갤러리), 상점, 미션, 설정 등 아주 깊은 계층구조와 UI스택을 가져가게 되버렸다.
- 디자인 패턴은 MVVC를 따르도록 설계했으나, 위에서 언급했듯이 구조적으로 불가피하게 패턴을 깨버려야만 했다. 그에 대한 설명을 밑에서 하도록 하겠다.
- 아주 깊은 계층구조는 예를 들어, 인벤토리 선택 -> 인벤토리 뷰 생성 -> 유저가 보유한 토이 모델 (nOn이라 지칭) 썸네일 클릭 -> RenderTexture 를 생성하여 RawImage에 표시하는 nOn의 전체적인 모습의 뷰 -> 투고 버튼 클릭 -> 투고 뷰 생성 -> 배경, 애니메이션 선택 -> 본문, 해시태그, 공개 설정 작성 -> 해시태그 검색 뷰 생성 -> 투고 버튼 클릭 -> 다시 nOn 뷰로 돌아오기… 이까지 엄청나게 많은 뷰 들과 팝업들이 존재한다.
- 여기서 우리는 메인 뷰, 서브 뷰의 개념을 도입하였다.
- 메인 뷰는 말 그대로 서브 뷰들을 총괄하는 컨트롤러역할을 한다. 홈에서는 HomeView가 메인뷰의 역할이고 InventoryView, PostView들이 서브뷰의 역할을 한다.
- 하지만 여기서 서브뷰에 너무나도 많은 기능을 요구하는 바람에 서브뷰가 오히려 메인뷰 보다 기능들과 코드들이 비대해지면서 서브뷰를 Partial 클래스로 기능 별로 분리하여 관리하도록 했다.
- 또한 각종 공통적인 기능을 처리하고 서브 뷰들의 스택을 관리하는 인터페이스를 만들고 그를 상속하여 메인뷰의 기능을 담당하는 Handler 를 도입했고 메인 뷰에 세팅할 (각종 S3와 통신하여 업/다운로드할 이미지들, 텍스트들, 로컬라이즈, NGword 필터링, 애니메이션 등등) xxViewData 클래스를 도입했다.
- UIHandler는 메인뷰를 관리, 메인뷰는 메인뷰 데이터와 서브뷰를 관리, 서브뷰는 서브뷰 데이터를 관리하는 형태를 베이스로 가져갔다.
- 서브뷰 간 이동은 이전 뷰는 스택에 저장 후 삭제, 현재 뷰 생성이고 만약 뒤로가기를 누르면 스택에 저장한 이전 뷰를 생성 후 현재 뷰 삭제와 같은 구조이다.
- 쓰다 보니 글 밖에 없어서 코드와 이미지를 섞어서 보여줄 수 있도록 좀 고민해봐야겠다..
이 과정에서 겪은 딜레마
- 서브 뷰들의 게임 오브젝트를 on off 하면서 메모리상에 유지 시킬지 vs Instantiate/Destroy 하며 GC를 유발하지만 메모리 효율을 가져갈지
- 계층구조가 1층,2층 밖에 없으면 전자를 선택했겠지만.. 위에 설명한 계층구조를 봤듯이 많게는 9개 10개까지 쌓여있다..
- 게임 오브젝트의 SetActive는 계층구조가 복잡하면 복잡할 수록 엄청 비효율적이다. GC발생은 물론이고 퍼포먼스가 저하되므로 복잡한 계층구조를 가진 게임 오브젝트를 켜고 끄는건 지양해야한다.
- 또한 우리는 투고 게시물이라는 특수 케이스가 존재하고 그곳엔 엄청나게 많은 양의 이미지가 포함되기 때문에 메모리 구조상 여러 개의 서브 뷰들을 메모리 상에 상주시키는게 매우매우 비효율적이다.
- 따라서 생성 파괴를 통해 다소 가비지 컬렉터가 발생하더라도 가비지 컬렉터를 관리하는 차선을 선택했다. (여기선 어드레서블 시스템을 사용하는것도 결정적인 영향을 줌)
- 서브뷰의 생성 파괴를 하기 때문에 뒤로가기 버튼 -> 이전 뷰가 그대로 보여져야하는 이슈가 발생했다.
- 이전 뷰를 스택에 담아두고 뒤로가기 입력 시 현재 뷰를 파괴 -> 이전 뷰 생성 및 데이터 셋을 함으로 해결했다.