티스토리 뷰

반응형

React를 처음 배우면, useState를 상태관리를 위해 사용한다. 이 글을 보고 있는 여러분도 적어도 한번 정도는 사용해 봤을 것이다. useState는 리액트에 제공하는 가장 기본적은 Hook으로 useEffect와 함께 가장 많이 사용된다. 상태관리가 무엇인지에 대해 알아본 후 useState의 본질적인 사용법에 대해 알아보도록 하자.

 

 


 

React에서 State

state란 무엇일까? 쉽게 말하면 state는 데이터라고 생각하면 이해가 빠르다. 따라서 상태관리란, 데이터 관리라고 할 수 있다. 공식 문서에 따르면 state를 구분하는 방식은 아래와 같다.

 

  1. 시간에 따라, 변한다면 state가 아니다.
  2. parent component에서 Props를 받을 수 있다면, state가 아니다.
  3. 기존의 state나 props로 만들 수 있다면, state가 아니다.

 

state의 기본적인 원칙은 중복되지 않아야 하고, 특정 이벤트에 따른 변화 발생 시 UI가 변화할 수 있어야 한다. 여기서 props와 state의 개념이 나오는데, props와 state를 혼동하면 안 된다. props는 기본적으로 함수로 전달받는 인자의 개념으로 부모 컴포넌트에서 자식 컴포넌트로 데이터를 내려주는 값이라고 할 수 있다. state는 컴포넌트의 메모리에 저장되는 값으로, 특정 이벤트에 따른 변화를 발생시킬 수 있는 상태라고 할 수 있다.

 


 

State의 위치

리액트에서는 useState를 통해 상태를 관리한다. 그렇다면, state를 담아두는 useState는 어느 컴포넌트에 넣어야 할까? 가장 먼저 생각해야 할 부분은 컴포넌트의 책임/역할이라고 할 수 있겠다. 기본적으로 리액트는 단방향 데이터 흐름을 가진다. 따라서 부모 컴포넌트에서 자식 컴포넌트로 데이터를 내려준다. 이러한 흐름은 컴포넌트의 책임이라는 관점에서는 어떤 컴포넌트가 state의 책임을 가지는지 판단하기에 어려울 수 있다. 공식문서에서는 아래의 순서로 데이터에 관한 컴포넌트의 책임을 찾도록 가이드한다

.

  1. state와 관련된 모든 컴포넌트를 확인한다.
  2. 확인한 컴포넌트의 부모 컴포넌트를 확인한다.
  3. 일반적으로 부모 컴포넌트에 state를 넣어준다. 만약 마땅한 부모 컴포넌트가 없다면, 컴포넌트를 만들어서 state를 넣어도 된다.

 

정리하면 state 공통으로 사용하는 최상단 컴포넌트에 useState를 넣고, props로 자식 컴포넌트들이 공유하는 형식이라 할 수 있다. 

 

 


 

useState 사용해 보기

기본적으로 html은 변하지 않는 값이다. html을 변화시키기 위해서는 화면을 새로고침해야 한다. 하지만 전체 화면을 모두 새로고침을 하는 것은 비효율적이라 할 수 있다. 따라서 변경되는 부분(Component)만 새로고침을 하기 위해 useState를 사용한다고 할 수 있겠다. 초급자 기준으로 간단히 코드 설명을 해보겠다.

 

 

사실 useState는 위의 동그라미 친 3 부분만 추가하면 사용 가능하다. 간단히 코드를 확인해 보자

 

> import React, {useState} from 'react';

이 부분은 useState를 사용하기 위해 import 해야 한다.

 

> const [hanpy, setHanpy] = useState();

이 부분은 useState를 사용하는 부분이다. []를 통해서 이름을 짓는다.

  • []의 첫 번째 변수 hanpy : 변수를 지정한 부분이다.
  • []의 두 번째 변수 setHanpy : 변수 변경은 이것을 사용해서 한다. 통상적으로 첫 번째 변수인 hanpy에서 앞에 set을 붙여 사용한다.
  • useState() : () 안에는 초기 값을 넣어준다.

 

> {hanpy}

html 코드 내부에서 useState 변수를 사용하려면 {} 안에 넣서 사용해 주면 된다. 첫 번째 변수인 hanpy를 넣어서 사용한다. setHanpy는 html내부에서 사용하지 않는다.

 

 

 

이벤트로 상태값 변화시키기

useState에 저장된 값을 변경하는 코드는 아래와 같다.

 

 

이전 코드에서 2개만 더 추가를 했다. button에 onClick를 걸어서 지정한 함수가 실행되게 했다.

 

> const handleTest = () => {}

함수 지정 시 위와 같은 방법으로 사용하자. 이는 component에서 class형이 아닌 함수형으로 사용하기로 한 방식과 동일하다.

 

> setHanpy(hanpy+1)

hanpy값을 변경하는 부분이다. useState에 포함된 변수변경은 hanpy = hanpy + 1 이런식으로 사용하면 안 된다. 그러면 useState를 사용한 이유가 없어진다. 반드시 hanpy의 내부의 값 변경 시 setHanpy를 사용하자.  setHanpy로 변경을 하면 UI에 자동으로 반영되어 hanpy 변수가 변경된다.

 

setHanpy(hanpy + 1)  대신 setHanpy((pre) => pre+1)를 자주 사용한다. pre란 이전 값을 의미한다. 
위 코드에서 setHanpy(hanpy + 1) 로 적은 이유는 글을 읽으시는 분들의 직관적 이해도 상승을 위함임을 알자.

 

> onClick={}

이벤트 리스너를 react에서 적을 때, 주의할 점은, onClick대신, onclick으로 적으면 작동되지 않는다. 반드시 캐멀케이스인 onClick으로 적어주자.

 

useState의 사용법에 대해 간단히 알아보았다. 사실 여기까지는 기초적인 내용이고, 본격적으로 조금 더 깊숙하게 확인해 보자.

 


useState 심화 ( useState와 외부 API 결합하기 )

하나의 state는 하나의 useState를 사용한다.

너무나도 당연한 이야기지만, 코드 작성 시 무의식적으로 useState를 추가하여 사용한다. 기본적으로 API로 데이터를 가지고 와서 화면에 보여주는 순서는 다음과 같다. 

 

  1. 데이터를 가지고 온다
  2. 데이터를 usestate로 저장한다.
  3. 가져온 데이터가 저장을 했으면, 데이터 가공을 통해 추가 데이터를 저장한다.

 

이러한 로직은 아래와 같은 코드로 만들 수 있다.

 

import { useGetData } from "./hooks";
import { computeAnalysisResults } from "./utils";

const App = () => {
  const [data, setData] = useState(null);
  const [analysisResults, setAnalysisResults] = useState([]);
  
  useEffect(() => {
    async function fetch() {
      const response = await useGetData();
      setData(response.data);
    }
    
    fetch();
  }, []);
  
  useEffect(() => {
    if (data) {
      setAnalysisResults(computeAnalysisResults(data));
    }
  }, [data]);
  
  return <>Dummy UI</>;
}

 

useEffect를 2번 사용하는 로직에는 크게 문제가 없고, 잘 작동할 것이다. 하지만, 위의 코드는 앞으로 문제가 될 수 있는 가능성이 포함되어 있는 코드이다.  위의 코드에서 필자는 analysisResults가 data에만 의존하도록 코드를 작성했다. 그러나 협업하는 개발자가 위의 정확한 로직을 모르고 아래와 같이 getOtherAnalysisResults 함수를 추가하여 버튼을 만들었다고 해보자.

 

import { useGetData } from "./hooks";
import { computeAnalysisResults, getOtherAnalysisResults } from "./utils";

const App = () => {
  const [data, setData] = useState(null);
  const [analysisResults, setAnalysisResults] = useState([]);
  
  useEffect(() => {
    async function fetch() {
      const response = await useGetData();
      setData(response.data);
    }
    
    fetch();
  }, []);
  
  useEffect(() => {
    if (data) {
      setAnalysisResults(computeAnalysisResults(data));
    }
  }, [data]);
  
  return (
    <>
      Dummy UI
      <button onClick={() => setAnalysisResults(getOtherAnalysisResults())}>
       GET-OTHER-DATA
      </Button>
    </>
  )
}

 

위 코드를 보면 setAnalysisResults가 두 부분에서 사용된다. data를 fetch 해서 업데이트할 때와 버튼을 누를 때 사용된다. 따라서 AnalysisResult가 data로 가져온 값인지 버튼을 통해서 가져온 값인지 모호해진다. 이 말을 쉽게 이야기하면, 이러한 코드에 계속해서 추가 작업을 진행한다면, 코드가 복잡해지고 에러 추적이 힘들어질 것이다. 

 

기본적으로 useEffect는 외부 데이터와 내부 데이터의 동기화를 통해 UI에 나타낼 때, 사용한다. 그러나 위의 사용은 내부의 2개의 state를 동기화하는데, useEffect를 사용한다. 이는 잘못된 방법이라 할 수 있다. 아래와 같이 useEffect를 하나 제외하여 state를 삭제하자.

 

 

이러한 방식은 복잡도를 줄일 수 있고, 다른 개발자가 AnalysisResult를 수정하고 싶을 때 다른 함수를 만드는 것이 아니라 computeAnalysisResult를 우선 확인하여 함수를 추가/수정할 것이다. 동일한 데이터를 2가지로 나누는 것이 아니라, SSOT 원칙을 React 코드에도 반영하도록 하자. 즉, 같은 state는 하나의 상태만 설정하자.

 

 

컴포넌트에서 useState 초기값이란

useState의 초기값에 대해 생각해 보자. useState 선언 시 위 코드에서는 useState(null), useState([]) 와 같이 사용을 했다. 여기서 초기값으로 설정한 값은 null과 []를 의미한다. 이때 초기값은 컴포넌트가 mount 될 때만 사용을 한다. 그 후부터 re-render 시, 초기값은 사용되지 않음을 이해하자.

 

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함