가치투자자

[React/기술면접] State와 Props 본문

Programming/React

[React/기술면접] State와 Props

미주민 2023. 6. 13. 16:39
728x90
반응형

 State와 Props 

* 기술면접 질문은 맨 아래에 적혀 있습니다

 

 

웹 브라우저에서 데이터가 변경되었을 때 어떻게 반영될까? 자바스크립트에서는 DOM에 직접 접근하여 데이터 변경사항을 반영한다. 아래의 예시 코드를 보면, 버튼을 클릭해 카운트가 증가할 때마다 아이디가 count-text인 DOM 요소에 접근해 텍스트를 변경해준다.

let count = 0;

function handleClick() {
  count = count +1;
  rerendering();
}

function rerendering() {
  const cntText = document.querySelector('#count-text');
  cntText.innerText = `Count : ${count}`;
}

 

그러나 count 데이터가 변경될 때마다 DOM을 조작하게 되면 변경된 부분만 반영되는게 아니라 불필요한 리렌더링이 일어날 수 있다. 그렇다면 React에서는 변경된 데이터를 어떻게 전달하고 반영할까? 이와 관련하여 state와 props에 대해 살펴보자.

 

 

728x90

 


 1. State 

 State (상태) 는 React 컴포넌트의 상태, 즉 컴포넌트에서 변경 가능한 데이터를 말한다. 좀 더 정확히 말하자면 컴포넌트에 대한 데이터 또는 정보를 나타내는데 사용되는 React 내장 객체다.

자바스크립트에선 일반 변수를 사용해 데이터를 저장해줬는데, React에서는 왜 굳이 state를 쓸까? 그 이유는 변수 값이 변경되어도 렌더링이 이루어지지 않기 때문에 자동으로 브라우저 창에 반영되지 않는다. 아래의 코드를 보면 카운트 버튼을 클릭했을 때 count의 값은 올라가지만, 그것이 브라우저 창에는 반영되지 않는다. 콘솔창에서 console.log(count)를 찍어보면, count 값이 올라가는 것을 확인할 수 있다.

const Counter = () => {
  let count = 0;
  const handleClick = () => {
    count = count +1
    // console.log(count)
  }

  return (
    <div>
      <h2>현재 Count : {count}</h2>
      <button onClick={handleClick}>카운트 +1</button>
    </div>
  );
}

export default Counter;

 

state는 변경 가능한 데이터이기에 변경될 때마다 다시 렌더링되며, 그렇기에 렌더링이나 데이터 흐름에 사용되는 값만 state로 사용해야 한다. 왜냐하면 불필요한 데이터가 state에 포함되어 있으면 불필요한 리렌더링으로 성능을 떨어틀일 수 있기 때문이다.

 

이러한 state에는 클래스형 컴포넌트의 state와 함수형 컴포넌트에서 useState라는 함수를 사용하는 state 2가지가 있다.

 

 


 1-1. 클래스형 컴포넌트의 state 

아래의 코드는 카운트 버튼을 클릭했을 때, 카운트의 값(count)이 1씩 증가하는 컴포넌트다.

클래스형 컴포넌트에서는 this.state로 state의 초기값을 설정해준다. 이때 state는 객체 형식이어야 한다.

import React, { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    
    // state 초기값 설정
    this.state = {
      count: 0
    };
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        <h2>현재 Count : {count}</h2>
        <button onClick={() => {
          this.setState({ count: count+1 })
        }}>카운트 +1</button>
      </div>
    );
  }
}

export default Counter;

 

그리고  setState 를 통해 state의 값을 변경해주고 있다. 왜 직접적으로 state 값을 변경하지 않고 setState() 메서드를 이용하는 것일까?

만약 아래와 같이 handleClick() 함수 내에서 state의 count 값을 직접 변경한다면, 변경한 곳의 count의 값은 변경되었을지 몰라도 React는 이 변경 사항을 인지하지 못한다. 앞서 살펴봤듯이 state는 객체 형태이다. 그렇기에 그 객체가 있던 곳, 즉 기존 객체의 참조(주소)의 값을 변경해줘야 React가 변화를 인지할 수 있다. 그러나 state를 직접 변경하면 기존 객체의 참조는 유지되기 땜에 React 입장에선 아무것도 변한 것이 없다. 따라서 state는 불변성을 유지해줘야 한다.

  • 불변성

    : 메모리 영역에서의 직접적인 변경을 하지 않고, 기존의 값을 수정하지 않으면서 새로운 값을 만들어내는 것

 

handleClick() {
  const { count } = this.state;
  // state 값을 직접 변경
  this.state.count = count + 1;
}

render() {
  const { count } = this.state;
  return (
    <div>
      <h2>현재 Count: {count}</h2>
      <button onClick={() => this.handleClick()}>카운트 +1</button>
    </div>
  );
}

 

 1) setState의 동작 

이러한 setState는 어떻게 작동할까?

 

  1. setState를 통해 state의 값을 변경해주고,
  2. React가 state의 변경을 감지하면
  3. 화면을 리렌더링해준다

그렇다면 아래 count의 최종값은 몇일까? 3이라고 생각할 수 있지만 결과는 1이다.

그 이유는 state가 비동기적으로 동작하기 때문이다. 비동기적으로 동작하기 때문에 handleCount() 함수에서 state 값이 즉기 반영되지 않고, 그저 Count() 함수를 3번 중복해서 요청하는 것이므로 결과는 1이 나온다. 즉, setState는 값을 변경하는 함수가 아니라, state의 값에 대한 변화를 요청하기만 하기 때문에 리렌더링되기 전에 state가 변경되지 않는다.

Count() {
  this.setState({ count: this.state.count +1 });
}

hadleCount() {
  this.Count();
  this.Count();
  this.Count();
}

 

만약 count를 3번 올려주고 싶다면 setState를 넘겨주는 것이 아니라, 아래처럼 업데이트 함수(updator)를 넘겨주면 된다.

Count() {
  this.setState((state) => {
    return { count: this.state.count +1 }
  });
}

hadleCount() {
  this.Count();
  this.Count();
  this.Count();
}

 

그렇다면 setState는 왜 비동기적으로 동작할까?

이는 리렌더링과 관련이 있다. state 값이 바뀌면 React는 화면을 다시 렌더링해줘야 하는데, 리렌더링이 많아질수록 어플리케이션의 속도가 떨어지고 성능이 안 좋아지기 때문이다. 그래서 16ms 동안 변경된 state 값을 한번에 렌더링하는데, 이를 batch update (일괄 업데이트)라고 한다.

 

 

<참고자료>

 

 

Queueing a Series of State Updates – React

The library for web and native user interfaces

react.dev

 


 1-2. 함수형 컴포넌트의 state 

Hook이 등장하기 이전에는 함수형 컴포넌트에서 state를 사용하지 못했다. 그 이유는 함수 내 변수는 함수가 끝날 때마다 사라지며, state 값을 업데이트 해두었다고 하더라도 함수가 실행될 때마다 그 값이 초기화된다. 따라서 과거에는 단순히 props를 받아와 UI를 렌더링하는 역할만 수행했다.

 

React 16.8 버전부터 Hook 중 하나인  useState 를 통해 state를 사용할 수 있게 되었다.

useState를 사용하기 위해선 import 해주고, useState()를 선언해주면 된다. 여기서 state는 현재 상태값이며, setState 함수를 통해 state 값을 업데이트 해줄 수 있다. 이때 setState 함수의 이름은 카멜 케이스(camelCase)로 작성해줘야 한다.

useState의 인자에는 초기값이 들어간다. 이때 초기값은 어떤 타입이여도 상관없다. 그리고 리렌더링 시 useState는 가장 최신 state 값을 state에 반환해준다.

import { useState } from 'react';

// 초기값은 생략 가능
const [state, setState] = useState(초기값);

// 예시
const [count, setCount] = useState(0);

 

근데 여기서 의문점이 들 수 있다.

주로 const로 state를 선언해주는데, 어떻게 최신 state 값이 반환될 수 있을까? 이는 리렌더링시 useState가 배열의 첫 번째 요소로 state의 최신값을 반환하기 때문에 가능한 것이다.

 

 


 2. Props 

 Props 는 properties의 약자로, 컴포넌트의 속성을 설정할 때 사용하는 요소를 말한다.

좀 더 쉽게 설명하자면, props의 값은 컴포넌트의 속성을 통해 컴포넌트에서 컴포넌트로 전달할 때 사용하는 매개변수다. 주로 부모 컴포넌트에서 자식 컴포넌트로 전달해 사용하는데, 함수형 컴포넌트에서는 인자로 props값을 전달한다.

 

아래의 예시를 보면, 부모 컴포넌트의 속성값에 props값 name을 담아 자식 컴포넌트로 전달해주고 있다.

// 부모 컴포넌트
import React from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  return (
    <div>
      <ChildComponent name="React" />
    </div>
  );
};

export default ParentComponent;
// 자식 컴포넌트
import React from 'react';

function ChildComponent(props) {
  return (
    <div>
      <h2>환영합니다! {props.name}님</h2>
    </div>
  );
};

export default ChildComponent;

// 환영합니다! React님

 

이때 주의할 점은 props값을 전달받은 자식 컴포넌트에선 props값을 변경할 수 없다. 그 이유는 바로 props가 읽기 전용(immutable) 데이터이기 때문이다. 이처럼 props 값의 불변성을 보장하는 이유는 props값을 전달받았을 때 예측 가능한 동작을 보장하기 위해서다. 만약 자식 컴포넌트에서 props값이 쉽게 변경 가능하다면, 이 값을 사용하는 다른 컴포넌트에서 예측하지 못한 결과가 나올 수 있다.

따라서 props의 값을 변경하기 위해선 props를 전달해준 최상위 부모 컴포넌트에서 변경하거나 추가로 state를 만들어줘야 한다.

 

 

반응형

 


🌟 React 기술면접 질문

1. State와 Props의 차이를 설명하세요.

 

  state는 렌더링 시 뷰에 반영되어야 할 컴포넌트의 변경 가능한 데이터로, state 값이 변경되면 리렌더링된다. 따라서 불필요한 렌더링을 줄이기 위해 렌더링이나 데이터 흐름에 사용되는 값만 state로 사용해야 한다. 또한, state는 불변성을 유지해줘야 하므로, 클래스형 컴포넌트에서는 setState를 통해 값을 변경해주고, 함수형 컴포넌트에서는 useState를 사용한다.

  props는 부모 컴포넌트에서 자식 컴포넌트로 전달되는 데이터로, 함수형 컴포넌트에서는 인자를 통해 전달된다. 이때 전달된 props값은 읽기 전용 데이터이기에 직접 변경이 불가능하며, props 값을 변경하기 위해선 최상위 부모 컴포넌트에서 변경하거나 추가로 state를 만들어줘야 한다.

 

 

2. state를 직접 변경하지 않고 setState를 사용하는 이유에 대해 설명하세요.

 

  state를 직접 변경하지 않는 이유는 불변성과 관련이 있다. state는 React 내장 객체이기에 state를 직접적으로 변경해주면 기존 객체의 참조에선 변경사항이 반영되지 않는다. 따라서 React가 변경 사항을 인지하기 위해선 setState를 통해 state를 변경해줘야 하며, 이때서야 React는 얕은 비교를 통해 state 변화를 인지하고 변경 사항을 반영해준다.

 

  • 불변성

    : 메모리 영역에서의 직접적인 변경을 하지 않고, 기존의 값을 수정하지 않으면서 새로운 값을 만들어내는 것

 

3. setState가 비동기적으로 작동하는 이유를 설명하세요.

 

  setState가 비동기적으로 작동하는 이유는 리렌더링과 관련이 있다. State 값이 변경되면 React는 이를 감지하고 기존의 가상 돔과 비교하여 변경 사항을 반영해준다. 그러나 State 값이 변경될 때마다 동기적으로 반영해주게 된다면 그 때마다 리렌더링이 일어나고, 이로 인해 어플리케이션의 속도가 떨어지고 성능이 안 좋아진다. 따라서 setState는 변경된 state를 한번에 일괄 업데이트(batch update) 해준다.

 

 


</> 끊임없이 성장하기 위해 공부한 내용을 글로 작성하고 있습니다. 틀린 부분이나 추가해야 할 부분이 있다면 언제든 댓글로 남겨주세요❗️

 


References

 

 

728x90
반응형

'Programming > React' 카테고리의 다른 글

[React/기술면접] Hook (2)  (0) 2023.07.10
[React/기술면접] Hook (1)  (1) 2023.07.09
[React/기술면접] 생명 주기 (Life Cycle)  (0) 2023.07.03
[React/기술면접] ref란?  (0) 2023.07.01
[React/기술면접] React란?  (0) 2023.06.10