Skip to content

[feature] 앱인토스 API 공통 응답 처리 클라이언트 추가 - #77

Open
kyer5 wants to merge 3 commits into
chore/toss-settingfrom
feat/toss-api-client
Open

kyer5 wants to merge 3 commits into
chore/toss-settingfrom
feat/toss-api-client

Conversation

@kyer5

@kyer5 kyer5 commented Oct 4, 2026 •

Copy link
Copy Markdown
Member

📌 개요 (why, what)

  • 앱인토스 API는 모든 응답을 resultType / success / error 공통 형식으로 감싸고, 비즈니스 오류도 HTTP 200 + resultType: FAIL로 응답합니다.
  • 기능마다 응답을 직접 해석하면 실패 처리가 중복·누락되기 쉬워서, 공통 응답을 한 곳에서 해석하는 TossApiClient를 추가했습니다.
  • 선행 PR [chore] 앱인토스 mTLS 인증서 및 미니앱 CORS 설정 #76 위에 쌓인 PR입니다. #76이 머지되면 base를 develop으로 변경하겠습니다.

🛠️ 구현 방법 (how)

toss 패키지 (최상위)

  • toss/client — TossApiResponse, TossApiError: 공통 응답 형식 record
  • toss/client — TossApiClient.post(uri, body, responseType): 성공 시 success 값만 반환하고, 실패 시 BusinessException으로 변환합니다.
상황 결과
연결 실패 (타임아웃, TLS 오류 등) TOSS_001 (503)
resultType이 SUCCESS가 아님 TOSS_002 (502)
요청 한도 초과 (errorCode: 4095) TOSS_003 (429)
4xx/5xx + 공통 형식 본문 본문의 errorCode 기준으로 위와 동일
4xx/5xx + 비정상 본문 (HTML 등) TOSS_002 (502)
SUCCESS인데 success가 null TOSS_002 (502)
  • 토스 원본 errorCode / reason은 로그에만 남기고 클라이언트에는 노출하지 않습니다.
  • toss/exception — TossErrorCode가 BaseError를 구현해 기존 GlobalExceptionHandler 흐름을 그대로 탑니다.

기타

  • TossApiClientConfig, TossApiProperties를 global/config → toss/client로 이동했습니다. ([chore] 앱인토스 mTLS 인증서 및 미니앱 CORS 설정 #76 CodeRabbit 리뷰 반영: global에는 전역 공통 설정만 둔다는 컨벤션)
  • Boot의 RestClient.Builder를 주입받도록 변경했습니다. (공통 Jackson 설정·Observation 적용)
    • Boot 4에서는 RestClient.Builder 자동 구성이 별도 모듈이라 spring-boot-starter-restclient 의존성을 추가했습니다. 없으면 prod 기동 시 빈을 찾지 못합니다.
  • 인증서/개인 키가 커밋되지 않도록 .gitignore에 *.key, *.crt, *.pem을 추가했습니다.

검증

  • MockRestServiceServer 기반 단위 테스트 7개 (위 표의 케이스)
  • 로컬에서 실제 앱인토스 API를 호출해, 실제 응답(4050)이 TossApiResponse로 역직렬화되고 TOSS_002로 변환되는 것을 확인했습니다.

🤔 검토한 대안과 선택 이유 (trade-off)

대안 장점 단점 선택 여부
공통 클라이언트에서 응답 해석 후 예외 변환 실패 처리 일관, 호출부는 값만 사용 호출부에서 토스 원본 에러 접근 불가 ✅
호출부마다 resultType 직접 확인 기능별로 유연 중복·누락 위험 (특히 HTTP 200 실패) ❌
4xx/5xx를 상태 코드로 바로 예외 처리 구현 단순 공통 형식 본문의 errorCode(예: 4095)를 잃음 ❌

💭 리뷰 포인트

파일/영역 검토 내용 리뷰 요청 사항
toss 패키지 위치 여러 도메인(로그인, 사용자 식별키 등)이 공유하는 외부 연동이라 특정 도메인 하위가 아닌 최상위 toss 패키지로 분리했습니다. (client, exception 하위 구조는 기존 도메인과 동일) 패키지 구성이 적절한지 확인 부탁드립니다.
TossErrorCode HTTP 상태 토스 실패를 502/503/429로 매핑했습니다. 매핑이 적절한지 확인 부탁드립니다.

📚 Reference (Optional)

🤖 Generated with Claude Code

@kyer5
kyer5 requested a review from gihhyeon as a code owner October 4, 2026 17:17
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • develop

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: Nexters/gotggam-server/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: d4cd387f-7235-4d05-ba61-79814e4ce60e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kyer5 kyer5 self-assigned this Oct 4, 2026
@kyer5 kyer5 added the 💫 feat 기능 구현 이슈 label Oct 4, 2026
@kyer5 kyer5 changed the title [feat] 앱인토스 API 공통 응답 처리 클라이언트 추가 [feature] 앱인토스 API 공통 응답 처리 클라이언트 추가 Oct 4, 2026

@gihhyeon gihhyeon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다! 리뷰 확인 부탁드릴게여

@@ -0,0 +1,5 @@
package com.nexters.gotggam.toss.client;

public record TossApiError(String errorCode, String reason) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

응답 형식 문서를 봤을 땐 에러 객체에 errorType, data, title도 있고, 요청 한도를 초과하면 HTTP 200에 errorCode: "4095"와 error.data.retryAfterSeconds가 내려간다고 써있던데 일부러 errorCode랑 reason만 받도록 의도하신 건지 궁금합니다!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

분기에 필요한 errorCode와 로깅용 reason만 의도적으로 받았습니다!

요청 한도 초과는 errorCode: 4095로 판단해 TOSS_003(429)으로 변환하고 있습니다. data 안의 retryAfterSeconds도 고민해봤는데, 토스 api 호출은 백엔드 내부에서 처리되는 부분이라 프론트에 전달하는 건 과하다고 생각했고, 로그에 남기기에도 불필요하다고 판단했습니다.

백엔드 내부 재시도에 활용할 여지는 있지만, 아직 토스에 미니앱으로 등록하지 않은 상태라 retryAfterSeconds 규모를 아직 알 수 없어서 지금 시점에서 재시도 전략을 정하기 어려웠습니다 ㅜ 실제로 4095가 발생하면 그때 값을 보고 추가하려고 합니다!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💫 feat 기능 구현 이슈

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants