BLoC 이해 하기 및 간단 정리 하기

박성룡 ( Andrew park )
8 min readAug 25, 2019

--

BLoC Pattern 이란 Bussiness Logic Component 의 줄임말이다.

BLoC Pattern 은 Flutter 의 상태 관리를 제어하기위해서 Google 개발자에 의해서 디자인 되었다.

Flutter 에서는 상태에 따라서 렌더링이 일어나기 때문에, 상태 관리가 매우 중요하다.

BLoC 은 UI 와 Bussiness Logic 을 분리하여, 각각 코드의 의존성을 낮추게한다.

Flutter 을 위해 설계 되었지만, 디자인 패턴이기 때문에, 어떠한 프레임워크 나 언어에서도 사용이 가능하다.

만약 Reactive Programming 을 이해하고 있다면, BLoC Pattern 을 이해하기 쉽다.

BLoC 의 형태

BLoC 에서 각 UI 객체 들은 BLoC 객체를 구독하고 있다.

BLoC 객체의 상태가 변경되면, BLoC 의 상태를 구독중인 UI 객체 들은 그 즉시 해당 상태로 UI 를 변경한다.

BLoC 객체는 UI 객체로 부터 이벤트를 전달받으면, BLoC 객체는 필요한 Provider 나 Repository 로 부터 데이터를 전달받아, Bussiness Logic 을 처리한다.

Bussiness Logic 을 처리한후, BLoC 객체를 구독중인 UI 객체 들에게 상태를 전달한다.

class BLoC {
provider: Provider = new Provider();
stream: Subject = new Subject();
async sink() {
const data = await Provider.getCounterModel();
const result = await this.BussinessLogic(data);
this.stream.next(result);
}
private async BussinessLogic() {
// ...
}
}

UI 객체는 구독중이던 BLoC 객체의 상태가 변경되면 상태를 전달받는데, 이때 얻은 상태를 이용하여 화면을 재구성한다.

class UI {
bloc = new BLoC();
constructor() {
this.bloc.stream.subscribe((data) => {
this.render(data);
});
}
render(data) {
return (
<div onClick={this.bloc.sink}>
{data}
</div>
)
}
}

BLoC 의 특징

  • UI 에서는 여러 BLoC 이 존재할 수 있다.
  • UI 에서는 화면에 집중하고, BLoC 에서는 Logic 에 집중한다.
  • UI 에서는 BLoC 의 내부 구현에 대해서 몰라도 된다.
  • BLoC 은 여러 UI 에서 구독 할 수 있다. 때문에 재사용이 용의하다.
  • BLoC 만을 분리해서 테스트가 가능하다.

Flutter 와 BLoC

Flutter 에서는 Stream 을 이용해서 BLoC 을 구현한다.

StreamController 으로 Observable 객체를 생성한다.

StreamController 의 Sink 를 이용해서 값을 전달한다.

StreamController 의 Steam 를 이용해서 상태를 구독한다.

이때 RxDart 를 이용하여 StreamController 을 쉽게 만들 수 있다.

class Bloc {
final _repository = Repository();
final _subject = PublishSubject<String>();
Observable<String> get stream => _subject.stream; action() async {
String result= await _repository.getData();
_subject.sink.add(result);
}
dispose() {
_subject.close();
}
}

UI 에서는 StatefulWidget 을 쓰지 않고, 그리고 setState 를 쓰지 않고도 bloc 의 상태 변경에 따라 UI 를 변경할 수 있다.

class UI extends StatelessWidget {
UI() {
bloc.action();
}
@override
Widget build(BuildContext context) {
return Scaffold(
// ...
body: StreamBuilder(
stream: bloc.stream,
builder: (context, AsyncSnapshot<String> snapshot) {
return Text(snapshot.data);
},
),
);
}
}

React 와 BLoC

React 에서도 RxJS 를 이용하여 BLoC 을 구현할 수 있다.

만약 RxJS 를 쓸수 없다면, Observer Pattern 을 구현하거나, Object.observe() 등등 다양한 방법으로 구현이 가능하다.

class BLoC {
stream = new Subject();
provider = new Provider();
constructor() { } async sink() {
const result = await this.provider.getData();
this.subject.next(result);
}
}

React Hook 을 이용해서 생성시점에 stream 을 구독하고, stream 에 변경사항이 있을때마다, 최신 상태로 다시 렌더링 한다.

click 이 일어날때 마다, sink 을 호출하여 BLoC 에게 이벤트를 전달한다.

const bloc = new BLoC();
function UI() {
const [state, setState] = useState('');
useEffect(() => {
bloc.stream.subscribe((data) => {
setState(data);
});
return () => {
bloc.stream.unsubscribe();
}
}, []);
return (
<div onClick={bloc.sink}>
{data}
</div>
)
}

Redux 와 BLoC

BLoC 패턴을 보다보면 Redux 와 유사해 보일 수 있다.

하지만, Redux 와는 다른 부분이 존재한다.

UI 는 BLoC 을 구독한다 는 점이다.

Redux 에서는 Dispach 라는 진입점을 가지고, Action 과 Reducer 를 통해 새로운 상태를 만들어, View 에게 전달한다.

BLoC 은 Sink 라는 진입점을 가지고, Bussiness Logic 을 처리하고, 새로운 상태를 만들어, Steam 을 구독하고 있는 UI 에게 전달한다.

순서 상으로 비슷해보이지만 명확히 다른점은 subscribe 한다는 점이다.

때문에, UI 에서 어떠한 진입점이 없더라도, BLoC 에서 상태가 변경되면 이를 구독하고 있는 UI 는 최신 상태로 변경한다.

UI 는 더 이상 로직에는 신경쓰지 않고, 최신 상태를 화면에 어떻게 구현할지, 즉 오직 화면에만 집중하게 된다.

Angular 와 BLoC

Angular 는 service 로 BLoC 을 쉽게 구현 할 수 있다.

sink 와 stream 을 분리해서 sink 는 변경 요청을 구독하고, stream 은 변경 요청에 따른 상태를 만들고 이를 알리는 형태로 구현하였다.

@Injectable({
providedIn: 'root',
})
class BlocService {
sink = new Subject<string>();
stream = new Observable<string>((observer: Observer<string>) => {
this.sink.subscribe((data: string) => {
observer.next(data);
})
});
constructor(private provider: Provider) { } Action() {
this.provider.getData$().subscribe(result => {
this.sink.next(result);
});
}
}

Angular 의 AsyncPipe 를 이용해서, stream 의 변경이 일어날때 마다 상태를 구독하게 하였다.

@Component({
selector: 'app-ui',
template: `
<div (click)="bloc.Action()">
value: {{ bloc.stream | async }}
</div>`,
})
export class UIComponent {
constructor(private bloc: BlocService) { }
ngOnInit() {
this.bloc.Action();
}
}

마무리

BLoC 은 Flutter 의 상태를 관리하기위해서 디자인되었다.

때문에 상태를 관리하고, Observer Pattern 이 구현된 어떠한 곳에서도 쓰일수 있을듯하다.

Reactive Programming 을 어떻게하면 쉽게 React 에 녹일 방법이 있을지 고민하던 와중 좋은 패턴을 알게 되어, 많은 배움을 얻을 수 있었다.

--

--