메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

한빛랩스 - 지식에 가능성을 머지하다 / 강의 콘텐츠 무료로 수강하시고 피드백을 남겨주세요. ▶︎

IT/모바일

플러터의 필수개념 이해하기 - 아키텍처와 위젯

한빛미디어

|

2024-11-21

|

by 김성덕

1,116

플러터는 확장할 수 있는 계층화된 시스템으로 설계되어 있습니다. 또한 플러터 애플리케이션은 다른 네이티브 애플리케이션과 동일한 방식으로 패키징됩니다.

 

플러터 아키텍처 구조

 

플러터 아키텍처는 크게 임베더, 엔진, 프레임워크 3가지로 나뉘며 각각의 특징만 알고 있어도 개발하는 데 큰 문제가 없을 것입니다.

 

 

1️⃣ 임베더(Embedder)

 

임베더는 적합한 언어로 각 플랫폼에 맞게 작성된 코드를 말합니다. 예를 들어 안드로이드는 자바 및 C++로 iOS와 macOS는 Objective-C와 Objective-C++, 윈도우와 리눅스는 C++로 작성됩니다. 임베더를 사용하면 플러터 코드를 기존 애플리케이션에 모듈로 통합하거나 애플리케이션의 전체 내용으로 사용할 수 있습니다. 임베더는 화면 렌더링, 접근성, 입력 등의 기능을 제공하며 이벤트루프의 메시지를 관리합니다.

 

2️⃣ 엔진(Engine)

 

플러터의 중심에는 대부분 C++로 작성된 플러터 엔진이 있습니다. 이 엔진은 모든 플러터 애플리케이션을 지원하는 기본 기능을 제공합니다. 엔진은 화면을 새로 그릴 때마다 이미지를 처리하고 보여주는 역할을 합니다. 플러터 엔진은 다음과 같은 기능을 제공합니다.

 

1 그래픽: Skia라는 도구를 사용하여 화면에 그림을 그립니다.

2 텍스트 레이아웃: 글자를 배치하고 보여줍니다.

3 파일 및 네트워크 I/O: 파일을 읽고 쓰거나, 인터넷에서 데이터를 가져옵니다.

4 접근성 지원: 시각장애인 등이 앱을 사용할 수 있도록 돕습니다.

5 플러그인 아키텍처: 다른 기능을 쉽게 추가할 수 있게 합니다.

6 다트 런타임 및 컴파일 도구: 플러터 앱이 실행될 수 있도록 돕습니다.

 

간단히 말해 플러터 엔진은 앱이 화면에 그림을 그리고, 글자를 보여주고, 파일을 읽고 쓰는 등의 모든 기본적인 일을 하도록 도와줍니다.

 

3️⃣ 프레임워크(Framework)

 

플러터 프레임워크는 다트 언어로 작성된 최신 반응형 프레임워크입니다. 이 프레임워크를 통해 플러터와 상호작용을 할 수 있습니다. 

 

플러터 프레임워크는 여러 계층으로 구성된 다양한 플랫폼, 레이아웃, 기본 라이브러리를 제공합니다. 이를 통해 앱 개발을 보다 체계적으로 관리할 수 있습니다. 플러터는 앱을 프레젠테이션 계층, 비즈니스 로직 계층, 데이터 계층으로 나누어 관리합니다.

 

 

프레젠테이션 계층(Presentation Layer)

이 계층은 앱의 사용자 인터페이스를 담당합니다. 즉, 사용자가 앱을 볼 때 보이는 부분입니다. 플러터에서 프레젠테이션 계층은 위젯을 사용하여 만듭니다. 위젯은 앱의 기본 빌딩 블록으로 고정된 모습으로 유지되는 상태 비저장 위젯과, 동적으로 변경될 수 있는 상태 저장 위젯이 있습니다.

 

 

비즈니스 논리 계층(Business Logic Layer)

이 계층은 앱의 로직(논리)과 데이터 처리를 담당합니다. 사용자가 앱과 상호작용할 때 어떤 일이일어날지를 결정합니다. 플러터에서는 다트 클래스를 사용하여 비즈니스 논리 계층을 구현합니다.이 계층은 앱의 상태를 관리하고 사용자 입력을 처리하며 외부 API나 서비스와 상호작용을 합니다.

 

 

데이터 계층(Data Layer)

이 계층은 앱의 데이터를 관리하고 비즈니스 논리 계층에 데이터를 제공합니다. 플러터에서 데이터계층은 JSON 처리를 통해 데이터를 직렬화하거나 SQLite 데이터베이스, REST API 등을 사용하여구현할 수 있습니다.

 

 

플러터는 반응형 프로그래밍 모델을 사용합니다. 이 말은 앱의 상태가 변경되면 위젯 트리를 통해 자동으로 전파되어 UI가 업데이트된다는 것을 의미합니다. 이를 통해 코드가 더 선언적이고 이해하기쉬우며, 앱 개발 속도가 빨라지고 유지 관리가 쉬워집니다.

 

전반적으로 플러터 아키텍처는 확장성과 유연성이 뛰어나도록 설계되어 있기 때문에 개발자는 복잡한 앱을 쉽게 분리하고 관리할 수 있습니다.

 

 

 

✅ 위젯이란?

 

플러터에서 위젯은 앱을 구성하는 가장 기본적인 단위입니다. 버튼, 텍스트 같은 사용자와 상호작용을 하는 요소뿐만 아니라 패딩padding이나 마진margin처럼 눈에 보이지 않는 요소도 모두 위젯입니다.

 

즉, 플러터에서 위젯을 잘 설계하면 공장에서 물건을 찍어내듯 앱을 만들 수 있으며 복잡한 앱부터간단한 앱까지 모두 자유롭게 커스텀할 수 있습니다.

 

 

쉽게 설명하면 플러터는 레고 상자와 같습니다. 레고 상자 속의 개별 레고 블록을 위젯이라고 생각하면 됩니다. 

 

레고 블록 하나만으로는 큰 의미가 없지만 여러 블록을 조합하면 멋진 결과물을 만들 수있듯이, 위젯도 여러 개를 조합하여 훌륭한 앱을 만들 수 있습니다. 이제부터 플러터에서 사용하는다양한 작은 단위의 위젯을 하나씩 살펴보겠습니다.

 

플러터에는 다양한 위젯이 있지만 가장 기본이 되는 3가지 위젯은 다음과 같습니다.

 

1. StatelessWidget

2. StatefulWidget

3. InheriedWidget

 

 

1️⃣ StatelessWidget

 

StatelessWidget은 변하지 않는(immutable) 속성을 가진 위젯입니다. 쉽게 말해, 한 번 클래스를 만들면 내부 값이 변하지 않는다는 뜻입니다. 

일반 클래스는 값이 변할 수 있지만(mutable), StatelessWidget 클래스는 한 번 만든 후에는 변경할 수 없습니다. 이 위젯은 build 함수가 호출될때까지 상태를 유지합니다. 따라서 불변한 클래스의 장점이 StatelessWidget의 장점이라고 볼 수있습니다.

 

StatelessWidget의 장점

 

 

1 스레드 안전성: 한 번 만들어지면 상태를 변경할 수 없어서, 여러 작업이 동시에 일어나도 안전합니다. 따라서 코드이해가 쉬워지고 동시성 문제가 적습니다.

 

2 보안: 상태를 변경할 수 없기 때문에 해킹에 덜 취약합니다. 비밀번호나 개인정보 같은 민감한 데이터를 다루기 좋습니다.

 

3 단순성: 상태 변화가 없기 때문에 코드가 간단하고 유지 보수가 쉽습니다. 버그 발생 가능성도 줄어듭니다.

 

4 예측 가능성: 외부 상태 변화에 영향을 받지 않아 예측 가능한 작동을 합니다. 그래서 테스트와 디버깅이 쉬워집니다.

 

5 캐싱: 상태가 고정되어 있어 안전하게 캐시할 수 있습니다. 새 인스턴스를 계속 만들 필요가 없어서 성능이 좋아집니다. 또한 플러터에서 StatelessWidget은 한 번만 빌드하면 되므로 성능을 최적화할 수 있습니다.

 

 

사용 방법

 

StatelessWidget을 만들려면 원하는 클래스를 정의하고 StatelessWidget을 상속받아 build 함수를 재정의하면 됩니다.

 

class FirstMyWidget extends StatelessWidget {
  const FirstMyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Text('나의 첫 번째 위젯입니다.');
  }
}

 

StatelessWidget은 라이프사이클이 없습니다. 클래스가 생성되면 build 함수가 호출되어 위젯을화면에 표시하고, 그 역할을 다하게 됩니다. 이 위젯은 상태를 가지지 않으며, 만약 위젯을 변경하려면 상위 위젯에서 FirstMyWidget을 다시 생성하는 방법밖에 없습니다.

 

2️⃣ StatefulWidget

 

StatefulWidget은 런타임 중에 모양과 작동을 변경할 수 있는 위젯입니다. 사용자 상호작용이나 데이터 변경과 같이 외부 요인에 따라 위젯의 UI를 변경해야 할 때 사용됩니다. 위젯이 처음 생성되면 createState() 메서드가 호출되어 State 객체의 인스턴스를 생성합니다. 

 

이 State 객체는 위젯과 연결되어 있으며 위젯의 상태를 관리합니다. State 객체의 build() 메서드를 호출하여 위젯의 초기 UI를 만듭니다. 

 

위젯의 상태가 변경되면 setState() 메서드를 호출하여 위젯의 상태를 업데이트합니다. 그러면 State 객체의 build() 메서드가 다시 호출되어, 업데이트된 상태에 맞게 위젯의 UI가 다시 빌드됩니다.

 

 

StatefulWidget의 장점

 

1 동적 대화형 UI: StatefulWidget을 사용하면 동적 대화형 UI를 만들 수 있습니다. 위젯의 상태를 관리함으로써 사용자 상호작용이나 데이터 변경에 따라 실시간으로 UI를 업데이트할 수 있습니다.

 

2 성능: StatefulWidget은 자체적으로 변경되지 않습니다. 대신 State 객체만 변경되기 때문에 플러터는 위젯 트리의 렌더링을 최적화할 수 있습니다. 이로 인해 앱의 성능이 향상되고 다시 렌더링되는 횟수가 줄어듭니다.

 

3 재사용성: StatefulWidget 내에서 상태를 캡슐화하면 여러 곳에서 재사용할 수 있는 구성 요소를 만들 수 있습니다. 이는 화면이나 다른 앱 간에도 가능합니다.

 

4 유연성: StatefulWidget은 애니메이션 처리, 네트워크 요청 또는 복잡한 데이터 처리에도 유용합니다

 

 

사용방법

 

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StatefulWidget 예제'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Counting : $_counter'),
            ElevatedButton(
              onPressed: _incrementCounter,
              child: Text('더하기'),
            ),
          ],
        ),
      ),
    );
  }
}

 

이 예시에서 MyWidget은 카운터와 버튼을 표시하는 StatefulWidget입니다. State 객체는 카운터의 상태를 관리합니다. 

 

사용자가 버튼을 누르면 _incrementCounter() 메서드가 호출되어 _ counter 값을 증가시킵니다. 이때 setState 메서드가 호출되어 위젯의 상태가 업데이트되고, build메서드가 다시 실행되어 UI가 새로 그려집니다. 

 

결과적으로 증가된 _counter 값이 화면에 표시됩니다. 쉽게 말해, 버튼을 누를 때마다 숫자가 올라가고, 그 숫자가 화면에 보이는 것입니다.

 

 

3️⃣ InheritedWidget

 

InheritedWidget은 플러터의 위젯 트리 내에서 데이터를 쉽게 공유할 수 있도록 해주는 클래스입니다. 

 

종종 상위 위젯에서 하위 위젯으로 데이터를 전달해야 하는 상황이 발생합니다. InheritedWidget을 사용하지 않으면 데이터를 전달할 때 중간의 모든 위젯을 거쳐 데이터를 넘겨줘야 할 수 있습니다. 

 

이렇게 하면 불필요한 위젯들까지 다시 빌드되는 문제가 발생할 수 있습니다. InheritedWidget은 이러한 문제를 해결하고 성능을 개선해줍니다.

 

 

다음은 InheritedWidget을 사용하여 위젯 간에 데이터를 공유하는 방법을 나타낸 예시입니다.

 

class MyInheritedWidget extends InheritedWidget {
  final int data;

  MyInheritedWidget({
    Key? key,
    required Widget child,
    required this.data,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return oldWidget.data != data;
  }

  static MyInheritedWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
  }
}

 

이 예시에서는 정숫값을 보유한 MyInheritedWidget이라는 새로운 InheritedWidget을 만들었습니다. 

 

updateShouldNotify 메서드는 데이터가 변경될 때 위젯을 다시 빌드해야 하는지 확인합니다. of() 메서드는 데이터를 보유한 위젯의 인스턴스를 가져오는 데 사용됩니다.

 

 

이제 MyInheritedWidget을 사용하여 데이터에 접근하는 위젯을 만들어보겠습니다

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final myData = MyInheritedWidget.of(context)?.data ?? 0;
    return Text('data : $myData');
  }
}

 

of() 메서드를 호출하여 MyInheritedWidget에 저장된 데이터에 접근하는 MyWidget이라는 새로운 위젯을 만들었습니다. 데이터가 null인 경우 기본값은 0입니다. 이 위젯은 위젯 트리의 어느 곳에나 배치할 수 있으며 항상 MyInheritedWidget에 저장된 데이터에 접근할 수 있습니다.

 

 

✅ 번외: Provider가 탄생한 이유

 

InheritedWidget은 플러터에서 상태를 관리하기 위한 강력한 도구이지만, 몇 가지 제한 사항이 있습니다. 특히 위젯 트리가 크고 복잡할 때 데이터를 수동으로 위젯 트리 아래로 전달하는 것이 번거로울 수 있습니다. 

 

개발자들은 더 효율적이고 쉬우며 유지 보수가 용이한 방법을 선호합니다. 이런이유로 플러터는 InheritedWidget을 기반으로 몇 가지 개선 사항을 추가하여 Provider라는 상태관리 라이브러리를 출시했습니다. 

 

Provider는 사용하기 편리하고 오류 발생 가능성을 줄여주기 때문에 상태 관리를 하는 데 유용하게 활용할 수 있습니다.

 


 

다트 기초 문법 + 플러터 기본 개념 + 클론 코딩

 

『핸즈온 플러터』는 구글이 개발한 크로스 플랫폼 프레임워크인 플러터의 기본 개념부터 실무 환경에서의 구현까지 단계별로 다루는 종합 가이드입니다. 플러터는 다양한 위젯을 조합하여 애플리케이션을 만들 수 있게 도와주는 강력한 도구입니다. 플러터를 활용하면 하나의 코드베이스로 iOS와 안드로이드 애플리케이션을 개발할 수 있어 시간과 비용을 절약할 수 있습니다.

 

이 책은 플러터의 기본 개념과 실무에서 활용할 수 있는 다양한 기술을 배울 수 있도록 도와줍니다. 특히 클론 코딩 프로젝트를 통해 플러터의 여러 기능을 익힐 수 있으며, 복잡한 애플리케이션을 효율적으로 개발하는 팁과 사용자에게 최고의 경험을 제공하는 데 필요한 모든 것을 배울 수 있습니다. 『핸즈온 플러터』로 크로스 플랫폼 개발의 효율성을 직접 느끼면서, 다양한 도구와 라이브러리를 사용하여 매력적인 애플리케이션을 만들어보세요.

TAG :
댓글 입력
자료실

최근 본 상품0