React Hooks Vs. RxJS - React는 Fully Reactive할까?

Jane Shin·2021년 9월 9일
14

약 1년간 전 회사에서 Angular로 개발을 하다가, 퇴사하고 나서 React로 넘어왔다. 보통 Angular는 프레임워크기 때문에 배우기 가장 어렵고, React는 그보다는 쉽다고 하던데 웬걸..React를 받아들이는 데 Angular보다 더 애먹었다. 기존에 갖고 있던 Angular에서의 사고방식과 충돌했던 것이다. 처음에 가장 이해가 안 갔던 건 Hooks를 통한 상태관리였다.

'Angular에서는 RxJS observable을 사용해서 데이터를 '관찰(observe)'하고 리액티브 프로그래밍을 할 수 있었는데, React에서는 RxJS를 안 사용해도 Hooks가 상태를 observe할 수 있다니..? 그럼 Hooks는 상태를 관찰하는 거니까 Reactive Programming같은 건가?'

당시에는 Hooks의 사용법도 잘 몰랐던 리액트 뉴비 상태였기에, 리액트를 더 사용하다 보면 뭔가 알게 되겠지..라면서 넘어갔었다. 이제는 그때에 비하면 React도 꽤 익숙해졌기에, 이 물음을 다시 한번 꺼내 보려고 한다.

결론적으로, Hooks는 RxJS처럼 'Reactive programming'을 위해 만들어진 것인가?

아니다.

"아니...'React'라며..." (웅성웅성)

왜일까?

RxJS와 Reactive Programming이란

우선 RxJS가 처음인 사람들을 위하여 간단히 소개하고자 한다. 필자는 RxJS를 Angular와 함께 사용했지만, 사실 React나 다른 라이브러리에도 사용할 수 있다. RxJS 공식 문서를 보자.

RxJS is a library for composing asynchronous and event-based programs by using observable sequences. It provides one core type, the Observable, satellite types (Observer, Schedulers, Subjects) and operators inspired by Array#extras (map, filter, reduce, every, etc) to allow handling asynchronous events as collections.

Rx는 'Reactive extensions'의 줄임말로, ReactiveX API 를 사용한다. RxJS이외에도 ReactiveX API를 사용하는 RxJava, RxSwift 등등은 결국 Reactive Programming이라는 개념을 공유한다.

Reactive Programming이란 비동기 데이터 스트림에 기반을 둔 프로그래밍 패러다임으로, 배열 등의 데이터를 데이터 스트림이라는 일관된 형식으로 만들고 이를 구독(subscribe)하여 데이터 스트림의 상태 변를 관찰(observe)하고 반응하는 애플리케이션을 작성하는 방식이다.

'상태 변화를 관찰하고 반응하는 방식으로 동작' 어디서 많이 들어보지 않았는가? React도 컴포넌트의 상태 변화가 view에 반영되기 때문에 뭔가 똑같은 말 같다. 하지만, 엄밀하게 얘기해서 React는 Reactive Programming이 아니다.

Push 와 Pull 방식

React와 Reactive programming의 가장 큰 차이는 architecture의 차이에 있다.

React docs의 design principles에서 Scheduling 섹션을 보자.

Some popular libraries implement the “push” approach where computations are performed when the new data is available. React, however, sticks to the “pull” approach where computations can be delayed until necessary. - React docs, design principles

여기서도 볼 수 있듯이, React의 경우 원칙적으로 pull approach를 사용한다. Pull approach란 새로운 데이터가 제공되었을 때 연산이 필요시까지 지연될 수 있다는 의미이다. 이 방식을 사용하여, 만일 데이터가 frame 주사율보다 빠르게 도착하면 업데이트를 합쳐 일괄 처리(batch)할 수도 있고 업데이트마다 각각의 우선순위들을 가질 수도 있다.

그러나 RxJS와 같이 Functional Reactive Programming은 Observable과 같은 push approach를 사용한다. 이 경우 애플리케이션(프로그래머)가 스케쥴링이 어떻게 작동할지 직접 결정하여야 한다. Pull기반 접근은 React로 하여금 똑똑하게 그 결정사항들을 대신 결정할 수 있게 해 준다. 즉 알아서 스케쥴링 해 주는 것이다.

(이 글을 쓰는 데 참고한 React Fiber architecture에서는 아직 리액트에서 스케쥴링은 발전 단계에 있다고 하며 이 스케쥴링을 개선하고자 Fiber가 만들어졌다고 하는데, 5년 전 글이라 이때보다는 많이 발전했으리라고 생각한다)

There is an internal joke in the team that React should have been called “Schedule” because React does not want to be fully “reactive”. - React docs, design principles

그럼 이제, approach상에서의 차이를 보았으니 코드를 보도록 하자.

Angular & RxJS

//user.service.ts
import { Observable } from 'rxjs'
import {
  HttpClient,
  HttpErrorResponse,
  HttpHeaders,
  HttpParams
} from '@angular/common/http';

getUsers(
    id: string,
  ): Observable<{ list: Member[] }> {
    const url = `/api/users`;
    return this.http.get<any>(url).pipe(retry(3), catchError(this.handleError));
  }
//컴포넌트 코드(사용처)
import {userService} from 'user.service';

this.userService.getUsers(user_id).subscribe(
      res => {
           this.users = res;
         },
       error => {
	   console.log(error)
	}
)

Observable한 데이터를 subscribe하는 방식으로 동작한다.

비슷한 기능을 리액트로 만들어 보자.

React

// useUser custom hook
import {useState, useEffect} from 'react'

const useUsers = () => {
const [users, setUsers] = useState()

    const getUsers = async () => {
          try {
		const response = await axios.get('/api/users');
		return response;
          }
          catch(e){
		console.log(e)
          }
	}
		
	useEffect(() => {
	getUsers().then((res) => {
		setUsers(res);
	  })
	},[])
		
	return users;
}
// 컴포넌트 내 사용

function Users() => {
	const users = useUsers();
    // users데이터를 업데이트하는 로직
}

여기서 보면, useUsers() hook을 사용해서 Users컴포넌트가 상태를 관찰하고 업데이트하는 것처럼 보인다. 하지만 이건 view update를 스케쥴링하는 리액트의 방식이다.

RxJS는 그 자체로는 컴포넌트 렌더링 사이클과 관련이 없다. 하지만 React hooks의 'reactivity'는 Framework(혹은 library)와 단단히 연결되어 있고, view와 컴포넌트와 관련성이 크다.

즉, Hooks는 컴포넌트 렌더링에 중점을 둔 개념이고 이는 비동기적인 이벤트 스트림을 처리하려고 만들어진 Reactive programming과는 시작점 자체가 다르기 때문에 Reactive programming이라고 할 수 없는 것이다.

그러나 이는 React Hooks와 Reactive Programming이 서로 상충한다는 의미는 아니다. 둘은 충분히 같이 쓰일 수 있고, 실제로 Facebook에서는 React와 RxJS를 같이 사용한다고 한다. 또 RxJS를 위한 리액트 바인딩들을 제공해주는 React-RxJS 오픈소스 프로젝트도 존재하니, 관심 있다면 확인해 보자.

8개의 댓글

comment-user-thumbnail
2021년 11월 9일

글 잘 읽었어요! 궁금한점이 있는데
React + RxJS 조합은 어떻게 생각하시나요??
(꼭 사용해야할까? 등등)

개인적으로 RxJS 를 학습할 카테고리로 삼고있는데, 고민입니다!

1개의 답글
comment-user-thumbnail
2022년 1월 24일

글 잘 읽었습니다!

  • 리액트는 컴포넌트 렌더링(뷰)의 관점에서 보면 새로운데이터(상태변화)에 반응해서 렌더링이 일어난다.
  • Reactive Programming 은 비동기 이벤트 스트림에 대해 반응하여 연산을 수행하는 것이다.
  • 하지만 리액트는 새로운데이터에 대해서 컴포넌트 렌더링 시점까지 연산을 지연, 즉 컴포넌트를 렌더링 하는 시점을 기준으로 동기화 되어 작동하기 때문에 "fully reactive" 하지 않다.

이해한 바 대로 요약해봤습니다. 이 내용이 맞을까요?

1개의 답글
comment-user-thumbnail
2022년 5월 24일

hook이 reactive program 인지에대해 찾다가읽게 되었는데 결국 react은 데이터계산시 pull 접근을 고수하여 react 자체 스케줄링에의해 처리되지만 reactive program은 push 방식이라 사용자가 직접 정해야한다 정도로 이해가 됐네요 ㅠ 이러한 의도로 글작성하신게 맞나요?

1개의 답글
comment-user-thumbnail
2022년 6월 13일

좋은 글 감사합니다!

답글 달기