목차
반응형
컴포넌트(클래스 / 함수)
함수 컴포넌트와 클래스 컴포넌트의 차이가 무엇인가요?
클래스 컴포넌트
- 먼저 클래스 컴포넌트는 자바스크립트의 class 키워드로 시작하여 React.Component를 상속받는 형태를 가지고 있다.
- 컴포넌트별로 state 속성을 정의할 수 있고, state 초기화를 위해 constructor 함수를 사용할 수 있다.
- 그리고 컴포넌트 라이프사이클별 메소드를 임의로 정의하여 사용할 수 있다.
- 마지막으로 render 메소드를 통해 컴포넌트가 렌더링 할 JSX를 반환한다.
함수 컴포넌트
- 함수 컴포넌트는 기존에 state를 관리할 수 없고 라이프사이클별 코드를 작성할 수 없어서 선호되지 않았다.
- 하지만 리액트 Hook의 추가로 useState와 같은 Hook을 통해 상태와 여러가지 상황 별 로직을 작성할 수 있게 되었다.
- 함수 컴포넌트를 사용하면 클래스 컴포넌트에 비해 메모리를 덜 사용하는 장점이 있다.
- state나 메소드를 위한 this 키워드를 사용하지 않기 때문에 클래스 컴포넌트의 this 바인딩 문제를 해결할 수 있다.
함수형 컴포넌트와 함수 컴포넌트의 차이가 무엇인가요?
- 리액트 팀에서는 처음에 함수형 컴포넌트라는 단어를 사용했다.
- 함수형 이라는 단어가 함수형 프로그래밍이라는 언어와 겹쳐서, 함수형 프로그래밍이 가능한 컴포넌트로 오해하는 일이 많았다.
- 하지만 함수형 프로그래밍은 순수 함수를 지향하는데, 실제로 함수 컴포넌트에서 Hook을 사용해보면 사이드이펙트가 자주 발생한다.
- 그래서 함수형 프로그래밍과 헷갈리지 않도록 리액트 팀에서도 함수 컴포넌트라고 다시 이름을 바꿨다.
컴포넌트 라이프 사이클에 대해 설명해주세요.
클래스 컴포넌트
- 컴포넌트는 크게 마운트, 업데이트, 언마운트 주기를 가집니다.
- 마운트 : 컴포넌트가 처음 실행될 때를 의미한다. 컴포넌트의 상태를 초기화하고 컴포넌트가 DOM에 설정되어 브라우저에 보여지는 구간이다.
- 업데이트 : props나 state가 변경되었을 때, 부모 컴포넌트가 다시 렌더링 될 때, forceUpdate 메소드를 통해 강제로 렌더링을 트리거 할 때나, componentDidUpdate 메소드를 통해 업데이트 이후 컴포넌트가 추가로 업데이트 된다.
- 언마운트 : 컴포넌트를 DOM 에서 제거하는 과정을 말한다.
함수 컴포넌트
- 함수 컴포넌트는 호출되었을 때 함수 내부가 먼저 실행된다.
- 이후 함수의 반환값으로 반환하는 HTML과 컴포넌트들을 화면에 렌더링한다.
- 이후 생명주기는 useEffecet Hook을 통해 관리할 수 있다.
- 렌더링되면 useEffect가 마운팅 되면서 실행되고, 조건에 따라 useEffect를 통해 업데이트한다.
- 컴포넌트가 언마운트 될 때, 컴포넌트가 제거되기 전에 useEffect의 클린업 함수를 호출하게 된다.
- 이후 DOM에서 컴포넌트를 제거한다.
props와 state에 대해 설명해주세요.
- props는 단방향 바인딩을 지원하는 리액트에서 부모 컴포넌트에서 자식 컴포넌트로 내려주는 데이터이다.
- state는 컴포넌트 내부적으로 관리하는 동적인 데이터이다. 그리고 리액트에서 state가 변경되면 해당 컴포넌트가 다시 렌더링 된다.
Props Drilling 이란 무엇인가요?
- 리액트는 데이터를 전달할 때 부모 컴포넌트에서 자식 컴포넌트로 Props 를 통해 단방향으로 전달한다.
- 이런 특성으로 인해 컴포넌트 계층이 깊어지면 상위 컴포넌트에서 하위 컴포넌트로 어떤 props를 전달하기 위해서 중간에는 props를 전달하기 위해 props를 전달만 하는 컴포넌트가 생길 수 있다.
- 이러한 전달 방식을 props drilling 이라고 한다.
Props Drilling 을 어떻게 해결하나요?
- 리액트 자체적으로 제공하는 Context를 이용할 수 있다.
- 혹은 redux, recoil 과 같은 서드파티 상태관리 라이브러리를 활용할 수 있다.
컴포넌트가 언제 다시 렌더링 되나요?
- 다시 호출되면서 props가 변경될 때
- 컴포넌트 내부의 state가 변경될 때
- 부모 컴포넌트가 다시 렌더링 될 때
- 클래스 컴포넌트의 forceUpdate 메소드가 호출되었을 때
리액트 state의 불변성에 대해 설명해주세요.
- 불변성은 값이나 상태를 변경할 수 없는 특성을 의미한다. 이 때, 변경은 값이 아닌 메모리 영역에서의 변경을 의미한다.
- 리액트에서는 상태(state)가 변경되면 다시 렌더링되는데, 이 변경을 얕은 비교를 통해 수행한다.
- 즉, 값 하나 하나를 비교하는 것이 아니라 이전 참조값과 현재 참조값을 비교하는 방식으로 변화를 감지한다.
- 그래서 상태를 업데이트 할 때, 객체 그대로 업데이트 하는 대신 의도적으로 스프레드 연산자 등을 통해 값은 같아도 새로운 참조 값을 가진 객체를 전달하는 방식으로 불변성을 유지할 수 있다.
스프레드 연산자의 단점에 대해 설명해주세요.
- 스프레드 연산자를 통해 배열과 같은 iterable의 원소들을 나열할 수 있다.
- 객체 리터럴 안에서 사용 할 경우 객체에도 적용할 수 있다.
- 스프레드 연산자로 이런 객체들을 복사하려고 할 때 문제가 생길 수 있다.
- 객체 안에 객체가 있는 등 깊이가 있는 객체들을 복사할 때, 내부에 있는 객체는 참조 값이 복사된다.
- 따라서 복사 로직이 복잡해 질 수 있는데, 간단하게 해결하기 위해 immer 라이브러리 등을 사용할 수 있다.
리액트에서 자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달할 수는 없나요?
- 기본적으로 리액트에서 컴포넌트 사이 데이터 전송은 props로 이루어진다.
- 리액트는 단방향 바인딩이기 때문에 props는 부모 컴포넌트에서 자식 컴포넌트로만 넘겨줄 수 있다.
- 하지만 자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달하기 위한 방법으로 함수를 사용할 수 있다.
- props로 부모 컴포넌트의 상태를 변경하는 함수를 전달하고, 자식 컴포넌트에서 props로 받은 함수를 호출해서 부모 컴포넌트의 상태를 변경할 수도 있다.
Hooks
Hook이란 무엇인가요? (Hook 종류에는 어떤게 있나요?)
- 리액트 16.8 버전에서 추가된 기능으로 함수 컴포넌트에서 상태 관리를 포함한 클래스 컴포넌트의 여러 기능을 처리할 수 있도록 하는 기능이다.
- Hook 등장 이전에는 함수 컴포넌트에서 상태를 관리할 수 없어서 상태 관련된 로직을 재사용하는데에 어려움이 있었다. 그리고 라이프사이클 메소드를 사용할 수 없어서 복잡한 로직을 처리할 수 없었다.
- 리액트 Hook을 통해 함수 컴포넌트에서 상태와 라이프사이클을 후킹할 수 있게 되었다.
- Hook의 종류에는 사용자가 직접 정의하는 커스텀 Hook과, 리액트에서 제공하는 Hook이 있다.
- 대표적인 리액트의 Hook에는 useState, useEffect, useReducer, useContext, useRef, useMemo, useCallback 등이 있다.
useState Hook을 설명해주세요.
- useState는 리액트 Hook 중에서 가장 기본적인 Hook으로, 상태를 관리하기 위한 Hook이다.
- useState의 파라미터로 상태의 초기값을 전달할 수 있고, 첫번째 반환값으로 상태, 두번째 반환값으로 상태를 업데이트하는 함수를 반환한다.
- useState로 생성한 상태를 업데이트하는 함수로 상태값을 변경하면 상태 값이 변경되고, 해당 상태를 가지고 있는 컴포넌트가 다시 렌더링 된다.
useState는 동기적인가요? 비동기적인가요? 그 이유를 설명해주세요.
- useState는 비동기적으로 작동한다.
- 리액트에서 state는 변경될 때 마다 다시 렌더링이 발생한다. 만약 상태 변경이 동기적으로 작동한다면 하나의 상태가 변경될 때 마다 다시 렌더링이 발생할 수 있다.
- 그러나 useState는 비동기적으로 작동한다. 리액트에서는 state batch update 라는 이름으로 일정 기간동안 변경된 상태를 실제 DOM에 일괄적으로 업데이트 한다. 리액트는 이를 통해 렌더링 횟수를 줄여서 성능을 최적화 해 줍니다.
useEffect Hook을 설명해주세요.
- useEffect는 특정한 상황에서 사이드이펙트를 실행시킬 수 있는 Hook이다.
- useEffect의 첫번째 파라미터에는 사이드이펙트를 실행할 콜백함수, 두번째 파라미터에는 배열을 입력한다.
- 이펙트 함수는 렌더링 이후 실행되는 함수면서, 만약 이펙트 함수의 반환값으로 콜백 함수가 있다면 컴포넌트가 언마운트 될 때 실행된다. 이때 실행되는 콜백 함수를 클린업 함수라고 한다.
- 두번째 파라미터로 전달된 배열은 의존성 배열이라고 부르면서 렌더링 될 때 뿐만 아니라 배열 내의 값 중 하나라도 업데이트가 되면 이펙트 함수가 실행된다.
- useEffect Hook을 적절히 사용하면 클래스 컴포넌트에서 사용하는 생명주기 메소드와 비슷한 기능을 구현할 수 있다.
useLayoutEffect Hook을 설명해주세요.
- useEffect Hook은 브라우저가 화면을 페인트 한 후 실행된다. 그래서 useEffect Hook을 통해 화면에 표시할 값을 변경 할 경우 처음 그려진 화면이 먼저 표시되고, 그 이후에 한번 더 화면이 바뀌게 된다.
- useLayoutEffect는 useEffect Hook과 다르게 리액트 돔이 업데이트 된 직후 실행되서 브라우저가 페인팅 되기 전에 실행된다. useEffect Hook을 사용했을 때 발생하는 약간의 문제점을 해결할 수 있다.
useReducer Hook을 설명해주세요.
- 먼저 reducer는 현재 상태와 액션 객체를 파라미터로 받아와서 다음 상태를 반환하는 순수 함수이다.
- useReducer 함수는 파라미터로 순수 함수인 리듀서 함수와 초기 상태값을 입력한다.
- 그리고 첫번째 반환값으로 상태 객체, 두번째 반환값으로 디스패쳐 함수를 반환한다.
- 반환된 디스패쳐 함수에 정의한 액션 타입에 맞는 객체를 전달해서 상태를 변경시킬 수 있다.
- useReducer Hook을 이용하면 상태를 정의하고, 상태를 업데이트하는 복잡한 로직을 조금 더 효율적으로 관리할 수 있다.
useContext Hook을 설명해주세요.
- Context API는 리액트에서 일반적인 props를 통한 전달 대신에 전역적으로 값을 공유할 수 있도록 제공하는 기능이다.
- React.createContext 함수를 통해 공유할 컨텍스트 객체를 생성하고, 컨텍스트 객체의 Provider 컴포넌트를 이용해 하위 컴포넌트에 컨텍스트의 변화를 알릴 수 있다.
- useContext 는 이렇게 생성된 컨텍스트를 함수형 컴포넌트에서 사용하는 Hook 이다.
Context API 는 어디에 사용되나요?
- 데이터를 공유할 때 사용한다. 대표적으로 프로그램 전체에서 사용하는 테마 정보를 공유할 수 있다.
Context API 를 상태관리 라이브러리처럼 사용할 수 있나요?
- 결과적으로 사용은 가능한다.
- 하지만 상태관리를 위해 사용했을 때 렌더링 이슈가 발생할 수 있다.
- Context API 는 상태관리를 위한 도구가 아니라 단순히 공유를 위한 도구이다. 따라서 useState Hook 등으로 따로 공유할 상태를 설정하게 된다.
- 이렇게 상태를 관리하면, 다른 상태관리 라이브러리와는 다르게 상태가 변경되면 Provider로 감싸진 컴포넌트의 재렌더링이 발생하게 된다.
- 그래서 상태를 사용하지 않더라도 다시 렌더링이 발생하는 이슈가 있다.
useRef Hook을 설명해주세요.
- 리액트에서 DOM을 조작하는 방식과 다르게, ref 는 DOM Element에 직접 접근하는 객체이다.
- useRef Hook을 사용하면 querySelector와 같은 방식으로 접근할 수 있는 ref 오브젝트를 반환한다.
- ref 오브젝트는 일종의 참조값이고, ref.current라는 속성에 실제로 값을 저장할 수 있다.
- ref 에는 특정 값을 저장할 수도 있고, 필요에 따라 DOM Element의 ref 속성으로 전달하여 해당 DOM Element를 직접 접근할 수 있다.
제어 컴포넌트와 비제어 컴포넌트에 대해 설명해주세요.
- 제어 컴포넌트는 리액트 안에서 state 등으로 렌더링과 같은 부분을 관리하는 컴포넌트를 의미한다.
- 비제어 컴포넌트는 리액트가 아닌 자바스크립트를 이용하여 관리하는 컴포넌트로, ref 를 이용해서 값을 참조하여 관리한다.
ref 에 대해 알고 있나요?
- ref 는 컴포넌트 라이프사이클과 별개로 유지되는 오브젝트이다. 일반적으로 DOM Element에 직접 접근해서 조작할 때 사용한다.
- 생성된 ref 는 힙 메모리에 자바스크립트 객체로 저장되고 매번 렌더링 때 마다 생성된 동일한 객체를 반환한다.
- 객체 내부의 current 값이 변경되더라도 컴포넌트를 재렌더링 시키지 않는다는 특징이 있다.
ref 는 언제, 어떻게 사용하나요?
- ref 오브젝트는 createRef 나 useRef 를 호출해서 생성할 수 있다.
- 이렇게 생성한 ref 오브젝트를 JSX 로 표현한 Element 의 ref 속성에 할당해서 그 Element를 참조할 수 있다.
- 대표적으로 컴포넌트가 렌더링 될 때 input 태그에 focus가 되도록 하거나, 미디어 재생을 조작하거나 하는 부분에서 사용할 수 있다.
callback ref 에 대해 알고 있나요?
- Element의 ref 속성에는 두가지 타입을 입력할 수 있다. 하나는 RefObject 타입이고, 다른 하나가 RefCallback 타입이다.
- callback ref는 RefCallback 타입으로, ref 객체가 아닌 함수를 전달하는 방식이다.
- node 라고 하는 Element를 파라미터로 받아서 실행되는 함수로, 컴포넌트가 렌더링 된 후에 실행된다.
- node 파라미터에는 callback ref 를 등록한 그 Element가 전달된다.
- DOM Element가 렌더링 후에 상호작용 해야 하는 경우 사용한다.
forwardRef 에 대해 알고 있나요?
- forwardRef 는 ref를 컴포넌트간 전달하기 위해 사용한다.
- 이때 리액트에서 ref는 key와 마찬가지로 props가 아닌 다른 용도로 전달되도록 설정되어 있다.
- 그래서 하위 컴포넌트의 props에서 전달받은 ref를 참조할 수 없다.
- 이때 하위 컴포넌트를 forwardRef 함수로 감싸게 되면 부모 컴포넌트에서 전달한 ref를 하위 컴포넌트에서 참조할 수 있다.
useMemo Hook을 설명해주세요.
- useMemo Hook은 말 그대로 이전에 저장된 값을 메모이제이션 하는 Hook이다.
- useMemo Hook의 첫번째 파라미터로 콜백 함수, 두번째 파라미터로 배열을 저장한다.
- 콜백 함수에서는 실제로 저장할 값을 계산하는 로직을 포함한다.
- useEffect의 의존성 배열과 마찬가지로 useMemo의 두번째 파라미터인 의존성 배열에 포함된 값이 업데이트 될 때 마다 해당 콜백 함수를 다시 실행해서 값을 업데이트 한다.
- useMemo를 적절히 활용하면 오래 걸리는 복잡한 연산의 결과값을 저장해놓고 사용해서 최적화를 할 수 있다.
useCallback Hook을 설명해주세요.
- useCallback Hook은 useMemo Hook과 비슷한 기능으로 함수를 메모이제이션 하는 Hook이다.
- useCallback Hook도 첫번째 파라미터로 저장할 콜백 함수, 두번째 파라미터로 배열을 저장한다.
- 두번째 파라미터로 제공된 의존성 배열에 포함된 값이 업데이트 될 때 마다 콜백 함수를 다시 생성한다.
리액트에서 성능 개선을 하는 방법을 설명해주세요.
- 첫번째는 useMemo, useCallback 으로 시간이 오래걸리는 계산 결과를 메모이제이션하는 방법이 있다.
- 두번째로는 코드 스플리팅 방식이 있다. 리액트를 사용한 자바스크립트 코드를 하나로 묶어서 빌드하면 굉장히 크기가 크기 때문에 페이지를 로딩하는 속도가 느려지게 된다. 이를 해결하기 위해서 다이나믹 import 를 사용하거나 React.Lazy를 사용해 컴포넌트를 비동기적으로 로딩할 수 있다. 또는 Next.js 프레임워크를 사용하면 프레임워크에서 코드 스플리팅을 지원한다.
useMemo와 useCallback Hook의 공통점과 차이점을 설명해주세요.
- 가장 큰 차이는 useMemo는 값을, useCallback은 함수를 저장한다는 점이다.
- 하지만 둘 다 기존에 저장하던 값을 계속 저장하면서 렌더링마다 다시 계산해야 하는 불필요한 과정을 최적화 할 수 있다.
- 그리고 useCallback은 단순히 useMemo 콜백 함수에 콜백 함수를 저장하는 것과 동일한 기능을 한다.
Hook 라이프사이클
componentDidMount, componentDidUpdate 등 클래스 컴포넌트에서 사용되던 생명주기 메소드를 함수 컴포넌트에서는 어떻게 구현하나요?
- useEffect Hook을 통해 구현할 수 있다.
- useEffect는 사용에 따라 컴포넌트가 마운트 되었을 때, 컴포넌트의 특정 상태가 업데이트 될 때, 컴포넌트가 언마운트(제거)될 때 모두 처리할 수 있다.
- 왜냐하면 useEffect Hook은 의존성이 변경될 때 마다 등록된 사이드이펙트 함수를 실행하는 Hook이다.
- 의존성에 변경을 감지할 상태나 props를 등록하면 componentDidUpdate, getDerivedStateFromProps 처럼 사용할 수 있다.
- 그리고 의존성에 빈 배열을 등록하면 처음 마운트 되었을 때만 실행되는 componentDidMount 처럼 사용할 수 있다.
- 마지막으로 사이드이펙트 함수의 반환값으로 콜백 함수를 등록할 수 있다.
이 콜백 함수는 컴포넌트가 언마운트(제거)될 때 실행되는 함수로 클린업 함수라고 한다. 이를 통해 componentWillUnmount 처럼 사용할 수 있다.
클래스 컴포넌트의 forceUpdate를 알고 있나요?
- forceUpdate는 리액트에서 공식적으로 제공하는 API로 해당 컴포넌트를 상태의 변화가 없더라도 재렌더링 시키는 메소드이다.
함수 컴포넌트에서는 사용할 수 없나요?
- 함수 컴포넌트에서는 공식적으로 지원하고 있지는 않지만 약간의 트릭을 이용할 수 있다.
- 상태가 변경되면 해당 컴포넌트가 다시 렌더링 되므로 useState를 통해 상태를 선언하고 forceUpdate라는 함수를 선언해서 상태를 변경하면 호출 시 다시 렌더링 되게 됩니다.
- 필요하다면 사용자 정의 Hook으로 만들어 사용할 수 있다.
// forceUpdate 역할을 하는 사용자 정의 Hook
import { useState } from 'react';
function useForceUpdate() {
const [, setTick] = useState(0);
const update = () => {
setTick(tick => tick + 1);
}
return update;
}
function MyComponent() {
const forceUpdate = useForceUpdate();
const handleClick = () => {
// 상태 값을 변경하지 않고, 강제로 컴포넌트를 다시 렌더링
forceUpdate();
}
return (
<button onClick={handleClick}>
강제로 다시 렌더링
</button>
);
}
Reference
반응형