IT

천상계 개발자들이 면접 때 준비하는 이것? 시스템 디자인 인터뷰 뽀개기

작가DevPill 데브필

Introduction

개발자 면접 과정을 떠올려봅시다. 어떤 게 있을까요? 보통은 아래와 같은 단계를 떠올리실 거예요.

 

서류 -' 코딩 인터뷰(알고리즘) -' 기술 인터뷰(and 라이브 코딩) -' 최종 인성 면접 그런데 요즘 꽤나 괜찮은(=잘나가는) 스타트업이라면 하나 더 추가되는 관문이 있습니다. 바로 시스템 디자인 인터뷰인데요. 예를 들면 이런 식입니다.

당근마켓 앱 게시물에 좋아요 기능을 추가해달라는 요구사항이 들어왔어요. 이를 위한 마이크로서비스를 하나 설계한다고 할 때, 어떻게 설계할 것인지 설명해주세요.

시스템 디자인 인터뷰를 처음 접한 면접자라면 매우 당황하겠죠? 그런데 이미 해외에서는 시스템 디자인 인터뷰가 너무나도 자연스러운 면접 관문 중 하나라는 것, 아시나요? 잘나가는 개발자로 거듭나기 위해서는 이제 알고리즘 풀이만으로는 좋은 인상을 보여줄 수 없습니다. 

아래 글은 뉴스레터 DEV DESIGN DIGEST의 How to Answer a System Design Interview Problem을 읽고 번역한 글입니다.


시스템 디자인 인터뷰 문제에 답하는 방법

시스템 디자인 인터뷰는 가장 어려운 인터뷰 중 하나일 수 있지만, 구조화된 접근 방식을 통해서라면 이러한 인터뷰를 더 효과적으로 뚫을 수 있습니다.

이 글에서는 인터뷰에서 시스템 디자인 문제가 나왔을 때, 이를 효과적으로 다루기 위한 단계별 접근 방식을 자세히 살펴보겠습니다.

1단계: 요구사항 명확화

시스템 디자인 문제에 접근하는 첫 번째 단계는 문제 설명을 철저히 이해하고, 모호한 점을 명확히 하며, 설계하려는 시스템에 대한 정보를 최대한 많이 수집하는 것입니다.

면접관의 말을 주의 깊게 듣고 질문하여 시스템에 요구되는 모든 주요 기능을 파악합니다. 명확히 해야 할 요구사항은 1) 기능적 요구사항과 2) 비기능적 요구사항 두 가지가 있습니다. 이러한 요구사항을 명확히 하는 가장 좋은 방법은 요구사항과 관련된 질문을 하는 것입니다.

  • 기능적 요구사항(Functional Requirements)
  • 시스템이 지원해야 하는 핵심 기능은 무엇인가요?
  • 다른 기능보다 더 중요한 특정 기능이 있나요?
  • 누가 이 시스템을 사용할 것인가요(고객, 내부 팀 등)?
  • 사용자가 시스템에서 수행할 수 있어야 하는 구체적인 작업은 무엇인가요?
  • 사용자는 어떻게 시스템과 상호작용할 것인가요(웹, 모바일 앱, API 등)?
  • 시스템이 여러 언어나 로케일을 지원해야 하나요?
  • 시스템이 처리해야 하는 주요 데이터 유형은 무엇인가요(텍스트, 이미지, 구조화된 데이터 등)? 이는 데이터베이스 선택에 영향을 미칠 수 있습니다.
  • 시스템이 통합해야 하는 외부 시스템이나 서드파티 서비스가 있나요?
  • 비기능적 요구사항(Non-functional Requirements)
  • 사용자 수와 요청 수 측면에서 시스템의 예상 규모는 어떻게 되나요?
  • 시스템이 처리해야 할 데이터 볼륨은 어느 정도로 예상되나요?
  • 시스템의 입력과 출력은 무엇인가요?
  • 예상되는 읽기 대 쓰기 비율은 어떻게 되나요?
  • 시스템이 일정 시간 동안 다운타임을 가질 수 있나요, 아니면 높은 가용성이 필요한가요?
  • 특정 지연 요구사항이 있나요?
  • 데이터 일관성은 얼마나 중요한가요? 가용성을 위해 어느 정도의 최종 일관성을 허용할 수 있나요?
  • 우리가 집중해야 할 특정 비기능적 요구사항(성능, 확장성, 신뢰성)이 있나요? 위와 같은 질문으로 초기에 우리가 설계해야 할 시스템의 범위를 이해하면 잘못된 방향으로 나아가는 것을 방지할 수 있습니다.

2단계: 용량 추정

요구사항을 명확히 한 후에는 설계할 시스템의 용량을 추정하는 것이 중요합니다. 사전에 규모를 추정하면 이 정보가 이후에 설계할 때 의사결정을 가이드하고 시스템이 원하는 기준을 충족할 수 있도록 보장합니다. 여기에는 예상되는 일일/월간 사용자, 초당 읽기/쓰기 요청, 데이터 스토리지 및 네트워크 대역폭 요구사항과 같은 것들이 포함될 수 있습니다.

  • 사용자: 일일 사용자 수(DAU)(또는 MAU)와 최대 동시 접속 사용자 수를 추정합니다.
  • 트래픽: 예상되는 초당 읽기/쓰기를 계산합니다. 피크 트래픽 기간과 사용량의 잠재적 급증을 고려합니다.
  • 스토리지: 다양한 유형의 데이터(구조화된 데이터, 비정형 데이터, 멀티미디어)를 고려하고 필요한 총 스토리지 양(및 그 증가율)을 추정합니다.
  • 메모리: 지연 시간을 줄이고 성능을 향상시키기 위해 캐싱의 잠재적 이점을 평가합니다. 자주 액세스되는 데이터를 저장하는 데 필요한 메모리 양을 추정합니다.
  • 네트워크: 예상 트래픽 볼륨과 데이터 전송 크기를 기반으로 대역폭 요구사항을 추정합니다. 미래의 성장과 확장성 요구사항을 고려하여 시스템이 시간이 지남에 따라 증가하는 부하를 처리할 수 있도록 합니다.

 

3단계: 상위 수준 설계

요구사항과 예상 용량을 고려하고 나서는 본격적으로 시스템의 상위 수준(=큰그림) 아키텍처 설계를 시작합니다. 먼저, 프론트엔드, 백엔드, 데이터베이스, 캐시 및 외부 서비스와 같은 주요 구성 요소 또는 모듈로 시스템을 큰 단위에서 분류합니다.

클라이언트에서 백엔드를 거쳐 다시 클라이언트로 돌아오는 데이터와 요청의 상위 수준 흐름과 주요 시스템 구성 요소를 개략적으로 설명하는 간단한 블록 다이어그램을 그려봅니다.(역: 이때 miro 등을 이용해 실시간으로 면접관에게 공유해주면 좋습니다.)

상위 수준 설계를 위한 다이어그램

상위 수준 설계를 위한 다이어그램

다이어그램에 포함할 내용

  • 클라이언트 애플리케이션: 사용자가 시스템과 상호 작용하는 방식(웹 브라우저, 모바일 앱, 데스크톱 애플리케이션 등)을 나타냅니다.
  • 웹 서버: 클라이언트로부터 들어오는 요청을 처리하는 서버입니다.
  • 로드 밸런서: 상당한 트래픽을 처리하기 위해 트래픽을 서버에 고르게 분산하는 데 사용됩니다.
  • 애플리케이션 서비스: 시스템의 핵심 기능이 구현되는 백엔드 로직 계층입니다.
  • 데이터베이스: 데이터베이스 유형을 지정합니다: SQL vs. NoSQL, 그리고 이유를 간략히 설명합니다.
  • 캐싱 계층: 데이터베이스에 대한 부하를 줄이기 위해 사용하는 경우 캐싱(예: Redis, Memcached)을 지정합니다.
  • 메시지 큐: 비동기 통신을 사용하는 경우
  • 외부 서비스: 시스템이 서드파티 API(예: 결제 게이트웨이)에 의존하는 경우 포함합니다.

모든 구성 요소에 대해 반드시 트레이드오프를 고려하고 특정 기술이나 아키텍처를 선택한 이유를 정당화해야 합니다(예: "강력한 일관성이 필요하므로 관계형 데이터베이스(RDB)가 적합합니다").

상위 수준 다이어그램 작성 시 유의 사항

  • 단순하고 깔끔하게 유지합니다.
  • 적절한 표기법과 기호를 사용하여 구성 요소, 구성 요소 간의 상호 작용 및 데이터 흐름을 나타내는 게 좋습니다.
  • 다양한 유형의 구성 요소나 상호 작용을 구분하기 위해 서로 다른 색상, 선 스타일 또는 기호를 사용합니다.
  • 구성 요소를 나타내는 간단한 상자와 방향성 데이터 흐름을 보여주는 화살표를 사용합니다.
  • 입력에서 저장 및 검색까지 데이터가 시스템을 통해 어떻게 흐르는지 화살표를 사용하여 보여줍니다.
  • 너무 많은 세부 정보나 불필요한 요소로 다이어그램을 복잡하게 만들지 마세요(1번을 까먹으시면 안됩니다 - 단순하고 깔끔하게 유지하기.)
  • 사소한 세부 사항에 대해 너무 깊이 생각하지 마세요. 이것은 큰 그림에 관한 것입니다.  

4단계: 데이터베이스 설계

이 단계에서는 데이터를 모델링하고, 시스템에 적합한 스토리지를 선택하고, 데이터베이스 스키마를 설계하고, 액세스 패턴에 따라 데이터의 저장 및 검색을 최적화하는 작업을 수행합니다.

데이터 모델링

  • 시스템이 저장하고 관리해야 하는 주요 데이터 엔티티 또는 객체를 식별합니다(예: 사용자, 제품, 주문).
  • 이러한 엔티티 간의 관계와 상호 작용 방식을 고려합니다.
  • 각 엔티티와 관련된 속성 또는 속성을 결정합니다(예: 사용자에게는 이메일, 이름, 주소가 있음).
  • 각 엔티티에 대한 고유 식별자 또는 기본 키를 식별합니다.
  • 데이터 무결성을 보장하고 중복을 최소화하기 위해 정규화 기술을 고려합니다.

올바른 스토리지 선택

  • 데이터의 요구사항과 특성을 평가하여 가장 적합한 데이터베이스 유형을 결정합니다.
  • 데이터 구조, 확장성, 성능, 일관성 및 쿼리 패턴과 같은 요소를 고려합니다.
  • 관계형 데이터베이스(예: MySQL, PostgreSQL)는 복잡한 관계와 ACID 속성을 가진 구조화된 데이터에 적합합니다.
  • NoSQL 데이터베이스(예: MongoDB, Cassandra)는 비정형 또는 반정형 데이터, 높은 확장성 및 최종 일관성에 적합합니다.
  • 서로 다른 데이터 하위 집합이 서로 다른 요구사항을 가진 경우 데이터베이스의 조합을 사용하는 것을 고려해 보십시오.

데이터베이스 스키마 설계

예: Twitter 데이터베이스 스키마는 사용자와 트윗을 저장하는 데 사용됨

예: Twitter 데이터베이스 스키마는 사용자와 트윗을 저장하는 데 사용됨

  • 선택한 데이터베이스 유형에 따라 테이블, 열, 데이터 유형 및 관계를 정의합니다.
  • 쿼리 성능을 최적화하기 위해 기본 키, 외래 키 및 필요한 인덱스를 지정합니다.
  • 필요한 경우 읽기 성능을 향상시키기 위해 중복 또는 사전 집계와 같은 비정규화 기술을 고려합니다.

데이터 액세스 패턴 정의(역자 의견: 매우 중요한 부분!)

  • 시스템이 수행할 일반적인 데이터 액세스 패턴과 쿼리를 식별합니다.
  • 이러한 액세스 패턴을 기반으로 데이터베이스 스키마와 인덱스를 최적화하여 효율적인 데이터 검색을 보장합니다.
  • 자주 액세스되는 데이터를 저장하고 데이터베이스 부하를 줄이기 위해 적절한 캐싱 메커니즘을 사용합니다.
  • 확장성을 위해 데이터를 여러 데이터베이스 또는 테이블에 분할하거나 샤딩하는 것을 고려해 보십시오.  

5단계: API 및 통신 프로토콜 설계

API(Application Programming Interface) 및 통신 프로토콜을 설계하면 시스템의 다양한 구성 요소가 서로 상호 작용하는 방식과 외부 클라이언트가 시스템의 기능에 액세스할 수 있는 방법이 정의됩니다.

API 요구사항 식별:

  • API를 통해 시스템이 노출해야 하는 주요 기능과 서비스를 결정합니다.
  • API와 상호 작용할 다양한 유형의 클라이언트(예: 웹, 모바일, 서드파티 서비스)를 고려합니다.
  • 각 API 엔드포인트에 대한 데이터 입력, 출력 및 특정 요구사항을 식별합니다.

API 스타일 선택:

  • 시스템 요구사항과 클라이언트 요구사항에 따라 적절한 API 스타일을 선택합니다.
  • RESTful API(Representational State Transfer)는 웹 기반 시스템에 일반적으로 사용되며 리소스 조작을 위한 균일한 인터페이스를 제공합니다.
  • GraphQL API는 클라이언트가 특정 데이터 필드를 쿼리하고 검색할 수 있는 유연하고 효율적인 방법을 제공합니다.
  • RPC(Remote Procedure Call) API는 잘 정의된 프로시저 또는 함수가 있는 시스템에 적합합니다.

API 엔드포인트 정의:

예: Twitter API

예: Twitter API

  • 시스템의 기능과 데이터 모델을 기반으로 명확하고 직관적인 API 엔드포인트를 설계합니다.
  • 원하는 작업을 나타내기 위해 각 엔드포인트에 적절한 HTTP 메서드(예: GET, POST, PUT, DELETE)를 사용합니다.

데이터 형식 지정:

  • API 요청 및 응답에 대한 데이터 형식을 선택합니다. 일반적인 형식으로는 JSON(JavaScript Object Notation)과 XML(eXtensible Markup Language)이 있습니다.
  • 클라이언트 및 시스템 구성 요소와의 가독성, 파싱 효율성 및 호환성과 같은 요소를 고려합니다.

통신 프로토콜 선택:

  • HTTPS: RESTful API 및 웹 기반 통신에 일반적으로 사용됩니다.
  • WebSockets: 클라이언트와 서버 간의 실시간 양방향 통신에 유용합니다(예: 채팅 애플리케이션).
  • gRPC(gRPC Remote Procedure Call): 마이크로서비스 아키텍처에서 서비스 간 통신에 효율적입니다.
  • 메시징 프로토콜: 비동기 메시징을 위한 AMQP, MQTT(메시지 큐와 함께 자주 사용됨).  

6단계: 핵심 구성 요소에 대해 자세히 살펴보기

면접관은 특정 영역에 집중하고 싶어 할 가능성이 높아요. 그러니 주의를 기울여 해당 사항에 대해 더 자세히 논의합니다.

심층 분석을 위한 일반적인 영역:

  • 데이터베이스: 데이터 볼륨이 대폭 증가하는 경우 어떻게 처리하시겠습니까? 샤딩(여러 데이터베이스에 데이터 분할), 복제(읽기/쓰기 복제본) 에 대해 설명하세요.
  • 웹 서버/애플리케이션 서버: 트래픽 증가에 대응하기 위해 로드 밸런서 뒤에 더 많은 서버를 추가하는 방법은 무엇입니까?
  • 로드 밸런서: 사용할 로드 밸런싱 기술과 알고리즘(예: 라운드 로빈, 최소 연결)은 무엇입니까?
  • 캐싱: 더 많은 캐시 계층을 어디에 추가합니까(웹 서버 앞? 애플리케이션 계층?), 그리고 캐시 무효화는 어떻게 처리합니까?
  • 단일 실패 지점: 구성 요소를 식별하고, 이러한 구성 요소의 장애가 시스템을 중단시킬 수 있으며, 이를 해결하는 방법에 대해 논의하십시오.
  • 인증/권한 부여: 사용자 액세스 및 권한을 안전하게 관리하는 방법은 무엇입니까?
  • Rate Limiting(특정 시간 내에 호출할 수 있는 API 횟수 제한): API의 과도한 사용이나 남용을 어떻게 방지합니까?  

7단계: 핵심 문제 해결

이 단계에서는 시스템 설계가 직면할 가능성이 있는 핵심 과제를 파악하고 해결하는 것이 포함됩니다. 이러한 과제는 확장성 및 성능부터 신뢰성, 보안 및 비용 문제에 이르기까지 다양할 수 있습니다.

확장성 및 성능 문제 해결:

  • 더 많은 노드를 추가하고 로드 밸런서를 사용해 노드 간에 트래픽을 고르게 분산하여 scale out(수평으로 확장)합니다.
  • 개별 리소스(예: CPU, 메모리, 스토리지)의 용량을 늘려 scale up(수직으로 확장)합니다.
  • 백엔드 시스템에 대한 부하를 줄이고 응답 시간을 개선하기 위해 캐싱을 구현합니다.
  • 중요한 작업을 위해 효율적인 데이터 구조와 알고리즘을 선택합니다.
  • 데이터베이스 쿼리 및 인덱스를 최적화합니다.
  • 조인 작업을 줄이기 위해 필요한 경우 데이터를 비정규화합니다.
  • 쿼리 성능 향상을 위해 데이터베이스 파티셔닝 및 샤딩을 사용합니다.
  • 지리적으로 분산된 서버에서 정적 resource을 제공하기 위해 콘텐츠 전송 네트워크(CDN)를 구현합니다.
  • 동시 요청을 효율적으로 처리하기 위해 비동기 프로그래밍 모델을 활용합니다.

신뢰성(Reliability) 문제 해결

신뢰성(reliability)은 오류나 장애가 발생하더라도 시스템이 올바르고 일관되게 기능할 수 있는 능력을 말합니다.

단일 장애 지점 - Wikipedia 출처: https://en.wikipedia.org/wiki/Single_point_of_failure

단일 장애 지점 - Wikipedia 출처: https://en.wikipedia.org/wiki/Single_point_of_failure

 시스템을 더 신뢰할 수 있게 만들기 위한 주요 고려 사항은 다음과 같습니다.

  • 시스템 아키텍처를 분석하고 잠재적인 단일 장애 지점(single point of failure)을 식별합니다.
  • 단일 장애 지점을 제거하기 위해 시스템 구성 요소에 중복성을 설계합니다(여러 로드 밸런서, 데이터베이스 복제본).
  • 지역적 장애나 재해로부터 보호하기 위해 지리적 중복성을 고려하십시오.
  • 데이터 가용성과 내구성을 보장하기 위해 데이터 복제(Replication) 전략을 구현합니다.
  • 연쇄 장애를 방지하고 과부하로부터 시스템을 보호하기 위해 circuit breaker 패턴을 구현합니다.
  • 일시적인 장애를 처리하고 복구 중에 시스템에 과부하가 걸리지 않도록 Exponential backoff 전략을 사용하여 retry 메커니즘을 구현합니다.
  • 장애, 성능 문제 및 이상 징후를 감지하기 위해 종합적인 모니터링 및 경보 시스템을 구현합니다. 이상으로 7단계를 소개했습니다. 시스템 디자인 인터뷰에서 우리의 목표는 완벽한 솔루션을 제공하는 것이 아니라 문제를 분석하고, 합리적인 설계 결정을 내리고, 사고 과정을 명확하게 전달할 수 있는 능력을 보여주는 것임을 기억하세요.

위의 7단계는 여러분이 시스템 디자인 인터뷰 문제에 답할 때 모든 다양한 측면을 다룰 수 있도록 안내해줄 것입니다.


어떠셨나요? 솔직히 저는 읽던 와중에 이거 공유 안하고 싶다(...)는 생각이 들 정도로 엄청나게 감동받았네요.. 글이 길다고 느껴질 수 있지만 현대적인 소프트웨어 아키텍처에서 다루는 거의 모든 관점을 압축해 담았다는 것을 고려하면 그리 길지만도 않은 것 같습니다 :) 모두들 이 글을 통해 성공적인 개발자 커리어를 쌓으시길 바라겠습니다. 여러분의 성공을 도와줄 처방전, 데브필이었습니다.