App_Dev/Flutter

[ 앱 개발자 도전기 : 크로스플랫폼_Flutter & Dart ] Flutter 개괄 - [1]

안다미로 : Web3 & D.S 2024. 11. 13. 17:09

 

 

 

[ 앱 개발자 도전기 : 크로스플랫폼_Flutter & Dart ] 

Flutter 개괄 - [1]

 

 


∇ Flutter 개괄.

목 차

1. 플러터란?
2. 플러터에서 다트를 사용하는 이유는 무엇일까
3. Dart란?
4. 모바일 개발의 종류.
5. 플러터의 장점.
6. 플러터의 동작 원리.
7. 플러터 렌더링 : 내부 동작 원리.

 

 


Ⅰ. 플러터란 ?


 

      - 구글에서 만들어 오픈소스로 공개한 모바일 SDK.

 

      - 앱을 만들기 위한 기존의 방식은 안드로이드와 IOS를 별도로 구현해 배포해야하기에 요구되는 기술스펙과 비용 高.

            == > " FLutter "를 사용하면 한번의 구현으로 양쪽 진영에 모두 배포가 가능하다는 장점.

 

      - 플러터는  '렌더링엔진' / ' UI컴포넌트' /  '테스트 프레임워크 ' /  '도구' / '라우터'  등

         앱 제작에 필요한 기능을 모두 제공합니다.

         [ 개발자는 앱 구현에 집중하여서,  생산성 높일 수 있음 ]

 

 

     


 

Ⅱ. 플러터에서 다트를 사용하는 이유는 무엇일까


 

      √  '스프링(부트) 환경' 이나 '안드로이드 환경' 에서는  "자바" 혹은 "코틀린"을 사용하고,

              IOS 환경에서는 Object-c 혹은 Swift 를 사용합니다.

 

     ☆ Flutter에서는  '다트(Dart)'를 사용해 구현합니다. (구글 소유의 언어)

 

                √  '다트'는 JIT (Just - In - Time) 컴파일과  AOT( Ahead - Of - Time ) 컴파일을 모두 지원합니다.

 

                        ∇ " AOT 컴파일러 " 는 다트 코드를  상응하는 '네이티브 코드'로 바꿔줌으로써

                               플러터로 된 프로덕트가 모든 사용자에게 빠르게 동작할 수 있기 때문에

                               플러터 전체 프레임워크의 대부분을 다트로 구현합니다.

   

                        ∇ 다트의 선택형   " JIT 컴파일러 " 는 "핫 리로드(hot-relaod) "  지원하기 때문에

                              개발속도와 반복(iteration)을 가능하게 해 생산성을 높여줍니다.

 

                √  '다트'는 객체지향이기에 마크업 언어를 사용하지 않고, 다트 언어만으로 UI/UX를 쉽게 구현합니다.

 

                √  기존 언어 ( Java, JavaScript 등 ) 과 유사해, 생산성이 좋고 예측이 가능합니다.

                          - 모든 언어들이 그렇듯, 타 언어를 한번 깊게 먹어보면,  다트도 쉽게 접근 가능합니다.

 

 


 

Ⅲ. 다트(Dart)란?


    ※ Dart는 베이스가 다른 개발 언어와 유사하기에, 

        ' 자바스크립트' , '자바' 혹은 'C'  등등을 사용한 경험이 있다면 다트 문법에서 비슷한 부분을 발견 가능합니다.

 

         √ '다트'의 최초 개발 목적은 '웹 용 개발' 언어였습니다.

               - 그렇기에, JS를 대체하려고 했다가 방향을 전환해 컴파일러를 구현하기로 결정.

                   == 다트의 모든 기능은 JS로 표현이 가능하다는 의미.

 

         √ '다트'는 자바와 비슷하지만, 코드가 더 간결합니다.

                ex) 다트에서는 논리값으로  true/false만 가집니다.

                          - JS나 Java 에서 if (1) {} 문법을 사용한다면,  true로 판단하여 블록 내 코드를 수행하지만,

                               "Dart" 에서는 실행을 허용하지 않습니다.

 

         √ '다트'는 모듈을 지원하지 않고 오직 '객체지향'을 기반으로 합니다.

 


 

Ⅳ. 모바일 개발의 종류


 

     ◎ 네이티브 개발 [ IOS , AOS(Android) ]

             - 네이티브 앱을 사용하는 방법으로, 앱을 가장 완벽하게 제어하며 디버깅 도구, 성능을 100% 활용 가능합니다.

                  but, 플랫폼별로 앱을 두번 구현해야 하기 때문에  자원리소스(인력 &자원)가 두배로 소모됩니다.

             

 

 

     ◎ 자바스크립트 기반 크로스 플랫폼

            - 웹뷰(Web VIew), 리엑트-네이티브(RN)와 같은 JS 기반의 크로스 플랫폼을 사용하는 방법.

            - JS에 대한 이해가 있다면, 웹 개발자도 앱 개발에 참여 가능하다는 장점.

 

            -  웹뷰(WebView)는 웹킷(WebKit)(브라우저 렌더링 엔진)으로 구동되는데,

               웹페이지를 그대로 보여줍니다,  이 경우 DOM을 처리하는 일에 대한 비용이 과도하기에 성능이 좋지 않습니다.

 

            - 단점 : 자바스크립트  다리(JavaScript bridge)

                          *JavaScript bridge : 안드로이드와 WebView의 통신 작업을 위해 만들어진 JS용 Interface

                                - Web에서 발생하는 이벤트에서 안드로이드 동작(메서드)을 직접적으로 통제할 수 없음.

                                   --> Bridge라는 통로를 통해 웹에서 안드로이드 동작을 호출.

                                == Bridge는 WebView에 적용될 인터페이스 구현체이며,

                                     WebView는 객체의 메서드들을 인스턴스를 통해 호출 가능.

 

                      - bridge를 쓰면, DOM을 직접 사용하지 않기 때문에 속도가 빨라지게 된다는 점이 좋음

                    다만! 앱이 렌더링 엔진과 소통할 때마다  JS코드를 네이티브 코드로 컴파일해줘야 한다는 점이 단점입니다.

 

 

bridge에서 병목현상이 발생.

 

 


 

     ◎ 플러터.

           : "플러터"는 빌드를 할 때   "ARM코드"로 컴파일합니다. 

 

                    *ARM코드

                       - 임베디드 기기에 주로 사용되는 RISC 프로세서.

                       - 모바일 기기 또는 IOT 디바이스에도 사용.  

 

               그리고 자체 렌더링 엔진을 탑재하고 있기 때문에 앱이 네이티브로 구동되며

               JavaScript Bridge와 같은 인터페이스도 필요 없습니다.

플러터의 렌더링

                 ++ 다양한 크로스 플랫폼의 비용문제들을 플러터는 자체 렌더링 엔진을 통해 해결 !

 

 


 

Ⅴ. 플러터의 장점.


 

     ☆ JavaScript bridge가 없음.

 

            - 웹뷰로 앱을 개발할 때, JavaScript bridge를 사용하는데, 이에 필요한 비용이 상당히 높기에 

                   앱의 최적화 및 디버깅이 힘듦니다.

 

           반면에 ! ,   ' 플러터 ' 는 자체 렌더링 엔진을 이용합니다.

                   - > 실제 네이티브 코드로 컴파일하여, 크롬이 사용하는 렌더링 엔진(Skia)을 사용하기에,

                             실행시 다트를 변환하지 않습니다. 그렇기에 앱의 최적화가 더 유리합니다.

 

 

     ☆ 컴파일 시간.

             - 플러터의 전체 컴파일 시간은 30초에서 최대 1분을 넘기지 않으며   '핫 리로드 ' 를 제공해서

                   개발 생산성을 보장합니다.

 

 

     ☆ 크로스 플랫폼.

             - IOS와 안드로이드를  플러터-다트를 사용해서 한 번만 구현하면 동시에 배포가 가능합니다.

                  + 플러터는 테스팅 위젯 라이브러리도 제공합니다.

 

 

     ☆ 코드 공유.

            - 플러터 기술은 JS로도 가능하기 때문에(네이티브 언어 개발로는 불가능) 

                 웹과 모바일 앱은 클라이언트 뷰를 제외하고, 모든 코드 공유가 가능합니다.

 

           - DI를 사용하면 앵귤러 다트앱과 플러터 앱에 같은 모델과 컨트롤러를 사용할수도 있습니다.

 

           - IOS와 안드로이드는 필연적으로 모든 코드를 공유합니다.

                == 경우에 따라, 각각 네이티브 언어로 커스터마이징을 해야하는 경우도 있습니다.

 

 


 

Ⅵ. 플러터의 동작 원리.


 

     ※ 넓게 보면,  플러터는 웹의 리액트와 같이  " 리엑티브 선언형 조합"을 할 수 있는 뷰 계층 라이브러리입니다.

          == ( 플러터는 자체 렌더링 엔진도 포함하기 때문에, 실제로는 리엑트 + 브라우저와 비슷 )

 

◇ Widget.

   

        ∇ 즉, "Widget(위젯)" 이라는 작은 컴포넌트를 조합해, 모바일 UI를 그립니다.

                  - 플러터의 모든 것은 위젯으로 구성되며, 위젯은 View를 묘사하는 다트 클래스입니다.

                  - 구조 / 스타일 애니메이션 그리고 그 밖에 모든 UI를 구성하는 모든 것이 위젯입니다.

 

다른 객체가 없다는 것은 아니고, 앱의 모든 세부 조각이 위젯이라는 의미.

          

이미지, 버튼 모두 위젯.

 

 

            - 위젯은 상태를 가집니다.

           

             -   위의 코드에서 "BETTER SHOES" 위젯은 'qty"라는 상태를 가지며, 

  위젯의 플러스(+) 혹은 마이너스(-) 버튼을 누르면, 내부 상태가 바귀며 이 상태에 의존하는 모든 위젯을 갱신합니다.

 

 

           ★ " Widget "  &   " 상태갱신 "  두 가지는 플러터에서 신경써야하는 핵심 개념입니다.

                        

           

 


◇ 결국 모든 것이 위젯의 은혜이겠지요..

  

       위에서,  플러터는 모든 것이 위젯이라고 했습니다.

          이 위젯은 앱 뷰의 모든 정보를 정의하는데, Row와 같은 위젯은 레이아웃 정보를 정의하고,

              Button, TextFiled와 같은 위젯더 구체적이며 구조적인 요소를 정의합니다.

          심지어 앱의 루트도 위젯입니다.

 

  

           ○ 자주 쓰이는 위젯들.

                 √ 레이아웃 : Row, Column, Scaffold, Stack                  √ 구조: Button, Toast, MenuDrawer

 

                √ 애니메이션: FadelnPhoto, Transform                         √ 스타일: TextStyle, Color

 

                 √ 위치와 정렬: Center, Padding

 

 


◇ Widget으로 UI 구성하기.

        - 플러터는 "상속(inheritance) " 보다는 " 조합(Composition) " 을 우선시하며, 이를 활용하여 고유한 위젯을 만듭니다.

              ==> 대부분의 위젯은 작은 위젯들을 합쳐서 만들게 됩니다.

                         == 다른 위젯을 상속 받아서, 커스텀 위젯을 만들지 않는다는 의미.

 

 

//class AddToCartButton extends Button{} // 잘못 된 코드

class AddCartButton extends StatelessWidget{
	//...클래스 멤버
	// Button을 다른 위젯으로 감싸서, 즉 위젯을 조합해 커스텀 위젯을 만든다.
	@override
	build() {
		return Center( // AddToCartButton을 중앙으로 정렬하는 위젯
			child: Button( // 텍스트를 전달하는 새 커스텀 컴포넌트를 만든다.
				child: Text('Add to Cart'),
			)
		);
	}
}

              - 작고 재사용이 용이한 컴포넌트를 조합하는 방식은 React와 유사합니다.

 

              ◎ 위젯은 다양한 생명주기(Life-cycle) 메서드와 객체 멤버를 포함합니다.

 

              ◎ 가장 중요한 메서드는  build()인데 모든 플러터 위젯은 build() 메서드를 반드시 정의해야 합니다.

                       == 반환하는 위젯을 통해 뷰를 실질적으로 묘사합니다.

 

 


 

                     

 

◇ Widget을 구성하는 형식.

       : 대부분의 위젯은 

          " 상태가 있는 위젯(Statefulwidget) "   혹은  "상태가 없는 위젯(StatelessWidget) " 입니다.


 

   ∇ 상태가 없는 위젯 ( StatelessWidget ) 

           -  특정한 정보를 저장하지 않기 때문에, 사라지더라도 영향이 없으며 언제 파괴되어도 괜찮은 위젯.

 

                   - > '생명주기' 가 외부에 의해 결정되며,

                         그렇기 때문에, 언제 위젯을 트리에서 제거 혹은 리빌드 해야할지를 프레임워크에 알리지 않으며,

                         리빌드하는 시점을 프레임워크에서 알려줍니다.

class AddCartButton extends StatelessWidget{
	@override
	build() {
		return Center( 
			child: Button(
				child: Text('Add to Cart'),
			)
		);
	}
}

          - 'AddCartButton' 은 상태가 없는 위젯입니다.

               - > 상태를 따로 관리하거나 트리의 어떤 부분도 알 필요가 없습니다.

               - > 그저 사용자가 해당 버튼을 눌렀을 때, 지정된 함수를 실행시키는게 이 위젯의 임무.!

 

              + 이 버튼의 텍스트를 "Add to Cart"에서 "Remove from Cart"로 다른 위젯에서 정보를 전달하면

                    "AddCartBurtton"은 다시 그려지게됩니다.

 

                    == 즉 !  " 상태가 없는 위젯(StatelessWidget) " 은 새로운 정보에 "반응(react)"  합니다.

 


   ∇ 상태가 있는 위젯 ( StatefulWidget ) 

           -  항상 State 객체를 가집니다.

                  - > State 객체는 setState라는 메소드를 제공하는데,

                               이는 '위젯'을 다시 글야 함을 플러터 엔진에 알립니다.

ex : 버튼, 텍스트필드, 레이아웃 위젯을 조합한 QuantityCounter

               - 위의 그림은 수량을 나타내는 QuantityCount(StatefulWidget)으로 

                     여러개의 내장 위젯들을 조합해서 만든 커스텀 위젯입니다.

Widget build(BuildContext context){
  return Container( // build 메서드는 항상 위젯을 반환한다.
    child: Row(
      children: List<Widget> [
        IconButton(
          icon: Icons.subtract,
          onPressed: (){
            setState((){ // 사용자의 동작을 감지하는 버튼 위젯의 내장 프로퍼티
              this.quantity--; // 상태에 저장된 수량을 감소시킨다
            });
          }
        ),
        new Text("Qty:" ${this.quantity}), //상태 객체의 수량이 바뀌면 트리의 위젯을 다시 그린다.
        new IconButton(
          icon: Icons.add,
          onPressed: (){ // 이 콜백이 상태의 수량을 증가시키는 setState를 호출한다
            setState((){
              this.quantity++;
            });
          }
        ),
      ],
    )
  );
}

     - 이 위젯은 State 객체 클래스에서 상속받은 메서드를 사용하는데,  이중 setstate 메서드가 핵심.

 

         [+], [-] 버튼을 누르면 앱은 setState 메서드를 호출하는데, 

                 이 메서드는 위젯의 상태를 갱신시키며,  이 상태에 영향받는 모든 위젯을 다시 그리도록 플러터에 지시합니다.

 

 

 ★ 이렇게 위젯을 빌드하고, 갱신하는 과정을 "생명주기(Life-Cycle)"이라고 합니다.

 

        ☆ 상태가 있는 위젯(StatefulWidget)의 생명주기(LifeCycle)

 

 

 

 


Ⅶ. 플러터 렌더링 : 내부 동작 원리.


 

     ♣ 위젯 트리.

ex) 장바구니 위젯 트리

 

               - 플러터는 커다란 규모의 위젯 트리를 매우 빠르게 빌드합니다.

            

               - 위의 위젯트리 중 cartItem 위젯을 보면 이 위젯은 상태를 가지고 있어서, 

                          위젯의 자식은 위젯 상태에 의존합니다.

 

               - cartItem의 상태가 변경되면  - >  이 위젯을 포함한 모든 하위 위젯들이 다시 그려지게 됩니다.

               - 플러터 위젯은 반응형이기에 외부나 setState를 통해서  새로운 정보를 얻게 되면

                   이에 반응해서 필요한 경우 플러터가 위젯을 다시 그립니다.

 

 


                 ∞ 렌더링 과정.

렌더링 구조.

 

                   - 애니메이션 티거(Animation ticker)가 동작하면서 그리기 작업을 시작합니다.

                         

                       ex) 리스트를 스크롤하거나, 위젯을 다시 그려야 하는 상황에서 화면의처음 요소 위치부터 최종 위치까지

                              조금식 이동하며 애니메이션이 부드럽게 일어납니다.

 

                                  - > 요소들이 움직여야 하는 시간을, 애니메이션 티커가 제어해줍니다.

 


         □ 위젯 트리와 레이아웃 조립 과정.

 

                 ● 위젯 : 화면엔 나타낼 요소를 결정하는 데이터와 설정.

                                  - 트리에서 버튼을 빌드할 때 실제로 특정 색깔 사각형과 그 안의 텍스트를 빌드하는 것이 아닌

                                        화면에 나타낼 요소의 설정을 처리할 뿐입니다.

 

                        ● 위젯 트리가 완성되면, 플러터는 레이아웃을 처리합니다.

                        ● 플러터는 필요할 때, 트리를 한 차례 탐색합니다 ( 선형 시간 소요 )

                        ● 트리를 탐색하며, 위젯의 위치정보를 수집합니다.

                        ● 레이아웃과 크기 제약(constraint)은 부모 -> 자식 위젯 순으로 작성됩니다.

                                 == 트리를 거슬러 올라가면, 모든 위젯은 자신의 제약을 알고있는 상태이기에 

                                           실제 크기와 위치를 부모 위젯에 알립니다.

                                         - > 위젯은 서로의 관계를 정리하며 최종 레이아웃을 결정합니다.

 

 

 


         □  조립 과정.

 

                - 위젯은 레이아웃 결정 및 위젯간 충돌여부를 확인했고, 이제 위젯을 화면에 그리지만

                       래스터라이징하거나, 물리적으로 화면에 픽셀을 칠하는 것이 아닙니다.

 

                         - 조립과정을 진행하며, 플러터는 위젯에 실제 화면상의 좌표를 제공하며,

                                위젯은 자신이 차지할 실제 픽셀의 수를 알게 됩니다.

                                [ 의도적을 분리해서 진행  -- >> 덕분에 조립된 위젯을 재사용 가능 ! ]

 

 

 


         □  화면에 그리기.

 

                  - 엔진은 전체 트리를 그릴 수 있는 뷰로 모은 다음,  운영체제를 통해서 화면에 그리도록 요청합니다.

 

                       -> 이를 "레스터라이징"이라고 부르며,   이 과정을 끝으로 위젯이 화면에 그려지게 됩니다.

 

 

 


 

☆ 기억할 포인트 4가지.

            - 플러터는 리액티브다

            - 모든 것은 위젯이다.

            - State 객체는 오래 살아남으며,  종종 재사용됩니다.

            - 위젯의 제약은 부모가 서술합니다.