1주차에 기획문서, erd, api명세서를 확정하고나서 백엔드 내부에서 GitRepo, codeconvention, package구조, pull reqeust md를 공통적으로 합의하고서 개발에 들어가기로 했다.
- API 명세서 : https://www.notion.so/API-12ab4d82e56880e5b249fc4f538a326a
- 기획문서 : https://www.notion.so/125b4d82e568804797c6c13a1a933c9a
- ERD : https://www.erdcloud.com/d/CaMg2mhPBTcPW7js9

나는 우선 커뮤니티 > 참여기능(redis lock) > 결재기능(redis lock) 순서대로 구현하기로 하였다.
커뮤니티 기능
- 크게 어려움이 없었고 작성과 조회시에 Transaction 처리를 어느수준으로 해야하는지 잠깐 고민을 하고 스터디를 하였음
-> 조회시에 반복조회가 없으므로 별도로 무결성을 위해서 Trasnaction처리를 할 필요는 없었고 작성은 첨부파일 저장과 포스트 저장을 하나의 트랜잭션으로 묶기위해서 @transaction으로 처리하였음.
결재기능
- 카카오페이 결제기능에 대해서 공부하여 구현
https://developers.kakaopay.com/docs/getting-started/api-common-guide/api-type

- API를 구현하기 위해서 고민하다 보니깐 ERD 데이터 테이블의 종속구조가 나의 지향점과 맞지 않다는 것을 발견함.
-> (1) 현재 데이터 테이블의 계층구조가 게시판ID - 참가유저ID - 참가유저의 보증금 결제 - 결제이력의 계층구조인데, 결제이력이 결국 게시판에 종속되는 구조로 종속의 방향이 잘못되었다고 생각하였음. 왜냐하면 결제이력이 더 포괄적인 개념으로 향후 확장성을 고려하였을 때
-> (2) 그리고 단순히 게시글 아이디에 종속되는 것이 아니라 엄밀하게 말하면 게시글의 참가에 종속되는게 맞다고 생각하였음. 현재는 정책상 다회차 참가와 참가취소가 불가능하나 논리적 종속구조로는 하나의 유저가 다회 참가하는 경우가 있을 수 있기때문임.
-> (3) 결제이력도 단순히 카카오페이로 두기보다는 일반적인 결제이력을 두고 카카오 페이인 경우에만 관련 상세정보를 조회할 수 있도록 하는게 맞다고 생각하였음. 그래야 PG사와 관련없이 결제이력을 전체적으로 한꺼번에 조회가 가능하다고 생각하였음.
이렇게 논리적 종속구조를 고려해야 향후 확장가능한 설계가 가능하다고 생각하였음.
- ERD 수정을 위해 고려점
유저참가신청 -> Deposit주문생성 -> 결제시도생성 -> 결제성공 or 실패 or 환불
비즈니스 로직을 보면 아래와 같은데 참가가 불가해졌을때, 결제가 불가해졌을때 deposit이력을 남겨두는게 맞을까 라는 고민이 된다. deposit 주문이 존재해야 이에 대한 결제가 존재하는 것인데, 결제가 실패했을 때 deposit주문 이력은 어떻게 되는 건지?
안은
1) 결제시도과 deposit주문을 별개로 관리한다.
2) 결제시도와 deposit주문을 연동하여 관리해서 deposit이 주문실패하더라도 이를 남겨둔다.
두가지 경우의 수 밖에 없어보이는데 어떤게 좋은건지 모르겠네 ?
항상 비즈니스 프로세스는 모두 연동해서 관리하는게 좋다. 그래서 deposit은 주문이라는 데이터로써 결제의 결과와는 무관하게 살려두기로 하였다. 그리고
- 데이터 무결성과 추적성: 모든 거래(성공 및 실패 포함)에 대한 완전한 기록을 유지함으로써 데이터의 일관성과 무결성을 보장할 수 있습니다. 이는 감사, 고객 지원, 문제 해결 시 매우 중요합니다.
- 비즈니스 인사이트: 결제 실패 이력을 통해 결제 과정에서의 문제점을 파악하고, 결제 시스템 개선에 필요한 인사이트를 얻을 수 있습니다. 예를 들어, 특정 결제 수단에서 실패율이 높다면 이를 분석하여 대안을 마련할 수 있습니다.
- 고객 경험 개선: 결제가 실패했을 때도 주문 이력이 남아 있으면 고객에게 재결제 안내나 지원을 제공할 수 있습니다. 이는 고객 만족도 향상에 기여합니다.
- 시스템 일관성: 결제시도와 주문이 연동되어 있으면 데이터 모델이 일관성을 가지며, 이는 시스템 유지보수와 확장성 측면에서 유리합니다.
참여기능
- 참여기능과 redis lock
- 참여기능의 entity 변경설계
- <?> 다형성을 사용한 포괄적인 메소드 생성
- 참여기능과 결제기능의 연동
비즈니스 로직상, 참여를 누르고 결제준비, 결제완료가 되어서야 비로서 참여행위가 완료되는 것으로 정의되었다. 그러므로 코드도 참여신청을 받고서 결제모듈에 결제준비를 클라이언트에 내리고, 결제완료신호를 클라이언트로부터 받은 후에야 참여데이터 생성이 완료되는 방식으로 최초작성을 하였다.
(1) 비동기적 처리방식
- 최초에는 위의 로직을 feign client를 사용해서 동기적으로 적용할 까 생각하였는데, 위의 로직대로면 쓰레드가 block 상태로 최대 1분이상 대기해야하기 때문에 쓰레드 자원의 한계로 인해서 다른 응답을 호출하지 못한다. 때문에 비동기처리를 위해서 webclient + completable future을 사용하려고한다.
- 비동기가 반드시 나은 것은 아니고, client로부터 회신이 늦고 데이터량이 적은 경우에는 비동기적으로 처리하는게 적합하다고 생각한다. 반면 대규모 환경에서 금방 응답이 오는데도 비동기적으로 처리하는 것은 context switching이 발생할 수 있고, 한번에 client로부터 응답이 오는 경우에 서버에 예상치못한 부하가 발생할 수 있는 단점이 있을 수 있다.
(2) 대기시간이 짧도록 로직변경 + 동기적 처리방식
- 위와 같이 스레드의 활용도를 높이기 위해서 비동기적 처리방식을 고려하였으나 대기작업을 저장하기 위한 작업큐가 소모하는 메모리의 누수 관점과 오랜 대기시간동안 생길수 있는 여러가지 오류들로 인한 작업안정성의 관점에서 대기시간을 길게 가져가는 것은 결코 좋은 방식은 아니라는 생각이 들었다. participation을 결제완료가 된 시점에 생성하고 이 작업을 lock을 걸어서 하나의 트랜잭션으로 처리하는 방식보다는 임시적으로 participation data를 생성해서 더이상 참여를 못하게 막는 방식으로하면 대기시간을 길게 가져가지 않아도 되어서 동기적으로 처리해도 무리가 없다.
최초에는 참여신청을 받으면 이걸 lock을 걸고서 pay모듈의 결제신청 - future을 사용해서 결제완료 응답수신 - 참여데이터 생성이라는 굉장히 긴 과정으로 하려고 햇으나, 이렇게 하면 스레드의 점유가 너무 오래지속되고, 비동기처리를 한다고해서 메모리 누수가 심할 것으로 예상되어 각 단계를 별도의 독립적인 단계로 나누고 중간에 검증로직을 추가하는 방식으로 변경함. 그리고 단순한 방식으로 처리하기 때문에 동기화방식을 채택함.
(3) redis Lock구현
- AOP를 사용해서 도입하는데 왜 굳이 AOP를 써야하는지에 대한 의문이 든다. LockService를 받아서 getlock과 unlock을 해도 되지않나
- 장점은 공통의 관심사를 비즈니스코드에서 분리하고 싶고 재사용성때문이다.
- 단점은 구현이 복잡하고 내부로직이 드러나지 않는다는 데에 있겠네
- Redis lock의 장단점이 무엇인지?
- 장점은 동시성 문제를 해결할 수 있고 inmemory이기때문에 lock획득과 반납이 빠르다.
- 단점은 중간에 통신이 종료되거나 장애가 생기면 unlock을 못하면 그 방에는 아에 참가가 안되는 단점이 있겠다.
'개발 프로젝트 > 2024 제로베이스 프로젝트' 카테고리의 다른 글
제로베이스 팀프로젝트 진행록4 - 리팩토링 & 멘토 피드백 (0) | 2024.11.11 |
---|---|
제로베이스 팀프로젝트 진행록3 - 협업하여 개발하기 (0) | 2024.11.04 |
제로베이스 팀프로젝트 진행록1 - 개발기획과 팀프로젝트 (0) | 2024.10.23 |
제로베이스 개인프로젝트 진행록6 - 성능개선 리팩터링/캐쉬 (0) | 2024.10.20 |
제로베이스 개인프로젝트 진행록5 - 성능개선 리팩터링/멀티스레드 (2) | 2024.10.14 |