React State & Props

HanSungUk·2022년 6월 12일
0

React

목록 보기
3/13
post-thumbnail

React SPA

현재 코드스테이츠 강의를 통해 프론트엔드를 학습하고 있습니다.
본 포스트는 해당 강의에 대한 내용 정리를 목적으로 합니다.

학습목표

  • state, prop의 개념에 대해서 이해하고, 실제 프로젝트에 바르게 적용할 수 있다.
  • 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의 위치에 대해 고민한다.

1. State & Props

React State란 컴포넌트의 사용 중 컴포넌트 내부에서 변할 수 있는 값을 의미합니다.
Props는 외부로부터 전달 받은 값을 의미하고 State는 내부에서 변화하는 값을 의미합니다.

예를 들어 이름, 성별,나이, 현재 사는 곳, 취업 여부, 결혼/연애 여부 와 같은 데이터를 Props와 State로 구분한다면 Props는 이름, 성별 이 적합하고,
State는 나이, 현재 사는 곳, 취업 여부, 결혼/연애 여부와 같은 데이터가 적합하다고 할 수 있습니다.

2. Props

  • 특징
    • 컴포넌트의 속성(property)를 의미합니다.
      Props는 성별이나 이름처럼 변하지 않은 외부로부터 전달받은 값 으로 웹 애플리케이션에서 해당 컴포넌트가 가진 속성에 해당합니다. 속성은 여러 개 지정할 수 있습니다.
    • 부모 컴포넌트(상위 컴포넌트)로부터 전달받은 값입니다.
      React 컴포넌트는 JavaScript 함수와 클래스로, props를 함수의 전달인자(arguments)처럼 전달받아 이를 기반으로 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환합니다. 따라서 컴포넌트가 최초 렌더링 될 때 화면에 출력하고자 하는 데이터를 담은 초깃값으로 사용할 수 있습니다.

    ES6 class 문법으로도 컴포넌트를 만들 수 있고, 이를 클래스 컴포넌트라고 합니다. 이와 대조해서 함수 문법을 이용하여 만든 컴포넌트를 말 그대로 함수 컴포넌트라고 부릅니다. Hook이 나오기 전에는 state는 클래스 컴포넌트에만 다룰 수 있었으나, Hook이 나오면서 함수 컴포넌트도 state를 다룰 수 있게 되었습니다.

    • 객체 형태입니다.
      props로 어떤 타입의 값도 넣어 전달할 수 있도록 props는 객체의 형태를 가집니다.
    • props는 읽기 전용입니다.
      props는 성별이나 이름처럼 외부로부터 전달받아 변하지 않는 값(immutable)입니다. 그래서 props는 함수로 변경될 수 없는 읽기 전용(read-only)객체 입니다. 함부로 변경되지 않아야 하기 때문입니다.

읽기 전용 객체가 아니라면 props를 전달받은 하위 컴포넌트 내에서 props를 직접 수정 시 props를 전달한 상위 컴포넌트의 값에 영향을 미칠 수 있게 됩니다. 즉, 개발자가 의도하지 않은 side effect가 생기게 되고 이는 React의 단방향, 하향식 데이터 흐름 원칙에 위배됩니다.

  • 사용법
    props를 사용하는 방법은 아래와 같이 3단계 순서로 나눌 수 있습니다.
    1. 하위 컴포넌트에 전달하고자 하는 값(data)과 속성을 정의한다.
    2. props를 이용하여 정의된 값과 속성을 전달한다.
    3. 전달받은 props를 렌더링한다.

위 단계에 맞추어 props를 사용하기 위해 우선 <Parent><Child>라는 컴포넌트를 선언하고, <Parent>컴포넌트 안에 <Child>컴포넌트를 작성합니다.

function Parent() {
  return (
    <div className="parent">
      <Child text={"I'm the eldest child"} />
      <Child />
    </div>
  );
};

function Child(props) {
  return (
    <div className="child">
    <p>{props.text}</p>
	</div>
  );
};

컴포넌트를 만든 후 전달하고자 하는 속성을 정의합니다.
HTML에서 속성과 값을 할당하는 방법은 다음과 같습니다.

<a href = "www.naver.com">Click me to visit naver</a>

React에서 속성 및 값을 할당하는 방법도 이와 유사합니다. 다만, 전달하고자 하는 값을 중괄호({})를 이용하여 감싸주면 됩니다.

<Child attribute={value} />

위 방법을 이용해서 text라는 속성을 선언하고, 이 속성에 문자열을 할당하여 <child>컴포넌트에 전달할 수 있습니다.

<Child text={"I'm the eldest child"} />

이제 <Parent>컴포넌트에서 전달한 "I'm the eldest child"라는 문자열을 <Child>컴포넌트에서 받을 수 있습니다.
함수에 인자를 전달하듯이 React 컴포넌트에 props를 전달하면 되고, 이 props가 필요한 모든 데이터를 가지고 오게 됩니다.

// <child> 컴포넌트에서 props 매개변수 사용 예시
function Child(props) {
  return (
    <div className="child"></div>
    <p>{props.text}</p>
  );
};

props를 전달받은 후 마지막으로 이 props를 렌더링하면 됩니다.
props를 렌더링하려면 JSX안에 직접 불러서 사용하면 됩니다. 다만, props는 객체라고 하였고, 이 객체의 {key: value}<Parent>컴포넌트에서 정의한 {attribute: value}의 형태를 띠게 됩니다.
따라서 JavaScript에서 객체의 value에 접근할 때 dot notation을 사용하는 것과 동일하게 props의 value 또한 dot notation으로 접근할 수 있습니다.
아래와 같이 props.text를 JSX에 중괄호와 함께 작성하면 잘 작동합니다.

// <child> 컴포넌트에서 props.text 렌더링 예시
function Child(props) {
  return (
    <div className="child">
    	<p>{props.text}</p>
    </div>
  );
};
  • props.children
    props를 전달하는 또 다른 방법으로 여는 태그와 닫는 태그의 사이에 value를 넣어 전달하는 방법이 있습니다. props.children을 이용하면 해당 value에 접근하여 사용할 수 있습니다.
function Parent(){
	return (
    	<div className="parent">
      		<Child>I'm the eldest child</Child>
    	</div>
    );
};

function Child(props){
	return (
    	<div className="child">
      		<p>{props.children}</p>
		</div>
    );
};

3. State

컴포넌트 내에서 변할 수 있는 값, 즉 상태는 React state로 다뤄야 합니다.

  • useState 사용법
    React에서는 state를 다루는 방법 중 하나로 useState라는 함수를 제공합니다.
    • useState를 이용하기 위해서는 React로부터 useState를 불러와야 합니다. import키워드로 useState를 불러옵니다.
// useState 불러오기
  import {useState} from "react";

이후 useState를 컴포넌트 안에서 호출해 줍니다. useState를 호출한다는 것은 "state"라는 변수를 선언하는 것과 같으며, 이 변수의 이름은 아무 이름으로 지어도 됩니다. 일반적인 변수는 함수가 끝날 때 사라지지만, state 변수는 React에 의해 함수가 끝나도 사라지지 않습니다.

function CheckboxExample() {
	// 새로운 state 변수를 선언하고, 여기서는 이것을 ischecked라고 부르겠습니다.
  const [isChecked, setIsChecked] = useState(false);
}
// 문법적으로 isChecked와 setIsChecked는 useState의 리턴값을 구조 분해 할당한 변수입니다.
// 위 코드를 풀어쓰면 아래 코드와 같습니다.
  const stateHookArray = useState(false); 
  const isChecked = stateHookArray[0];
  const setIsChecked = stateHookArray[1];

useState를 호출하면 배열을 반환하는데, 배열의 0번째 요소는 현재 state 변수이고, 1번째 요소는 이 변수를 갱신할 수 있는 함수입니다. useState의 인자로 넘겨주는 값은 state의 초깃값입니다.

const [state 저장 변수, state 갱신 함수] = useState(state 초기 값);

즉, 아래의 코드는 다음과 같이 해석할 수 있습니다.

function CheckboxExample() {
  const [isChecked, setIsChecked] = useState(false);
}
// const [state 저장 변수, state 갱신 함수] = useState(state 초기 값);

isChecked: state를 저장하는 변수
setIsChecked: state isChecked를 변경하는 함수
useState: state hook
false: state 초깃값

이 state 변수에 저장된 값을 사용하려면 JSX엘리먼트 안에 직접 불러서 사용하면 됩니다. 여기서는 isChecked가 boolean 값을 가지기 때문에 trueor false여부에 따라 다른 결과가 보이도록 삼항연산자를 사용합니다.

<span>{isChecked ? "checked!!" : "Unchecked"}</span>
  • state 갱신하기
    • state를 갱신하려면 state 변수를 갱신할 수 있는 함수인 setIsChecked를 호출합니다.
    • 아래 예시 코드에서는 사용자가 체크박스 값을 변경하면 onChange이벤트가 이벤트 핸들러 함수인 handleChecked를 호출하고, 이 함수가 setIsCheked를 호출하게 됩니다. setIsChecked가 호출되면 호출된 결과에 따라 isChecked 변수가 갱신되며, React는 새로운 isChecked변수를 CheckboxExample컴포넌트에 넘겨 해당 컴포넌트를 다시 렌더링 합니다.
      React 컴포넌트는 state가 변경되면 새롭게 호출되고, 리렌더링 됩니다.
function CheckboxExample() {
  const [isChecked, setIsChecked] = useState(false);

  const handleChecked = (event) => {
    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가 제대로 변경되지 않습니다.

4. 이벤트 처리

React의 이벤트 처리(이벤트 핸들링; Event handling) 방식은 DOM의 이벤트 처리 방식과 유사합니다. 단 몇 가지 문법 차이가 있습니다.

  • React에서 이벤트는 소문자 대신 카멜 케이스(camelCase)를 사용합니다.
  • JSX를 사용하여 문자열이 아닌 함수로 이벤트 처리 함수(이벤트 핸들러; Event handler)를 전달합니다.
// html의 이벤트 처리 방식
<button onclick = "handleEvent()">Event</button>

// React의 이벤트 처리 방식
<button onClick = {handleEvent}>Event</button>
  • onChange
    <input>, <textarea>, <select>와 같은 폼(Form)엘리먼트는 사용자의 입력값을 제어하는데 사용됩니다. React에서는 이러한 변경될 수 있는 입력값을 일반적으로 컴포넌트의 state로 관리하고 업데이트합니다. onChange이벤트가 발생하면 e.target.value를 통해 이벤트 객체에 담겨 있는 input 값을 읽어올 수 있습니다.
    컴포넌트 return 문 안의 input태그에 valueonChange를 넣어주었습니다. onChangeinput의 텍스트가 바뀔때마다 발생하는 이벤트입니다. 이벤트가 발생하면 handleChange함수가 작동하며, 이벤트 객체에 담긴 input값을 setName을 통해 새로운 state로 갱신합니다.

function NameForm() {
  const [name, setName] = useState("");

  const handleChange = (e) => {
    setName(e.target.value);
  }

  return (
    <div>
      <input type="text" value={name} onChange={handleChange}></input>
      <h1>{name}</h1>
    </div>
  )
};
  • onClick
    onClick이벤트는 말 그대로 사용자가 클릭이라는 행동을 하였을 때 발생하는 이벤트입니다. 버튼이나 <a>tag를 통한 링크 이동 등과 같이 주로 사용자의 행동에 따라 애플리케이션이 반응해야 할 때 자주 사용하는 이벤트입니다.
    onClick이벤트에 함수를 전달할 때는 함수를 호출하는 것이 아니라 아래와 같이 리턴문 안에서 함수를 정의하거나 리턴문 외부에서 함수를 정의 후 이벤트에 함수 자체를 전달해야합니다.
    단, 두 가지 방법 모두 arrow function을 사용하여 함수를 정의하여야 해당 컴포넌트가 가진 state에 함수들이 접근할 수 있습니다.
// onClick 이벤트에 리턴문 안에서 함수를 정의하는 방법
function NameForm() {
  const [name, setName] = useState("");

  const handleChange = (e) => {
    setName(e.target.value);
  }

  return (
    <div>
      <input type="text" value={name} onChange={handleChange}></input>
	  <button onClick={()=>alert(name)}>Button</button>
      <h1>{name}</h1>
    </div>
  )
};
// 리턴문 외부에서 함수를 정의 후 onClick 이벤트에 함수 자체를 전달
function NameForm() {
  const [name, setName] = useState("");

  const handleChange = (e) => {
    setName(e.target.value);
  }

  const handleClick = () => {
  	alert(name)
  }
  return (
    <div>
      <input type="text" value={name} onChange={handleChange}></input>
	  <button onClick={handleClick}>Button</button>
      <h1>{name}</h1>
    </div>
  )
};

HTML에서 <input>, <textarea>, <select>와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트 합니다.
React에서는 변경할 수 있는 state가 일반적으로 컴포넌트의 state 속성에 유지되며 setState()에 의해 업데이트 됩니다.
아래 예시에서 const [name, setName]= useState("")와 같습니다.

function App(){
	const [name, setName] = useState("");

  return (
      <div className="App">
          <input
              type="text"
              value={name}
              onChange={(event)=>setName(event.target.value)}></input>
          );
}

<input>, <textarea>, <select>와 같은 폼 엘리먼트를 렌더링하는 React 컴포넌트는 폼에 발생하는 사용자 입력값을 제어합니다.
이러한 방식으로 React에 의해 값이 제어되는 입력 폼 엘리먼트를 제어 컴포넌트(controlled component)라고 합니다. 즉, 제어 컴포넌트를 사용하면, 위 input의 값은 항상 React state에 의해 결정됩니다.

5. React 데이터 흐름

React의 개발 방식의 가장 큰 특징은 페이지 단위가 아닌 컴포넌트 단위라는 것입니다. 아래 이미지처럼 컴포넌트를 만들고 다시 페이지를 조립해서 나갑니다.

컴포넌트

이러한 방식을 상향식(bottom-up)으로 앱을 만든하고 합니다. 이것의 가장 큰 장점은 테스트가 쉽고 확정성이 좋습니다.

컴포넌트 트리구조

위 이미지는 컴포넌트의 관계를 트리 구조로 형상화하여 표현한 것입니다. 컴포넌트는 컴포넌트 바깥에서 props를 이용해 데이터를 마치 인자(arguments) 혹은 속성(attributes)처럼 전달 받을 수 있습니다. 즉, 데이터를 전달하는 주체는 부모 컴포넌트가 됩니다. 이는 데이터 흐름이 하향식(top-down)임을 의미합니다.
이처럼 단방향 데이터 흐름(One-way data flow)은 React를 대표하는 설명 중 하나입니다. 또한 컴포넌트는 props를 통해 전달받은 데이터가 어디서 왔는지 전혀 알지 못합니다.

0개의 댓글