logo
Published on

React 개발자가 찍어먹어본 Flutter, Dart 솔직 후기

Authors
  • avatar
    Name
    shleecloud
    Twitter

들어가며

먼저 이 글은 깊게 Flutter에 대해 깊게 파고들지 않은 상태에서 쓰는 글임을 밝히고 싶다. 평소 Flutter에 대해 관심이 있던 상태에서 Javascript, React와 얼마나 다를지 호기심이 생기게 됐다. 그렇게 개발자 경험이 좋다고 하는데 정말 그런걸까. 호기심 반 진심 반으로 입문해봤다.

완전히 새로운 언어나 프레임워크를 배울 때는 강의가 효율적이다. 내가 참고한 강의는 Nomad Coder에서 무료로 제공하는 Dart 시작하기 강의와 Flutter로 웹툰 앱 만들기강의였다. 내가 만든 Repository 링크는 이쪽이다.


Dart

Flutter의 가장 큰 장벽은 Dart 언어였다. Flutter만을 위해 새로운 언어를 배울 가치가 있을까? 적어도 Javascript 언어에 익숙하다면 그럴 가치가 있다. 배우기 쉽기 때문에.

Typescript와 유사한 구조

코드 구조가 굉장히 유사하다. Javascript에 익숙하다면 읽는데 문제가 없는 정도로 유사하다. 다른 점이라면 변수 타입을 Kotlin 처럼 선언하는 정도다. 러닝 커브가 높지 않아 가볍게 배워보고 경험해도 괜찮다고 느꼈다.

class Webtoon extends StatelessWidget {
  final String title, thumb, id; // String 타입의 변수 3개를 선언,
                                 // final은 변수를 한 번 할당하면 재할당 할 수 없다

  const Webtoon({ // class 생성자 함수
    super.key,
    required this.title,
    required this.thumb,
    required this.id,
  });

현대적인 언어

Dart는 Javascript에 비교해서 더 다양한 기능을 가지고 있다. 아래 코드를 첨부했다.
기능이 풍부하면서 동시에 명시적이다. 비교적 최근 만들어진 언어라 꼼꼼한 설계와 고민 끝에 만든 언어라고 느껴진다.

var oldFriends = ['Alice', 'Bob', 'Charlie'];
var newFriends = [
  'David',
  'Edward',
  'Frank',
  for (var friend in oldFriends) '💖 $friend'
];
print(newFriends); // [David, Edward, Frank, 💖 Alice, 💖 Bob, 💖 Charlie]

Flutter

레고를 조립하는 듯한 UI 개발

어릴 때 나는 레고를 좋아했다. 플러터를 표현하는 문구 중에서 레고를 많이 들더라. 체험해본 나도 이건 레고에 가깝다는 생각에 동의한다.

Flutter는 모든 것이 위젯 단위다. 앱 상단의 Bar가 위젯이고 Body에 들어가는 Padding도 위젯이고 Column, Row도 위젯이고 Text도 위젯이다. 위젯은 HTML로 비유하면 엘리먼트 또는 컴포넌트로 매칭한다. 금새 익숙해질 수 있다. 자주 사용하는 저수준 위젯은 CSS에 있는 용어와 기능을 대부분 공유하고 있기 때문이다.

속성은 정확히 객체를 통해 지정해야 한다. 예를 들어 CSS 사용할 때 처럼 문자열을 입력하면 안된다. Flutter는 반드시 MainAxisAlignment 객체가 가진 키 값을 입력해야 한다. 평소에 Typescript Enum 기능을 자주 사용해서 이런 양식을 보자마자 감탄이 나왔다. 이로 인하여 VSCode에서 해당 속성에 대한 가이드라인을 볼 수 있었다.

justify-content: space-between // CSS
mainAxisAlignment: MainAxisAlignment.spaceBetween // Flutter

특정 엘리먼트에 스타일도 FLutter에서 지정한 객체 안에 보내야한다. 소괄호를 객체처럼 쓰는 것은 Dart의 Named Parameter 양식이다. 파라미터에 이름을 붙여서 가독성과 IDE 호환을 높인다. Javascript의 객체를 키로 전달하는 것과 동일하다.

Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Text( // Named Parameter
      '이 자리에는 보여질 문자열이 들어갑니다.',
      style: const TextStyle( // TextStyle 함수가 제공하는 Named Parameter 스타일만 사용할 수 있습니다.
        color: Colors.white, // 색깔도 Colors 또는 Color를 참조해야합니다.
        fontSize: 16,
      ),
    ),
    const Icon(
      Icons.chevron_right_rounded,
      color: Colors.white,
    ),
  ],
),

React와 똑닮은 상태를 다루는 구조

공식 문서에서 대놓고 React에서 영감을 받았다고 밝히고 있다. 그 정도로 유사하다.

  • Stateful Widget 에서 변수를 할당하면 그 자체로 상태 변수가 된다.
  • 상태 변수를 JS처럼 재할당한다.
  • setState 함수를 호출(또는 setState 파라미터로 들어간 함수에 포함)하면 화면이 Re Rendering 된다.

Flutter 위젯은 React에서 영감을 얻은 최신 프레임워크를 사용하여 구축되었습니다. 핵심 아이디어는 위젯으로 UI를 구축한다는 것입니다. 위젯은 현재 구성 및 상태를 고려하여 뷰가 어떤 모습이어야 하는지 표현합니다. 위젯의 상태가 변경되면 위젯은 해당 표현(HTML, CSS) 을 다시 작성합니다. 프레임워크는 현재 상태에서 다음 상태로 전환하기 위해 기본 렌더 트리에 필요한 최소한의 변경 사항을 결정하기 위해 이전과 비교합니다. (링크)

class DetailScreen extends StatefulWidget {
  final String title, thumb, id;

  const DetailScreen({
    super.key,
    required this.title,
    required this.thumb,
    required this.id,
  });

  
  State<DetailScreen> createState() => _DetailScreenState(); // 위젯에 상태를 부여한다.
}

class _DetailScreenState extends State<DetailScreen> {
  bool isLiked = false; //
  ...

  if (likedToons.contains(widget.id)) {
    setState(() {
      isLiked = true;
    });
  }
  ...
}

FutureBuilder 상태까진 필요 없지만 비동기는 필요해

상태가 필요하지 않는 위젯은 Stateless Widget 으로 만들 수 있다. 하지만 대부분의 화면은 비동기로 받아온 데이터가 필요하고 비동기 데이터를 보여주려면 상태가 필요하다. React에서도 오직 비동기 데이터를 갱신하고 UI를 그리기 위해 상태를 만든다.

FutureBuilder 위젯은 Promise를 파라미터로 받는다. 두 번째 파라미터는 콜백 함수가 들어가는데, Promise 상태가 변경되면 개발자가 선택한 UI를 그려준다.

강의에서 Stateful Widget을 많이 쓰지 않을 것이라고 강조했다. 이런 위젯이 있다면 불필요한 상태를 만들지 않아도 된다.

class HomeScreen extends StatelessWidget {
  HomeScreen({super.key});

  // Promise === Future
  // webtoons 변수는 api를 호출해서 비동기 이후에 WebtoonModel 타입의 배열을 받게 될 것이다.
  final Future<List<WebtoonModel>> webtoons = ApiService.getTodaysToons();

  
  Widget build(BuildContext context) {
    return ...

      // webtoons 변수가 fulfilled 변경된다면 builder 함수에 인자값으로 전달하여 UI를 그릴 것이다.
      body: FutureBuilder(
        future: webtoons,
        builder: (context, snapshot) {
          if (snapshot.hasData) { // webtoons 변수가 fulfilled 상태라면
            return Column(
              children: [
                const SizedBox(height: 50),
                // makeList는 아래에 선언한 함수며 배열로 리스트 위젯(컴포넌트)를 만들어준다.
                Expanded(child: makeList(snapshot)),
              ],
            );
          }
          return const Center( // pending 상태라면 로딩 인디케이터를 표기한다.
            child: CircularProgressIndicator(),
          );
        },
      ),
    );
  }

  ListView makeList(AsyncSnapshot<List<WebtoonModel>> snapshot) {
    return ListView.separated(
      scrollDirection: Axis.horizontal,
      itemCount: snapshot.data!.length,
      ...

개발 경험

Widget Inspector 이런 개발자 도구 처음이야

이 스크린샷을 직접 봐야한다. 앱 화면에서 글자가 화면 밖으로 삐져나오자 경고를 보여준다. 심지어 얼마나 빠져나왔는지도 알려준다. 감동적이다.

vscode에 있는 Widget Inspector 에서 width is unconstrained 라고 나온다. 웹 브라우저에선 UI가 잘못되어도 그 모습이 의도된 것이라고 생각한다. 물론 Flutter라고 만능은 아니겠고 기기 화면이 정해졌기 때문에 가능한 것이겠지만 그래도 이런 배려는 감동적이다.

gordon-ramsay

VSCode 노란 전구

  • Wrap with widget 메뉴는 위젯을 감싸는 위젯을 만든다. HTML에서 div 감싸는 작업을 단축해준다.
  • Extract Widget 메뉴는 컴포넌트처럼 위젯을 밖으로 빼낼 수 있다.

노란 전구로 이렇게 다양한 일을 할 수 있다. 특히 Extract Widget 메뉴는 코드 재사용성을 늘려주는 컴포넌트화를 버튼 하나로 단축해준다. 너무 편해서 부디 React에서도 도입됐으면 좋겠다.

gordon-ramsay

마치며

이 글은 React Native와 비교하는 내용이 아니다. Flutter를 체험하면서 느꼈던 체험기에 가깝다. 열심히 썼는데도 글에 다 담지 못한 부분이 많다. 사용할 때 느꼈던 감정 같은. 플러터를 써야되나 말아야되나는 차치하고 개발의 미래를 엿보고 온 기분이다. 스마트폰을 쓰기 전에 스마트폰 이후를 상상하기 어려웠던 감각과 닮았다. 한 번은 경험하는 것을 강력하게 추천한다. 앱 개발의 미래를.