edit outer sequence

This commit is contained in:
cherry2250 2025-10-22 17:32:19 +09:00
parent 7a1fcc96de
commit a8c6397edf
3 changed files with 64 additions and 189 deletions

View File

@ -7,9 +7,7 @@ actor "사용자\n(소상공인)" as User
participant "Frontend\n(Web/Mobile)" as Frontend participant "Frontend\n(Web/Mobile)" as Frontend
participant "API Gateway" as Gateway participant "API Gateway" as Gateway
participant "User Service" as UserService participant "User Service" as UserService
database "Redis\nCache" as Redis
database "User DB\n(PostgreSQL)" as UserDB database "User DB\n(PostgreSQL)" as UserDB
participant "국세청 API\n(외부)" as NTSApi
== UFR-USER-010: 회원가입 플로우 == == UFR-USER-010: 회원가입 플로우 ==
@ -40,49 +38,6 @@ alt 중복 사용자 존재
Frontend --> User: "이미 가입된 전화번호입니다" Frontend --> User: "이미 가입된 전화번호입니다"
else 신규 사용자 else 신규 사용자
' 사업자번호 검증 (Circuit Breaker 적용)
UserService -> Redis: GET user:business:{사업자번호}
activate Redis
Redis --> UserService: 캐시된 검증 결과 확인
deactivate Redis
alt 캐시 HIT (검증 결과 있음)
UserService -> UserService: 캐시된 검증 결과 사용\n(응답 시간: 0.1초)
else 캐시 MISS (검증 필요)
UserService -> NTSApi: POST /사업자번호_검증\n(사업자번호)\n[Circuit Breaker, Timeout 5초]
activate NTSApi
alt 국세청 API 정상 응답
NTSApi --> UserService: 200 OK\n(사업자번호 유효, 영업 상태)
deactivate NTSApi
UserService -> Redis: SET user:business:{사업자번호}\n검증 결과 (TTL 7일)
activate Redis
Redis --> UserService: 캐싱 완료
deactivate Redis
else 국세청 API 장애 (Circuit Breaker Open)
NTSApi --> UserService: 500 Internal Server Error\n또는 Timeout
deactivate NTSApi
UserService -> UserService: Fallback 실행:\n사업자번호 검증 스킵\n(수동 확인 안내)
note right of UserService
**Resilience 패턴 적용**
- Circuit Breaker: 실패율 50% 초과 시 Open
- Retry: 최대 3회 재시도 (지수 백오프: 1초, 2초, 4초)
- Timeout: 5초
- Fallback: 검증 스킵 (수동 확인 안내)
end note
end
end
alt 사업자번호 검증 실패 (휴폐업 등)
UserService --> Gateway: 400 Bad Request\n(사업자번호 검증 실패)
Gateway --> Frontend: 400 Bad Request
Frontend --> User: "유효하지 않은 사업자번호입니다.\n휴폐업 여부를 확인해주세요."
else 사업자번호 검증 성공
UserService -> UserService: 비밀번호 해싱\n(bcrypt, Cost Factor 10) UserService -> UserService: 비밀번호 해싱\n(bcrypt, Cost Factor 10)
UserService -> UserService: 사업자번호 암호화\n(AES-256) UserService -> UserService: 사업자번호 암호화\n(AES-256)
@ -101,11 +56,6 @@ else 신규 사용자
UserService -> UserService: JWT 토큰 생성\n(user_id, role=OWNER,\nexp=7일) UserService -> UserService: JWT 토큰 생성\n(user_id, role=OWNER,\nexp=7일)
UserService -> Redis: SET user:session:{token}\n(user_id, role, TTL 7일)
activate Redis
Redis --> UserService: 세션 저장 완료
deactivate Redis
UserService --> Gateway: 201 Created\n(JWT 토큰, 사용자 정보) UserService --> Gateway: 201 Created\n(JWT 토큰, 사용자 정보)
deactivate UserService deactivate UserService
@ -118,7 +68,6 @@ else 신규 사용자
Frontend -> Gateway: 대시보드 화면으로 이동 Frontend -> Gateway: 대시보드 화면으로 이동
deactivate Frontend deactivate Frontend
end
end end
== UFR-USER-020: 로그인 플로우 == == UFR-USER-020: 로그인 플로우 ==
@ -159,11 +108,6 @@ else 사용자 존재
UserService -> UserService: JWT 토큰 생성\n(user_id, role=OWNER,\nexp=7일) UserService -> UserService: JWT 토큰 생성\n(user_id, role=OWNER,\nexp=7일)
UserService -> Redis: SET user:session:{token}\n(user_id, role, TTL 7일)
activate Redis
Redis --> UserService: 세션 저장 완료
deactivate Redis
UserService -> UserDB: UPDATE users\nSET last_login_at = NOW()\nWHERE user_id = ? UserService -> UserDB: UPDATE users\nSET last_login_at = NOW()\nWHERE user_id = ?
activate UserDB activate UserDB
UserDB --> UserService: 업데이트 완료 UserDB --> UserService: 업데이트 완료
@ -203,10 +147,7 @@ Gateway -> Gateway: JWT 토큰 검증
Gateway -> UserService: POST /api/users/logout\n(JWT 토큰) Gateway -> UserService: POST /api/users/logout\n(JWT 토큰)
activate UserService activate UserService
UserService -> Redis: DEL user:session:{token} UserService -> UserService: JWT 토큰 블랙리스트에 추가\n(만료 시까지 유효)
activate Redis
Redis --> UserService: 세션 삭제 완료
deactivate Redis
UserService --> Gateway: 200 OK\n(로그아웃 성공) UserService --> Gateway: 200 OK\n(로그아웃 성공)
deactivate UserService deactivate UserService
@ -221,28 +162,4 @@ Frontend --> User: "안전하게 로그아웃되었습니다"
Frontend -> Gateway: 로그인 화면으로 이동 Frontend -> Gateway: 로그인 화면으로 이동
deactivate Frontend deactivate Frontend
note over User, NTSApi
**Resilience 패턴 적용 요약**
**Circuit Breaker (국세청 API)**:
- 실패율 50% 초과 시 Open
- 30초 후 Half-Open 상태로 전환
- 3개 요청 테스트 후 상태 결정
**Retry Pattern**:
- 최대 3회 재시도
- 지수 백오프: 1초, 2초, 4초
- 재시도 대상: SocketTimeoutException, ConnectException
**Timeout Pattern**:
- 국세청 API: 5초
**Fallback Pattern**:
- 국세청 API 장애 시: 사업자번호 검증 스킵 (수동 확인 안내)
**Cache-Aside Pattern**:
- 사업자번호 검증 결과 캐싱 (TTL 7일)
- 캐시 HIT: 0.1초, MISS: 5초 (외부 API 호출)
end note
@enduml @enduml

View File

@ -10,11 +10,13 @@ participant "Analytics Service" as Analytics
participant "Redis Cache\n(TTL 5분)" as Redis participant "Redis Cache\n(TTL 5분)" as Redis
participant "Analytics DB" as AnalyticsDB participant "Analytics DB" as AnalyticsDB
participant "Kafka\n(Event Topics)" as Kafka participant "Kafka\n(Event Topics)" as Kafka
box "외부 시스템" #LightGray
participant "우리동네TV API" as WooriAPI note over AnalyticsDB
participant "지니TV API" as GenieAPI **배치 처리로 수집된 데이터**
participant "SNS APIs\n(Instagram/Naver/Kakao)" as SNSAPI - 외부 채널 통계는 배치 작업으로
end box 주기적으로 수집하여 DB에 저장
- 목업 데이터로 시작, 점진적으로 실제 API 연동
end note
== 1. 대시보드 조회 - Cache HIT 시나리오 == == 1. 대시보드 조회 - Cache HIT 시나리오 ==
@ -83,56 +85,28 @@ AnalyticsDB --> Analytics: 이벤트 통계\n- 총 참여자 수\n- 예상 ROI\n
deactivate AnalyticsDB deactivate AnalyticsDB
||| |||
== 2.2. 외부 채널 API 병렬 호출 (Circuit Breaker 적용) == == 2.2. 배치 수집된 채널 통계 데이터 조회 ==
par 병렬 채널 API 호출 Analytics -> AnalyticsDB: SELECT channel_stats\nWHERE event_id = {id}
Analytics -> WooriAPI: GET /stats/{eventId}\n+ API Key\n[Circuit Breaker] activate AnalyticsDB
activate WooriAPI
alt Circuit Breaker CLOSED (정상) note right of Analytics
WooriAPI --> Analytics: 200 OK\n- 노출 수: 5,000\n- 조회 수: 1,200 **배치 처리 방식**
deactivate WooriAPI - 외부 API는 별도 배치 작업으로 주기적 수집
- 수집된 데이터는 DB에 저장
- 대시보드에서는 DB 데이터만 조회
- 응답 시간 단축 및 외부 API 의존성 제거
end note
note right of Analytics AnalyticsDB --> Analytics: 채널별 통계 데이터\n- 우리동네TV: 노출 5,000, 조회 1,200\n- 지니TV: 노출 10,000, 클릭 500\n- Instagram: 좋아요 300, 댓글 50\n- Naver: 조회 2,000\n- Kakao: 공유 100
**Resilience 패턴** deactivate AnalyticsDB
- Circuit Breaker: 실패율 50% 초과 시 Open
- Timeout: 10초
- Retry: 최대 3회 (지수 백오프)
- Fallback: 캐시된 이전 데이터 반환
end note
else Circuit Breaker OPEN (장애) note right of Analytics
Analytics -> Analytics: **Fallback 실행**\n캐시된 이전 데이터 사용 **목업 데이터 활용**
note right of Analytics - 초기에는 목업 데이터로 시작
Circuit Breaker OPEN 상태 - 점진적으로 실제 배치 작업 구현
- 빠른 실패로 응답 시간 단축 - 배치 주기: 10분마다 수집
- 30초 후 Half-Open으로 전환 end note
end note
end
else
Analytics -> GenieAPI: GET /campaign/{eventId}/stats\n+ API Key\n[Circuit Breaker]
activate GenieAPI
alt 정상 응답
GenieAPI --> Analytics: 200 OK\n- 광고 노출 수: 10,000\n- 클릭 수: 500
deactivate GenieAPI
else Timeout (10초 초과)
Analytics -> Analytics: **Timeout 처리**\n기본값 반환 (0)
note right of Analytics
Timeout 발생
- 리소스 점유 방지
- Fallback으로 기본값 설정
end note
end
else
Analytics -> SNSAPI: GET /posts/{eventId}/insights\n+ Access Token\n[Circuit Breaker]
activate SNSAPI
SNSAPI --> Analytics: 200 OK\n- Instagram: 좋아요 300, 댓글 50\n- Naver: 조회 수 2,000\n- Kakao: 공유 수 100
deactivate SNSAPI
end
||| |||
== 2.3. 데이터 통합 및 ROI 계산 == == 2.3. 데이터 통합 및 ROI 계산 ==

View File

@ -11,7 +11,6 @@ participant "AI Service" as AI
participant "Content Service" as Content participant "Content Service" as Content
participant "Distribution Service" as Dist participant "Distribution Service" as Dist
participant "Kafka" as Kafka participant "Kafka" as Kafka
participant "Redis Cache" as Cache
database "Event DB" as EventDB database "Event DB" as EventDB
participant "외부 AI API" as AIApi participant "외부 AI API" as AIApi
participant "이미지 생성 API" as ImageApi participant "이미지 생성 API" as ImageApi
@ -21,14 +20,8 @@ participant "배포 채널 APIs" as ChannelApis
User -> FE: 이벤트 목적 선택 User -> FE: 이벤트 목적 선택
FE -> Gateway: POST /events/purposes\n{목적, 매장정보} FE -> Gateway: POST /events/purposes\n{목적, 매장정보}
Gateway -> Event: 이벤트 목적 저장 요청 Gateway -> Event: 이벤트 목적 저장 요청
Event -> Cache: 캐시 조회\nkey: purpose:{userId} Event -> EventDB: 이벤트 목적 저장
alt 캐시 히트 EventDB --> Event: 저장 완료
Cache --> Event: 캐시된 데이터
else 캐시 미스
Event -> EventDB: 이벤트 목적 저장
EventDB --> Event: 저장 완료
Event -> Cache: 캐시 저장\nTTL: 30분
end
Event --> Gateway: 저장 완료\n{eventDraftId} Event --> Gateway: 저장 완료\n{eventDraftId}
Gateway --> FE: 200 OK Gateway --> FE: 200 OK
FE --> User: AI 추천 화면으로 이동 FE --> User: AI 추천 화면으로 이동
@ -42,38 +35,29 @@ Event --> Gateway: Job 생성 완료\n{jobId, status: PENDING}
Gateway --> FE: 202 Accepted\n{jobId} Gateway --> FE: 202 Accepted\n{jobId}
FE --> User: "AI가 분석 중입니다..." (로딩) FE --> User: "AI가 분석 중입니다..." (로딩)
note over AI: Kafka Consumer\nai-job-topic 구독 note over AI: Kafka Consumer\nai 이벤트 생성 topic 구독
Kafka --> AI: Consume Job Message\n{jobId, eventDraftId, ...} Kafka --> AI: Consume Job Message\n{jobId, eventDraftId, ...}
AI -> Cache: 트렌드 분석 캐시 조회\nkey: trend:{업종}:{지역}
alt 캐시 히트 AI -> EventDB: 과거 이벤트 데이터 조회
Cache --> AI: 캐시된 트렌드 데이터 EventDB --> AI: 이벤트 통계 데이터
else 캐시 미스 AI -> AIApi: 트렌드 분석 및 이벤트 추천 요청\n{목적, 업종, 지역, 과거데이터, 매장정보}
AI -> EventDB: 과거 이벤트 데이터 조회 AIApi --> AI: 3가지 추천안 + 트렌드 요약\n(예: "여름철 시원한 음료 선호도 증가")
EventDB --> AI: 이벤트 통계 데이터 AI -> EventDB: 추천 결과 및 트렌드 요약 저장
AI -> AIApi: 트렌드 분석 요청\n{업종, 지역, 과거데이터}
AIApi --> AI: 트렌드 분석 결과
AI -> Cache: 트렌드 캐시 저장\nTTL: 1시간
end
AI -> AIApi: 이벤트 추천 요청\n{목적, 트렌드, 매장정보}
AIApi --> AI: 3가지 추천안 생성
AI -> EventDB: 추천 결과 저장
EventDB --> AI: 저장 완료 EventDB --> AI: 저장 완료
AI -> Cache: Job 상태 업데이트\nkey: job:{jobId}\nstatus: COMPLETED AI -> EventDB: Job 상태 업데이트\nstatus: COMPLETED
AI -> Kafka: Publish to event-topic\nEventRecommended\n{jobId, eventDraftId, recommendations} AI -> Kafka: Publish to event-topic\nEventRecommended\n{jobId, eventDraftId, recommendations, trendSummary}
group Polling으로 상태 확인 group Polling으로 상태 확인
loop 상태 확인 (최대 30초) loop 상태 확인 (최대 30초)
FE -> Gateway: GET /jobs/{jobId}/status FE -> Gateway: GET /jobs/{jobId}/status
Gateway -> Event: Job 상태 조회 Gateway -> Event: Job 상태 조회
Event -> Cache: 캐시에서 Job 상태 확인 Event -> EventDB: Job 상태 및 결과 조회
Cache --> Event: {status, result} EventDB --> Event: {status, result}
alt Job 완료 alt Job 완료
Event --> Gateway: 200 OK\n{status: COMPLETED, recommendations} Event --> Gateway: 200 OK\n{status: COMPLETED, recommendations, trendSummary}
Gateway --> FE: 추천 결과 반환 Gateway --> FE: 추천 결과 및 트렌드 요약 반환
FE --> User: 3가지 추천안 표시\n(제목/경품 수정 가능) FE --> User: 트렌드 요약 표시\n3가지 추천안 표시\n(제목/경품 수정 가능)
else Job 진행중 else Job 진행중
Event --> Gateway: 200 OK\n{status: PENDING/PROCESSING} Event --> Gateway: 200 OK\n{status: PENDING/PROCESSING}
Gateway --> FE: 진행중 상태 Gateway --> FE: 진행중 상태
@ -100,7 +84,7 @@ Event --> Gateway: Job 생성 완료\n{jobId, status: PENDING}
Gateway --> FE: 202 Accepted\n{jobId} Gateway --> FE: 202 Accepted\n{jobId}
FE --> User: "이미지 생성 중..." (로딩) FE --> User: "이미지 생성 중..." (로딩)
note over Content: Kafka Consumer\nimage-job-topic 구독 note over Content: Kafka Consumer\n이미지 생성 topic 구독
Kafka --> Content: Consume Job Message\n{jobId, eventDraftId, ...} Kafka --> Content: Consume Job Message\n{jobId, eventDraftId, ...}
par 심플 스타일 par 심플 스타일
@ -116,15 +100,15 @@ end
Content -> EventDB: 이미지 URL 저장 Content -> EventDB: 이미지 URL 저장
EventDB --> Content: 저장 완료 EventDB --> Content: 저장 완료
Content -> Cache: Job 상태 업데이트\nkey: job:{jobId}\nstatus: COMPLETED Content -> EventDB: Job 상태 업데이트\nstatus: COMPLETED
Content -> Kafka: Publish to event-topic\nContentCreated\n{jobId, eventDraftId, imageUrls} Content -> Kafka: Publish to event-topic\nContentCreated\n{jobId, eventDraftId, imageUrls}
group Polling으로 상태 확인 group Polling으로 상태 확인
loop 상태 확인 (최대 30초) loop 상태 확인 (최대 30초)
FE -> Gateway: GET /jobs/{jobId}/status FE -> Gateway: GET /jobs/{jobId}/status
Gateway -> Event: Job 상태 조회 Gateway -> Event: Job 상태 조회
Event -> Cache: 캐시에서 Job 상태 확인 Event -> EventDB: Job 상태 및 결과 조회
Cache --> Event: {status, imageUrls} EventDB --> Event: {status, imageUrls}
alt Job 완료 alt Job 완료
Event --> Gateway: 200 OK\n{status: COMPLETED, imageUrls} Event --> Gateway: 200 OK\n{status: COMPLETED, imageUrls}