카카오스타일 지그재그UX그룹이 네이티브 앱 UI 를 배포 없이 바꾸기 위해 Server Driven UI 설계를 도입한 기록이다. 서버가 GraphQL 유니온 타입으로 UI 컴포넌트 목록을 내려주고 클라이언트가 그걸 조립해 화면을 만든다. 컴포넌트와 내용을 분리한 컴포넌트 메서드 개념까지 다룬다.
핵심 포인트- 문제는 네이티브 앱이 빌드와 배포 후에는 수정이 불가능하다는 점이다. 잘못된 위치의 버튼도 사용자가 직접 업데이트해야만 고쳐진다.
- A/B 테스트도 같은 벽에 부딪힌다. 사용자를 나눠 서로 다른 UI 를 주려면 UI 가 클라이언트 배포로부터 자유로워야 한다.
- 서버가 API 응답에 UI 정보를 담고 클라이언트가 그에 따라 렌더링하면, 서버 응답 변경만으로 화면 구성을 바꿀 수 있다.
- GraphQL 을 쓰면 서버와 클라이언트가 공유하는 스키마로 타입 안전성이 확보되고, 프래그먼트가 재사용 가능한 필드 묶음이라 컴포넌트를 주고받기에 적합하다.
- 클라이언트는 사용 가능한 모든 컴포넌트 프래그먼트를 요청해야 한다. 구현 시점에 쓰는 것만 요청하면 나중에 서버가 다른 컴포넌트를 넣어도 보여줄 수 없다.
상세 정리- UI 요소를 전부 재사용 가능한 컴포넌트로 만들지 않으면 여러 화면에서 교체하기 어려워진다. 컴포넌트화가 전제 조건이다.
- 컴포넌트 목록은 유니온 타입이나 인터페이스로 표현한다. 인터페이스 방식은 어떤 타입이 UI 컴포넌트인지 명확해진다는 장점이 있다.
- 하위호환성은 GraphQL 특성에서 나온다. 서버가 신규 컴포넌트나 필드를 추가해도 구버전 클라이언트는 요청하지 않으므로 영향이 없다.
- 다만 기존 컴포넌트에서 구버전이 쓰는 필드를 제거하거나 non-nullable 을 nullable 로 바꾸면 하위호환성이 깨진다.
- 디자인 시스템이 선행돼야 한다. UI 레벨에서 정립돼 있지 않으면 컴포넌트 개념 도입 자체가 어불성설이고, 서버와 클라이언트 개발자, 디자이너 사이 용어가 달라져 커뮤니케이션 비용도 오른다.
- 데모는 플러터로 만들었다. 머티리얼 디자인 시스템 구현 수준이 높고, 위젯 개념이 컴포넌트와 부합하기 때문이다. 실제 지그재그 앱은 플러터를 쓰지 않는다.
- 클라이언트에는 GraphQL 스키마를 바탕으로 한 컴포넌트 레지스트리가 필요하다. 응답의 각 프래그먼트를 자기 컴포넌트에 대응시키고 그것을 위젯으로 바꾼다.
- 레지스트리에 등록되지 않은 컴포넌트가 응답에 섞여 있으면 자연스럽게 필터링된다.
- 도입 전에는 화면마다 전용 쿼리를 써야 했고, 새 UI 요소를 넣으려면 응답에 필드를 추가하고 클라이언트 작업도 따로 해야 했다.
- 지금은 여러 화면이 하나의 쿼리를 쓰고 같은 컴포넌트를 재사용한다. 홈탭의 상품 카드 컴포넌트를 베스트탭에서도 그대로 쓴다.
- 컴포넌트 종류나 순서 변경은 서버 응답만 바꾸면 되므로 A/B 테스트용 유저군별 UI 는 물론 개인화 UI 도 쉽게 제공한다.
- 같은 컴포넌트를 다른 내용으로 채우기 위해 컴포넌트와 메서드를 짝지은 컴포넌트 메서드 개념을 만들었다. 컴포넌트는 UI 요소, 메서드는 그 안을 무엇으로 채울지를 뜻한다.
- 서버는 화면마다 어떤 컴포넌트를 어떤 메서드로 제공할지 적은 설계도를 갖는다. 홈 탭과 베스트 탭 모두 상품 카드 목록을 쓰되 메서드가 다르다.
- 컴포넌트 생성 구현체는 컴포넌트 메서드 프로퍼티와 컴포넌트를 반환하는 함수를 가진 인터페이스를 구현한다.
- 스프링부트를 쓰기 때문에 생성자가 구현체 목록을 받아 컴포넌트 메서드와 구현체의 키-값 맵을 미리 만들어둔다. 덕분에 화면별 응답 함수를 따로 쓰지 않고 설계도만 바꾸면 된다.
- 남은 과제로 컴포넌트의 상위 개념인 레이아웃, 특정 컴포넌트 내 페이지네이션, 페이지 일부만 리로딩하는 방법을 꼽는다.
왜 읽나앱 배포 주기 때문에 UI 실험이 막힌 팀에게, GraphQL 스키마로 컴포넌트를 주고받는 구조와 컴포넌트와 데이터를 분리하는 설계를 구체적으로 보여주는 글.