Skip to content

Instantly share code, notes, and snippets.

@ninanung
Last active November 22, 2020 11:02
Show Gist options
  • Save ninanung/6a2d37bebe1358591b48ac364261e744 to your computer and use it in GitHub Desktop.
Save ninanung/6a2d37bebe1358591b48ac364261e744 to your computer and use it in GitHub Desktop.
때늦은 React Hooks 시리즈 3탄 - useContext/useMemo

이 글의 코드는 저자의 Github에서 확인할 수 있습니다.

때늦은 React Hooks 시리즈 3탄 - useContext/useMemo

useContext

useContext를 알기 위해서는 먼저 React의 Context기능에 대해서 알아야 합니다. 해당 기능에 대한 설명은 공식 페이지에 자세하게, 심지어 한글로 적혀있으니 모르겠다 하시는 분들은 먼저 보시는걸 추천합니다. Context, 그것은 React에서 props를 넘기는 과정을 돕기위해 만들어 졌습니다. 저-기 밑에있는 컴포넌트를 위해 자식 컴포넌트마다 props를 넘기는 건 비효율적이니까요. 하지만 그렇게 만들어진 Context조차도 완벽하지는 않았습니다. 함수형 컴포넌트에서 하나의 Context를 위해서는 하나의 Wrapper가 필요한데, 이 말은 결국 많은 Context를 위해서는 많은 Wrapper가 필요해진다는 말입니다. 결국 복잡한 React 프로젝트를 Wrapper Hell로 만들어 버리기 충분한 수준입니다. 이러한 문제를 해결하기 위해서 useContext가 탄생했습니다.

useContext는 더이상 함수형 컴포넌트에서 Context를 사용하기 위해

<SomeContext.Consumer>
    <HelloWorld />
</SomeContext.Consumer>

형태의 Wrapper를 사용할 필요가 없게 만들었습니다. 전체 코드를 보시면 쉽게 이해가 되실겁니다.

import React, {useContext, createContext} from 'react';

const TestContext = createContext();
const TestContext2 = createContext();

const UseContextExample = () => {
  const hello = useContext(TestContext);
  const world = useContext(TestContext2);
  return (
    <div>
      {hello + ' ' + world}
    </div>
  )
}

function App() {
  return (
    <div className="App">
      <TestContext.Provider value='hello'>
        <TestContext2.Provider value='world!'>
          <UseContextExample />
        </TestContext2.Provider>
      </TestContext.Provider>
    </div>
  );
}

export default App;

쉽죠? 그냥 함수형 컴포넌트 안에서 useContext를 사용해서 만들어진 Context에서 값을 불러오기만 하면 됩니다. 코드를 보면 값으로 'hello'와 'world!'스트링을 줬습니다. UseContextExample컴포넌트에서는 해당 값을 useContext(TestContext)를 통해 가져오고 있습니다. 어때요, 정말쉽죠? 진짜로 이게 그냥 끝입니다. 더 설명할 내용도 없을 것 같네요.

useMemo

React는 state가 변경될 때 마다 다시 렌더링 된다는 얘기를 useEffect부분에서 얘기 했습니다. 기억이 안나는 사람은 이전 글을 참고하세요. 어쨌든 그렇게 다시 렌더링 하는 과정에서 간혹 필요없는 것들을 계속해서 불러오곤 합니다. 이렇게 될 경우 자원의 낭비로 이어지고, 복잡한 서비스가 만들어 졌을 때 성능을 저하시키는 요인이 됩니다. 따라서 특정 상황에만 특정 동작을 하도록 유도할 필요가 있습니다. 흠, 뭔가 들어본 것 같은 내용인가요? 네 맞습니다. useEffect의 내용과 사실 유사한 점이 있죠. 하지만 조금 다른 점은 지금부터 설명한 useMemo는 렌더링 될 때가 아니라 특정 값이 변경되었을 때만 동작한다는 겁니다. 우선 비효율적인 예부터 보겠습니다.

const UseMemoExample = () => {
  const [string, setString] = useState('');
  const [stringList, setStringList] = useState([]);

  const insert = () => {
    const newList = stringList.slice();
    newList.push(string);
    setStringList(newList);
  }

  const sum = (list) => {
    console.log('문자들을 합치는 중입니다...');
    let stringSum = '';
    for(let value of list) {
      stringSum += value + ' ';
    }
    return stringSum;
  }

  return (
    <div>
      <input type='text' onChange={(e) => {setString(e.target.value)}}/>
      <button onClick={insert}>문자열 추가</button>
      {sum(stringList)}
    </div>
  )
}

위의 코드를 보면, input에 텍스트를 입력하면 onChange를 통해 string의 값을 변화시키고 버튼을 누르면 해당 텍스트를 stringList에 추가합니다. 이후 sum함수는 콘솔에 '문제를 합치는 중입니다...'을 입력하고 stringList의 값들을 불러와서 문자열로 연결한 후 리턴합니다. 마지막으로 리턴된 문자열은 화면에 보여지게 됩니다. 흠, 괜찮을 것 같은데 뭐가 문제일까요? 이 코드를 테스트 해 보면 한가지 엄청난 문제가 있습니다. 바로 input의 값이 변할때(string state의 변화로 다시 렌더링 될 때)마다 sum함수가 호출된다는 겁니다.

usememo1

그런데 사실 이건 비효율 적입니다. 왜냐하면, 어차피 sum함수는 stringList에 새로운 문자열이 추가될 때에만 불려지면 되는 함수니까요. 이러한 비효율적인 함수의 호출을 막기 위한 방법으로 useMemo가 만들어진 겁니다. 코드를 변경해 보도록 합시다.

const UseMemoExample = () => {
  const [string, setString] = useState('');
  const [stringList, setStringList] = useState([]);

  const insert = () => {
    const newList = stringList.slice();
    newList.push(string);
    setStringList(newList);
  }

  const sum = (list) => {
    console.log('문자들을 합치는 중입니다...');
    let stringSum = '';
    for(let value of list) {
      stringSum += value + ' ';
    }
    return stringSum;
  }

  const result = useMemo(() => sum(stringList), [stringList]);

  return (
    <div>
      <input type='text' onChange={(e) => {setString(e.target.value)}}/>
      <button onClick={insert}>문자열 추가</button>
      {result}
    </div>
  )
}

이전 코드를 그대로 사용하지만, useMemo를 이용하여 result변수를 새로 선언하도록 했습니다. 해당 부분에서는 state중 stringList가 변경될때만 함수가 호출되도록 해놨습니다. 형태가 useEffect와 아주 유사하지 않나요? 방식도 비슷합니다. 두번째 인자로, 변경을 감지하고 싶은 변수를 넣기만 하면 됩니다. 자 이렇게 하면 예상 할 수 있듯이, stringListstate가 변경될 때만 함수를 호출하게 됩니다. 어디 한번 살펴볼까요?

usememo2

콘솔에 입력되는 문자를 보면 아시겠지만, stringList에 문자가 추가될 때에만 반응하고 있습니다. 하지만 이전 코드와 완전히 같은 동작을 구현할 수 있죠. 이처럼 useMemo는 불필요한 동작으로 인해 자원을 소모하는 것을 막아줄 수 있습니다. 그 밖에도, 특정 값이 변경될때만 특정 동작을 실행해야 하는 경우, state를 특정하여 변화를 감지해야 하는 경우에도 사용할 수 있습니다. 예제에서는 한가지 경우를 보여드렸지만, React Hooks의 힘은 여러분의 힘에 달렸다는 걸 알아주셨으면 좋겠네요. 다음 글에서는 useCallback과 가능하다면 useRef까지도 알아보도록 하겠습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment