Commerce Operations
의류 쇼핑몰 운영 개선
부분 환불 하나가 매출과 고객 집계까지 바꾸고 있었다.
출발점과 담당 범위
커머스 운영 · 결제와 배송 연동
맡은 범위
관리 화면, PHP 처리, SQLite 집계와 외부 결제·배송·메시지 API를 함께 수정했습니다. 거래 대조와 정정 전 미리보기, 후행 확인까지 맡았습니다.
시작할 때의 상태
기존 상품·주문 데이터와 가격 모듈이 있는 운영 서비스에 참여했습니다.
개발 범위 · 기술 · 협업 자세히 읽기
시작할 때의 상태
기존 상품·주문 데이터와 가격 모듈이 있는 운영 서비스에 참여했습니다. 최초 쇼핑몰 전체를 신규 구축했다고 소개하지 않고, 확인된 운영 개선 범위를 구분합니다.
작업 개요
운영 중인 쇼핑몰의 주문·환불·배송을 개선했습니다. 눈에 보이는 화면 오류에서 출발해 금액을 계산하는 서버와 같은 값을 읽는 관리 화면까지 따라갔습니다.
개발·개선 범위
- 부분 환불·주문 종결 규칙과 요청 화면
- 매출·주문·고객 구매액 집계
- 송장 발급·저장·출력·배송 웹훅
- 알림톡·문자 공급자 요청 계약
- 기존 가격 계산을 활용한 상품 피드
- 사용한 기술
- PHP · SQLite · JavaScript · Toss Payments · 배송 API · 알림톡 / SMS
기존 자산 · 외부 서비스 · AI 활용
회사 운영 프로젝트입니다. 기존 상품·주문 처리와 가격 모듈을 활용하고, Claude Code·Codex와 함께 진단·구현했습니다. Toss·배송·메시지 공급자의 API와 내부 상태를 연결한 범위입니다.
제품 화면과 구조 요약
문제 해결
PG의 부분취소와 주문의 종료는 다른 판단이다
- 서버 판정
결제 잔액·취소액·공제액으로 주문 종결을 계산
- 외부 취소
PG 요청 결과 확인; 내부 DB와 비원자적
- 내부 기록
환불 누적액과 조건에 맞는 주문 상태를 저장
- 관련 조회
대시보드·목록·구매액이 환불 반영 금액을 사용
문제
일부 금액만 환불했는데 주문 전체가 종료되고 매출에서도 전액이 빠졌습니다.
바꾼 점
클라이언트의 종결 값을 없애고 서버가 결제 잔액·취소액·공제액으로 다시 판단하도록 바꿨습니다.
확인한 것
정정 전 미리보기, 당시 PG와 주문 DB의 거래 대조, 종결 규칙의 경우별 검사를 사용했습니다.
조사·판단·검증 과정 읽기
마주한 문제
일부 금액만 환불했는데 주문 전체가 종료되고 매출에서도 전액이 빠졌습니다. 반대로 배송비를 공제한 전체 반품은 PG에서 부분취소로 보여도 업무상 주문은 끝나야 했습니다.
확인한 과정
화면을 열 때 계산한 종결 값을 환불액 변경 후에도 서버가 신뢰하고 있었습니다. 같은 주문 금액을 읽는 대시보드·주문 목록·고객 구매액·등급 계산도 환불을 반영하는지 이어서 확인했습니다.
선택한 방법
클라이언트의 종결 값을 없애고 서버가 결제 잔액·취소액·공제액으로 다시 판단하도록 바꿨습니다. 누적 환불액을 기록하고 관련 집계는 원 결제액에서 환불액을 빼도록 맞췄습니다.
어떻게 확인했는가
정정 전 미리보기, 당시 PG와 주문 DB의 거래 대조, 종결 규칙의 경우별 검사를 사용했습니다. 새 검사가 이전 로직에서는 실패하는지도 확인한 기록을 남겨 수정 효과를 구분했습니다.
단계별 처리와 경계
서버 판정
결제 잔액·취소액·공제액으로 주문 종결을 계산
외부 취소
PG 요청 결과 확인; 내부 DB와 비원자적
내부 기록
환불 누적액과 조건에 맞는 주문 상태를 저장
관련 조회
대시보드·목록·구매액이 환불 반영 금액을 사용
구조도와 처리 경계 자세히 보기
부분취소 금액과 주문 종결 조건을 분리했다
고객에게 돌려준 금액과 주문 종결을 따로 계산하고, 주문 데이터의 후속 조회까지 구분했습니다.
- 처리·명시된 호출
- 비동기·별도 요청
- 데이터 읽기·참조
- 실패·제한 분기
판정·계산식
B=PG 잔액(없으면 주문 총액), R=요청 취소액, D=max(0, 공제액) full = R ≤ 0 또는 R ≥ B · A = full ? B : R · settle = full 또는 A + D ≥ B
- 환불액과 업무상 종결은 다르다
- B=10만원일 때 R=3만원·D=0이면 3만원 환불 후 주문을 유지합니다. R=9.4만원·D=0.6만원이면 9.4만원만 환불하고 종결합니다. 누적액에는 공제액 D를 더하지 않습니다. (가상 예시)
- 결제 기록에서 후속 집계까지
- 당시 PG·DB 대조와 이전 로직의 실패 재현 기록을 확인했습니다. 후속 집계는 결제완료 주문의 총액에서 누적 환불액을 빼서 읽습니다. 등급은 이후 주문 처리에서 계산합니다.
- 동시 실행 방어의 적용 범위
- 3분 선점은 환불 요청 화면의 행에만 적용됩니다. 다른 주문 화면은 이 선점을 거치지 않습니다. 종결 상태의 조건부 갱신도 PG 호출 뒤라 중복 금전 실행까지 막지는 못합니다.
이 구조의 한계PG 취소와 DB 기록은 비원자적이며, 누적액 기록 실패를 로그만 남기고 계속 진행하는 경계와 별도 대조가 남아 있습니다.
단계별 설명 · 구조도를 글로 읽기
관리자 환불 입력
금액·귀책·환불 사유 · 공제액은 서버 정책 적용
- 요청 처리 선점 (처리·명시된 호출)
요청 처리 선점
요청 행을 처리 중으로 · 3분 내 중복 요청 거절
- 주문 상태 검사 (처리·명시된 호출)
주문 상태 검사
결제완료 주문만 허용 · 이미 종결된 주문은 거절
- PG 잔액 조회 (처리·명시된 호출)
PG 잔액 조회
남은 취소 가능액 B · 조회 실패면 취소 중단
- 서버 금액 계산 (데이터 읽기·참조)
서버 금액 계산
서버 산정액 A·공제 D · 종결 여부를 미리 판정
- 외부 PG 취소 (취소 요청)
외부 PG 취소
전액 또는 지정 금액 취소 · 성공 응답 뒤 DB 기록
- 주문 환불 누적 (데이터 읽기·참조)
주문 환불 누적
누적액에 A 추가 · 공제 배송비는 더하지 않음
- 주문 종결인가 (처리·명시된 호출)
- 금액 화면 조회 (데이터 읽기·참조)
- 후속 등급 계산 (데이터 읽기·참조)
- 기록 실패 로그 (DB 기록 실패)
주문 종결인가
full 또는 A + D ≥ B · 참: 종결 / 거짓: 유지
- 종결 상태 변경 (처리·명시된 호출)
- 부분 환불 유지 (비종결)
종결 상태 변경
취소·환불 상태 조건 갱신 · 성공자만 재고·쿠폰 복구
- 요청 결과 저장 (처리·명시된 호출)
부분 환불 유지
주문은 결제완료 유지 · 부분 재고 복구는 수동
- 요청 결과 저장 (처리·명시된 호출)
요청 결과 저장
요청 행 완료·PG 결과 기록 · 요청 완료와 주문 종결 구분
금액 화면 조회
매출·목록·누적 구매액 · 결제완료 금액에서 환불 차감
후속 등급 계산
이후 주문 때 누적액 조회 · 환불 직후 재계산은 아님
기록 실패 로그
처리는 그대로 계속 · 대조·보상은 별도 과제
구조 탐색과 실제 소스 대조를 바탕으로 편집한 작동도입니다. 런타임 전체의 자동 검증을 뜻하지 않습니다.
문제 해결
송장 API의 성공 응답만으로는 배송 업무가 끝나지 않았다
문제
송장 발급이 실패하거나 발급 후 DB에 남지 않았고, 저장한 출력 링크를 다시 열면 사용할 수 없는 문제가 이어졌습니다.
바꾼 점
요청의 계약 유형과 개별 항목 응답 처리를 교정했습니다.
확인한 것
당시 인계 기록에서 발급·저장·출력 경로의 수정과 배포 확인을 대조했습니다.
조사·판단·검증 과정 읽기
마주한 문제
송장 발급이 실패하거나 발급 후 DB에 남지 않았고, 저장한 출력 링크를 다시 열면 사용할 수 없는 문제가 이어졌습니다.
확인한 과정
실제 배송 계약 유형과 요청 값이 다르고, 응답의 중첩 구조와 항목별 결과를 빠뜨린 부분을 찾았습니다. 출력 링크는 영구 주소가 아니라 한 번 사용하도록 발급되는 값이었습니다.
선택한 방법
요청의 계약 유형과 개별 항목 응답 처리를 교정했습니다. 출력할 때 링크를 발급받도록 시점을 바꾸고, 배송 웹훅의 주문 매칭과 최초 운송장 저장 시 알림을 연결했습니다.
어떻게 확인했는가
당시 인계 기록에서 발급·저장·출력 경로의 수정과 배포 확인을 대조했습니다. 최상위 접수 성공, 항목별 발급 결과, 내부 저장을 별도의 확인 지점으로 나눴습니다.
결과와 현재의 경계
결과, 그리고 지금.
환불의 금전 처리와 주문 종결을 분리하고, 환불액을 반영하지 않던 집계 소비처를 함께 수정했습니다. 송장 발급부터 DB 저장·출력까지 공급자의 실제 계약에 맞춰 연결했습니다.
당시 운영 기록과 소스에서 확인된 개선입니다. PG와 DB는 하나의 트랜잭션이 아니므로 영구 정합성이나 모든 경로의 중복 환불 방지를 보장하지 않습니다.
작성 기준: 2026년 9월 작업 기록·소스 대조. 과거 검증 결과를 현재의 모든 동작에 대한 보증으로 사용하지 않습니다.
역할과 협업 이야기하기