◆ ESSAY
수신 번호처럼 민감한 값은 암호화해 보관하고 실제 요청을 만들 때만 복호화하도록 했다. 처리 이력에는 번호 대신 수신 대상 식별자를 남긴다. 조회 화면에서 번호가 필요하면 가운데 자리를 가려 보여 준다.
저장소 요청을 준비하는 동안 처리 이력
암호문 → 잠시 복호화된 번호 → 수신 대상 ID
암호화 키와 외부 API 인증값은 배포 환경에서 읽고 코드와 로그에 남기지 않는다. 예외를 기록할 때도 요청 본문이나 민감한 헤더를 그대로 남기지 않도록 경계를 둔다.
일부 메시지 서비스는 이미지를 먼저 등록한 뒤, 응답으로 받은 참조값을 메시지 요청에 넣도록 한다.
이미지 생성
→ 이미지 등록 요청
→ 이미지 참조값 수신
→ 메시지 요청
→ 결과 기록
데이터베이스 트랜잭션을 롤백해도 외부 서비스에 등록된 이미지는 사라지지 않는다. 메시지가 접수된 뒤 처리 이력을 저장하지 못할 수도 있다. 그래서 성공 기록이 없다는 사실만으로 외부 요청도 없었다고 판단하지 않도록 했다.
외부 서비스의 응답 코드는 요청을 접수했는지 알려 준다. 수신 기기의 배달 여부는 별도 상태다. 나는 접수와 배달을 같은 SUCCESS로 다루지 않기로 했다. 그렇게 하면 시스템이 보장하는 범위를 과장한다.
외부 업로드에 필요한 임시 파일은 요청이 끝난 뒤 지운다. 반면 처리 당시 어떤 그림을 만들었는지 확인해야 한다면 별도 보관 정책에 따라 결과 이미지를 남길 수 있다.
업로드용 파일 요청이 끝나면 정리
확인용 이미지 보존 기간에 따라 보관
둘은 목적과 수명이 다르다. 확인용 이미지를 저장한 뒤 이력 기록에 실패하면 고아 파일이 생길 수 있으므로 삭제, 만료 처리, 주기적 정리 중 한 방법을 정해야 한다. 보관 자체도 접근 권한과 보존 기간을 정한 뒤 선택해야 한다.
작업이 끝난 뒤 SUCCESS 또는 FAILED를 기록하는 방식은 결과를 간단히 읽게 해 준다. 하지만 외부 호출 도중 프로세스가 종료되면 아무 상태도 남지 않을 수 있다. 설정이 꺼져 있거나 받을 사람이 없어 작업을 건너뛴 경우도 실패와 구분할 수 없다.
상태를 더 잘 설명하려면 작업 예약, 전송 중, 건너뜀 같은 단계를 별도 기록해야 한다. 어디까지 저장할지는 장애 뒤 운영자가 알아야 할 내용과 기록 비용에 달려 있다.
이벤트별 성공 기록을 먼저 확인하고 외부 요청을 보내도 두 작업이 동시에 시작되면 둘 다 기록이 없다고 판단할 수 있다.
작업 A: 성공 기록 없음 확인
작업 B: 성공 기록 없음 확인
작업 A: 알림 요청 접수
작업 B: 알림 요청 접수
저장소와 외부 서비스가 하나의 트랜잭션에 참여하지 않는 한, 시스템 전체에서 정확히 한 번만 전송한다고 보장하기 어렵다. 이벤트별 잠금, 유일성 제약, 내구성 있는 작업 큐, 외부 API의 멱등 키 중 무엇을 쓸지 정해야 한다.
이런 선택은 알림 기능 하나의 문제가 아니다. 재시도할 때 중복을 허용할지, 접수와 배달 중 무엇을 성공으로 볼지, 실패 뒤 어떤 흔적을 남길지 먼저 정해야 구현의 보장 범위를 말할 수 있다.