면접에서 묻는 "의존성 주입 경험이 있나요?"의 의미

2025. 8. 26. 17:14·Frontend Development
728x90

프론트엔드 면접에서 자주 듣는 질문 중 하나가 "의존성 주입(Dependency Injection, DI) 경험이 있나요?"입니다.
이 질문은 단순히 특정 DI 프레임워크를 써봤냐를 묻는 게 아니라, 코드 구조와 의존성 관리 문제를 이해하고 있는지를 확인하는 의도에 가깝습니다.


1. 의존성 문제(Dependency Problem)란?

소프트웨어에서 어떤 클래스나 모듈이 다른 객체를 직접 생성하거나 강하게 참조하면 문제가 발생합니다.

class UserService {
  private api = new ApiClient(); // 직접 생성 → 강한 결합
}

이 경우:

  • 테스트하기 어려움 (ApiClient를 Mock으로 교체 불가)
  • 재사용성이 낮음 (다른 구현체로 교체하기 힘듦)
  • 결합도가 높아 유지보수가 어려움

즉, 의존성을 직접 관리하면 코드가 유연하지 않고 테스트/확장이 어렵다는 게 핵심 문제입니다.


2. 의존성 주입(DI)으로 해결하는 방법

DI는 외부에서 필요한 의존성을 “주입”받아 사용하는 패턴입니다.

class UserService {
  constructor(private api: ApiClient) {} // 외부에서 주입
}

// 실제 사용 시
const api = new ApiClient();
const userService = new UserService(api);

장점:

  • 테스트 시 Mock 객체를 쉽게 주입 가능
  • 결합도 감소 → 유지보수 용이
  • 환경별 구현체 교체 가능 (예: Dev API vs Prod API)

3. 프론트엔드에서의 DI 예시

프론트엔드에서는 Angular처럼 전용 DI 프레임워크를 쓰지 않아도, 다양한 형태로 DI 개념을 적용할 수 있습니다.

(예시) TypeScript 인터페이스 기반 서비스 주입

// contracts.ts
export interface UserApi {
  getUser(id: string): Promise<{ id: string; name: string }>
}

export class FetchUserApi implements UserApi {
  async getUser(id: string) {
    const res = await fetch(`/api/users/${id}`);
    return res.json();
  }
}

export class UserService {
  constructor(private readonly api: UserApi) {}
  load(id: string) {
    return this.api.getUser(id);
  }
}

(예시) React Context로 API 클라이언트 주입

// App.tsx
import React from 'react';
import type { UserApi } from './contracts';

export const UserApiContext = React.createContext<UserApi | null>(null);

export function App({ api, children }: { api: UserApi; children: React.ReactNode }) {
  return <UserApiContext.Provider value={api}>{children}</UserApiContext.Provider>;
}

export function useUserApi(): UserApi {
  const api = React.useContext(UserApiContext);
  if (!api) throw new Error('UserApiProvider가 필요합니다');
  return api;
}

(예시) Next.js(App Router)에서 환경별 구현 주입

// app/_lib/serverUserApi.ts
import type { UserApi } from '@/contracts';

export class ServerUserApi implements UserApi {
  async getUser(id: string) {
    const res = await fetch(`${process.env.API_BASE}/users/${id}`, {
      headers: { Authorization: `Bearer ${process.env.SERVICE_TOKEN}` },
      cache: 'no-store',
    });
    return res.json();
  }
}
// app/users/[id]/page.tsx
import { ServerUserApi } from '@/app/_lib/serverUserApi';
import { App } from '@/app/_components/App';

export default function Page({ params }: { params: { id: string } }) {
  const api = new ServerUserApi();
  return (
    <App api={api}>
      {/* 클라이언트 컴포넌트 내에서 useUserApi() 사용 */}
      {/* ... */}
    </App>
  );
}

(예시) 테스트에서 Mock 주입

// userService.test.ts
import { UserService } from './contracts';

const mockApi = { getUser: async (id: string) => ({ id, name: 'Mock' }) };

test('UserService는 API 결과를 그대로 반환한다', async () => {
  const service = new UserService(mockApi);
  await expect(service.load('1')).resolves.toEqual({ id: '1', name: 'Mock' });
});
// UserView.test.tsx
import { render, screen } from '@testing-library/react';
import React from 'react';
import { App } from './App';

const mockApi = { getUser: async () => ({ id: '1', name: 'Mock' }) };

test('mock API를 주입해 렌더링 결과를 검증한다', async () => {
  render(
    <App api={mockApi as any}>
      <div>Mount component using useUserApi()</div>
    </App>
  );
  // 실제 컴포넌트에서는 screen.findByText('Mock') 등으로 검증
});

(권장) 베스트 프랙티스와 흔한 실수

  • 인터페이스 우선: 구현보다 계약을 먼저 정의한다

  • 구성 루트 명확화: 의존성 생성·주입 지점을 한곳에 모은다

  • 환경 분리: SSR/CSR, Dev/Prod별 구현을 분리하고 주입으로 선택한다

  • 전역 싱글톤 남용 금지: 테스트 격리를 방해하고 숨은 결합을 만든다

  • Context 남용 주의: 재렌더 비용을 고려해 분리/메모화한다

  • React props 전달
    부모 컴포넌트에서 자식으로 props를 넘기는 것은 DI의 가장 기본적인 형태입니다.

  • Context API, Zustand, Redux
    전역 상태(store)를 컴포넌트에 직접 생성하지 않고 외부에서 주입받아 사용하는 것.

  • 서비스 레이어 분리
    API 호출을 fetchClient, apiService 같은 모듈로 분리하고, 훅이나 컴포넌트에서는 이를 주입받아 사용.
    → 테스트 시 Mock API로 교체 가능.

  • 테스트 코드에서 Mock 주입
    Jest/RTL 등을 사용해 실제 API 모듈 대신 Mock 객체를 주입해 테스트.


4. 면접 답변 포인트

면접관이 궁금한 건 다음과 같습니다:

  • 의존성 관리 문제를 이해하는지
  • 테스트 가능하고 유연한 구조를 고민해본 경험이 있는지
  • React/Next.js에서도 DI 개념을 적용해봤는지

👉 답변 예시:

“프론트엔드에서는 Angular처럼 DI 프레임워크를 직접 쓰진 않았지만,
React에서는 props 전달이나 Context, Zustand 같은 상태 관리 도구를 통해 의존성을 주입해본 경험이 있습니다.
특히 API 클라이언트를 서비스 레이어로 분리하고 외부에서 주입받도록 설계해,
테스트 시 Mock 객체로 교체 가능하게 한 경험이 있습니다.”


5. 결론

의존성 주입은 단순히 기술 스택 문제가 아니라 코드 아키텍처와 유지보수성에 대한 이야기입니다.
프론트엔드에서도 DI 개념을 적용하면 테스트 용이성, 유연한 확장성, 낮은 결합도를 달성할 수 있습니다.

결국 면접에서 “DI 경험이 있나요?”라는 질문은 "당신은 코드 구조와 테스트 가능성을 고려해서 개발해본 경험이 있나요?"라는 의미로 이해하면 됩니다.

728x90
저작자표시 비영리 변경금지 (새창열림)

'Frontend Development' 카테고리의 다른 글

setTimeout vs Promise.then vs queueMicrotask  (0) 2025.09.05
Next.js 최신 캐싱 전략 총정리  (0) 2025.09.03
useState vs useRef vs let: 언제 무엇을 써야 할까?  (0) 2025.08.21
Core Web Vitals: LCP, INP, CLS 개념과 개선 방법  (3) 2025.08.20
코드가 깔끔해지는 비밀: 프론트엔드에서 함수형 프로그래밍 활용하기  (3) 2025.08.18
'Frontend Development' 카테고리의 다른 글
  • setTimeout vs Promise.then vs queueMicrotask
  • Next.js 최신 캐싱 전략 총정리
  • useState vs useRef vs let: 언제 무엇을 써야 할까?
  • Core Web Vitals: LCP, INP, CLS 개념과 개선 방법
Kun Woo Kim
Kun Woo Kim
안녕하세요, 김건우입니다! 웹과 앱 개발에 열정적인 전문가로, React, TypeScript, Next.js, Node.js, Express, Flutter 등을 활용한 프로젝트를 다룹니다. 제 블로그에서는 개발 여정, 기술 분석, 실용적 코딩 팁을 공유합니다. 창의적인 솔루션을 실제로 적용하는 과정의 통찰도 나눌 예정이니, 궁금한 점이나 상담은 언제든 환영합니다.
  • Kun Woo Kim
    WhiteMouseDev
    김건우
  • 깃허브
    포트폴리오
    velog
  • 전체
    오늘
    어제
  • 공지사항

    • [인사말] 이제 티스토리에서도 만나요! WhiteMouse⋯
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (133)
      • Frontend Development (62)
      • Backend Development (25)
      • Algorithm (33)
        • 백준 (11)
        • 프로그래머스 (17)
        • 알고리즘 (5)
      • Infra (1)
      • 자료구조 (3)
      • Language (6)
        • JavaScript (6)
  • 링크

    • Github
    • Portfolio
    • Velog
  • 인기 글

  • 태그

    tailwindcss
    frontend development
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Kun Woo Kim
면접에서 묻는 "의존성 주입 경험이 있나요?"의 의미
상단으로

티스토리툴바