[BE-20] OCO 멱등키 전송과 알림 설정 키 보완

이 티켓은 분할 흡수됐습니다 — 멱등키는 BE-28-브로커-멱등키-불변식|BE-28], 알림 설정은 BE-32-알림-마스킹-설정검증|BE-32] 가 가져갑니다. 이 파일은 이력 보존용이며 착수 대상이 아닙니다.

작업 내용 (설계 의도)

두 건 다 wave 2 리뷰에서 후속으로 미룬 항목이다. 같은 티켓으로 묶는 이유는 둘 다 설정·계약 수준의 작은 보완이고 파일이 겹치지 않기 때문이다.

1. OCO 등록에 clientOrderId 전송

TossConditionalOrderGatewayImpl이 등록 요청에 clientOrderId를 보내지 않는다. 토스 스펙의 ConditionalOrderCreateRequest에는 이 필드가 존재하고, 응답에도 내려온다.

OCO 중복 등록은 동일 수량 매도 leg 2벌을 의미한다 — 한쪽이 체결되면 나머지가 취소되지만, 2벌이 등록돼 있으면 보유 수량의 2배가 매도 대기 상태가 된다. BE-10 LiveTradeExecutor에 재시도 정책이 붙기 전에 멱등키를 확정해야 한다.

키 형식은 TradingFillAT-{proposalId}와 충돌하지 않게 정한다(예: OCO-{positionId}). 형식과 근거를 KDoc에 남긴다.

2. notification.discord.error-webhook-url yml 선언

DiscordAutoTradingAlertGateway@Value("${notification.discord.error-webhook-url:${DISCORD_ERROR_WEBHOOK_URL:}}")로 읽는데, application.ymlnotification.discord 블록에는 webhook-url만 있다. 중첩 기본값 덕에 부팅은 실패하지 않고 no-op 로그로 떨어지지만, 운영자가 yml만 보면 error-alert 채널 미설정을 인지할 수 없다.

wave 2에서는 application.yml이 BE-08 소유라 의도적으로 남긴 항목이다.

롤백: 두 변경 모두 되돌리기가 안전하다 — 멱등키는 미전송 상태로, yml 키는 제거해도 중첩 기본값이 동작한다.

의존

  • BE-08 (OCO 어댑터), BE-09 (알림 어댑터) — 둘 다 main 머지 완료

다이어그램

처리 흐름

sequenceDiagram
    participant Exec as LiveTradeExecutor
    participant Gw as TossConditionalOrderGatewayImpl
    participant Toss as Toss Open API
    Exec->>Gw: placeOcoOrder(spec)
    Gw->>Toss: POST conditional-orders (clientOrderId 포함)
    Toss-->>Gw: 같은 키 재요청 시 중복 등록 없음

클래스 의존

flowchart LR
    Impl[TossConditionalOrderGatewayImpl] --> Spec[OcoOrderSpec]
    Impl --> Dtos[TossConditionalOrderDtos]
    Alert[DiscordAutoTradingAlertGateway] --> Yml[application.yml]

테스트 케이스

  • OCO 등록 요청 본문에 clientOrderId가 포함된다(MockWebServer 본문 검증).
  • 같은 포지션으로 두 번 등록해도 같은 clientOrderId가 전송된다(멱등키 파생 규칙).
  • clientOrderId 형식이 TradingFillAT- 접두와 충돌하지 않는다.
  • application.ymlerror-webhook-url 키가 선언돼 있고 환경변수 미설정 시 빈 문자열로 해석된다.
  • error-webhook-url이 빈 문자열이면 notifyOrderFailure가 no-op + 경고 로그로 떨어지고 예외를 던지지 않는다.