코딩 인터뷰 준비: UMPIRE 풀이 절차, 복잡도 설명, 엣지 케이스, 화이트보드 요령

이 글의 핵심

면접관은 최종 코드보다 거기에 도달하는 과정을 보려 하는데, 조용히 구현만 하다 보면 좋은 풀이도 제대로 평가받지 못합니다. 질문을 리스크 관리로 쓰는 법, 상각 복잡도를 구분해 말하는 법, 틀렸을 때의 복구 멘트와 힌트 요청 방법, 요구사항과 에러 계약을 먼저 합의하는 실무형 면접 대응까지 이어집니다.

들어가며: 맞는 코드보다 먼저 오는 것

라이브 코딩 면접에서 면접관은 최종 코드만큼이나 그 코드에 도달하는 과정을 봅니다. 같은 정답이라도 요구사항을 먼저 정확히 짚고, 자료구조를 고른 이유를 말하고, 복잡도를 스스로 설명하고, 엣지 케이스를 걸러 내고, 구현 뒤 직접 검증하는 지원자는 함께 일할 때의 모습이 그려지기 때문에 신뢰를 얻습니다.

이 글은 알고리즘 목록보다 면접이라는 상황에서의 사고, 말하기, 검증 방법을 다룹니다. 패턴 학습은 코딩 테스트 준비 전략: 알고리즘 학습 순서와 시험장 실전 팁에서 다루므로, 여기서는 그 패턴을 면접 현장에서 어떻게 보여 줄지에 집중합니다.


문제 해결 프레임워크: UMPIRE를 대화 스크립트로 쓰기

UMPIRE는 CodePath 등의 교육 과정에서 소개하는 라이브 코딩 절차입니다. 단계 이름을 외우기보다, 각 단계에서 면접관에게 할 한마디를 준비해 두는 용도로 쓰면 효과가 큽니다.

단계할 일면접에서의 역할
Understand문제, 제약, 입출력을 정확히 읽는다오해를 없애고 질문할 기회를 만든다
Match알려진 패턴이나 유사 문제와 연결한다”투 포인터 문제로 보입니다”처럼 가설을 말로 고정한다
Plan접근 단계, 자료구조, 복잡도를 적는다구현 전에 면접관과 방향을 합의한다
Implement계획을 코드로 옮긴다변수명, 경계, 불변 조건을 일관되게 유지한다
Review작은 예로 코드를 따라가며 검증한다버그를 스스로 잡는 모습을 보여 준다
Evaluate복잡도, 대안, 확장을 말한다트레이드오프 논의로 이어 간다

Understand: 질문은 리스크 관리

이해 단계에서는 다음을 한 번에 하나씩 확인합니다.

입력 규모는 $n$, $m$의 범위, 문자열 길이의 합, 그래프의 간선 수 같은 것으로, 여기서 허용되는 복잡도의 상한이 정해집니다. 값의 범위로는 음수와 0이 있는지, 중복이 있는지, 정렬되어 있는지, 인덱스가 0부터인지를 확인하고, 합이나 곱이 언어의 정수 범위를 넘을 수 있는지도 짚습니다. 출력 형식은 인덱스를 반환하는지 값을 반환하는지, 답이 여러 개면 아무거나 되는지 사전순 최소인지를 묻습니다. 마지막으로 제자리(in-place) 수정 여부, 추가 메모리 제한, 입력이 스트림으로 들어오는지 같은 부수 조건이 있습니다.

예를 들면 이렇게 말할 수 있습니다. “$n$이 최대 $10^5$라서 $O(n^2)$이면 약 $10^{10}$번 연산이라 시간 안에 어렵습니다. 배열이 정렬되어 있다면 이진 탐색이나 투 포인터를 먼저 떠올리겠는데, 정렬 여부와 중복 허용 여부만 확인해도 될까요?”

Match: 패턴을 말로 붙이기

패턴을 속으로만 떠올리면 중간에 로직이 꼬였을 때 어디로 돌아가야 할지 잃기 쉽습니다. “의존 관계가 있는 작업 순서를 정하는 문제라 위상 정렬 후보입니다”처럼 이름을 붙이면 선택지가 줄고, 면접관도 방향을 알 수 있습니다. 비슷해 보이지만 맞지 않는 패턴을 함께 짚으면 이해의 깊이가 드러납니다. 예를 들어 “누적합으로 풀 수 있어 보이지만 중간에 값이 갱신되니 세그먼트 트리나 펜윅 트리가 필요할 수도 있습니다. 먼저 갱신 횟수가 얼마나 되는지 보겠습니다”처럼 말합니다.

Plan: 의사코드 수준에서 멈추기

구현에 들어가기 전에 입력을 어떤 자료구조에 담을지, 핵심 루프 한 번이 무엇을 보장하는지만 적어도 충분합니다. 면접관이 “그 방향으로 가시죠”라고 하면 이후 시간을 안심하고 구현에 쓸 수 있고, 방향이 틀렸다면 이 시점에 바로잡아 줍니다. 반대로 말없이 코드부터 쓰다가 방향이 틀린 것을 늦게 알면, 되돌리는 데 남은 시간을 대부분 씁니다.

Implement와 Review: 조용한 구현 피하기

구현 중에는 짧게라도 지금 쓰는 줄의 의도를 말합니다. 침묵은 집중으로 보일 수도 있지만, 라이브 코딩에서는 막혀 있는 것으로 읽히기 쉽습니다. Review 단계에서는 적어도 두 가지를 확인합니다. 하나는 첫 인덱스와 마지막 인덱스 같은 경계에서 코드가 맞게 동작하는지, 다른 하나는 루프의 불변 조건이 매 반복에서 유지되는지입니다. “여기서 off-by-one이 날 수 있어서 i <= n 대신 i < n으로 두겠습니다” 같은 한마디가 신뢰를 줍니다.

Evaluate: 복잡도와 다음 질문을 함께

마지막에는 시간과 공간 복잡도를 말하되, 최악인지 평균인지, 입력이 정렬되어 있다는 전제에서만 성립하는지까지 밝힙니다. 시간이 남으면 “해시맵 대신 정렬과 투 포인터를 쓰면 추가 공간은 $O(1)$로 줄지만 시간은 $O(n \log n)$으로 늘어납니다”처럼 대안 하나를 트레이드오프와 함께 제시합니다.


시간 복잡도 설명: 숫자보다 설명 순서

면접에서 복잡도는 정답을 맞히는 시험이라기보다 추론 과정을 말로 보여 주는 시간에 가깝습니다.

먼저 비용 모델을 정한다

복잡도를 말하기 전에 기본 연산의 비용을 정합니다. “해시맵 조회와 삽입은 평균 $O(1)$로 두고, 길이 $L$인 문자열 비교와 해싱은 $O(L)$로 보겠습니다”처럼 말하면 면접관과 같은 가정 위에서 이야기할 수 있습니다. 문자열을 키로 쓰는 해시맵 조회가 사실은 $O(L)$이라는 점을 놓치는 경우가 많으니 특히 짚어 둘 만합니다.

루프 개수를 그대로 지수로 쓰지 않는다

삼중 루프라도 안쪽 루프가 상수 번만 돈다면 전체는 $O(n)$입니다. 투 포인터처럼 바깥 루프 안에 while이 있어도 안쪽 포인터가 전체 실행 동안 최대 $n$번만 움직인다면 합쳐서 $O(n)$입니다. 반대로 루프는 하나지만 매 반복에서 이진 탐색을 하면 $O(n \log n)$입니다. 각 루프가 전체 실행 동안 몇 번 도는지를 말로 풀어 설명하는 습관이 필요합니다.

최악, 평균, 상각을 구분한다

면접에서는 보통 최악의 경우를 기본으로 말합니다. 해시 테이블은 충돌이 몰리면 조회가 $O(n)$이 되고, 퀵 정렬은 피벗이 계속 한쪽 끝으로 잡히면 $O(n^2)$이 된다는 점을 짚을 수 있으면 좋습니다. 평균 복잡도를 말할 때는 “입력이 무작위라고 가정하면”처럼 전제를 밝힙니다.

상각(amortized) 복잡도는 연산 하나하나가 아니라 연산 여러 번의 총비용을 횟수로 나눈 값입니다. 동적 배열의 push는 가끔 전체를 복사하는 $O(n)$ 연산이 끼지만, 용량을 두 배씩 늘리면 $n$번 추가의 총비용이 $O(n)$이라 상각 $O(1)$입니다. 경로 압축과 랭크 기반 합치기를 함께 쓰는 유니온 파인드는 연산당 상각 $O(\alpha(n))$으로, 실질적으로 상수입니다. “거의 매번 $O(1)$“이라고 뭉뚱그리기보다 상각이라는 용어로 구분해 말하면 정확합니다.

공간 복잡도는 보조 배열만이 아니다

재귀 DFS는 재귀 깊이만큼 호출 스택을 씁니다. 한쪽으로 길게 늘어진 트리라면 깊이가 $n$이 되어 Python 같은 언어에서는 재귀 한도에 걸릴 수도 있습니다. 문자열을 슬라이싱해 새 문자열을 계속 만드는 코드는 그 복사본만큼 메모리와 시간을 추가로 씁니다. “제자리에서”라는 조건이 있다면 입력 배열을 변형해도 되는지도 함께 물어보면 공간 논의가 정확해집니다.

틀렸을 때의 복구 멘트

복잡도를 잘못 말했다면 짧게 인정하고 바로 수정안을 붙입니다. 예를 들어 “방금 $O(n)$이라고 했는데, 처음에 정렬을 하니 전체는 $O(n \log n)$이 맞습니다. 정렬 이후의 투 포인터 부분만 $O(n)$입니다”처럼 어떤 부분이 지배적인지까지 설명하면 실수가 오히려 이해도를 보여 주는 기회가 됩니다.


엣지 케이스 찾기: 목록이 아니라 축으로

엣지 케이스를 외우기보다 다음 축을 습관적으로 훑으면 누락이 줄어듭니다.

규모 축

빈 입력(빈 배열, 빈 문자열, 노드가 없는 그래프)에서 코드가 예외를 던지지 않는지 봅니다. 원소가 하나뿐이면 루프가 한 번도 돌지 않는 분기가 생기고, 반환값이 의도대로인지 확인해야 합니다. 모든 원소가 같은 경우에는 해시맵 키가 하나로 합쳐지거나, “더 큰 값”을 찾는 조건이 한 번도 참이 되지 않거나, 퀵 정렬 분할이 한쪽으로 쏠리는 문제가 드러납니다. 최댓값과 최솟값 근처에서는 오버플로와 경계 인덱스를 봅니다.

정렬, 중복, 부호 축

정렬이 알고리즘의 전제인지(이진 탐색, 투 포인터) 확인하고, 중복을 허용하는지 제거해야 하는지, 같은 값이 여러 개일 때 첫 번째와 마지막 중 무엇을 반환하는지 정합니다. 음수와 0이 섞이면 곱의 부호가 뒤집히고, 구간 합에서 “합이 커지기만 한다”는 전제가 깨져 슬라이딩 윈도우를 쓸 수 없게 됩니다.

자료구조 경계 축

트리는 노드가 하나뿐인 경우와 한쪽으로만 이어진 선형 체인을 봅니다. 그래프는 연결되지 않은 컴포넌트, 자기 자신으로 가는 간선, 같은 두 정점 사이의 중복 간선을 봅니다. 구간 문제는 겹치는 구간, 끝점이 맞닿은 구간([1,3]과 [3,5]를 겹친다고 볼지), 다른 구간을 완전히 포함하는 구간을 봅니다.

구현 실수로 이어지는 케이스

<=와 <, 슬라이딩 윈도우의 끝점 포함 여부 같은 off-by-one이 가장 흔합니다. 정수 나눗셈은 언어마다 음수에서 동작이 다릅니다(Python의 //는 음의 무한대 쪽으로 내리고, C++과 Java의 /는 0 쪽으로 버립니다). 누적 곱을 나눗셈으로 되돌리는 풀이는 0이 하나라도 있으면 깨집니다.

코드를 실행할 수 없는 환경이라면 “확인할 작은 예 세 개”를 정해 말로 따라갑니다. 일반적인 예 하나, 최소 규모 예 하나, 가장 까다로워 보이는 경계 예 하나면 대부분의 버그가 드러납니다.


화이트보드와 공유 에디터 기법

물리 보드든 화면 공유 에디터든 면접관이 따라오기 쉬운 배치라는 원칙은 같습니다.

공간 배치

함수 시그니처와 입력 타입을 맨 위에 먼저 쓰고, 핵심 로직은 가운데에, 불변 조건과 복잡도 메모는 오른쪽이나 아래에 둡니다. 물리 보드에서는 나중에 끼워 넣을 줄을 위해 여백을 남겨 두면 수정이 훨씬 쉽습니다.

이름은 짧되 의미는 분명하게

i, j는 인덱스, lo/hi는 이진 탐색 경계처럼 관례를 따르면 면접관이 추적하기 쉽습니다. 반대로 mp, g, tmp2 같은 약어는 읽는 사람이 매번 해석해야 하므로, 의미가 있는 값은 heights, inDegree처럼 풀어 씁니다.

한 번에 한 블록씩

전체를 한꺼번에 쓰다가 지우기를 반복하는 것보다, 시그니처, 핵심 루프의 골격, 세부 처리 순으로 채워 가면서 “여기까지가 뼈대입니다”라고 말하는 편이 면접관이 진행 상황을 이해하기 쉽습니다.

지우기보다 고쳐 쓰기

가정이 바뀌면 이전 내용을 지우기보다 줄을 긋고 옆에 수정본을 쓰는 편이 낫습니다. 면접관은 생각이 어떻게 바뀌었는지도 보고 싶어 하므로, 깨끗한 최종본만 남기려는 노력이 오히려 손해가 될 수 있습니다.

힌트 요청은 패배가 아니다

몇 분 이상 진전이 없으면 구체적으로 힌트를 요청합니다. “그리디로 풀 수 있는지 DP가 필요한지 판단이 서지 않는데, 제약에서 놓친 조건이 있을까요?”처럼 어디서 막혔는지를 좁혀서 물으면 면접관이 돕기 쉽고, 문제를 어떻게 분해하고 있는지도 함께 보여 줄 수 있습니다.


실무형 코딩 면접 패턴

회사와 직무에 따라 알고리즘만이 아니라 실무에 가까운 역량을 보는 면접도 많습니다.

요구사항을 코드 전에 합의한다

“이번 시간에는 A까지 구현하고, B는 인터페이스만 남겨 두겠습니다”처럼 범위를 자르는 능력은 실무에서 그대로 쓰입니다. 시간이 부족할수록 동작하는 최소 해법과 확장 지점을 먼저 말로 합의해 둡니다.

API와 오류 계약을 말한다

함수가 실패할 수 있다면 null을 반환할지, Optional이나 Result 타입을 쓸지, 예외를 던질지 호출 계약을 짚습니다. 입력이 잘못되었을 때 조용히 틀린 값을 반환하지 않도록 어디서 검증할지를 설명하면 좋은 신호가 됩니다.

테스트 가능성을 열어 둔다

입출력과 핵심 로직을 분리해 순수 함수로 만들 수 있는지, 어떤 경계 사례를 단위 테스트로 고정하고 싶은지를 말하면 품질에 대한 감각이 드러납니다.

성능은 측정을 전제로

실무에서 병목은 추측보다 프로파일링으로 찾습니다. 면접에서도 “먼저 $O(n)$으로 정확하게 만들고, 실제 데이터에서 병목이 확인되면 캐시를 붙이겠습니다”처럼 이른 최적화를 피하는 태도가 설득력 있습니다.

트레이드오프를 한 줄로 끝내지 않는다

“해시가 빠릅니다”에서 끝내지 말고, 추가 메모리, 최악의 경우 충돌, 순서 보장이 필요한지 같은 조건을 하나씩 짚습니다. 운영 경험이 많지 않아도 “이 데이터가 메모리에 다 올라간다고 가정해도 될까요?” 같은 질문으로 실무 감각을 보여 줄 수 있습니다.


같이 보면 좋은 글