독서 기록 및 공유 서비스
이 프로젝트는 독서 기록 및 공유 서비스를 만드는 것으로, 사용자는 회원 가입 후 독서 기록을 작성하고 팔로우 하고 있는 다른 사용자들의 기록을 조회할 수 있습니다.
특정 사용자에 대한 팔로우, 언팔로우가 가능하며 다른 사용자가 팔로우 하는 경우 이에 대한 알림을 받을 수 있습니다.
많은 독서 애호가들이 독서 경험을 공유하고 다른 사람들의 추천을 받고 싶어하는데, 이를 위한 플랫폼이 부족하다고 생각했습니다.
따라서 독서 커뮤니티를 구축하여 사용자들이 서로의 독서 경험을 나누고 소통할 수 있는 공간을 제공하고자 합니다.
독서 경험 공유를 위한 플랫폼에 필요한 간단한 핵심 기능들의 구현을 목표로 삼고 있습니다.
-
안정적이고 확장 가능한 독서 기록 및 공유 서비스 구현
- 유지보수와 확장이 용이한 코드를 작성
- 객체지향 프로그래밍 원리에 대한 학습
- 단위 테스트를 통해 코드의 안정성을 확보
-
CI/CD를 통한 자동화된 빌드 및 배포 과정을 구축하여 원활한 협업 배경에 대한 이해
시간 관계 상 구현까지는 못했으나, 관련 개념에 대해 공부하고 정리하였습니다.
- Refresh Token 탈취 대응 (RTR 기법)
- 인플루언서 피드 발행 시 pull모델 적용(push + pull 모델 혼합 적용)
- 다양한 형태의 피드 생성 (NoSQL 적용)
- 적절한 캐싱전략
- 멀티 그레들 모듈을 이용한 코드 의존성 관리
- DDD 적용
(+비용 문제로 실제 클라우드 환경에서는 Redis, MySQL은 한 대씩 떠있습니다. 추구하는 방향의 아키텍처라고 생각해주세요.)
- Java 17, Spring Boot 3.2.2, Gradle, Spring Data JPA
- MySQL, Redis
- Firebase Admin SDK
- JUnit5, Mockito
- Github Action, Docker
- Jmeter, Prometheus, Grafana
로그인 기능
-
로그인 성공 후 서버에서 Access Token, Refresh Token 생성, Refresh Token은 Redis에 저장한다.
- 생성된 Access Token은 Header로 응답 받음
- 생성된 Refresh Token은 HttpOnly 옵션 쿠키로 응답 받음
-
이후 클라이언트에서 API 요청 시 서버에서 Access Token을 검증한다.
- Access Token 만료 시 클라이언트에 만료 응답 보냄 (Refresh Token 보낼 것을 요청)
- 클라이언트에서 만료 응답 받은 후 Refresh Token으로 서버에 재발급 요청
- 서버에서 Refresh Token 받은 후 확인하여 검증
- Refresh Token 일치하면 Access Token 재발급, 일치하지 않는다면 에러 발생
sequenceDiagram
participant A as Client
participant B as Server
participant R as Redis
A->>B: 로그인 요청
B->>B: Access Token, Refresh Token 생성
B->>R: Refresh Token 저장
B-->>A: Access Token, Refresh Token 응답 (Header, HttpOnly 옵션 쿠키)
A->>B: API 요청 (Access Token 포함)
B->>B: Access Token 검증
B-->>A: API 응답
갱신 플로우
sequenceDiagram
participant A as Client
participant B as Server
participant R as Redis
B-->>A: Access Token 만료 응답
A->>B: Refresh Token으로 재발급 요청
B->>R: Refresh Token 검증
alt Refresh Token 일치
B->>B: Access Token 재발급
B-->>A: 새로운 Access Token 응답
else Refresh Token 불일치
B-->>A: UnAuthorized 에러 응답
end
알림 기능(다른 사용자가 나를 팔로우 할 때 알림)
- Firebase SDK를 이용하여 알림 토큰을 생성한다. (이 토큰은 해당 기기를 식별하는 고유한 값으로, 알림을 받을 대상을 나타낸다.)
- 클라이언트는 생성된 알림 토큰을 서버로 전달한다.
- 서버는 전달 받은 토큰과 클라이언트의 사용자 Id를 함께 데이터베이스에 저장한다.
(어떤 사용자에게 알림을 보내야 하는지 식별 가능하다.) - 서버에서 특정 이벤트가 발생하면, 알림을 받아야 하는 대상의 토큰을 데이터베이스에서 가져온다.
- 서버는 가져온 알림 토큰과 전송할 알림 내용을 Firebase에 전달한다.
- Firebase는 받은 토큰에 해당하는 클라이언트에 알림을 전송한다.
sequenceDiagram
participant A as Client
participant B as Server
participant F as Firebase
participant DB as Database
A->>F: 알림 토큰 생성 요청
F-->>A: 알림 토큰 생성 완료
A->>B: 알림 토큰 전달
B->>DB: 알림 토큰과 사용자 Id 저장 요청
DB-->>B: 저장 완료
loop 이벤트 발생
B->>DB: 알림을 받아야 하는 대상의 토큰 가져오기 요청
DB-->>B: 알림 대상 토큰 반환
B->>F: 알림 전송 요청(토큰, 알림 내용)
F-->>A: 알림 전송 완료
end
피드 기능
- Fan Out On Write, Push 모델
- 독서 기록 작성 시 팔로워(나를 팔로우 하고 있는 사용자) 목록을 조회한다.
- 조회 된 팔로워가 있을 경우 팔로워의 피드 데이터를 생성한다.
- Redis sorted set을 이용
- key는 follower의 id, value는 피드에 보여져야 하는 독서 기록 정보
- 팔로워가 피드 조회 시에는 Redis에서 본인(팔로워) Id로 검색 후 생성되어 있는 데이터를 조회할 수 있다.
sequenceDiagram
participant A as Client
participant B as Server
participant DB as Database
participant R as Redis
A->>B: 독서 기록 작성 요청
B->>DB: 팔로워 목록 조회 요청
DB-->>B: 팔로워 목록 반환
alt 팔로워가 있는 경우
loop 각 팔로워마다
B->>R: 팔로워의 피드 데이터 생성 요청
R-->>B: 피드 데이터 생성 완료
end
end
도서 검색 기능
- 네이버의 도서 검색 오픈API 이용하여 구현
- 인증정보 발급 받아 API 호출 시 HTTP 헤더에 포함 후 전송해야 한다.
- 클라이언트에서 검색 키워드로 도서를 검색한다.
- 도서 검색 API 호출 실패 시 3번까지 요청 재 시도한다.
- 재 시도 성공 시 검색된 도서 리스트를 응답한다.
- 재 시도 실패 시 검색 실패 에러를 응답한다.
- 서버에서 API 호출 성공 시 검색된 도서 리스트를 응답한다.
sequenceDiagram
participant A as Client
participant B as Server
participant N as Naver API
A->>B: 도서 검색 요청(키워드)
B->>N: 네이버 도서 검색 API 호출 (HTTP Header에 인증정보 포함)
alt 도서 검색 API 성공
N-->>B: 검색된 도서 리스트
B-->>A: 검색된 도서 리스트 응답
else 도서 검색 API 실패
loop 3번 재시도
B->>N: 네이버 도서 검색 API 재시도
alt 성공
N-->>B: 검색된 도서 리스트
B-->>A: 검색된 도서 리스트 응답
else 실패
N-->>B: 에러 응답
end
end
B-->>A: 검색 실패 응답
end
독서 기록 등록/수정/삭제 기능
- 클라이언트에서 독서 기록을 작성한 후 기록 내용과 책 정보를 서버에 전달한다.
- 서버에서 독서 기록을 저장한다.
- 책 정보나 기록 내용이 존재하지 않는 경우 예외가 발생한다.
- 독서 기록을 저장하기 전 책 정보 먼저 Book 테이블에 저장한다.
- 동일한 ISBN 값으로 Book 테이블에 이미 책 정보가 존재하는 경우에는 저장하지 않는다.
- Review 테이블에 독서 기록을 저장한다.
- 독서 기록을 수정/삭제 하려는 경우에는 작성자와 수정자가 같아야 한다.
sequenceDiagram
participant A as Client
participant B as Server
participant DB as Database
A->>B: 독서 기록 작성 및 책 정보 전달
B->>B: 예외 확인 (책 정보 또는 기록 내용이 없는 경우)
alt 예외 발생
B-->>A: 예외 응답
else 정보 확인
B->>DB: 책 정보 저장 요청
alt 이미 존재하는 책 정보
DB-->>B: 책 정보 이미 존재
else 책 정보 저장
DB-->>B: 책 정보 저장 완료
end
B->>DB: 독서 기록 저장 요청
DB-->>B: 독서 기록 저장 완료
B-->>A: 독서 기록 저장 완료 응답
end
A->>B: 독서 기록 수정/삭제 요청 (작성자와 수정자 동일 여부 확인)
B->>DB: 수정/삭제 권한 확인
alt 권한 있음
DB-->>B: 수정/삭제 권한 확인 완료
B-->>A: 수정/삭제 진행 가능 응답
else 권한 없음
DB-->>B: 수정/삭제 권한 없음
B-->>A: 권한 없음 예외 응답
end
팔로우/언팔로우 기능
- 팔로우
- 클라이언트에서 팔로우 할 사용자의 id(이메일)를 서버에 전달한다.
- 서버에서 검증 후 팔로우 정보를 저장한다.
- 자기 자신을 팔로우 하려는 경우 예외가 발생한다.
- 동일한 사용자를 반복해서 팔로우 하려는 경우 예외가 발생한다.
- 존재하지 않는 회원을 팔로우 하려는 경우 예외가 발생한다.
- 언팔로우
- 클라이언트에서 언팔로우 할 사용자의 id(이메일)를 서버에 전달한다.
- 서버에서 검증 후 팔로우 정보를 삭제한다.
- 팔로우 정보가 있는 경우에만 삭제한다.
- 존재하지 않는 회원을 언팔로우 하려는 경우 예외가 발생한다.
sequenceDiagram
participant A as Client
participant B as Server
participant DB as Database
A->>B: 팔로우 요청 (사용자 id)
B->>B: 요청 검증
alt 팔로우 가능
B->>DB: 팔로우 정보 저장 요청
DB-->>B: 팔로우 정보 저장 완료
B-->>A: 팔로우 완료 응답
else 팔로우 불가
B-->>A: 팔로우 실패 응답
end
sequenceDiagram
participant A as Client
participant B as Server
participant DB as Database
A->>B: 언팔로우 요청 (사용자 id)
B->>B: 요청 검증
alt 언팔로우 가능
B->>DB: 팔로우 정보 삭제 요청
DB-->>B: 팔로우 정보 삭제 완료
B-->>A: 언팔로우 완료 응답
else 언팔로우 불가
B-->>A: 언팔로우 실패 응답
end