2년차 서버 개발자인 당신은 이직 시즌에 맞춰, 이 정도면 갈만하지~ 라는 생각과 함께 회사에 지원한 당신은 서류에 통과하게 됐다. 채용 프로세스는 다음과 같이 진행된다.
- 과제 전형
- 기술 면접
- 시스템 설계 면접
- 인성 면접
1단계인 과제 전형을 성공적으로 해낸 당신은 기술 면접 또한, 이력서를 기반으로 훌륭하게 해냈다. 그리고 이어지는 시스템 설계 면접, 시스템 설계 면접…?
면접장에 들어선 당신에게, 시스템 설계 관련 채용 담당자의 질문이 계속해서 이어지고 당신은 아마 이런 표정을 짓고 있을 것이다.
시스템 설계 면접에 대해서 하나도 준비된 것이 없는 당신은 처참하게 물어 뜯기고 면접장에서 나오게 될 것이다.
그리고 울부짖을 것이다. 왜 하늘은 나에게만…!
이런 당신을 위해 가상면접 사례로 배우는 대규모 시스템 설계 기초에서는 시스템 설계 면접을 보는 당신을 위한 공략법을 제공한다.
일반적으로 시스템 설계 면접은 유명한 소프트웨어(트X터, 쏘X, XX오톡…)를 설계해보라는 식으로 광범위하게 나올 수도 있으며, 굉장히 구체적인 문서형태로 주어질 수도 있다. 대게는 모호하고 광범위하기 때문에 어디서부터 시작해야 할 지도 모르며 면접 자체가 불편하게 느껴질 것이다.
물론 면접자가 시스템 설계 면접에서 모든 설계를 끝마치리란 기대는 하지 않겠지만, 그럼에도 불구하고 모호한 개념에 대해서 구체적인 이미지를 그리고 자신의 네트워크와 시스템에 대한 이해를 바탕으로 하나의 결정에 대한 의사결정과 해결 등… 자신의 방어 능력을 선보이는 자리라고 할 수 있다.
실제로 시스템 설계 면접은 지원자의 설계 능력과 기술적 측면 뿐만 아니라, 협력에 적합한지 압박을 해쳐나갈 수 있는지 모호한 문제를 건설적으로 해결할 능력이 있는지 등 종합적인 지원자의 능력을 알아보는 자리이며, 좋은 질문을 던질 수 있는지도 보는 자리다.
효과적 면접을 위한 4단계 접근법
시스템 설계는 면접장마다 제각각이겠지만, 대체적으로 공통적인 부분은 존재한다.
1단계 : 문제 이해 및 설계 범위 확정
시스템 설계 면접장에선, 생각나는 즉시 바로 답을 내놓는 것은 좋지 않을 수 있다. 시스템 설계 면접은 100:1을 연상시키는 퀴즈쇼가 아니다. 정답이 없는 안개 길을 헤쳐나가는 과정이다.
깊이 생각하고 스스로에게 질문하여 요구 사항과 가정들을 분명히 해야 한다. 중요한 것은
- 올바른 질문을 하는 것
- 적절한 가정을 하는 것
- 시스템 구축에 필요한 정보를 모으는 것
이다.
일반적으로 다음과 같은 질문들을 생각할 수 있다.
- 구체적으로 어떤 기능을 만들어야 하는가?
- 사용자 수는 어느정도로 추정해야 하는가?
- 회사의 규모와 앞으로의 수준은?
- 주로 사용하는 기술 스택은? 보일러 플레이트의 존재 여부는?
당신은 문제를 풀면, 캐쉬를 주는 서비스를 설계하라는 요구를 받았다.
(실제 기업 과제에서의 요구 사항과 질문을 기반으로 작성했습니다.)
이제 내가 경험했던 실제 사례를 바탕으로 어떻게 시스템 설계 면접을 봐야할지 살펴보려고 한다.
위에서 예상되는 질문들로 당신은 채용 담당자에게 질문해야 한다.
- 구체적으로 어떤 기능을 만들어야 하는가?
- Q : Web / App 중 어느 플랫폼을 기반으로 개발을 진행하게 됩니까? 둘 다입니까?
- A : 둘 다 지원해야 한다.
- Q : 가장 중요한 기능은 무엇입니까?
- A : 문제를 풀고, 문제를 푼 유저가 광고를 시청 후 캐쉬를 얻는 것이다.
- Q : 사용자가 문제를 푸는데 제한이 존재합니까?
- A : 월, 일, 시간 등 제한이 존재한다.
- Q : 객관식입니까 주관식 입니까?
- A : 둘 다 지원해야 한다.
- Q : 문제는 유저가 출제합니까?
- A : 서비스 차원에서 생성해야 한다. 또한, 문제간 공통으로 공유하는 공통 값이 존재한다.
- 사용자 수는 어느정도로 추정해야 하는가?
- Q : 서비스의 월 이용자 수는 어느정도로 예상하고 계십니까?
- A : 현재는 월 최대 10만명 정도로 예상하고 있다. 일(DAU)는 3000명 정도
- Q : 해당 추정치는 Web과 App 모두 포함한 추정치 입니까?
- A : 그렇다.
- 회사의 규모와 앞으로의 수준은?
- Q : 해당 프로젝트를 개발 할 개발팀 인력은 몇 명으로 생각해야 합니까?
- A : 프론트엔드(web 1명, app 1명) 2명, 백엔드 1명이라고 생각하는게 좋다.
- Q : 프로젝트를 위한 추가 채용 계획이 있습니까?
- A : 현재는 없다.
- 주로 사용하는 기술 스택은? 레퍼런스가 될 보일러 플레이트의 존재 여부는?
- Q : 해당 프로젝트에서 사용해야 할 기술 스택은 무엇입니까?
- A : 서버는 Node.js와 Nest.js, 프론트는 React와 크로스 플랫폼을 위한 React Native 그리고 인프라는 AWS, 데이터베이스는 mysql을 사용할 생각이다.
- Q : 해당 프로젝트를 진행하기 전 참고할만한 프로젝트가 회사에 존재합니까?
- A : 현재는 없다.
- Q : 참여하는 인력들은 모두 해당 기술 스택에 익숙합니까?
- A : 그렇다.
설계에 앞서, 위와 같은 프로젝트에 가용할 수 있는 인력과 프로젝트의 기술 스택 및 핵심에 대해서 질문하고 요구사항을 명확히 해야한다.
참고로, 실제 진행했던 과제에선 다음과 같은 요구사항이 있었다.
“공통 값인 MID라는 값이 있고, 각 MID는 매일 밤 자정에 임의의 숫자로 생성되고, 각 문제는 임의의 미드 값을 전달 받아 생성됩니다.”
스케줄링 작업이라는 명확한 요구사항이 추가됐다.
2단계 개략적인 설계안 제시 및 동의 구하기
2단계에서는 위에서 확인한 요구사항을 토대로 개략적인 설계안을 제시하고 그에 따른 동의를 얻는 것이다. 이 과정에서,
- 설계안에 대한 최초 청사진을 제시하고 의견을 구해야 한다. 개발자들은 대체로 아는척 하는 것을 좋아하며 설계 과정에 개입하는 것에 거부감을 느끼지 않을 것이다.
- 핵심 컴포넌트를 포함하는 다이어그램을 그려라, 클라이언트 / API / 웹 서버 / 데이터 저장소 / 캐시 / CDN 등이 포함될 수 있다.
- 이 청사진이 시스템 규모에 관계된 제약사항들을 만족하는지 개략적인 추산을 해보라. 그리고 이런 추산이 필요한지 면접관에게 미리 물어볼 것
문제를 풀고, 캐쉬를 얻는 서비스를 어떻게 설계할지에 대하여 개략적인 설계를 시작해야 한다. 대략적인 플로우는 문제 생성(post question)과 문제 풀이(question solvce)에 있을 것이다.
- 문제 생성: 관리자인 어드민은 시험 제목 / 시험 정답 / 공통 값과 같은 정보를 담아, 문제를 생성한다. 해당 문제는, 캐시 / 데이터베이스에 기록되고 사용자의 문제 리스트에 뜨게 된다.
- 문제 풀이: 특정 기준으로 정렬되어 보여지는 리스트 중 하나의 문제를 클릭하여 정답을 입력하고 정답이 맞다면, 해당 아이디로 캐쉬가 적립된다.
대략적인 기본 흐름을 정해놓고, 추가적인 제안을 해야한다.
나의 경우, 해당 과정에서 캐쉬 작업과 문제 작업을 하나의 서버에서 처리하려고 했었는데 해당 채용 담장자분께선 결제서버를 따로 나누기를 원하셔서 의견 청취를 통해 별도의 서버로 분리했다. 그리고, 각 문제별 제한적인 조건에 대해서 청취하여 해당 조건에 대한 명확한 피드백을 받게 됐다.
3단계 상세 설계
상세 설계 단계에 왔다면, 다음 목표를 달성했을 것이다.
- 시스템에서 전반적으로 달성해야 할 목표와 기능 범위
- 전체 적인 개략적 청사진 마련
- 해당 청사진에 대한 면접관의 의견 청취
- 상세 설계에서 집중해야 할 영역들 확인
이제 채용 담당자인 면접관과 해야 할 일은 설계 대상들의 우선순위를 정하는 것이다. 어떤 사람은 각 시스템 간 성능과 스택 선정에 관하여 질문을 던질 수 있고, 어떤 사람은 병목 구간이나 자원 요구랑 추정에 대하여 초점이 맞춰져 있을 것이다.
나의 경우 가장 중요한 초점으로, 수 많은 문제들을 사용자가 골고루 풀었으면 좋겠다는 것이었고 사용자가 리로딩할 시 새로운 문제가 등장해야 해서 해당 트래픽과 어떻게 리스트 검색을 최적화 할 것인지가 주요 쟁점이었다.
대용량 트래픽의 경우, ALB를 통한 고가용성을 확보하고 ASG 그룹을 통해 scale out 전략을 취하는 식으로 기본적인 서버 구성을 수행한다고 했으며 웹 API 서버와 결제 서버를 분리하는 식으로 시스템을 설계한다고 했다. 스케줄링 작업의 경우 API 서버에 붙어있을 경우 scale out되면서, 다른 스케줄러가 동시에 여러 값을 생성할 수 있기 때문에 이벤트 브릿지의 스케줄링 기능을 통해 람다로 매 정각마다 하나씩 생성하는 식으로 구성했다.
4단계 마무리
이 마지막 단계에서 면접관은 몇 가지 후속 질문을 던질 수 있다. 만약 시스템 병목 구간 혹은 좀 더 개선 가능한 지점을 찾아내라고 한다거나, 자신의 비판적사고를 통해(완벽한 것은 없기 때문…) 좋은 인상을 남기기 위해 고심하는 척 하는 것은 중요한 일이다.
설계를 한 번 더 요약하는 것도 의미 있을 수 있다.
운영적으로 추가적인 논의를 하는 것도 중요하다. 매트릭은 어떻게 수집할 것이며, 모니터링은? 로그는? 시스템 배포(CI/CD)는 어떻게 할 것인가?
마지막으로 미래에 닥칠 규모 확장 요구에 대해서는 어떻게 대처할 것인가? 현재 설계는 월 10만을, 일 3000명 정도를 예상으로 설계 됐다. 만약 월 100만명 이상의 서비스라면…?
그렇기 때문에 면접자는 마지막으로 확인해야 한다.
해야할 것
- 질문을 통해 확인하라, 스스로의 가정이 항상 옳을 수는 없다.
- 요구사항을 이해하라
- 정답이나 최선의 답안은 없다는 것을 명심해야 한다.
- 나의 생각 흐름을 이해할 수 있어야 한다. 면접관이,
- 여러 해법을 함께 제시하라
- 개략적 설계(2단계)에 면접관이 동의한다면, 컴포넌트의 세부 사항을 설명하라, 가장 중요한 것부터 진행해라
- 면접관의 아이디어를 이끌어 내라
- 포기하지 말 것
하지말아야 할 것
- 전형적인 면접 문제들도 대비하지 않은 상태에서 면접장에 가지 말라
- 요구사항이나 가정들을 분명이 하지 않은 상태에서 설계를 제시하지 말라, 개략적 설계를 마친 뒤 나아가라
- 진행 중 막혔다면, 힌트를 청하기를 주저하지 말라
- 소통을 주저하지 말 것, 침묵 속에 설계를 진행하지 말 것
- 설계안을 내놓는 순간(3단계) 끝났다고 생각하지 말 것, 끝났다고 하기 전까지는 끝난 것이 아니다.
시간배분
- 1단계 : 문제 이해 및 설계 범위 확정(3 ~ 10분)
- 2단계 : 개략적 설계안 제시 및 동의 구하기 (10 ~ 15분)
- 3단계 : 상세 설계 (10 ~ 25분)
- 4단계 : 마무리 (3 ~ 5분)
마치며.
해당 아키텍쳐는 위 과정에서 살펴본 간단한 시스템 구조이다. 면접 과정에서 추가적으로 대화한 내용을 살퍼보면, 일반적으로는… 음 크게 나무랄 것도 없다고 판단되지만, 크게 잘 구성한 아키텍쳐도 아니다. 여기서 가장 큰 고민은 캐시서버 구성이었다.
Q : 캐시서버에서 유저에게 보여줄 문제 리스트의 기준은 어떻게 정해집니까? 이 기준이야말로 이 서비스에서 가장 중요한 정보입니다.
A : … 캐시 서버를 없앨까요?
문제 리스트는 특정 기준에 따라 유저에게 노출되야 하는 문제가 계속해서 달라져야 한다. 이 경우 캐시 서버가 없어지는 것이 처음에 맞다고 생각했지만, 문제를 새로고침을 할 때마다 업데이트 된 문제가 보여아 하며 유저가 mid 값 당 풀 수 있는 문제 수가 정해져 있고… 계속해서 문제의 조건이 복잡하게 달라지기 때문에 역시 없어지는게 맞나…? 라고 생각하다가도 결국 해당 조건과 분기에 따라 특정 유저들에게 노출되는 공통의 문제리스트는 공통 분모를 향해 달려가기 때문에 결국 캐시 서버가 있어야 한다고 결정 내렸다. 결국은 비슷한 데이터셋을 유저에게 보여주는 기능이라는 생각을 했다.
그렇다면, 캐시 서버를 어떻게 구축할 것인가? 변화하는 기준에 따라 어떤 기준을 정해 맞춰야 하는가?
- 캐시 서버는 Redis같은 리소스를 사용하여 구성한다.
- 문제 데이터베이스와 유저 데이터베이스의 종합적인 정보를 토대로 여러 기준을 만들어 조건에 맞는 캐시 로직을 구축해 데이터 셋을 만든다.
- 매번 변동되면 캐시 서버의 리소스가 낭비되기 때문에, 특정 주기마다 한 번씩 로직을 교체하고 캐시 데이터 무효화 후 새롭게 업데이트한다.
위 가정을 통해서 전반적인 문제 리스트를 유저에게 보여주는 식으로 제안 했지만, 돌아온 답은
Q : 유저들마다 적용되는 기준은 어떻게 반영하실 건가요? 해당 사안은 특정 유저를 고려하지 않은 서비스 전반적인 기준인 것 같습니다.
A : … 캐시서버를 두지 않고, 코드 레벨에서 비즈니스로직으로 처리하는 것이 옳은 것 같습니다.
결론적으로는 캐시 서버를 없애는 것으로 결정 됐다.
비슷한 리스트를 두고 유저에게 보여주는 것으로 생각했으며, 당연히 배운대로 캐시 서버를 두는 것이 좋다고 생각했지만 사실은 처음부터 캐시 서버가 없다는 가정으로 시작 했어야 했다.
위 사례를 통해 하고 싶은 말은 다음과 같다.
기본적인 시스템 설계에 따라, 당연히 존재해야 하는 만능의 도구 같은 것은 존재하지 않으며 서비스의 상황과 요구사항에 맞춰 시스템을 설계해야 한다는 것… 당연히 있어야 하는 건 없다.
심지어 그 회사에선… 람다를 사용해 스케줄링 작업을 하는 것도 scale out말고 scale up을 하는 것이 더 좋지 않냐고 말하며… 이해하지 못했다(그냥 스케줄링 서버를 단일적으로 구성하고 싶어했다. ⇒ 왜..? ⇒ 그게 간단하니까…). 회사의 성향과 규모에 따라 더 간단하고 심플하게 시스템을 구성할 필요도 있다.
Share article