LabHub
배우기 러닝패스 코스

되돌릴 수 없는 변경 · 고객 변경을 끝까지 인계하기 · 이론

고객 변경을 끝까지 인계하기

LabHub 에서 이어서 보기

한 줄 요약

고객 변경의 조정기는 승인된 입력을 고정하고 실행 응답·DB 관측·보고 저장·별도 보상을 서로 다른 결과로 관리해야 합니다.

왜 이게 필요했나

우주 축제 취소 작업의 마지막 날입니다. 고객은 취소를 승인했고 주문은 실제로 cancelled가 됐습니다. 그런데 담당자의 화면에는 오류가 떴습니다. 보고서를 저장할 디스크에 문제가 생겼기 때문입니다. 화면에 오류가 있으니 취소를 다시 실행해야 할까요? 원래대로 되돌려야 할까요? 무엇이 실패했는지 구분하지 못하면 복구 작업이 새로운 사고가 됩니다.

앞 단원에서는 입력 경계, 조건부 변경, 감사, 보상, 청크 재개, 스키마 공존, 증거 보고를 따로 배웠습니다. 이번에는 그 함수를 다시 길게 작성하는 것이 목표가 아닙니다. 이미 검증한 기능을 작은 라이브러리로 받아 고객 요청 하나를 처리하는 조정기를 만듭니다. 어떤 입력을 누구의 승인으로 받아들이고, 어떤 결과에서 무엇을 더 호출해도 되는지가 새로운 과제입니다.

제공 라이브러리가 SQL을 잘 처리하더라도 조정기가 새 ID로 계속 재시도하거나 보고 오류를 이유로 자동 보상하면 전체 시스템은 틀립니다. 부품의 시험 통과와 부품을 연결한 업무의 정확성은 별개입니다. 종합 과제는 그 사이를 검사합니다.

어떻게 동작하나

1. 미리보기와 승인은 같은 파일이 아니다

미리보기는 특정 고객의 명시한 ID를 읽고 revision과 수량을 보여 줍니다. 이 관측에서 실행 요청의 모양을 만들 수는 있지만 approved는 False로 둡니다. 관측한 값이 있다는 이유만으로 그 값의 변경을 사람이 승인했다고 간주하면 안 됩니다.

학습자가 내용을 확인하고 approved를 실제 True로 바꾼 파일만 실행 요청으로 받습니다. 문자열 true나 정수 1은 승인으로 처리하지 않습니다. 이 플래그는 이번 가상 업무의 명시적 입력 계약이며 전자서명이나 사용자 인증이 아닙니다. 실제 조직에서는 누가 어떤 권한으로 승인했는지 확인할 별도 체계가 필요합니다. JSON 파일을 편집할 수 있다는 사실은 승인자의 신원을 증명하지 않습니다.

승인 이후 주문 값이 바뀌면 이전 승인을 새 값으로 몰래 갱신하지 않습니다. 고객이 qty=4, revision=9를 보고 승인했는데 실행 직전 qty=6, revision=10으로 바꿔 적용하는 것은 같은 대상이라도 다른 요청입니다. 실패를 줄이려고 자동으로 preview를 다시 부르는 코드가 오히려 승인 경계를 무너뜨릴 수 있습니다.

2. 실행 응답은 네 종류로 나눈다

제공 apply가 True를 반환하면 이번 호출이 새 변경을 확정했습니다. False는 동일 ID·동일 승인 내용의 기록이 이미 있어서 재적용하지 않았다는 뜻입니다. 이것만으로 현재 행까지 원하는 값이라고 보장하지 않습니다. 이후 다른 작업자가 바꿀 수 있기 때문입니다.

Conflict는 제공 연산이 승인·현재 조건의 충돌을 거절한 경우입니다. 그 외 일반 예외는 결과 불명으로 분리합니다. 특히 커밋 뒤 응답 전달에 실패하면 예외를 받았더라도 DB 변경은 존재할 수 있습니다. 조정기는 예외를 실패 확정이라고 바꾸거나 무조건 성공이라고 바꾸지 않습니다.

다시 호출할 필요가 생기더라도 같은 승인과 같은 ID를 유지해야 하는 이유가 여기에 있습니다. 이번 조정기는 자동 재시도를 하지 않고 실행 한 번의 결과와 이후 읽기 관측을 반환합니다. 사람이나 상위 시스템이 다음 행동을 선택할 근거를 만들며, 그 선택 자체를 추측하지 않습니다.

3. 관측에서는 ID뿐 아니라 승인 내용을 대조한다

실행 응답을 받은 뒤 observe로 원 승인·감사·현재 행을 확인합니다. 저장된 change_id가 같아도 tenant나 targets가 다르면 다른 승인입니다. 개수만 같은 목록, 같은 ID의 다른 revision이나 수량을 허용하지 않습니다. 다른 승인에 해당하는 보고서를 현재 요청의 결과처럼 발행하는 것도 막습니다.

관측이 없거나 DB 조회가 실패하면 unknown입니다. 같은 승인이라는 대조에 실패하면 mismatch입니다. 조회에 성공했고 같은 승인이라면 앞 단원의 complete·incomplete·hold 판정을 그대로 유지합니다. 실행 응답이 replayed라고 해서 hold를 complete로 바꾸지 않습니다.

실행과 관측이 한 트랜잭션인 것은 아닙니다. 이 구조는 확정한 작업을 나중에 조사하는 흐름입니다. [PostgreSQL 격리 수준](https://www.postgresql.org/docs/16/transaction-iso.html)을 읽을 때 한 트랜잭션의 일관성과 서로 다른 호출 사이의 현재 상태를 구별하세요. 조정기가 실행을 마쳤다는 사실이 후속 쓰기를 멈춰 주지는 않습니다.

4. 보고 실패는 별도 작업의 실패다

실행과 관측이 끝난 뒤 보고 파일을 저장합니다. 결과는 saved 또는 failed로 따로 둡니다. saved이면 반환된 SHA-256도 남기지만 그것이 작성자 인증이라는 뜻은 아닙니다. DB 관측이 complete여도 보고 저장은 failed일 수 있으며, 보고 파일 저장이 성공해도 그 안의 업무 판정은 hold일 수 있습니다.

보고만 다시 만드는 report 동작에는 apply나 undo 호출이 없어야 합니다. 이미 취소된 주문을 또 바꾸거나 다시 pending으로 만들 이유가 없습니다. 보고 오류의 메시지에 복구라는 단어가 들어갔다고 업무 보상 권한이 생기는 것도 아닙니다.

관측과 보고서 수집도 별도 호출입니다. 그 사이 후속 변경이 일어나면 먼저 반환한 observed와 나중에 파일에 담긴 판정이 다를 수 있습니다. observed는 앞선 관측의 결과이고 report=saved는 파일 저장 여부입니다. 둘을 합쳐 “현재 시스템 전체가 정상이며 보고서도 같은 시점이다”라고 주장하지 않습니다. 고객에게 전달하는 최종 내용은 보고 파일의 관측 시각·범위와 함께 읽어야 합니다.

5. 보상은 자동 오류 처리기가 아니다

undo 동작에는 별도 undo_id, 원 change_id, 공백만이 아닌 사유, 명시적 승인이 필요합니다. 원 감사를 검증하고 현재 행이 원 변경 직후의 고객·수량·상태·revision과 맞을 때만 조건부 보상을 실행합니다. 중간 한 행이 바뀌었다면 앞 행의 복원까지 함께 롤백합니다.

보상은 revision을 과거 값으로 내리지 않습니다. pending으로 돌아가더라도 새로운 변경이므로 버전이 증가하고 별도 감사가 남습니다. 원 승인과 원 감사는 삭제하지 않습니다. 오류 흔적을 없애는 것과 오류 영향을 복구하는 것은 다른 작업입니다.

보상에서도 일반 예외는 uncertain입니다. 보상 결과가 불명확한데 다른 undo_id를 만들어 다시 실행하지 않습니다. 이번 조정기는 보상 후 자동 후속 작업을 하지 않습니다. 보상 영수증·현재 상태를 확인하는 절차는 앞 보상 단원의 inspect_undo와 reconcile_undo로 이어집니다. 자동화 범위와 사람이 확인해야 할 경계를 문서에 남기는 것도 인계의 일부입니다.

현장에서 만나는 모습

오류 경계를 너무 넓게 잡았을 때

try 하나 안에 apply, observe, publish를 넣고 except에서 undo를 호출하면 어느 단계가 실패했는지 사라집니다. 쓰기 오류와 읽기 장애와 파일 오류는 필요한 다음 행동이 다릅니다. 이번 실습은 작은 함수로 경계를 나누고 각 단계의 결과를 합성합니다. 단순히 예외를 잡았다는 이유로 안정성이 생기는 것이 아닙니다.

[psycopg 트랜잭션 관리](https://www.psycopg.org/psycopg3/docs/basic/transactions.html)는 트랜잭션 문맥과 연결 수명을 구분합니다. 제공 연산은 각 호출이 소유한 연결을 정리하며, 조정기에서 전체 요청을 외부 트랜잭션으로 다시 감싸지 않습니다. 그렇게 감싸면 하위 함수의 완료가 실제 커밋인지 savepoint 해제인지 달라질 수 있습니다. 실습은 설치된 psycopg 3.2.3에서 실제 효과를 확인합니다.

종료 코드 0을 업무 완료로 오해했을 때

CLI는 요청을 처리하고 구조화된 결과를 출력했으면 종료 코드 0을 반환합니다. JSON 안의 execution=conflict, observed=hold, report=failed도 정상적인 처리 결과일 수 있습니다. 코드 0만 보고 성공 알림을 보내는 상위 자동화는 잘못된 결론을 만들 수 있습니다. 요청 형식·파일 해석 자체가 실패했으면 코드 2와 고정 오류 객체를 반환합니다.

오류 출력에 DSN·비밀번호·원 예외 문자열을 그대로 넣지 않습니다. 학습 자료는 가상 DB지만 실제 운영 습관은 여기서 형성됩니다. 잘못된 UTF-8, 중복 키, 잘린 JSON, 비유한 상수와 크기 제한도 처리합니다. [Python JSON 문서](https://docs.python.org/3.12/library/json.html)의 object_pairs_hook과 parse_constant는 이런 입력을 데이터로 검사하는 도구입니다. eval로 실행해서는 안 됩니다.

고객의 다음 행동을 분명하게 만드는 FDE

[Palantir FDSE 공고](https://jobs.lever.co/palantir/dab396d4-2f14-4796-aac0-0d82883dccf0)는 고객 맞춤 소프트웨어와 이해관계자 협업을 다룹니다. 이 종합 과제는 그 요구를 “고객이 어떤 변경을 승인했고, 지금 무엇을 확인했으며, 다음에 누가 무엇을 결정할지 설명하는 도구”로 해석한 자체 학습 과제입니다. 특정 회사의 내부 절차나 채용 시험을 재현한 것이 아닙니다.

다음 실습에서 할 것

우주 축제의 취소 요청을 JSON으로 받아 미리보기·실행·관측·보고·별도 보상으로 연결합니다. 실제 DB에서 오래된 승인 거절, 커밋 뒤 응답 유실, 보고 파일 실패, 후속 변경과 보상을 확인합니다. 마지막에는 실제 CLI를 호출해 종료 코드와 업무 결과를 따로 읽습니다. 정답뿐 아니라 자동 재승인·자동 보상·거짓 완료를 만드는 오답도 거절합니다.

학습자별 가상 자료와 일회용 DB만 사용합니다. 고객 권한 관리, 승인 서명, 외부 보고 전송, 분산 트랜잭션, 서버 전원 장애 내구성을 완성하는 수업은 아닙니다.