가치투자자

[React/기술면접] Hook (1) 본문

Programming/React

[React/기술면접] Hook (1)

미주민 2023. 7. 9. 21:02
728x90
반응형

 React Hooks 

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

 

 1. Hook의 등장 배경 

컴포넌트에는 클래스형 컴포넌트와 함수형 컴포넌트가 있다.

클래스형 컴포넌트에서는 constructor(생성자)에서 state를 정의하고 setState() 함수를 통해 state를 업데이트 해줄 수 있다. 거기다가 생명주기 함수들까지 명확하게 정의되어 있기 때문에 편리하게 가져다 쓰면 된다.

 

반면, 함수형 컴포넌트에서는 별도로 state를 정의하거나 생명주기에 따른 기능 구현이 불가능했다.

함수가 state를 가지지 못하는 이유는 리렌더링될 때 함수 내에 작성된 모든 코드들이 다시 실행되기 때문이다. 이로 인해 기존에 가지고 있던 state를 전혀 관리(기억)할 수 없기에 함수형 컴포넌트에서는 state를 사용할 수 없었다.

 

 

그러나 클래스형 컴포넌트 역시 다음과 같은 단점을 가지고 있었다.

 

  1. 함수형 컴포넌트에 비해 코드가 복잡하고, 재사용성도 떨어진다.

  2. 상태 관리 및 생명주기 함수를 사용하기 위한 추가적인 학습이 필요하다.

  3. this가 가리키는 값이 변경되면 문제를 발생시킬 수 있다.

    ex> A를 팔로우하려고 했으나 팔로우 되기 이전 B 유저로 바꾸면서 컴포넌트가 리런데링되고, this가 가리키는 값이 B로 변경되어 B가 팔로우됨

이러한 점들 때문에 함수형 컴포넌트에서 부족했던 기능인 state와 생명주기 기능을 가능을 할 수 있게 해주는 것이 hook이다.

 

 

728x90

 


 2. Hook이란? 

Hook은 함수형 컴포넌트에서 React state와 생명주기 기능을 "연동(hook into)"할 수 있게 해주는 함수로, 리액트 16.8 버전부터 추가 되었다. 이름 그대로 React state와 생명주기 기능을 갈고리처럼 걸어 원하는 시점에 정해진 함수가 실행될 수 있도록 해준다.

Hook을 사용한 함수형 컴포넌트가 state를 가질 수 있는 이유는 Closure를 이용해 함수형 컴포넌트 바깥에 state를 저장하기 때문이다. 그래서 state가 업데이트 되어도 컴포넌트 바깥에 선언되어 있는 변수들이기 때문에 업데이트 한 이후에도 이 변수들에 접근할 수 있다.

2-1. Hook의 규칙

Hook을 사용하기 위해선 다음과 같은 규칙을 지켜야 한다.

 

  1. 함수(컴포넌트)의 최상위에서만 Hook을 호출

    - 그래야 컴포넌트가 렌더링될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장된다

  2. React 함수 내에서만 Hook을 호출

    - 일반적인 JavaScript 함수에서는 호출 X

    - Custom Hook에서는 Hook을 호출할 수 있다

  3. Hook의 이름이 use로 시작해야 한다

2-2. Hook의 장점

이러한 Hook은 다음과 같은 장점을 가지고 있다.

 

  • 더 간결한 코드

    : 클래스형 컴포넌트처럼 this 키워드나 생명주기 함수 등의 복잡한 문법을 사용하지 않아도 되므로, 코드 작성량이 줄어들고 가독성도 좋아진다.

  • 컴포넌트 로직의 재사용성

    : 원하는 기능을 함수로 만들어 필요한 곳에 넣어주기만 하면 되기 때문에 로직을 재사용하기 더욱 쉬워진다.

  • 상태 관리의 용이성

    : 다양한 상태관리 hook을 활용해 컴포넌트 내에서 간단하게 상태(state)를 관리할 수 있다.

 


 3. Hook API 

Hook에는 각 기능에 따라 여러 Hook이 있으며, 이름 앞에는 use로 시작한다. 개발자가 직접 커스텀 Hook을 만들 때에도 use로 시작해야 한다.

 3-1. useState 

useState는 함수형 컴포넌트에서 state를 사용하기 위한 Hook이다.

useState를 사용하기 위해선 import해와야 하며, useState의 인자에는 state의 초기값이 들어간다. 이때 useState 함수 내에 선언되는 상태(state)들은 "배열"의 형식으로 저장된다. 이 배열은 state로 선언된 변수와 이 state값을 변경할 수 있는 set 함수를 가지고 있기에 이를 구조분해할당해서 state와 set 함수를 사용한다.

 

set 함수의 이름은 카멜(camel) 형식으로 "set + 변수명"으로 사용하는 것이 좋다. 예를 들어, 변수명이 name이면, set 함수는 setName이 되면 된다. 그 이유는 각 state마다 별개의 set 함수를 만들어줘야 하기 때문이다. 클래스형 컴포넌트에서는 setState 함수 하나로 모든 state값을 업데이트할 수 있었지만, 함수형 컴포넌트에서는 각 변수마다 별개의 set 함수를 만들어줘야 한다. 따라서 각 state에 대응하는 set함수가 어떤 것인지 파악하기 쉽도록 set + 변수명으로 이름을 지어주는 것을 권장한다.

이러한 set 함수를 통해 state 값을 변경할 수 있는데, state가 변경되면 자동으로 리렌더링이 된다.

import React, { useState } from 'react';

const [변수, set함수] = useState(초기값);

 

아래의 예시를 보면, count의 초기값은 0이고, 각 버튼에 따라 setCount 함수가 count 값을 업데이트해준다.

import React, { useState } from 'react';

const Counter = () {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>현재 카운터 값은 {count}입니다.</p>
      <button onClick={() => setCount(count +1)}>+1</button>
      <button onClick={() => setCount(count -1)}>-1</button>
    </div>
  );
};

 


 3-2. useEffect 

useEffect는 side effect를 수행하기 위한 Hook이다.

여기서 side effect는 함수가 실행되면서 함수 외부에 존재하는 값이나 상태를 변경시키는 등의 행위를 말한다. 예를 들면, 서버에서 데이터를 받아오거나 수동으로 DOM을 변경하는 작업 등이 있을 수 있다. 이 행위를 effect라 부르는 이유는 이 행위가 다른 컴포넌트에 영향을 미칠 수 있고, 렌더링 중에는 작업이 완료될 수 없기 때문이다. 즉, 렌더링 이후에 실행되어야 하는 작업들이다.

1) useEffect 사용법

useEffect의 사용법은 다음과 같다.

여기서 의존성 배열(dependency array)는 이 effect가 의존하고 있는 배열인데, 배열 내에 그 어떤 변수 하나라도 변경되게 되면 effect 함수가 실행된다. 기본적으로 effect 함수는 처음 컴포넌트가 렌더링된 이후와 업데이트로 인한 리렌더링 이후에 실행된다.

import React, { useEffect } from 'react';

// (콜백함수, dependency array)
useEffect(이펙트 함수, 의존성 배열);

 

아래와 같이 의존성 배열 내에 어떤 value 값이 들어가게 되면, 첫 렌더링 될 때와 value 값이 바뀔 때마다 effect 함수가 실행된다.

useEffect(() => {
  // 작업
}, [value]);

 

만약 effect 함수가 mount와 unmount 시에 단 한 번씩만 실행되게 하고 싶다면(업데이트될 때 실행되지 않게 하려면), 의존성 배열 자리에 빈 배열을 넣어주면 된다. 이렇게 되면 해당 effect가 props나 state에 있는 어떤 값에도 의존하지 않게 되므로 여러 번 실행되지 않는다.

useEffect(() => {
  // 작업
}, []);

 

그리고 의존성 배열을 생략하게 되면 컴포넌트가 업데이트될 때마다 호출된다.

useEffect(() => {
  // 작업
});

 

useEffect에서는 정리(clean-up) 작업을 할 수도 있다.

만약 effect 함수에 타이머를 등록했거나 이벤트 리스너를 등록했다면, return 값으로 정리 작업을 넣어줄 수 있다. 이렇게 함수를 리턴해주면, 해당 컴포넌트가 언마운트될 때 혹은 다음 렌더링 시 useEffect가 실행되기 이전에 clean-up 함수가 실행된다.

useEffect(() => {
  // 구독 작업...
  
  return () => {
    // 구독 해지...
  }
}, []);

 

아래는 clean-up의 예시이다.

Toggle 버튼을 눌러 Timer 컴포넌트가 화면에 보일지 말지 결정할 수 있으며, Timer가 등장했을 때 1초마다 콘솔창이 찍힌다.

만약 Timer 컴포넌트를 사라지게 한다면, clearInterval가 실행되어 타이머가 중단된다. 만약 이 clean-up이 없다면, Timer는 종료된 것이 아니므로, 화면상에서 Timer 컴포넌트가 보이지 않더라도 콘솔창에서 타이머는 계속해서 작동한다.

import React, { useEffect } from 'react';

const Timer = (props) => {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('타이머 돌아가는 중...');
    }, 1000);

    return () => {
      clearInterval(timer);  // 타이머 clean-up
    };
  }, []);
  
  return (
    <div>
      <span>타이머를 시작합니다. 콘솔을 보세요!</span>
    </div>
  );
};
import React, { useState, useEffect } from 'react';
import Timer from './component/Timer';

function App() {
  const [showTimer, setShowTimer] = useState(false);
  return (
    <div>
      {showTimer && <Timer />}
      <button onClick={() => setShowTimer(!showTimer)}>Toggle</button>
    </div>
  );
}

 

2) 라이프사이클

이러한 useEffect는 Class의 라이프사이클 함수와 유사한 기능을 수행할 수 있다. 즉, useEffect는 componentDidMount와 componentDidUpdate, componentWillUnmount를 한번에 실행할 수 있다.

 

아래는 클래스형 컴포넌트에서의 생명주기 함수 코드이다.

class Example extends Component {
  constructor() {
    super();
    this.state = {
      name: "",
    };
  };
}
// componentDidMount 함수
componentDidMount() {
  console.log("mount");
}
// componentDidUpdate 함수
componentDidUpdate(prevProps, prevState) {
  if (this.state.name !== prevState.name) {
    console.log(`update ${this.state.name}`);
  }
}
// componentWillUnmount 함수
componentWillUnmount() {
  console.log("unmount");
}

render() {
  return <div>{this.state.name}</div>
}

 

useEffect를 활용하면 아래처럼 간단한 코드로 바꿀 수 있다.

const Example = () => {
  const [name, setName] = useState("");
  
  // componentDidMount 함수
  useEffect(() => {
    console.log("mount");
    // componentWillUnmount 함수
    return () => {
      console.log("unmount");
    };
  }, []);
  
  // componentDidUpdate 함수
  useEffect(() => {
    console.log(`update ${name}`);
  }, [name]);
};

 

반응형

 


🌟 React 기술면접 질문

1. React Hook의 장점은 무엇인가요?

 

  • 더 간결한 코드

    : 클래스형 컴포넌트처럼 this 키워드나 생명주기 함수 등의 복잡한 문법을 사용하지 않아도 되므로, 코드 작성량이 줄어들고 가독성도 좋아진다.

  • 컴포넌트 로직의 재사용성

    : 원하는 기능을 함수로 만들어 필요한 곳에 넣어주기만 하면 되기 때문에 로직을 재사용하기 더욱 쉬워진다.

  • 상태 관리의 용이성

    : 다양한 상태관리 hook을 활용해 컴포넌트 내에서 간단하게 상태(state)를 관리할 수 있다.

 

 


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

 


References

 

 

728x90
반응형

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

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