반응형 프로그래밍

박세영·2022년 8월 14일
1

What is Reactive Programming?

… reactive programming is a delcarative programming paradigm concerned wiht datat streams and the propagation of change - wikipidea

위 인용에서 키워드는 다음과 같다. 패러다임, 변경 사항의 전파, 데이터의 흐름, 선언적 프로그래밍

위 정의가 언뜻 듣기에는 난해해 보이지만, 사실 반응형 프로그래밍은 아주 친숙한 개념이다. 가장 쉽게 반응형 프로그래밍의 패러다임을 이해할 수 있는 예제는 바로 스프레드 시트이다.

출처: https://velog.io/@teo/reactive-programming#3-반응형-프로그래밍-deep-dive

*C1셀에 =A1+B1 이라는 수식을 선언적으로 작성해 두면, A1 혹은 B1이 변경될 때마다 변경사항이 전파되어 C1의 값이 자동으로 변경되는 것을 볼 수 있다. D1에는 =A1+B1+C1이라는 수식을 선언적으로 작성해 두었는데 역시 A1 혹은 B1이 변경될 때 변경사항이 전파되어 C1의 값이 변경되고 다시 그 값이 D1의 수식에 반영이 되어 D1의 값이 변경이 되는 데이터의 흐름이 만들어졌다.*

이러한 개념을 조금 더 확장해서 프로그래밍을 할 수 있다면 좋지 않을까 하는 패러다임이 바로 반응형 프로그래밍이다.

언제부터 웹에서 반응형 프로그래밍이 중요해졌을까?

웹 개발에서 반응형 프로그래밍이 중요해진 계기는 바로 웹 프레임워크로의 전환이다.

💡 `jQuery`에서 `React`와 같은 웹 프레임워크(라이브러리)로의 전환은 DOM을 더욱 쉽게 조작하도록 도와준다. 편한 방식으로 컴포넌트를 구축하면 알아서 DOM으로 변환한 뒤 렌더링 해준다.

웹 프레임워크의 본질을 반응형 프로그래밍 패러다임의 관점에서 다시 적어보자.

💡 **Web Framework의 본질(MVVM Pattern)** Change detection + Binding + Template + Auto render = Reactive

이렇게 정리를 해보니 평소 자주 사용하던 웹 프레임워크가 반응형 프로그래밍과 매우 닮은 점이 많다는 것을 느낄 수 있다. 웹 프레임워크의 등장은 기존의 방식을 뒤엎는 혁신적인 패러다임이었기 때문에 웹 프론트에서 아주 중요한 개념이 되었고 이를 정의하는 Reactive Programming이라는 용어의 관심도가 높아졌다.

초기 Reactive Programming 패러다임은 이렇게 데이터의 변경을 감지하고 선언적으로 프로그래밍을 하는 방법을 통해 View를 업데이트 하는 방식으로 발전했다.바

반응형 프로그래밍 패러다임으로의 전환

이렇듯 웹 프로그래밍은 데이터를 가져와서 화면을 만드는 방식에서 무엇을 할지 선언을 한 뒤 변경된 데이터를 감지하고 전파하는 방향으로 패러다임이 변했다.

초기에는 이 반응형 프로그래밍 패러다임이 뷰에 집중되어 있었다면 최근에는 뷰를 넘어 비즈니스 로직을 포함한 모든 스크립트에서 사용할 수 있도록 개념이 확장되었다.

이후 Redux를 필두로한 상태관리라고 하는 것들 역시 데이터의 변경을 감지하고 변경을 전파해서 선언적으로 값을 만들어낸다는 반응형 프로그래밍 패러다임에 속하게 된다.

기존 JS 비동기 대응 문제점

JS는 비동기 로직을 다루기 위해 많은 진화를 거쳐왔다. 기존 콜백 지옥에서 Promise개념이 등장해서 체이닝으로 처리할 수 있게 되었고 ES7이후에는 async-await 개념이 등장했다. 하지만 과연 이정도로 충분할까? 다음 Real World Problem을 살펴보자. 요구사항은 아래와 같다.

  • 타이핑 할때 마다 서버에서 데이터를 받아서 보여주세요.
  • 너무 잦은 요청은 부하가 심하니 타이핑 간격이 좁으면 대기하다가 입력이 늦어지면 그때 서버에 요청하세요.
  • 같은 내용일때는 요청하지 마세요.
  • 일정 시간 동안 응답이 없으면 3회 재시도 하고 그래도 응답이 없으면 에러 메시지를 출력해 주세요.
  • 데이터는 캐시로 보관을 해서 먼저 보여주고 요청이 완료되면 새로 갱신된 데이터를 보여주세요.
  • 엔터를 누르면 서버로 요청한 건 취소하고 검색 결과를 보여주세요.

Promiseaysnc-await으로 이 문제를 해결할 수 있을까요? 기존 pull기반으로 비동기 순서를 맞추는 방식의 패러다임에서는 비동기가 복합적으로 존재하면 개발 난이도가 비약적으로 상승합니다.

때문에 이렇게 복합적인 비동기 로직을 처리할 수 있는 새로운 패러다임의 필요성이 부각되었습니다.

반응형 프로그래밍 Deep Dive

반응형 프로그래밍은 변경을 감지하고 전파하고 선언적으로 프로그래밍을 작성한다는 패러다임을 구현하기 위해서 아래와 같은 구조를 갖는다.

addEventListender가 이렇게 동작한다.

DOM에서 사용하는 이벤트 리스너를 등록하고 전달하는 방식 역시 반응형 프로그래밍이다. 이러한 방식으로 인해 웹 프레임워크는 자연스레 반응형 프로그래밍의 구조를 따르게 되었다.

반응형 프로그래밍이 지향하는 것은 모든 스크립트에서 이러한 Event Listener의 관점으로 프로그래밍을 하는 것이다.

왜 Event 방식으로 개발을 하나요?

https://cycle.js.org/streams.html#streams-reactive-programming

제어의 역전

여기 2개의 모듈이 있다. Foo 모듈에서는 네트워크 요청을 받으면 Bar 모듈의 값을 증가시키는 로직이 있다고 상상해보자. Foo 모듈에서는 Bar 모듈에게 영향을 끼치므로 위와 같이 그림을 그려봤다. 코드로 표현하자면 아래와 같다.

// Foo.js

import Bar

function onNetworkRequest() {
  // ...
  Bar.incrementCounter(value);
  // ...
}

이 때, 상태를 변화시키는 로직은 Bar에 존재하는데 동작은 Foo에서 수행한다. 더군다나 Bar에서는 Foo의 존재를 알 수 없다. 이렇게 하면 Bar는 수동적인 모듈이 되고 Foo 모듈과는 강결합되어 Bar에서 자체 상태관리를 못할 뿐더러 수정이 필요한 모든 로직을 Foo에 공개하는 형식으로 만들어야 한다.

어떻게 하면 이러한 문제를 수정할 수 있을까? 이 접근 방식의 대안은 화살표의 방향을 반전시키지는 않고 화살표의 소유권을 반전시키는 것이다.

// Bar.js

import Foo

const incrementCounter = () => { ... }

Foo.onNetworkRequest((event) => {
  // ...
  incrementCounter(event.value);
  // ...
}

이렇듯 모듈과 모듈간의 결합 시 참조의 주체를 바꾸어 사용하는 방법을 제어의 역전(Inversion of Control)이라고 부른다.

이 접근 방식을 통해서 모듈의 상태를 변화하는 로직을 외부에 의존하지 않고 모듈 내부에서 처리함으로써 보다 캡슐화와 느슨한 결합을 하기 용이하도록할 수 있다.

책임의 분리와 비동기 처리

반대로 Foo의 입장에서는 어떨까요? Foo 모듈에서도 로직을 수행하는 부분에 있어서 자유로워졌다. 이제 누구에게 어떻게 데이터를 전달해야할지 신경쓸 필요가 없다. 그저 데이터가 만들어지는 시점에 전달만 하면 된다. 클릭 이벤트라던가 Promise역시 그렇게 동작한다.

const p = new Promise(resolve => {
//...
  
// 필요한 값이 만들어진 시점에 그저 던지면 된다. 누가 받을지 몰라도 된다.
resolve(value)
  
//...
})

// 전달 받는 쪽에서는 실행 시점이나 순서를 몰라도 된다.
p.then(() => {
// ...
})

처음에 언급했던 데이터의 흐름, 변경사항의 전파선언적 반응형 프로그래밍 패러다임의 키워드라고 언급했었다. 복잡한 비동기 프로그래밍도 반응형 프로그래밍을 이용하면 선언적으로 데이터 흐름을 통제할 수 있다.

Reactive First!

이러한 제어의 역전 방식은 이미 DOM의 Event나 Observer 패턴, Pub/Sub 패턴과 같이 오래전부터 사용해오던 방식이었다.

책임의 분리는 주로 미들웨어나 플러그인과 같은 방식에도 이미 자주 사용되던 방식이다.

반응형 프로그래밍은 이러한 관점을 일부가 아닌 모든 스크립트에 적용해서 항상 Reactive를 먼저 고려하자는 패러다임이다.

이러한 방식은 모듈간의 결합이 복잡해질 수록 더 유효하다.

출처: https://medium.com/@thebabscraig/javascript-design-patterns-part-2-the-publisher-subscriber-pattern-8fe07e157213

0개의 댓글