jayeon@portfolio — jsh — 92×34
jayeon@portfolio~/projects%cat projects/yashik.md
제보→관리자 승인→상호작용 — 상태머신 기반 생명주기
문제: 사용자 제보 데이터의 신뢰성을 위해 관리자 승인 게이트가 필요하고, 리뷰·좋아요·댓글의 생성·수정·삭제를 일관되게 다뤄야 함.
설계: 제보(맛집·메뉴)를 상태머신(대기→승인/반려)으로 관리해 승인분만 노출. 리뷰·좋아요·댓글은 생성·수정·삭제 생명주기로 운영하고, 익명 커뮤니티 글은 서버 생성 익명 닉네임 + 소프트 삭제로 처리.
효과: 미검증 제보의 노출을 차단하고, 상태 기반으로 운영·모더레이션을 일관되게 유지.
RefreshToken 보안 강화 — SHA-256 해시 저장 + 회전 + 60초 Grace Period
문제: 모바일 환경의 네트워크 불안정으로 RefreshToken 중복 갱신 요청 발생. 토큰 탈취 시 재사용 탐지 필요.
설계: RefreshToken을 SHA-256 해시로 저장. 갱신 시마다 토큰 회전. 60초 Grace Period 내 재요청에는 멱등 응답, 초과 시 전체 토큰 revoke로 재사용 탐지.
효과: 중복 갱신 요청 멱등 처리로 모바일 불안정 환경 대응. 탈취된 토큰 재사용 시 자동 탐지·전체 revoke로 유출 방어.
트랜잭션 커밋 후 비동기 알림 + 무료 인프라 스팸 방어
문제: 알림 발송이 트랜잭션 지연을 유발. Redis 무료 플랜 부재로 캐시·Rate Limit 구현 제약. 비로그인 스팸 요청 방어 필요.
설계: @TransactionalEventListener(AFTER_COMMIT)+@Async로 알림을 트랜잭션 커밋 후 비동기 처리. 알림 인프라 장애 시 NoOp 폴백. Caffeine(TTL 24h)으로 인메모리 캐시. @GuestAccess 커스텀 어노테이션으로 비로그인 요청 속도 제한.
효과: 알림 발송 실패가 핵심 트랜잭션에 영향 없음. Redis 없이 무료 인프라만으로 캐시·스팸 방어 구현.
Vendor 평점 동기 집계 캐시 — 조회 성능 + 정합성
문제: 맛집 목록·상세 조회마다 리뷰를 집계하면 성능 부담, 캐시 컬럼을 잘못 다루면 음수·불일치 위험.
설계: 리뷰 작성·수정·삭제 시점에 맛집의 평균 평점·리뷰 수를 증분 재계산. 마지막 리뷰 삭제 시 0으로 리셋, 좋아요 감소는 0 하한 가드로 음수 방지.
효과: 목록 조회 시 별도 집계 쿼리 없이 캐시값을 바로 노출하면서 평점 정합성을 유지.
```mermaid
sequenceDiagram
  participant 클라이언트
  participant AuthService
  participant TokenDB

  클라이언트->>AuthService: RefreshToken 갱신 요청
  AuthService->>TokenDB: SHA-256 해시 조회
  alt Grace Period 내 재요청 (60초)
    AuthService-->>클라이언트: 기존 토큰 재사용 (멱등 응답)
  else 정상 갱신
    AuthService->>TokenDB: 신규 토큰 저장 (회전)
    AuthService-->>클라이언트: 새 AccessToken + RefreshToken
  else 만료된 이전 토큰 재사용 탐지
    AuthService->>TokenDB: 전체 토큰 revoke
    AuthService-->>클라이언트: 401 재로그인 요구
  end
```
RefreshToken 회전 + Grace Period — 멱등성·유출 방어
```mermaid
graph TD
  A[리뷰 등록 트랜잭션] -->|AFTER_COMMIT 이벤트| B[알림 이벤트 발행]
  B -->|"@Async"| C[비동기 알림 처리]
  C -->|성공| D[FCM 발송]
  C -->|인프라 장애| E[NoOp 폴백]
  F[비로그인 요청] -->|"@GuestAccess 어노테이션"| G[Caffeine TTL 24h Rate Limit]
  G -->|허용| H[처리]
  G -->|초과| I[429 Too Many Requests]
```
@TransactionalEventListener + Caffeine — 무료 인프라 비동기 알림
# 관련 링크
2026.04 — 2026.05 · (개인)

야식 (YASHIK)

KBO 10개 구단 야구장 음식점 메뉴·가격 + 팬 리뷰·별점·익명 커뮤니티 플랫폼의 백엔드(제보→관리자 승인→리뷰·좋아요·댓글 생명주기).

Java 21Spring BootQueryDSLMySQLJWTCaffeineCloudflare R2

제보→관리자 승인→상호작용 — 상태머신 기반 생명주기

문제
사용자 제보 데이터의 신뢰성을 위해 관리자 승인 게이트가 필요하고, 리뷰·좋아요·댓글의 생성·수정·삭제를 일관되게 다뤄야 함.
설계
제보(맛집·메뉴)를 상태머신(대기→승인/반려)으로 관리해 승인분만 노출. 리뷰·좋아요·댓글은 생성·수정·삭제 생명주기로 운영하고, 익명 커뮤니티 글은 서버 생성 익명 닉네임 + 소프트 삭제로 처리.
효과
미검증 제보의 노출을 차단하고, 상태 기반으로 운영·모더레이션을 일관되게 유지.

RefreshToken 보안 강화 — SHA-256 해시 저장 + 회전 + 60초 Grace Period

문제
모바일 환경의 네트워크 불안정으로 RefreshToken 중복 갱신 요청 발생. 토큰 탈취 시 재사용 탐지 필요.
설계
RefreshToken을 SHA-256 해시로 저장. 갱신 시마다 토큰 회전. 60초 Grace Period 내 재요청에는 멱등 응답, 초과 시 전체 토큰 revoke로 재사용 탐지.
효과
중복 갱신 요청 멱등 처리로 모바일 불안정 환경 대응. 탈취된 토큰 재사용 시 자동 탐지·전체 revoke로 유출 방어.

트랜잭션 커밋 후 비동기 알림 + 무료 인프라 스팸 방어

문제
알림 발송이 트랜잭션 지연을 유발. Redis 무료 플랜 부재로 캐시·Rate Limit 구현 제약. 비로그인 스팸 요청 방어 필요.
설계
@TransactionalEventListener(AFTER_COMMIT)+@Async로 알림을 트랜잭션 커밋 후 비동기 처리. 알림 인프라 장애 시 NoOp 폴백. Caffeine(TTL 24h)으로 인메모리 캐시. @GuestAccess 커스텀 어노테이션으로 비로그인 요청 속도 제한.
효과
알림 발송 실패가 핵심 트랜잭션에 영향 없음. Redis 없이 무료 인프라만으로 캐시·스팸 방어 구현.

Vendor 평점 동기 집계 캐시 — 조회 성능 + 정합성

문제
맛집 목록·상세 조회마다 리뷰를 집계하면 성능 부담, 캐시 컬럼을 잘못 다루면 음수·불일치 위험.
설계
리뷰 작성·수정·삭제 시점에 맛집의 평균 평점·리뷰 수를 증분 재계산. 마지막 리뷰 삭제 시 0으로 리셋, 좋아요 감소는 0 하한 가드로 음수 방지.
효과
목록 조회 시 별도 집계 쿼리 없이 캐시값을 바로 노출하면서 평점 정합성을 유지.
RefreshToken 회전 + Grace Period — 멱등성·유출 방어
@TransactionalEventListener + Caffeine — 무료 인프라 비동기 알림