반응형 프로그래밍과 상태 관리

nyoung·2024년 4월 23일
0

반응형 프로그래밍 패러다임 관점에서의 상태 관리를 공부하고 싶은 사람들이 보면 좋을 글

상태 관리에 대해 공부하면서, 반응형 프로그래밍에 대해 알게 되었습니다. 반응형 프로그래밍에 대해서는 이야기를 많이 들어봤지만 한번도 제대로 알아본 적이 없어서, 이번 기회에 공부하게 되었습니다. 또, 공부한 내용을 공유하려고 합니다.

먼저 반응형 프로그래밍에 대해 이야기하기 앞서, 프로그래밍 패러다임에 관한 이야기를 해보겠습니다. 반응형 프로그래밍도 하나의 패러다임 안에 속하기 때문입니다.

패러다임은 쉽게 말해서, 관점이라고 할 수 있습니다.

프로그래밍 패러다임

프로그래밍 패러다임은 프로그래밍을 어떠한 관점으로 바라보고 설계를 하느냐에 관한 것입니다.

객체지향 프로그래밍은 프로그래머들이 프로그램을 상호작용하는 객체들의 집합으로 볼 수 있게 하는 반면, 함수형 프로그래밍은 상태값을 지니지 않는 함수값(순수함수)들의 연속으로 생각할 수 있게 해준다.

이런 여러한 프로그래밍 패러다임을 알고 있다면, 그 중 더 현재 상황에 맞고 유리한 관점을 선택적으로 취할 수 있게 됩니다.

하나 이상의 패러다임을 적용할 수도 있습니다. 특히 자바스크립트 같은 경우엔 객체 지향, 함수형 등 여러가지 패러다임을 같이 사용할 수 있습니다.

대표적인 프로그래밍 언어들은 여러가지 패러다임을 지원하기 위한 API들을 제공하고 있습니다. (자바스크립트 같은 경우 객체 지향적인 패러다임을 손쉽게 사용할 수 있도록 Class 문법이 추가되었고, 자바 같은 경우 함수형 프로그래밍을 지원하기 위해 람다식 등을 도입했습니다.)

이제 본격적으로 반응형 프로그래밍 패러다임에 대해 알아보겠습니다.

반응형 프로그래밍 패러다임

반응형 프로그래밍이란, 데이터의 흐름과 변경사항의 전파에 중점을 둔 선언적인 프로그래밍 패러다임입니다.

값이 변경되었을 때, 변경된 값을 사용해서 알아서 변경사항을 적용해서 렌더링한다 === 반응형

반응형 프로그래밍은 비동기적 이벤트가 많은 프론트엔드 환경에서 사용하기 좋은 프로그래밍 패러다임이라고 생각합니다.

생각해보면, 이미 우리는 반응형 프로그래밍의 일부를 사용하고 있습니다. 대표적인 예가 EventListener 입니다.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Button Click Counter</title>
</head>
<body>
<button id="clickMe">Click me!</button>
<p>Clicks: <span id="count">0</span></p>

<script>
let count = 0;

document.getElementById('clickMe').addEventListener('click', function() {
    count += 1;
    document.getElementById('count').textContent = count;
});
</script>
</body>
</html>

해당 코드는 사용자의 행동이나 시스템 이벤트에 반응해서 특정 함수나 작업을 실행하는 것입니다.

이는 반응형 프로그래밍의 핵심 개념인 “데이터나 이벤트에 대한 반응”과 매우 유사합니다.

반응형 프로그래밍은 3가지 주요 키워드를 가집니다.

  • Data Stream
  • Functional Programming
  • Asynchronous observers

하나씩 알아보도록 합니다.

Data Stream

반응형 프로그래밍은 이벤트들을 Data Stream으로 보고, 이 스트림에 대한 다양한 연산을 적용해 애플리케이션의 상태를 업데이트합니다.

우리가 자바스크립트에서 많이 사용하는 filter, map 등을 살펴보면 값들을 받아서 특정 함수로 처리하고 반환하는 연산을 적용하는 것을 봅니다.

비동기 이벤트들을 Stream으로 보고, 이것들을 Array의 Method를 다루듯이 작성하는 것입니다.

아래는 대표적인 라이브러리인 RxJS를 활용한 코드 입니다.

const { fromEvent } = rxjs;
const { map, filter } = rxjs.operators;

// 스크롤 이벤트를 구독
const scrollStream = fromEvent(document, 'scroll');

// 특정 연산을 처리
const scrollPositionStream = scrollStream.pipe(
    map(event => window.scrollY),
    filter(y => y > 100)
);

// 스트림을 구독해서 이벤트 발생 시 함수 실행
scrollPositionStream.subscribe(position => {
    console.log('User scrolled more than 100 pixels:', position);
});

Functional Programming

반응형 프로그래밍은 함수형 프로그래밍으로써 구현됩니다. 선언적인 프로그래밍으로도 생각 할 수 있습니다.

선언형 프로그래밍은 보통 명령형 프로그래밍보다 가독성, 재사용성, 독립성, 유지보수성 면에서 장점이 있습니다.

반응형 프로그래밍에서는 이러한 함수형 패러다임을 통해 데이터 스트림을 변형하고, 상태 변화를 관리합니다.

절차형 프로그래밍 vs 함수형 프로그래밍

Asynchronous observers

반응형 프로그래밍의 핵심은 애플리케이션이 모델 변경, HTTP요청, 사용자 등작, 탐색 등과 같은 이벤트를 방출할 수 있는 옵저버블로 동작하도록 구현하는 것입니다.

Untitled

이때 Push 방식을 사용하는데, Pull 방식과 비교해 이야기할 수 있습니다.

Pull과 Push 방식

데이터 생산자(Producer)와 소비자(Consumer) 간의 통신 방식입니다.

  • Pull 방식

Pull 방식은 데이터 소비자가 생산자로부터 데이터를 받을 시기를 결정합니다. 생산자는 데이터가 소비자에게 전달될 시기를 알지 못합니다. 즉, 소비자가 데이터의 흐름을 제어합니다.

소비자는 필요 시 데이터를 생산자에게서 직접 요청하고, 가져옵니다.

대표적으로, 자바스크립트 함수는 Pull 시스템입니다. 함수는 데이터의 생산자이고, 함수를 호출하는 코드는 호출 결과로부터 반환값을 당겨 소비합니다.

ES2015는 제너레이터 함수와 이터레이터(function*)를 도입했습니다. 이 또한 Pull 시스템의 한 종류입니다. iterator.next()를 호출하는 코드는 소비자로서 이터레이터(생산자)로부터 여러 값을 가져옵니다.(Pull)

자바스크립트 함수 호출: 함수를 호출하는 순간, 함수는 값을 계산하고 반환합니다. 함수의 소비자는 함수를 호출함으로써 데이터를 '당겨오는' 주체가 됩니다.

function getData() {
    return "데이터";
}

// 소비자가 함수를 호출하여 데이터를 '당겨옵니다'
const data = getData(); 
console.log(data);  // 출력: 데이터

이터레이터 사용: 이터레이터는 next() 메소드를 통해 소비자가 다음 데이터를 요청할 때까지 아무런 값도 반환하지 않습니다.

function* numberGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

const gen = numberGenerator();

console.log(gen.next().value);  // 1
console.log(gen.next().value);  // 2
console.log(gen.next().value);  // 3
  • Push 방식

Push 방식에서는 생산자가 데이터의 흐름을 제어합니다. 생산자는 자신의 판단에 따라(데이터 변경 시 등) 데이터를 소비자에게 자동적으로 밀어(Push)넣습니다.

이벤트 리스너: 웹 페이지에서의 클릭 이벤트 리스너는 사용자가 버튼을 클릭할 때마다 이벤트를 자동으로 처리합니다.

<button id="myButton">클릭하세요</button>
<script>
document.getElementById("myButton").addEventListener("click", function() {
    alert("버튼이 클릭되었습니다!");
});
</script>

Observable (RxJS 사용): RxJS의 Observable은 데이터 스트림을 생성하며, 이 스트림의 데이터는 구독하는 순간부터 소비자에게 전달됩니다.

const { fromEvent } = rxjs;

const clicks = fromEvent(document, 'click');
clicks.subscribe(clickEvent => console.log('Clicked!', clickEvent));

Pull 방식은 데이터를 요구하는 주체가 소비자이며, Push 방식은 데이터를 제공하는 주체가 생산자입니다.

제어의 역전이라는 키워드와도 관련있는데, 모듈과 모듈 간의 결합 시 참조의 주체를 바꾸어 사용하는 방법이 제어의 역전 입니다.

React의 State 관리는 반응형일까요?

정답은 “X” 입니다.

앞의 Hook을 공부할 때 나왔듯이, React는 데이터 계산 시 pull 방식을 사용합니다.

다만 state의 변화가 일어날 때 리액트가 알아서 pull 시점을 결정해주고, 렌더링을 일으키는 것이다. 알아서 스케줄링해주고, 그것이 거의 즉각적으로 렌더링되기 때문에 push 방식처럼 보이는 것입니다.

하지만 실제로는 batch 업데이트라던지, reconciliation 등 연산 과정을 처리하고 업데이트하는 것이다. 이때는 연산이 필요 시까지 지연하게 됩니다.

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

하지만 이것이 상충된다는 의미는 아니며, 충분히 같이 쓸 수 있습니다. 실제로 Facebook과 넷플릭스 등 많은 빅테크에서는 React와 RxJS를 같이 사용합니다.

반면 앵귤러는 미리 선언이 되어있는 구조에서 값이 변화할 때 마다 템플릿으로 데이터를 전달하는 Push의 관점으로 설계됩니다. 스벨트 또한 거의 완벽한 반응형으로 구현되어있습니다.

대표적인 라이브러리

대표적인 자바스크립트 라이브러리로는 reactivex.ioRxJS 가 있습니다.

profile
코드는 죄가 없다,,

0개의 댓글

관련 채용 정보