- common : 공통모듈로서 예외와, 유틸성클래스들을 모아두었습니다
- profile-search : 프로필 조회와 유저관련 모듈입니다
- payment : 결제 관련 모듈입니다
- payment-external : 결제 외부api 모듈입니다.
구현 방법은 여러가지가 있다고 생각합니다.
Redis의 싱글스레드를 이용하여 조회를 할때마다 redis에 Hash자료구조를 사용하여 key는 profileId를 두고 증가 시키고 스케줄링을 통해서 조회수를 증가시키는 방법이 있습니다. redis의 cache를 통해서 매번 업데이트를 치는것을 보여주기보다 스케줄러가 끝낼때 캐시데이터를 적재시켜서 업데이트를 보여주는 방법입니다. 고가용성을 생각하면 redis가 전부 마비된 경우에는 자바의 ConcurrentHashMap을 통해 저장을하고 이를 스케줄링을 통해 업데이트하는 것이 생각났습니다.
profile조회할 때 마다 조회수를 증가시키는데 증가시키는 로직을 비동기 처리로 따로 처리합니다. 동시성 문제가 발생하기때문에 Lock을 통해 증가시킬거 같습니다.
1,2 번 방법을 택하지 않은 이유는 저는 처음부터 프로필을 조회하면 프로필을 조회한 사람이
누구인지 로그를 저장하려고 해서 추후 데이터로 활용한다면 좋을 것이라고 생각했기떄문에 프로필 조회 테이블을 만들었습니다.
프로필을 조회할때 이벤트를 발생시켜 로그를 저장하도록 설계했습니다.
Spring의 자체적인 이벤트시스템인 ApplicationEventPublisher 사용했습니다.
위에서 프로필을 누가조회했는지
가 데이터로 활용될 수도 있지만
프로필 조회로그 데이터는 과연 꼭 안전하게 저장되어할 정도로 중요한가? 라는 의문을 가졌습니다.
이에 대한 답변으로는 Pull Request #13 에 적었습니다
프로필 목록조회는 페이지네이션을 활용했습니다. 큰 어려움없이 페이지네이션을 만들었는데 common모듈에 페이지네이션 포맷을 만들어서 사용했습니다. 왜냐하면 spring data jpa에서도 제공해주지만 spring data jpa를 사용하지 않는 경우도 존재하기때문입니다.
프로필 상세조회와, 프로필 목록조회에는 캐시를 사용해서 트래픽에 대비했습니다. Redis를 사용하다가 만약에 Redis가 다운된다면? 이를 대비하면 서킷브레이커 패턴을 사용해서 Redis다운되면 직접 DB에 select하게 만들었습니다. 물론 이는 굉장히 위험할 수 도있습니다. 왜냐하면 db에 많은 트래픽이 가다가 db가 다운되면 전체적인 시스템에 영향을 주기때문입니다. 그러나 redis의 고가용성 전략으로 센티널이나 혹은 클러스터를 사용하다가 복구작업을 한다면 오래걸리지 않을 뿐더러 프로덕션 환경에서는 주로 DB도 master-slave를 따르기떄문에 읽기전용 db만 사용한다면 redis복구시간 동안은 안전할 거라고 생각했습니다.
전략패턴을 사용해 pgGateway들어오는것을 PG사에 맞게 처리하게 만들었습니다. 프론트에서 승인요청값과 맞는지 확인하기위해 결제 위젯에서 결제전에 요청을 보내서 저장합니다
먼저 FeignClient로 PG사 외부 api를 호출했습니다. RestTemplate에 비해 중복되는 코드가 줄어들며 간결해서 택했습니다. 결제 승인 성공이 된다면 상태변경을 하고 지금은이벤트로 안했지만 Profile(유저)서비스에 Feign으로 호출해서 포인트 적립을 했습니다. 만약 보완한다면 msa간 통신으로 kafka를 선택해서 통신할 것 같습니다.
결제 승인 실패가 된다면 실패 상태로 변경시키고 만약 추가 구현한다면 배치작업을 통해 지우거나 상태 변경을 통해 관리할 것 같습니다.
요청이 실패한다면 이 또한 대기상태에서 실패상태로 변경하고 위와같이 배치작업을 통해 제거하거나 상태 변경을 통해 관리할 것 같습니다.
포인트 지급도 만약 결제승인은 성공했는데 포인트 지급 실패가 나거나 누락된 경우가 만약 존재한다면 이를 대비하면 배치성으로 포인트 지급 성공했는데 포인트 지급결과(로그)가 없다면 이를 지급하도록 만들거 같습니다.
외부와의 통신은 Feign으로 구현했습니다. 선택한 이유는 위에서 언급한 내용과 같습니다.