민프

[Flutter] BLoC - 1. BLoC (Business Logic Component)이란? 본문

[Flutter]

[Flutter] BLoC - 1. BLoC (Business Logic Component)이란?

민프야 2023. 10. 19. 20:21

이번 포스팅에서는 BLoC에 대해서 알아보겠습니다.

먼저 개념에 대해서 정리하고 

다음은 예제 코드를 통해서 알아보겠습니다.  

 

소개

BLoC는 공식홈페이지에 보면 아래와 같이 나와있습니다.

Bloc makes it easy to separate presentation from business logic, making your code fast, easy to test, and reusable.

해석해보면 Bloc 패턴은 프레젠테이션과 비즈니스 로직을 분리하여 코드를 빠르고, 테스트하기 쉽고, 재사용 가능하게 만드는 데 도움이 된다고 합니다.

 

그렇다면 BLoC의 핵심은 무엇일까요?

Simple: Easy to understand & can be used by developers with varying skill levels.
Powerful: Help make amazing, complex applications by composing them of smaller
components.Testable: Easily test every aspect of an application so that we can iterate with confidence.

심플하고, 강력하고, 테스트에 유리하다고 합니다. 

 

공식홈페이지에서는

Bloc을 알려면

Steam, Cubit, bloc 키워드를 알아야한다고 하는데 

Cubit, Bloc은 다음 포스팅에서 알아보고

먼저 Steam 개념부터 알아보겠습니다. 

 

Steam이란?

Steam은 비동기 데이터의 연속입니다

 

공식 홈페이지에서 나와있는 비유는 아래와 같습니다. 

Steam은 물이 흐르는 파이프에 비유할 수 있는데,

파이프는 비동기 데이터 Stream이고, 물은 비동기 데이터입니다.

 

제가 이해한 바로는 파이프(Steam)에 물(비동기 데이터)이 흐르도록 하면 그와 관련 된 기능들이 계속적으로 진행된다.

입니다.

 

Socket.io, STOMP와 유사한 동작 방식 인 것 같습니다. 

구독시켜놓고 -> 정보가 업데이트 되면 -> 구독자에게 정보가 계속적으로 업데이트 되는 것 처럼

Stream의 동작방식을 보면

  1. 데이터 생성: 스트림은 데이터 소스로부터 데이터를 생성합니다. 이 데이터 소스는 API, 데이터베이스, 소켓 등 다양한 것이 될 수 있습니다.
  2. 데이터 발행: 스트림은 생성된 데이터를 연속적으로 발행합니다. 이 데이터는 스트림을 통해 시간에 따라 순차적으로 전달됩니다.
  3. 데이터 구독: 클라이언트나 다른 컴포넌트는 스트림을 구독할 수 있습니다. 구독자는 스트림으로부터 데이터를 실시간으로 받아 처리할 수 있습니다.
  4. 데이터 처리: 구독자는 스트림으로부터 받은 데이터를 처리하고, 필요에 따라 UI를 업데이트하거나 다른 액션을 수행합니다.

 

예시 코드는 아래와 같습니다.

Stream<int> countStream(int max) async* {
    for (int i = 0; i < max; i++) {
        await Future.delayed(Duration(seconds: 1)); // 1초마다 대기
        yield i; // i 값을 스트림에 전달
    }
}

void main() async {
    Stream<int> stream = countStream(10); // 0부터 9까지의 값을 1초 간격으로 생성하는 스트림
    await for (int value in stream) {
        print(value); // 스트림에서 값을 받아와 출력
    }
}

 

그럼 계속해서 아키텍쳐에 대해서 알아보겠습니다. 

아키텍쳐

BLoC 아키텍쳐는 애플리케이션을 아래와 같이 세 가지 주요 레이어로 분리합니다.

  • Presentation (UI)
  • Business Logic
  • Data
    • Repository
    • Data Provider

그럼 하나씩 살표보겠습니다.

 

BLoC
https://totally-developer.tistory.com/82

Data Layer

Data Layer는 애플리케이션의 아키텍처에서 가장 낮은 레벨에 위치하며, 데이터를 검색하고 조작하는 역할을 담당합니다. 

주로 데이터베이스, 네트워크 요청, 그리고 다른 비동기 데이터 소스와 상호작용하고,
일반적으로 RepositoryData Provider 두 부분으로 나눌 수 있습니다.

 

- Data Provider

Data Provider는 원시 데이터를 제공하는 역할을 합니다. 

이는 애플리케이션의 Data Layer에서 중요한 부분을 차지하며, 

데이터베이스, 네트워크, 파일 시스템 등 다양한 데이터 소스로부터 데이터를 가져올 수 있어야 하고,

CRUD 작업을 수행하는 간단하고 명확한 API를 제공해야 합니다.

 

이렇게 구성된 Data Provider는 Repository 레이어에서 사용되어

원시 데이터를 가져와 필요에 따라 처리한 후 Business Logic 레이어에 제공하는 역할을 수행합니다.

 

예제 코드는 아래와 같습니다.

class DataProvider {
    Future<RawData> readData() async {
        // Read from DB or make network request etc...
    }
}

 

- Repository

Repository 레이어는 Data ProviderBloc 레이어 사이의 중간 매개체 역할을 합니다.

Data Provider로부터 원시 데이터를 받아와서 필요에 따라 변환, 필터링 또는 조합한 후,

그 결과를 Business Logic 레이어에 전달합니다.

 

예제 코드는 아래와 같습니다. 

// Repository 클래스 정의. 데이터 제공자(Data Providers)로부터 데이터를 가져와 필터링하거나 변환하는 역할을 합니다.
class Repository {
    
    final DataProviderA dataProviderA;
    final DataProviderB dataProviderB;

    Repository(this.dataProviderA, this.dataProviderB);

    // getAllDataThatMeetsRequirements 메서드 정의. 특정 요구 사항을 충족하는 데이터를 비동기적으로 가져옵니다.
    Future<Data> getAllDataThatMeetsRequirements() async {
        
        final RawDataA dataSetA = await dataProviderA.readData();
        final RawDataB dataSetB = await dataProviderB.readData();

        // _filterData 메서드를 사용하여 dataSetA와 dataSetB를 필터링하거나 변환하여 최종 Data 타입의 데이터를 생성합니다.
        final Data filteredData = _filterData(dataSetA, dataSetB);
        
        // 필터링 또는 변환된 데이터를 반환합니다.
        return filteredData;
    }

}

 

Business Logic Layer 

비지니스 로직 계층을 UI(presentation Layer)와 Data Layer 사이의 브리지로 생각하시면 될 것 같습니다.

 

Business Logic Layer는 애플리케이션의 핵심 로직을 처리하는 레이어입니다. 

이 레이어는 Presentation Layer로부터 입력을 받아 처리하고(이벤트 처리)

그 결과를 다시 Presentation Layer에 전달하여 UI를 업데이트하는 역할을 합니다. 

또한 프레젠테이션 계층이 사용할 새로운 상태를 구축하기 위해 repository와도 통신합니다.

 

정리해보자면

Business Logic Layer는 애플리케이션의 중심적인 로직을 처리하며, 

사용자 인터페이스와 데이터 레이어 사이의 다리 역할을 합니다. 

사용자의 액션과 이벤트를 처리하고,

필요한 데이터를 Repository로부터 가져와 상태를 업데이트하며,

이 상태를 기반으로 사용자 인터페이스를 업데이트합니다.

 

예제 코드는 아래와 같습니다.

class BusinessLogicComponent extends Bloc<MyEvent, MyState> {
    
    // 생성자. Repository 인스턴스를 주입받아 초기화합니다.
    BusinessLogicComponent(this.repository) {
        
        // AppStarted 이벤트가 발생했을 때의 처리 로직을 정의합니다.
        on<AppStarted>((event, emit) {
            
            // try 블록 내에서 Repository를 사용해 데이터를 가져옵니다.
            try {
                
                // Repository의 getAllDataThatMeetsRequirements 메서드를 비동기로 호출하여 데이터를 가져옵니다.
                final data = await repository.getAllDataThatMeetsRequirements();
                
                // 데이터를 성공적으로 가져왔다면, Success 상태를 emit하여 상태를 업데이트합니다.
                emit(Success(data));
                
            // 데이터를 가져오는 과정에서 예외가 발생하면 catch 블록이 실행됩니다.
            } catch (error) {
                
                // 예외가 발생했다면, Failure 상태를 emit하여 오류 정보를 상태에 포함시킵니다.
                emit(Failure(error));
            }
        });
    }

    // Repository 인스턴스를 저장하는 멤버 변수입니다. 
    // 이를 통해 Business Logic Layer에서 데이터를 가져올 수 있습니다.
    final Repository repository;
}

 

 

Presentation Layer

Presentation Layer는 사용자 인터페이스(UI)를 구성하고, 

사용자의 입력과 애플리케이션의 생명주기 이벤트를 처리하는 역할을 합니다. 

쉽게말해 Bloc의 상태에 기반하여 UI를 렌더링하는 방법을 결정합니다.

 

아래 예시 코드에 AppStart 이벤트가 나오는데

AppStart 이벤트는 대부분의 애플리케이션 플로우는 AppStart 이벤트로 시작됩니다.

이 이벤트는 애플리케이션을 시작하고 초기 데이터를 로드하는 트리거 역할을 합니다.

 

// PresentationComponent 클래스는 UI를 구성하고, Bloc의 상태에 따라 UI를 업데이트하는 역할을 합니다.
class PresentationComponent {
    
    // Bloc 인스턴스를 멤버 변수로 가집니다.
    final Bloc bloc;

    // 생성자에서 AppStarted 이벤트를 Bloc에 추가하여 애플리케이션의 초기 데이터 로딩을 트리거합니다.
    PresentationComponent() {
        bloc.add(AppStarted());
    }

    build() {
        // render UI based on bloc state
    }
}

 

다음 포스팅에서는 BLoC패턴을 이용하여 간단하게 앱을 하나 만들어보겠습니다. 


참고링크

https://totally-developer.tistory.com/82

https://bloclibrary.dev/#/architecture

Comments