프론트엔드 개발자 면접 질문

안지원·2025년 1월 5일

프로그래머스 프로젝트 팀원들끼리 면접질문에 대해 각자 조사해오는 팀활동을 진행하게 되었는데 이 참에 꾸준히 여기에 면접질문 관련된 내용을 쓰면 좋을거 같아 만들게 되었다.

바로 시작

1. setState가 비동기 동작을 취했을 때 얻을 수 있는 이점은 무엇인가요?

렌더링 횟수를 최소화
상태 업데이트 순서의 일관성을 유지

  • 많은 setState의 변경사항을 한번에 처리해 렌더링 횟수를 최소화 할 수 있습니다.

  • 예를 들어, 하나의 함수 내에서 10개의 상태 변경이 발생하면 React는 이러한 상태 변경을 비동기적으로 처리하여 마지막 상태 변경이 완료된 후에 한 번의 렌더링으로 병합합니다. 이로 인해 각 상태 변경마다 컴포넌트가 재렌더링되는 것이 아니라, 마지막 상태 변경이 완료된 후 한 번만 재렌더링되어 성능 최적화에 이점이 있습니다.

  • 비동기적으로 setState를 처리함으로써 리액트는 최신 상태를 보장합니다. 여러 setState 호출이 서로에게 영향을 미칠 수 있는 상황(side Effect)에서, 리액트는 상태 업데이트 순서를 조정하여 일관성을 유지합니다.

  • 예를 들어, count가 1 증가하는 상태 변경이 하나의 함수에서 두 번 실행된다고 가정해본다면 동기 처리의 경우, 각 상태 변경이 서로의 결과를 반영하지 않기 때문에 상태 변경이 두 번 발생하더라도 결과는 1만 증가하게 됩니다. 반면, 비동기 처리의 경우, React는 상태 변경 요청을 업데이트 큐(React 내부에서 상태나 props 변경을 임시로 저장하는 공간)에 추가하고, 배치 처리를 통해 이를 한 번에 처리합니다. 이 과정에서 이전 상태를 기반으로 순차적으로 업데이트가 이루어지기 때문에 최종 결과는 2가 증가하게 됩니다. 이러한 방식으로 React는 성능 최적화를 이루며, 불필요한 재렌더링을 줄입니다.

2. 의존성 배열은 shallow equal, deep equal중 무엇을 하게 되나요?

얕은 비교(shallow equal)를 사용
객체의 불변성 유지하는 법

  • 우선 의존성 배열은 리액트의 useEffect 같은 훅의 2번째 인자로 들어가는 배열 []을 뜻하며 이 의존성 배열의 값이 변경될때 마다 useEffect 콜백함수가 작동하게 됩니다.

  • 리액트에서 훅의 의존성 배열은 얕은 비교(shallow equal)를 사용합니다. 얕은 비교는 배열이나 객체의 내용이 동일하더라도 참조가 다르면 다른 것으로 간주합니다. 예를 들어, 의존성 배열에 객체를 포함시킨 경우, 객체의 속성 값이 동일하더라도 참조가 변경되면 훅이 다시 실행됩니다. 이를 방지하기 위해 상태를 업데이트할 때는 불변성을 유지해야 합니다.

  • 불변성을 유지하는 방법으로는 spread 연산자를 사용해 객체의 참조값을 유지하는 방법, immutable.js 같은 라이브러리를 사용하는 방법이 있습니다.

3. props로 전달받은 함수는, props나 상태가 업데이트될 때마다 새로 생성이 됩니다. 이 때 최적화할 수 있는 방법은 어떤게 있나요?

리액트의 memo 기능
상태관리 라이브러리

  • props를 전달 받은 함수는 자신보다 상위에 있는 함수의 props나 상태가 변경될 때에도 함수가 새로 생성됩니다. 만약 props로 연결된 함수가 100개가 넘을때 최상위 함수에서 props가 변경된다면 그 하위 함수들도 전부 함수가 새로생성(재랜더링) 되기 때문에 성능에 큰 악영향을 주게 됩니다. 참고로 이런 현상을 props 드릴링이라고 합니다.
  • 이를 해결하는 방법으로 리액트의 memo 기능을 사용하는 방법이 있습니다.
  • 리액트의 memo 기능은 React.memo, useCallback, useMemo 총 3가지가 있습니다.
  • React.memo 기능은 컴포넌트를 메모이제이션하여 props가 변경되지 않는 한 재렌더링되지 않도록 합니다.
const MyComponent = React.memo(({ someProp }) => {
  const handleClick = () => {
    console.log(someProp);
  };

  return <button onClick={handleClick}>Click me</button>;
});
  • useCallback훅은 함수의 참조를 메모이제이션하여, 의존성 배열에 명시된 값들이 변경되지 않는 한 동일한 함수를 반환합니다.(함수의 재생성 방지)
import React, { useCallback } from 'react';

const MyComponent = ({ someProp }) => {
  const memoizedCallback = useCallback(() => {
    // 함수 내용
  }, [someProp]);

  return <ChildComponent callback={memoizedCallback} />;
};
  • useMemo 훅은 컴포넌트의 특정 로직을 메모이제이션함으로써 불필요한 재계산을 방지할 수 있습니다.(값의 재계산 방지)
import React, { useMemo } from 'react';

const MyComponent = ({ someProp }) => {
  const memoizedValue = useMemo(() => {
    // 값 계산
    return someProp * 2;
  }, [someProp]);

  return <ChildComponent value={memoizedValue} />;
};
  • memo 기능을 사용하는 것 이외에도 context API, redux, zustand 등 전역으로 상태를 관리해주는 라이브러리를 사용하면 props 드릴링을 최소화 하면서 memo의 기능을 수행하기 때문에 이런 라이브러리를 사용하는 것도 하나의 방법입니다.

4. 실행 컨텍스트에 대해서 설명해주세요

키워드

  • 자바스크립트의 코드 실행 환경
  • 전역 / 함수 실행 컨텍스트

실행 컨텍스트의 의미

  • 실행 컨텍스트는 자바스크립트에서 코드가 실행되는 환경을 의미합니다.

  • 자바스크립트 엔진이 코드를 실행할 때, 그 코드가 실행될 때의 환경을 정의하고 관리하기 위해 존재하는 것이 실행 컨텍스트입니다.

  • 실행 컨텍스트는 전역과 함수 컨텍스트로 나눌 수 있습니다.

  • 전역 실행 컨텍스트는 자바스크립트가 처음 실행될 때 생성되는 컨텍스트입니다.

  • 이 전역 컨텍스트는 프로그램이 종료될 때 까지 유지되며 전역에 선언된 변수나 함수가 모두 포함됩니다.

  • 전역 실행 컨텍스트에 선언된 변수와 함수는 프로그램 내 어디서든 접근 가능합니다.

  • 자바스크립트는 싱글 스레드이기 때문에 전역 실행 컨텍스가 1개만 존재합니다.

  • 함수 실행 컨텍스트는 함수가 호출될 때 마다 생성되는 컨텍스트를 의미합니다.

  • 각 함수는 자신만의 실행 컨텍스트를 가지며, 이 컨텍스트 내에서 선언된 변수와 함수는 해당 함수 내에서만 유효합니다.

  • 함수가 종료되면 그 실행 컨텍스트도 함께 사라집니다.

  • 정리하면 실행 컨텍스트는 구성요소를 바탕으로 자바스크립트 코드가 실행되는 동안의 환경을 관리하고, 코드 실행시 변수의 유효 범위나 함수 호출의 맥락을 결정 짓습니다.

4.1 실행 컨텍스트는 어떤 구성요소로 이루어져 있나요?

  • 실행 컨텍스트는 변수 객체, 스코프 체인, this 라는 구성요소로 이루어져 있습니다.

  • 변수 객체란 실행 컨텍스트 내에서 사용되는 변수와 함수 선언을 저장하는 공간입니다.

  • 전역 컨텍스트 내에선 전역 객체가 변수 객체의 역할을 합니다.

  • 함수 컨텍스트 내에선 활성화 객체가 변수와 매개변수를 관리합니다.

  • 스코프 체인은 현재 실행 중인 컨텍스트와 외부 렉시컬 환경의 연결을 유지합니다.

  • 변수를 참조할 때 현재 컨텍스트에서 찾지 못하면 외부 환경으로 범위를 넓혀가며 변수를 찾습니다.

  • this는 실행 컨텍스트에 따라 참조하는 객체가 달라집니다.

  • 전역 컨텍스트에선 this가 전역 객체를 가리키며 함수 컨텍스트에선 함수 호출 방법에 따라 달라집니다.

5. 리액트의 props와 state에 대해 설명해주세요

키워드

  • 컴포넌트 재렌더링
  • 단방향 데이터 흐름 원칙
  • 상태 끌어올리기
  • props는 부모 컴포넌트가 자식 컴포넌트에 전달하는 데이터입니다.
  • props는 읽기 전용으로 자식 컴포넌트가 props를 수정할 수 없습니다.
<script>
function ChildComponent(props) {
  props.name = "New Name"; // 오류 발생 가능
  return <div>{props.name}</div>;
}
</script>
  • state는 컴포넌트 내부에서 관리되는 데이터입니다.
  • state는 state변경 함수를 통해 동적으로 변경될 수 있으며, 이는 컴포넌트의 렌더링에 영향을 미칩니다.
  • state가 변경되면 컴포넌트는 다시 렌더링되며, UI가 업데이트 됩니다.
  • state는 주로 사용자 입력이나 네트워크 요청의 응답에 따라 변하는 데이터를 관리할 때 사용됩니다.

5.1 props가 자식 컴포넌트에서 변하지 않는 이유는 무엇인가요?

  • props가 자식 컴포넌트에서 변하지 않는 이유는 리액트의 단방향 데이터 흐름 원칙 때문입니다.
  • 리액트는 부모가 자식 컴포넌트에 데이터를 전달할 때 단방향으로 전달되도록 설계되었습니다.
  • 데이터를 단방향으로만 전달하게 하면 컴포넌트 간의 데이터 흐름을 예측하기 쉽고 일관성 있게 애플리케이션을 만들 수 있기 때문입니다.
  • 단방향 설계로 props는 읽기 전용으로 설정되어있어 부모 컴포넌트에서 전달된 값이 자식 컴포넌트에서 임의로 변경되지 않습니다. 이를 통해 디버깅할때 props로 연결되어있는 상태의 변화가 어디에서 발생했는지 예측하기 한결 수월해집니다.

5.2 만약 자식 컴포넌트에서 부모 컴포넌트로 받은 props를 변경해야 한다면 어떻게 해야 할까요?

  • 만약 자식 컴포넌트가 부모로부터 받은 props를 변경해야 한다면, 부모 컴포넌트에서 해당 상태 변경 함수를 props로 전송해 상태를 변경하는 방법이 있습니다.
  • 이렇게 하면 상태는 부모 컴포넌트가 관리하고 있어 데이터는 여전히 단방향으로 흐르고, 일관성을 유지할 수 있습니다.
  • 이러한 기법을 상태 끌어올리기 라고 합니다.
profile
frontend개발자가 되기 위해 노력합니다.

0개의 댓글