FE 인터뷰 준비 - 리액트

Shyuuuuni·2023년 2월 17일
172

💬 개발자 면접

목록 보기
3/3
post-thumbnail

모의 면접 스터디를 하면서 준비한 질문/답변 + prepare_frontend_interview 에 있는 질문들을 정리한 포스팅입니다.
최근 업데이트: 23/02/17

React

React는 라이브러리인가요 프레임워크인가요?

  • 공식문서에서도, 리액트는 View 에 관련된 기능 개발을 위한 라이브러리라고 소개하고 있습니다.
  • 리액트를 사용할 때, 라우팅이나 상태관리와 같은 개발에 필요한 여러 기능들은 서드파티 라이브러리에 의존해야 합니다.
  • 따라서 리액트는 개발을 위한 자바스크립트 라이브러리라고 생각합니다.

라이브러리와 프레임워크의 차이를 알고 있나요?

  • 라이브러리는 특정 기능 개발을 도와주는 도구들을 모아놓은 개념이고, 프레임워크는 프로그램을 개발하기 위한 구조를 제공하는 개념입니다.
  • 그래서 프레임워크를 사용하면 개발에 대한 전체적인 기능을 제공하고, 프로그램 제어 흐름을 프레임워크에서 관리하는 차이점이 있습니다. 이런 특징을 제어의 역전이라고 합니다.

리액트의 특징을 설명해주세요. (리액트를 사용한 이유)

  • 리액트는 단방향 데이터 플로우를 가지고 있습니다. 부모에서 자식 컴포넌트로 props를 전달하는 방식으로 작동합니다.
  • 리액트는 가장 많은 사용자가 사용하는 라이브러리로, 생태계가 넓어서 많은 문서와 라이브러리가 존재합니다.
  • 리액트는 컴포넌트라는 단위로 개발을 진행합니다. 독립적인 컴포넌트 단위로 나눠서 개발하고 관리하기 때문에 유지보수가 용이합니다. 그리고 반복되는 기능을 컴포넌트를 재사용하여 해결할 수 있습니다.
  • 그리고 가상 돔을 사용합니다. 리액트를 사용하면 메모리상에 가상의 돔 오브젝트를 생성하고 관리합니다. 가상 돔을 통해 뷰 변경사항을 확인하고 실제 돔에 업데이트 하는 기능을 리액트가 담당합니다. 또한 가상돔에서 변경 사항을 비교하는 과정을 통해 돔 업데이트 횟수를 최적화하여 렌더링 속도를 어느정도 최적화 할 수 있습니다.
  • 그 외에도 싱글 페이지 어플리케이션 개발을 용이하게 하고, 리액트 네이티브라는 모바일 어플리케이션 프레임워크를 통해 네이티브 플랫폼 개발로 확장할 수 있습니다.

Suspense

  • Suspense는 리액트에서 컴포넌트가 렌더링하기 전에 다른 작업이 진행되는 것을 대기하는 방식입니다.
  • 컴포넌트에서 사용할 데이터가 준비되지 않았다는 걸 리액트에게 알리고, 준비하는 동안 보여줄 컴포넌트를 설정할 수 있습니다.
  • Suspense를 사용하면 데이터 페칭과 로딩 상황, 에러 상황의 처리 등을 선언적으로 처리할 수 있습니다.
  • 그리고 Suspense가 중첩되어도 순서 등에 상관없이 병렬적으로 데이터를 페칭할 수 있습니다.

JSX (JavaScript XML)

JSX 를 알고있나요?

  • JSX는 자바스크립트 XML 라는 의미로, 자바스크립트 문법을 확장한 문법입니다.
  • 리액트에서는 JSX 문법으로 작성된 코드를 해석하고, 실행할 때 React Element 로 변환해서 사용하고 있습니다.

리액트에서 JSX 를 사용하면 어떤 장점이 있나요?

  • HTML 문법을 알고 있다면 UI 작업 시 더 직관적으로 코드를 이해할 수 있습니다.
  • 그리고 리액트는 마크업 파일과 여러가지 로직을 담고있는 파일을 분리하지 않고 컴포넌트라는 개념으로 하나의 파일에 코드를 작성합니다. 그래서 높은 응집도의 코드를 작성할 수 있습니다.
  • 마지막으로 느슨한 연결을 통해 관심사의 분리가 가능합니다. JSX 문법으로 표현한 UI 부분과, 상태를 변경하거나 하는 렌더링 로직을 분리해서 작성할 수 있습니다.

리액트에서 JSX 가 어떻게 작동하나요?

  • 리액트에서 JSX 는 리액트 라이브러리의 createElement 를 Syntax Sugar 한 개념입니다.
  • JSX 문법이 적용된 코드는 babel 을 통해 React.createElement 라는 함수 호출로 변경됩니다.

React 가 직접 호출되지 않는데 import React 를 해야 하는 이유를 알고 있나요?

  • 리액트는 JSX 라는 문법을 사용하는데, 리액트 17 이전에는 JSX 문법으로 작성된 코드를 babel 을 통해 React.createElement 라는 함수 호출로 변경했습니다.
  • 그래서 JSX 로 작성된 코드에서는 보이지 않지만, 실제로 실행할 때에는 React 가 import 되어야합니다.
  • 하지만 리액트 17버전 이후부터는 babel 에서 빌드 시점에 따로 처리해 주기 때문에 import 할 필요가 없게 되었습니다. (좋은 글 : https://so-so.dev/react/import-react-from-react/)

리액트에서 JSX 사용 시 주의해야 할 점이 있을까요?

  • 컴포넌트를 JSX 문법으로 호출할 때, 컴포넌트 이름은 반드시 대문자로 시작해야 합니다.
    • (이유) 왜냐하면 소문자로 시작하는 Element 는 내장 컴포넌트로 인식합니다. 내장 컴포넌트는 createElement 에서 ‘div’나 ‘span’ 같은 문자열 형태로 전달하고 처리하기 때문에 문제가 발생합니다.
  • 그리고 HTML 에서 Element 의 속성으로 class 속성을 많이 사용하는데, JSX도 자바스크립트 문법을 확장한 문법이기 때문에 class 라는 키워드를 사용할 수 없습니다. 대신에 className 이라는 속성을 사용합니다. (추가적으로 몇몇 속성들도 camelCase 를 사용해야 합니다.)

가상 돔 (Virtual DOM)

리액트는 어떻게 화면을 변경하나요?

  • 리액트는 Virtual DOM 이라는 개념을 통해 UI 를 관리하고, 실제 DOM 과 동기화해서 화면을 변경합니다.
  • 조금 더 자세하게 설명드리면, 변경사항들을 메모리 상의 Virtual DOM 에 먼저 적용하고, 바뀐 부분만 실제 DOM 에 적용합니다.

가상 돔을 사용하는 이유가 있을까요?

  • 자바스크립트 코드를 통해 화면을 나타내는 DOM 을 수정하면 렌더링 과정 중 레이아웃이나 페인트 과정이 다시 발생하게 됩니다. 이 과정을 리플로우와 리페인트라고 합니다.
  • 반응형 웹에서 이런 수정이 많이 발생해서 리플로우와 리페인트, 특히 리플로우 과정이 많이 발생하면 브라우저 성능이 떨어지게 됩니다.
  • 그래서 리액트는 Virtual DOM 에 변경 사항을 적용하고, 실제로 변경된 부분을 계산해서 DOM 에 일괄로 적용하는 Batch Update 를 적용해서 성능을 최적화 했습니다.
  • 그리고 개발자는 선언적으로 코드를 작성할 수 있습니다. 어떤 부분을 어떻게 실제 DOM 에 적용하는지와 같은 과정을 Virtual DOM 에게 위임할 수 있습니다.

리액트에서 가상 돔 작동 방식을 설명해주세요.

~~- 리액트는 재조정 이라는 과정을 통해 Virtual DOM 을 이용해서 실제 DOM 을 조작합니다.

  • 리액트는 항상 두개의 Virtual DOM 을 가지고 있습니다. 하나는 업데이트를 위한 Virtual DOM, 다른 하나는 업데이트가 되지 않은 Virtual DOM 입니다.
  • 일정 시간동안 컴포넌트의 상태가 변경되면 변경사항을 업데이트를 위한 Virtual DOM 트리에 적용합니다.
    • (참고) Virtual DOM 에 변경사항을 적용하는 부분은 오래 걸리지 않습니다.
  • 그리고 시간이 지나면 업데이트된 Virtual DOM 과 업데이트 전의 Virtual DOM 을 diffing 알고리즘을 사용해서 비교합니다.
  • diffing 알고리즘의 결과로 실제로 변경된 부분을 알 수 있고, 그 부분을 일괄적으로 실제 DOM 에 적용합니다.
  • 이렇게 Virtual DOM 을 사용해 여러개의 상태 변경을 일괄적으로 실제 DOM 에 적용하는 것을 Batch Update 라고 합니다.~~

리액트에서는 가상 돔의 변경점을 어떻게 알아내나요? (diffing 알고리즘)

  • 리액트는 두개의 가상돔을 가지고 있고, 업데이트 된 가상돔과 업데이트 전 가상돔을 비교해서 변경사항을 찾고 있습니다.
  • 모든 DOM 트리를 비교하는 알고리즘은 O(n^3) 시간이 들어서, 리액트에서는 특정 조건에서 휴리스틱한 O(n) 알고리즘을 사용합니다.
    • 첫번째 조건은 타입이 다른 Element 는 다른 트리를 만든다는 가정이고, 두번째 조건은 개발자가 key 라는 Props 를 통해 트리의 변경 사항을 알려준다는 가정입니다.
  • 그래서 비교 과정에서 각 Virtual DOM 트리의 루트부터 비교를 시작하는데, 다른 Element로 변경됐을 경우 해당 서브트리를 제거하고 새로운 서브트리를 적용합니다.
  • 만약 같은 Element 라면 className 이나 다른 props 들을 비교하여 변경사항만 적용합니다.
  • 그리고 이 과정을 재귀적으로 하위 Element에 대해 반복합니다.

fiber (파이버) 란 무엇인가요?

  • fiber는 리액트 가상 돔 변경 과정에서 발생하는 동시성, 애니메이션과 같은 문제점들을 해결하기 위해 적용한 알고리즘입니다.

컴포넌트의 key 에 대해 설명해주세요.

  • key 은 리액트에서 Element 의 고유성을 부여하기 위한 값으로, 리액트에서 Element를 식별하고, 변경하거나 제거할 때 사용합니다.
  • 그래서 Element 리스트를 표현할 때에는 각 Element 마다 항목을 고유하게 식별할 수 있는 id를 key로 전달하게 됩니다.
  • 이렇게 전달된 key는 리액트에서 변경을 감지할 때 사용합니다. key가 없다면 자식 Element 를 비교할 때, 하나라도 다르면 트리를 다시 구성해서 나쁜 성능을 보입니다.
  • 하지만 key 을 통해, key 값을 기준으로 추가되거나 삭제된 Element 를 식별하고, 이를 기반으로 갱신합니다.
    • (그래서 변경될 수 있는 index와 같은 값을 key로 사용하는 것 보다는 고유한 id를 사용해야 합니다.)

랜더링(SPA, SSR 등)

SPA 에 대해 설명해주세요.

  • SPA 는 싱글 페이지 어플리케이션의 약자로, 하나의 페이지로 이루어진 어플리케이션이라는 의미입니다.
  • 전통적인 멀티 페이지 어플리케이션 방식은 페이지가 변경될 때 마다 서버에서 새로운 HTML 파일을 응답받아서 표시하는 방식을 사용했습니다.
  • 하지만 최근 클라이언트 환경의 성능이 높아져서 리액트와 같은 라이브러리를 통해 클라이언트가 뷰 구을성서담당할 수 있게 되었습니다. 그리고 AJAX 통신을 통해서 전체 페이지가 아닌 필요한 데이터에 대해서만 서버 API 호출을 통해서 받아오고, 그걸 적용해서 페이지 새로고침 없이 뷰를 수정할 수 있게 되었습니다.

SPA 특징과 장점을 설명해주세요.

  • 주로 싱글 페이지 어플리케이션은 클라이언트 사이드 렌더링 방식을 사용합니다.
  • 처음 요청에 따라서 서버에서 HTML 파일과 함께 렌더링에 필요한 자바스크립트, CSS 파일을 받아옵니다. HTML 파일을 받아오고 나서 자바스크립트를 통해 돔을 수정하는 방식으로 작동합니다.
  • 그래서 첫 페이지 로딩 이후에는 전체 페이지가 아닌 변경이 필요한 부분의 데이터만 서버와 통신하여 업데이트합니다. 그래서 로딩 이후 전체적인 트래픽을 줄일 수 있고, 인터렉티브한 사용자 경험을 제공할 수 있습니다.
  • 그리고 부분적으로 업데이트 하는 방식이기 때문에 컴포넌트별 개발 방식과 잘 맞습니다.

SPA 단점을 설명해주세요.

  • 싱글 페이지 어플리케이션은 처음 요청시에 서버에서 렌더링 로직을 포함한 HTML, 자바스크립트, CSS 파일을 모두 가져옵니다. 많은 리소스를 한번에 받아와야 하기 때문에 처음 로딩 속도가 상대적으로 느립니다.
  • 그리고 검색엔진 최적화 이슈가 발생할 수 있습니다. 일반적으로 싱글 페이지 어플리케이션은 클라이언트 사이드 렌더링 방식을 사용하는 경우가 많습니다. 그래서 구글 크롤러 봇처럼 자바스크립트를 실행할 수 있는 봇이 아니면, 아직 렌더링되지 않은 비어있는 HTML을 크롤링 하는 문제가 발생할 수 있습니다.

SEO(검색엔진최적화) 가 뭔가요?

  • 구글과 같은 사이트의 검색 엔진들이 서버에 등록된 사이트를 돌아다니면서 문서를 분석합니다. 분석한 데이터로 검색 결과를 표시하게 되는데, 이때 콘텐츠에 포함된 정보를 검색엔진이 잘 수집할 수 있도록 하는게 검색엔진 최적화입니다.

클라이언트 사이드 렌더링에서 검색엔진 최적화 이슈가 발생하는 이유가 있나요?

  • 클라이언트 사이드 렌더링으로 작성된 사이트들은 일반적으로 처음 접속하면 body가 비어있는 HTML 파일을 받아오고, 자바스크립트를 통해 동적으로 콘텐츠를 렌더링합니다.
  • 검색엔진의 봇은 사이트를 돌아다니면서 HTML 문서 정보를 수집하는데, 싱글 페이지 어플리케이션에서는 body가 비어있는 HTML 문서를 수집하게 됩니다.
  • 구글의 봇은 자바스크립트를 실행하여 수집할 수 있지만, 대부분의 봇들은 싱글 페이지 어플리케이션의 비어있는 문서를 수집하기 때문에 검색엔진 최적화가 잘 되지 않습니다.

검색엔진 최적화를 할 수 있는 방법을 설명해주세요.

  • 서버 사이드 렌더링을 적용하는 방법이 있습니다. 서버 사이드 렌더링은 서버에서 HTML문서를 만들어서 응답하기 때문에 사용자도, 검색엔진도 완성된 HTML 파일을 응답받기 때문입니다.
  • 클라이언트 사이드 렌더링을 사용한다면, 사전 렌더링 방식을 적용할 수 있습니다. 검색 엔진이 빈 HTML 파일 대신 내용이 포함된 HTML을 가져갈 수 있도록 빌드타임에 데이터가 포함된 HTML 파일을 생성하는 방식을 사용할 수 있습니다.
  • 그리고 제목이나 헤드 태그 등 HTML 파일에 문서 정보를 잘 나타낼 수 있도록 설정해야 합니다.

TTV, TTI를 알고 있나요? (time-to-view, time-to-interect)

  • TTV는 타임 투 뷰의 약자로, 콘텐츠가 화면에 보여지는 시점이고, TTI는 타임 투 인터렉트로 사용자와 상호작용이 가능한 시점을 의미합니다.
  • 클라이언트 사이드 렌더링을 사용하면 TTV와 TTI가 동일합니다. 왜냐하면 서버에서 빈 HTML 파일을 받아오고, 자바스크립트 파일도 모두 가져온 이후에 렌더링하기 때문에 보이는 시점과 상호작용 하는 시점이 동일합니다.
  • 하지만 서버 사이드 렌더링을 사용하면 TTV가 TTI보다 더 빠릅니다. 왜냐하면 서버에서 완성된 HTML 파일을 받아오면 콘텐츠가 화면에 먼저 보이게 됩니다. 그리고 나서 서버에 인터렉션을 위한 자바스크립트를 요청하게 되고, 응답 받게 되면 그때부터 실제로 상호작용이 가능합니다.

컴포넌트

함수 컴포넌트와 클래스 컴포넌트의 차이가 무엇인가요?

클래스 컴포넌트

  • 먼저 클래스 컴포넌트는 JS의 class 키워드로 시작하여 React.Component를 상속받는 형태를 가지고 있습니다.
  • 컴포넌트별로 state 속성을 정의할 수 있고, state 초기화를 위해 constructor 함수를 사용할 수 있습니다.
  • 그리고 컴포넌트 라이프사이클별 메소드를 임의로 정의하여 사용할 수 있습니다.
  • 마지막으로 render 메소드를 통해 컴포넌트가 렌더링 할 JSX를 반환합니다.

함수 컴포넌트

  • 함수 컴포넌트는 기존에 state를 관리할 수 없고 라이프사이클 별 코드를 작성할 수 없어서 선호되지 않았었습니다.
  • 하지만 리액트 훅 추가로 useState와 같은 훅을 통해 상태와 여러가지 상황 별 로직을 작성할 수 있게 되었습니다.
  • 함수 컴포넌트를 사용하면 클래스 컴포넌트에 비해 메모리를 덜 사용하는 장점이 있습니다. 그리고 state나 메소드를 위한 this 키워드를 사용하지 않기 때문에 클래스 컴포넌트의 this 바인딩 문제를 해결할 수 있습니다.

함수형 컴포넌트와 함수 컴포넌트의 차이가 무엇인가요?

  • 리액트 팀에서는 처음에 함수형 컴포넌트라는 단어를 사용했습니다.
  • 함수형 이라는 단어가 함수형 프로그래밍이라는 언어와 겹쳐서, 함수형 프로그래밍이 가능한 컴포넌트로 오해하는 일이 많았습니다.
  • 하지만 함수형 프로그래밍은 순수 함수를 지향하는데, 실제로 함수 컴포넌트에서 훅을 사용해보면 사이드이펙트가 자주 발생합니다.
  • 그래서 함수형 프로그래밍과 헷갈리지 않도록 리액트 팀에서도 함수 컴포넌트라고 다시 이름을 바꿨습니다.

컴포넌트 라이프 사이클에 대해 설명해주세요.

클래스 컴포넌트

  • 컴포넌트는 크게 마운트, 업데이트, 언마운트 주기를 가집니다.
  • 마운트 단계는 컴포넌트가 처음 실행될 때를 의미합니다. 컴포넌트의 상태를 초기화하고, 컴포넌트가 돔에 설정되어 브라우저에 보여지는 구간입니다.
  • 다음으로 업데이트는 props나 state가 변경되었을 때, 부모 컴포넌트가 다시 렌더링 될 때, forceUpdate 메소드를 통해 강제로 렌더링을 트리거 할 때나, componentDidUpdate 메소드를 통해 업데이트 이후 컴포넌트가 추가로 업데이트 됩니다.
  • 마지막으로 언마운트 과정을 통해 컴포넌트를 DOM 에서 제거합니다.

함수 컴포넌트

  • 함수 컴포넌트는 호출되었을 때 함수 내부가 먼저 실행됩니다.
  • 이후 함수의 반환값으로 반환하는 HTML과 컴포넌트들을 화면에 렌더링합니다.
  • 이후 생명주기는 useEffecet 훅을 통해 관리할 수 있습니다.
  • 렌더링되면 useEffect가 마운팅 되면서 실행되고, 조건에 따라 useEffect 를 통해 업데이트합니다.
  • 컴포넌트가 언마운트 될 때에는, 컴포넌트가 제거되기 전에 useEffect의 clean up 함수를 호출하게 됩니다.
  • 이후 DOM 에서 컴포넌트를 제거합니다.

props와 state에 대해 설명해주세요.

  • props는 단방향 바인딩을 지원하는 리액트에서 부모 컴포넌트에서 자식 컴포넌트로 내려주는 데이터입니다.
  • state는 컴포넌트 내부적으로 관리하는 동적인 데이터입니다. 그리고 리액트에서 state가 변경되면 해당 컴포넌트가 다시 렌더링 됩니다.

Props Drilling 이란 무엇인가요?

  • 리액트는 데이터를 전달할 때 부모 컴포넌트에서 자식 컴포넌트로 Props 를 통해 단방향으로 전달합니다.
  • 이런 특성으로 인해 컴포넌트 계층이 깊어지면, 상위 컴포넌트에서 하위 컴포넌트로 어떤 props 를 전달하기 위해서 중간에 props를 전달하기 위해서 props 받고 넘겨주는 컴포넌트가 생길 수 있습니다.
  • 이러한 전달 방식을 props drilling 이라고 합니다.

Props Drilling 을 어떻게 해결하나요?

  • 리액트 자체적으로 제공하는 Context 를 이용할 수 있습니다.
  • 아니면 redux, recoil 과 같은 서드파티 상태관리 라이브러리를 활용할 수 있습니다.

컴포넌트가 언제 다시 렌더링 되나요?

  • 다시 호출되면서 props가 변경되거나,
  • 컴포넌트 내부의 state가 변경되거나,
  • 부모 컴포넌트가 다시 렌더링 되거나,
  • 클래스 컴포넌트의 forceUpdate 메소드가 호출되었을 때 발생합니다.

리액트 state의 불변성에 대해 설명해주세요.

  • 불변성은 값이나 상태를 변경할 수 없는 특성을 의미합니다. 이때 변경은 값이 아닌 메모리 영역에서의 변경을 의미합니다.
  • 리액트에서는 상태가 변경되면 다시 렌더링되는데, 이 변경을 얕은 비교를 통해 수행합니다.
  • 즉 값 하나 하나를 비교하는 것이 아니라, 이전 참조값과 현재 참조값을 비교하는 방식으로 변화를 감지합니다.
  • 그래서 상태를 업데이트 할 때, 객체 그대로 업데이트 하는 대신 의도적으로 스프레드 연산자 등을 통해 값은 같아도 새로운 참조 값을 가진 객체를 전달하는 방식으로 불변성을 유지할 수 있습니다.

스프레드 연산자의 단점에 대해 설명해주세요.

  • 스프레드 연산자를 통해 배열과 같은 iterable의 원소들을 나열할 수 있습니다. 그리고 객체 리터럴 안에서 사용 할 경우 객체에도 적용할 수 있습니다.
  • 스프레드 연산자로 이런 객체들을 복사하려고 할 때 문제가 생길 수 있습니다.
  • 객체 안에 객체가 있는 등 깊이가 있는 객체들을 복사할 때, 내부에 있는 객체는 참조값이 복사되게 됩니다.
  • 그래서 복사 로직이 복잡해 질 수 있고, 간단하게 해결하기 위해서 immer 라이브러리 등을 사용할 수 있습니다.

리액트에서 자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달할 수는 없나요?

  • 기본적으로 리액트에서 컴포넌트 사이 데이터 전송은 props로 이루어집니다.
  • 리액트는 단방향 바인딩이기 때문에 props는 부모 컴포넌트에서 자식 컴포넌트로만 넘겨줄 수 있습니다.
  • 하지만 자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달하기 위한 방법으로 함수를 사용할 수 있습니다. props로 부모 컴포넌트의 상태를 변경하는 함수를 전달하고, 자식 컴포넌트에서 props로 넘겨받은 함수를 호출해서 부모 컴포넌트의 상태를 변경할 수도 있습니다.

Hook

훅이란 무엇인가요? (훅 종류에는 어떤게 있나요?)

  • 리액트 16.8 버전에서 추가된 기능으로, 함수 컴포넌트에서 상태 관리를 포함한 클래스 컴포넌트의 여러 기능을 처리할 수 있도록 하는 기능입니다.
  • 훅 등장 이전에는 함수 컴포넌트에서 상태를 관리할 수 없어서 상태 관련된 로직을 재사용하기 어려웠습니다. 그리고 라이프사이클 메소드를 사용할 수 없어서 복잡한 로직을 처리할 수 없었습니다.
  • 리액트 훅을 통해 함수 컴포넌트에서 상태와 라이프사이클을 후킹할 수 있게 되었습니다.
  • 훅의 종류에는 사용자가 직접 정의하는 커스텀 훅과, 리액트에서 제공하는 훅이 있습니다.
  • 대표적인 리액트의 훅에는 useState, useEffect, useReducer, useContext, useRef, useMemo, useCallback 등이 있습니다.

useState 훅을 설명해주세요.

  • useState는 리액트 훅 중에서 가장 기본적인 훅으로, 상태를 관리하기 위한 훅입니다.
  • useState의 파라미터로 상태의 초기값을 전달할 수 있고, 첫번째 반환값으로 상태, 두번째 반환값으로 상태를 업데이트하는 함수를 반환합니다.
  • useState로 생성한 상태값을 함수로 변경하면 상태 값이 변경되고, 해당 상태를 가지고 있는 컴포넌트가 다시 렌더링 됩니다.

useState는 동기적인가요? 비동기적인가요? 그 이유를 설명해주세요.

  • useState는 비동기적으로 작동합니다.
  • 리액트에서 state는 변경될 때 마다 다시 렌더링이 발생합니다. 만약 상태 변경이 동기적으로 작동한다면 하나의 상태가 변경될 때 마다 다시 렌더링이 발생할 수 있습니다.
  • 그러나 useState는 비동기적으로 작동합니다. 리액트에서는 state batch update 라는 이름으로, 일정 기간동안 변경된 상태를 실제 DOM에 일괄적으로 업데이트 합니다. 리액트는 이를 통해 렌더링 횟수를 줄여서 성능을 최적화 해 줍니다.

useEffect 훅을 설명해주세요.

  • useEffect는 특정한 상황에서 사이드이펙트를 실행시킬 수 있는 훅입니다.
  • useEffect의 첫번째 파라미터에는 사이드이펙트를 실행할 콜백함수, 두번째 파라미터에는 배열을 입력합니다.
  • 이펙트 함수는 렌더링 이후 실행되는 함수면서, 만약 이펙트 함수의 반환값으로 콜백 함수가 있다면 컴포넌트가 언마운트 될 때 실행됩니다. 이때 실행되는 콜백 함수를 클린업 함수라고 합니다.
  • 두번째 파라미터로 전달된 배열은 의존성 배열이라고 부르면서 렌더링 될 때 뿐만 아니라 배열 내의 값 중 하나라도 업데이트가 되면 이펙트 함수가 실행됩니다.
  • useEffect 훅을 적절히 사용하면 클래스 컴포넌트에서 사용하는 생명주기 메소드와 비슷한 기능을 구현할 수 있습니다.

useLayoutEffect 훅을 설명해주세요.

  • useEffect 훅은 브라우저가 화면을 페인트 한 후 실행됩니다. 그래서 useEffect 훅을 통해 화면에 표시할 값을 변경 할 경우 처음 그려진 화면이 먼저 표시되고, 그 이후에 한번 더 화면이 바뀌게 됩니다.
  • useLayoutEffect 는 useEffect 훅과 다르게 리액트 돔이 업데이트 된 직후 실행되서 브라우저가 페인팅 되기 전에 실행됩니다. useEffect 훅을 사용했을 때 발생하는 약간의 문제점을 해결할 수 있습니다.

useReducer 훅을 설명해주세요.

  • 먼저 reducer는 현재 상태와 액션 객체를 파라미터로 받아와서 다음 상태를 반환하는 순수 함수입니다.
  • useReducer 함수는 파라미터로 그런 리듀서함수와 초기 상태값을 입력합니다.
  • 그리고 첫번째 반환값으로 상태 객체, 두번째 반환값으로 디스패쳐 함수를 반환합니다.
  • 반환된 디스패쳐 함수에 정의한 액션 타입에 맞는 객체를 전달해서 상태를 변경시킬 수 있습니다.
  • useReducer 훅을 이용하면 상태를 정의하고, 상태를 업데이트하는 복잡한 로직을 조금 더 효율적으로 관리할 수 있습니다.

useContext 훅을 설명해주세요.

  • Context API는 리액트에서 일반적인 props 를 통한 전달 대신에 전역적으로 값을 공유할 수 있도록 제공하는 기능입니다.
  • React.createContext 함수를 통해 공유할 컨텍스트 객체를 생성하고, 컨텍스트 객체의 Provider 컴포넌트를 이용해 하위 컴포넌트에 컨텍스트의 변화를 알릴 수 있습니다.
  • useContext 는 이렇게 생성된 컨텍스트를 함수형 컴포넌트에서 사용하는 훅 입니다.

Context API 는 어디에 사용되나요?

  • 데이터를 공유할 때 사용합니다. 대표적으로 프로그램 전체에서 사용하는 테마 정보를 공유할 수 있습니다.

Context API 를 상태관리 라이브러리처럼 사용할 수 있나요?

  • 결과적으로 사용은 가능합니다.
  • 하지만 상태관리를 위해 사용했을 때 렌더링 이슈가 발생할 수 있습니다.
  • Context API 는 상태관리를 위한 도구가 아니라 단순히 공유를 위한 도구입니다. 그래서 useState 훅 등으로 따로 공유할 상태를 설정하게 됩니다.
  • 이렇게 상태를 관리하면, 다른 상태관리 라이브러리와는 다르게 상태가 변경되면 Provider로 감싸진 컴포넌트의 재렌더링이 발생하게 됩니다.
  • 그래서 상태를 사용하지 않더라도 다시 렌더링이 발생하는 이슈가 있습니다.

useRef 훅을 설명해주세요.

  • 리액트에서 돔을 조작하는 방식과 다르게, ref 는 돔 요소에 직접 접근하는 객체입니다.
  • useRef 훅을 사용하면 querySelector와 같은 방식으로 접근할 수 있는 ref 오브젝트를 반환합니다.
  • ref 오브젝트는 일종의 참조값이고, ref.current 라는 속성에 실제로 값을 저장할 수 있습니다.
  • ref 에는 특정 값을 저장할 수도 있고, 필요에 따라 돔 요소의 ref 속성으로 전달하여 해당 돔 요소를 직접 접근할 수 있습니다.

제어 컴포넌트와 비제어 컴포넌트에 대해 설명해주세요.

  • 제어 컴포넌트는 리액트 안에서 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 훅을 설명해주세요.

  • useMemo 훅은 말 그대로 이전에 저장된 값을 메모이제이션 하는 훅입니다.
  • useMemo 훅의 첫번째 파라미터로 콜백 함수, 두번째 파라미터로 배열을 저장합니다.
  • 콜백 함수에서는 실제로 저장할 값을 계산하는 로직을 포함합니다.
  • useEffect 의 의존성 배열과 마찬가지로 useMemo의 두번째 파라미터인 의존성 배열에 포함된 값이 업데이트 될 때 마다 해당 콜백 함수를 다시 실행해서 값을 업데이트 합니다.
  • useMemo 를 적절히 활용하면 오래 걸리는 복잡한 연산의 결과값을 저장해놓고 사용해서 최적화를 할 수 있습니다.

useCallback 훅을 설명해주세요.

  • useCallback 훅은 useMemo 훅과 비슷한 기능으로, 함수를 메모이제이션 하는 훅입니다.
  • useCallback 훅도 첫번째 파라미터로 저장할 콜백 함수, 두번째 파라미터로 배열을 저장합니다.
  • 두번째 파라미터로 제공된 의존성 배열에 포함된 값이 변경될 때 마다 콜백 함수를 다시 생성합니다.

리액트에서 성능 개선을 하는 방법을 설명해주세요.

  • 첫번째는 useMemo, useCallback 으로 시간이 오래걸리는 계산 결과를 메모이제이션하는 방법이 있습니다.
  • 두번째로는 코드 스플리팅 방식이 있습니다. 리액트를 사용한 자바스크립트 코드를 하나로 묶어서 빌드하면 굉장히 크기가 크기 때문에 페이지를 로딩하는 속도가 느려지게 됩니다. 이를 해결하기 위해서 다이나믹 import 를 사용하거나, React.Lazy 를 사용해 컴포넌트를 비동기적으로 로딩할 수 있습니다. 또는 Next.js 프레임워크를 사용하면 프레임워크에서 코드 스플리팅을 지원합니다.

useMemo와 useCallback 훅의 공통점과 차이점을 설명해주세요.

  • 가장 큰 차이는 useMemo는 값을, useCallback은 함수를 저장한다는 점입니다.
  • 하지만 둘 다 기존에 저장하던 값을 계속 저장하면서 렌더링마다 다시 계산해야 하는 불필요한 과정을 최적화 할 수 있습니다.
  • 그리고 useCallback은 단순히 useMemo 콜백 함수에 콜백 함수를 저장하는 것과 동일한 기능을 합니다.

Hook 라이프사이클

componentDidMount, componentDidUpdate 등 클래스 컴포넌트에서 사용되던 생명주기 메소드를 함수 컴포넌트에서는 어떻게 구현하나요?

  • useEffect 훅을 통해 구현할 수 있습니다.
  • useEffect 는 사용에 따라 컴포넌트가 마운트 되었을 때, 컴포넌트의 특정 상태가 업데이트 될 때, 컴포넌트가 제거될 때 모두 처리할 수 있습니다.
  • 왜냐하면 useEffect 훅은 의존성이 변경될 때 마다 등록된 effect 함수를 실행하는 훅입니다.
  • 의존성에 변경을 감지할 상태나 props 를 등록하면 componentDidUpdate, getDerivedStateFromProps 처럼 사용할 수 있습니다.
  • 그리고 의존성에 빈 배열을 등록하면 처음 마운트 되었을 때 실행되는 componentDidMount 처럼 사용할 수 있습니다.
  • 마지막으로 effect 함수의 반환값으로 콜백 함수를 등록할 수 있습니다. 이 콜백 함수는 컴포넌트가 제거될 때 실행되는 함수로, clean up 함수라고 합니다. 이 clean up 함수를 통해 componentWillUnmount 처럼 사용할 수 있습니다.

클래스형 컴포넌트의 forceUpdate를 알고 있나요?

  • forceUpdate 는 리액트에서 공식적으로 제공하는 API로, 해당 컴포넌트를 상태의 변화가 없더라도 재렌더링 시키는 메소드입니다.

함수 컴포넌트에서는 사용할 수 없나요?

  • 함수 컴포넌트에서는 공식적으로 지원하고 있지는 않지만, 약간의 트릭을 이용할 수 있습니다.
  • 상태가 변경되면 해당 컴포넌트가 다시 렌더링 되므로, useState를 통해 상태를 선언하고, forceUpdate 라는 함수를 선언해서 상태를 변경하면, 호출 시 다시 렌더링 되게 됩니다.
  • 저는 필요하다면 커스텀 훅으로 정의해서 사용하고 있습니다.
profile
배짱개미 개발자 김승현입니다 🖐

2개의 댓글

comment-user-thumbnail
2023년 2월 24일

멋진 글이에요

답글 달기
comment-user-thumbnail
2023년 2월 27일

그냥 사용하기만했지 이런 이론적인건 깊게 안봤는데 인상적이네요

답글 달기