개발일지

프론트엔드 기술 면접 질문 본문

frontEnd/기술면접

프론트엔드 기술 면접 질문

푸린푸린김푸린 2025. 1. 12. 20:30

 

 

Q. 타입스크립트에서 Mapped Type이란 무엇인지 설명하고, 이를 활용해 객체의 모든 속성을 읽기 전용(readonly)으로 만드는 타입을 작성해보세요.

A. Mapped Type은 기존 타입의 속성들을 변환하여 새로운 타입을 만드는 기능입니다. 이를 통해 모든 속성을 readonly로 만들거나, 옵셔널로 변경하는 등 타입의 속성을 일괄적으로 수정할 수 있습니다.

// 모든 속성을 readonly로 만듭니다
type MakeReadonly<T> = {
  readonly [P in keyof T]: T[P]
}

interface User {
  name: string;
  age: number;
  email: string;
}

// User 타입의 모든 속성이 readonly가 됩니다
type ReadonlyUser = MakeReadonly<User>

// 실제 사용
const user: ReadonlyUser = {
  name: "김철수",
  age: 25,
  email: kim@email.com
}

// 아래 코드는 에러가 발생합니다
// user.name = "박철수"  // ❌ Error: readonly 속성은 수정할 수 없습니다

 

 

Q.타입스크립트에서 keyof 연산자와 인덱스드 타입을 설명해주세요.

A. keyof는 객체 타입에서 모든 키값들을 유니온 타입으로 추출합니다. 

interface User {
  name: string;
  age: number;
  email: string;
}

// UserKeys는 "name" | "age" | "email" 타입이 됩니다
type UserKeys = keyof User;

 

인덱스드 타입은 타입의 특정 속성을 선택할 수 있게 해줍니다.

interface Product {
  id: number;
  name: string;
  price: number;
  stock: number;
}

// ProductInfo는 string 타입이 됩니다
type ProductInfo = Product["name"];

// 여러 속성을 선택하기도 가능!!
type ProductDetails = Product["name" | "price"]; // string | number

 

 

Q. 타입스크립트에서 Partial<T>와 Required<T> 유틸리티 타입의 차이를 설명해주세요

A. Partial<T>는 모든 프로퍼티를 선택적으로 만들어 일부 데이터만 필요할 때 사용하고, Required<T>는 모든 프로퍼티를 필수로 만들어 전체 데이터가 필요할 때 사용합니다.

 

 

Q. React의 useEffect 훅에서 의존성 배열(dependency array)의 역할은 무엇인가요? 의존성 배열을 잘못 관리했을 때 발생할 수 있는 문제점과, 이를 방지하기 위한 방법을 설명해주세요.

A. useEffect의 의존성 배열은 effect가 실행되어야 하는 시점을 제어합니다. 배열 내의 값이 변경될 때마다 effect가 재실행되며, 빈 배열([])을 사용하면 컴포넌트 마운트 시에만 실행됩니다.

의존성 배열을 잘못 관리했을 때에는 무한 루프 발생, 잘못된 데이터 동기화 등이 일어날 수 있습니다.

이러한 문제를 방지하기 위해서는

  • ESLint의 exhaustive-deps 규칙을 활용하여 의존성 검사
  • effect 내부에서 사용되는 모든 값들을 의존성 배열에 포함시키는 습관 형성
  • 복잡한 로직의 경우, custom hook으로 분리하여 관리하는 것을 고려

 

하는 것이 좋습니다. 또한 useMemo나 useCallback을 사용하여 함수를 만들고 함수를 useEffect안에 포함하는 것이 좋습니다

 

Q. Zustand에서 미들웨어(Middleware)를 활용하는 방법을 설명하고, 상태 변경 로깅 기능을 추가하는 미들웨어를 구현하는 방법을 설명해주세요

A. zustand의 미들웨어는 상태 관리 로직을 확장하는 기능입니다. 

상태 변경을 추적하는 로깅 미들웨어를 구현할 때는 config를 받아 set과 get 함수를 활용하여 상태 변경 전후를 기록할 수 있습니다. 이를 통해 디버깅이나 상태 변화 추적이 필요할 때 효과적으로 활용할 수 있습니다

 

아래는 기본 zustand 사용법입니다

interface BearState {
  bears: number
  increase: () => void
}

const useStore = create<BearState>((set) => ({
  bears: 0,
  increase: () => set((state) => ({ bears: state.bears + 1 })),
}))

 

만약 상태 변경 전/후를 콘솔로 나타내도록 하는 미들웨어를 만든다고 하면 이렇게 미들웨어를 정의하고

type StoreMiddleware = <T>(
  config: StateCreator<T>,
  options?: Partial<StoreMutatorIdentifier>
) => StateCreator<T>


const logMiddleware: StoreMiddleware = (config) => (set, get, api) =>
  config(
    (...args) => {
      console.log('이전 상태:', get())
      set(...args)
      console.log('현재 상태:', get())
    },
    get,
    api
  )

 

그리고 만들어 둔 미들웨어를 스토어에 적용할 때에는 아래와 같은 모습이 됩니다

const useStore = create<BearState>()(
  logMiddleware(
    (set) => ({
      bears: 0,
      increase: () => set((state) => ({ bears: state.bears + 1 })),
    })
  )
)

 

실제 컴포넌트에서 사용할 때는 이런 식으로 곰의 마릿수를 늘릴 수 있고 늘어날 때마다 콘솔에는

이전 상태: { bears: 0 } 현재 상태: { bears: 1 } 와 같이 나오게 됩니다

function BearCounter() {
  const bears = useStore((state) => state.bears)
  const increase = useStore((state) => state.increase)

  return (
    <div>
      <span>곰 수: {bears}</span>
      <button onClick={increase}>증가</button>
    </div>
  )
}
Comments