사용자 요청이 타임아웃됐는데 하위 API 호출과 DB 작업은 계속 실행됩니다. 응답 대기 종료와 작업 취소를 어떻게 구분하고 어디까지 전파하나요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
타임아웃은 결과를 기다리는 쪽이 정해진 시간을 넘겼다는 뜻이고, 취소는 더 이상 작업이 필요 없다는 의사를 실행하는 쪽에 전달하는 것입니다. 대기를 끝냈다고 실제 작업도 끝나는 것은 아닙니다. 그래서 요청 수명과 작업 수명을 따로 추적해야 합니다.
웹 요청이 취소됐는데 DB 쿼리와 하위 RPC가 계속 실행되면 이미 쓸모없어진 작업이 자원을 차지합니다. 저는 요청이 끝나는 데드라인을 하위 호출에 전달하고, 각 단계가 남은 시간 안에서만 기다리도록 하겠습니다. 단계마다 원래 타임아웃을 새로 주면 전체 요청 시간이 계속 늘 수 있습니다. 취소를 받으면 새 작업을 만들지 않고, 대기 중인 채널이나 I/O에서 빠져나와 보유 자원을 정리해야 합니다.
다만 취소할 수 없는 순간도 있습니다. DB가 이미 커밋했거나 외부 결제가 완료됐다면 취소 신호가 그 효과를 되돌리지는 않습니다. 저는 실행 전 취소, 실행 중 취소, 커밋 뒤 취소를 상태로 나누고, 마지막 경우에는 결과 확인이나 보상이 필요한지 결제 취소·환불 같은 기능의 규칙으로 정하겠습니다. 반드시 완료해야 할 작업은 요청 고루틴에 매달지 않고 내구성 있는 작업 큐로 넘기는 편이 낫습니다. 검증은 호출자에게 오류가 돌아왔는지만이 아니라 하위 작업과 연결이 실제로 정리되는지까지 확인하겠습니다.
남은 시간을 전달하는 계약이 필요합니다
호출마다 1초씩 기다리는 것과 요청 전체를 1초 안에 끝내는 것은 다릅니다. 인증에 0.2초, 앞선 조회에 0.5초를 썼다면 다음 호출에는 전체 예산 중 0.3초만 남습니다. 완료해야 할 최종 시점을 데드라인(deadline)이라고 하고, 하위 단계가 그 예산을 함께 쓰게 하는 방식을 데드라인 전파(deadline propagation)라고 부릅니다. 재시도와 큐 대기도 같은 예산에 포함해야 합니다. 하위 호출 시간은 보통 자신의 한도와 부모에게 남은 시간 중 더 짧은 쪽을 택하고, 응답 직렬화나 정리 작업에도 시간을 남기겠습니다.
한 프로세스 안의 경과 시간은 시각 보정에 뒤로 가지 않는 단조 시계로 재겠습니다. 서로 다른 서버에 로컬 단조 시계의 절대 값을 그대로 보내면 의미가 통하지 않습니다. RPC가 제공하는 상대 timeout이나 데드라인 프로토콜을 사용하고, 전송·대기 시간과 서버 시계 오차를 어떻게 다루는지 확인해야 합니다. 외부 사용자가 임의로 긴 데드라인을 넣어도 내부 자원이 오래 붙잡히지 않도록 서버 자체 상한을 적용하겠습니다. 시간 예산은 성능 설정이면서 자원 보호의 계약입니다.
취소 요청과 종료 확인은 다릅니다
실행 중인 코드를 강제로 없애는 대신 작업이 취소 신호를 확인하고 안전한 지점에서 빠져나오게 하는 방식을 협력적 취소(cooperative cancellation)라고 합니다. 대기 API가 취소를 지원하면 대기에서 깨어나고, 긴 계산이라면 일정한 간격으로 신호를 확인해야 합니다. 취소 함수가 반환했다는 것은 신호를 보냈다는 뜻일 수 있을 뿐 실제 DB 실행이나 OS I/O가 끝났다는 뜻은 아닙니다. 취소 요청 뒤 정상 완료가 도착하는 경합도 가능하므로, 완료·취소 중 최종 상태를 한 번만 확정하고 자원을 중복 반환하지 않는 소유 규칙이 필요합니다.
예를 들어 HTTP 응답 대기는 끝났지만 송신 버퍼를 OS가 아직 읽고 있다면, 그 버퍼를 즉시 풀에 반환해서는 안 됩니다. DB 쿼리에 취소를 요청했어도 드라이버가 프로토콜 상태를 정리하기 전 연결을 다른 요청에 넘기면 이전 결과가 섞일 수 있습니다. 작업 종료 확인과 드라이버의 연결 재사용 계약을 따른 뒤 반환하고, 상태를 신뢰할 수 없으면 연결을 폐기해야 합니다. 취소를 받는 경로에서도 락·연결·세마포어 허가가 빠짐없이 반환되는지 확인하겠습니다.
요청 하나에서 여러 하위 작업을 만들었다면 부모가 끝나기 전에 자식의 취소와 종료를 함께 관리해야 합니다. 이런 수명 관계를 구조화된 동시성(structured concurrency)이라고 부릅니다. 반면 결제 후 영수증 발행처럼 사용자의 연결과 무관하게 완료해야 하는 작업은 무작정 백그라운드 스레드로 떼어 놓지 않겠습니다. 작업 의도와 상태를 내구 저장하고 독립된 워커가 소유하게 해야 프로세스 재시작 후에도 이어 갈 수 있습니다. 연결이 끊긴다는 사실과 작업을 없애도 된다는 판단을 분리하는 것입니다.
검증은 취소 신호 수가 아니라 취소 이후 실제 종료까지 걸린 시간, 남은 작업 수, 반환되지 않은 연결과 버퍼를 보겠습니다. 제출 전 취소, 실행 중 취소, 커밋 직후 취소를 각각 재현해야 합니다. 특히 이미 성공한 결제를 취소 응답 하나로 실패 처리하면 재시도가 중복 결제를 만들 수 있으므로, 최종 결과가 불확실한 상태는 별도로 저장하고 조회·대사·보상으로 해결해야 합니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- 대기 종료와 실행 종료를 구분한다. 취소 전달만으로 끝났다고 보지 않는다.
- 전체 시간 예산을 전파한다. 단계마다 시간을 다시 시작하지 않는다.
- 커밋 이후의 의미를 정한다. 취소와 롤백·보상은 다름을 설명한다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- 타임아웃이면 하위 작업도 멈춘다고 말한다. 호출 대상이 취소에 반응해야 한다.
- 모든 취소를 실패로 되돌리면 된다고 말한다. 이미 반영된 외부 효과가 있을 수 있다.
- 취소 함수 반환만 확인한다. 실제 종료와 자원 반환을 검증해야 한다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
- 요청 연결이 끝나도 반드시 완료할 작업을 접수합니다. 내구성·소유권·결과 조회를 어떻게 분리하나요?
- 취소와 정상 완료가 동시에 도착합니다. 사용자에게 결과를 한 번만 전달하면서 실제 늦은 효과는 어떻게 기록하나요?
- 하위 서비스가 데드라인을 지원하지 않으면 자원 고갈을 어떻게 제한할까요?
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.