Flutter 앱에서 작업을 수행 할 서비스 만들기

GetIt 패키지 사용 가이드

직원이 무거운 작업을 수행하도록하십시오.

이 기사를 다시 읽고 있다면 하단 요약 섹션으로 스크롤 하십시오.

이전 기사 에서 FilledStacks 의 provider_architecture 패키지를 사용하여 Flutter 앱을 쉽게 설계하는 방법에 대해 이야기 했습니다. 주요 아이디어는 MVVM 패턴 을 사용 하여 UI 레이아웃에서 로직을 뷰 모델에 넣어 제거하는 것이 었습니다. 이것은 훨씬 더 깨끗하고 유지 관리하기 쉬운 앱을 만듭니다.

보기는 단일 페이지에 대한 위젯 트리입니다.

하지만 뷰 모델이 모든 작업을 수행하도록하는 것은 실수입니다. 그들의 주요 임무는 데이터를보기에 표시 할 준비를하는 것입니다. 무거운 작업은 서비스에 전달되어야합니다. 이는 유지 관리가 가능한 앱의 이점 인 우려 사항을 더욱 분리하는 것 입니다.

지난 기사에서 서비스에 대해 언급했지만 어떻게 만드는지에 대해서는 많이 언급하지 않았습니다. 이 기사에서는 서비스를 좀 더 자세히 다루고 Flutter 앱에서 서비스를 사용하는 방법을 보여 드리겠습니다. 이 항목은 MVVM과 독립적이며 다른 아키텍처 스타일을 사용하는 경우에도 서비스 패턴을 사용할 수 있습니다.

서비스 란 무엇입니까?

서비스는 일반적인 Dart 클래스입니다. Android라는 단어 , 즉 장기간 실행되는 백그라운드 작업으로 생각하지 마십시오 . 앱에서 특정 작업을 수행하기 위해 작성하는 클래스 일뿐입니다. 당신은 그들에게 서비스를 부를 필요조차 없습니다. FilledStacks가 그들을 부르기 때문에 나는 그들을 부르고 있습니다. 그들을 헬퍼 또는 리포지토리 라고 부를 수 있지만 서비스는 좋은 이름이며 마이크로 서비스 의 아이디어와 일치합니다 . 그러나 네트워크 전체에 분산되는 대신 앱 내에 포함됩니다.

다음은 처리 할 서비스를 작성할 수있는 몇 가지 일반적인 예입니다.

  • 로컬 저장소 (데이터베이스, 공유 기본 설정, 파일) 읽기 및 쓰기
  • 웹 API에 액세스
  • 사용자 로그인
  • 무거운 계산 수행
  • Firebase 또는 기타 타사 패키지 래핑

서비스의 목적은 일부 작업을 분리하고 나머지 앱에서 구현 세부 정보를 숨기는 것입니다.

이것이 왜 중요한지 생각해보십시오.

일부 사용자 데이터를 저장하기 위해 공유 환경 설정 을 사용하고 있다고 가정 해 보겠습니다 . 앱의 여러 위치에서 해당 데이터를 저장하고 검색해야합니다.

여러 클래스에서 공유 기본 설정 호출

많은 양의 데이터를 저장해야하므로 공유 기본 설정 대신 SQL 데이터베이스 를 사용하기로 결정했습니다 . 공유 기본 설정에 대한 모든 참조를 데이터베이스 코드로 대체합니다. 고통. 그러나 당신은 한 곳을 잊어 버립니다. 곤충.

나중에 해당 데이터베이스 Pub 패키지의 관리자는 유지 관리를 중지합니다. 어쨌든 더 인기있는 데이터베이스 패키지가 있으므로 패키지를 전환하고 코드의 모든 참조를 업데이트하십시오. 고통. 그리고 한 곳에서 매개 변수를 엉망으로 만듭니다. 곤충.

앱이 점점 인기를 얻고 있지만 사용자가 여러 기기에 동기화를 요청하기 시작했습니다. 따라서 데이터를 로컬 저장소에 저장하는 대신 서버에 저장해야한다고 결정합니다. 당신이 변경합니다. 앱 전체. 고통. 당신은 그 고통을 원하지 않으므로 새로운 사람에게 맡기십시오. 곤충.

앱의 인기는 높아지지만 서버의 부하가 성능에 영향을 미치기 시작합니다. 클라우드 기반 솔루션으로 변경하고 싶지만 백엔드는 프런트 엔드와 동일한 아키텍처 스타일로 구축되었습니다. 변경 비용은 너무 고통스럽고 버그가 너무 많습니다. 그래서 당신의 회사는 성장하고 죽습니다. 끝.

이것은 인위적인 예 였지만 요점은 코드 주변에 흩어져있는 일부 함수에 긴밀하게 결합되어 있으면 변경하기 어렵고 오류가 발생하기 쉽다는 것입니다.

서비스가 들어오는 곳이 여기에 있습니다. 새로운 클래스를 만들고 StorageService. 앱의 나머지 클래스는 내부적으로 어떻게 작동하는지 모릅니다. 서비스에서 메서드를 호출하여 데이터를 저장하고 검색합니다.

이 서비스는 코드에 대한 블랙 박스입니다.

이렇게하면 쉽게 변경할 수 있습니다. 공유 기본 설정에서 데이터베이스로 전환 하시겠습니까? 문제 없어요. 서비스 클래스 내부의 코드를 변경하기 만하면됩니다. 타사 데이터베이스 패키지를 전환하거나 웹 API로 전환하는 경우에도 동일합니다. 서비스 코드를 업데이트하면 앱 내에서 서비스가 사용되는 모든 곳에 자동으로 영향을줍니다.

그것보다 나아집니다. 앱에서 사용할 수 있도록하려는 메서드를 사용하여 서비스를 추상 클래스로 선언 할 수 있습니다. 그런 다음 구체적인 구현으로 추상 서비스 클래스를 확장하십시오.

쉽게 전환 할 수있는 구현

이를 통해 구현을 쉽게 바꿀 수 있습니다. 하드 코딩 된 데이터를 반환하는 "가짜"구현을 만들 수도 있습니다. 이렇게하면 앱의 나머지 부분에 집중하고 나중에 서비스를 코딩 할 수 있습니다. 앱을 개발하는 팀이 있다면 작업을 나누는 방법이기도합니다.

구현 자체는 여러 다른 서비스에 의존 할 수 있습니다. 예를 들어 StorageService일부 데이터 유형에는 로컬 스토리지 서비스를, 다른 데이터 유형에는 네트워크 서비스를 사용할 수 있습니다. 다시 말하지만, 이것은 앱의 나머지 부분에서 중요하지 않은 구현 세부 사항입니다.

실제 서비스를 만드는 방법을 보여 드리겠습니다. 몇 번 해보면 꽤 쉬워집니다. Flutter 앱 설계대한 초보자 가이드 에서 보여 드린 동일한 예제 앱을 참조 할 것 입니다. 그 앱에서 우리는 MVVM 스타일 아키텍처를 사용했습니다. 다시 아키텍처는 다음과 같습니다.

뷰 모델은 서비스를 사용하여 일부 데이터를 가져온 다음 UI가 업데이트되도록 리스너 (뷰)에 알립니다. 뷰 모델 대신 BLoC 패턴을 사용하는 경우에도 동일한 원리입니다.

서비스를 설정할 때 따라야 할 단계는 다음과 같습니다.

1. 추상 클래스로 서비스 정의

정수를 저장하고 검색하는 스토리지 서비스를 만들 것입니다. 이 시점에서 데이터가 저장되는 위치는 중요하지 않습니다. 우리는 앱이 서비스와 상호 작용하는 방식을 정의하고 있습니다.

라는 파일 만들기 storage_service.dart 에서 /lib폴더를. 다음 코드를 붙여 넣으십시오.

카운터 앱 데이터는 정수이므로 하나는 저장 int하고 다른 하나는 가져 오는 두 가지 방법 이 있습니다. 이것이 우리의 실제 서비스 구현에 대한 청사진입니다.

이 추상 클래스는 경계를 만들었고 우리는 그 경계의 양쪽에서 자유롭게 작업 할 수 있습니다. 스토리지 서비스 구현을 위해 작업하거나 이미 작동하는 것처럼 앱에서 스토리지 서비스를 사용할 수 있습니다. 이번에는 먼저 서비스를 구현하겠습니다.

2. 추상 서비스 클래스 구현

개발 중에는 실제 서비스를 구현하는 방법에 대해 걱정하기 전에 나머지 앱을 빌드 할 수 있도록 가짜 데이터를 반환하는 가짜 서비스를 만드는 것으로 시작할 것입니다.

가짜 구현

다음은 가짜 구현입니다. 실제로 아무것도 저장하지 않으며 요청하면 더미 데이터를 반환합니다.

storage_service_fake.dart 라는 파일을 만듭니다 . 다음 코드를 붙여 넣으십시오.

공유 환경 설정 구현

이 구현은 공유 환경 설정에서 데이터를 저장하고 검색합니다. shared_preferences 패키지 사용에 대한 자세한 내용은 이 게시물을 읽어 보세요.

storage_service_shared_pref.dart 라는 새 파일을 만듭니다 . 다음 코드를 붙여 넣으십시오.

이렇게하면 카운터 값이 공유 기본 설정, 즉 사용자 장치의 로컬 저장소에 저장됩니다.

Sqflite 데이터베이스 구현

이 섹션을 지나서 자유롭게 스크롤하십시오. 데이터베이스는 단일 정수를 저장하기에는 확실히 과잉입니다. 구현 세부 정보가 나머지 앱에 중요하지 않다는 점을 강조하기 위해 포함했습니다.

이 구현은 SQLite 데이터베이스에서 데이터를 저장하고 검색합니다. sqflite 패키지 사용에 대한 자세한 내용은 이 게시물을 읽어보십시오 .

storage_service_database.dart 라는 새 파일을 만듭니다 . 다음 코드를 붙여 넣으십시오.

내가 말했듯이 그것은 과잉이었다.

네트워크 구현

이 구현은 원격 서버에서 데이터를 저장하고 검색합니다. 이 예제를 위해 원격 서버를 만들고 싶지 않았으므로 다음 코드는 테스트되지 않았습니다. 그러나 Dart를 사용하여 HTTP 서버를 만드는 데 관심이 있다면 이 소개 기사 를 읽고 Aqueduct를 사용한 Server Side Dart 에 대한 저의 시리즈를 참조하십시오 .

Dart에서 HTTP 요청을하려면 http 패키지를 사용할 수 있습니다 . 설정에 도움 이 필요한 경우이 문서 를 읽어 보세요.

storage_service_web.dart 라는 새 파일을 만듭니다 . 다음 코드를 붙여 넣으십시오.

StorageService위에서 정의한 추상 클래스 를 구현하기위한 더 많은 옵션이 있습니다 . 하지만 여기에있는 예제를 통해 그것이 어떻게 이루어 졌는지 맛보 았기를 바랍니다.

3. 서비스 로케이터 만들기

서비스 로케이터는 앱에서 사용하는 모든 서비스를 등록하는 중앙 위치입니다. 이를 사용하면 코드 내 어디에서나 서비스에 액세스 할 수 있습니다. 일반적으로 의존성 주입의 대안입니다.

어떤 사람들은 서비스 로케이터가 안티 패턴이며 테스트하기 어렵다고 불평합니다. 원한다면 서비스 로케이터 없이도 서비스를 사용할 수 있습니다. 필요한 클래스의 생성자에 서비스를 주입하기 만하면됩니다.

하지만 서비스 로케이터는 매우 사용하기 쉽기 때문에 정말 좋아합니다. ProxyProvider 와 같은 것으로 종속성 주입을 시도하는 것은 매우 혼란 스러울 수 있습니다 . 저는 이해하기 쉬운 것을 좋아합니다. "안티 패턴"에 관해서 는 실행 가능한 옵션으로 칭찬하는 Martin Fowler 의이 기사 를 읽으십시오 . (하지만 균형을 맞추기 위해이 기사를 읽을 수도 있습니다 .) 테스트하기 어렵다는 점은 사실이 아닙니다. 잠시 후에 서비스 로케이터를 사용하는 클래스를 테스트하는 방법을 보여 드리겠습니다.

Flutter에서 가장 많이 사용되는 서비스 로케이터 패키지는 GetIt 입니다. pubspec.yaml에 종속성을 추가하여 "얻을"수 있습니다 .

dependencies:
  get_it: ^3.1.0

여기서는 locator앱의 어디에서나 액세스 할 수 있는 전역 변수를 만듭니다 . 이것은 서비스 로케이터입니다. 아래 방법으로 서비스를 등록하면 앱 시작시 실행됩니다.

StorageServicelazy singleton으로 등록 하고 있습니다. 처음 사용할 때만 초기화됩니다. 앱 시작시 초기화하려면 registerSingleton()대신 사용하십시오. 싱글 톤이므로 항상 동일한 서비스 인스턴스를 갖게됩니다.

또한 우리 StorageServiceFakeStorageService. 이것이 추상 클래스 사용의 아름다움이 들어오는 곳입니다. 위에서 작성한 다른 구현 중 하나를 사용하려면이 한 줄의 코드를 변경하기 만하면됩니다.

여기서는 하나의 서비스 만 등록 StorageService했지만 ( ) 여러 서비스를 쉽게 등록 할 수 있습니다. 예를 들어 로그인 서비스 또는 Firebase 서비스가 있습니다.

4. 서비스 로케이터 초기화

앱 시작시 서비스를 등록해야하므로 main.dart 에서 등록 할 수 있습니다 .

표준 교체

void main() => runApp(MyApp());

import 'service_locator.dart';
void main() {
  setupServiceLocator();
  runApp(MyApp());
}

5. 서비스 받기

이제 서비스 로케이터가 초기화되고 서비스가 등록되었으므로 앱 어디에서나 서비스에 대한 참조를 가져올 수 있습니다.

Flutter 앱 설계에 대한 초보자 가이드 의 뷰 모델 클래스 예제를 사용해 보겠습니다 . counter_viewmodel.dart 라고 합니다. 다음 코드로 바꿉니다.

서비스를 찾기 위해해야 ​​할 일은 서비스 로케이터에게 서비스 유형을 알려주는 것입니다 : locator<StorageService>(). 이것은 GetIt이 그것을 얻기에 충분합니다. 그 후에 서비스를 제공 할 수 있습니다. 여기에있는 코드에는 서비스의 구체적인 구현에 대한 참조가 없습니다. 그것은 중요하지 않은 내부 세부 사항입니다.

6. 서비스를 사용하는 테스트 클래스

모의 할 수있는 생성자에 전달 된 것이없는 경우에도 서비스 로케이터를 사용하는 클래스를 테스트하는 것은 쉽습니다.

위의 뷰 모델 클래스를 테스트 해 보겠습니다. 테스트는 mockito 패키지를 사용하므로 설정하는 데 도움이 필요하면 이 문서를 참조하십시오 .

에서 /test폴더라는 파일 생성 counter_viewmodel_test.dart을 . 다음 코드를 붙여 넣으십시오.

서비스 로케이터를 조롱의 핵심은 설정하는 것입니다 allowReassignmenttrue setUpAll()방법. 그런 다음 뷰 모델의 서비스를 모의 객체로 바꿀 수 있습니다.

그것을 시도

여기에서 제공 한 코드를 사용 하여 아키텍처 자습서 예제 앱을 따라 조정 했다면 이제 가짜 구현으로 실행할 수 있습니다.

문제가있는 경우 전체 프로젝트가 GitHub에 있습니다 .

service_locator.dart에서 StorageServiceSharedPreferences를 StorageServiceFake로 바꿉니다. 그런 다음 앱을 실행하십시오. 증가 버튼을 몇 번 누르고 앱을 다시 실행하십시오. 서비스에서 가져온 저장된 값을 사용해야합니다.

앱 상태를 저장하는 데 서비스가 사용됩니다.

요약

여기에 모든 설명이없는 간단한 요약이 있습니다. 이 페이지를 즐겨 찾기에 추가하고 재교육이 필요할 때 여기로 돌아 오십시오.

추상 서비스 클래스

my_service.dart 라는 파일을 만듭니다 . 서비스의 기능을 정의하는 추상 클래스를 만듭니다.

abstract class MyService {
  Future<int> getSomeValue();
  Future<void> doSomething(int value);
}

my_service_impl.dart 라는 파일을 만듭니다 . MyService클래스를 확장 하고 필요한 메소드를 구현하십시오.

import 'my_service.dart';

class MyServiceImpl extends MyService {
  @override
  Future<int> getSomeValue() async {
    // do something
    return someValue;
  }

  @override
  Future<void> doSomething(int value) async {
    // do something
  }
}

GetIt 서비스 로케이터 패키지를 pubspec.yaml에 추가합니다 .

dependencies:
  get_it: ^3.1.0

import 'package:get_it/get_it.dart';
import 'my_service_impl.dart';
import 'my_service.dart';
GetIt locator = GetIt.instance;
setupServiceLocator() {
  locator.registerLazySingleton<MyService>(() => MyServiceImpl());
}

main.dart 에서 앱을 실행하기 전에 서비스 로케이터를 초기화하십시오 .

void main() {
  setupServiceLocator();
  runApp(MyApp());
}

서비스 로케이터를 사용하여 코드의 어느 곳에서나 서비스에 대한 참조를 가져옵니다.

class MyClass {

  MyService _myService = locator<MyService>();

  ...
}
  • _myService.getSomeValue()
  • _myService.doSomething(someValue)

서비스는 앱의 나머지 부분, 특히 변동성이 있거나 향후 변경이 필요한 타사 패키지에서 기능을 분리하는 데 유용한 도구입니다. GetIt과 같은 서비스 로케이터를 사용하면 코드 전체에서 이러한 서비스를 편리하게 제공 할 수 있습니다.

GetIt과 서비스 로케이터를 처음 소개 해준 FilledStacks의 Dane Mackier에게 감사드립니다. 여기 에서 그의 튜토리얼을 확인 하십시오 .

Suggested posts

Express.js 시작하기

Express.js 시작하기

Express는 웹 및 모바일 앱을 만드는 경험을 즐겁게 만드는 기능 세트가 포함 된 Node.js 프레임 워크입니다.

실패하지 않는 테스트를 절대 신뢰하지 마십시오

실패하지 않는 테스트를 절대 신뢰하지 마십시오

시험에 합격하는 것이 항상 좋은 것은 아닙니다. 테스트 플레이크를 통해 응원하는 것은 힘들고 실망스러운 경험이 될 수 있습니다.

Related posts

"실용적인 프로그래머"의 5 가지 필수 사항

역대 베스트셀러 코딩 북의 요점

"실용적인 프로그래머"의 5 가지 필수 사항

Pragmatic Programmer는 1999 년에 처음 출판되었으며 이후 역대 최고의 프로그래밍 책으로 선정되었습니다. 저자 Andy Hunt와 David Thomas는 Agile Manifesto의 원저자 중 하나였으며 몇 가지 심각한 자격을 가지고 있습니다.

대규모 GraphQL 쿼리 공격으로부터 보호

공격자가 공개적으로 사용 가능한 GraphQL 인터페이스를 사용하여 사이트를 스크랩하거나 서비스 거부 공격을 실행하는 방법에 대해 알아보십시오. 이들은 4 가지 방법 중 하나로이를 수행 할 수 있습니다. 단일 대형 쿼리를 신중하게 구성하여 실행하고, 관련 데이터를 가져올 수있는 병렬 쿼리를 많이 작성하고, 일괄 요청을 사용하여 많은 쿼리를 연속적으로 실행하고, 마지막으로 많은 요청을 보냅니다.

기술 인터뷰의 사회적 구성 요소

코딩 문제는 스트레스가 많지만 스트레스에 대한 당신의 반응은 당신의 기술적 능력보다 더 크게 말합니다.

기술 인터뷰의 사회적 구성 요소

기술 업계의 직책을 위해 인터뷰 할 때 일반적으로 제안을 고려하기 전에 최소한 3 차례의 인터뷰를 거치게됩니다. 라운드는 일반적으로 다음과 같습니다. 그렇게 생각하면 잘못된 것입니다.

훌륭한 개발자의 3 가지 행동 특성

훌륭한 개발자의 3 가지 행동 특성

훌륭한 개발자를 만드는 비 기술적 인 것들 나는이 기사를 작성하는 것을 한동안 미루고 있습니다. 나는 그것을 작성할 자격이 있다고 생각하지 못했습니다. 오늘은 쓸 때라고 생각했습니다.