- 현재 존재하는 P2P 대출 플랫폼은 중개 플랫폼이 다수입니다.
- 대출 조건을 플랫폼에서 강제하지 않고, 당X마켓과 같이 거래를 활성화시키기 위해 게시글, 채팅 기능을 추가합니다. 사용자끼리 커뮤니티를 구성하여 대출자를 구인합니다.
- 개인이 작성한 계약서를 바탕으로 변호사에게 상담을 신청하여 계약서를 검토합니다.
- 대출 조건을 플랫폼에서 강제하지 않음으로써 구매력이 강해집니다.
위의 조건들을 적용하여 기존의 서비스들과의 차별화가 진행된 대출 플랫폼 서비스를 만드는 것이 목표입니다.
- 레디스를 로컬에서 띄웁니다.
- Dockerfile 에서 active profile 을 local로 설정 후 아래의 과정을 수행합니다.
$ gradlew build
$ docker compose up --build -d
-
- 변호사 등록: 사이트에 변호사 등록을 신청하여 승인을 받으면 변호사로 등록됩니다.
- 등록 신청 시 변호사 소개, 자격 정보, 경력 사항들을 함께 업로드합니다.
- 상담 신청: 변호사 정보 조회 시 변호사의 이름, 소개, 자격 정보, 경력 사항들이 제공되며 채팅방에 변호사 초대 버튼 클릭시 변호사에게 상담 신청 요청을 보냅니다.
- 변호사 수락시 기존 대화에서 변호사와 셋이서(예비 채권자, 예비 채무자, 변호사) 대화가 가능합니다.
- P2P 대출이 이루어질 때, 유저는 채팅방에 계약서를 업로드하여 검토받을 수 있습니다.
- 변호사 리뷰: 계약이 체결되면 사용자는 상담을 신청했던 변호사에게 별점과 코멘트를 남겨 리뷰를 달 수 있습니다.
- 변호사 등록: 사이트에 변호사 등록을 신청하여 승인을 받으면 변호사로 등록됩니다.
-
- 게시글 등록: 사용자는 게시글을 게시 및 조회할 수 있고, 자신의 게시글만 수정, 삭제가 가능합니다.
- 게시글에는 '대출해드립니다', '대출 받습니다' 두 개의 카테고리가 존재합니다.
- 게시글에는 해당 대출에 적용되는 계약 상태를 게시글 상단에 노출시킵니다.
- 모집 중(OPEN) → 대출자를 모집 중인 단계
- 진행 중(IN_PROGRESS) → 대출이 진행중인 단계
- 계약 완료(COMPLETED) → 계약이 체결된 단계
- 계약이 생성되거나 해당 계약이 성사되면 게시글의 상태가 업데이트됩니다.
- 게시글 등록: 사용자는 게시글을 게시 및 조회할 수 있고, 자신의 게시글만 수정, 삭제가 가능합니다.
-
- 계약 생성: 계약은 게시글에 종속된 상태로 채권자, 채무자 간 합의에 의해 생성됩니다.
- 한쪽의 계약 생성 요청 시 상대의 요청 허가 후 생성됩니다.
- 계약 완료, 취소 요청도 마찬가지로 상호 합의하에 진행됩니다.
- 계약이 생성, 완료, 취소됨에 따라 연결된 게시글의 상태가 변경됩니다.
- 계약 생성: 계약은 게시글에 종속된 상태로 채권자, 채무자 간 합의에 의해 생성됩니다.
-
- 채팅방 생성: 사용자는 누구든 원하는 상대와 채팅을 할 수 있습니다.
- 계약서 파일을 채팅방에 업로드하여 공유할 수 있습니다.
- 원하는 변호사를 초대하여 계약서 파일을 검토받을 수 있습니다.
- 채팅방 생성: 사용자는 누구든 원하는 상대와 채팅을 할 수 있습니다.
- Backend
- Java 21
- Spring Boot 3.3.4
- JWT
- JPA
- QueryDSL
- Database
- AWS RDS(PostgreSQL)
- AWS ElastiCache(Redis)
- AWS S3
- Build
- gradle
- CI/CD
- Docker
- AWS - EC2
- Github Actions
- Version
- Git
- Github
DDD
,멀티 모듈 프로젝트
구조를 적용하여 프로젝트의 유연성 및 확장성이 고려된 MSA 구조로 진행하였습니다.
- 하나의 서비스를 각 레이어 별 3개의 모듈로 나누었습니다
- 이러한 구조를 가지면 우선, 도메인 영역(Business Logic)을 특정 라이브러리에서 완전히 분리할 수 있습니다.
- 이렇게 되면 버전 업데이트, 내부 요구사항 등으로 라이브러리 변경이 필요할 때 비즈니스 로직에 영향없이 작업이 가능합니다.
- 또한, 레이어드 아키텍처 원칙에 따라 레이어 역류, 건너뛰기를 방지할 수 있습니다.
- 이 구조에서 API 모듈은 스토리지 모듈을 런타임에만 의존하게 되고 스토리지 모듈은 도메인 모듈의 명세를 따라 구현체의 역할만 수행하게 됩니다.
- 이렇게 되면 API 모듈은 도메인 모듈만 알고 그 구현체가 어떻게 되어있는지 모르게 되어 참조가 불가능해 집니다.
- 이렇게 함으로써, 격리된 환경에서 비즈니스를 확장할 수 있고, Layered Architecture가 익숙하지 않은 팀원이 참여해도 규칙에 따라 쉽게 개발할 수 있어 해당 구조를 채택했습니다.
변호사 평점 조회 Redis 도입
- 기존 변호사의 평점을 조회하기 위해서는 해당 변호사의 리뷰 전체를 조회해 평균을 냈습니다.
- 평점을 위해 매번 DB를 조회하는 것은 대규모 시스템에서 성능 상 큰 문제가 있습니다.
- 때문에 Redis 도입을 고려했습니다.
- Redis는 인메모리 디비로 기존 RDB 보다 빠른 데이터 읽기와 수정이 가능합니다.
- Redis에 변호사의 총 리뷰 개수, 현재 평점을 저장하고 리뷰가 추가 될 때 이를 갱신하는 방식으로 구현했습니다.
- WebSocket
- 클라이언트와 서버 간 양방향 실시간 통신이 가능 합니다.
- 연결 후 상시 유지되어 실시간 데이터 주고받기 적합합니다.
- 실시간성이 중요한 경우에 적합합니다.
- Spring WebFlux
- 비동기/논블로킹 방식으로 높은 성능 제공 합니다.
- Reactive Streams 기반의 고성능/확장성
- REST API, WebSocket, Server-Sent Events(SSE) 등 다양한 비동기 통신을 지원 합니다.
- 적은 스레드로 많은 동시 요청 처리에 유리 합니다.
- WebFlux 선택 이유
- WebSocket, SSE 등 다양한 실시간 데이터 스트리밍 방식을 지원 하기 때문에 선택 했습니다.
- 비동기 처리로 자원 효율적 사용 및 고성능 보장하기 때문에 선택 했습니다.
- 대규모 실시간 채팅 기능에 적합 하다고 판단하여 선택 했습니다.
- 확장성: 향후 알림 기능 구현 등에도 활용 가능 하기 때문에 선택 했습니다.
- WebFlux는 다양한 실시간 통신 방식과 성능, 확장성을 고려한 최적의 선택입니다.
- 실시간 채팅뿐만 아니라 향후 추가될 기능에서도 유연한 대응이 가능합니다.
- 사용자가 증가하면 채팅방 및 메시지도 증가 → 데이터 축적 및 트래픽 문제 예상
- 대규모 처리를 위한 적합한 메시징 시스템 필요
- Apache Kafka
- 장점:
- 높은 처리량과 확장성 제공
- 내구성과 파티셔닝 지원
- 실시간 스트리밍 데이터 처리에 적합
- 단점:
- 복잡한 설치 및 운영
- 메시지 브로커로서 기능 제한
- 적합성: 트래픽이 많은 실시간 채팅 시스템에서 안정적 처리 가능
- 장점:
- RabbitMQ
- 장점:
- 메시지 라우팅 기능 및 유연한 메시지 패턴 지원
- 간단한 설치 및 운영
- 메시지 우선순위 및 딜레이 큐 지원
- 단점:
- 처리량 한계 존재
- 내구성에서 제한적
- 적합성: 복잡한 메시지 라우팅 또는 작업 큐가 필요한 중소규모 트래픽 처리에 적합
- 장점:
- Kafka 선택 이유
- 대규모 트래픽 처리에 더 유리
- 수평적 확장 및 장애 내성 지원
- 실시간 스트리밍 데이터 처리에 적합
- 대규모 트래픽 환경에서의 안정성과 확장성 중시
- 대규모 트래픽과 실시간 처리가 핵심인 서비스에서는 Kafka가 더 적합
- 트래픽이 적거나 유연한 라우팅이 필요한 시스템에서는 RabbitMQ가 적합
- Kafka를 선택하여 대규모 실시간 처리 요구를 충족
멀티 모듈 프로젝트에서의 의존성 주입 설정 문제
- 위는 현재 프로젝트에서 사용된 멀티 모듈 프로젝트의 모듈 구조입니다. 에러 핸들링 과정에서 Service단에서의 여러 분기(없는 Entity 조회, 권한 부족 등)에 따른 예외를 발생시키기 위해 직접 구현한 ErrorType 코드, Exception 클래스가 필요했습니다.
- 그러나 ErrorType, Exception 클래스는 support-api 모듈에 정의되어 있어 Service 로직에서 참조가 불가하였습니다.
- 그래서 고안한 방법들은 아래와 같습니다.
- Custom Exception 관련 코드를 support-domain으로 옮긴다면 api모듈에서도, domain 모듈에서도 사용이 가능하지만, support-api에도 Custom Exception을 필요로 하므로 문제가 발생합니다.
- 그렇다면 Custom Exception 관련 코드를 복사하여 support-domain에도 동시에 두게 된다면, 중복성이 발생하고 spring-boot-starter-web 의존성이 support-domain에 들어가게 되어 순수 자바 코드만 사용하기로 했던 support-domain 모듈의 컨벤션에 어긋납니다.
- 최종적으로 수정된 구조입니다. 공통적으로 사용되는 error 모듈을 새로운 공통 모듈로 생성하여 api 모듈, domain 모듈에 주입하여 사용할 수 있게 함으로써 문제를 해결할 수 있었습니다.
- 문제 상황:
- 실시간 채팅 구현 중 WebSocket 연결 실패 발생
- MSA 구조에서 API 애플리케이션만 실행하고, WebSocket 애플리케이션을 실행하지 않음
- 원인:
- WebSocket 애플리케이션 미실행으로 양방향 통신이 불가능
- 해결 방법:
- API 애플리케이션과 WebSocket 애플리케이션을 동시에 실행하여 문제 해결
- 문제 상황:
- API와 WebSocket 애플리케이션 실행 시, WebFlux와 WebSocket의 의존성 충돌 발생
- WebFlux에서 WebSocket을 사용하면서, 두 기술의 의존성이 충돌
- WebFlux는 비동기/논블로킹 방식, WebSocket은 MVC 형태로 혼용 불가
- 원인:
- WebFlux와 WebSocket의 서로 다른 의존성으로 인해 충돌 발생
- 해결 방법:
- WebSocket 연결 실패는 애플리케이션의 동시 실행으로 해결
- WebFlux와 WebSocket 의존성 충돌은 MVC 자동 구성 제외 설정으로 문제 해결
팀원명 | 포지션 | 담당(개인별 기여점) | 깃허브 링크 |
---|---|---|---|
길태환(팀장) | - GW - 유저 - Auth - 변호사 |
▶ 멀티 모듈 프로젝트 구조 세팅 - 전체적인 모듈 구조 작성 - 팀원 온보딩을 위한 예제 모듈 및 코드 작성 ▶ Cloud 구성 - Eureka, gateway 설정 - gateway 인증 필터 구현 - 인증 서버 구현 - 인증-유저 간 통신 구현 ▶ 변호사 도메인 기능 구현 - 변호사 CRUD 구현 - 변호사 유저 간 통신 구현 ▶ CI/CD 구현 - dockerfile, docker-compose.yml 파일 작성 등 도커 환경 구성 - 깃헙 액션 스크립트 작성 - aws vpc 생성 등 배포 환경 세팅 |
https://github.com/road3144 |
김규준 | - 채팅 | ▶ 채팅 도메인 기능 구현 - 채팅방 CR + HTML 구현 - 채팅방에 변호사 초대 기능 구현 - 채팅방에 접속한 사용자 전제 조회 - 채팅방과 게시글 간 통신 구현 ▶ 웹소켓 연결 및 실시간 채팅 시스템 구현 - HTML에 WebSocket 연결 실시간 채팅 기능 구현 ▶ 채팅 기능 kafka 도입 - Kafka를 통해 채팅 메세지 전송, 대규모 처리 |
https://github.com/mbc2579 |
박상훈 | - 게시글 - 계약 |
▶ 게시글 도메인 기능 구현 - 게시글 CRUD + Search 기능 구현 - 유저 도메인과 연결하여 권한 관리 ▶ 계약 도메인 기능 구현 - 계약 CRUD + Search 기능 구현 - 유저 도메인과 연결하여 권한 관리 - 게시글과 연동하여 status 값 관리 ▶ S3를 통한 계약서 파일 업로드 구현 - AWS S3와의 연동을 통해 계약서 파일의 업로드 구현 ▶ support-error 모듈 작성 - 에러 코드 작성 및 공통모듈화 |
https://github.com/asd0236 |
프리 커밋 설정으로 커밋 전 코드 포맷 검사
$ git config core.hookspath .githooks
- 테스트 설정
// Gradle Build and run with IntelliJ IDEA
Build, Execution, Deployment > Build Tools > Gradle > Run tests using > IntelliJ IDEA
수동으로 코드 포맷 수정
./gradlew format