실무 역량 인터뷰
장애 대응과 디버깅 면접 준비
재현·좁히기·가설·검증의 절차, 이분 탐색, 스택 트레이스 읽기, 지연과 오류의 구분, 재시작 전 증거 수집까지 다루는 면접 가이드입니다.
이 면접은 지식이 아니라 절차를 봅니다
디버깅 질문은 다른 면접 질문과 성격이 다릅니다. 문법 질문이나 자료구조 질문은 알면 맞히고 모르면 틀립니다. 그런데 장애 대응 질문은 애초에 답을 모르는 상태를 전제로 시작합니다. 면접관도 정답을 기대하지 않습니다. 모르는 상태에서 후보를 어떻게 줄여 가는지를 봅니다.
그래서 이 영역에서 가장 나쁜 답은 틀린 답이 아니라 근거 없이 맞는 답입니다. “아마 데이터베이스 커넥션 풀이 부족한 것 같습니다”라고 첫 문장에서 단정하면, 설령 그것이 정답이더라도 좋은 점수를 받지 못합니다. 다음에 다른 장애가 왔을 때 같은 방식으로 찍을 것이기 때문입니다. 반대로 원인을 끝내 못 찾더라도 후보를 절반씩 줄여 나간 과정이 보이면 좋은 평가를 받습니다.
실무에서도 같습니다. 경험이 쌓일수록 직감이 정확해지는 것은 사실이지만, 좋은 엔지니어와 그렇지 않은 엔지니어의 차이는 직감의 정확도가 아니라 직감이 틀렸을 때 얼마나 빨리 그것을 알아채는가에 있습니다. 그 알아챔을 만드는 것이 절차입니다.
절차: 재현, 좁히기, 가설, 검증
순서가 정해져 있고, 이 순서를 건너뛰면 시간이 늘어납니다.
재현이 첫 단계인 이유는 재현할 수 없으면 고쳤는지도 알 수 없기 때문입니다. 재현은 “가끔 일어난다”에서 “이 조건에서 일어난다”로 옮기는 작업입니다. 완전한 재현이 안 되더라도 발생 조건을 좁히는 것 자체가 큰 진전입니다. 특정 사용자만, 특정 시간대만, 특정 요청 크기 이상에서만 일어난다는 사실을 알아내면 후보가 급격히 줄어듭니다.
좁히기는 문제가 존재하는 범위를 절반씩 잘라 내는 단계입니다. 이 단계에서는 원인을 추측하지 않습니다. 추측 대신 “문제가 이 경계의 앞쪽에서 생기는가 뒤쪽에서 생기는가”만 판정합니다. 요청이 지나가는 구간을 나누고, 각 경계에서 데이터가 이미 잘못됐는지 확인하는 방식입니다. 잘못된 값이 처음 나타나는 지점을 찾으면 그 앞의 코드는 전부 용의선상에서 빠집니다.
가설은 좁혀진 범위 안에서만 세웁니다. 그리고 가설은 반드시 틀렸음을 확인할 수 있는 형태여야 합니다. “커넥션 풀이 문제인 것 같다”는 검증할 수 없습니다. “활성 커넥션 수가 풀 최대치에 붙어 있고 대기 큐 길이가 0보다 크다면 커넥션 부족이다”는 검증할 수 있습니다. 가설을 세울 때 그것을 반증할 관측값을 함께 정하는 습관이 이 면접에서 가장 확실하게 점수를 버는 지점입니다.
검증은 가설이 맞았는지 확인하는 단계이자, 고친 뒤에 다시 한번 하는 단계입니다. 여기서 자주 생략되는 것이 “고치기 전에 이 수정으로 증상이 사라진다는 것을 어떻게 알 것인가”를 미리 정하는 일입니다. 수정 후에 마침 증상이 안 보였다는 것과, 수정이 원인을 제거했다는 것은 다른 이야기입니다.
가설을 말할 때는 “~일 것 같습니다” 대신 “~라면 이 지표가 이렇게 보일 것입니다. 확인해 보겠습니다”라고 말하십시오. 문장 형태를 바꾸는 것만으로 답의 성격이 추측에서 검증으로 바뀝니다.
로그 발췌를 좁혀 나가는 실제 과정
LOG · 결제 API 지연 신고 (시각과 수치는 설명을 위한 예시입니다)
# 1) 신고 내용: "오후부터 결제가 가끔 안 됩니다"
# 먼저 확인한 것 — 실패인가 지연인가
[metrics] api=/pay window=5m
14:00 rps=120 p50=45ms p99=180ms err5xx=0.0% timeout=0
14:20 rps=124 p50=47ms p99=210ms err5xx=0.0% timeout=0
14:40 rps=118 p50=52ms p99=2,140ms err5xx=0.3% timeout=4
15:00 rps=121 p50=58ms p99=8,900ms err5xx=2.1% timeout=31
15:20 rps=119 p50=61ms p99=9,950ms err5xx=4.7% timeout=68
# 읽은 것:
# - rps는 그대로다 -> 트래픽 급증이 원인이 아니다
# - p50은 거의 안 변했는데 p99만 폭증 -> 전부 느린 게 아니라 일부만 막힌다
# - 5xx는 지연을 뒤따라 올라간다 -> "실패"는 결과이고 원인은 "지연"이다
# => 후보에서 제외: 배포 직후 전면 오류, 트래픽 폭주
# => 남은 후보: 특정 의존 대상의 지연, 자원 고갈, 잠금 경합
[app] 15:04:11 WARN pool=db active=50/50 idle=0 waiting=37 waitAvgMs=6210
[app] 15:04:11 INFO pool=http-outbound active=6/64 idle=58 waiting=0
[app] 15:04:12 ERROR /pay reason=PoolTimeout waited=10000ms txId=8f21c4
# 읽은 것:
# - DB 풀은 꽉 찼고 대기가 길다 / 외부 HTTP 풀은 한가하다
# - 즉 "느린 것"은 DB 커넥션을 잡고 있는 쪽이다
# - 아직 결론이 아니다: 풀이 작은 것인가, 쿼리가 느린 것인가,
# 아니면 커넥션을 붙잡고 안 놓는 코드가 있는가 — 셋은 대책이 다르다
[db] 15:04:13 slow-query log
duration=5,930ms rows_examined=1,840,000 rows_sent=1
SELECT * FROM payments WHERE merchant_ref = ? ORDER BY created_at DESC LIMIT 1
# 읽은 것:
# - 한 건을 얻으려고 184만 행을 훑었다 -> 인덱스를 못 타고 있다
# - 이 쿼리 하나가 6초 가까이 커넥션을 점유한다
# - 50개 커넥션 * 6초면 초당 처리 가능한 이 쿼리는 8건 남짓
# (거친 어림셈이지만 대기 큐가 왜 쌓이는지는 설명된다)
# 2) 남은 질문: 왜 오후부터인가? 코드가 그대로라면 데이터가 변한 것이다
[deploy] 어제 21:40 merchant_ref 컬럼 추가 배포 (인덱스 없음)
[batch] 오늘 13:50 가맹점 정산 배치 시작 — payments 테이블 급증
# 3) 결론과 검증 계획
# 가설: 인덱스 없는 merchant_ref 조회가, 테이블이 커지자 임계를 넘었다
# 반증 조건: 이 쿼리를 제외해도 풀 대기가 남는다면 가설은 틀렸다
# 즉시 조치: 해당 조회 경로 차단 또는 인덱스 추가
# 검증: p99와 pool waiting을 함께 본다. 둘 다 내려와야 원인이 맞다이 발췌에서 배울 것은 결론이 아니라 제외의 순서입니다. 처음에 확인한 것은 원인 후보가 아니라 “지금 벌어지는 일이 어떤 종류의 일인가”였습니다. 초당 요청 수가 그대로라는 한 줄로 트래픽 원인이 통째로 빠졌고, 중앙값은 그대로인데 상위 백분위만 튄다는 관찰로 “전부 느림”이 빠졌습니다. 그다음에야 어느 자원이 막혔는지로 내려갔고, 자원을 특정한 뒤에도 곧바로 결론을 내지 않고 “풀이 작은가, 쿼리가 느린가, 반납이 안 되는가”라는 세 갈래를 세웠습니다. 각 갈래의 대책이 다르기 때문에 여기서 뭉뚱그리면 엉뚱한 수정을 하게 됩니다.
마지막으로 “왜 하필 오늘 오후인가”라는 질문을 던진 것이 중요합니다. 코드가 바뀌지 않았는데 증상이 생겼다면 데이터나 환경이 바뀐 것입니다. 시간 축에 배포와 배치 기록을 겹쳐 보는 것은 좁히기의 강력한 방법이며, 면접에서 이 접근을 말하면 운영 경험이 있다는 신호로 읽힙니다.
느린 것과 실패하는 것을 갈라야 합니다
“서비스에 문제가 생겼습니다”라는 신고를 받으면 가장 먼저 할 일은 그것이 지연인지 오류인지 정하는 것입니다. 이 둘은 보는 지표도, 의심하는 대상도, 대응의 긴급도도 다릅니다.
오류는 이산적입니다. 어떤 요청이 실패했고 실패한 이유가 코드에 남습니다. 그래서 오류율과 오류 종류의 분포를 봅니다. 특정 오류 하나가 급증했는지, 여러 종류가 고르게 늘었는지에 따라 이야기가 완전히 달라집니다. 하나만 급증했다면 그 경로를 보면 되고, 고르게 늘었다면 공통 의존 대상이나 자원 고갈을 의심합니다.
지연은 연속적이고, 그래서 평균으로 보면 거의 항상 놓칩니다. 요청 백 건 중 한 건만 십 초씩 걸려도 평균은 별로 안 움직입니다. 그래서 분포를 봐야 하고, 중앙값과 상위 백분위를 함께 놓고 봐야 합니다. 중앙값과 상위 백분위가 같이 올라갔다면 전체가 느려진 것이고 대개 공통 자원이 원인입니다. 중앙값은 그대로인데 상위만 올라갔다면 일부 요청만 다른 경로를 타는 것이고, 특정 조건에서만 느린 코드나 대기 큐를 의심합니다.
그리고 이 둘은 서로 변합니다. 지연이 심해지면 타임아웃이 걸리고, 타임아웃은 오류로 집계됩니다. 위 로그에서도 오류율 증가가 지연 증가를 뒤따라 올라갔습니다. 그래서 오류가 늘었을 때 “언제부터 늘었나”와 “지연은 그전에 이미 올라가고 있었나”를 함께 보면 인과를 거꾸로 짚는 실수를 피할 수 있습니다. 시스템 전반의 관점을 정리하려면 확장 가능한 서비스 설계도 함께 보시면 좋습니다.
스택 트레이스는 위에서도 아래에서도 읽습니다
스택 트레이스를 어떻게 읽느냐고 물으면 “맨 위 줄을 봅니다”라는 답이 많이 나옵니다. 맞지만 절반입니다. 두 방향으로 읽으면 얻는 것이 다릅니다.
위에서 아래로 읽으면 예외가 어디에서 터졌는가를 알 수 있습니다. 맨 위의 몇 줄이 실제로 실패한 지점이고, 여기서 던져진 예외의 종류와 메시지가 무엇이 잘못됐는지를 말해 줍니다. 다만 예외가 터진 곳이 원인이 있는 곳과 같다는 보장은 없습니다. 값이 잘못 들어와서 터진 것이라면 원인은 그 값을 만든 훨씬 앞쪽에 있습니다.
아래에서 위로 읽으면 어떤 경로로 여기까지 왔는가를 알 수 있습니다. 아래쪽은 진입점입니다. 어떤 요청 처리에서 시작됐는지, 어떤 스케줄러가 불렀는지, 어떤 컨트롤러를 거쳤는지가 나옵니다. 재현 조건을 찾을 때는 이쪽이 훨씬 중요합니다. 위쪽만 봐서는 “왜 이 코드가 실행됐는지”를 알 수 없습니다.
실전에서는 세 번째 시선이 필요합니다. 내 코드가 처음 나오는 줄을 찾는 것입니다. 트레이스의 대부분은 프레임워크와 라이브러리 내부이고, 우리가 바꿀 수 있는 지점은 우리 패키지의 프레임입니다. 그 줄이 라이브러리 안쪽과 만나는 경계이며, 대개 거기서 잘못된 인자를 넘겼습니다.
감싸인 예외도 반드시 확인하십시오. 원인 예외가 여러 겹으로 포장되어 있으면 맨 바깥의 메시지는 “처리 중 오류”처럼 아무 정보가 없고, 실제 정보는 가장 안쪽 원인에 있습니다. 로그에서 원인 사슬이 잘려 나가도록 예외를 다시 던지는 코드는 그 자체로 고쳐야 할 대상입니다.
이분 탐색은 디버깅의 만능 도구입니다
좁히기 단계에서 쓸 수 있는 방법은 사실상 하나로 수렴합니다. 절반으로 자르고 어느 쪽인지 판정하기입니다. 이 방법이 강력한 이유는 후보가 천 개여도 열 번이면 하나로 줄기 때문이고, 무엇보다 원인을 몰라도 쓸 수 있기 때문입니다.
자를 수 있는 축은 여러 가지입니다. 시간으로 자르면 어느 변경 이후에 문제가 생겼는지 알 수 있습니다. 버전 관리 도구가 제공하는 이분 탐색 기능이 이것을 자동화합니다. 코드 경로로 자르면 요청이 지나는 중간 지점에서 데이터를 찍어 어느 구간에서 값이 망가지는지 판정할 수 있습니다. 입력으로 자르면 문제를 일으키는 데이터의 절반을 빼고 여전히 재현되는지 보면서 최소 재현 입력을 만들 수 있습니다. 구성으로 자르면 설정이나 기능 플래그를 절반씩 끄면서 어느 것이 관여하는지 알 수 있습니다. 환경으로 자르면 서버 한 대만 문제인지 전부인지, 특정 지역만인지를 가릅니다.
이분 탐색을 제대로 하려면 두 가지 조건이 필요합니다. 하나는 일관된 판정 기준입니다. 매번 “문제 있음/없음”을 같은 방법으로 판정할 수 있어야 하며, 그래서 재현이 앞 단계인 것입니다. 다른 하나는 한 번에 하나만 바꾸는 것입니다. 두 가지를 동시에 바꾸고 증상이 사라지면 어느 쪽이 원인인지 알 수 없고, 결국 처음부터 다시 해야 합니다. 급할수록 여러 개를 한꺼번에 바꾸고 싶어지지만, 그렇게 해서 나아진 장애는 원인을 모른 채 끝나고 대개 다시 돌아옵니다.
재시작하기 전에 남겨야 하는 것
운영 중 프로세스가 이상해졌을 때 재시작은 가장 빠른 완화 수단입니다. 그리고 동시에 가장 확실한 증거 인멸입니다. 메모리에 있던 모든 것이 사라집니다. 어떤 스레드가 무엇을 기다리고 있었는지, 힙에 무엇이 쌓여 있었는지, 어떤 연결이 열려 있었는지가 전부 없어집니다. 재시작으로 증상이 사라지면 원인을 찾을 기회는 다음 장애까지 미뤄집니다.
그래서 좋은 답은 “재시작합니다”가 아니라 “이것들을 먼저 뜨고 재시작합니다”입니다. 최소한 다음을 남겨야 합니다.
첫째, 스레드 덤프입니다. 멈춘 것처럼 보이는 장애에서는 이것 하나로 원인이 드러나는 경우가 많습니다. 여러 스레드가 같은 잠금이나 같은 외부 호출에서 대기하고 있으면 그 지점이 곧 병목입니다. 몇 초 간격으로 두세 번 뜨면 정지 상태인지 진행 중인지도 구분됩니다.
둘째, 힙 스냅숏입니다. 메모리가 계속 차오르는 장애에서 무엇이 자리를 차지하고 있는지, 그것을 누가 놓아주지 않는지를 나중에라도 분석할 수 있습니다. 다만 스냅숏을 뜨는 동안 프로세스가 멈추므로 이미 장애 중일 때만 정당화됩니다.
셋째, 직전 로그와 지표의 원본입니다. 보관 기간이 짧게 설정된 경우가 많아 며칠 뒤에는 사라집니다. 장애 구간을 따로 저장해 두십시오.
넷째, 자원 상태입니다. 열린 파일과 소켓 수, 커넥션 풀의 활성·대기 수, 디스크 여유, 스레드 수 같은 값들입니다. 재시작하면 전부 초기화되므로 그 전에 한 번 찍어 두는 것으로 충분합니다.
그리고 완화와 원인 규명은 다른 작업이며 동시에 진행할 수 있다는 점을 말할 수 있어야 합니다. 사용자 영향을 먼저 줄이는 것이 옳고, 그것과 원인을 끝까지 파는 것은 충돌하지 않습니다. 증거만 남겨 두면 됩니다.
장애 중에 로그 레벨을 올리거나 디버그 출력을 추가하는 것은 조심해야 합니다. 이미 부하가 높은 상태에서 로그 양을 늘리면 디스크와 로그 수집 경로가 새로운 병목이 되어 장애를 키울 수 있습니다. 추가할 관측은 좁은 범위에, 표본만, 그리고 되돌릴 수 있는 형태로 넣으십시오.
실전 문제: 되묻기부터 시작하는 답
질문: “배포한 지 두 시간이 지났는데 사용자들이 ‘화면이 가끔 안 나온다’고 신고합니다. 서버 오류율은 평소와 같습니다. 어떻게 접근하시겠습니까?”
약한 답
“배포 이후에 생겼으니 배포가 원인일 가능성이 큽니다. 일단 롤백해서 증상이 사라지는지 보겠습니다. 사라지면 배포 내용을 검토해서 문제가 된 커밋을 찾고, 안 사라지면 인프라 쪽을 확인하겠습니다. 로그를 보면서 에러가 있는지도 찾아보겠습니다.”
강한 답
“먼저 신고 내용의 모호한 부분을 정리하겠습니다. ‘화면이 안 나온다’가 무엇을 뜻하는지 확정하지 않으면 엉뚱한 곳을 팔 수 있습니다. 빈 화면인지, 오래 도는 중인지, 오류 메시지가 뜨는지, 일부 영역만 비는지를 확인하겠습니다. 스크린숏 한 장이나 발생 시각과 계정 하나면 충분합니다.
그다음으로 눈에 띄는 단서를 짚겠습니다. 서버 오류율이 평소와 같다는 점입니다. 이 한 줄이 후보를 크게 나눕니다. 서버가 정상 응답을 주고 있다면 실패는 서버 바깥에서 일어나고 있을 가능성이 큽니다. 클라이언트 자바스크립트 오류, 정적 자산 로딩 실패, 캐시나 CDN에 남은 옛 버전, 브라우저별 호환성, 그리고 응답은 200인데 내용이 비어 있는 경우가 후보입니다. 반대로 서버 쪽 후보는 ‘실패하지 않으면서 느린’ 경우로 좁혀집니다.
그래서 저는 지표를 두 종류로 나눠 보겠습니다. 서버 측에서는 오류율만이 아니라 응답 시간 분포를 봅니다. 중앙값은 그대로인데 상위 백분위만 올라갔다면 일부 요청만 막히는 것이고, 신고가 ‘가끔’인 것과 잘 맞습니다. 클라이언트 측에서는 자바스크립트 오류 수집과 정적 자산 요청의 실패율을 봅니다. 서버 로그만 보면 클라이언트에서 나는 실패는 아예 보이지 않습니다.
‘가끔’이라는 단어도 그냥 넘기지 않겠습니다. 무작위로 가끔인지, 어떤 조건에서만인지를 가르는 것이 좁히기의 핵심입니다. 확인할 축은 서버 인스턴스, 사용자 세그먼트, 브라우저와 앱 버전, 지역, 캐시 적중 여부, 그리고 시간대입니다. 예를 들어 특정 인스턴스에서만 발생한다면 배포가 일부에만 반영됐거나 그 인스턴스만 자원이 고갈된 것이고, 특정 브라우저에서만이면 클라이언트 코드 문제입니다. 이 분해가 되면 롤백 여부와 무관하게 원인을 좁힐 수 있습니다.
배포와의 인과에 대해서도 말씀드리겠습니다. 시간상 배포 뒤에 생겼다는 것은 강한 단서지만 증거는 아닙니다. 같은 시간에 배치 작업, 트래픽 변화, 의존 서비스의 변경이 있었을 수 있습니다. 그래서 배포 내용에 이 증상과 연결될 만한 것이 있는지, 예를 들어 자산 경로나 캐시 헤더나 응답 형식이 바뀌었는지를 먼저 훑어보겠습니다.
대응 순서는 이렇게 잡겠습니다. 영향 범위가 넓고 배포가 유력하다면 롤백으로 사용자 영향을 먼저 끊습니다. 다만 롤백 전에 현재 버전의 로그, 실패한 요청의 추적 아이디, 클라이언트 오류 표본을 확보해 두겠습니다. 롤백은 증상과 증거를 함께 지우기 때문입니다. 영향이 좁다면 롤백보다 조건을 더 좁히는 쪽이 낫습니다. 원인을 모른 채 되돌리면 다음 배포에서 같은 문제가 다시 나옵니다.
마지막으로 판정 기준을 미리 정해 두겠습니다. 이 증상은 ‘가끔’이라서 조치 후 잠깐 안 보이는 것으로는 해결됐다고 말할 수 없습니다. 신고 발생률 또는 클라이언트 오류 수를 조치 전후로 같은 길이의 구간에서 비교하겠습니다.”
왜 강한 답이 이기는가
두 답 모두 롤백을 언급했고, 실제로 롤백이 옳은 조치일 수도 있습니다. 차이는 롤백이 답의 몇 번째에 등장하는가입니다.
약한 답은 첫 문장에서 원인을 배포로 확정했습니다. 그러면 그다음 행동이 전부 그 가정 위에 놓입니다. 롤백해서 증상이 사라지면 원인을 찾았다고 믿게 되는데, ‘가끔’ 나는 증상은 잠시 안 나타나는 일이 흔하므로 이 판단은 신뢰할 수 없습니다. 반대로 사라지지 않으면 “인프라 쪽을 보겠다”는 막연한 다음 단계밖에 남지 않습니다. 후보를 나눠 두지 않았기 때문에 되돌아갈 지점이 없습니다. 게다가 “서버 오류율이 평소와 같다”는 문제에 주어진 가장 강한 단서를 전혀 쓰지 않았습니다.
강한 답은 원인 후보를 좁히는 데 필요한 정보부터 모읍니다. 증상의 정의를 확정하고, 주어진 단서로 서버 안과 밖을 가르고, ‘가끔’을 조건으로 바꾸기 위한 분해 축을 나열합니다. 롤백은 원인 규명 수단이 아니라 영향 완화 수단으로 제자리에 놓였고, 그래서 롤백 전에 증거를 확보한다는 문장이 자연스럽게 따라 나왔습니다. 그리고 마지막에 판정 기준을 미리 정했습니다.
정리하면 약한 답은 가설 → 조치 → 관찰 순서이고, 강한 답은 증상 확정 → 후보 분할 → 관측 설계 → 조치 순서입니다. 같은 지식을 가지고도 순서만 바꾸면 답의 신뢰도가 달라지며, 면접관이 조건을 하나 바꿔 되물었을 때 무너지지 않는 쪽은 후자입니다.
자주 나오는 실수
증상과 원인을 같은 위치로 보는 것입니다. 예외가 터진 줄이 버그가 있는 줄이라는 보장은 없습니다. 잘못된 값이 만들어진 지점과 그 값 때문에 터진 지점은 대개 다릅니다.
한 번에 여러 가지를 바꾸는 것입니다. 급할 때 가장 하기 쉬운 실수이고, 결과적으로 무엇이 효과가 있었는지 영원히 모르게 됩니다. 되돌리기 어려운 변경일수록 하나씩 해야 합니다.
평균만 보고 지연을 판단하는 것입니다. 사용자가 겪는 것은 평균이 아니라 자기 요청 하나입니다. 분포를 보지 않으면 소수만 겪는 심각한 지연을 통째로 놓칩니다.
재현되지 않는다고 해결됐다고 보고하는 것입니다. 특히 타이밍이나 부하에 의존하는 문제에서 위험합니다. 왜 더 이상 발생하지 않는지 설명할 수 없다면 아직 끝난 것이 아닙니다.
로그에 남길 것을 미리 설계하지 않는 것입니다. 장애 때 필요한 정보는 대개 그 자리에서 만들 수 없습니다. 요청 식별자를 로그마다 붙여 두었는지, 외부 호출의 소요 시간을 기록하는지, 오류 메시지에 어떤 입력이었는지가 들어 있는지가 장애 시간을 좌우합니다.
사람을 원인으로 지목하는 것입니다. 면접에서 회고를 물으면 누가 실수했는지가 아니라 왜 그 실수가 배포까지 통과했는지를 말해야 합니다. 검토, 테스트, 배포 단계 중 어디에서 걸렸어야 했는지로 답하는 것이 실무에서도 옳은 방향입니다.
이어지는 질문들
운영에서만 재현되는 문제는 어떻게 하시겠습니까?
운영과 개발 환경의 차이를 목록으로 만들어 하나씩 지우는 방식으로 답합니다. 데이터 규모, 동시 요청 수, 설정값, 의존 서비스 버전, 네트워크 지연이 대표적인 축입니다. 운영에서 직접 관측을 늘려야 한다면 표본만 수집하고 되돌릴 수 있게 넣겠다는 조건을 붙입니다.
로그와 지표와 추적은 각각 언제 씁니까?
지표는 “무언가 이상하다”를 빠르게 알려 주고, 추적은 “어느 구간에서 시간이 갔는가”를 보여 주며, 로그는 “그때 정확히 무슨 값이었는가”를 알려 준다고 구분합니다. 셋을 요청 식별자로 연결해 두면 지표에서 시작해 로그까지 한 번에 내려갈 수 있다는 점까지 말하면 좋습니다.
고친 뒤 재발을 어떻게 막습니까?
같은 버그를 재현하는 테스트를 먼저 추가하고 그 테스트가 실패하는 것을 확인한 뒤 수정합니다. 그리고 이 유형이 다시 통과하지 못하도록 검출 지점을 앞당깁니다. 검증 추가, 정적 분석 규칙, 배포 전 점검 중 어디에 넣을지를 함께 말하면 완결됩니다.
메모리가 계속 늘어나는 문제는 어떻게 접근합니까?
먼저 진짜 누수인지 캐시 증가인지 가릅니다. 부하를 멈췄을 때 내려가면 누수가 아닐 가능성이 큽니다. 그다음 힙 스냅숏을 두 시점에 떠서 늘어난 객체 종류와 그것을 붙잡고 있는 참조 경로를 비교합니다.
외부 서비스 장애로 우리 서비스가 같이 죽었습니다
원인은 외부에 있어도 전파를 허용한 것은 이쪽 설계라고 답하는 것이 맞습니다. 타임아웃이 없거나 너무 길었는지, 재시도가 부하를 키웠는지, 실패를 격리하는 구조가 없었는지를 점검 항목으로 제시합니다.
장애 회고에는 무엇을 씁니까?
시간순 경과, 사용자 영향의 범위와 크기, 탐지가 늦은 이유, 원인, 그리고 재발 방지 항목입니다. 재발 방지는 담당자와 기한이 붙은 구체적인 작업이어야 하며, “주의하겠습니다”류는 항목이 될 수 없다는 점을 덧붙이면 좋습니다.
준비 점검표
- 첫 문장에서 원인을 단정하지 않고 증상 정의부터 확인하는가
- 지연인지 오류인지 먼저 가르는가
- 지연을 말할 때 평균이 아니라 분포로 말하는가
- 가설과 함께 그것을 반증할 관측값을 정하는가
- 좁히기에서 한 번에 하나만 바꾸는 원칙을 지키는가
- 이분 탐색을 시간·코드 경로·입력·구성·환경 중 어디에 적용할지 고르는가
- 스택 트레이스를 두 방향으로 읽고 내 코드의 경계를 찾는가
- 재시작 전에 남길 증거 목록을 말할 수 있는가
- 완화와 원인 규명을 분리하되 동시에 진행한다고 말하는가
- 조치의 성공 여부를 어떤 지표로 판정할지 미리 정하는가
- 회고를 사람이 아니라 절차의 문제로 정리하는가
연습 방법
가장 좋은 연습 재료는 자기가 최근에 고친 버그입니다. 하나를 골라 발견부터 수정까지의 과정을 시간순으로 적어 보십시오. 그리고 각 단계마다 “여기서 무엇을 알고 있었고, 어떤 후보가 남아 있었으며, 다음 행동으로 어떤 후보가 제외됐는가”를 적습니다. 제외되지 않은 행동이 여럿 보인다면 그것이 낭비된 시간이고, 다음에 줄일 수 있는 부분입니다. 이 정리를 두세 건만 해 두면 면접에서 사례를 물었을 때 절차 중심으로 답할 재료가 생깁니다.
두 번째 연습은 로그를 거꾸로 읽는 것입니다. 위의 발췌처럼 지표와 로그를 늘어놓고, 결론을 보지 않은 상태에서 어느 후보가 언제 제외되는지를 표시해 보십시오. 세 번째는 관측을 설계하는 연습입니다. 지금 담당하는 기능이 새벽에 조용히 실패한다면 무엇을 보고 알아챌 수 있는지, 알아챌 수 없다면 무엇을 추가해야 하는지를 적어 보는 것입니다. 도구를 활용한 디버깅 흐름은 디버깅 실전 가이드에서, 동시성 관련 장애의 원인 구조는 공유 상태 동시성에서, 비동기 코드의 지연과 취소는 비동기 면접 가이드에서 이어 보실 수 있습니다.