사람들은 왜 선언형 UI에 열광할까?

kimdohun0104
9 min readAug 9, 2020

--

서문

  1. 2017년 5월 구글, 크로스 플랫폼 프레임워크 플러터 공개.
  2. 2019년 6월 애플, UI 개발을 위한 프레임워크 Swift UI 추가.
  3. 2020년 1월 구글, 안드로이드 네이티브 UI를 작성하기 위한 새로운 도구 Jetpack Compose 0.1.0-dev04 버전 공개.

2017년부터 3년 동안 모바일 개발(특히 UI)에는 많은 변화가 찾아왔습니다. 위 발전의 공통적인 특징은 선언형(Declarative) UI 패러다임을 적용한 것입니다.

몇몇 개발자분들은 이 상황에 대해 의구심을 가질 수 있습니다. 안드로이드나 iOS는 이미 UI를 구성하기 위한 자신만의 방식이 존재했으며, 꽤 오래 지속되어 왔습니다.

하지만 안드로이드 프레임워크에서는 선언형 UI를 구현하기 위한 시도가 지속되어왔습니다. 대표적으로 Airbnb의 Epoxy, Facebook의 Litho가 존재합니다. 심지어 Airbnb는 이렇게 말했습니다.

왜 사람들은 선언형 UI에 열광할까요? 이번 글에서는 이 질문에 답하기 위해서 선언형 UI의 다양한 부분에 대해서 다룹니다. 기존 UI 개발의 문제점, 선언형 UI의 개념과 특징, 프레임워크와 개발 경험에 대해서 알아봅시다.

기존 UI개발의 문제점

모든 프로그래밍 패러다임이 그렇듯이 선언형 UI는 기존 문제점을 해결하기 위해 등장했습니다. 이번 섹션에선 실제 개발 사례를 통해서 어떤 문제점들이 발생하는지 알아봅시다.

커스텀뷰 개발

매치-메이킹 프로젝트 디자인

매치-메이킹 프로젝트에서 사용되었던 디자인입니다. 저 같은 경우엔 소셜 로그인 버튼을 재활용하고 싶은 욕구를 참을 수 없었습니다. 사실 모든 클라이언트 개발자분들이 그렇게 느낄 것입니다. 저희는 반복을 싫어하니까요.

안드로이드 프레임워크에서는 UI를 재활용하기 위해 Fragment 혹은 커스텀 뷰를 사용하게 됩니다. 보통 작은 View 단위에서는 커스텀 뷰를 많이 사용합니다. 실제 구현이 어떻게 이루어지는지 확인해봅시다.

소셜 로그인 버튼을 구현하기 위해…

커스텀 뷰를 작성하기 위해서 attrs.xml, layout.xml, java|kotlin 등 다양한 파일을 작성해야 합니다. 그리고 라인 수가 적은 편도 아닙니다. 간단한 소셜 로그인 버튼을 재활용하기 위해서 많은 비용이 소요됩니다. 이는 개발자로 하여금 생산성을 크게 낮출 것입니다.

그 외

그 외에도 재사용성, 확정성 부분에서 문제가 되는 부분에 대해서 예제 코드를 준비했지만, 글이 너무 길어지고 안드로이드 개발 중심적이라 과감하게 제거했습니다.

주요 내용을 요약하자면 UI를 “어떻게” 구현할 것인지 개발자가 상세하게 기술해야 하므로 재사용성과 확장성이 떨어집니다. “어떻게”라는 표현에 대해서는 아래에서 더 자세히 다룰 것입니다.

선언형 UI의 개념

선언형 UI는 상태와 함께 “무엇을” 렌더링할지 정의해주면 자세한 부분은 프레임워크에서 처리해줍니다. 위에서 말했던 “어떻게”와 반대되는 개념입니다. flutter.dev의 선언형 UI 소개의 예시 코드와 함께 비교해봅시다.

명령형과 선언형 비교

명령형과 비교해 더 적은 코드로 직관적인 UI를 구성할 수 있습니다. UI의 속성을 순차적으로 나열하는 명령형과는 다르게, 파라미터로서 속성을 지정하는 것을 확인할 수 있습니다.

선언형 UI에서의 한 가지 공식

하나의 진리

선언형 UI에서 가장 중요한 것은 위 공식입니다. 이것은 하나의 진리로 통할 수 있습니다. ‘위젯 또는 함수’의 인자로 상태를 넘겨주면, 그에 맞는 View를 생성합니다. 함수형 프로그래밍 개념의 순수 함수처럼 같은 상태에 대해서는 항상 똑같은 View를 리턴합니다.

플러터에서 모든 UI는 위젯으로 구성되어 있지만, Jetpack Compose는 @Composable 함수로 이루어져 있기 때문에 ‘위젯 또는 함수’라고 표현했습니다. 이는 각 프레임워크에서 설계 의도에 따라 달라질 뿐 진리는 변하지 않습니다.

이런 특징으로 인해 선언형 UI에서는 XML, HTML 등을 사용하지 않고, 코드로 UI를 작성하는 것이 일반적입니다.

UI 업데이트와 교체

각 위젯은 부모 자식 관계를 형성합니다. 그 관계는 마치 위 그림처럼 트리 형태를 이룹니다. 이를 플러터는 Widget Tree, Jetpack Compose는 Function Tree라고 부릅니다.

트리가 변경되는 경우를 간단하게 코드와 다이어그램으로 나타내봅시다.

만약 user 정보가 null이라면, progress를 나타내고, 아니면 정보를 렌더링합니다. 기존 위젯의 상태를 변경하는 것이 아닌, 위젯 트리를 재구성합니다. 그래서 UI의 일부를 변경하고, 제거하는데 명확하고 간결한 코드를 유지할 수 있습니다.

플러터 위젯의 특징이나 렌더링 방식에 대해 궁금하시다면 ‘플러터는 어떻게 위젯을 렌더링할까?’ 글을 확인해주세요.

프레임워크와 개발 경험

아무리 뛰어난 프레임워크라 하더라도 최종적으로 개발 경험이 개발자에게 가장 큰 영향을 미칠 것입니다. 디버깅, 린트, 컴파일 등 보편적으로 IDE에서 지원하는 기능 외에도 선언형 UI에 맞는 새로운 무엇인가 필요합니다. 각 프레임워크에서는 개발 경험을 위해서 어떤 기능을 제공할까요?

Jetpack Compose를 지원하는 IDE

Jetpack Compose는 Android Stduio에서 사용하기 때문에 강력한 기능들과 플러그인을 그대로 사용할 수 있습니다. 안드로이드 개발자분들은 익숙한 환경에서 개발하실 수 있습니다.

Jetpack Compose의 @Preview

만약 UI 미리 보기를 지원하지 않는다면 어떨까요? 정상적으로 UI가 구성되었는지 확인하기 위해서 UI를 수정할 때마다 앱 전체를 빌드해야 할 것입니다. 이것은 상상만 해도 끔찍합니다.

다행히도 Jetpack Compose는 @Preview를 통해서 앱 전체를 빌드하지 않고 UI를 확인할 수 있습니다. 하나의 @Composable함수에 여러 @Preview를 생성해 다양한 속성에서 UI가 어떻게 변하는지 확인할 수 있습니다. 위 코드에서는 미리 보기에 대한 설명을 부여하는 것으로 끝났지만, 실제로는 그룹 설정, apiLevel, 크기, 화면 방향(orientation) 등 다양한 미리 보기 설정을 할 수 있습니다.

Jetpack Compose의 Interactive Preview

Jetpack Compose는 Preview에서 한발 더 나아가, 앱을 빌드하지 않고 UI와 상호작용할 수 있는 기능을 공개했습니다. @Preview 어노테이션으로 생성된 미리 보기에서 ‘interactive’를 클릭하면 실제로 클릭, 스크롤 등 이벤트를 줄 수 있습니다.

플러터를 지원하는 IDE

플러터는 Android Studio, Intellij, vscode 모두 사용할 수 있습니다. 조금 무겁더라도 Jetbrains 계열의 강력함을 원하는 분은 Android Studio 혹은 Intellij. 가볍고 다양한 extensions를 활용해 자신에게 딱 맞는 IDE를 구성하는 분들 모두 만족할 수 있습니다.

플러터의 Hot reload

플러터에서는 Preview를 지원하지 않습니다. 하지만 Hot reload기능을 지원합니다. flutter.dev의 Hot reload를 확인해봅시다.

Hot reload works by injecting updated source code files into the running Dart Virtual Machine (VM). After the VM updates classes with the new versions of fields and functions, the Flutter framework automatically rebuilds the widget tree, allowing you to quickly view the effects of your changes.

Hot reload는 업데이트된 코드 파일을 실행 중인 Dart VM에 주입함으로써 동작합니다. VM이 변경된 필드와 함수를 업데이트한 후, 플러터 프레임워크는 자동으로 위젯 트리를 리빌드하여 변경사항을 빠르게 확인할 수 있습니다.

Hot reload기능을 통해 개발자는 UI뿐만 아니라 클래스, 함수에 적용되는 변경까지 500ms도 안 되는 짧은 시간으로 확인할 수 있습니다.

플러터의 DevTools

DevTools는 Dart와 플러터를 위한 성능 모니터링, 디버깅 툴입니다. 더 자세한 내용은 Dart Devtools영상을 참고해주세요.

저같은 경우에는 Provider, ScopedModel 처럼 Scope를 통해서 상태 관리를 도와주는 플러그인을 사용할 때 큰 도움이 되었습니다.

마무리

지금까지 사람들이 왜 선언형 UI에 열광하는지 알아보았습니다. 기존 UI 개발의 문제점인 재활용, 확장성을 해결한 것도 크지만, 개발자의 경험을 고려해 제공되는 기능들도 한몫한 것 같습니다.

긴 글 읽어주셔서 감사합니다.

--

--