🙊

깨달음 줍줍 ~

문제 상황 및 해결 과정

1. 결제 시스템(Pay-Transaction-Payment)

결합도

상황: 결제/환불 요청 흐름에서 로직간 결합도가 높았음
문제: 유저가 결제를 요청하면 성공/실패 응답 받기까지 시간 오래 걸림
해결: 트랜잭션 생성 완료되자마자 유저에게 결제 상태 변경(pending) 반환
트랜잭션 성공 → Settlement, Notification, Schedule 서비스는 kafka로 통신
 Pay Service가 죽었을 경우?
상황: 유저가 결제 요청 → (Transaction Service → …) 로직 진행 중에 Pay Service 죽음 ⇒ 유저가 결제 성공/실패 어떻게 알 건지
잘못하면 결제 업체가 돈 먹을 수 있음
⇒ Pay Service가 죽었는지 살아있는지 매번 확인? retryCount?

외부 클라이언트

상황: 내부 결제 시스템만 생각해서 단순히 결제하는 내부 클라이언트가 요청하는 것으로 생각
결제 과정 프론트엔드가 존재하는데 결제 클라이언트와 별개로 생각 X
= (클라이언트) 결제 과정 페이지에서 결제 요청 → (서버) 결제 처리라고 생각
(외부 클라이언트) 결제 요청 → (결제 과정 클라이언트) 결제 페이지 → (결제 서버) 결제
문제: (외부 클라이언트 → 결제 과정 클라이언트 → 서버) 로직은 보안 위험
최종적으로 결제 완료 후 외부 클라이언트에게 반환했을 때도 외부 클라이언트가 자신의 서버에 결제 안된 것처럼 속일 위험 있음
해결: (외부 클라이언트) 결제 요청 → (외부 서버) 유저인증, 결제 요청 → (결제 서버) 결제, 리다이렉트 (결제 과정 클라이언트) 결제 성공/실패 응답

실제 결제할 때 한 번 더 확인?

 결제 요청을 하고 카드 어플이나 웹사이트로 결제 확인을 하고 다시 결제 요청 화면으로 돌아왔을 때
유저가 체크 표시 O → “진짜” 결제 / X → 결제 취소
⇒ 결제 요청을 한 것 자체는 트랜잭션에 기록 되어야 함(상태 변경: Pending → Succss/Faild/Canceld)
이 때, 유저의 계좌에서 진짜 실제 돈을 빼서 들고 있을까 아님 시스템상으로만 들고 있을까
이 경우 만약 유저가 체크 표시 화면에서 나가서 다른 상품을 결제하면 어떻게 될까? ⇒ 유저가 체크 표시를 하고 최종 버튼을 눌러야 “진짜” 결제가 될 때 실제 돈이 빠져나가야 함
결제 요청 금액을 어떻게 관리하는가
은행의 역할까지 수행 → 고객 계좌에 holding된 금액을 별도 관리 → 금액 이상만큼 다른데서 못찾아가게 처리
플랫폼의 역할만
진자 결제를 할때 돈을 차감해오는 과정에서 돈 부족하다면 모두 롤백처리
⇒ 최종 결제시에는 무조건 해당 금액이 있는지 체크하고 없다면 롤백
→ 이전: pay service에서 결제 진입 시에만 1차 금액 확인 후 금액이 없다면 결제 X
→ 수정: payment service에서 실제 결제(최종 결제)시에 다시 한 번 해당 금액 확인
상황: 결제 요청하면 바로 알아서 흘러가서 결제되는 형식
문제: 결제할 때 결제 프론트엔드에서 최종적으로 결제 버튼을 눌러야 “진짜” 결제가 됨
즉, 최종 결제 버튼 누르지 않고 나가면 결제 취소
해결: 로직 한 번 더 분리
v1/payment 처음 결제 요청 → 트랜잭션 Pending 상태, 결제 프론트엔드로 리다이렉트
v1/payment/verify 리다이렉트 url에 있던 토큰으로 상태 확인
v1/payment (결제 프론트엔드) 최종 결제 버튼 → 트랜잭션 ID 찾고 실제 결제 로직
payment service에서 최종 결제 전에 한 번 더 페이에 돈이 있는지 확인
현재 로직에서는 pay service에 account, pay, point가 있어서 이렇게 그림이 나올 수 밖에 없음

포인트 및 실결제

상황: 포인트 사용, 결제 API가 각각 존재
PUT /v1/pay/points/use
POST /v1/pay/payment/{sellerId}
클라이언트가 결제 전 포인트 사용 API 호출 → (원래 금액 - 포인트 사용 금액) 결제 API 호출
문제: 클라이언트에서 포인트 사용 API를 호출하지 않고 (원래 금액 - 포인트 사용 금액)을 넣은 결제 API 호출할 수 있음
해결: 포인트 사용은 결제할 때만 사용 → 결제 로직 내부에서 포인트 사용까지 처리

계좌 예외 처리

상황: 계좌 생성, 수정, 삭제 API 존재
문제: 페이는 무조건 한 개 이상의 계좌를 가지고 있어야 함
같은 계좌 번호가 여러 개 등록되는 상황(은행API를 연결하지 않았기 때문)
해결: 페이 생성 시 계좌 생성 로직 재활용하기
커스텀 예외 처리 활용하기: 같은 계좌 번호 사용 X 계좌가 1개일 때 삭제 X

취소 및 환불

 한 서비스 내에 로직이 많아지는 것 vs. 통신이 많아지는 것 → 어떤 게 효율적?
각 서비스가 어디서부터 어디까지 담당할 것인가 명확하게 표현되어야 함
상황: payment service와 refund service 분리
payment service: 결제
refund service: 취소와 환불
해결: http 통신이 적어서 더 효율적이라고 판단 → payment service와 refund service 통합

2. Acitivity History

Admin Service에는 관리자(manger, admin)의 행동 히스토리 작성 (OpenFeign 사용)
상황: Item/Funding Service에서 Admin Service(ActivityHistory) 호출해서 작성
v1/items, v1/fundings
문제: Admin Service가 죽으면 Item 서비스에도 영향 O
해결: Admin Service에서 Item Service 호출
→ Admin Service가 과부화될 위험 있음 But, 죽어도 다른 서비스에 영향 X
→ URI가 v1/admin/으로 시작하게 할 수 있음
AdminItemController: v1/admin/items
AdminFundingController: v1/admin/fundings

3. 관리자

상황: 관리자와 유저의 URI가 같고 안에 서비스 로직에서 권한 확인 후 각각의 결과 반환
문제: 관리자와 유저는 반환 값이 다름
해결: 유저는 Page, 관리자는 List로 반환, URI 분리
단, 유저는 상황마다 다름
펀딩 리스트는 정보량이 많아 페이지네이션 적용하기
아이템이나 기부사 리스트는 리스트로 반환
Before
After

4. 코드 분리

서비스 내부에서 작동하는 코드 → Internal 인터페이스로 분리
ex. public class ItemServiceImpl Implements ItemService, InternalService {...}

5. 아이템 반환 with 채민

상황: MyItem은 UPDATE가 많아 MyItem 중 현재 사용 중인 아이템은 UsingItem 테이블로 분리(일대일 관계)
사용 중 아이템 한 개마다 서버를 호출하는 구조
POST /v1/items/my-items/using-items, PUT DELETE /v1/items/my-items/using-items/{usingItemId}
문제: 서버 요청이 빈번한 상황 → 서버 부하 문제
고민: 새로운 API 만들어서 → action으로 CREATE, UPDATE, DELETE 넣어 보내기
/v1/items/my-items/using-items/action
문제: 서버가 action에 다른 값 들어오는 상황을 처리해야 함
고민: 편집 모드를 넣기
편집 버튼 클릭 → 아이템 수정 → 완료 버튼 클릭 + 리스트로 보내기
문제: 서버 부하는 적어지나 사용자가 바로바로 아이템을 수정할 수 없어 유저 친화적 X
해결: GraphQL 도입
REST보다 네트워크 트래픽 감소
하나의 mutation → 단일 엔드포인트 ⇒ 복잡한 작업(CREATE, UPDATE, DELETE) 처리
subscription 기능 사용 → 실시간으로 UI에 반영

6. 채팅방

상황: 채팅 AI 모델 SpringBoot 서버(WebSocket) 클라이언트
→ 실시간성, 다중 사용자 동시 처리 및 성능이 중요함
문제
1.
동기적 I/O 사용하는 Tomcat으로 설계 → 동시 요청 처리량 제한 ⇒ 성능 저하
2.
비효율적인 비동기 처리
Thread.sleep(500): 메시지 응답 생성에서 불필요한 블로킹 호출
CompletableFuture.runAsync : 별도의 스레드풀 X or 최적화 X → 요청량 ↑ ⇒ 병목 현상
3.
AI 응답 생성 병목
RestTemplate 사용해 동기적으로 처리 → 호출량 ↑ ⇒ 병목 현상
해결
WebFlux + Netty 사용
WebFlux: 비동기 논블로킹 방식
Netty: 적은 스레드로 높은 동시 요청 처리할 수 있음
AI 응답에 gRPC 사용
네트워크 오버헤드 줄이고 요청-응답 시간 단축
HTTP/2
Protocol Buffers → 데이터 직렬화 최적화
JSON/텍스트 기반 프로토콜보다 데이터 크기 작고 전송 속도 빠름
Kafka도 비동기
메시지 브로커 거쳐 메시지 전달 → 지연 발생
이벤트 기반 동작 → 소비자 지속적으로 polling하는 구조 추가로 구현
⇒ 수십만 명 사용자 동시접속하는 대규모 채팅 애플리케이션에서 확장성 위해 사용 O