ONE QUESTION AT A TIME.

천천히 생각하고, 분명하게.

TODAY’S QUESTION1376 / 1500
설계난이도 하5분 답변 목표 · 보강본

요청 하나에서 여러 하위 API를 호출하는데 하나가 실패하거나 사용자가 연결을 끊었습니다. 꼭 필요한 결과와 선택 결과를 나누고 남은 작업의 수명을 어떻게 관리하나요?

잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?

답변과 점검 포인트 펼치기답변 접기
01

구두 답변

정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.

정책은 먼저 응답 계약으로부터 정합니다. 모든 하위 결과가 있어야 유효한 요청인지, 일부 결과가 없어도 기본 응답을 만들 수 있는지에 따라 한 실패 뒤 나머지를 취소할지 끝까지 기다릴지가 달라집니다. 첫 오류를 반환하는 것 자체는 자식 작업이 끝났다는 뜻이 아니므로, 연결·버퍼·늦은 쓰기가 남지 않게 자식의 취소와 종료를 부모 범위에서 관리해야 합니다.

필수 프로필·권한·재고 조회 중 하나가 실패하면 부모가 deadline을 전파하고 다른 자식에 취소를 알린 뒤, 자식들이 실제로 종료됐는지 기다리고 대표 오류를 반환하겠습니다. 취소 신호를 무시하는 하위 라이브러리라면 호출을 제한 시간 안에 격리하되, 백그라운드에서 계속 실행되는 작업의 소유자와 결과 폐기 규칙을 별도로 관리해야 합니다. 구조화된 동시성은 생성한 작업의 수명이 부모 작업의 범위 밖으로 새지 않게 하는 원칙입니다.

추천 목록처럼 없어도 기본 응답이 유효하면 deadline 안에 도착한 결과만 사용하고 누락을 명시할 수 있습니다. 이때 전체 API는 성공이어도 선택 하위 호출 실패율을 별도 지표로 기록해야 품질 저하를 숨기지 않습니다. 팬아웃 수에는 부모별 동시성 제한과 서비스 전체 예산을 함께 둬 하위 장애가 연결 폭주로 번지지 않게 합니다.

테스트는 첫 오류, 동시 오류, 부모 취소, 응답 직후 완료, 취소 무시 작업을 주입합니다. 반환 후에도 자식이 남아 있지 않은지와 선택 실패가 관측되는지를 확인하겠습니다.

요청의 모양과 작업의 수명을 함께 맞춥니다

하나의 요청을 여러 하위 요청으로 나누는 것을 팬아웃(fan-out), 결과를 다시 모으는 것을 팬인(fan-in)이라고 합니다. 부모가 만든 자식의 완료·오류·취소를 같은 범위에서 관리하는 원칙은 구조화된 동시성(structured concurrency)입니다. 단순히 여러 future를 배열에 담는 것보다 강한 원칙입니다. 부모가 반환한 뒤 누가 소유하는지도 모르는 작업이 살아 있으면 요청의 호출 구조와 실제 실행 수명이 어긋납니다. 스택에서 함수가 끝났다는 사실만으로 외부 작업이 끝나지는 않기 때문입니다.

예를 들어 프로필과 권한은 필수이고 추천은 선택이라고 하겠습니다. 권한 조회 실패 때는 나머지를 성공으로 조합할 수 없으므로 불필요한 자식에 취소를 전달합니다. 반면 추천 실패라면 필수 결과로 응답하되 추천이 생략됐음을 관측 가능하게 남깁니다. 이런 결과 정책을 먼저 정한 뒤 실행 도구를 고르겠습니다. JavaScript Promise.all처럼 하나가 거부되면 결과를 빨리 거부하지만 다른 작업은 취소하지 않는 도구도 있습니다. ‘오류가 빨리 돌아온다’와 ‘형제 작업의 종료가 확인됐다’는 서로 다른 계약입니다.

첫 실패와 최종 정리를 분리합니다

복구 불가능한 첫 오류에서 성공을 더 기다리지 않는 것을 빠른 실패(fail-fast)라고 합니다. 그렇다고 정리를 생략하고 곧바로 소유권을 버리는 뜻은 아닙니다. 취소를 전달한 뒤 자식이 반환하는 단계가 필요하며, 여러 자식이 동시에 실패했다면 사용자에게 보여 줄 대표 오류와 운영 진단에 남길 나머지 오류를 구분하겠습니다. 첫 번째로 도착한 오류가 반드시 근본 원인도 아닙니다. 부모 취소 때문에 생긴 형제의 취소 오류가 원래 DB 실패를 덮지 않도록 분류해야 합니다.

하위 함수가 취소를 지원하지 않는다면 두 목표가 충돌합니다. 부모가 기한 안에 반환하면서 자식도 반드시 그 전에 끝난다고 약속할 수는 없습니다. 프로세스 격리나 API 자체의 timeout으로 실제 실행을 제한할지, 별도 감독자가 남은 작업을 맡을지 정해야 합니다. 후자는 엄격한 부모 범위의 종료가 아니라 명시적인 소유권 이전입니다. 이를 정상적인 정리가 끝난 것으로 표시하지 않고 잔여 작업 수·최대 수명·결과 폐기 규칙을 기록하겠습니다. 강제 종료도 이미 외부에 반영된 변경을 되돌려 주지는 않습니다.

병렬 호출은 지연과 부하를 함께 바꿉니다

필수 호출을 함께 시작하면 순차 실행의 시간 합 대신 가장 늦은 호출이 전체 응답을 좌우하게 됩니다. 전체 시간이 느린 가지에 지배되는 현상을 꼬리 지연 증폭(tail latency amplification) 관점에서 볼 수 있습니다. 요청당 하위 호출을 20개 만들면 동시 부모 100개가 2,000개의 하위 작업을 만들 수 있으므로 부모별 상한만으로 부족합니다. 전체 동시성 예산과 하위 서비스별 제한을 함께 두겠습니다. 느린 호출을 다른 복제본에 한 번 더 보내는 방식도 가능하지만 부하와 중복 부수 효과가 늘므로 멱등적인 읽기 등 제한된 조건에서만 검토합니다.

검증에서는 같은 시점에 두 자식이 실패하는 경우, 부모가 결과 수집 직전에 취소되는 경우, 모든 필수 결과는 끝났지만 선택 작업이 멈추지 않는 경우를 재현하겠습니다. 응답 코드뿐 아니라 반환 뒤 남은 작업·연결·버퍼와 오류 원인이 정확히 기록되는지 확인해야 합니다. 핵심은 병렬 호출 문법을 쓰는 것이 아니라 어떤 결과가 필요하고 누가 언제까지 실행을 책임지는지 설명하는 것입니다.

02

득점 포인트

이해가 드러나는 지점을 확인해 보세요.

  • 결과 계약으로 전부 취소·부분 성공 정책을 정한다.
  • 부모 취소 전파와 자식 종료 확인을 작업 수명과 연결한다.
  • 선택 작업 실패와 팬아웃 자원 한도를 별도 관측한다.
03

감점 포인트

놓치기 쉬운 조건과 오해를 짚어 보세요.

  • 첫 오류를 반환하면 자식 정리도 끝났다고 말한다.
  • 모든 작업을 항상 끝까지 기다리는 것이 안전하다고 말한다.
  • 선택 작업 실패를 완전히 숨긴다.
04

더 파고들 거리

하나 더 질문하며 이해의 범위를 넓혀 보세요.

득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.

원리부터 공부하고 싶다면

답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.

← 전체 질문 보기