[react] flux 디자인 패턴의 이해1: 데이터 바인딩과 데이터 흐름
flux 디자인 패턴은 react를 공부하는 데 중요한 스탭이지만, useState 사용법 같은 문법에 비해 이해하기 어려운 개념이다.
그 이유는 아래와 같다
- 데이터 바인딩, viewModel 같은 배경 지식 필요(방금 JS 개발 공부하고 넘어온 프론트엔드. 특히 나같은 비전공 취준은 이런 이론적 개념에 약할 거다)
- 양방향 데이터 바인딩과 단방향 데이터 바인딩을 이해해야 하는데, 좋은 글들이 많지만 "구체적으로 이런 거다"라고 나 스스로는 이해하지 못했다.
그래서 이 글을 정리하며 내 식대로 데이터 바인딩, viewModel, 나아가 양방향 데이터 바인딩과 단방향 데이터 바인딩에 대해 이해해보려 한다.
데이터 바인딩이란?
다른 질문을 먼저 해보자. 웹 페이지는 어떻게 데이터를 사용자에게 보여주는가.
MVC 패턴을 가정해보자면
- 사용자가 서버에 페이지 요청
- 요청을 받은 controller에서 model에 데이터를 요청. 반환 받은 데이터를 view에 전달
- controller도 함수. model도 함수. view도 함수라고 생각하자. controller는 요청을 받으면 model을 실행. model은 db에서 데이터를 꺼내와 반환. controller는 반환 받은 데이터를 view 함수에 전달한다는 느낌이다.
- view는 템플릿과 css에 데이터를 넣어준 후 html,css 파일을 생성하여 유저에게 응답한다
* 이건 서버사이드 렌더링 방식의 MVC 패턴이라 말하는 게 좀 더 정확하다. 그런데, 이 맥락에서는 굳이 서버 사이드까지 언급할 필요는 없을 것 같다.
만약 웹페이지가 이렇게만 작동한다면 무척이나 정적이다. 하지만 현대의 웹페이지는 사용자가 다양한 상호작용을 통해 데이터의 수정 요청을 한다. 예컨대, 게임캐릭터를 생성하는 창을 생각해보자(메이플스토리라던지...). 보이는대로만 변화하는 건 이렇다.
- 초기 생성창에서 캐릭터 모델에 빡빡머리로 선택되어 있음
- 유저가 옵션에서 더벅머리를 선택
- 캐릭터 모델이 더벅머리로 바뀜
이런 과정을 구현하려면 아마 바닐라 자바스크립트에서는 이럴 것이다.
- 초기에 전달된 캐릭터 객체에서는 머리 프로퍼티가 빡빡머리 값으로 설정
- 유저가 옵션에서 더벅머리 선택
- 이벤트 핸들러에서 ajax로 해당 선택값을 api 요청
- 컨트롤러는 모델로 개릭터 객체의 머리 값을 변경 후 클라이언트에게 전달
- 클라이언트 브라우저의 자바스크립트(view)는 변경된 데이터 정보를 토대로 캐릭터 모델 리렌더링
여기서 핵심은 model과 view에서의 데이터 변경사항을 사용자 화면의 데이터와 일치시킨다는 점이다.
js는 내부의 상태값을 변경한다고 해서 알아서 뚝딱 ui의 값까지 변경시키지 않는다. 대신, 이를 업데이트해주는 과정이 필요하다.(appendChild 라던지, innerHtml이라던지)
이것을 데이터 바인딩이라 부른다. 데이터 바인딩이란, 서버의 데이터와 클라이언트의 데이터 및 화면, 다르게 말하자면 view 데이터와 model 데이터 중 어느 한쪽의 변동으로 인한 불일치를 다른 한쪽으로 일치시키는 작업이다.
참고. MVC와 view-model, 그리고 데이터 바인딩
서버사이드 렌더링 기반의 MVC 구조에서는 Model= 데이터. 데이터를 CRUD하는 로직, View= 사용자 화면. 화면을 렌더링, 업데이트, Controller = Model에서 전달할 데이터를 View로, 혹은 그 반대로의 연결을 하는 로직.
실제로 데이터 및 상호작용은 M과 V가 담당하고, C는 M과 V를 적절하게 연결해주는 라우팅의 개념이다.
반면, react는 클라이언트 사이드 렌더링이고 view-model 구조를 취한다.
이 때 model = 데이터를 CRUD(rest api의 ajax 요청으로 데이터를 가져오고 변환하는 과정. useEffect로 패칭 날리고 setState 하는 거), view = 데이터를 사용자 화면에 반영하거나 사용자 상호작용을 데이터 변경 요청으로 이행하는 로직이다.
진짜 단순화하면 react의 함수형 컴포넌트에서 jsx는 view. useState, useEffect는 model의 영역이다.
(이벤트 핸들러는 양쪽 다 걸친 느낌)
단방향 데이터 바인딩
흔히 react는 단방향 데이터 바인딩, vue는 경우에 따라 양방향 데이터 바인딩이 가능하다고 한다. 그러면 단방향은 뭐고 양방향은 뭘까.
먼저 짚고 넘어갈 건, react나 vue 같은 JS 프레임워크의 동작 방식 중 하나는 "자동 데이터 바인딩" 이라는 점이다.
이게 무슨 소리인가.
바닐라 자바스크립트는 데이터 바인딩을 완전히 수동으로 해야 한다. 예컨대, input으로 입력한 값을 어떤 div에 출력하고 싶다고 치자. 그러면 input에다 onChange 이벤트를 리슨하고 핸들러에서 event.target.value 같은 식으로 값을 참조하여 어떤 변수에 저장한다. 그리고 그 변수를 타겟 div에다가 innerText 같은 프로퍼티로 반영해야 한다.
반면, react는 useState라는 함수를 사용하면 state와 setState를 반환하는데, setState로 state를 변경하면 알아서 state를 참조하는 html의 값이 변경된다. 즉, append 처럼 수동으로 넣어주는 과정 일부를 자동으로 한다. view-model의 관점에서 보자면, model(state)이 변경되면 자동으로 view로 바인딩된다.
대충 코드는 이런 느낌이다.
export default function App() {
// model
const [input, setInput] = useState("");
// state 변경 로직
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInput((prev) => e.target.value);
};
return (
<div className="App">
<input type="text" onChange={onChange} />
// state가 변경되면 알아서 반영
<p>{input}</p>
</div>
);
}
그런데, 이 로직을 보면 input으로 상호작용 시, input 이라는 상태를 setState로 변경하는 로직은 개발자가 직접 작성해야 한다. 이벤트 핸들러를 걸고, 이벤트 객체를 참조하여 setState를 하는 것들을 말이다. 즉, view => model로의 바인딩은 수동이다.
이걸 단방향 데이터 바인딩이라 부른다. model => view는 자동인데, view => model은 명령형으로 작성해야 하기 때문.
양방향 데이터 바인딩
그러면 양방향 데이터 바인딩은?
둘 다 자동인거다.
아래는 vue의 양방향 바인딩 예시다.
참고로 필자는 vue 알못이라 gpt 도움을 받았다.
하지만 코드를 대충 보면, input에 onChange 핸들러가 없어도 text라는 model로 바인딩되는 것이 보인다.
<template>
<input v-model="text" />
</template>
<script setup>
import { ref } from "vue";
const text = ref(""); // Model (데이터)
</script>
(그러면, 자동 단방향 데이터 바인딩, 자동 양방향 데이터 바인딩이라 부르면 처음 배우는 사람들이 덜 헷갈리지 않을까 생각한다...)
단방향 데이터 흐름? 양방향 데이터 흐름?
아마 어딘가에서 단방향 데이터 흐름이라는 말을 들어봤을 수도 있다. react의 측면에서 이건 데이터가 부모 컴포넌트에서 자식 컴포넌트로 props를 통해서만 움직이는 걸 의미한다. 즉, 부모의 model이 변경되면 자식에게까지 데이터바인딩이 일어난다(리엑트의 리렌더링). 하지만, 자식에게 소속된 model이 변경될때는 부모의 데이터 바인딩이 일어나지 않는다.
근데, 리엑트는 props를 위로 못올리기 때문에 기본 구조가 단방향이나, 양방향 비슷하게 코드를 짜기도 한다.
예컨대, 부모 컴포넌트가 state를 가지고 있고 자식에게 setState를 props로 전달하면 자식에서 setState를 호출하여 자식의 데이터를 부모에게로 전달할 수 있다. 많이 사용하는 패턴이다
그러면 vue는?
기본적으로 단방향 데이터 흐름이지만, emit 이벤트라는 걸 이용해서 자식 컴포넌트에서 부모 컴포넌트로 데이터를 올릴 수 있다.
근데, 이건 위에서도 말했지만, react에서도 원한다면 그렇게 짤 수 있다.
정리
데이터 바인딩
view(사용자 화면. html, css)와 model(데이터. 자바스크립트. react의 state) 중 어느 한쪽의 변동으로 인한 불일치가 생기면 변경 값을 다른 한쪽으로 일치시키는 작업
단방향 데이터 바인딩
model=>view 로의 데이터 바인딩이 개발자의 수동적인 코드 작성 없이 프레임워크가 알아서 한다.
반대로 view => model 로의 데이터 바인딩은 개발자가 수동으로 코드를 작성해야 한다.
따라서 작성 시간이 더 걸리지만 디버깅이 단순해진다.
양방향 데이터 바인딩
model=>view, view=>model 로의 바인딩을 프레임워크가 알아서 한다.
가독성의 이점이 있으나, 데이터 흐름을 추적하고 디버깅하는 게 어려워질 수 있다.
데이터 흐름
컴포넌트 아키텍처에서 데이터가 부모 => 자식 혹은 자식 => 부모로 전달되는 흐름.