
데이터 바인딩이란 화면에 있는 객체와 데이터를 일치시키는 것을 말한다.
양방향 바인딩을 지원하는 Vue나 Angular 의 경우, 사용자의 입력 값이 곧바로 코드 상의 변수에 바인딩될 수 있지만
단방향 바인딩을 지원하는 React의 경우, 적절한 Event를 통해서만 코드상 변수에 데이터 값이 담긴다.
React와 Vue를 이용해 단방향, 양방향 데이터 바인딩을 비교해보자.
React는 props와 state가 계층 구조 아래로 흐르면서 올바르게 렌더링된다.
하지만 사용자 입력에 따라 state를 변경하려면 다른 방식으로 데이터가 흐르도록 지원해야 한다.React는 이 데이터 흐름을 명시적으로 만들지만, 양방향 데이터 바인딩보다 약간 더 많은 타이핑을 필요로 한다.
입력 창에서 무언가를 입력하거나 상자를 체크하려고 하면 React가 입력을 무시하는 것을 볼 수 있다.
input의 value를 설정되지 않으면 입력은 변경되지 않는다.사용자가 입력을 변경할 때마다 변경 사항을 반영하도록 만들고 싶다면 내부에서 이벤트 헨들러를 추가하고 onChange 함수를 통해 부모 상태를 설정하면 된다.
공식 문서에서도 말하는 바와 같이 React에서는 데이터가 부모 컴포넌트에서 자식 컴포넌트로, 즉 위에서 아래로만 흐른다.
React는 상태(state)와 속성(props)을 통해 데이터가 전달되며, 데이터 변경은 주로 상태 업데이트를 통해 이루어진다고 할 수 있다.
이를 확인할 수 있는 간단한 TODO App 예시를 만들어보자.
TODO를 생성하는 input이 있는 부모 컴포넌트이다.
// 부모 컴포넌트
import React, { useState } from 'react';
import TodoList from './TodoList';
function TodoApp() {
// 상태 관리
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState('');
// 입력 필드의 변경을 감지하여 inputValue 상태를 업데이트
const onChangeInput = (event) => {
setInputValue(event.target.value);
};
// 입력된 값을 todos 리스트에 추가
const onClickAddTodo = () => {
if (inputValue.trim()) {
setTodos([...todos, inputValue]);
setInputValue('');
}
};
// 선택된 항목을 todos 리스트에서 삭제
const onClickDeleteTodo = (index) => {
setTodos(todos.filter((_, i) => i !== index));
};
return (
<div>
<h1>Todo App</h1>
<input
type="text"
value={inputValue}
onChange={onChangeInput}
placeholder="Enter a todo"
/>
<button onClick={onClickAddTodo}>Add Todo</button>
<TodoList todos={todos} onClickDeleteTodo={onClickDeleteTodo} />
</div>
);
}
export default TodoApp;
부모 컴포넌트 (TodoApp)에서 useState hook을 사용하여 todos 와 inputValue를 관리한다.
onChangeInput 함수를 통해서 입력 필드의 변경을 감지하여 inputValue 상태를 업데이트한다.
Add Todo 버튼을 클릭하면 onClickAddTodo 함수를 통해 todos에 새롭게 입력된 inputValue를 추가한다.
TODO 목록을 렌더링 하는 자식 컴포넌트이다.
// 자식 컴포넌트
import React from 'react';
function TodoList({ todos, onClickDeleteTodo }) {
return (
<div>
<h2>Todo List</h2>
<ul>
{todos.map((todo, index) => (
<li key={index}>
{todo} <button onClick={() => onClickDeleteTodo(index)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
export default TodoList;
부모 컴포넌트는 todos와 onClickDeleteTodo 함수를 자식 컴포넌트(TodoList) 에 props로 전달한다.
자식 컴포넌트는 props로 전달받은 todos를 렌더링하고, 각 항목에 대해 삭제 버튼을 추가하여 삭제 이벤트를 처리한다.
이때 삭제 버튼을 클릭하면 todos의 상태를 변경하게 된다.
React에서는 이러한 단뱡향 데이터 바인딩 과정을 통해서 부모 컴포넌트는 상태를 관리하고,
자식 컴포넌트는 해당 상태를 렌더링하고 조작할 수 있게 된다.
이렇게 데이터의 흐름이 한 방향으로만 이루어지는 방식을 단방향 데이터 바인딩이라고 한다. 주로 Model(데이터 소스)에서 View(화면)로 데이터가 전달된다.
사용자가 데이터를 변경해도 Model에 직접적으로 반영되지 않기 때문에 예측 가능하고 명확한 데이터 흐름을 제공한다.
하지만 복잡한 상태 관리를 위해서는 더 많은 코드가 필요할 수 있다는 단점이 있다.
만약 props drilling이 너무 깊어진다면 Context API나 상태 관리 라이브러리를 사용하는 것이 좋다.
그렇다면 이제 Vue에서는 데이터 바인딩을 어떻게 하는지에 대해 알아보자.
Vue는 양방향 데이터 바인딩을 지원한다.
Vue의 v-model 디렉티브를 사용하면 데이터가 Model과 View 간에 양방향으로 자동으로 동기화된다. 이로 인해 폼 입력과 같은 UI 요소의 상태 관리를 더 쉽게 할 수 있다.
같은 TODO App을 만들어서 Vue의 데이터 바인딩을 확인해보자.
// 부모 컴포넌트
<template>
<div id="app">
<h1>Todo App</h1>
<input
v-model="inputValue"
placeholder="Enter a todo"
@keyup.enter="addTodo"
/>
<button @click="addTodo">Add Todo</button>
<TodoList :todos="todos" @delete-todo="deleteTodo" />
</div>
</template>
<script>
import TodoList from "./TodoList.vue";
export default {
name: "TodoApp",
components: {
TodoList,
},
data() {
return {
inputValue: "",
todos: [],
};
},
methods: {
addTodo() {
if (this.inputValue.trim()) {
this.todos.push({ id: Date.now(), text: this.inputValue });
this.inputValue = "";
}
},
deleteTodo(id) {
this.todos = this.todos.filter((todo) => todo.id !== id);
},
},
};
</script>
<style scoped>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
margin-top: 60px;
}
</style>
부모 컴포넌트(TodoApp.vue)에서 v-model 디렉티브를 사용하여 inputValue 데이터 속성과 입력 필드 간에 양방향 데이터 바인딩을 설정한다.
사용자가 입력 필드에 텍스트를 입력하면 inputValue 데이터 속성이 자동으로 업데이트된다.
// 자식 컴포넌트
<template>
<div>
<h2>Todo List</h2>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
<button @click="$emit('delete-todo', todo.id)">Delete</button>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "TodoList",
props: {
todos: {
type: Array,
required: true,
},
},
};
</script>
<style scoped>
ul {
list-style-type: none;
padding: 0;
}
li {
margin: 10px 0;
padding: 10px;
background-color: #f9f9f9;
border: 1px solid #ddd;
}
button {
margin-left: 10px;
}
</style>
양방향 데이터 바인딩은 데이터의 흐름이 양방향으로 이루어지는 방식이다.
부모와 자식 모두 데이터를 직접 변경할 수 있다.
즉, Model(JavaScript)에서 View(HTML)로의 데이터 변경이 반영되고, View에서의 변경도 Model에 자동으로 반영된다. 이는 데이터와 UI 간의 상호작용을 더 쉽게 만들어준다.
Vue는 양방향 데이터 바인딩을 지원하여 UI와 데이터 모델 간의 상호작용을 간편하게 만든다.
하지만 대규모 애플리케이션에서는 데이터 흐름이 복잡해질 수 있다.

이 내용을 기반으로 React 와 Vue 의 데이터 바인딩을 비교해보면 다음과 같다.
| 특징 | React | Vue |
|---|---|---|
| 데이터 바인딩 방식 | 단방향 데이터 바인딩 | 양방향 데이터 바인딩 |
| 상태 관리 | useState, useReducer 등 훅을 사용 | data 속성으로 상태 관리 |
| 컴포넌트 간 데이터 전달 | props 와 state를 사용한 단방향 데이터 흐름 | props와 emit으로 데이터 전달, v-model을 사용한 양방향 바인딩 |
| 이벤트 핸들링 | 명시적으로 이벤트 핸들러를 정의 | v-model과 같은 디렉티브로 간편한 데이터 동기화 |
| 장점 | 예측 가능성, 명확한 데이터 흐름 | 간편한 상태 관리, 직관적인 양방향 바인딩 |
| 단점 | 코드 복잡성 증가 가능, 추가적인 상태 관리 필요 | 대규모 애플리케이션에서 데이터 흐름이 복잡해질 수 있음 |