원래는 innerHTML을 사용해야했는데, 이를 사용하면 조금 수정을 할 때도 계속 DOM에 접근하여 성능이 좋지 않았다.
이를 위해 각 노드를 생성, 연결하여 진행했지만, 이렇게 한다면 너무 코드가 길어지고 하나의 노드를 만드는 것에도 복잡하고 불편하다.
이제 react를 사용한다면 js 안에서도 HTML에 바로 접근할 수 있다.
React는 JSX 문법을 사용해 HTML과 유사한 구조를 JavaScript 코드 안에 직접 작성할 수 있도록 지원한다.
덕분에 innerHTML 없이도 UI를 선언적이고 일관성 있게 관리할 수 있으며, DOM에 직접 접근하는 대신, React가 가상 DOM(Virtual DOM)을 통해 필요한 변경 사항만을 효율적으로 반영해 성능을 높인다.
UI 생성
컴포넌트로 다 쪼개기 (기능 별로 다 함수 처리)
function App() { } 만들어서 안에 각 컴포넌트 넣고 마지막에 ReactDOM.createRoot(document.getElementById("root")).render(<App />); 넣기
➡️ App은 리액트에서 최상위 컴포넌트로 사용되는 함수이다.
이 App 컴포넌트를 만들어서 그 안에 다른 컴포넌트를 넣으면, 애플리케이션의 모든 UI를 하나의 컴포넌트로 관리할 수 있다.
App은 보통 애플리케이션의 구조를 정의하고, 최상위에서 필요한 상태나 데이터를 자식 컴포넌트로 전달하는 역할을 한다.
ReactDOM.createRoot(document.getElementById("root")).render(<App />); 는 App 컴포넌트를 <div id="root"> </div> 에 렌더링하도록 지정하는 코드이다.
(render: 컴포넌트를 실제 DOM에 그려서 화면에 나타나도록 하는 과정)
보낼 때는 <Todo itemList={itemList} /> 형식으로 불러오면서 보낼 수 있고, 받을 때는 props로 받으면 된다.
<TodoList itemList={props.itemList}
function Todo(props)로 받은 후 {props.itemList} 로 사용한다. 프로퍼티를 전부 받는 것과 같다.
function Todo(props) {
// {itemList} 로 매개변수 정해서 바로 꺼내쓸 수도 있음--> 구조 분해할당
return (
<div id="main">
<div id="container">
<ul>
<li>
<h2>쇼핑 목록</h2>
<TodoInput />
<TodoList itemList={props.itemList} />
</li>
</ul>
</div>
</div>
);
}
function App() {
// 샘플 목록
const itemList = [
{ _id: 1, title: "두부", done: true },
{ _id: 2, title: "계란", done: false },
{ _id: 3, title: "라면", done: true },
];
return (
<div id="todo">
<Header />
<Todo ⭐️itemList={itemList}⭐️ />
<Footer />
</div>
);
}
위에서 {props.itemList}를 구조 분해 할당으로 받는다고 생각하면 된다. function TodoList({ itemList })로 받은 후 바로 itemList 라는 변수로 사용하면된다.
function TodoList({ itemList }) {
// props 사용해도 됨
const list = itemList.map((item) => (
<TodoItem key={item._id} item={item} />
)); // 아이템 리스트 불러온 후, 각 아이템에 아이디, 아이템 연결해서 데이터 보내기
return <ul className="todolist">{list}</ul>;
}
React의 함수형 컴포넌트에서 상태(state)를 관리할 때 사용한다.
상태는 컴포넌트 내부에서 값이 변할 수 있는 데이터를 의미하며, 이 데이터를 변경하려면 useState를 사용해야 한다.
useState는 컴포넌트 내에서 값을 동적으로 관리하고 싶을 때 사용한다.
예를 들어, 사용자 입력, 클릭한 버튼, 또는 API 호출 결과와 같이 시간이 지남에 따라 변하는 데이터를 처리할 때 사용한다.
useState는 상태 변수와 그 상태를 변경하는 함수를 반환한다.
상태 변수는 컴포넌트 내부에서 값을 추적하는 변수이다.상태 변경 함수는 상태를 업데이트할 때 사용하는 함수이다.💡 결론
즉, useState를 사용하면, 상태가 변경되면 화면이 재렌더링 되기 때문에 동적인 데이터를 쉽게 관리할 수 있고, 컴포넌트의 UI가 자동으로 업데이트된다.
이를 통해 사용자와의 상호작용에 따라 동적으로 변하는 UI를 간편하게 구현할 수 있다.
기존 방식에서는 input에 값을 입력하면 input.value를 직접 받아와서 작업을 했다.
그러나 React에서는 컴포넌트의 state가 변경될 때마다 화면이 다시 렌더링된다.
이 때문에 input 값을 처리할 때는 입력된 값이 state로 관리되고, 값이 변경되면 새로운 state 값으로 input의 value를 업데이트해주는 방식을 사용한다.
즉, input에 값이 입력되면 state 값이 변경되고, 그 변경된 값이 다시 input의 value로 설정되는 순환 구조를 통해 React에서 입력값을 관리한다.
이 방식을 통해 React는 컴포넌트의 상태와 화면을 일관성 있게 유지할 수 있다.
const [title, setTitle] = React.useState('')
function TodoInput() {
const [title, setTitle] = React.useState("");
const addItem = (item) => {
itemList.push(item);
};
const handleAdd = () => {};
const handleKeyUp = (event) => {
if (event.key === "Enter") handleAdd();
};
return (
<div className="todoinput">
<input
type="text"
autoFocus
onKeyup={(event) => handleKeyUp(event)}
value={title}
⭐️onChange={event = > setTitle(event.target.value)}⭐️
/> // 글자가 바뀔 때마다 렌더링 됨
<button type="button" onClick={handleAdd}>
추가
</button>
</div>
);
}
이 함수에서 상태가 바뀌면 화면은 다시 렌더링된다.
❗️ 주의할 점은 전체 화면이 아니라 컴포넌트만 재렌더링된다는 것이다.
이렇게 컴포넌트를 잘 쪼개면, 상태가 변경될 때 필요한 부분만 렌더링하게 되어 성능을 최적화할 수 있다.
예전 JavaScript에서는 innerHTML = ''처럼 DOM을 직접 수정해서 input을 초기화했다.
하지만 React에서는 상태(state)를 사용하여 이를 처리한다.
React에서는 setTitle('')처럼 상태를 변경하는 함수(setTitle)를 사용하여 input의 값을 초기화한다. 이렇게 하면 React가 상태 변경을 감지하고, 화면을 자동으로 재렌더링해서 input을 업데이트해준다.
즉, React에서는 DOM을 직접 변경하는 대신, 상태를 관리하고 상태가 바뀌면 React가 자동으로 화면을 업데이트해준다. 그래서 innerHTML = ''과 같은 직접적인 DOM 수정은 필요하지 않다.