WEB지식

[상태관리와 반응형 프로그래밍]

claire 2022. 5. 12. 01:57

상태란? 

변화하는 데이터다. UI에 동적으로 표현되는 데이터, 사용자의 액션에 따라 변경될 수 있는 컴포넌트의 부분을 나타내는 JS의 객체라고 볼 수 있다. 

상태 관리란? 

여러 컴포넌트 간의 데이터 전달과 이벤트 통신을 한 곳에서 관리하는 것을 의미한다. 앞서 말한 상태, 즉 계속 변화하는 데이터를 알맞게 관리하기 위해 나온 개념.

상태가 변경되면서 UI가 변경된다. 

상태 관리가 왜 필요해? 

ui는 사용자와 끊임없이 상호작용을 한다. 

이전에 사용자들은 잦은 페이지 이동이 있는 사이트를 사용했었다. 페이지가 바뀔 때마다 서버에서 데이터를 가져오기 때문에 매번 페이지가 다시 그려진다. 

그런데 지금의 사용자들은 이전과 같이 페이지 전환이 많은 웹 페이지를 사용하기를 원하지 않는다. 

데이터가 바뀌어도 페이지가 다시 렌더링 되는 것을 원하지 않고, 변화된 부분만 쏙 바뀌기를 원한다. 

기술이 발전하면서 사용자의 입맛 또한 함께 바뀌었다. 

페이스북에서 좋아요를 누르면 페이지가 새로고침되지 않고 실시간으로 반영이 되고, 인스타그램에서 누군가를 팔로우하면 그 사람의 팔로우 수가 바로 반영이 된다. 

 

또 다른 이유가 있다. 

상태들이 굉장히 복잡하기 때문이다. 만약 한 웹페이지에 상태가 하나면 굳이 관리할 필요가 없다. 하지만 이제 상태가 많아지고, 그 상태들이 서로 복잡하게 얽혀있다면 그 상태가 어떻게 상호 간에 의존을 하고 있는지, 그리고 상태들이 페이지 내부에서 어떻게 흘러가고 그에 따라 UI가 어떻게 변하는지 알아차리기가 굉장히 어렵다. 

게다가 이러한 상태 값들이 비동기적이라면 끔찍하다. 상태를 관리하기가 더 힘들어질것이다. 

이렇게 하나의 페이지에서도 다양한 상태들이 존재하고, 어떻게 변화하는지 제대로 알고 있어야 하기 때문에 이를 효과적으로 관리할 필요가 생긴 것이다.  

 

우리는 주로 컴포넌트를 기반으로 프로젝트를 만들게 된다. 이런 컴포넌트들은 서로 데이터를 공유한다. 데이터가 연결되어있는 경우가 많다. 컴포넌트 간에 통신을 할 때나 상호 간의 데이터 전달을 조금 더 자연스럽게 관리할 필요성이 대두되면서, 상태의 관리에 대한 중요성도 높아졌다. 

따라서 상태관리는 상태가 많은, 복잡한 중대형 규모의 웹 애플리케이션에서 빛을 발한다. 앱에 규모가 커지면, 데이터 이동이 빈번해지면서 중간에 거쳐야 할 컴포넌트들이 많아지고, 데이터 이동이 많아지면 컴포넌트 간의 데이터 흐름을 파악하기가 힘들어진다. 이런 문제를 해결하기 위해 상태라는 것이 필요하다. 

 

상태 관리와 관련된 다양한 패턴

 

상태를 어떻게 관리할 것인지 대해 멋진 천재 개발자들이 미리 어떻게 할 것인지 다 생각하고 고민해놓은 것. 

 

여기서 잠깐!!!

-> 반응형 프로그래밍이란?

상태라는 것은 변하는 데이터. 데이터가 변화하면 그 데이터를 사용하는 측에서도 업데이트가 같이 되어야 한다. JS 언어 자체가 반응형 프로그래밍 언어가 아니기 때문에 개발자가 일일이 만들어줘야 한다. 

이런 귀찮은 상태 업데이트에 관한 일들을 쉽게 해주는 게 바로 반응형 프로그래밍이라 보면 된다. 

데이터가 변화하면 그 데이터가 변함에 따라서 알아서 반응을 하는 프로그래밍을 반응형 프로그래밍이라고 한다. 

만약 반응형 언어였다면 price가 2000으로 갱신되었을 때 total은 4000이 나왔을 것이다. 

 

반응형 프로그래밍은 데이터 스트림과 변화의 전파에 관련된 선언형 프로그래밍 패러다임이다. 

스트림이란 (시간 순으로 발생하는 이벤트의 나열). 반응형 프로그래밍에서는 모든 데이터를 스트림으로 본다. 

데이터 생산자가 하나의 타임라인에서 이벤트를 통해 발생한 자신의 데이터를 생산하고 지나간다. 그리고 이걸 바라보는 관찰자는 최종으로 나오는 데이터를 가지고 자신의 작업을 수행한다. 이런 스트림에는 그럼 어떤 이벤트들이 들어갈까? 마우스 클릭 이벤트, 키보드 이벤트뿐 아니라 http 요청, 알림 변수의 변화 같은 것들이 데이터 스트림에 다 들어가게 된다. 

이 스트림은 값, 에러, 완료 신호를 만들어낼 수 있다. 

 

선언형 프로그래밍은 어떻게 할 건지 말해주는 것이 아니라 무엇을 할 건지 말해주는 것이다.  명령형은 과정 하나하나 설명해주는 것이고, 선언형 프로그래밍은 목표만 명시. 

반응형 프로그래밍은 데이터가 변경이 될 때마다 이벤트를 발생시켜서 바뀐 데이터를 지속적으로 전달하는 목표를 가진 프로그래밍이라고 할 수 있다. 

반응형 프로그래밍의 핵심은 프로그램이 외부 환경과 어떻게 커뮤니케이션 하는가에 있다고 할 수 있다. 

프로그램이 외부 환경과 커뮤니케이션 하는 방법은 pull과 push이다. 

데이터 생산자와 데이터 소비자. 

push와 pull은 제어권을 가진 자 입장에서 pull과 push이다. 

데이터 생산자는 외부 환경. 

데이터 소비자는 프로그램.

pull은 외부환경에 직접 명령을 하고 응답이 오기까지 기다린다. 

push 방식은 데이터 생산자가 제어권을 가진다. 외부에서 프로그램으로 요청을 밀어 넣는다. 외부에서 응답이 오면 그때그때 반응한다. 외부 환경에 제어권을 넘긴다. 

비동기 처리에 유리하다. 

실제로 반응형 프로그래밍을 작성하려면 까다로운데 리액트는 반응에 관련된 내용들을 만들고 숨겨버렸다.

따라서 리액트를 사용하면 주어진 데이터를 가지고 어떻게 렌더링 할까만 신경 쓰면 된다. 

리액트는 정말 반응형일까? NO

리액트는 pull 방식을 내부적으로 사용하기 때문이다. pull방식을 사용해서 마치 push를 사용하는 것처럼 구현을 해놨다. 

 

<반응형 프로그래밍의 기반이 되는 패턴>

  •  Observer Pattern

객체 상태를 관찰하는 관찰자들이 있고, 객체를 구독하고 객체의 상태 변화가 있을 때마다 메서드를 통해서 객체가 목록에 등록된 관찰자들한테 통지하는 패턴을 말한다. 객체는 등록을 요청한 관찰자들을 배열로 가지고 있다. 

state가 publish되면 관찰자에게 알려줘야 한다. 자신이 변한 값인 state를 포함해서 통보를 해준다. 옵저버한테 전달되는 방식을 fire라고 한다. 전달받은 옵저버는 객체로부터 받은 데이터로 각자 일처리를 하게 된다. 만약 옵저버가 더이상 원하지 않으면 구독을 알아서 해지하면 된다. 

옵저버 패턴은 이벤트 핸들러에서 사용된다. 이벤트 핸들러가 옵저버 역할을 하고, 이벤트는 구경을 당하는 객체이다. 이벤트를 등록만 해주었을 뿐인데 나중에 이벤트가 발생이 되었을 때 우리한테 알려준다. 내부에 옵저버 패턴이 구현이 되어있기 때문에 가능하다. 옵저버 패턴은 실시간으로 한 객체의 변경 사항을 다른 객체에 전파를 해서 객체 간의 의존성을 제거할 수 있다는 장점이 있다. 

  • pub/sub 패턴(publish/subscribe 패턴)

이것은 옵저버 패턴의 변화된 형태라고 볼 수 있다. 기반은 옵저버 패턴과 동일하다. 

pub/sub 패턴은 기본 옵저버 패턴과 달리 중간에 이벤트 관리자를 하나 둔다. pub/sub 패턴에서는 구독자를 바로 객체에 등록하는 게 아니라 중간에 이벤트 관리자를 구독한다. 마찬가지로 객체도 변화하면 바로 구독자한테 퍼블리시를 하는 게 아니라 중간에 그 이벤트 채널을 통해서 구독자에게 변화된 내용을 전달한다. 

구독자와 객체가 직접적으로 서로 연락하는 것이 아니라 서로 간의 결합도가 낮아진다. 

메시지 브로커로 메시지 큐를 많이 사용하기 때문에 비동기 워크플로우가 된다. 서로 각자 할 일을 하면서 응답을 받을 수 있다. 반응형 프로그래밍에 딱 알맞은 패턴이라 할 수 있다. 

 

  • 프록시 패턴

프록시는 대리라는 뜻.

프록시 패턴은 하나의 객체가 프록시 역할을 수행해서 상황에 따라 다른 객체에 접근하게 해 주거나 다른 함수를 호출하게 해주는 패턴이다. 

프록시 패턴에 대해 알기 위해선 프록시 객체에 대해 알아야 한다. 프록시 객체는 프록시 패턴을 사용할 때 실제 객체를 대신하는 객체를 말한다. 

프록시는 기존의 state를 변경시키지 않으면서 그 state 객체를 감싸서 사용할 수 있게 해 준다. 

리액트의 이벤트에서 프록시 패턴을 사용하고 있다.   

 

출처 : https://www.youtube.com/watch?v=alsCMx6vpG4&list=PLgXGHBqgT2TvpJ_p9L_yZKPifgdBOzdVH