3장은 리액트의 핵심 기능과 개념을 설명한다.
이 장의 도입부에서 프레임워크나 라이브러리를 온전히 이해하지 못한 채로 프로젝트를 진행하면 기술 부채가 늘어난다고 했다.
기술 부채, 내가 이 책을 산 이유다.
나는 리액트를 접하고 개발을 해오면서 기능은 구현할 수 있었지만, 확신이 없었다.
그러다 보니 어느 순간 성장이 더뎌진 것을 느꼈고, 리액트를 제대로 공부해야겠다는 생각이 들었다.
기술 부채를 갚아야 할 때가 온 것이다.
UI 데이터
리액트는 UI용 라이브러리다.
UI 라이브러리는 UI 데이터를 관리하는 방법을 제공한다.
UI 데이터는 UI에 보이는 데이터다.
로그인할 때 우리가 입력하는 아이디, 비밀번호 등도 UI 데이터다.
이 UI 데이터가 변경되면 우리가 보는 화면도 그에 맞게 변해야 한다.
원래는 UI 데이터가 변경될 때마다 직접 돔 요소를 수정해야 하지만, 이는 코드를 복잡하게 만든다.
비즈니스 로직에 돔 요소를 조작하는 코드가 추가되기 때문이다.
그래서 리액트가 등장했다.
리액트는 UI 데이터가 변경되면 컴포넌트 함수를 이용해 자동으로 화면을 변경해준다.
이게 리액트에 가장 중요한 기능이다.
명령형(imperative) 프로그래밍 <> 선언형(declarative) 프로그래밍
먼저 UI 라이브러리를 사용하지 않고 HTML, JS로만 간단한 메모 웹을 만들어보자.
<html>
<body>
<div>
<h3>메모</h3>
<ul id="list"></ul>
<input type="text" id="desc" />
<button onclick="add()">추가</button>
</div>
<script>
const memoList = [];
let currentId = 1;
function add() {
const inputEl = document.querySelector("#desc");
const memo = { id: currentId, desc: inputEl.value };
memoList.push(memo);
currentId++;
const listEl = document.querySelector("#list");
listEl.appendChild(makeTodoEle(memo));
}
function makeTodoEle(memo) {
const itemEl = document.createElement("li");
const idEl = document.createElement("span");
const descEl = document.createElement("span");
idEl.innerHTML = memo.id + "번째 ";
descEl.innerHTML = memo.desc;
itemEl.appendChild(idEl);
itemEl.appendChild(descEl);
return itemEl;
}
</script>
</body>
</html>
로직과 UI 코드가 복잡하게 얽혀있다.
다음은 리액트로 만들어보자.
import React, { useState } from "react";
export default function Memo() {
const [desc, setDesc] = useState("");
const [currentId, setCurrentId] = useState(1);
const [memoList, setMemoList] = useState([]);
const add = () => {
const memo = { id: currentId, desc };
setCurrentId(currentId + 1);
setMemoList([...memoList, memo]);
};
return (
<div>
<h3>메모</h3>
<ul>
{memoList.map((memo) => {
return (
<li key={memo.id}>
<span>{memo.id}번째 </span>
<span>{memo.desc}</span>
</li>
);
})}
</ul>
<input
type="text"
value={desc}
onChange={(e) => setDesc(e.target.value)}
/>
<button onClick={add}>추가</button>
</div>
);
}
리액트는 로직과 UI 코드가 분리되어 있어 보기 편하다.
그리고 전자와 달리 돔 요소를 조작하는 코드가 없다.
UI 데이터 desc, currentId, memoList가 변경되면, 리액트는 함수 컴포넌트를 실행함으로써 화면을 갱신할 뿐이다.
우리는 리액트가 화면을 어떻게 갱신하는지 알 필요가 없다.
전자는 화면에 어떻게 그리는지를 집중했고, 이를 명령형 프로그래밍이라 부른다.
후자는 화면에 무엇을 그리는지를 집중했고, 이를 선언형 프로그래밍이라 부른다.
리액트는 화면을 그리는 모든 코드를 컴포넌트 함수에 선언형으로 작성하도록 해서 비지니스 로직에 집중할 수 있게 만들어준다.
상탯값
리액트의 UI 데이터는 두가지다.
1. 상탯값 - 컴포넌트 내부에서 관리된다.
2. 속성값 - 부모 컴포넌트에서 내려 준다.
UI 데이터를 상태값이나 속성값으로 관리하지 않는다면, 데이터가 변경되어도 화면이 갱신되지 않는다.
// 속성값과 상태값을 사용하지 않을 때
export function Count1() {
let num = 0;
const count = () => {
num += 1;
console.log(num);
};
return (
<div>
<div>{num}</div>
<button onClick={count}>1 증가</button>
</div>
);
}
버튼을 누르면 숫자가 1씩 증가하는 컴포넌트다.
UI 데이터를 상태값이나 속성값으로 관리하지 않아서, 버튼을 눌러도 숫자가 증가하지 않았다.
상태값을 만들려면 리액트의 useState 훅을 사용해야한다.
useState를 사용한 코드로 바꿔보자.
// 상태값으로 UI 데이터 관리
import React, { useState } from 'react';
export function Count2() {
const [num, setNum] = useState(0);
const count = () => {
setNum(num + 1);
console.log(num);
};
return (
<div>
<div>{num}</div>
<button onClick={count}>1 증가</button>
</div>
);
}
useState를 사용하여 UI 데이터를 상탯값으로 관리하니, 데이터가 변경되었을 떄 화면도 잘 갱신되었다.
useState는 배열을 반환하는데, 첫번째 요소는 상탯값이고 두번째 요소는 상탯값을 변경하는 함수다.
변경 함수의 네이밍은"set상탯값"을 카멜 표기법으로 작성한다.
다른 이름으로 해도 동작은 하지만, 리액트 개발자들의 컨벤션이므로 지켜주자.
선언할 때는 위의 코드처럼 배열 비구조화 문법을 사용한다.
배열 비구조화 문법은 아래에 나와 있다.
속성값
속성값은 부모 컴포넌트에서 전달해주는 데이터다.
UI데이터를 속성값으로 관리해보자.
// 속성값으로 UI 데이터 관리
export function CountParent() {
const [num, setNum] = useState(0);
const count = () => {
setNum(num + 1);
console.log(num);
};
return (
<div>
<CountChild num={num} />
<button onClick={count}>1 증가</button>
</div>
);
}
export function CountChild(props) {
return <div>{props.num}</div>;
}
위와 똑같이 숫자를 1씩 증가시키는 컴포넌트다.
여기선 CountParent 컴포넌트에서 CountChild 컴포넌트로 num을 내려준다.
리액트에서 내려준 속성값은 객체로 전달된다. (props는 파라미터여서 아무거나 사용해도 된다.)
객체로 전달되기 때문에 나는 주로 비구조화를 해서 사용한다.
// props 비구조화
export function CountChild({ num }) {
return <div>{num}</div>;
}
아래는 속성값(props) 사용법에 대한 공식문서다.
상태값과 마찬가지로 속성값도 변경되면 함수 컴포넌트가 실행되어 화면이 자동 갱신된다.
또 한가지 리액트 특징이 있는데, 바로 부모 컴포넌트가 렌더링되면 자식 컴포넌트도 자동 렌더링된다는 것이다.
따라서 부모 컴포넌트가 렌더링되면 props가 변경되지 않아도 자식 컴포넌트는 자동 렌더링 된다.
속성값은 불변 변수여서 변경할 수 없다.
상탯값은 불변 변수가 아니라서 변경할 수는 있지만, 불변 변수로 관리하는게 좋다.
const [var, setVar] = useState();
// 상탯값은 직접 수정 가능하지만,
// 리액트는 변경된 사실을 모르기 때문에 화면이 갱신되지 않는다.
var = 1;
// 변경값이 원래값과 갔다면 변경 함수를 사용해도 갱신되지 않는다.
setVar(var);
// 화면이 갱신된다.
setVar(var * 2);
'프론트엔드 > 실전 리액트 프로그래밍' 카테고리의 다른 글
[스터디 with 실전 리액트 프로그래밍] 11편 - 훅(Hook) (0) | 2022.08.16 |
---|---|
[스터디 with 실전 리액트 프로그래밍] 10편 - 가상돔 (0) | 2022.08.15 |
[스터디 with 실전 리액트 프로그래밍] 8편 - 제너레이터 (0) | 2022.08.12 |
[스터디 with 실전 리액트 프로그래밍] 7편 - 프로미스 (0) | 2022.08.06 |
[스터디 with 실전 리액트 프로그래밍] 6편 - ES6+에서 객체와 배열 (0) | 2022.08.05 |