Skip to content

2024-TEAM-05/integrated-feed-backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

소셜 미디어 통합 Feed 서비스

인스타그램 쓰레드 페이스북 트위터 등 다양한 SNS에 게시된 컨텐츠를 한 곳에 모아 보여주는 서비스

목차

✨ 개요

다양한 소셜 미디어 플랫폼을 통해 수많은 정보를 접할 수 있게 되었지만, 여러 SNS 상에서 산발적으로 게시되는 컨텐츠를 일일이 확인하고 관리하는 건 굉장히 번거로운 일입니다. 어떤 주제나 브랜드에 대해 다양한 사용자 입장에서 모니터링하려면 각 플랫폼을 따로 관리해야 하는 불편함이 있습니다. 이런 문제를 해결할 수 있는 소셜 미디어 통합 Feed 애플리케이션입니다.

소셜 미디어 통합 Feed 서비스유저 계정 또는 브랜드의 #해시태그를 기반으로 인스타그램, 쓰레드, 페이스북, 트위터(X) 등 다양한 SNS에 게시된 관련 컨텐츠를 하나의 피드로 통합하여 제공합니다. 이를 통해 관심 있는 주제나 브랜드의 SNS 노출 현황과 통계를 한눈에 쉽게 확인할 수 있습니다.

🧩 Skills

💌 API 명세서

스웨거 주소

http://15.164.10.5:9020/swagger-ui/index.html#/

📄 요구사항 정리 및 기술 명세서

회원가입 API

요약 (Summary)

유저는 계정, 비밀번호, 이메일로 가입요청을 진행합니다.
가입 요청 시, 이메일로 발송된 코드를 입력하여 가입승인을 받고 서비스 이용이 가능합니다.

목표 (Goals)

  • 계정unique 합니다.
  • 이메일 은 올바른 이메일 구조인지 검증되어야 합니다.
  • 비밀번호 는 아래 중 2가지 이상의 제약 조건을 가지며, 암호화되어 저장됩니다.
    • 제약조건
      • 다른 개인 정보와 유사한 비밀번호는 사용할 수 없습니다.
      • 비밀번호는 최소 10자 이상이어야 합니다.
      • 통상적으로 자주 사용되는 비밀번호는 사용할 수 없습니다.
      • 숫자로만 이루어진 비밀번호는 사용할 수 없습니다.
      • 숫자, 문자, 특수문자 중 2가지 이상을 포함해야 합니다.
      • 이전 비밀번호와 동일하게 설정할 수 없습니다.
      • 3회 이상 연속되는 문자 사용이 불가합니다.
  • 위 조건을 만족한 상태에서 가입요청 시, 유저가 생성되고 6자리의 랜덤한 코드가 입력한 이메일로 발송됩니다.
  • 계정 , 비밀번호, 인증코드 가 올바르게 입력되었을 시 가입승인이 되어 서비스 이용이 가능합니다.

목표가 아닌 것 (Non-Goals)

  • 실제 이메일 발송

계획 (Plan)

Step 1. 회원가입

〰️ 플로우 차트
flowchart TD
   A((회원 가입 요청\nwith 계정, 이메일, 비밀번호)) --> B{계정이 이미 존재하는가?}
   B --> |NO| C{이메일이 유효한가?}
   B --> |YES| D((409 실패 응답 반환)) 
   C --> |YES| E{이메일이 이미 존재하는가?}
   C --> |NO| F((400 실패 응답 반환))
   E --> |NO| G{비밀번호가 유효한가?}
   E --> |YES| H((409 실패 응답 반환)) 
   G --> |YES| I[유저 생성 후 DB에 저장]
   G --> |NO| J((400 실패 응답 반환))
   I --> K[유저 생성 이벤트 발송]
   K --> L((202 성공 응답 반환)) 
   K --> M[인증 번호 생성 후 DB에 저장]
   M --> N[이메일 발송]
   N --> O((요청 로그 기록))
Loading
💾 회원가입에 필요한 유저(멤버) ERD
erDiagram
member {
	member_id BIGINT PK "BIGSERIAL"
	account VARCHAR(50) UK "NOT NULL"
	email VARCHAR(320) UK "NOT NULL"
	password VARCHAR(255) "NOT NULL"
	status VARCHAR(15) "NOT NULL DEFAULT UNVERIFIED"
    created_at TIMESTAMP "NOT NULL DEFAULT CURRENT_TIMESTAMP"
	updated_at TIMESTAMP "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
}
Loading

Account 필드
username 길이 제한
✔ 인스타그램: 30자
✔ 쓰레드: 30자 (인스타그램과 같음)
✔ 트위터: 15자
✔ 페이스북: 50자

Email 필드
이메일 주소는 최대 320자입니다. 이 값은 로컬 파트(64자)와 도메인 파트(255자)를 합친 후 “@” 기호를 추가한 것입니다. (by. 챗지피티)

Password 필드
비밀번호는 암호화되어 저장됨으로 고정 길이임. Argon2 알고리즘을 사용하면 일반적으로 90자 ~ 130자 (by. 챗지피티)

Status 필드
UNVERIFIED: 이메일 인증 전 상태
VERIFIED: 이메일 인증 완료 상태

🔐 비밀번호 암호화 알고리즘

1️⃣ BCryptPasswordEncoder
✔ 오래전부터 사용된 알고리즘으로, 비밀번호 해시에 대한 가장 일반적인 선택 중 하나 ✔ bcrypt는 최대 입력 길이가 72바이트로 제한되기 때문에 긴 비밀번호는 잘릴 수 있음

2️⃣ Argon2PasswordEncoder 🌟
✔ 비밀번호 해시에 대한 최신 표준 (2015년 암호 해싱 대회에서 우승)

3️⃣ Pbkdf2PasswordEncoder
✔ FIPS 인증이 필요할 때 좋은 알고리즘 ✔ bcrypt나 Argon2보다는 조금 더 느리고, 설정에 따라 매우 높은 보안성을 제공하지 못할 수도 있음

4️⃣ SCryptPasswordEncoder
✔ bcrypt나 Argon2에 비해 설정이 복잡할 수 있으며, 메모리 사용량이 높아 일부 환경에서 부적합

👩🏻‍⚖️ 결론은 보안 표준이면서, 가장 강력한 보안을 제공하는 Argon2PasswordEncoder를 사용하자!

🧮 이메일 형식 확인을 위한 정규식

Hibernate Validator에서 @Email 애너테이션을 사용할 때 기본적으로 적용되는 정규식

^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$
📋 비밀번호 제약조건 전략

📌 다른 개인 정보와 유사한 비밀번호는 사용할 수 없습니다.

🔍 질문 1. 다른 개인 정보에는 뭐가 있을까? : 보통 사용자 이름, 이메일, 전화번호, 생년월일이 있지만, 현재 서비스에서는 이메일만 존재

🔍 질문 2. 유사한지는 어떻게 판단하지? : 직접 문자열 비교, 부분 문자열 비교, Levenshtein 거리 알고리즘

🔍 질문 3. Levenshtein 거리 알고리즘이 뭐지? : 문자열 간의 편집 거리를 계산하는 알고리즘을 사용하여 비밀번호와 개인 정보의 유사성을 판단할 수 있습니다. 이 알고리즘을 사용하면 문자열이 얼마나 유사한지를 수치적으로 계산할 수 있습니다. 편집 거리가 짧으면 비밀번호가 개인 정보와 유사하다고 판단할 수 있습니다. (by. 챗지피티)

💡 tip) 비교 시 대소문자를 무시하고, 공백이나 특수문자를 제거한 후 비교하는 것이 좋음

📌 통상적으로 자주 사용되는 비밀번호는 사용할 수 없습니다.

✔ 자주 사용되는 비밀번호 목록이 필요! (ex. most common password in Korea)
✔ 목록을 DB에 저장 (변경이 거의 없기 때문에 캐싱 or 파일 시스템 활용 가능)
✔ 비밀번호 검사 시 사용 (Set 자료구조를 사용할 수 있을 듯)

🎰 인증 코드 생성 방식

1️⃣ 랜덤한 6자리 숫자 코드
✔ 숫자만으로 구성된 코드는 사용자가 쉽게 입력할 수 있음
✔ 브루트포스 공격에 취약 (6자리 숫자 코드는 1,000,000(10^6)가지 경우의 수만 있음)

🛠️ 구현방법
java.security.SecureRandom 모듈을 사용하면, 보안적으로 안전한 난수를 생성할 수 있음

❓ 보안적으로 안전한 난수
일반적인 Random 클래스는 시드(seed)를 기반으로 난수를 생성하지만, SecureRandom은 운영체제에서 제공하는 강력한 난수 생성기를 사용하여 보다 예측 불가능한 난수를 생성합니다.


2️⃣ 랜덤한 6자리 코드 (숫자 + 문자) ✔ 경우의 수가 급격히 증가 (보안↑) ✔ 사용자가 입력하기 불편 ✔ ‘1’, ‘I’ 등 사용자들이 혼동할 수 있음

👩🏻‍⚖️ 결론, 사용성을 위해 숫자만 사용하는 1️⃣번 방식으로 하되, 부르트포스 공격을 막기 위해 처리율을 제한할 수 있음

🎰 인증 코드 저장 방식
erDiagram
verification_code {
	verification_code_id BIGINT PK "BIGSERIAL"
	user_id BIGINT FK "NOT NULL"
	code CHAR(6) "NOT NULL"
	expire_at TIMESTAMP "NOT NULL"
}
Loading

1️⃣ DB에 저장
✔ 가장 일반적인 방식

🤔 그럼 user 테이블과 합쳐야 할까? 분리해야 할까?
✔ user 테이블과 합치면, 하나의 트랜잭션으로 모든 작업을 처리할 수 있기 때문에 성능면에서 장점이 있음 👍🏻
✔ 인증 코드는 일시적인 데이터이기 때문에 인증 번호가 유효하지 않게 되거나 만료된 후에도 계속해서 유저 테이블에 남아 있을 수 있기 때문에 데이터 관리가 어려워짐 👎🏻
✔ 인증 코드를 위해 만료시간 등 추가적인 데이터가 필요할 수 있는데, 이러한 정보를 모두 user 테이블에 넣게 되면 user 테이블이 불필요하게 커질 수 있음 👎🏻

👩🏻‍⚖️ 결론은 분리하자!

2️⃣ 캐시(Redis)에 저장
장점
✔ 조회 성능 ↑
✔ TTL 관리가 쉬움

단점
✔ 데이터 휘발 가능성
💡 데이터가 휘발된 경우 유저에게 재인증 요청
💡 데이터 영속화 → 캐시

👩🏻‍⚖️ 결론은 일단 RDB에 저장한 뒤, 성능 개선이 필요하다면 캐시를 활용하자!

📂 클래스 다이어그램
classDiagram
		class VerificationCodeRepository {
        +VerificationCode save(VerificationCode verificationCode)
    }
    
    class VerificationCodeGenerator {
        +String generateCode()
    }

    class MailService {
        +void sendVerificationCode(String sendTo, String code)
        +void send(String sendTo, String subject, String content)
    }
    
    class MemberEventListener {
        +void handleSignedUpEvent(SignedUpEvent event)
    }
    
    class SignedUpEvent {
    }
    
    class MemberEventPublisher {
        +void publishSignedUpEvent(Member member)
    }

    class MemberService {
        +void signup(MemberSignupReq request)
    }
    
    class MemberRepository {
        +boolean exitsByEmail(String email)
        +boolean exitsByAccount(String account)
        +Member save(Member member)
    }
    

    class MemberController {
        +BaseApiResponse<Void> signup(MemberSignupReq request)
    }
		
		MemberEventListener --> MailService
		MemberEventListener --> VerificationCodeRepository
		MemberEventListener --> VerificationCodeGenerator
		MemberEventListener --> SignedUpEvent
		MemberEventPublisher --> SignedUpEvent
    MemberService --> MemberEventPublisher
    MemberService --> MemberRepository
    MemberController --> MemberService
Loading

이외 추가 고려 사항들 (Other Considerations)

🤔 DB에 저장된 인증 코드의 만료 관리
🤔 가입 승인 요청에 대한 처리율 제한 (ex. 10분 동안 10번 넘게 요청 X)
🤔 이메일 발송 실패 시 대응

마일스톤 (Milestones)

~ 8.21(수): 비밀번호 제약 조건 관련 리서치
~ 8.23(금): 회원가입 기능 구현 및 이메일 발송 관련 리서치
~ 8.25(일): 가입 승인 기능 구현
~ 8.26(월): 코드 리팩토링 및 미진한 내용 보충

로그인 API

요약 (Summary)

유저는 가입한 계정, 비밀번호로 로그인합니다. 서비스 로그인 시 메뉴는 통합 Feed 단일 입니다.

목표 (Goals)

  • 계정, 비밀번호 로 로그인 시 JWT 가 발급됩니다.
  • 이후 모든 API 요청 Header 에 JWT 가 항시 포함되며, JWT 유효성을 검증합니다.
  • 서비스 로그인 시, 메뉴는 통합 Feed 단일 입니다.

목표가 아닌 것 (Non-Goals)

  • JWT 발급받은 후 유저를 통합 Feed 페이지로 프론트엔드 리디렉션 처리
  • 계정 잠금, 자동 로그아웃 등 사용자 경험 관련 추가 로직

로그인 api 요구사항 상세

  • 설명
    • 사용자는 계정과 비밀번호를 입력하여 로그인할 수 있으며, 로그인 성공 시 JWT 토큰을 발급받습니다. (Authentication)
    • 이후 모든 API에서 JWT 토큰을 사용하여 인가 요청을 보낼 수 있습니다. (Authorization)
  • 입력 데이터
    • 계정(아이디)
    • 비밀번호
  • 출력 데이터
    • JWT 토큰
  • 처리 과정:
    1. 사용자가 입력한 계정과 비밀번호를 검증합니다.
    2. 검증이 성공하면 JWT 토큰을 생성합니다.
    3. 생성된 JWT 토큰을 사용자에게 반환합니다.
    4. 사용자는 이후의 API 요청 시 이 JWT 토큰을 헤더에 포함하여 서버에 요청을 보냅니다.
    5. 서버는 각 요청에서 JWT 토큰의 유효성을 검증하여 인가 처리합니다.
  • 예외 사항
    • 입력한 계정(아이디)이나 비밀번호가 잘못된 경우, 오류 메시지와 함께 인증 실패 응답을 반환합니다.
    • 만료된 JWT 토큰으로 요청한 경우, 인증 오류 응답을 반환합니다.
    • JWT 토큰이 없는 경우, 인증되지 않은 상태로 처리하여 오류 응답을 반환합니다.

플로우 차트
flowchart TD
    A[유저] -->|로그인 요청| B[로그인 API]
    B -->|검증| C[(사용자 DB)]
    
    C -->|유효한 사용자| D{JWT 생성 및 반환}
    C -->|유효하지 않은 사용자| E[Error Message]

    D --> F[API 요청]

    subgraph "🔒 Authorization"
        F --> G[보호된 API]
        G --> H{JWT 유효성 검증}
        H -->|유효| J[응답]
        H -->|유효하지 않음| K[Unauthorized error]
    end
Loading
유저 ERD
erDiagram
member {
	member_id BIGINT PK "BIGSERIAL"
	account VARCHAR(50) UK "NOT NULL"
	email VARCHAR(320) UK "NOT NULL"
	password VARCHAR(255) "NOT NULL"
	status VARCHAR(15) "NOT NULL DEFAULT UNVERIFIED"
  created_at TIMESTAMP "NOT NULL DEFAULT CURRENT_TIMESTAMP"
	updated_at TIMESTAMP "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
}
Loading

계획 (Plan)

  • DTO, 서비스, 레파지토리 구현
    • 계정(아이디) 및 비밀번호를 사용한 유저 인증 로직 작성
  • JWT 토큰 생성 로직 구현
  • JWT 검증 로직 구현
    • JWT 검증 필터 설정
      • Spring Security 설정
      • JWT 인증 필터 작성 - 로그인, 회원가입 및 인증 제외 모든 요청이 필터를 통과하도록
    • 기본적인 예외 처리 로직 구현 (유효하지 않은 JWT, 만료된 JWT 등)
  • 로그인 컨트롤러 구현
  • 테스트 코드 작성 및 기능 테스트
    • JWT, 로그인 API, Spring Security 설정 유닛 테스트
    • 통합 테스트

이외 추가 고려 사항들 (Other Considerations)

  • JWT 만료 시간, 비밀키 저장 방법, 만료 전 갱신 등
  • 로그인 시도, 실패, 성공, 토큰 만료 등의 이벤트 로깅
  • 잘못된 로그인 정보, 만료된 토큰, 권한 부족 등 에러 처리

마일스톤 (Milestones)

image

게시물 상세 조회 API

요약 (Summary)

게시물 상세 조회 API는 사용자에게 특정 게시물의 상세 정보를 제공하는 기능입니다. 사용자는 이 API를 통해 게시물의 제목, 내용 및 게시물 관련 메타데이터를 조회할 수 있습니다.

목표 (Goals)

  • 사용자가 특정 게시물의 모든 세부 정보를 한 번의 API 호출로 가져올 수 있도록 한다.
  • 사용자가 요청한 게시물이 존재하지 않는 경우, 적절한 에러 메시지를 반환한다.
  • 특정 게시물의 상세 정보 반환에 성공할 경우, 해당 게시물의 view_count를 1 증가시킨다.

계획 (Plan)

플로우 차트
graph TD
    A((게시물 id)) --> B{유효성 검증}

    B -->|실패| C((400 Bad Request 반환))
    
    B -->|성공| D{게시물 존재 확인}
    
    D -->|부재| E((404 Not Found 반환))
    
    D -->|존재| H((200 성공 응답 반환))
Loading
시퀀스 다이어그램
sequenceDiagram
    participant C as Client
    participant Co as Controller
    participant S as Service
    participant R as Repository
    
    C->>Co: Request (GET /api/posts/{id})
    Co->>S: getPostDetail(id)
    S->>R: findDetailById(id)
    R-->>S: Post
    S-->>Co: PostDetailRes
    Co-->>C: Response (JSON)
Loading

유닛 테스트

  • [성공] 게시물 상세 정보가 정상적으로 반환된다.
  • [실패] 게시물이 없을 때 예외를 던진다.
  • [성공] 조회된 게시물의 view_count가 1 증가한다.
  • [실패] 잘못된 형식의 ID가 제공되면 400 Bad Request가 반환된다.

마일스톤 (Milestones)

~ 8.21(수): 요구사항 분석

~ 8.22(목): 테크 스펙 작성

~ 8.23(금): 기능 구현 및 단위 테스트 작성

~ 8.25(일): 테스트 수정 및 리드미 작성

게시물 “좋아요수”, “공유수” 증가 API

요약 (Summary)

해당 서비스에서 좋아요, 공유 버튼을 클릭할 시 각 게시물의 원래 소셜 미디어의 좋아요 , 공유 수를 증가시킬 수 있게 합니다.

목표 (Goals)

  • infra 계층에 각 외부서비스들의 클라이언트를 만듭니다.
  • 본 서비스의 좋아요, 공유 수를 외부 서비스에 반영시킵니다.
  • 각 게시물의 소셜미디어에 맞게 외부서비스에서 좋아요공유를 늘리는 API 엔드포인트를 구현합니다.
  • 실제 API 호출을 시뮬레이션하여 외부 API와의 상호 작용을 추상화함으로써 실제 환경으로 전환할 때 변경 사항의 영향을 줄입니다.
  • event 를 활용하여 타사 API 상호 작용 시뮬레이션을 비동기식으로 처리하여 서비스 내의 '좋아요' 및 '공유'에 대한 업데이트가 기본 애플리케이션 흐름을 차단하지 않고 효율적으로 처리되도록 합니다.

목표가 아닌 것 (Non-Goals)

  • 실제 외부 서비스의 API를 직접 호출하는 기능 개발
  • 처리 중에 실제 외부 서비스의 응답을 동기적으로 기다리는 기능 개발
  • 살제 외부 서비스의 응답을 기반으로 내부 서비스 로직을 처리하는 기능
  • 좋아요 수와 공유 수를 저장했다가 한번에 반영하는 것

계획 (Plan)

플로우 차트
graph TD
   A((게시물 좋아요, 공유 요청)) --> BB{JWT 토큰 확인}
   BB --> |유효| B{postId 존재 여부 확인}
   BB --> |유효하지 않음| GG((401 인증 실패 반환)) 
   B --> |존재| E{비동기로 내부 db 종아요, 공유 수 증가 호출}
   E --> |증가 성공| U[내부 db 좋아요, 공유 수 1 증가]
   E --> |증가 실패| KK((500 서버 오류 반환))
   B --> |존재| D[비동기로 좋아요, 공유 수 증가 이벤트 발송]
   D --> F{외부 API 호출 시뮬레이션}
   F -->  UU((요청 로그 기록))
   B --> |존재하지 않음| PP((400 잘못된 요청 반환))

Loading
클래스 다이어그램
classDiagram
    class InstagramAdapter {
        +InstagramApi instagramApi
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    class TwitterAdapter {
        +TwitterApi twitterApi
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    class FacebookAdapter {
        +FacebookApi facebookApi
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    class ThreadsAdapter {
        +ThreadsApi threadsApi
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    class InstagramApi {
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    class TwitterApi {
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    class FacebookApi {
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    class ThreadsApi {
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    class PostService {
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    class PostController {
        +void increaseLikeCount(Long postId)
        +void increaseShareCount(Long postId)
    }

    PostService --> InstagramAdapter
    PostService --> TwitterAdapter
    PostService --> FacebookAdapter
    PostService --> ThreadsAdapter
    InstagramAdapter --> InstagramApi
    TwitterAdapter --> TwitterApi
    FacebookAdapter --> FacebookApi
    ThreadsAdapter --> ThreadsApi
    PostController --> PostService

Loading
시퀀스 다이어그램
sequenceDiagram
    participant Client
    participant PostController
    participant PostService
    participant InstagramAdapter
    participant TwitterAdapter
    participant FacebookAdapter
    participant ThreadsAdapter
    participant InstagramApi
    participant TwitterApi
    participant FacebookApi
    participant ThreadsApi

    Client ->> PostController: POST /posts/{postId}/like
    PostController ->> PostService: increaseLikeCount(postId)
    
    alt Instagram Post
        PostService ->> InstagramAdapter: increaseLikeCount(postId)
        InstagramAdapter ->> InstagramApi: increaseLikeCount(Long postId)
    end

    alt Twitter Post
        PostService ->> TwitterAdapter: increaseLikeCount(postId)
        TwitterAdapter ->> TwitterApi: increaseLikeCount(Long postId)
    end

    alt Facebook Post
        PostService ->> FacebookAdapter: increaseLikeCount(postId)
        FacebookAdapter ->> FacebookApi: increaseLikeCount(Long postId)
    end

    alt Threads Post
        PostService ->> ThreadsAdapter: increaseLikeCount(postId)
        ThreadsAdapter ->> ThreadsApi: increaseLikeCount(Long postId)
    end

    InstagramApi -->> InstagramAdapter: Acknowledgment
    TwitterApi -->> TwitterAdapter: Acknowledgment
    FacebookApi -->> FacebookAdapter: Acknowledgment
    ThreadsApi -->> ThreadsAdapter: Acknowledgment

    InstagramAdapter -->> PostService: Acknowledgment
    TwitterAdapter -->> PostService: Acknowledgment
    FacebookAdapter -->> PostService: Acknowledgment
    ThreadsAdapter -->> PostService: Acknowledgment

    PostService -->> PostController: Acknowledgment
    PostController -->> Client: Acknowledgment

Loading

마일스톤 (Milestones)

~ 8.22(목): controller 계층 구현

~ 8.23(금): infra 계층의 adapter,client 구현

~ 8.24(토): service 구현, 단위 테스트 작성

~ 8.25(일): Rollout

게시물 목록 API

요약 (Summary)

사용자가 Feed에서 볼 수 있는 게시물 목록을 조회하는 기능을 제공합니다. 다양한 쿼리 파라미터를 지원하여 해시태그, 게시물 유형, 정렬 순서, 검색 키워드 등 사용자가 원하는 방식으로 게시물을 필터링하고 정렬할 수 있게 합니다.

목표 (Goals)

  • 유저가 자신의 account가 해시태그로 포함된 게시물 목록을 확인할 수 있게 한다.
  • 유저가 지정한 한 개 hashtag 가 게시물의 해시태그 중 한 개와정확히 일치하는 게시물 목록을 확인할 수 있게 한다.
  • 유저가 지정한 소셜 미디어 플랫폼의 게시물 목록을 확인할 수 있게 한다.
  • 유저가 오름차순 및 내림차순을 모두 지원하여 좋아요개수, 공유 개수, 조회수, 생성 날짜, 업데이트 날짜와 같은 여러 필드를 기준으로 오름차순, 내림차순으로 정렬된 게시물 목록을 확인할 수 있게 한다.
  • 유저가 게시물의 제목과 내용을 검색하여 포함된 게시물 목록을 확인할 수 있게 한다.
    • 내용은 최대 20자까지만 포함되게 한다.

목표가 아닌 것 (Non-Goals)

  • 여러 개의 hashtag 가 포함된 게시물을 목록을 확인 기능 개발
  • 여러 정렬 기준을 동시에 지원하는 기능 개발
  • 특정 hashtag를 제외한 게시물 목록을 검색하는 기능 개발
  • hashtag를 포함하거나 유사하지만 정확히 일치하지 않는 게시물을 검색하는 기능 개발

계획 (Plan)

페이지 네이션

  • 공통으로 요청을 받을 수 있는 PaginationQuery 생성
  • 공통으로 응답을 받을 수 있는 PaginationMetadata 생성
    • 응답 예시
    {
      "hasNext": true,
      "total": 100
    }

QueryDSl을 사용한 조회

  • 동적 쿼리 생성을 위해 QueryDSL 사용

테스트

  • 단위 테스트 작성
  • 리포지토리 테스트 작성
플로우 차트
graph TD
    A((게시물 목록 조회 요청)) --> BB{jwt 확인}
    BB --> |존재| B{hashtag \n search \n orderBy \n page_count \n page \n \n 존재}
    BB --> |존재하지 않음| GG((401 오류 응답 반환))
    B --> |존재| C[게시물 목록 조회 쿼리에 요청 값 포함]
    B --> |존재하지 않음| G[기본깂이 존재하면 포함, 존재하지 않으면 불포함]
	  C --> dd[게시물 목록 반환]
	  G --> dd[게시물 목록 반환]
	  dd --> df((성공응답 반환))

Loading
시퀀스 다이어그램
sequenceDiagram
    participant 사용자
    participant 클라이언트
    participant 컨트롤러
    participant 서비스
    participant 레포지토리
    participant 데이터베이스

    사용자->>클라이언트: 게시물 조회 요청
    클라이언트->>컨트롤러: 쿼리 파라미터와 함께 HTTP GET /api/posts 요청
    컨트롤러->>서비스: 요청 유효성 검사
    서비스->>레포지토리: 필터, 검색, 정렬, 페이지네이션 적용하여 게시물 조회
    레포지토리->>데이터베이스: 쿼리 실행
    데이터베이스-->>레포지토리: 일치하는 게시물 반환
    레포지토리-->>서비스: 게시물 데이터 반환
    서비스->>컨트롤러: 응답 형식으로 데이터 정리
    컨트롤러-->>클라이언트: 게시물 목록 반환
    클라이언트-->>사용자: 게시물 사용자에게 표시

Loading

마일스톤 (Milestones)

~ 8.24(목): 요구사항 정리 및 문서화, controller, repository 계층 구현, queryDSL 공부

~ 8.25(금): service 계층 구현 , 테스트 코드 추가

~ 8.26일(토): 코드 리팩토링

통계 API

요약 (Summary)

사용자가 지정한 파라미터에 맞는 게시물을 통계를 내어 반환합니다.

사용자는 type, hashtag, value, start(조회 시작일), end(조회 종료일)를 지정할 수 있습니다.

목표 (Goals)

  • 쿼리 파라미터 사용
    • API에서 제공하는 쿼리 파라미터를 통해 통계 데이터를 요청하고, 유효성을 검사하여 올바른 결과를 반환합니다.
  • 유효성 검사
    • startend의 날짜 유효성 확인 및 type에 따른 날짜 간격 제한을(30일, 7일) 설정합니다.
  • 데이터 집계
    • 요청된 기간 내에 게시물의 조회수, 좋아요 수, 공유 수 등을 집계하여 반환하는 기능을 구현합니다.

목표가 아닌 것 (Non-Goals)

  • 통계 기능을 위한 테이블은 생성하지 않습니다.

계획 (Plan)

QueryDSl을 사용한 조회

  • 많은 쿼리 파라미터를 처리할 동적 쿼리 생성을 위해 QueryDSL 사용
  • typedate인 경우와 hour인 경우를 나눠서 메소드 작성
  • Expressions.dateTimeTemplate을 사용하여 날짜 및 시간 반환 타입 변환

API 응답 형식

# type이 date / value는 지정되지 않았으므로 count / hashtag는 springboot / star와 end는 지정되지 않았으므로 오늘로부터 7일 전 ~ 오늘
[
{
"date": "2024-08-19",
"countByValue": 3
},
{
"date": "2024-08-20",
"countByValue": 10
} # ... "2024-08-26"까지 data 반환
]
플로우 차트
graph TD
    A((쿼리 파라미터 요청)) --> B{유효성 검사}
    B --> |start가 end보다 나중| C((400 BAD_REQUEST 반환))
    B --> |type이 date이고 간격 30일 초과| D((400 BAD_REQUEST 반환))
    B --> |type이 hour이고 간격 7일 초과| E((400 BAD_REQUEST 반환))
    B --> |유효| F{post에서 데이터 반환}
    F --> G((데이터 반환))

Loading
클래스 다이어그램
classDiagram
    class PostStatisticsController {
        + List<PostStatisticsListRes> getPostStatistics(String type, String hashtag, String value, LocalDate start, LocalDate end)
    }

    class PostStatisticsService {
        + List<PostStatisticsListRes> getPostStatistics(PostStatisticsListReq request)
    }

    class PostRepository {
        +PostStatistics findByPostId(Long postId)
        +void save(PostStatistics postStatistics)
    }

    class PostStatisticsCustomRepository {
        +List<PostStatisticsListRes> findPostStatisticsByQueryParameter(PostStatisticsListReq request)
        +List<PostStatisticsListRes> findPostStatisticsByQueryParameterWithHour(PostStatisticsListReq request);
    }

    PostStatisticsController --> PostStatisticsService
    PostStatisticsService --> PostRepository
    PostRepository --> PostStatisticsCustomRepository

Loading

마일스톤 (Milestones)

~ 8.22(목): 요구 사항 분석, 테크 스펙 작성

~ 8.23(금): 더미 데이터 생성. api 명세 구상

~ 8.24(토): dto, controller 계층 작성

~ 8.25(일): service, repository계층 기능 개발

~ 8.26(월): Rollout

🏗️ ERD

🕝 프로젝트 진행 및 이슈 관리

  • Github Project 칸반보드 활용
  • 각 기능별 이슈 작성 후 하위 이슈 추가
스크린샷 2024-08-26 18 06 36 스크린샷 2024-08-26 18 06 58

👾 Authors

주다애 강경원 이도은 정의진 조혜온 이예림

jooda00

toughCircle

medoeun

uijin-j

hye-on

yerim123456

About

소셜 미디어 통합 Feed 서비스

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages