state, props의 개념에 대해서 이해하고, 실제 프로젝트에 바르게 적용할 수 있다.
React 함수 컴포넌트(React Function Component)에서 state hook을 이용하여 state를 정의 및 변경할 수 있다.
React 컴포넌트(React Component)에 props를 전달할 수 있다.
이벤트 핸들러 함수를 만들고 React에서 이용할 수 있다.
실제 웹 애플리케이션의 컴포넌트를 보고 어떤 데이터가 state이고 props에 적합한지 판단할 수 있다.
실제 웹 애플리케이션 개발 시 적합한 state와 props의 위치를 스스로 정할 수 있다.
React의 단방향 데이터 흐름(One-way data flow)에 대해 자신의 언어로 설명할 수 있다.
JSX 문법의 기본과 컴포넌트 기반 개발에 대해서 숙지한다.
React Router DOM으로 React에서 SPA(Single-Page Application)을 구현할 수 있다.
state hook을 이용하여, 컴포넌트에서 데이터를 변화시킬 수 있다.
props를 이용하여, 부모 컴포넌트의 데이터를 자식 컴포넌트로 전달할 수 있다.
바람직한 컴포넌트 구조와 state와 props의 위치에 대해 고민한다.
컴포넌트의 속성(property)을 의미.
props는 성별이나 이름처럼 변하지 않는(immutable) 외부로부터 전달받은 값으로, 웹 애플리케이션에서 해당 컴포넌트가 가진 속성에 해당
부모 컴포넌트(상위 컴포넌트)로부터 전달받은 값!
객체 형태이다.
props로 어떤 타입의 값도 넣어 전달할 수 있도록 props는 객체의 형태를 가진다.
읽기 전용이다.
외부로부터 전달받아서 변하지 않는 값이기 때문에 함부로 변경될 수 없다.
클래스 컴포넌트뿐만 아니라 함수형 컴포넌트에서도 사용할 수 있다.
Props는 여러 개 지정할수 있다.
Props 사용하는 3단계!
- 하위 컴포넌트에 전달하고자 하는 값(data)과 속성을 정의한다.
- props를 이용하여 정의된 값과 속성을 전달한다.
- 전달받은 props를 렌더링한다.
예시와 함께 좀 더 자세하게 살펴보자.
우선 <Parent>
와 <Child>
라는 컴포넌트를 선언하고, <Parent>
컴포넌트 안에 <Child>
컴포넌트를 작성한다. 아래 코드와 같이!!
// 부모 컴포넌트에 function Parent() { return ( <div className="parent"> <h1>I'm the parent</h1> // 아래에 만든 자식 컴포넌트를 넣어준다. <Child /> </div> ); }; function Child() { return ( <div className="child"></div> ); };
이제 전달하고자 하는 속성을 정의해보자.
아래 코드처럼 전달하고자하는 속성을 중괄호 {}
로 감싸서 적어주면 끝!
// 기본 형태 <Child attribute={value} /> // text라는 속성을 선언하고 문자열 값을 할당해서 Child 컴포넌트에 전달하는 코드! <Child text={"I'm the eldest child"} /> // Parent 컴포넌트로 부터 전달받은 문자열 값을 React 컴포넌트(현재 Child)에 props로 전달하면, // 문자열= props가 되고 이 props가 필요한 모든 데이터를 가지고 와서 사용(렌더링)할 수 있게 된다! function Child(props) { return ( <div className="child"> // 가져온 props의 text를 사용 중..! <p>안에 값으로 넣어준 것. // 즉 아래 코드는 <p>"I'm the eldest child"</p>와 같다 <p>{props.text}</p> </div> ); };
props를 전달하는 또 다른 방법으로 여는 태그와 닫는 태그의 사이에 value를 넣어 전달하는 방법이 있다.
이 경우 props.children을 이용하면 해당 value에 접근하여 사용할 수 있다.
function Parent() { return ( <div className="parent"> <h1>I'm the parent</h1> // 이렇게 여는 태그, 닫는 태그 사이에 값을 넣고 <Child>I'm the eldest child</Child> </div> ); }; function Child(props) { return ( <div className="child"> // 요로코롬! 사용할 수 있다. <p>{props.children}</p> </div> ); };
변수가 살면서 변할 수 있는 값(mutable). 컴포너트 사용 중 컴포넌트 내부에서 변할 수 있는 값
인간이 살면서 아주 다양한 상태를 겪듯이, 컴퓨터도 상태라는 것이 있다.
예를 들면 쇼핑몰에서 쇼핑을 하면서 장바구니의 목록을 삭제하려고 체크박스를 선택한 상태와, 선택하지 않은 상태. 이 두가지 상태는 true
와 false
로도 구분해볼 수 있겠다.
체크 여부에 따라 구매할 물건의 개수나 구매 금액이 변경되고, 이에 따라 사용자의 화면 또한 달라질 것이다.
이처럼 컴포넌트 내에서 변할 수 있는 값, 즉 상태는 React state로 다뤄야 한다!
React에서 제공하는 state 를 다루는 방법 중 하나!
우선 사용하기 위해서 import로 불러와야 한다.
import { useState } from "react";
이후 useState 를 컴포넌트 안에서 호출한다.
useState 를 호출한다는 것 = "state" 라는 변수를 선언하는 것 으로, 일반적인 변수는 함수가 끝날 때 사라지지만, state 변수는 React에 의해 함수가 끝나도 사라지지 않는다.
function CheckboxExample() { // useState 문법 // const [state 저장 변수, state 갱신 함수] = useState(state 초깃값); // [isChecked, setIsChecked]는 useState 호출시 반환받는 배열로, // 0번째 요소는 현재 상태 변수, 1번째 요소는 이 변수를 갱신할 수 있는 함수! // useState()안에 인자로 넘겨주는 값응 초깃값이다. const [isChecked, setIsChecked] = useState(false); // 1번 //... // useState 구조 분해 할당 예시 const stateHookArray = useState(false); // 2번 const isChecked = stateHookArray[0]; const setIsChecked = stateHookArray[1]; }
<span>{isChecked ? "Checked!!" : "Unchecked"}</span>
setIsChecked
(위 예시에서) 를 호출한다. 시스템의 흐름과 함께 살펴보자. 언제 state 갱신 함수를 호출할까?
사용자가 체크박스 값을 변경하면 onChange
이벤트가 이벤트 핸들러 함수인 handleChecked
를 호출하고, 이 함수가 setIsChecked
를 호출하게 된다.
아래 예시 코드를 보면서 다시 생각해보자.
function CheckboxExample() { const [isChecked, setIsChecked] = useState(false); // handleChecked 함수 // 이 함수는 체크박스에 onChange이벤트가 발생하면 호출된다. const handleChecked = (event) => { // 체크박스가 check되면 isChecked의 상태를 true로 바꿔줌. setIsChecked(event.target.checked); }; return ( <div className="App"> <input type="checkbox" checked={isChecked} onChange={handleChecked} /> <span>{isChecked ? "Checked!!" : "Unchecked"}</span> </div> ); }
React 컴포넌트는 state가 변경되면 새롭게 호출되고, 리렌더링된다.
React state는 꼭! 상태 변경 함수 호출로 변경해야 한다. 강제로 변경을 시도하면 리렌더링이 안 되거나 state가 제대로 변경되지 않기 때문에 하면 안 된다!
React의 개발 방식의 가장 큰 특징은 페이지 단위가 아닌, 컴포넌트 단위로 시작한다는 점
즉, 페이지를 만들기 이전에 페이지를 구성하는 각각의 컴포넌트가 뭐가 있는지 확인하고(컴포넌트 계층 구조로 나누고), 그 컴포넌트를 먼저 만들고 이후에 컴포넌트들을 조립하는, 상향식(bottom-up)으로 앱을 만드는 것. 이것의 가장 큰 장점은 테스트가 쉽고 확장성이 좋다!
컴포넌트를 구분할 때는 단일 책임 원칙에 따른 구분을 하자. 하나의 컴포넌트는 한 가지 일만 한다는 것을 기억하기!
컴포넌트는 컴포넌트 바깥에서 props를 이용해 데이터를 마치 인자(arguments) 혹은 속성(attributes)처럼 전달받을 수 있다.
즉, 데이터를 전달하는 주체는 부모 컴포넌트가 된다. 이는 데이터 흐름이 하향식(top-down) 임을 의미한다.
➡ 단방향 데이터 흐름(one-way data flow)이라는 키워드가 React를 대표하는 설명 중 하나일 정도로 하향식 데이터 흐름 원칙은 매우매우 중요하다!!
또한 컴포넌트는 props를 통해 전달받은 데이터가 어디서 왔는지 전혀 알지 못한다.
모든 데이터를 "상태"로 둘 필요는 없다. 상태는 많아질수록 애플리케이션은 복잡해지기 때문에, 상태는 최소화하는 것이 가장 좋다.
상태가 특정 컴포넌트에서만 유의미하다면, 특정 컴포넌트에만 두면 되니까 크게 어렵지 않지만, 만일 하나의 상태를 기반으로 두 컴포넌트가 영향을 받는다면 이때에는 공통 소유 컴포넌트를 찾아 그곳에 상태를 위치시켜야 한다.