Server Driven UI (Feat.Flutter)

상황에 맞는 맞춤형 사용자 경험을 위하여

kimdohun0104
10 min readJul 7, 2020

Swiggy의 상황에 맞는 사용자 경험

Swiggy는 인도에서 가장 규모가 큰 음식 배달 애플리케이션입니다. 아래의 두 화면은 동일한 버전의 앱입니다. 하지만 약간 다르다는 것을 눈치챌 수 있습니다.

집에 도착한 후 주변의 Swiggy 음식점을 확인할 수 있는 UI가 추가되었습니다. 이처럼 사용자의 환경에 따라 적절한 UI를 제공함으로써 개인화되고 상황에 맞는 경험을 제공할 수 있습니다.

어떻게 구현되었을까요? 다음과 같은 코드를 상상할 수 있습니다.

if (swiggyStores == null || currentLocation = HOME) {
view.visibility = GONE
}

하지만 위 코드에는 몇 가지 문제점이 있습니다.

  1. UI를 더 추가하기 위해서 화면과 코드를 새롭게 작성해야 합니다.
  2. 사소한 UI 변경에도 코드를 다시 작성하여 배포해야 합니다.
  3. 스토어에 따라 배포가 바로 적용되지 않고, 검증 기간이 필요할 수 있습니다. (지연 시간 발생)
  4. 사용자가 앱을 업데이트하지 않는 경우엔 변경 사항을 적용할 수 없습니다

위 문제들을 어떻게 해결할까요? Swiggy, Airbnb, Flipkart 등 많은 기업에서는 Server Driven UI(이하 SDUI)를 통해서 이 문제를 해결하고자 했습니다.

Server Driven UI?

Server Driven UI를 그대로 해석하면 ‘서버 주도 개발’이라는 뜻입니다. 약간 어색하게 들릴 가능성이 있습니다. 저는 서버가 UI와는 많이 동떨어진 영역이라고 생각했습니다. 하지만 그런 서버가 UI를 주도한다는 것은 약간 장난처럼 들리기도 합니다.

PickUp 과 함께 살펴보는 구현 예시

어색함을 떨쳐내기 위해 ‘PickUp’ 프로젝트를 예로 들어 SDUI를 적용하기 전과 후 차이점을 비교해봅시다.

REST API를 사용한 모습

클라이언트의 요청에 따라 서버는 적절한 데이터를 JSON 형태로 반환합니다. 너무 익숙한 형태이기 때문에 바로 SDUI 예시로 넘어가겠습니다.

SDUI를 적용한 모습

JSON형식으로 응답이 오는 것은 동일하지만, type이라는 부분에서 Rest API와 차이가 분명해집니다. type은 어떤 ViewComponent를 렌더링할지 명시해주는 부분입니다.

여기서 말하는 ViewComponent는 왼쪽의 사진처럼 독립적으로 구현되고 동작하는 각각의 View를 말합니다.

다양하게 구성할 수 있는 ViewComponent

클라이언트는 서버의 응답에 따라 순서대로 ViewComponent를 렌더링하기 때문에 서버가 보내주는 것에 따라 순서가 바뀔 수 있습니다. 심지어 특정 View를 렌더링하지 않을 수 있습니다. 서버가 UI 렌더링에 대한 주도권을 잡고 있는 모습을 확인할 수 있습니다.

왜 SDUI를 사용할까?

유연한 UI

이제 위 화면을 어떻게 구현했는지 예상할 수 있습니다. 아마 Card 형태의 ViewComponent가 존재할 것이고, 그것은 title, description, image, action 등의 속성을 포함할 것입니다. 서버는 사용자의 위치에 따라서 해당 ViewComponent를 줄 것인지 말 것인지 결정하여 응답합니다.

만약 새로운 기능을 담당하는 Card UI를 추가하고 싶다면 새로운 클라이언트 배포 없이 서버에서 처리할 수 있습니다.

배포 전달력

SDUI의 배포 전달력

안드로이드나 iOS 앱 같은 경우에는 새로운 버전을 배포하면 사용자가 무조건 업데이트를 한다는 가정이 없습니다. 게다가 특정 스토어같은 경우에는 추가적인 심사 과정 때문에 지연 시간이 소요될 수 있습니다.

하지만 웹의 세계는 정반대입니다. 개발자가 배포를 마치면 사용자는 그저 F5를 클릭하여 변경 사항을 적용할 수 있습니다.

SDUI는 이 사이에 있습니다. 그 이유는 서버에서 어떤 ViewComponent를 어떤 정보로 렌더링할지 결정하기 때문입니다. 앱을 새로 배포하지 않고, 서버를 수정하는 것만으로 변경사항을 전달할 수 있습니다.

하지만 앱의 버전에서 해당 ViewComponent가 구현되지 않았다면 렌더링할 수 없습니다. 그렇기 때문에 웹만큼 자유롭다고 할 수 없습니다.

구현 방법 선택

개념만 알아보기에는 아쉬운 점이 너무 많아 직접 구현해보기로 했습니다.

안드로이드 프레임워크

CustomView or Fragment: 가장 먼저 독립적인 ViewComponent를 구현하기 위해서 커스텀뷰와 프래그먼트 사용을 고려해보았습니다. 두 방법 모두 상당한 보일러 플레이트를 유발하며, 개발 비용이 상당합니다. 현실적으로 불가능한 요소가 많다고 판단했습니다.

Library: Airbnb, Facebook, Flipkart, Swiggy 등 이미 많은 회사에서 SDUI를 구현하기 위한 라이브러리를 자체적으로 제작하여 사용하고 있습니다. 모두 멋진 라이브러리지만, 제가 생각하던 구현 모습과는 거리가 있었습니다.

JetpackCompose: JetpackCompose는 개별적으로 화면을 구성할 수 있는 Composable함수를 지원하며, 안드로이드 스튜디오에서 Preview를 확인할 수 있습니다. 독립적인 ViewComponent를 우아하게 개발할 수 있습니다.

하지만 현재 JetpackCompose는 알파도 베타도 아닌 개발 단계입니다. 게다가 안드로이드 스튜디오 Canary 버전을 따로 깔아야만 사용할 수 있습니다.

또다시 플러터

모든 것이 위젯인 플러터에서는 독립적인 ViewComponent를 구현하기가 상당히 쉽습니다. 구현을 위해 많은 코드가 필요하지 않습니다. 그저 Widget을 상속함으로써 새로운 ViewComponent를 구현할 수 있습니다. 마치 SDUI를 위해 디자인된 모습 같습니다.

전재

저는 SDUI를 개발하기 전 하나의 전재를 만들었습니다. 모든 ViewComponent는 하나의 Column 안에 포함되어야 한다는 것입니다. 이 말은 ViewComponent들이 가로로 연결되어 하나의 페이지를 구성한다는 것입니다.

JSON 형식

SDUI를 구현하기 위해서 가장 중요한 부분은 JSON 형식입니다. 아마 이 부분에서 서버 개발자와 가장 많이 소통해야 할 것입니다.

너무 구체적으로 정보를 세분화하면 그만큼 개발도 힘들어지고, 그렇다고 너무 간소화하면 의미가 없어집니다. Airbnb의 발표에서 많은 영감을 얻어 다음과 같은 JSON 형식을 사용하기로 했습니다.

type: 어떤 ViewComponent를 사용할 것인가?

Content: ViewComponent는 어떤 정보를 담는가?

Action: 해당 ViewComponent로 실행할 수 있는 액션은 무엇인가?

핵심 코드 구현

Composable

Composable 추상 클래스는 compose함수를 포함하고 있습니다. 이 compose 함수는 하나의 ViewComponent를 리턴합니다.

예시로 Composable을 구현하는 FooAppBarComposer 를 확인해봅시다. map을 통해 서버에서 전달받은 content에 접근하여 AppBar를 구성하여 리턴하는 모습입니다.

ComposableScaffold

composerByName을 통해 서버에서 불러온 type과 구현된 Composer를 매핑합니다. 그리고 실제로 Column에 위젯을 적용하는 코드는 다음과 같습니다.

composerInfoMaps.map((widget) => 
composerByName[widget['type']].compose(widget, context))
.toList())

composerByName에서 해당 type을 통해 composer를 찾아 compose로 위젯을 구성합니다.

장점

SDUI의 장점으로는 업데이트, 클라이언트 로직 없이 UI를 변경하기 때문에 상황에 따른 개인화된 UI를 제공할 기회가 생깁니다.

그리고 독립적으로 ViewComponent 구현에 집중하기 때문에 구조적으로 UI 개발을 할 수 있습니다

단점

서버 개발자는 UI에 대해 고려해본 경험이 없을 가능성이 높습니다. 그렇기 때문에 직접 주도하여 화면을 구성하는 것은 큰 도전이 될 수 있습니다. 게다가 REST API와는 다른 통신 처리 방법, 문서 작성 등 연구 비용이 많이 들 수 있습니다.

하지만 전체를 SDUI로 구현하는 것이 아닌 필요한 부분에만 개인화된 UI를 제공하는 것이 비용과 리스크를 낮출 수 있다고 생각합니다.

앞으로의 방향

ViewComponent 버전

만약 서버에서 ‘FooWidget’ 렌더링을 요구했지만, 클라이언트에서 대응하지 못했거나, 사용자가 업데이트하지 않았을 수 있습니다. 이런 상황에서는 어떻게 될까요? Crash가 발생하거나 저희가 원하는 경험과 어긋날 수 있습니다.

이런 문제를 해결하기 위해서 각 Component에 버전을 부여할 수 있습니다. 부여된 버전은 클라이언트가 서버가 렌더링을 요청하는 ViewComponent에 대한 구현을 확실하게 보장할 수 있어, 효율적인 개발이 가능합니다.

Kotlin DSL

Airbnb는 문서를 작성하기 위해서 Kotlin DSL을 사용했습니다. 쉽게 말해서 Kotlin코드를 통해서 특정 문법에 따라 문서를 작성하는 것입니다. 더 안전하고 관리하기 쉬운 문서를 작성할 수 있습니다.

그래서 저도 경험 삼아 아래와 같이 작성하는 DSL을 제작해보았습니다.

마무리

이렇게 SDUI에 대해서 알아보고 구현해 보았습니다. 아래 깃허브에서 전체 코드를 확인할 수 있습니다. 아직 부족하고 추가할 부분이 많지만, 피드백은 언제나 환영입니다. 감사합니다.

--

--