ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Flutter] 위젯, 뷰, 컴포넌트, 스크린 어떤 차이일까? UI 구조를 잡아보자!
    Flutter/project 2024. 1. 30. 11:06
    처음 flutter 개발을 시작하면 가장 먼저 접하는 것이 widget을 이용해 간단한 UI를 제작하는 것입니다. 학습을 하는 단계에서는 위젯을 간단하게 사용해서 화면을 만들지만, UI/UX디자이너와 협업을 하게 되면 요구되는 디자인에 맞게 거의 대부분의 위젯을 커스텀하는 작업이 필요합니다. UI를 구조를 잡기 전엔 제작한 위젯과 화면이 많아질수록 파일 간 이동할 일 있을 때, 방금 작업한 파일의 위치도 생각이 나지 않아 하나하나 파일을 눌러 코드를 보면서 찾았던 기억이 있습니다. 그때 스스로 학습한 것을 바탕으로 프로젝트에 개념을 적용했던 과정을 글로 남겨보도록 하겠습니다.

     

     

    ✓ Widget

    플러터에서 위젯은 UI를 구성하는 기본 단위입니다.  위의 화면은 단순히 모든 위젯을 노란박스로 표시한 상태입니다. 하지만 "UI 구조를 잡는 관점"에서 다시 설명하자면 Widget은 본인이 만든 위젯의 가장 작은 단위를 의미하게 됩니다. 개발자마다 판단의 기준이 다르기 때문에 위의 화면을 보고 누구는 위젯이 20개가 나올 수도 있고, 누구는 5개만 나오는 상황이 벌어질 수도 있죠. 우리는 협업과 유지보수를 위해 일단 명확하게 기준을 세우고 위젯을 분리하는 작업이 필요합니다.

     

    ✓ Component

     

    • 컴포넌트란?
      • 시스템을 구성하는 독립적이고 재사용이 가능한 부분을 말합니다.
      • 코드 중복을 줄이고 생산성을 높이기 위해 하는 행위입니다.

    출처: Flutter 공식문서

    UI를 만들때 기본적으로 사용되는 모든 위젯(Container, Column, Row, Text 등)을 컴포넌트 위젯이라고 합니다. 이 위젯들은 재사용되며, 속성(property)만 조금씩 변경하면 본질은 같으나 색상이나 정렬 등이 변경되는 특징이 있습니다.

     

     

    위의 예시에서 힌트를 얻으면 컴포넌트 위젯을 만들 때 본질은 같지만 조금씩 다른 것, 다시 말해 UI가 중복되거나 비슷한 구조로 생긴 위젯들을 모두 컴포넌트화 시켜 재사용할 수 있습니다. 가장 쉬운 예시로는 Card가 있습니다.

     

     

    저 같은 경우는 코드를 치면서 위젯을 나눕니다. 그리고 동시에 "지금 내가 만들고 있는 이 위젯은 컴포넌트화가 가능한 위젯인가?"라는 생각 합니다.  view와 screen단위를 제외하면, 위젯의 종류는 "컴포넌트화가 되냐, 안 되냐" 두 개로 나눠지기 때문에 컴포넌트화가 가능한 것만 분리한다면 첫 번째 단계는 끝나게 됩니다.

     

    저는 컴포넌트화 할 때, 최대한 위젯간에 중복되거나 비슷한 구조로 생긴 것을 찾습니다.

    제가 볼땐 다음과 같이 나눌 수 있을 것 같습니다. (파란 박스 == component)

    •  "쿠폰, 포인트, 내리뷰" 위젯은 아이콘과 타이틀이 중복되고 숫자가 있는 부분을 require로 받지 않는다면 내 리뷰일 때는 숫자를 보여주지 않도록 할 수 있으므로 컴포넌트화 할 수 있습니다.
    • 하얀색 보드같이 생긴 위젯은 단순히 Layer 또는 Card 같은 역할을 하기 때문에 컴포넌트화 할 수 있습니다.
    • 하단 내비게이션 바의 버튼들도 구성이 모두 같기 때문에 컴포넌트화 할 수 있습니다.

     

    ✓ View & Screen

     

    이런 모든 위젯들이 모여서 결국 하나의 화면을 이루게 됩니다. '화면'은 라우팅을 설정할 때 사용되는 단위이며 플러터에서는 라우트(Route)라고도 부르고, UI구조를 잡을 땐 이 화면 단위를 View 또는 Screen이라고 표현합니다.

     

    📌 View와 Screen의 분리

    유지보수 측면에서 적용해 본 방법입니다. 이렇게 해도 되는지, 아니면 틀린 방법인지 모르겠으나 제 프로젝트에 관한 내용을 바탕으로 글을 쓰고 있기 때문에 소개해보도록 하겠습니다.

     

     

    네트워크 상태에 따른 인터페이스(error, loading, data)는 어디로 분류하는 게 좋을까요? 라우팅이 되는 개념은 아니기 때문에 확실히 스크린 단위는 아닌 것 같습니다. 그렇다고 위젯이라고 분류하게 되면 data 상태인 경우엔 스크린 단위와 크기가 같아져 버리게 됩니다.

     

    저는 유지보수 측면에서 네트워크 상태에 따른 인터페이스도 가시적으로 관리하고 싶었기 때문에 이러한 경우는 아래와 같이 view라고 하고, 화면단위는 screen이라고 명칭 했습니다.

     

     

     

    class StoreDetailScreen extends ConsumerStatefulWidget {
      static String get routeName => 'store_detail';
      final String id;
      const StoreDetailScreen({
        required this.id,
        super.key,
      });
    
      @override
      ConsumerState<StoreDetailScreen> createState() => _StoreDetailScreenState();
    }
    
    class _StoreDetailScreenState extends ConsumerState<StoreDetailScreen> {
      @override
      Future<void> didChangeDependencies() async {
    	//생략
      }
    
      @override
      Widget build(BuildContext context) {
        final state = ref.watch(storeDetailControllerProvider);
    
        return state.when(
          loading: () => const StoreDetailLoadingView(),
          data: (data) => StoreDetailView(model: data),
          error: (error) => StoreDetailErrorView(
              errorFun: () => ref
                  .read(storeDetailControllerProvider.notifier)
                  .getStoreDetail(storeId: int.parse(widget.id))),
        );
      }
    }

     

    이방식을 사용하면 스크린 화면을 통해 네트워크 통신상태(+controller에서 필요한 상태)에따라 어떻게 보일 것인지 한눈에 볼 수 있어 직관적이고 가독성이 좋다는 장점이 있습니다.

     

     


     

    DRY (Don't Repeat Yourself): 같은 행동을 반복하지 말 것

     

    제가 생각했을 때 위젯을 효율적으로 관리하는 핵심은 '컴포넌트화'입니다. 컴포넌트화를 통해 중복 코드를 최소화하고, 유지보수성을 높이며, 코드의 일관성과 가독성을 향상시킬 있습니다. 이는 단순히 위젯을 나누는 것뿐 아니라 개발에서 전반적으로 중요한 행위이며 중복 코드를 줄이는 것은 소프트웨어 개발에서의 기본 원칙  하나로, 코드의 품질을 높이고 생산성을 향상하는데 중요한 역할을 합니다.

     

    결론: 컴포넌트화를 열심히 합시다!  👀

     

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

     

    이 글이 꼭 정답은 아닙니다. 잘못된 부분이나 부족한 부분을 알려주시면 학습 후 수정하겠습니다.
Designed by Tistory.