feat : 역설계 파일 추가
This commit is contained in:
parent
409d7abdc6
commit
04c32417e7
26
design/API 설계서.txt
Normal file
26
design/API 설계서.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
사용자 컨텍스트|User Service|USR-001|구글 로그인|AuthController|Google OAuth 인증 처리|POST|/api/users|/auth/google-login||||GoogleLoginRequest|FALSE|{googleAccessToken: String, googleIdToken: String}|LoginResponse|FALSE|{accessToken: String, refreshToken: String, userId: String, isNewUser: boolean, message: String}
|
||||||
|
사용자 컨텍스트|User Service|USR-002|프로필 완료|AuthController|사용자 프로필 정보 저장|POST|/api/users|/profile/complete||||UserRegistrationRequest|FALSE|{name: String, birthDate: String, occupation: String}|UserRegistrationResponse|FALSE|{userId: String, message: String, status: String, profileCompletedAt: String}
|
||||||
|
사용자 컨텍스트|User Service|USR-003|사용자 기본 정보 조회|AuthController|사용자 프로필 조회|GET|/api/users|/profile|||||||UserProfileResponse|FALSE|{userId: String, name: String, age: int, occupation: String, registeredAt: String, lastLoginAt: String}
|
||||||
|
사용자 컨텍스트|User Service||직업 코드 변환|UserController|직업명을 코드로 변환|GET|/api/users|/occupations/code||occupationName <String> occupationName|||||OccupationCodeResponse|FALSE|{occupationCode: String, occupationName: String}
|
||||||
|
사용자 컨텍스트|User Service||직업명 조회|UserController|직업 코드를 명칭으로 변환|GET|/api/users|/occupations/name||occupationCode <String> occupationCode|||||OccupationNameResponse|FALSE|{occupationCode: String, occupationName: String}
|
||||||
|
사용자 컨텍스트|User Service||전체 직업 목록|UserController|모든 직업 목록 조회|GET|/api/users|/occupations|||||||OccupationListResponse|TRUE|{occupations: [{occupationCode: String, occupationName: String, category: String}], totalCount: int}
|
||||||
|
|
||||||
|
건강 컨텍스트|Health Service|USR-009|건강검진 연동|HealthController|건강보험공단 건강검진 데이터 연동|POST|/api/health|/checkup/sync||userId <String> userId|||||HealthSyncResponse|FALSE|{syncedRecords: int, newRecords: int, updatedRecords: int, skippedRecords: int, lastSyncedCheckup: Object, message: String}
|
||||||
|
건강 컨텍스트|Health Service||건강검진 이력 조회|HealthController|사용자 건강검진 이력 조회|GET|/api/health|/checkup/history||limit <int> limit|||||HealthHistoryResponse|FALSE|{checkupHistory: [{referenceYear: int, heightCm: double, weightKg: double, waistCm: double, bmi: double, systolicBp: int, diastolicBp: int, fastingGlucose: int, totalCholesterol: int, healthScore: int, riskLevel: String, abnormalIndicators: [String], analysisDate: String}], totalRecords: int, averageHealthScore: double, trendAnalysis: String, normalRangeReference: Object}
|
||||||
|
건강 컨텍스트|Health Service||건강검진 파일 업로드|HealthController|건강검진 파일 Azure Blob 저장|POST|/api/health|/checkup/upload||||CheckupFileRequest|FALSE|{userId: String, fileName: String, fileType: String, fileContent: String}|FileUploadResponse|FALSE|{fileId: String, uploadUrl: String, status: String, message: String}
|
||||||
|
건강 컨텍스트|Health Service||정상치 기준 조회|HealthController|성별별 건강검진 정상치 기준|GET|/api/health|/normal-ranges||genderCode <int> genderCode|||||NormalRangeResponse|TRUE|{normalRanges: [{itemCode: String, itemName: String, genderCode: int, normalMin: double, normalMax: double, cautionMin: double, cautionMax: double, dangerMin: double, dangerMax: double, unit: String}], genderCode: int}
|
||||||
|
|
||||||
|
목표 컨텍스트|Goal Service|USR-010|목표 설정 시작|GoalController|미션 선택 및 목표 설정|POST|/api/goals|/missions/select||||MissionSelectionRequest|FALSE|{userId: String, selectedMissionIds: [String]}|GoalSetupResponse|FALSE|{goalId: String, selectedMissions: [{missionId: String, title: String, description: String, startDate: String}], message: String, setupCompletedAt: String}
|
||||||
|
목표 컨텍스트|Goal Service|USR-011|설정한 목표 관리|GoalController|설정된 목표 조회|GET|/api/goals|/missions/active|||||||ActiveMissionsResponse|FALSE|{dailyMissions: [{missionId: String, title: String, description: String, status: String, completedToday: boolean, streakDays: int, nextReminderTime: String}], totalMissions: int, todayCompletedCount: int, completionRate: double}
|
||||||
|
목표 컨텍스트|Goal Service|USR-012|목표 달성 기록|GoalController|미션 완료 처리|PUT|/api/goals|/missions/{missionId}/complete|missionId <String> missionId|||MissionCompleteRequest|FALSE|{userId: String, completed: boolean, completedAt: String, notes: String}|MissionCompleteResponse|FALSE|{message: String, status: String, achievementMessage: String, newStreakDays: int, totalCompletedCount: int, earnedPoints: int}
|
||||||
|
목표 컨텍스트|Goal Service|USR-013|목표 달성 이력|GoalController|미션 달성 이력 조회|GET|/api/goals|/missions/history||startDate <String> startDate, endDate <String> endDate, missionIds <String> missionIds|||||MissionHistoryResponse|FALSE|{totalAchievementRate: double, periodAchievementRate: double, bestStreak: int, missionStats: [{missionId: String, title: String, achievementRate: double, completedDays: int, totalDays: int}], chartData: Object, period: {startDate: String, endDate: String}, insights: [String]}
|
||||||
|
목표 컨텍스트|Goal Service||목표 재설정|GoalController|미션 재설정|POST|/api/goals|/missions/reset||||MissionResetRequest|FALSE|{userId: String, reason: String, currentMissionIds: [String]}|MissionResetResponse|FALSE|{message: String, newRecommendations: [{missionId: String, title: String, description: String, category: String}], resetCompletedAt: String}
|
||||||
|
|
||||||
|
지능형서비스 컨텍스트|Intelligence Service|USR-004|AI 3줄 요약 진단|AnalysisController|건강검진 결과 AI 3줄 요약|GET|/api/intelligence|/health/diagnosis||userId <String> userId|||||HealthDiagnosisResponse|FALSE|{threeSentenceSummary: [String], healthScore: int, riskLevel: String, occupationConsiderations: String, analysisTimestamp: String, confidenceScore: double}
|
||||||
|
지능형서비스 컨텍스트|Intelligence Service|USR-004|AI 추천 건강 미션|AnalysisController|AI 기반 미션 추천|POST|/api/intelligence|/missions/recommend||||MissionRecommendationRequest|FALSE|{userId: String, currentHealthStatus: String, preferences: [String]}|MissionRecommendationResponse|FALSE|{missions: [{missionId: String, title: String, description: String, category: String, difficulty: String, healthBenefit: String, occupationRelevance: String, estimatedTimeMinutes: int}], recommendationReason: String, totalRecommended: int}
|
||||||
|
지능형서비스 컨텍스트|Intelligence Service|USR-005|챗봇 상담|ChatController|AI 채팅 상담|POST|/api/intelligence|/chat/consultation||||ChatRequest|FALSE|{message: String, sessionId: String, context: String}|ChatResponse|FALSE|{response: String, sessionId: String, timestamp: String, suggestedQuestions: [String], responseType: String}
|
||||||
|
지능형서비스 컨텍스트|Intelligence Service||채팅 히스토리 조회|ChatController|채팅 기록 조회|GET|/api/intelligence|/chat/history||sessionId <String> sessionId, limit <int> messageLimit|||||ChatHistoryResponse|FALSE|{sessionId: String, messages: [{role: String, content: String, timestamp: String}], totalMessageCount: int, cacheExpiration: String}
|
||||||
|
지능형서비스 컨텍스트|Intelligence Service|USR-007|미션 달성 축하|NotificationController|미션 달성 축하 메시지|POST|/api/intelligence|/notifications/celebration||||CelebrationRequest|FALSE|{userId: String, missionId: String, achievementType: String, consecutiveDays: int, totalAchievements: int}|CelebrationResponse|FALSE|{congratsMessage: String, achievementBadge: String, healthBenefit: String, nextMilestone: String, encouragementLevel: String, visualEffect: String}
|
||||||
|
지능형서비스 컨텍스트|Intelligence Service|USR-006|미션 독려|NotificationController|미션 독려 메시지 생성|POST|/api/intelligence|/notifications/encouragement||||EncouragementRequest|FALSE|{userId: String, missionsStatus: [{missionId: String, completed: boolean}]}|EncouragementResponse|FALSE|{message: String, motivationType: String, timing: String, personalizedTip: String, priority: String}
|
||||||
|
지능형서비스 컨텍스트|Intelligence Service||배치 알림 처리|BatchController|주기적 AI 알림 트리거|POST|/api/intelligence|/batch/notifications||||BatchNotificationRequest|FALSE|{triggerTime: String, targetUsers: [String], notificationType: String}|BatchNotificationResponse|FALSE|{processedCount: int, successCount: int, failedCount: int, nextScheduledTime: String}
|
||||||
|
|
||||||
206
design/내부 시퀀스 설계서 - Goal Service.txt
Normal file
206
design/내부 시퀀스 설계서 - Goal Service.txt
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam sequenceArrowThickness 2
|
||||||
|
skinparam sequenceParticipantBorderThickness 2
|
||||||
|
skinparam sequenceActorBorderThickness 2
|
||||||
|
skinparam sequenceGroupBorderThickness 2
|
||||||
|
|
||||||
|
title Goal Service 내부 시퀀스 다이어그램 (역설계 - 개발 소스 기반)
|
||||||
|
|
||||||
|
participant "GoalController" as GoalCtrl
|
||||||
|
participant "GoalUseCase" as GoalUC
|
||||||
|
participant "GoalDomainService" as GoalDomainSvc
|
||||||
|
participant "MissionProgressDomainService" as ProgressDomainSvc
|
||||||
|
participant "GoalRepository" as GoalRepo
|
||||||
|
participant "MissionProgressRepository" as ProgressRepo
|
||||||
|
participant "IntelligenceServiceAdapter" as IntelAdapter
|
||||||
|
participant "CacheAdapter" as CacheAdapter
|
||||||
|
participant "EventPublisherAdapter" as EventAdapter
|
||||||
|
participant "PostgreSQL" as PostgreSQL
|
||||||
|
participant "Redis Cache" as Redis
|
||||||
|
participant "Azure Service Bus" as ServiceBus
|
||||||
|
|
||||||
|
== 1. POST /api/goals/missions/select (미션 선택 및 목표 설정) ==
|
||||||
|
|
||||||
|
GoalCtrl -> GoalUC: selectMissions(missionSelectionRequest)
|
||||||
|
note right: {userId, selectedMissionIds}
|
||||||
|
|
||||||
|
GoalUC -> GoalDomainSvc: validateMissionSelection(userId, missionIds)
|
||||||
|
GoalDomainSvc -> GoalDomainSvc: checkMaximumMissions(selectedMissionIds)
|
||||||
|
note right: 최대 5개 미션 제한 검증
|
||||||
|
|
||||||
|
GoalUC -> GoalRepo: findActiveGoalByUserId(userId)
|
||||||
|
GoalRepo -> PostgreSQL: SELECT * FROM user_mission_goals WHERE user_id = ? AND is_active = true
|
||||||
|
PostgreSQL -> GoalRepo: 기존 활성 목표 조회
|
||||||
|
|
||||||
|
alt 기존 활성 목표가 있는 경우
|
||||||
|
GoalUC -> GoalDomainSvc: deactivateExistingGoal(existingGoal)
|
||||||
|
GoalDomainSvc -> GoalRepo: updateGoalStatus(goalId, false)
|
||||||
|
GoalRepo -> PostgreSQL: UPDATE user_mission_goals SET is_active = false
|
||||||
|
end
|
||||||
|
|
||||||
|
GoalUC -> GoalDomainSvc: createNewGoalWithMissions(userId, selectedMissionIds)
|
||||||
|
GoalDomainSvc -> GoalDomainSvc: generateGoalId()
|
||||||
|
GoalDomainSvc -> GoalDomainSvc: createUserMissionEntities(goalId, missionIds)
|
||||||
|
|
||||||
|
GoalDomainSvc -> GoalRepo: saveGoalWithMissions(goalEntity, missionEntities)
|
||||||
|
GoalRepo -> PostgreSQL: BEGIN TRANSACTION
|
||||||
|
GoalRepo -> PostgreSQL: INSERT INTO user_mission_goals
|
||||||
|
GoalRepo -> PostgreSQL: INSERT INTO user_missions (batch)
|
||||||
|
GoalRepo -> PostgreSQL: COMMIT
|
||||||
|
|
||||||
|
GoalUC -> CacheAdapter: invalidateActiveMissionsCache(userId)
|
||||||
|
CacheAdapter -> Redis: DEL goals:active:{userId}
|
||||||
|
|
||||||
|
GoalUC -> EventAdapter: publishGoalSetEvent(userId, goalId, selectedMissions)
|
||||||
|
EventAdapter -> ServiceBus: 목표 설정 이벤트 발행
|
||||||
|
|
||||||
|
GoalUC -> GoalCtrl: GoalSetupResponse 반환
|
||||||
|
note right: {goalId, selectedMissions, message, setupCompletedAt}
|
||||||
|
|
||||||
|
== 2. GET /api/goals/missions/active (활성 미션 조회) ==
|
||||||
|
|
||||||
|
GoalCtrl -> GoalUC: getActiveMissions(userId)
|
||||||
|
|
||||||
|
GoalUC -> CacheAdapter: getCachedActiveMissions(userId)
|
||||||
|
CacheAdapter -> Redis: GET goals:active:{userId}
|
||||||
|
Redis -> CacheAdapter: 캐시된 활성 미션 또는 null
|
||||||
|
|
||||||
|
alt 캐시 미스인 경우
|
||||||
|
GoalUC -> GoalRepo: findActiveMissionsByUserId(userId)
|
||||||
|
GoalRepo -> PostgreSQL: SELECT um.*, mp.* FROM user_missions um LEFT JOIN mission_progress mp ON um.mission_id = mp.mission_id WHERE um.user_id = ? AND um.is_active = true
|
||||||
|
PostgreSQL -> GoalRepo: 활성 미션 및 진행 상황
|
||||||
|
|
||||||
|
GoalUC -> ProgressDomainSvc: calculateMissionProgress(activeMissions)
|
||||||
|
ProgressDomainSvc -> ProgressDomainSvc: calculateCompletionRate(missions)
|
||||||
|
ProgressDomainSvc -> ProgressDomainSvc: calculateStreakDays(missions)
|
||||||
|
ProgressDomainSvc -> ProgressDomainSvc: determineTodayStatus(missions)
|
||||||
|
|
||||||
|
GoalUC -> CacheAdapter: cacheActiveMissions(userId, processedMissions)
|
||||||
|
CacheAdapter -> Redis: SETEX goals:active:{userId} 1800 {data}
|
||||||
|
note right: 30분 TTL로 캐싱
|
||||||
|
end
|
||||||
|
|
||||||
|
GoalUC -> GoalCtrl: ActiveMissionsResponse 반환
|
||||||
|
note right: {dailyMissions, totalMissions, todayCompletedCount, completionRate}
|
||||||
|
|
||||||
|
== 3. PUT /api/goals/missions/{missionId}/complete (미션 완료 처리) ==
|
||||||
|
|
||||||
|
GoalCtrl -> GoalUC: completeMission(missionId, missionCompleteRequest)
|
||||||
|
note right: {userId, completed, completedAt, notes}
|
||||||
|
|
||||||
|
GoalUC -> GoalDomainSvc: validateMissionCompletion(userId, missionId)
|
||||||
|
GoalDomainSvc -> GoalRepo: findUserMission(userId, missionId)
|
||||||
|
GoalRepo -> PostgreSQL: SELECT * FROM user_missions WHERE user_id = ? AND mission_id = ? AND is_active = true
|
||||||
|
PostgreSQL -> GoalRepo: 사용자 미션 정보
|
||||||
|
|
||||||
|
GoalUC -> ProgressRepo: findTodayProgress(userId, missionId)
|
||||||
|
ProgressRepo -> PostgreSQL: SELECT * FROM mission_progress WHERE user_id = ? AND mission_id = ? AND DATE(completed_at) = CURRENT_DATE
|
||||||
|
PostgreSQL -> ProgressRepo: 오늘의 진행 상황
|
||||||
|
|
||||||
|
alt 오늘 이미 완료한 경우
|
||||||
|
GoalUC -> GoalCtrl: 409 Conflict 응답
|
||||||
|
note right: "오늘 이미 완료된 미션입니다"
|
||||||
|
else 새로운 완료 기록
|
||||||
|
GoalUC -> ProgressDomainSvc: recordMissionCompletion(userId, missionId, completionData)
|
||||||
|
ProgressDomainSvc -> ProgressDomainSvc: calculateNewStreakDays(previousProgress)
|
||||||
|
ProgressDomainSvc -> ProgressDomainSvc: calculateEarnedPoints(mission, streakDays)
|
||||||
|
|
||||||
|
ProgressDomainSvc -> ProgressRepo: saveMissionProgress(progressEntity)
|
||||||
|
ProgressRepo -> PostgreSQL: INSERT INTO mission_progress
|
||||||
|
PostgreSQL -> ProgressRepo: 진행 기록 저장 완료
|
||||||
|
|
||||||
|
GoalUC -> ProgressDomainSvc: updateMissionStatistics(userId, missionId)
|
||||||
|
ProgressDomainSvc -> GoalRepo: updateMissionStats(missionId, newStats)
|
||||||
|
GoalRepo -> PostgreSQL: UPDATE user_missions SET total_completed_count = ?, current_streak_days = ?
|
||||||
|
end
|
||||||
|
|
||||||
|
GoalUC -> CacheAdapter: invalidateUserCaches(userId)
|
||||||
|
CacheAdapter -> Redis: DEL goals:active:{userId} goals:history:{userId}
|
||||||
|
|
||||||
|
GoalUC -> EventAdapter: publishMissionCompletedEvent(userId, missionId, achievementData)
|
||||||
|
EventAdapter -> ServiceBus: 미션 완료 이벤트 발행
|
||||||
|
|
||||||
|
GoalUC -> GoalCtrl: MissionCompleteResponse 반환
|
||||||
|
note right: {message, status, achievementMessage, newStreakDays, totalCompletedCount, earnedPoints}
|
||||||
|
|
||||||
|
== 4. GET /api/goals/missions/history (미션 달성 이력) ==
|
||||||
|
|
||||||
|
GoalCtrl -> GoalUC: getMissionHistory(userId, startDate, endDate, missionIds)
|
||||||
|
|
||||||
|
GoalUC -> CacheAdapter: getCachedMissionHistory(userId, period)
|
||||||
|
CacheAdapter -> Redis: GET goals:history:{userId}:{period}
|
||||||
|
Redis -> CacheAdapter: 캐시된 이력 또는 null
|
||||||
|
|
||||||
|
alt 캐시 미스인 경우
|
||||||
|
GoalUC -> ProgressRepo: findMissionHistoryByPeriod(userId, startDate, endDate, missionIds)
|
||||||
|
ProgressRepo -> PostgreSQL: SELECT mp.*, um.mission_title FROM mission_progress mp JOIN user_missions um ON mp.mission_id = um.mission_id WHERE mp.user_id = ? AND mp.completed_at BETWEEN ? AND ?
|
||||||
|
PostgreSQL -> ProgressRepo: 기간별 미션 이력 데이터
|
||||||
|
|
||||||
|
GoalUC -> ProgressDomainSvc: analyzeAchievementStatistics(historyData)
|
||||||
|
ProgressDomainSvc -> ProgressDomainSvc: calculateAchievementRates(histories)
|
||||||
|
ProgressDomainSvc -> ProgressDomainSvc: findBestStreak(histories)
|
||||||
|
ProgressDomainSvc -> ProgressDomainSvc: generateInsights(statistics)
|
||||||
|
ProgressDomainSvc -> ProgressDomainSvc: prepareChartData(histories)
|
||||||
|
|
||||||
|
GoalUC -> CacheAdapter: cacheMissionHistory(userId, period, analysisResult)
|
||||||
|
CacheAdapter -> Redis: SETEX goals:history:{userId}:{period} 3600 {data}
|
||||||
|
note right: 1시간 TTL로 캐싱
|
||||||
|
end
|
||||||
|
|
||||||
|
GoalUC -> GoalCtrl: MissionHistoryResponse 반환
|
||||||
|
note right: {totalAchievementRate, periodAchievementRate, bestStreak, missionStats, chartData, period, insights}
|
||||||
|
|
||||||
|
== 5. POST /api/goals/missions/reset (목표 재설정) ==
|
||||||
|
|
||||||
|
GoalCtrl -> GoalUC: resetMissions(missionResetRequest)
|
||||||
|
note right: {userId, reason, currentMissionIds}
|
||||||
|
|
||||||
|
GoalUC -> IntelAdapter: requestNewMissionRecommendations(userId, reason)
|
||||||
|
IntelAdapter -> GoalUC: 새로운 추천 미션 목록
|
||||||
|
|
||||||
|
GoalUC -> GoalDomainSvc: deactivateCurrentGoal(userId)
|
||||||
|
GoalDomainSvc -> GoalRepo: updateGoalStatus(userId, false)
|
||||||
|
GoalRepo -> PostgreSQL: UPDATE user_mission_goals SET is_active = false
|
||||||
|
|
||||||
|
GoalUC -> CacheAdapter: invalidateAllUserCaches(userId)
|
||||||
|
CacheAdapter -> Redis: DEL goals:active:{userId} goals:history:{userId}*
|
||||||
|
|
||||||
|
GoalUC -> EventAdapter: publishGoalResetEvent(userId, reason, newRecommendations)
|
||||||
|
EventAdapter -> ServiceBus: 목표 재설정 이벤트 발행
|
||||||
|
|
||||||
|
GoalUC -> GoalCtrl: MissionResetResponse 반환
|
||||||
|
note right: {message, newRecommendations, resetCompletedAt}
|
||||||
|
|
||||||
|
== 예외 처리 ==
|
||||||
|
|
||||||
|
alt 미션 개수 초과
|
||||||
|
GoalDomainSvc -> GoalUC: MissionLimitExceededException
|
||||||
|
GoalUC -> GoalCtrl: 400 Bad Request
|
||||||
|
note right: "미션은 최대 5개까지 선택 가능합니다"
|
||||||
|
end
|
||||||
|
|
||||||
|
alt 미션 권한 없음
|
||||||
|
GoalDomainSvc -> GoalUC: UnauthorizedMissionException
|
||||||
|
GoalUC -> GoalCtrl: 403 Forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
alt 이미 완료된 미션
|
||||||
|
ProgressDomainSvc -> GoalUC: MissionAlreadyCompletedException
|
||||||
|
GoalUC -> GoalCtrl: 409 Conflict
|
||||||
|
end
|
||||||
|
|
||||||
|
== 트랜잭션 및 캐싱 ==
|
||||||
|
|
||||||
|
note over GoalUC, CacheAdapter
|
||||||
|
**트랜잭션 관리**
|
||||||
|
- @Transactional 어노테이션 적용
|
||||||
|
- 미션 선택/완료 시 원자성 보장
|
||||||
|
- 예외 발생 시 자동 롤백
|
||||||
|
|
||||||
|
**캐싱 전략**
|
||||||
|
- 활성 미션: 30분 TTL
|
||||||
|
- 이력 데이터: 1시간 TTL
|
||||||
|
- 완료/재설정 시 관련 캐시 무효화
|
||||||
|
- 사용자별 캐시 키 분리
|
||||||
|
end note
|
||||||
179
design/내부 시퀀스 설계서 - Health Service.txt
Normal file
179
design/내부 시퀀스 설계서 - Health Service.txt
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam sequenceArrowThickness 2
|
||||||
|
skinparam sequenceParticipantBorderThickness 2
|
||||||
|
skinparam sequenceActorBorderThickness 2
|
||||||
|
skinparam sequenceGroupBorderThickness 2
|
||||||
|
|
||||||
|
title Health Service 내부 시퀀스 다이어그램 (역설계 - 정상치 기준 비교 포함)
|
||||||
|
|
||||||
|
participant "HealthController" as HealthCtrl
|
||||||
|
participant "CheckupSyncUseCase" as SyncUC
|
||||||
|
participant "CheckupQueryUseCase" as QueryUC
|
||||||
|
participant "FileUploadUseCase" as FileUC
|
||||||
|
participant "HealthProfileDomainService" as HealthDomainSvc
|
||||||
|
participant "CheckupAnalysisDomainService" as AnalysisDomainSvc
|
||||||
|
participant "NormalRangeDomainService" as NormalDomainSvc
|
||||||
|
participant "HealthRepository" as HealthRepo
|
||||||
|
participant "HealthCheckupRawRepository" as RawRepo
|
||||||
|
participant "NormalRangeRepository" as NormalRepo
|
||||||
|
participant "UserServiceAdapter" as UserAdapter
|
||||||
|
participant "BlobStorageAdapter" as BlobAdapter
|
||||||
|
participant "CacheAdapter" as CacheAdapter
|
||||||
|
participant "EventPublisherAdapter" as EventAdapter
|
||||||
|
participant "PostgreSQL" as PostgreSQL
|
||||||
|
participant "Redis Cache" as Redis
|
||||||
|
participant "Azure Blob Storage" as BlobStorage
|
||||||
|
participant "Azure Service Bus" as ServiceBus
|
||||||
|
|
||||||
|
== 1. POST /api/health/checkup/sync (건강검진 결과 연동) ==
|
||||||
|
|
||||||
|
HealthCtrl -> SyncUC: syncCheckupData(userId)
|
||||||
|
|
||||||
|
SyncUC -> UserAdapter: getUserInfo(userId)
|
||||||
|
UserAdapter -> SyncUC: UserInfo 응답 (memberSerialNumber, gender 포함)
|
||||||
|
|
||||||
|
SyncUC -> RawRepo: findNhisCheckupDataByMemberSerial(memberSerialNumber)
|
||||||
|
RawRepo -> PostgreSQL: SELECT * FROM health_checkup_raw WHERE member_serial_number = ? ORDER BY reference_year DESC
|
||||||
|
PostgreSQL -> RawRepo: 건강보험공단 건강검진 원본 데이터
|
||||||
|
|
||||||
|
SyncUC -> HealthRepo: findByMemberSerialNumber(memberSerialNumber)
|
||||||
|
PostgreSQL -> HealthRepo: 기존 가공 데이터 조회
|
||||||
|
|
||||||
|
alt 기존 데이터가 없거나 더 최신 원본 데이터가 있는 경우
|
||||||
|
SyncUC -> NormalRepo: getNormalRangesByGender(userGender)
|
||||||
|
NormalRepo -> PostgreSQL: SELECT * FROM health_normal_ranges WHERE gender_code IN (0, ?) ORDER BY item_code
|
||||||
|
note right: 성별별 + 공통 정상치 기준 조회
|
||||||
|
PostgreSQL -> NormalRepo: 성별별/공통 정상치 기준 데이터
|
||||||
|
|
||||||
|
SyncUC -> AnalysisDomainSvc: transformAndAnalyzeCheckupData(rawData, normalRanges, userGender)
|
||||||
|
|
||||||
|
AnalysisDomainSvc -> AnalysisDomainSvc: validateAndConvertData(rawData)
|
||||||
|
note right: 건강보험공단 데이터 검증 및 단위 변환
|
||||||
|
|
||||||
|
AnalysisDomainSvc -> NormalDomainSvc: compareWithNormalRanges(checkupData, normalRanges)
|
||||||
|
NormalDomainSvc -> NormalDomainSvc: evaluateEachIndicator(indicators, ranges)
|
||||||
|
note right: **각 지표별 정상/주의/위험 판정**\n- BMI, 혈압, 혈당, 콜레스테롤 등\n- 성별별 기준 적용
|
||||||
|
|
||||||
|
NormalDomainSvc -> NormalDomainSvc: calculateOverallRiskLevel(indicatorResults)
|
||||||
|
note right: **종합 위험도 레벨 계산**\n- 정상: 80-100점\n- 주의: 60-79점\n- 위험: 0-59점
|
||||||
|
|
||||||
|
NormalDomainSvc -> NormalDomainSvc: identifyAbnormalIndicators(indicatorResults)
|
||||||
|
note right: 이상 항목 식별 및 JSON 배열 생성
|
||||||
|
|
||||||
|
NormalDomainSvc -> AnalysisDomainSvc: NormalRangeAnalysisResult 반환
|
||||||
|
|
||||||
|
AnalysisDomainSvc -> AnalysisDomainSvc: createHealthCheckupEntity(transformedData, analysisResult)
|
||||||
|
AnalysisDomainSvc -> SyncUC: 변환된 HealthCheckupEntity (정상치 비교 결과 포함)
|
||||||
|
|
||||||
|
SyncUC -> HealthRepo: saveOrUpdateHealthCheckup(healthCheckupEntity)
|
||||||
|
HealthRepo -> PostgreSQL: INSERT/UPDATE health_checkups
|
||||||
|
note right: 정상치 비교 결과도 함께 저장\n- abnormal_indicators: JSON 배열\n- health_score: 0-100점\n- risk_level: normal/caution/danger
|
||||||
|
PostgreSQL -> HealthRepo: 저장 완료
|
||||||
|
end
|
||||||
|
|
||||||
|
SyncUC -> CacheAdapter: invalidateUserHealthCache(userId)
|
||||||
|
CacheAdapter -> Redis: DEL health:history:{userId} health:normal:{userId}
|
||||||
|
|
||||||
|
SyncUC -> EventAdapter: publishHealthDataSyncedEvent(userId, syncResult)
|
||||||
|
EventAdapter -> ServiceBus: 건강데이터 동기화 이벤트 발행
|
||||||
|
|
||||||
|
SyncUC -> HealthCtrl: HealthSyncResponse 반환
|
||||||
|
note right: {syncedRecords, newRecords, updatedRecords, skippedRecords, lastSyncedCheckup, message}
|
||||||
|
|
||||||
|
== 2. GET /api/health/checkup/history (건강검진 이력 조회) ==
|
||||||
|
|
||||||
|
HealthCtrl -> QueryUC: getHealthCheckupHistory(userId, limit)
|
||||||
|
|
||||||
|
QueryUC -> CacheAdapter: getCachedHealthHistory(userId)
|
||||||
|
CacheAdapter -> Redis: GET health:history:{userId}
|
||||||
|
Redis -> CacheAdapter: 캐시된 데이터 또는 null
|
||||||
|
|
||||||
|
alt 캐시 미스인 경우
|
||||||
|
QueryUC -> HealthRepo: findCheckupHistoryWithDetails(userId, limit)
|
||||||
|
HealthRepo -> PostgreSQL: SELECT * FROM health_checkups WHERE user_id = ? ORDER BY reference_year DESC LIMIT ?
|
||||||
|
PostgreSQL -> HealthRepo: 건강검진 이력 데이터 (정상치 비교 결과 포함)
|
||||||
|
|
||||||
|
QueryUC -> AnalysisDomainSvc: calculateTrendAnalysis(checkupHistory)
|
||||||
|
AnalysisDomainSvc -> AnalysisDomainSvc: analyzeTrendsByIndicator(historyData)
|
||||||
|
note right: **트렌드 분석**\n- 연도별 변화 추이\n- 개선/악화 항목 식별\n- 평균 건강점수 계산
|
||||||
|
|
||||||
|
QueryUC -> CacheAdapter: cacheHealthHistory(userId, analysisResult)
|
||||||
|
CacheAdapter -> Redis: SETEX health:history:{userId} 3600 {data}
|
||||||
|
note right: 1시간 TTL로 캐싱
|
||||||
|
end
|
||||||
|
|
||||||
|
QueryUC -> HealthCtrl: HealthHistoryResponse 반환
|
||||||
|
note right: {checkupHistory, totalRecords, averageHealthScore, trendAnalysis, normalRangeReference}
|
||||||
|
|
||||||
|
== 3. POST /api/health/checkup/upload (건강검진 파일 업로드) ==
|
||||||
|
|
||||||
|
HealthCtrl -> FileUC: uploadCheckupFile(uploadRequest)
|
||||||
|
note right: {userId, fileName, fileType, fileContent}
|
||||||
|
|
||||||
|
FileUC -> FileUC: validateFileFormat(fileType, fileContent)
|
||||||
|
note right: 파일 형식 및 크기 검증 (최대 10MB)
|
||||||
|
|
||||||
|
FileUC -> BlobAdapter: uploadToAzureBlob(fileName, fileContent)
|
||||||
|
BlobAdapter -> BlobStorage: Azure Blob Storage Upload
|
||||||
|
BlobStorage -> BlobAdapter: 업로드 완료 응답
|
||||||
|
|
||||||
|
FileUC -> HealthRepo: saveFileMetadata(fileMetadata)
|
||||||
|
HealthRepo -> PostgreSQL: INSERT INTO health_files (user_id, file_name, file_url, file_type, upload_status)
|
||||||
|
PostgreSQL -> HealthRepo: 파일 메타데이터 저장 완료
|
||||||
|
|
||||||
|
FileUC -> HealthCtrl: FileUploadResponse 반환
|
||||||
|
note right: {fileId, uploadUrl, status, message}
|
||||||
|
|
||||||
|
== 4. GET /api/health/normal-ranges (정상치 기준 조회) ==
|
||||||
|
|
||||||
|
HealthCtrl -> QueryUC: getNormalRangesByGender(genderCode)
|
||||||
|
|
||||||
|
QueryUC -> CacheAdapter: getCachedNormalRanges(genderCode)
|
||||||
|
CacheAdapter -> Redis: GET normal:ranges:{genderCode}
|
||||||
|
Redis -> CacheAdapter: 캐시된 정상치 기준 또는 null
|
||||||
|
|
||||||
|
alt 캐시 미스인 경우
|
||||||
|
QueryUC -> NormalRepo: getNormalRangesByGender(genderCode)
|
||||||
|
NormalRepo -> PostgreSQL: SELECT * FROM health_normal_ranges WHERE gender_code IN (0, ?) ORDER BY item_code
|
||||||
|
PostgreSQL -> NormalRepo: 성별별/공통 정상치 기준 데이터
|
||||||
|
|
||||||
|
QueryUC -> CacheAdapter: cacheNormalRanges(genderCode, normalRanges)
|
||||||
|
CacheAdapter -> Redis: SETEX normal:ranges:{genderCode} 86400 {data}
|
||||||
|
note right: 24시간 TTL로 캐싱 (변경 빈도 낮음)
|
||||||
|
end
|
||||||
|
|
||||||
|
QueryUC -> HealthCtrl: NormalRangeResponse 반환
|
||||||
|
note right: {normalRanges: [{itemCode, itemName, genderCode, normalMin, normalMax, cautionMin, cautionMax, dangerMin, dangerMax, unit}]}
|
||||||
|
|
||||||
|
== 예외 처리 (정상치 관련 추가) ==
|
||||||
|
|
||||||
|
alt 정상치 기준 데이터 없음
|
||||||
|
NormalRepo -> AnalysisDomainSvc: NoNormalRangeFoundException
|
||||||
|
AnalysisDomainSvc -> SyncUC: 기본 정상치 기준 사용
|
||||||
|
note right: 하드코딩된 기본값으로 대체
|
||||||
|
end
|
||||||
|
|
||||||
|
alt 정상치 비교 실패
|
||||||
|
NormalDomainSvc -> AnalysisDomainSvc: NormalRangeComparisonException
|
||||||
|
AnalysisDomainSvc -> SyncUC: 정상치 비교 없이 기본 저장
|
||||||
|
note right: 기본 건강검진 데이터만 저장
|
||||||
|
end
|
||||||
|
|
||||||
|
alt 파일 업로드 실패
|
||||||
|
BlobAdapter -> FileUC: BlobStorageException
|
||||||
|
FileUC -> HealthRepo: updateUploadStatus(fileId, "FAILED")
|
||||||
|
FileUC -> HealthCtrl: 500 Internal Server Error
|
||||||
|
end
|
||||||
|
|
||||||
|
== 캐싱 전략 (정상치 관련 추가) ==
|
||||||
|
|
||||||
|
note over QueryUC, CacheAdapter
|
||||||
|
**확장된 캐싱 전략**
|
||||||
|
- 건강검진 이력 + 정상치 비교: 1시간 캐시
|
||||||
|
- 정상치 기준 데이터: 24시간 캐시 (변경 빈도 낮음)
|
||||||
|
- 건강 점수 계산 결과: 포함 (이력과 함께)
|
||||||
|
- 트렌드 분석 결과: 포함 (이력과 함께)
|
||||||
|
- 파일 메타데이터: 30분 캐시
|
||||||
|
- 정상치 기준 업데이트 시 관련 캐시 무효화
|
||||||
|
end note
|
||||||
257
design/내부 시퀀스 설계서 - Intelligence Service.txt
Normal file
257
design/내부 시퀀스 설계서 - Intelligence Service.txt
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam sequenceArrowThickness 2
|
||||||
|
skinparam sequenceParticipantBorderThickness 2
|
||||||
|
skinparam sequenceActorBorderThickness 2
|
||||||
|
skinparam sequenceGroupBorderThickness 2
|
||||||
|
|
||||||
|
title Intelligence Service 내부 시퀀스 다이어그램 (역설계 - Claude AI 기반)
|
||||||
|
|
||||||
|
participant "AnalysisController" as AnalysisCtrl
|
||||||
|
participant "ChatController" as ChatCtrl
|
||||||
|
participant "NotificationController" as NotificationCtrl
|
||||||
|
participant "BatchController" as BatchCtrl
|
||||||
|
participant "HealthAnalysisUseCase" as HealthAnalysisUC
|
||||||
|
participant "ChatUseCase" as ChatUC
|
||||||
|
participant "NotificationUseCase" as NotificationUC
|
||||||
|
participant "BatchUseCase" as BatchUC
|
||||||
|
participant "AiAnalysisDomainService" as AiAnalysisDomainSvc
|
||||||
|
participant "ChatDomainService" as ChatDomainSvc
|
||||||
|
participant "NotificationDomainService" as NotificationDomainSvc
|
||||||
|
participant "HealthServiceAdapter" as HealthAdapter
|
||||||
|
participant "UserServiceAdapter" as UserAdapter
|
||||||
|
participant "GoalServiceAdapter" as GoalAdapter
|
||||||
|
participant "ClaudeApiAdapter" as ClaudeAdapter
|
||||||
|
participant "CacheAdapter" as CacheAdapter
|
||||||
|
participant "ChatHistoryRepository" as ChatRepo
|
||||||
|
participant "EventPublisherAdapter" as EventAdapter
|
||||||
|
participant "PostgreSQL" as PostgreSQL
|
||||||
|
participant "Redis Cache" as Redis
|
||||||
|
participant "Claude API" as ClaudeAPI
|
||||||
|
participant "Azure Service Bus" as ServiceBus
|
||||||
|
|
||||||
|
== 1. GET /api/intelligence/health/diagnosis (AI 3줄 요약 진단) ==
|
||||||
|
|
||||||
|
AnalysisCtrl -> HealthAnalysisUC: generateHealthDiagnosis(userId)
|
||||||
|
|
||||||
|
HealthAnalysisUC -> CacheAdapter: getCachedDiagnosis(userId)
|
||||||
|
CacheAdapter -> Redis: GET diagnosis:{userId}
|
||||||
|
Redis -> CacheAdapter: 캐시된 진단 또는 null
|
||||||
|
|
||||||
|
alt 캐시 미스인 경우
|
||||||
|
HealthAnalysisUC -> HealthAdapter: getLatestHealthCheckup(userId)
|
||||||
|
HealthAdapter -> HealthAnalysisUC: 최신 건강검진 데이터 (정상치 비교 결과 포함)
|
||||||
|
|
||||||
|
HealthAnalysisUC -> UserAdapter: getUserProfile(userId)
|
||||||
|
UserAdapter -> HealthAnalysisUC: 사용자 프로필 (직업, 나이 등)
|
||||||
|
|
||||||
|
HealthAnalysisUC -> AiAnalysisDomainSvc: createDiagnosisPrompt(healthData, userProfile)
|
||||||
|
AiAnalysisDomainSvc -> AiAnalysisDomainSvc: formatHealthDataForAI(checkupData)
|
||||||
|
note right: **AI 프롬프트 생성**\n- 건강검진 수치 및 정상치 비교 결과\n- 사용자 직업군 특성 반영\n- 3줄 요약 요청 형식
|
||||||
|
|
||||||
|
AiAnalysisDomainSvc -> ClaudeAdapter: requestHealthDiagnosis(prompt)
|
||||||
|
ClaudeAdapter -> ClaudeAPI: Claude AI 건강 진단 요청
|
||||||
|
ClaudeAPI -> ClaudeAdapter: AI 생성 3줄 요약 진단
|
||||||
|
|
||||||
|
AiAnalysisDomainSvc -> AiAnalysisDomainSvc: parseAndValidateAIResponse(aiResponse)
|
||||||
|
AiAnalysisDomainSvc -> AiAnalysisDomainSvc: calculateConfidenceScore(healthData, aiResponse)
|
||||||
|
|
||||||
|
HealthAnalysisUC -> CacheAdapter: cacheDiagnosis(userId, diagnosisResult)
|
||||||
|
CacheAdapter -> Redis: SETEX diagnosis:{userId} 1800 {data}
|
||||||
|
note right: 30분 TTL로 캐싱
|
||||||
|
end
|
||||||
|
|
||||||
|
HealthAnalysisUC -> AnalysisCtrl: HealthDiagnosisResponse 반환
|
||||||
|
note right: {threeSentenceSummary, healthScore, riskLevel, occupationConsiderations, analysisTimestamp, confidenceScore}
|
||||||
|
|
||||||
|
== 2. POST /api/intelligence/missions/recommend (AI 미션 추천) ==
|
||||||
|
|
||||||
|
AnalysisCtrl -> HealthAnalysisUC: recommendMissions(missionRecommendationRequest)
|
||||||
|
note right: {userId, currentHealthStatus, preferences}
|
||||||
|
|
||||||
|
HealthAnalysisUC -> HealthAdapter: getHealthAnalysisData(userId)
|
||||||
|
HealthAdapter -> HealthAnalysisUC: 건강 상태 및 위험 요인
|
||||||
|
|
||||||
|
HealthAnalysisUC -> UserAdapter: getUserOccupationInfo(userId)
|
||||||
|
UserAdapter -> HealthAnalysisUC: 직업군 정보
|
||||||
|
|
||||||
|
HealthAnalysisUC -> AiAnalysisDomainSvc: generateMissionRecommendations(healthData, occupation, preferences)
|
||||||
|
AiAnalysisDomainSvc -> AiAnalysisDomainSvc: createPersonalizedPrompt(data)
|
||||||
|
note right: **개인화 미션 추천 프롬프트**\n- 건강 위험 요인 기반\n- 직업군별 특성 고려\n- 사용자 선호도 반영
|
||||||
|
|
||||||
|
AiAnalysisDomainSvc -> ClaudeAdapter: requestMissionRecommendations(prompt)
|
||||||
|
ClaudeAdapter -> ClaudeAPI: Claude AI 미션 추천 요청
|
||||||
|
ClaudeAPI -> ClaudeAdapter: AI 생성 개인화 미션 목록
|
||||||
|
|
||||||
|
AiAnalysisDomainSvc -> AiAnalysisDomainSvc: parseMissionRecommendations(aiResponse)
|
||||||
|
AiAnalysisDomainSvc -> AiAnalysisDomainSvc: validateAndEnrichMissions(missions)
|
||||||
|
|
||||||
|
HealthAnalysisUC -> AnalysisCtrl: MissionRecommendationResponse 반환
|
||||||
|
note right: {missions, recommendationReason, totalRecommended}
|
||||||
|
|
||||||
|
== 3. POST /api/intelligence/chat/consultation (AI 채팅 상담) ==
|
||||||
|
|
||||||
|
ChatCtrl -> ChatUC: processChat(chatRequest)
|
||||||
|
note right: {message, sessionId, context}
|
||||||
|
|
||||||
|
ChatUC -> ChatDomainService: validateChatSession(sessionId)
|
||||||
|
ChatDomainService -> ChatRepo: findChatSession(sessionId)
|
||||||
|
ChatRepo -> PostgreSQL: SELECT * FROM chat_sessions WHERE session_id = ?
|
||||||
|
|
||||||
|
alt 새 세션인 경우
|
||||||
|
ChatDomainService -> ChatDomainService: createNewChatSession(userId)
|
||||||
|
ChatDomainService -> ChatRepo: saveChatSession(newSession)
|
||||||
|
ChatRepo -> PostgreSQL: INSERT INTO chat_sessions
|
||||||
|
end
|
||||||
|
|
||||||
|
ChatUC -> ChatRepo: getChatHistory(sessionId, limit=10)
|
||||||
|
ChatRepo -> Redis: LRANGE chat:history:{sessionId} 0 9
|
||||||
|
Redis -> ChatRepo: 최근 채팅 히스토리
|
||||||
|
|
||||||
|
alt 캐시 미스인 경우
|
||||||
|
ChatRepo -> PostgreSQL: SELECT * FROM chat_messages WHERE session_id = ? ORDER BY message_order DESC LIMIT 10
|
||||||
|
PostgreSQL -> ChatRepo: DB 채팅 히스토리
|
||||||
|
ChatRepo -> Redis: LPUSH chat:history:{sessionId} {messages}
|
||||||
|
end
|
||||||
|
|
||||||
|
ChatUC -> ChatDomainService: buildContextualPrompt(userMessage, chatHistory, context)
|
||||||
|
ChatDomainService -> ChatDomainService: formatChatContext(history)
|
||||||
|
note right: **채팅 컨텍스트 구성**\n- 이전 대화 맥락 유지\n- 건강 상담 톤앤매너 적용\n- 사용자 메시지 전처리
|
||||||
|
|
||||||
|
ChatDomainService -> ClaudeAdapter: requestChatResponse(contextualPrompt)
|
||||||
|
ClaudeAdapter -> ClaudeAPI: Claude AI 채팅 응답 요청
|
||||||
|
ClaudeAPI -> ClaudeAdapter: AI 생성 채팅 응답
|
||||||
|
|
||||||
|
ChatUC -> ChatDomainService: saveChatExchange(sessionId, userMessage, aiResponse)
|
||||||
|
ChatDomainService -> ChatRepo: saveChatMessages(sessionId, messages)
|
||||||
|
ChatRepo -> PostgreSQL: INSERT INTO chat_messages (batch)
|
||||||
|
ChatRepo -> Redis: LPUSH chat:history:{sessionId} {newMessages}
|
||||||
|
|
||||||
|
ChatUC -> ChatCtrl: ChatResponse 반환
|
||||||
|
note right: {response, sessionId, timestamp, suggestedQuestions, responseType}
|
||||||
|
|
||||||
|
== 4. GET /api/intelligence/chat/history (채팅 히스토리 조회) ==
|
||||||
|
|
||||||
|
ChatCtrl -> ChatUC: getChatHistory(sessionId, messageLimit)
|
||||||
|
|
||||||
|
ChatUC -> CacheAdapter: getCachedChatHistory(sessionId)
|
||||||
|
CacheAdapter -> Redis: LRANGE chat:history:{sessionId} 0 {limit-1}
|
||||||
|
Redis -> CacheAdapter: 캐시된 채팅 히스토리
|
||||||
|
|
||||||
|
alt 캐시 미스인 경우
|
||||||
|
ChatUC -> ChatRepo: findChatHistoryBySession(sessionId, messageLimit)
|
||||||
|
ChatRepo -> PostgreSQL: SELECT * FROM chat_messages WHERE session_id = ? ORDER BY message_order DESC LIMIT ?
|
||||||
|
PostgreSQL -> ChatRepo: DB 채팅 히스토리
|
||||||
|
|
||||||
|
ChatUC -> CacheAdapter: cacheChatHistory(sessionId, history)
|
||||||
|
CacheAdapter -> Redis: LPUSH chat:history:{sessionId} {messages}
|
||||||
|
end
|
||||||
|
|
||||||
|
ChatUC -> ChatDomainService: formatChatHistory(rawHistory)
|
||||||
|
ChatDomainService -> ChatDomainService: maskSensitiveInfo(messages)
|
||||||
|
note right: 민감정보 마스킹 처리
|
||||||
|
|
||||||
|
ChatUC -> ChatCtrl: ChatHistoryResponse 반환
|
||||||
|
note right: {sessionId, messages, totalMessageCount, cacheExpiration}
|
||||||
|
|
||||||
|
== 5. POST /api/intelligence/notifications/celebration (미션 달성 축하) ==
|
||||||
|
|
||||||
|
NotificationCtrl -> NotificationUC: generateCelebrationMessage(celebrationRequest)
|
||||||
|
note right: {userId, missionId, achievementType, consecutiveDays, totalAchievements}
|
||||||
|
|
||||||
|
NotificationUC -> GoalAdapter: getMissionDetails(missionId)
|
||||||
|
GoalAdapter -> NotificationUC: 미션 정보 및 사용자 진행 상황
|
||||||
|
|
||||||
|
NotificationUC -> NotificationDomainService: createCelebrationMessage(celebrationData)
|
||||||
|
NotificationDomainService -> NotificationDomainService: prepareCelebrationPrompt(data)
|
||||||
|
note right: **축하 메시지 프롬프트**\n- 달성 미션 정보\n- 연속 달성 일수\n- 동기부여 톤앤매너
|
||||||
|
|
||||||
|
NotificationDomainService -> ClaudeAdapter: requestCelebrationMessage(prompt)
|
||||||
|
ClaudeAdapter -> ClaudeAPI: AI 축하 메시지 요청
|
||||||
|
ClaudeAPI -> ClaudeAdapter: AI 생성 축하 메시지
|
||||||
|
|
||||||
|
NotificationDomainService -> NotificationDomainService: enhanceCelebrationMessage(aiMessage, achievementData)
|
||||||
|
note right: **축하 메시지 강화**\n- 달성 배지 정보 추가\n- 건강 효과 설명\n- 다음 마일스톤 안내
|
||||||
|
|
||||||
|
NotificationUC -> NotificationCtrl: CelebrationResponse 반환
|
||||||
|
note right: {congratsMessage, achievementBadge, healthBenefit, nextMilestone, encouragementLevel, visualEffect}
|
||||||
|
|
||||||
|
== 6. POST /api/intelligence/notifications/encouragement (독려 메시지) ==
|
||||||
|
|
||||||
|
NotificationCtrl -> NotificationUC: generateEncouragementMessage(encouragementRequest)
|
||||||
|
note right: {userId, missionsStatus}
|
||||||
|
|
||||||
|
NotificationUC -> GoalAdapter: getUserDailyProgress(userId)
|
||||||
|
GoalAdapter -> NotificationUC: 일일 진행 상황
|
||||||
|
|
||||||
|
NotificationUC -> NotificationDomainService: analyzeProgressPattern(userId, missionsStatus, dailyProgress)
|
||||||
|
NotificationDomainService -> NotificationDomainService: calculateProgressLevel(data)
|
||||||
|
NotificationDomainService -> NotificationDomainService: identifyFailurePoints(missionsStatus)
|
||||||
|
|
||||||
|
NotificationUC -> CacheAdapter: getCachedEncouragementMessage(userId, progressLevel)
|
||||||
|
CacheAdapter -> Redis: GET encouragement:{userId}:{progressLevel}
|
||||||
|
Redis -> CacheAdapter: 캐시된 독려 메시지 또는 null
|
||||||
|
|
||||||
|
alt 캐시 미스인 경우
|
||||||
|
NotificationDomainService -> NotificationDomainService: createEncouragementPrompt(progressAnalysis)
|
||||||
|
NotificationDomainService -> ClaudeAdapter: requestEncouragementMessage(prompt)
|
||||||
|
ClaudeAdapter -> ClaudeAPI: AI 독려 메시지 요청
|
||||||
|
ClaudeAPI -> ClaudeAdapter: AI 생성 독려 메시지
|
||||||
|
|
||||||
|
NotificationUC -> CacheAdapter: cacheEncouragementMessage(userId, progressLevel, message)
|
||||||
|
CacheAdapter -> Redis: SETEX encouragement:{userId}:{progressLevel} 1800 {message}
|
||||||
|
end
|
||||||
|
|
||||||
|
NotificationUC -> NotificationCtrl: EncouragementResponse 반환
|
||||||
|
note right: {message, motivationType, timing, personalizedTip, priority}
|
||||||
|
|
||||||
|
== 7. POST /api/intelligence/batch/notifications (배치 알림 처리) ==
|
||||||
|
|
||||||
|
BatchCtrl -> BatchUC: processBatchNotifications(batchRequest)
|
||||||
|
note right: {triggerTime, targetUsers, notificationType}
|
||||||
|
|
||||||
|
BatchUC -> GoalAdapter: getAllUsersProgress(targetUsers)
|
||||||
|
GoalAdapter -> BatchUC: 모든 대상 사용자의 진행 상황
|
||||||
|
|
||||||
|
loop 사용자별 처리
|
||||||
|
BatchUC -> NotificationDomainService: generatePersonalizedNotification(user, progress)
|
||||||
|
NotificationDomainService -> ClaudeAdapter: requestBatchNotification(personalizedPrompt)
|
||||||
|
ClaudeAdapter -> ClaudeAPI: AI 개인화 알림 요청
|
||||||
|
ClaudeAPI -> ClaudeAdapter: AI 생성 개인화 알림
|
||||||
|
end
|
||||||
|
|
||||||
|
BatchUC -> EventAdapter: publishBatchNotificationEvents(processedNotifications)
|
||||||
|
EventAdapter -> ServiceBus: 배치 알림 이벤트 발행
|
||||||
|
|
||||||
|
BatchUC -> BatchCtrl: BatchNotificationResponse 반환
|
||||||
|
note right: {processedCount, successCount, failedCount, nextScheduledTime}
|
||||||
|
|
||||||
|
== 예외 처리 ==
|
||||||
|
|
||||||
|
alt Claude API 호출 실패
|
||||||
|
ClaudeAdapter -> AiAnalysisDomainSvc: ClaudeApiException
|
||||||
|
AiAnalysisDomainSvc -> HealthAnalysisUC: 기본 메시지 사용
|
||||||
|
note right: Fallback 메시지로 대체
|
||||||
|
end
|
||||||
|
|
||||||
|
alt 캐시 접근 실패
|
||||||
|
CacheAdapter -> ChatUC: CacheAccessException
|
||||||
|
ChatUC -> ChatRepo: DB에서 직접 조회
|
||||||
|
end
|
||||||
|
|
||||||
|
alt 세션 만료
|
||||||
|
ChatDomainService -> ChatUC: SessionExpiredException
|
||||||
|
ChatUC -> ChatCtrl: 새 세션 생성 안내
|
||||||
|
end
|
||||||
|
|
||||||
|
== 성능 최적화 및 모니터링 ==
|
||||||
|
|
||||||
|
note over NotificationUC, CacheAdapter
|
||||||
|
**Claude AI 연동 최적화**
|
||||||
|
- 응답 캐싱으로 API 호출 최소화
|
||||||
|
- Circuit Breaker 패턴 적용
|
||||||
|
- Fallback 메시지 준비
|
||||||
|
- 배치 처리 시 우선순위 기반
|
||||||
|
- API 응답 시간 모니터링
|
||||||
|
- 캐시 히트율 추적
|
||||||
|
end note
|
||||||
177
design/내부 시퀀스 설계서 - User Service.txt
Normal file
177
design/내부 시퀀스 설계서 - User Service.txt
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam sequenceArrowThickness 2
|
||||||
|
skinparam sequenceParticipantBorderThickness 2
|
||||||
|
skinparam sequenceActorBorderThickness 2
|
||||||
|
skinparam sequenceGroupBorderThickness 2
|
||||||
|
|
||||||
|
title User Service 내부 시퀀스 다이어그램 (역설계 - 개발 소스 기반)
|
||||||
|
|
||||||
|
participant "AuthController" as AuthCtrl
|
||||||
|
participant "UserController" as UserCtrl
|
||||||
|
participant "AuthUseCase" as AuthUC
|
||||||
|
participant "UserUseCase" as UserUC
|
||||||
|
participant "AuthDomainService" as AuthDomainSvc
|
||||||
|
participant "UserDomainService" as UserDomainSvc
|
||||||
|
participant "UserRepository" as UserRepo
|
||||||
|
participant "OccupationRepository" as OccupationRepo
|
||||||
|
participant "GoogleAuthAdapter" as AuthACL
|
||||||
|
participant "JwtTokenAdapter" as JwtAdapter
|
||||||
|
participant "EventPublisherAdapter" as EventPub
|
||||||
|
participant "PostgreSQL" as DB
|
||||||
|
participant "Google SSO" as GoogleSSO
|
||||||
|
participant "Azure Service Bus" as ServiceBus
|
||||||
|
|
||||||
|
== 1. POST /api/users/auth/google-login (구글 로그인) ==
|
||||||
|
|
||||||
|
AuthCtrl -> AuthUC: authenticateWithGoogle(googleLoginRequest)
|
||||||
|
note right: {googleAccessToken, googleIdToken}
|
||||||
|
|
||||||
|
AuthUC -> AuthDomainSvc: validateGoogleTokens(accessToken, idToken)
|
||||||
|
AuthDomainSvc -> AuthACL: verifyGoogleToken(accessToken, idToken)
|
||||||
|
AuthACL -> GoogleSSO: Google Token Verification API
|
||||||
|
GoogleSSO -> AuthACL: Verified User Info
|
||||||
|
AuthACL -> AuthDomainSvc: GoogleUserInfo 응답
|
||||||
|
|
||||||
|
AuthDomainSvc -> UserRepo: findByGoogleId(googleId)
|
||||||
|
UserRepo -> DB: SELECT * FROM users WHERE google_id = ?
|
||||||
|
DB -> UserRepo: User 엔티티 또는 null
|
||||||
|
|
||||||
|
alt 신규 사용자인 경우
|
||||||
|
AuthDomainSvc -> AuthDomainSvc: createNewUser(googleUserInfo)
|
||||||
|
AuthDomainSvc -> UserRepo: saveUser(newUser)
|
||||||
|
UserRepo -> DB: INSERT INTO users
|
||||||
|
DB -> UserRepo: 저장 완료
|
||||||
|
|
||||||
|
AuthUC -> EventPub: publishUserRegisteredEvent(user)
|
||||||
|
EventPub -> ServiceBus: 신규 사용자 등록 이벤트 발행
|
||||||
|
end
|
||||||
|
|
||||||
|
AuthUC -> JwtAdapter: generateTokens(user)
|
||||||
|
JwtAdapter -> AuthUC: {accessToken, refreshToken}
|
||||||
|
|
||||||
|
AuthUC -> UserRepo: updateLastLoginAt(user.id)
|
||||||
|
UserRepo -> DB: UPDATE users SET last_login_at = NOW()
|
||||||
|
DB -> UserRepo: 업데이트 완료
|
||||||
|
|
||||||
|
AuthUC -> AuthCtrl: LoginResponse 반환
|
||||||
|
note right: {accessToken, refreshToken, userId, isNewUser, message}
|
||||||
|
|
||||||
|
== 2. POST /api/users/profile/complete (프로필 완료) ==
|
||||||
|
|
||||||
|
AuthCtrl -> UserUC: completeUserProfile(userRegistrationRequest)
|
||||||
|
note right: JWT에서 추출한 userId + {name, birthDate, occupation}
|
||||||
|
|
||||||
|
UserUC -> UserDomainSvc: validateProfileData(profileData)
|
||||||
|
UserDomainSvc -> OccupationRepo: validateOccupationCode(occupation)
|
||||||
|
OccupationRepo -> DB: SELECT * FROM occupation_types WHERE occupation_code = ?
|
||||||
|
DB -> OccupationRepo: Occupation 엔티티
|
||||||
|
|
||||||
|
UserDomainSvc -> UserRepo: findById(userId)
|
||||||
|
UserRepo -> DB: SELECT * FROM users WHERE id = ?
|
||||||
|
DB -> UserRepo: User 엔티티
|
||||||
|
|
||||||
|
UserDomainSvc -> UserDomainSvc: updateUserProfile(user, profileData)
|
||||||
|
UserDomainSvc -> UserRepo: updateUser(updatedUser)
|
||||||
|
UserRepo -> DB: UPDATE users SET name = ?, birth_date = ?, occupation = ?, updated_at = NOW()
|
||||||
|
DB -> UserRepo: 업데이트 완료
|
||||||
|
|
||||||
|
UserUC -> EventPub: publishUserProfileUpdatedEvent(user)
|
||||||
|
EventPub -> ServiceBus: 회원정보 업데이트 이벤트 발행
|
||||||
|
|
||||||
|
UserUC -> AuthCtrl: UserRegistrationResponse 반환
|
||||||
|
note right: {userId, message, status, profileCompletedAt}
|
||||||
|
|
||||||
|
== 3. GET /api/users/profile (사용자 기본 정보 조회) ==
|
||||||
|
|
||||||
|
AuthCtrl -> UserUC: getUserProfile(userId)
|
||||||
|
note right: JWT에서 추출한 userId
|
||||||
|
|
||||||
|
UserUC -> UserRepo: findById(userId)
|
||||||
|
UserRepo -> DB: SELECT * FROM users WHERE id = ?
|
||||||
|
DB -> UserRepo: User 엔티티
|
||||||
|
|
||||||
|
UserUC -> UserDomainSvc: calculateAge(user.birthDate)
|
||||||
|
note right: 생년월일로부터 나이 계산
|
||||||
|
|
||||||
|
UserUC -> OccupationRepo: findOccupationName(user.occupation)
|
||||||
|
OccupationRepo -> DB: SELECT occupation_name FROM occupation_types WHERE occupation_code = ?
|
||||||
|
DB -> OccupationRepo: 직업명
|
||||||
|
|
||||||
|
UserUC -> AuthCtrl: UserProfileResponse 반환
|
||||||
|
note right: {userId, name, age, occupation, registeredAt, lastLoginAt}
|
||||||
|
|
||||||
|
== 4. GET /api/users/occupations (직업 목록 조회) ==
|
||||||
|
|
||||||
|
UserCtrl -> UserUC: getAllOccupations()
|
||||||
|
|
||||||
|
UserUC -> OccupationRepo: findAllOccupations()
|
||||||
|
OccupationRepo -> DB: SELECT * FROM occupation_types ORDER BY category, occupation_name
|
||||||
|
DB -> OccupationRepo: Occupation 목록
|
||||||
|
|
||||||
|
UserUC -> UserCtrl: OccupationListResponse 반환
|
||||||
|
note right: {occupations: [{occupationCode, occupationName, category}], totalCount}
|
||||||
|
|
||||||
|
== 5. GET /api/users/occupations/name (직업명 조회) ==
|
||||||
|
|
||||||
|
UserCtrl -> UserUC: getOccupationName(occupationCode)
|
||||||
|
|
||||||
|
UserUC -> OccupationRepo: findOccupationByCode(occupationCode)
|
||||||
|
OccupationRepo -> DB: SELECT * FROM occupation_types WHERE occupation_code = ?
|
||||||
|
DB -> OccupationRepo: Occupation 엔티티
|
||||||
|
|
||||||
|
UserUC -> UserCtrl: OccupationNameResponse 반환
|
||||||
|
note right: {occupationCode, occupationName}
|
||||||
|
|
||||||
|
== 6. GET /api/users/occupations/code (직업코드 조회) ==
|
||||||
|
|
||||||
|
UserCtrl -> UserUC: getOccupationCode(occupationName)
|
||||||
|
|
||||||
|
UserUC -> OccupationRepo: findOccupationByName(occupationName)
|
||||||
|
OccupationRepo -> DB: SELECT * FROM occupation_types WHERE occupation_name = ?
|
||||||
|
DB -> OccupationRepo: Occupation 엔티티
|
||||||
|
|
||||||
|
UserUC -> UserCtrl: OccupationCodeResponse 반환
|
||||||
|
note right: {occupationCode, occupationName}
|
||||||
|
|
||||||
|
== 예외 처리 ==
|
||||||
|
|
||||||
|
alt 인증 실패 시
|
||||||
|
AuthACL -> AuthUC: AuthenticationException
|
||||||
|
AuthUC -> AuthCtrl: 401 Unauthorized 응답
|
||||||
|
end
|
||||||
|
|
||||||
|
alt 사용자 정보 없음
|
||||||
|
UserRepo -> UserUC: UserNotFoundException
|
||||||
|
UserUC -> AuthCtrl: 404 Not Found 응답
|
||||||
|
end
|
||||||
|
|
||||||
|
alt 입력 데이터 검증 실패
|
||||||
|
UserDomainSvc -> UserUC: ValidationException
|
||||||
|
UserUC -> AuthCtrl: 400 Bad Request 응답
|
||||||
|
note right: 유효성 검증 오류 메시지 포함
|
||||||
|
end
|
||||||
|
|
||||||
|
alt 직업 코드 없음
|
||||||
|
OccupationRepo -> UserUC: OccupationNotFoundException
|
||||||
|
UserUC -> UserCtrl: 404 Not Found 응답
|
||||||
|
end
|
||||||
|
|
||||||
|
== 트랜잭션 처리 ==
|
||||||
|
|
||||||
|
note over UserUC, UserRepo
|
||||||
|
**트랜잭션 범위**
|
||||||
|
- 사용자 생성/수정 작업
|
||||||
|
- 이벤트 발행은 트랜잭션 커밋 후
|
||||||
|
- @Transactional 어노테이션 적용
|
||||||
|
end note
|
||||||
|
|
||||||
|
== 보안 처리 ==
|
||||||
|
|
||||||
|
note over AuthUC, AuthACL
|
||||||
|
**보안 고려사항**
|
||||||
|
- JWT 토큰 생성 시 적절한 만료시간 설정
|
||||||
|
- Google SSO 응답 데이터 검증
|
||||||
|
- 개인정보 로깅 제외
|
||||||
|
- 비밀번호 없이 OAuth만 사용
|
||||||
|
end note
|
||||||
172
design/논리 아키텍처.txt
Normal file
172
design/논리 아키텍처.txt
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam componentStyle rectangle
|
||||||
|
skinparam componentFontSize 12
|
||||||
|
skinparam arrowFontSize 11
|
||||||
|
skinparam arrowThickness 2
|
||||||
|
|
||||||
|
title HealthSync 역설계 논리 아키텍처 (개발 소스 기반)
|
||||||
|
|
||||||
|
' 클라이언트 (Backends for Frontends 패턴)
|
||||||
|
package "클라이언트 계층" #lightcyan {
|
||||||
|
[React 모바일 웹앱] as MobileApp
|
||||||
|
}
|
||||||
|
|
||||||
|
' API Gateway & Service Mesh (Gateway Routing 패턴)
|
||||||
|
package "Gateway 계층" #lightyellow {
|
||||||
|
[API Gateway\n(Spring Cloud Gateway)] as APIGateway
|
||||||
|
[Service Mesh (Istio)] as ServiceMesh
|
||||||
|
}
|
||||||
|
|
||||||
|
' Clean Architecture 기반 마이크로서비스
|
||||||
|
package "사용자 컨텍스트" #lightgreen {
|
||||||
|
[User Service\n(Clean Architecture)] as UserService
|
||||||
|
note right of UserService
|
||||||
|
**주요 기능**
|
||||||
|
• Google SSO 인증
|
||||||
|
• 사용자 프로필 관리
|
||||||
|
• 직업군별 코드 변환
|
||||||
|
• JWT 토큰 생성/검증
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "건강 컨텍스트" #lightgreen {
|
||||||
|
[Health Service\n(Clean Architecture)] as HealthService
|
||||||
|
note right of HealthService
|
||||||
|
**주요 기능**
|
||||||
|
• 건강검진 데이터 연동
|
||||||
|
• 정상치 기준 비교 분석
|
||||||
|
• Azure Blob 파일 업로드
|
||||||
|
• 건강 위험도 계산
|
||||||
|
• 캐싱 기반 성능 최적화
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "목표 컨텍스트" #lightgreen {
|
||||||
|
[Goal Service\n(Clean Architecture)] as GoalService
|
||||||
|
note right of GoalService
|
||||||
|
**주요 기능**
|
||||||
|
• 미션 선택/관리
|
||||||
|
• 목표 달성 추적
|
||||||
|
• 이력 분석
|
||||||
|
• 진행률 계산
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "지능형서비스 컨텍스트" #lightgreen {
|
||||||
|
[Intelligence Service\n(Clean Architecture)] as IntelligenceService
|
||||||
|
note right of IntelligenceService
|
||||||
|
**주요 기능**
|
||||||
|
• Claude AI 기반 건강 진단
|
||||||
|
• 개인화 미션 추천
|
||||||
|
• 챗봇 상담
|
||||||
|
• 독려 메시지 생성
|
||||||
|
• 배치 알림 처리
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
' 통합 데이터 저장소
|
||||||
|
package "데이터 저장소" #lightpink {
|
||||||
|
[PostgreSQL\n(통합 DB)] as PostgreSQL
|
||||||
|
[Redis Cache] as Redis
|
||||||
|
[Azure Blob Storage\n(건강검진 파일)] as BlobStorage
|
||||||
|
|
||||||
|
note right of PostgreSQL
|
||||||
|
**통합 스키마**
|
||||||
|
• User 테이블
|
||||||
|
• Health 관련 테이블
|
||||||
|
• Goal 관련 테이블
|
||||||
|
• Normal Range 테이블 (신규)
|
||||||
|
• Chat History 테이블
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
' 메시징 인프라 (Event-Driven Architecture)
|
||||||
|
package "메시징 인프라" #lightsalmon {
|
||||||
|
[Azure Service Bus\n(Event Store)] as EventStore
|
||||||
|
[Command Queue] as CommandQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
' 외부 시스템 연동 (Anti-Corruption Layer 패턴)
|
||||||
|
package "외부 시스템" #lightblue {
|
||||||
|
[Google SSO] as GoogleSSO
|
||||||
|
[Claude API] as ClaudeAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
package "보호 계층" #orange {
|
||||||
|
[Auth ACL] as AuthACL
|
||||||
|
[AI Service ACL] as AIACL
|
||||||
|
}
|
||||||
|
|
||||||
|
' === 핵심 처리 흐름 (개발 소스 기반) ===
|
||||||
|
|
||||||
|
' 1. 클라이언트 요청
|
||||||
|
MobileApp -[#blue,thickness=3]-> APIGateway : "1. RESTful API 요청"
|
||||||
|
|
||||||
|
' 2. Gateway Routing
|
||||||
|
APIGateway -[#green,thickness=2]-> ServiceMesh : "2. 서비스 라우팅 & 로드밸런싱"
|
||||||
|
|
||||||
|
' 3. Clean Architecture 기반 서비스 호출
|
||||||
|
ServiceMesh -[#purple,thickness=2]-> UserService : "3a. /api/users/* - 인증 & 프로필"
|
||||||
|
ServiceMesh -[#purple,thickness=2]-> HealthService : "3b. /api/health/* - 건강데이터"
|
||||||
|
ServiceMesh -[#purple,thickness=2]-> GoalService : "3c. /api/goals/* - 목표관리"
|
||||||
|
ServiceMesh -[#purple,thickness=2]-> IntelligenceService : "3d. /api/intelligence/* - AI 분석"
|
||||||
|
|
||||||
|
' 4. 통합 데이터 저장 (PostgreSQL)
|
||||||
|
UserService -[#red,thickness=2]-> PostgreSQL : "4a. 사용자 & 직업코드 데이터"
|
||||||
|
HealthService -[#red,thickness=2]-> PostgreSQL : "4b. 건강검진 & 정상치 데이터"
|
||||||
|
GoalService -[#red,thickness=2]-> PostgreSQL : "4c. 목표 & 미션 데이터"
|
||||||
|
IntelligenceService -[#red,thickness=2]-> PostgreSQL : "4d. 채팅 & 분석 이력"
|
||||||
|
|
||||||
|
' 5. Azure Blob Storage 연동
|
||||||
|
HealthService -[#brown,thickness=2]-> BlobStorage : "5. 건강검진 파일 저장"
|
||||||
|
|
||||||
|
' 6. Redis 캐싱 (Cache-Aside 패턴)
|
||||||
|
HealthService -[#orange,thickness=2]-> Redis : "6a. 건강이력 캐싱 (1시간)"
|
||||||
|
IntelligenceService -[#orange,thickness=2]-> Redis : "6b. AI 분석결과 캐싱 (30분)"
|
||||||
|
GoalService -[#orange,thickness=2]-> Redis : "6c. 활성미션 캐싱 (30분)"
|
||||||
|
|
||||||
|
' 7. 외부 시스템 연동 (ACL 패턴)
|
||||||
|
UserService -[#navy,thickness=2]-> AuthACL : "7a. Google 인증 요청"
|
||||||
|
AuthACL -[#navy,thickness=2]-> GoogleSSO : "7b. OAuth 2.0 인증"
|
||||||
|
IntelligenceService -[#navy,thickness=2]-> AIACL : "7c. Claude AI 요청"
|
||||||
|
AIACL -[#navy,thickness=2]-> ClaudeAPI : "7d. AI 분석/채팅 API"
|
||||||
|
|
||||||
|
' 8. 이벤트 기반 통신
|
||||||
|
UserService -[#magenta,thickness=2,dashed]-> EventStore : "8a. 사용자 등록/수정 이벤트"
|
||||||
|
HealthService -[#magenta,thickness=2,dashed]-> EventStore : "8b. 건강데이터 동기화 이벤트"
|
||||||
|
GoalService -[#magenta,thickness=2,dashed]-> EventStore : "8c. 목표달성 이벤트"
|
||||||
|
|
||||||
|
' 9. Command/Query 분리
|
||||||
|
EventStore -[#cyan,thickness=2,dotted]-> CommandQueue : "9a. 배치 명령 큐"
|
||||||
|
CommandQueue -[#cyan,thickness=2,dotted]-> IntelligenceService : "9b. 주기적 알림 처리"
|
||||||
|
|
||||||
|
' === 패턴 적용 노트 ===
|
||||||
|
note right of APIGateway
|
||||||
|
**Gateway 패턴**
|
||||||
|
• JWT 토큰 검증
|
||||||
|
• Rate Limiting
|
||||||
|
• CORS 처리
|
||||||
|
• 로드밸런싱
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of PostgreSQL
|
||||||
|
**통합 DB 패턴**
|
||||||
|
• 마이크로서비스별 스키마 분리
|
||||||
|
• 트랜잭션 일관성
|
||||||
|
• 정상치 기준 중앙 관리
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of Redis
|
||||||
|
**캐싱 패턴**
|
||||||
|
• Cache-Aside
|
||||||
|
• TTL 기반 만료
|
||||||
|
• 성능 최적화
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of EventStore
|
||||||
|
**이벤트 패턴**
|
||||||
|
• 비동기 통신
|
||||||
|
• 서비스 간 결합도 최소화
|
||||||
|
• 확장성 확보
|
||||||
|
end note
|
||||||
291
design/데이터설계서 - Goal Service.txt
Normal file
291
design/데이터설계서 - Goal Service.txt
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam classAttributeIconSize 0
|
||||||
|
skinparam classFontSize 12
|
||||||
|
skinparam classAttributeFontSize 11
|
||||||
|
|
||||||
|
title HealthSync 역설계 - Goal Service 데이터 설계서
|
||||||
|
|
||||||
|
package "Goal Service Database Schema" as goal_db #lightcoral {
|
||||||
|
|
||||||
|
entity "user_mission_goals" as user_mission_goal {
|
||||||
|
* goal_id : VARCHAR(50) <<PK>>
|
||||||
|
--
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* is_active : BOOLEAN
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
* updated_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_user_id_active
|
||||||
|
+ 인덱스: idx_created_at
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "user_missions" as user_mission {
|
||||||
|
* id : BIGINT <<PK>>
|
||||||
|
--
|
||||||
|
* goal_id : VARCHAR(50) <<FK>>
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* mission_id : VARCHAR(50)
|
||||||
|
* mission_title : VARCHAR(200)
|
||||||
|
* mission_description : TEXT
|
||||||
|
* category : VARCHAR(50)
|
||||||
|
* difficulty : VARCHAR(20)
|
||||||
|
* estimated_time_minutes : INTEGER
|
||||||
|
* is_active : BOOLEAN
|
||||||
|
* current_streak_days : INTEGER
|
||||||
|
* total_completed_count : INTEGER
|
||||||
|
* start_date : DATE
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
* updated_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_goal_id
|
||||||
|
+ 인덱스: idx_user_id_active
|
||||||
|
+ 인덱스: idx_mission_id
|
||||||
|
+ 인덱스: idx_category
|
||||||
|
+ 인덱스: idx_start_date
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "mission_progress" as mission_progress {
|
||||||
|
* id : BIGINT <<PK>>
|
||||||
|
--
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* mission_id : VARCHAR(50) <<FK>>
|
||||||
|
* completed_at : TIMESTAMP
|
||||||
|
* notes : TEXT
|
||||||
|
* earned_points : INTEGER
|
||||||
|
* consecutive_days : INTEGER
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_user_mission_date
|
||||||
|
+ 인덱스: idx_completed_at
|
||||||
|
+ 인덱스: idx_mission_id
|
||||||
|
+ 인덱스: idx_earned_points
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "mission_completion_history" as mission_completion_history {
|
||||||
|
* id : BIGINT <<PK>>
|
||||||
|
--
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* mission_id : VARCHAR(50)
|
||||||
|
* goal_id : VARCHAR(50)
|
||||||
|
* completion_date : DATE
|
||||||
|
* completion_time : TIME
|
||||||
|
* completion_status : VARCHAR(20)
|
||||||
|
* notes : TEXT
|
||||||
|
* earned_points : INTEGER
|
||||||
|
* streak_days_at_completion : INTEGER
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_user_id_date
|
||||||
|
+ 인덱스: idx_mission_completion
|
||||||
|
+ 인덱스: idx_goal_id_date
|
||||||
|
}
|
||||||
|
|
||||||
|
note right of user_mission_goal
|
||||||
|
**사용자 목표 관리**
|
||||||
|
• 사용자별 목표 설정 단위
|
||||||
|
• 한 번에 하나의 활성 목표만 가능
|
||||||
|
• 목표 재설정 시 기존 목표 비활성화
|
||||||
|
• goal_id는 UUID 형태로 생성
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of user_mission
|
||||||
|
**사용자별 미션 정보**
|
||||||
|
• 목표에 포함된 개별 미션들
|
||||||
|
• Intelligence Service에서 추천받은 미션
|
||||||
|
• 최대 5개 미션까지 선택 가능
|
||||||
|
• 난이도별 포인트 차등 적용
|
||||||
|
• 연속 달성 일수 추적
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of mission_progress
|
||||||
|
**미션 수행 기록**
|
||||||
|
• 일별 미션 완료 기록
|
||||||
|
• 같은 날 중복 완료 방지
|
||||||
|
• 연속 달성 일수 계산 기준
|
||||||
|
• 포인트 적립 내역
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of mission_completion_history
|
||||||
|
**미션 완료 이력 (분석용)**
|
||||||
|
• 장기간 통계 분석용 테이블
|
||||||
|
• 사용자별 달성 패턴 분석
|
||||||
|
• 미션별 효과성 분석
|
||||||
|
• 시간대별 완료 패턴 추적
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "관계 정의" as relationships {
|
||||||
|
user_mission_goal ||--o{ user_mission : goal_id
|
||||||
|
user_mission ||--o{ mission_progress : mission_id
|
||||||
|
user_mission ||--o{ mission_completion_history : mission_id
|
||||||
|
user_mission_goal ||--o{ mission_completion_history : goal_id
|
||||||
|
|
||||||
|
note as n1
|
||||||
|
**외래키 관계**
|
||||||
|
user_missions.goal_id → user_mission_goals.goal_id
|
||||||
|
mission_progress.mission_id → user_missions.mission_id
|
||||||
|
mission_completion_history.mission_id → user_missions.mission_id
|
||||||
|
mission_completion_history.goal_id → user_mission_goals.goal_id
|
||||||
|
|
||||||
|
**참조 무결성**
|
||||||
|
• CASCADE 업데이트
|
||||||
|
• RESTRICT 삭제 (이력 보존)
|
||||||
|
|
||||||
|
**논리적 관계**
|
||||||
|
• User Service users ↔ user_mission_goals (사용자별 목표)
|
||||||
|
• Intelligence Service ↔ user_missions (미션 추천)
|
||||||
|
• 한 사용자는 하나의 활성 목표만 보유
|
||||||
|
• 하나의 목표는 여러 미션을 포함
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "데이터 타입 및 제약조건" as constraints {
|
||||||
|
note as n2
|
||||||
|
**user_mission_goals 제약조건**
|
||||||
|
• goal_id: UUID 형태, PRIMARY KEY
|
||||||
|
• user_id: User Service와 일치, NOT NULL
|
||||||
|
• is_active: DEFAULT TRUE
|
||||||
|
• 사용자당 활성 목표는 1개만 (UNIQUE 제약)
|
||||||
|
|
||||||
|
**user_missions 제약조건**
|
||||||
|
• id: AUTO_INCREMENT
|
||||||
|
• goal_id: NOT NULL, 존재하는 goal_id
|
||||||
|
• mission_id: Intelligence Service에서 제공
|
||||||
|
• mission_title: 최대 200자, NOT NULL
|
||||||
|
• category: 'EXERCISE', 'NUTRITION', 'STRESS_MANAGEMENT', 'SLEEP', 'PREVENTIVE_CARE'
|
||||||
|
• difficulty: 'EASY', 'MEDIUM', 'HARD'
|
||||||
|
• estimated_time_minutes: 1~180분 범위
|
||||||
|
• current_streak_days: 0 이상
|
||||||
|
• total_completed_count: 0 이상
|
||||||
|
• 목표당 최대 5개 미션 (체크 제약)
|
||||||
|
|
||||||
|
**mission_progress 제약조건**
|
||||||
|
• id: AUTO_INCREMENT
|
||||||
|
• user_id: NOT NULL
|
||||||
|
• mission_id: 존재하는 mission_id
|
||||||
|
• completed_at: NOT NULL
|
||||||
|
• earned_points: 0 이상
|
||||||
|
• consecutive_days: 1 이상
|
||||||
|
• 같은 날 같은 미션 중복 완료 방지 (UNIQUE 제약)
|
||||||
|
|
||||||
|
**mission_completion_history 제약조건**
|
||||||
|
• completion_status: 'COMPLETED', 'PARTIAL', 'SKIPPED'
|
||||||
|
• earned_points: 0 이상
|
||||||
|
• streak_days_at_completion: 0 이상
|
||||||
|
• completion_time: 00:00~23:59
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "미션 카테고리 및 난이도" as mission_categories {
|
||||||
|
note as n3
|
||||||
|
**미션 카테고리별 예시**
|
||||||
|
|
||||||
|
**EXERCISE (운동)**
|
||||||
|
• 계단 오르기 10분
|
||||||
|
• 산책 30분
|
||||||
|
• 스트레칭 15분
|
||||||
|
• 홈트레이닝 20분
|
||||||
|
|
||||||
|
**NUTRITION (영양)**
|
||||||
|
• 물 8잔 마시기
|
||||||
|
• 금연하기
|
||||||
|
• 금주하기
|
||||||
|
• 건강한 간식 선택
|
||||||
|
|
||||||
|
**STRESS_MANAGEMENT (스트레스 관리)**
|
||||||
|
• 명상 10분
|
||||||
|
• 심호흡 연습
|
||||||
|
• 일기 쓰기
|
||||||
|
• 취미 시간 갖기
|
||||||
|
|
||||||
|
**SLEEP (수면)**
|
||||||
|
• 규칙적인 취침시간
|
||||||
|
• 11시 전 잠들기
|
||||||
|
• 수면 8시간 유지
|
||||||
|
• 카페인 섭취 줄이기
|
||||||
|
|
||||||
|
**PREVENTIVE_CARE (예방관리)**
|
||||||
|
• 혈압 측정하기
|
||||||
|
• 체중 기록하기
|
||||||
|
• 건강검진 예약
|
||||||
|
• 병원 방문하기
|
||||||
|
|
||||||
|
**난이도별 포인트**
|
||||||
|
• EASY: 10~20 포인트
|
||||||
|
• MEDIUM: 25~40 포인트
|
||||||
|
• HARD: 50~80 포인트
|
||||||
|
• 연속 달성 보너스: +5 포인트/일
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "성능 및 운영 고려사항" as performance {
|
||||||
|
note as n4
|
||||||
|
**인덱스 전략**
|
||||||
|
• user_mission_goals: (user_id, is_active) 복합 인덱스
|
||||||
|
• user_missions: (user_id, is_active) 복합 인덱스
|
||||||
|
• mission_progress: (user_id, mission_id, DATE(completed_at)) 복합 인덱스
|
||||||
|
• mission_completion_history: (user_id, completion_date) 복합 인덱스
|
||||||
|
|
||||||
|
**파티셔닝**
|
||||||
|
• mission_progress: completed_at 기준 월별 파티셔닝
|
||||||
|
• mission_completion_history: completion_date 기준 월별 파티셔닝
|
||||||
|
|
||||||
|
**아카이빙**
|
||||||
|
• 1년 이상 된 mission_progress 데이터 아카이빙
|
||||||
|
• 비활성 목표 데이터는 history 테이블로 이관
|
||||||
|
|
||||||
|
**캐싱 전략**
|
||||||
|
• 활성 미션 목록: Redis 캐싱 (30분)
|
||||||
|
• 오늘 완료한 미션: Redis 캐싱 (실시간)
|
||||||
|
• 연속 달성 일수: Redis 캐싱 (6시간)
|
||||||
|
• 미션별 통계: Redis 캐싱 (1시간)
|
||||||
|
|
||||||
|
**배치 처리**
|
||||||
|
• 매일 자정에 연속 달성 일수 재계산
|
||||||
|
• 주간/월간 달성률 통계 생성
|
||||||
|
• 장기 미완료 미션 알림 발송
|
||||||
|
|
||||||
|
**모니터링 지표**
|
||||||
|
• 일일 미션 완료율
|
||||||
|
• 평균 연속 달성 일수
|
||||||
|
• 카테고리별 인기도
|
||||||
|
• 사용자별 참여율
|
||||||
|
• 목표 재설정 빈도
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "데이터 분석 및 인사이트" as analytics {
|
||||||
|
note as n5
|
||||||
|
**사용자 행동 분석**
|
||||||
|
• 미션 완료 시간대 패턴
|
||||||
|
• 요일별 달성률 차이
|
||||||
|
• 연속 달성 중단 요인 분석
|
||||||
|
• 난이도별 지속 가능성
|
||||||
|
|
||||||
|
**미션 효과성 분석**
|
||||||
|
• 카테고리별 건강 개선 효과
|
||||||
|
• 직업군별 선호 미션 유형
|
||||||
|
• 연령대별 적합한 미션 난이도
|
||||||
|
• 계절별 미션 선호도 변화
|
||||||
|
|
||||||
|
**개인화 추천 개선**
|
||||||
|
• 과거 달성 패턴 기반 추천
|
||||||
|
• 유사 사용자 그룹 분석
|
||||||
|
• 실패 미션 유형 회피
|
||||||
|
• 성공 확률 높은 미션 우선 추천
|
||||||
|
|
||||||
|
**동기부여 전략**
|
||||||
|
• 포인트 시스템 효과성
|
||||||
|
• 연속 달성 보상 최적화
|
||||||
|
• 사회적 비교 효과 분석
|
||||||
|
• 축하 메시지 반응 측정
|
||||||
|
|
||||||
|
**KPI 지표**
|
||||||
|
• 월간 활성 사용자 수 (MAU)
|
||||||
|
• 평균 목표 지속 기간
|
||||||
|
• 미션 완료율 (전체/개인별)
|
||||||
|
• 사용자 리텐션율
|
||||||
|
• 건강 개선 지표 상관관계
|
||||||
|
end note
|
||||||
|
}
|
||||||
303
design/데이터설계서 - Health Service.txt
Normal file
303
design/데이터설계서 - Health Service.txt
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam classAttributeIconSize 0
|
||||||
|
skinparam classFontSize 12
|
||||||
|
skinparam classAttributeFontSize 11
|
||||||
|
|
||||||
|
title HealthSync 역설계 - Health Service 데이터 설계서
|
||||||
|
|
||||||
|
package "Health Service Database Schema" as health_db #lightblue {
|
||||||
|
|
||||||
|
entity "health_checkups" as health_checkup {
|
||||||
|
* id : BIGINT <<PK>>
|
||||||
|
--
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* member_serial_number : BIGINT
|
||||||
|
* raw_id : BIGINT <<FK>>
|
||||||
|
* reference_year : INTEGER
|
||||||
|
* height_cm : DECIMAL(5,2)
|
||||||
|
* weight_kg : DECIMAL(5,2)
|
||||||
|
* waist_cm : DECIMAL(5,2)
|
||||||
|
* bmi : DECIMAL(4,2)
|
||||||
|
* visual_acuity_left : DECIMAL(3,1)
|
||||||
|
* visual_acuity_right : DECIMAL(3,1)
|
||||||
|
* hearing_avg : DECIMAL(4,1)
|
||||||
|
* systolic_bp : INTEGER
|
||||||
|
* diastolic_bp : INTEGER
|
||||||
|
* fasting_glucose : INTEGER
|
||||||
|
* total_cholesterol : INTEGER
|
||||||
|
* triglyceride : INTEGER
|
||||||
|
* hdl_cholesterol : INTEGER
|
||||||
|
* ldl_cholesterol : INTEGER
|
||||||
|
* hemoglobin : DECIMAL(4,1)
|
||||||
|
* urine_protein : INTEGER
|
||||||
|
* serum_creatinine : DECIMAL(4,1)
|
||||||
|
* ast : INTEGER
|
||||||
|
* alt : INTEGER
|
||||||
|
* gamma_gtp : INTEGER
|
||||||
|
* smoking_status : INTEGER
|
||||||
|
* drinking_status : INTEGER
|
||||||
|
* risk_level : VARCHAR(20)
|
||||||
|
* abnormal_indicators : JSON
|
||||||
|
* health_score : INTEGER
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
* updated_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_user_id_year
|
||||||
|
+ 인덱스: idx_member_serial_number
|
||||||
|
+ 인덱스: idx_risk_level
|
||||||
|
+ 인덱스: idx_health_score
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "health_checkup_raw" as health_checkup_raw {
|
||||||
|
* raw_id : BIGINT <<PK>>
|
||||||
|
--
|
||||||
|
* member_serial_number : BIGINT
|
||||||
|
* reference_year : INTEGER
|
||||||
|
* birth_date : DATE
|
||||||
|
* name : VARCHAR(50)
|
||||||
|
* region_code : INTEGER
|
||||||
|
* gender_code : INTEGER
|
||||||
|
* age : INTEGER
|
||||||
|
* height : INTEGER
|
||||||
|
* weight : INTEGER
|
||||||
|
* waist_circumference : INTEGER
|
||||||
|
* visual_acuity_left : DECIMAL(3,1)
|
||||||
|
* visual_acuity_right : DECIMAL(3,1)
|
||||||
|
* hearing_left : INTEGER
|
||||||
|
* hearing_right : INTEGER
|
||||||
|
* systolic_bp : INTEGER
|
||||||
|
* diastolic_bp : INTEGER
|
||||||
|
* fasting_glucose : INTEGER
|
||||||
|
* total_cholesterol : INTEGER
|
||||||
|
* triglyceride : INTEGER
|
||||||
|
* hdl_cholesterol : INTEGER
|
||||||
|
* ldl_cholesterol : INTEGER
|
||||||
|
* hemoglobin : DECIMAL(4,1)
|
||||||
|
* urine_protein : INTEGER
|
||||||
|
* serum_creatinine : DECIMAL(4,1)
|
||||||
|
* ast : INTEGER
|
||||||
|
* alt : INTEGER
|
||||||
|
* gamma_gtp : INTEGER
|
||||||
|
* smoking_status : INTEGER
|
||||||
|
* drinking_status : INTEGER
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_member_serial_year
|
||||||
|
+ 인덱스: idx_name_birthdate
|
||||||
|
+ 인덱스: idx_gender_age
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "health_normal_ranges" as health_normal_range {
|
||||||
|
* item_code : VARCHAR(20) <<PK>>
|
||||||
|
* gender_code : INTEGER <<PK>>
|
||||||
|
--
|
||||||
|
* item_name : VARCHAR(100)
|
||||||
|
* normal_min : DECIMAL(10,2)
|
||||||
|
* normal_max : DECIMAL(10,2)
|
||||||
|
* caution_min : DECIMAL(10,2)
|
||||||
|
* caution_max : DECIMAL(10,2)
|
||||||
|
* danger_min : DECIMAL(10,2)
|
||||||
|
* danger_max : DECIMAL(10,2)
|
||||||
|
* unit : VARCHAR(20)
|
||||||
|
* description : TEXT
|
||||||
|
* is_active : BOOLEAN
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
* updated_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_item_code
|
||||||
|
+ 인덱스: idx_gender_code
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "health_files" as health_file {
|
||||||
|
* file_id : VARCHAR(50) <<PK>>
|
||||||
|
--
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* file_name : VARCHAR(255)
|
||||||
|
* file_type : VARCHAR(50)
|
||||||
|
* file_url : VARCHAR(500)
|
||||||
|
* file_size : BIGINT
|
||||||
|
* upload_status : VARCHAR(20)
|
||||||
|
* uploaded_at : TIMESTAMP
|
||||||
|
* processed_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_user_id
|
||||||
|
+ 인덱스: idx_upload_status
|
||||||
|
+ 인덱스: idx_uploaded_at
|
||||||
|
}
|
||||||
|
|
||||||
|
note right of health_checkup
|
||||||
|
**가공된 건강검진 데이터**
|
||||||
|
• User Service의 사용자와 연결
|
||||||
|
• 정상치 기준과 비교 분석 완료
|
||||||
|
• 건강 점수 및 위험도 계산 결과
|
||||||
|
• 이상 항목을 JSON 배열로 저장
|
||||||
|
• 1명당 1개 레코드 (최신 데이터만)
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of health_checkup_raw
|
||||||
|
**건강보험공단 원본 데이터**
|
||||||
|
• 연도별 검진 데이터 보관
|
||||||
|
• 가공 전 원본 데이터 유지
|
||||||
|
• 개인정보 포함 (이름, 생년월일)
|
||||||
|
• 성별/나이별 통계 분석 용도
|
||||||
|
• 여러 연도 데이터 보관 가능
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of health_normal_range
|
||||||
|
**건강검진 정상치 기준**
|
||||||
|
• 성별별 정상치 기준 관리
|
||||||
|
• gender_code: 0(공통), 1(남성), 2(여성)
|
||||||
|
• 정상/주의/위험 3단계 범위
|
||||||
|
• 의료진 검토 후 업데이트
|
||||||
|
• 마스터 데이터 성격
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of health_file
|
||||||
|
**업로드된 건강검진 파일**
|
||||||
|
• Azure Blob Storage 연동
|
||||||
|
• PDF, 이미지 파일 지원
|
||||||
|
• 업로드 상태 추적
|
||||||
|
• OCR 처리 결과 연동 준비
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "관계 정의" as relationships {
|
||||||
|
health_checkup ||--|| health_checkup_raw : raw_id
|
||||||
|
health_checkup }|--|| health_normal_range : 정상치_비교
|
||||||
|
health_file }|--|| health_checkup : 파일_연동
|
||||||
|
|
||||||
|
note as n1
|
||||||
|
**외래키 관계**
|
||||||
|
health_checkups.raw_id → health_checkup_raw.raw_id
|
||||||
|
|
||||||
|
**논리적 관계**
|
||||||
|
• health_checkups ↔ health_normal_ranges (정상치 비교)
|
||||||
|
• health_files ↔ health_checkups (파일-데이터 연결)
|
||||||
|
• User Service users ↔ health_checkups (사용자별 데이터)
|
||||||
|
|
||||||
|
**참조 무결성**
|
||||||
|
• raw_id는 NOT NULL (원본 데이터 필수)
|
||||||
|
• user_id는 User Service와 일관성 유지
|
||||||
|
• member_serial_number로 건보공단 데이터 연결
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "데이터 타입 및 제약조건" as constraints {
|
||||||
|
note as n2
|
||||||
|
**health_checkups 제약조건**
|
||||||
|
• id: AUTO_INCREMENT
|
||||||
|
• user_id: 최대 50자, NOT NULL
|
||||||
|
• member_serial_number: User Service와 일치
|
||||||
|
• reference_year: 1990~현재년도
|
||||||
|
• bmi: 계산값, 10.0~50.0 범위
|
||||||
|
• blood pressure: systolic > diastolic
|
||||||
|
• risk_level: 'normal', 'caution', 'danger'
|
||||||
|
• health_score: 0~100 점수
|
||||||
|
• abnormal_indicators: JSON 배열 형태
|
||||||
|
|
||||||
|
**health_checkup_raw 제약조건**
|
||||||
|
• raw_id: AUTO_INCREMENT
|
||||||
|
• member_serial_number: NOT NULL
|
||||||
|
• reference_year: NOT NULL
|
||||||
|
• gender_code: 1(남성), 2(여성)
|
||||||
|
• age: 0~120 범위
|
||||||
|
• 모든 수치 데이터: 음수 불가
|
||||||
|
|
||||||
|
**health_normal_ranges 제약조건**
|
||||||
|
• 복합 기본키: (item_code, gender_code)
|
||||||
|
• gender_code: 0(공통), 1(남성), 2(여성)
|
||||||
|
• normal_min ≤ normal_max
|
||||||
|
• caution 범위는 normal 범위 밖
|
||||||
|
• danger 범위는 caution 범위 밖
|
||||||
|
|
||||||
|
**health_files 제약조건**
|
||||||
|
• file_id: UUID 형태
|
||||||
|
• file_type: 'pdf', 'jpg', 'png' 등
|
||||||
|
• file_size: 바이트 단위, 최대 10MB
|
||||||
|
• upload_status: 'uploading', 'completed', 'failed'
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "정상치 기준 예시 데이터" as normal_ranges_data {
|
||||||
|
note as n3
|
||||||
|
**주요 항목별 정상치 기준 (성인 남성 기준)**
|
||||||
|
|
||||||
|
| item_code | normal_range | caution_range | danger_range |
|
||||||
|
|-----------|-------------|---------------|--------------|
|
||||||
|
| BMI | 18.5~24.9 | 25.0~29.9 또는 <18.5 | ≥30.0 |
|
||||||
|
| SYSTOLIC_BP | 90~119 | 120~139 | ≥140 |
|
||||||
|
| DIASTOLIC_BP | 60~79 | 80~89 | ≥90 |
|
||||||
|
| FASTING_GLUCOSE | 70~99 | 100~125 | ≥126 |
|
||||||
|
| TOTAL_CHOLESTEROL | <200 | 200~239 | ≥240 |
|
||||||
|
| HDL_CHOLESTEROL | ≥40 | 35~39 | <35 |
|
||||||
|
| LDL_CHOLESTEROL | <130 | 130~159 | ≥160 |
|
||||||
|
| TRIGLYCERIDE | <150 | 150~199 | ≥200 |
|
||||||
|
| AST | <40 | 40~80 | >80 |
|
||||||
|
| ALT | <40 | 40~80 | >80 |
|
||||||
|
| GAMMA_GTP | <60 | 60~100 | >100 |
|
||||||
|
| HEMOGLOBIN | 13.0~17.0 | 12.0~12.9 | <12.0 |
|
||||||
|
|
||||||
|
**성별 차이**
|
||||||
|
• 여성은 HDL 콜레스테롤 ≥50, 혈색소 12.0~15.0
|
||||||
|
• 임신 가능 연령대 여성은 별도 기준 적용
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "성능 및 운영 고려사항" as performance {
|
||||||
|
note as n4
|
||||||
|
**인덱스 전략**
|
||||||
|
• health_checkups: (user_id, reference_year) 복합 인덱스
|
||||||
|
• health_checkup_raw: (member_serial_number, reference_year) 복합 인덱스
|
||||||
|
• health_normal_ranges: item_code 단일 인덱스
|
||||||
|
• health_files: (user_id, uploaded_at) 복합 인덱스
|
||||||
|
|
||||||
|
**파티셔닝**
|
||||||
|
• health_checkup_raw: reference_year 기준 연도별 파티셔닝
|
||||||
|
• health_files: uploaded_at 기준 월별 파티셔닝
|
||||||
|
|
||||||
|
**아카이빙**
|
||||||
|
• 5년 이상 된 raw 데이터는 별도 아카이브 테이블로 이관
|
||||||
|
• 업로드 실패 파일은 30일 후 자동 삭제
|
||||||
|
|
||||||
|
**캐싱 전략**
|
||||||
|
• health_normal_ranges: Redis에 전체 캐싱 (24시간)
|
||||||
|
• 사용자별 최신 건강검진: Redis 캐싱 (1시간)
|
||||||
|
• 건강 점수 계산 결과: Redis 캐싱 (6시간)
|
||||||
|
|
||||||
|
**모니터링 지표**
|
||||||
|
• 정상치 기준 업데이트 빈도
|
||||||
|
• 평균 건강 점수 추이
|
||||||
|
• 위험군 사용자 비율
|
||||||
|
• 파일 업로드 성공률
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "데이터 보안 및 규정 준수" as security {
|
||||||
|
note as n5
|
||||||
|
**개인정보 보호**
|
||||||
|
• health_checkup_raw.name: 암호화 저장
|
||||||
|
• health_checkup_raw.birth_date: 마스킹 처리
|
||||||
|
• 의료 데이터 접근 시 감사 로그 기록
|
||||||
|
|
||||||
|
**의료정보 보안**
|
||||||
|
• 의료법 및 개인정보보호법 준수
|
||||||
|
• 건강검진 데이터 3년 보존 의무
|
||||||
|
• 개인식별정보와 건강정보 분리 저장
|
||||||
|
|
||||||
|
**접근 제어**
|
||||||
|
• 의료진만 정상치 기준 수정 가능
|
||||||
|
• 사용자 본인 데이터만 조회 가능
|
||||||
|
• 관리자 접근 시 의료진 승인 필요
|
||||||
|
|
||||||
|
**데이터 무결성**
|
||||||
|
• 건강검진 데이터 변조 방지
|
||||||
|
• 원본 데이터 불변성 보장
|
||||||
|
• 정상치 기준 변경 시 이력 관리
|
||||||
|
|
||||||
|
**백업 및 복구**
|
||||||
|
• 건강검진 데이터 일일 백업
|
||||||
|
• 정상치 기준 변경 전 백업
|
||||||
|
• 재해 복구 시 의료진 검증 필수
|
||||||
|
end note
|
||||||
|
}
|
||||||
310
design/데이터설계서 - Intelligence Service.txt
Normal file
310
design/데이터설계서 - Intelligence Service.txt
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam classAttributeIconSize 0
|
||||||
|
skinparam classFontSize 12
|
||||||
|
skinparam classAttributeFontSize 11
|
||||||
|
|
||||||
|
title HealthSync 역설계 - Intelligence Service 데이터 설계서
|
||||||
|
|
||||||
|
package "Intelligence Service Database Schema" as intelligence_db #lightseagreen {
|
||||||
|
|
||||||
|
entity "chat_sessions" as chat_session {
|
||||||
|
* session_id : VARCHAR(50) <<PK>>
|
||||||
|
--
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* status : VARCHAR(20)
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
* last_activity_at : TIMESTAMP
|
||||||
|
* message_count : INTEGER
|
||||||
|
* context : TEXT
|
||||||
|
* session_type : VARCHAR(30)
|
||||||
|
* expires_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_user_id_status
|
||||||
|
+ 인덱스: idx_last_activity
|
||||||
|
+ 인덱스: idx_expires_at
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "chat_messages" as chat_message {
|
||||||
|
* message_id : VARCHAR(50) <<PK>>
|
||||||
|
--
|
||||||
|
* session_id : VARCHAR(50) <<FK>>
|
||||||
|
* role : VARCHAR(20)
|
||||||
|
* content : TEXT
|
||||||
|
* timestamp : TIMESTAMP
|
||||||
|
* message_order : INTEGER
|
||||||
|
* is_sensitive : BOOLEAN
|
||||||
|
* token_count : INTEGER
|
||||||
|
* response_time_ms : INTEGER
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_session_order
|
||||||
|
+ 인덱스: idx_timestamp
|
||||||
|
+ 인덱스: idx_role
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "analysis_results" as analysis_result {
|
||||||
|
* id : BIGINT <<PK>>
|
||||||
|
--
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* analysis_type : VARCHAR(50)
|
||||||
|
* result : TEXT
|
||||||
|
* confidence : DECIMAL(5,4)
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
* metadata : JSON
|
||||||
|
* source_data_hash : VARCHAR(64)
|
||||||
|
* claude_model_version : VARCHAR(20)
|
||||||
|
* processing_time_ms : INTEGER
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_user_analysis_type
|
||||||
|
+ 인덱스: idx_created_at
|
||||||
|
+ 인덱스: idx_confidence
|
||||||
|
+ 인덱스: idx_source_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "notification_logs" as notification_log {
|
||||||
|
* id : BIGINT <<PK>>
|
||||||
|
--
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* mission_id : VARCHAR(50)
|
||||||
|
* notification_type : VARCHAR(30)
|
||||||
|
* message : TEXT
|
||||||
|
* sent_at : TIMESTAMP
|
||||||
|
* delivery_status : VARCHAR(20)
|
||||||
|
* response_generated_by : VARCHAR(20)
|
||||||
|
* processing_time_ms : INTEGER
|
||||||
|
* user_reaction : VARCHAR(20)
|
||||||
|
* effectiveness_score : DECIMAL(3,2)
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_user_id_type
|
||||||
|
+ 인덱스: idx_sent_at
|
||||||
|
+ 인덱스: idx_delivery_status
|
||||||
|
+ 인덱스: idx_effectiveness
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "claude_api_usage" as claude_api_usage {
|
||||||
|
* id : BIGINT <<PK>>
|
||||||
|
--
|
||||||
|
* request_id : VARCHAR(50)
|
||||||
|
* user_id : VARCHAR(50)
|
||||||
|
* api_endpoint : VARCHAR(100)
|
||||||
|
* request_type : VARCHAR(50)
|
||||||
|
* input_tokens : INTEGER
|
||||||
|
* output_tokens : INTEGER
|
||||||
|
* cost_usd : DECIMAL(10,6)
|
||||||
|
* response_time_ms : INTEGER
|
||||||
|
* success : BOOLEAN
|
||||||
|
* error_message : TEXT
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_user_id_date
|
||||||
|
+ 인덱스: idx_request_type
|
||||||
|
+ 인덱스: idx_success
|
||||||
|
+ 인덱스: idx_cost
|
||||||
|
}
|
||||||
|
|
||||||
|
note right of chat_session
|
||||||
|
**AI 채팅 세션 관리**
|
||||||
|
• 사용자별 대화 컨텍스트 유지
|
||||||
|
• 세션별 상태 관리 (활성/비활성/만료)
|
||||||
|
• 컨텍스트 정보로 개인화 대화
|
||||||
|
• 자동 만료 기능 (24시간)
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of chat_message
|
||||||
|
**채팅 메시지 저장**
|
||||||
|
• 사용자와 AI 간 모든 대화 기록
|
||||||
|
• 메시지 순서 보장
|
||||||
|
• 민감정보 플래그 관리
|
||||||
|
• Claude API 토큰 사용량 추적
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of analysis_result
|
||||||
|
**AI 분석 결과 저장**
|
||||||
|
• 건강 진단, 미션 추천 등 분석 결과
|
||||||
|
• 신뢰도 점수로 품질 관리
|
||||||
|
• 소스 데이터 해시로 중복 방지
|
||||||
|
• Claude 모델 버전별 성능 추적
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of notification_log
|
||||||
|
**알림 발송 이력**
|
||||||
|
• 축하, 독려 메시지 발송 기록
|
||||||
|
• 사용자 반응 및 효과성 측정
|
||||||
|
• 개인화 알림 성능 분석
|
||||||
|
• A/B 테스트 결과 수집
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of claude_api_usage
|
||||||
|
**Claude API 사용량 추적**
|
||||||
|
• API 호출별 비용 및 성능 모니터링
|
||||||
|
• 토큰 사용량 기반 과금 관리
|
||||||
|
• 에러 발생 패턴 분석
|
||||||
|
• 사용자별 API 사용 통계
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "관계 정의" as relationships {
|
||||||
|
chat_session ||--o{ chat_message : session_id
|
||||||
|
chat_session }o--|| analysis_result : 분석_요청
|
||||||
|
notification_log }o--|| analysis_result : 알림_생성
|
||||||
|
claude_api_usage }o--|| analysis_result : API_호출
|
||||||
|
claude_api_usage }o--|| chat_message : API_호출
|
||||||
|
|
||||||
|
note as n1
|
||||||
|
**외래키 관계**
|
||||||
|
chat_messages.session_id → chat_sessions.session_id
|
||||||
|
|
||||||
|
**논리적 관계**
|
||||||
|
• User Service users ↔ chat_sessions (사용자별 채팅)
|
||||||
|
• Goal Service missions ↔ notification_logs (미션별 알림)
|
||||||
|
• Health Service checkups ↔ analysis_results (건강 분석)
|
||||||
|
|
||||||
|
**참조 무결성**
|
||||||
|
• CASCADE 삭제 (세션 삭제 시 메시지도 삭제)
|
||||||
|
• 사용자 ID는 User Service와 일관성 유지
|
||||||
|
• 미션 ID는 Goal Service와 연동
|
||||||
|
|
||||||
|
**데이터 일관성**
|
||||||
|
• 세션별 메시지 순서 보장
|
||||||
|
• API 사용량과 실제 호출 일치
|
||||||
|
• 알림 발송 성공/실패 상태 추적
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "데이터 타입 및 제약조건" as constraints {
|
||||||
|
note as n2
|
||||||
|
**chat_sessions 제약조건**
|
||||||
|
• session_id: UUID 형태, PRIMARY KEY
|
||||||
|
• user_id: User Service와 일치, NOT NULL
|
||||||
|
• status: 'ACTIVE', 'INACTIVE', 'EXPIRED', 'TERMINATED'
|
||||||
|
• session_type: 'HEALTH_CONSULTATION', 'GENERAL_CHAT', 'MISSION_GUIDANCE'
|
||||||
|
• message_count: 0 이상, 최대 100개 메시지
|
||||||
|
• expires_at: created_at + 24시간
|
||||||
|
|
||||||
|
**chat_messages 제약조건**
|
||||||
|
• message_id: UUID 형태, PRIMARY KEY
|
||||||
|
• role: 'USER', 'ASSISTANT', 'SYSTEM'
|
||||||
|
• content: 최대 10,000자
|
||||||
|
• message_order: 세션 내 순차 증가
|
||||||
|
• token_count: 0 이상
|
||||||
|
• response_time_ms: 0 이상
|
||||||
|
|
||||||
|
**analysis_results 제약조건**
|
||||||
|
• analysis_type: 'HEALTH_DIAGNOSIS', 'MISSION_RECOMMENDATION', 'RISK_ASSESSMENT'
|
||||||
|
• confidence: 0.0000~1.0000 범위
|
||||||
|
• result: 최대 50,000자
|
||||||
|
• source_data_hash: SHA-256 해시값
|
||||||
|
• claude_model_version: 'claude-3-sonnet', 'claude-3-opus' 등
|
||||||
|
|
||||||
|
**notification_logs 제약조건**
|
||||||
|
• notification_type: 'CELEBRATION', 'ENCOURAGEMENT', 'REMINDER', 'ACHIEVEMENT'
|
||||||
|
• delivery_status: 'SENT', 'DELIVERED', 'FAILED', 'PENDING'
|
||||||
|
• response_generated_by: 'CLAUDE_AI', 'TEMPLATE', 'FALLBACK'
|
||||||
|
• user_reaction: 'POSITIVE', 'NEGATIVE', 'NEUTRAL', 'NO_RESPONSE'
|
||||||
|
• effectiveness_score: 0.00~5.00 범위
|
||||||
|
|
||||||
|
**claude_api_usage 제약조건**
|
||||||
|
• request_type: 'CHAT', 'ANALYSIS', 'RECOMMENDATION', 'NOTIFICATION'
|
||||||
|
• input_tokens: 0 이상
|
||||||
|
• output_tokens: 0 이상
|
||||||
|
• cost_usd: 0.000001 이상 (최소 과금 단위)
|
||||||
|
• success: 성공/실패 여부
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "AI 모델 및 프롬프트 관리" as ai_models {
|
||||||
|
note as n3
|
||||||
|
**Claude AI 모델별 특성**
|
||||||
|
|
||||||
|
**claude-3-sonnet (기본 모델)**
|
||||||
|
• 건강 상담 및 일반 채팅
|
||||||
|
• 비용 효율적, 응답 속도 빠름
|
||||||
|
• 토큰당 비용: $0.003 (input), $0.015 (output)
|
||||||
|
|
||||||
|
**claude-3-opus (고급 모델)**
|
||||||
|
• 복잡한 건강 분석 및 진단
|
||||||
|
• 높은 정확도, 상세한 분석
|
||||||
|
• 토큰당 비용: $0.015 (input), $0.075 (output)
|
||||||
|
|
||||||
|
**프롬프트 최적화**
|
||||||
|
• 건강 상담: 의료진 톤앤매너 적용
|
||||||
|
• 미션 추천: 개인화 요소 강조
|
||||||
|
• 독려 메시지: 동기부여 심리학 적용
|
||||||
|
• 축하 메시지: 긍정적 감정 표현
|
||||||
|
|
||||||
|
**컨텍스트 관리**
|
||||||
|
• 최근 10개 메시지 유지
|
||||||
|
• 사용자 프로필 정보 포함
|
||||||
|
• 건강 상태 요약 정보 제공
|
||||||
|
• 이전 분석 결과 참조
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "성능 및 운영 고려사항" as performance {
|
||||||
|
note as n4
|
||||||
|
**인덱스 전략**
|
||||||
|
• chat_sessions: (user_id, status) 복합 인덱스
|
||||||
|
• chat_messages: (session_id, message_order) 복합 인덱스
|
||||||
|
• analysis_results: (user_id, analysis_type, created_at) 복합 인덱스
|
||||||
|
• notification_logs: (user_id, sent_at) 복합 인덱스
|
||||||
|
• claude_api_usage: (user_id, DATE(created_at)) 복합 인덱스
|
||||||
|
|
||||||
|
**파티셔닝**
|
||||||
|
• chat_messages: created_at 기준 월별 파티셔닝
|
||||||
|
• claude_api_usage: created_at 기준 월별 파티셔닝
|
||||||
|
• notification_logs: sent_at 기준 월별 파티셔닝
|
||||||
|
|
||||||
|
**캐싱 전략**
|
||||||
|
• 활성 채팅 세션: Redis 캐싱 (30분)
|
||||||
|
• 최근 분석 결과: Redis 캐싱 (1시간)
|
||||||
|
• Claude API 응답: Redis 캐싱 (10분)
|
||||||
|
• 자주 사용되는 프롬프트: Redis 캐싱 (24시간)
|
||||||
|
|
||||||
|
**비용 최적화**
|
||||||
|
• 중복 분석 요청 캐싱으로 API 호출 최소화
|
||||||
|
• 토큰 사용량 모니터링 및 알림
|
||||||
|
• 모델별 비용 효율성 분석
|
||||||
|
• 배치 처리로 API 호출 효율화
|
||||||
|
|
||||||
|
**모니터링 지표**
|
||||||
|
• 일일 Claude API 비용
|
||||||
|
• 평균 응답 시간
|
||||||
|
• API 성공률
|
||||||
|
• 사용자별 토큰 사용량
|
||||||
|
• 분석 결과 신뢰도 분포
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "데이터 보안 및 개인정보 보호" as security {
|
||||||
|
note as n5
|
||||||
|
**개인정보 보호**
|
||||||
|
• 채팅 내용 중 민감정보 자동 마스킹
|
||||||
|
• 의료 정보 포함 메시지 암호화 저장
|
||||||
|
• 사용자 식별 정보와 채팅 내용 분리
|
||||||
|
• 90일 경과 채팅 기록 자동 삭제
|
||||||
|
|
||||||
|
**AI 윤리 및 안전**
|
||||||
|
• 의료 조언 범위 제한 명시
|
||||||
|
• 응급 상황 감지 시 안내 메시지
|
||||||
|
• 편향성 방지를 위한 프롬프트 검증
|
||||||
|
• 부적절한 응답 필터링
|
||||||
|
|
||||||
|
**API 보안**
|
||||||
|
• Claude API 키 암호화 저장
|
||||||
|
• API 호출 시 Rate Limiting 적용
|
||||||
|
• 요청/응답 내용 로깅 (개인정보 제외)
|
||||||
|
• 이상 사용 패턴 모니터링
|
||||||
|
|
||||||
|
**감사 및 추적**
|
||||||
|
• 모든 AI 분석 결과 이력 보관
|
||||||
|
• 의료 관련 분석 시 추가 검증
|
||||||
|
• 사용자 신고 시 조치 이력 관리
|
||||||
|
• 정기적인 AI 응답 품질 검토
|
||||||
|
|
||||||
|
**규정 준수**
|
||||||
|
• 의료기기법 비대상 확인
|
||||||
|
• 개인정보보호법 준수
|
||||||
|
• AI 윤리 가이드라인 적용
|
||||||
|
• 의료 광고 규제 준수
|
||||||
|
end note
|
||||||
|
}
|
||||||
173
design/데이터설계서 - User Service.txt
Normal file
173
design/데이터설계서 - User Service.txt
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam classAttributeIconSize 0
|
||||||
|
skinparam classFontSize 12
|
||||||
|
skinparam classAttributeFontSize 11
|
||||||
|
|
||||||
|
title HealthSync 역설계 - User Service 데이터 설계서
|
||||||
|
|
||||||
|
package "User Service Database Schema" as user_db #lightgreen {
|
||||||
|
|
||||||
|
entity "users" as user {
|
||||||
|
* member_serial_number : BIGINT <<PK>>
|
||||||
|
--
|
||||||
|
* google_id : VARCHAR(255) <<UK>>
|
||||||
|
* name : VARCHAR(100)
|
||||||
|
* birth_date : DATE
|
||||||
|
* occupation : VARCHAR(50) <<FK>>
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
* updated_at : TIMESTAMP
|
||||||
|
* last_login_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_google_id
|
||||||
|
+ 인덱스: idx_occupation
|
||||||
|
+ 인덱스: idx_created_at
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "occupation_types" as occupation_type {
|
||||||
|
* occupation_code : VARCHAR(20) <<PK>>
|
||||||
|
--
|
||||||
|
* occupation_name : VARCHAR(100)
|
||||||
|
* category : VARCHAR(50)
|
||||||
|
* is_active : BOOLEAN
|
||||||
|
* created_at : TIMESTAMP
|
||||||
|
* updated_at : TIMESTAMP
|
||||||
|
--
|
||||||
|
+ 인덱스: idx_occupation_name
|
||||||
|
+ 인덱스: idx_category
|
||||||
|
}
|
||||||
|
|
||||||
|
note right of user
|
||||||
|
**사용자 테이블 특징**
|
||||||
|
• Google OAuth 기반 인증
|
||||||
|
• member_serial_number는
|
||||||
|
건강보험공단 데이터 연동 키
|
||||||
|
• occupation은 직업 코드 참조
|
||||||
|
• 소프트 삭제 미적용 (물리 삭제)
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of occupation_type
|
||||||
|
**직업 유형 테이블 특징**
|
||||||
|
• 표준 직업 분류 기반
|
||||||
|
• 카테고리별 그룹핑
|
||||||
|
• 활성/비활성 상태 관리
|
||||||
|
• 마스터 데이터 성격
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "관계 정의" as relationships {
|
||||||
|
user ||--o{ occupation_type : occupation
|
||||||
|
|
||||||
|
note as n1
|
||||||
|
**외래키 관계**
|
||||||
|
users.occupation → occupation_types.occupation_code
|
||||||
|
|
||||||
|
**참조 무결성**
|
||||||
|
• CASCADE 업데이트
|
||||||
|
• RESTRICT 삭제 (직업 유형 삭제 시 제한)
|
||||||
|
|
||||||
|
**데이터 정합성**
|
||||||
|
• occupation 필드는 NULL 허용 (프로필 미완성 상태)
|
||||||
|
• birth_date는 NOT NULL (필수 정보)
|
||||||
|
• google_id는 UNIQUE 제약 (중복 가입 방지)
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "데이터 타입 및 제약조건" as constraints {
|
||||||
|
note as n2
|
||||||
|
**users 테이블 제약조건**
|
||||||
|
• member_serial_number: AUTO_INCREMENT
|
||||||
|
• google_id: UNIQUE, NOT NULL
|
||||||
|
• name: NOT NULL, 최대 100자
|
||||||
|
• birth_date: NOT NULL, 1900-01-01 이후
|
||||||
|
• occupation: NULL 허용, 최대 50자
|
||||||
|
• created_at: DEFAULT CURRENT_TIMESTAMP
|
||||||
|
• updated_at: ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
• last_login_at: NULL 허용
|
||||||
|
|
||||||
|
**occupation_types 테이블 제약조건**
|
||||||
|
• occupation_code: PRIMARY KEY, 최대 20자
|
||||||
|
• occupation_name: UNIQUE, NOT NULL, 최대 100자
|
||||||
|
• category: NOT NULL, 최대 50자
|
||||||
|
• is_active: DEFAULT TRUE
|
||||||
|
• created_at: DEFAULT CURRENT_TIMESTAMP
|
||||||
|
• updated_at: ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "초기 데이터" as initial_data {
|
||||||
|
note as n3
|
||||||
|
**occupation_types 초기 데이터 예시**
|
||||||
|
|
||||||
|
| occupation_code | occupation_name | category |
|
||||||
|
|----------------|----------------|----------|
|
||||||
|
| OFF001 | 사무직 | 사무관리 |
|
||||||
|
| MFG001 | 제조업 생산직 | 제조생산 |
|
||||||
|
| SVC001 | 서비스업 | 서비스 |
|
||||||
|
| EDU001 | 교육직 | 전문직 |
|
||||||
|
| MED001 | 의료진 | 전문직 |
|
||||||
|
| IT001 | IT개발자 | 전문직 |
|
||||||
|
| SAL001 | 영업직 | 영업마케팅 |
|
||||||
|
| DRV001 | 운전직 | 운송 |
|
||||||
|
| CON001 | 건설업 | 건설 |
|
||||||
|
| FRE001 | 프리랜서 | 기타 |
|
||||||
|
|
||||||
|
**카테고리 분류**
|
||||||
|
• 사무관리: 앉아서 일하는 직종
|
||||||
|
• 제조생산: 신체 활동이 많은 직종
|
||||||
|
• 서비스: 고객 응대 직종
|
||||||
|
• 전문직: 전문 지식 요구 직종
|
||||||
|
• 영업마케팅: 외부 활동 직종
|
||||||
|
• 운송: 이동이 많은 직종
|
||||||
|
• 건설: 육체 노동 직종
|
||||||
|
• 기타: 분류하기 어려운 직종
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "성능 및 운영 고려사항" as performance {
|
||||||
|
note as n4
|
||||||
|
**인덱스 전략**
|
||||||
|
• users.google_id: 로그인 시 빠른 조회
|
||||||
|
• users.occupation: 직업별 통계 조회
|
||||||
|
• users.created_at: 가입 일자별 조회
|
||||||
|
• occupation_types.occupation_name: 직업명 검색
|
||||||
|
|
||||||
|
**파티셔닝**
|
||||||
|
• 현재 단계에서는 미적용
|
||||||
|
• 사용자 수 증가 시 created_at 기준 월별 파티셔닝 고려
|
||||||
|
|
||||||
|
**백업 및 복구**
|
||||||
|
• users: 매일 풀백업 (개인정보 보호)
|
||||||
|
• occupation_types: 주간 백업 (마스터 데이터)
|
||||||
|
|
||||||
|
**모니터링 지표**
|
||||||
|
• 신규 가입자 수 (일별/월별)
|
||||||
|
• 활성 사용자 수 (last_login_at 기준)
|
||||||
|
• 직업별 사용자 분포
|
||||||
|
• 평균 프로필 완성률
|
||||||
|
end note
|
||||||
|
}
|
||||||
|
|
||||||
|
package "데이터 보안" as security {
|
||||||
|
note as n5
|
||||||
|
**개인정보 보호**
|
||||||
|
• name: 암호화 저장 고려 (실명)
|
||||||
|
• birth_date: 날짜 정보 마스킹
|
||||||
|
• google_id: 해시 처리 또는 암호화
|
||||||
|
|
||||||
|
**접근 제어**
|
||||||
|
• 개인정보 조회 시 로그 기록
|
||||||
|
• 관리자 접근 시 승인 프로세스
|
||||||
|
• API 호출 시 사용자 본인 확인
|
||||||
|
|
||||||
|
**데이터 보존**
|
||||||
|
• 회원 탈퇴 시 개인정보 즉시 삭제
|
||||||
|
• 로그인 기록 90일 보존
|
||||||
|
• 감사 로그 1년 보존
|
||||||
|
|
||||||
|
**GDPR/개인정보보호법 준수**
|
||||||
|
• 데이터 처리 목적 명시
|
||||||
|
• 동의 철회 시 데이터 삭제
|
||||||
|
• 데이터 이동권 지원
|
||||||
|
end note
|
||||||
|
}
|
||||||
227
design/물리아키텍처.txt
Normal file
227
design/물리아키텍처.txt
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam componentStyle rectangle
|
||||||
|
skinparam componentFontSize 12
|
||||||
|
skinparam arrowFontSize 11
|
||||||
|
skinparam arrowThickness 2
|
||||||
|
|
||||||
|
title HealthSync 역설계 - Azure Cloud 물리 아키텍처
|
||||||
|
|
||||||
|
' 외부 사용자 및 시스템
|
||||||
|
package "External Systems" as external #lightgray {
|
||||||
|
[모바일 사용자] as MobileUser
|
||||||
|
[Google OAuth] as GoogleOAuth
|
||||||
|
[Claude AI API] as ClaudeAPI
|
||||||
|
[건강보험공단\n데이터] as NhisData
|
||||||
|
}
|
||||||
|
|
||||||
|
' Azure Front Door & CDN
|
||||||
|
package "Global Network" as global #lightblue {
|
||||||
|
[Azure Front Door] as FrontDoor
|
||||||
|
[Azure CDN] as CDN
|
||||||
|
}
|
||||||
|
|
||||||
|
' Azure Application Gateway & Load Balancer
|
||||||
|
package "Load Balancing" as lb #lightyellow {
|
||||||
|
[Application Gateway\n(WAF 포함)] as AppGateway
|
||||||
|
[Azure Load Balancer] as LoadBalancer
|
||||||
|
}
|
||||||
|
|
||||||
|
' AKS Cluster
|
||||||
|
package "Azure Kubernetes Service (AKS)" as aks #lightgreen {
|
||||||
|
package "Ingress Controller" as ingress {
|
||||||
|
[NGINX Ingress\nController] as IngressController
|
||||||
|
}
|
||||||
|
|
||||||
|
package "Application Pods" as pods {
|
||||||
|
[Gateway Pod] as GatewayPod
|
||||||
|
[User Service Pod] as UserPod
|
||||||
|
[Health Service Pod] as HealthPod
|
||||||
|
[Goal Service Pod] as GoalPod
|
||||||
|
[Intelligence Service Pod] as IntellPod
|
||||||
|
[Batch Service Pod] as BatchPod
|
||||||
|
}
|
||||||
|
|
||||||
|
package "Supporting Services" as support {
|
||||||
|
[Istio Service Mesh] as ServiceMesh
|
||||||
|
[Prometheus\n& Grafana] as Monitoring
|
||||||
|
[FluentD] as Logging
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
' Azure Database Services
|
||||||
|
package "Database Services" as database #lightpink {
|
||||||
|
[Azure Database\nfor PostgreSQL\n(Flexible Server)] as PostgreSQL
|
||||||
|
[Azure Cache\nfor Redis\n(Premium)] as Redis
|
||||||
|
[Azure Blob Storage\n(Hot Tier)] as BlobStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
' Azure Messaging & Events
|
||||||
|
package "Messaging & Events" as messaging #lightsalmon {
|
||||||
|
[Azure Service Bus\n(Standard)] as ServiceBus
|
||||||
|
[Azure Event Hub\n(Standard)] as EventHub
|
||||||
|
}
|
||||||
|
|
||||||
|
' Azure Security & Identity
|
||||||
|
package "Security & Identity" as security #orange {
|
||||||
|
[Azure Key Vault] as KeyVault
|
||||||
|
[Azure Active Directory\nB2C] as AzureAD
|
||||||
|
[Azure Application\nInsights] as AppInsights
|
||||||
|
}
|
||||||
|
|
||||||
|
' Azure DevOps & CI/CD
|
||||||
|
package "DevOps & CI/CD" as devops #lightsteelblue {
|
||||||
|
[Azure DevOps\nPipelines] as AzureDevOps
|
||||||
|
[Azure Container\nRegistry (ACR)] as ACR
|
||||||
|
[GitHub Repository] as GitHub
|
||||||
|
[Azure Monitor] as AzureMonitor
|
||||||
|
}
|
||||||
|
|
||||||
|
' Azure Backup & Recovery
|
||||||
|
package "Backup & DR" as backup #thistle {
|
||||||
|
[Azure Backup] as AzureBackup
|
||||||
|
[Azure Site Recovery] as SiteRecovery
|
||||||
|
[Geo-Redundant\nStorage] as GeoStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
' === 외부 접근 및 CDN (1-3) ===
|
||||||
|
MobileUser -[#blue,thickness=3]-> FrontDoor : "1. 모바일 앱 접근"
|
||||||
|
FrontDoor -[#blue,thickness=2]-> CDN : "2. 정적 자원 캐싱"
|
||||||
|
CDN -[#blue,thickness=2]-> AppGateway : "3. 동적 요청 라우팅"
|
||||||
|
|
||||||
|
' === 로드밸런싱 및 보안 (4-5) ===
|
||||||
|
AppGateway -[#green,thickness=2]-> LoadBalancer : "4. WAF 보안 필터링"
|
||||||
|
LoadBalancer -[#green,thickness=2]-> IngressController : "5. AKS 클러스터 진입"
|
||||||
|
|
||||||
|
' === AKS 내부 라우팅 (6-8) ===
|
||||||
|
IngressController -[#purple,thickness=2]-> GatewayPod : "6. API Gateway 라우팅"
|
||||||
|
GatewayPod -[#purple,thickness=2]-> ServiceMesh : "7. Service Mesh 통신"
|
||||||
|
ServiceMesh -[#purple,thickness=2]-> UserPod : "8a. User Service 호출"
|
||||||
|
ServiceMesh -[#purple,thickness=2]-> HealthPod : "8b. Health Service 호출"
|
||||||
|
ServiceMesh -[#purple,thickness=2]-> GoalPod : "8c. Goal Service 호출"
|
||||||
|
ServiceMesh -[#purple,thickness=2]-> IntellPod : "8d. Intelligence Service 호출"
|
||||||
|
|
||||||
|
' === 데이터베이스 연결 (9-11) ===
|
||||||
|
UserPod -[#red,thickness=2]-> PostgreSQL : "9a. 사용자 데이터 처리"
|
||||||
|
HealthPod -[#red,thickness=2]-> PostgreSQL : "9b. 건강검진 데이터 처리"
|
||||||
|
GoalPod -[#red,thickness=2]-> PostgreSQL : "9c. 목표/미션 데이터 처리"
|
||||||
|
IntellPod -[#red,thickness=2]-> PostgreSQL : "9d. AI 분석/채팅 데이터 처리"
|
||||||
|
|
||||||
|
' === 캐싱 및 스토리지 (10-11) ===
|
||||||
|
HealthPod -[#orange,thickness=2]-> Redis : "10a. 건강검진 결과 캐싱"
|
||||||
|
IntellPod -[#orange,thickness=2]-> Redis : "10b. AI 응답 캐싱"
|
||||||
|
GoalPod -[#orange,thickness=2]-> Redis : "10c. 활성 미션 캐싱"
|
||||||
|
HealthPod -[#brown,thickness=2]-> BlobStorage : "11. 건강검진 파일 저장"
|
||||||
|
|
||||||
|
' === 메시징 및 이벤트 (12-13) ===
|
||||||
|
UserPod -[#magenta,thickness=2]-> ServiceBus : "12a. 사용자 이벤트 발행"
|
||||||
|
HealthPod -[#magenta,thickness=2]-> ServiceBus : "12b. 건강데이터 이벤트 발행"
|
||||||
|
GoalPod -[#magenta,thickness=2]-> ServiceBus : "12c. 목표달성 이벤트 발행"
|
||||||
|
IntellPod -[#magenta,thickness=2]-> EventHub : "13. 대량 이벤트 스트리밍"
|
||||||
|
|
||||||
|
' === 외부 API 연동 (14-16) ===
|
||||||
|
UserPod -[#navy,thickness=2]-> GoogleOAuth : "14. Google SSO 인증"
|
||||||
|
IntellPod -[#navy,thickness=2]-> ClaudeAPI : "15. AI 분석/채팅 요청"
|
||||||
|
HealthPod -[#darkgreen,thickness=2]-> NhisData : "16. 건강보험공단 데이터 연동"
|
||||||
|
|
||||||
|
' === 배치 처리 (17) ===
|
||||||
|
BatchPod -[#darkred,thickness=2]-> ServiceBus : "17a. 배치 이벤트 구독"
|
||||||
|
BatchPod -[#darkred,thickness=2]-> IntellPod : "17b. 주기적 AI 알림 요청"
|
||||||
|
BatchPod -[#darkred,thickness=2]-> ClaudeAPI : "17c. 배치 AI 메시지 생성"
|
||||||
|
|
||||||
|
' === 보안 및 키 관리 (18-20) ===
|
||||||
|
GatewayPod -[#darkgray,thickness=1,dotted]-> KeyVault : "18a. Gateway 설정"
|
||||||
|
UserPod -[#darkgray,thickness=1,dotted]-> KeyVault : "18b. JWT Secret"
|
||||||
|
HealthPod -[#darkgray,thickness=1,dotted]-> KeyVault : "18c. DB 연결 정보"
|
||||||
|
IntellPod -[#darkgray,thickness=1,dotted]-> KeyVault : "18d. Claude API Key"
|
||||||
|
BatchPod -[#darkgray,thickness=1,dotted]-> KeyVault : "18e. 스케줄러 설정"
|
||||||
|
|
||||||
|
' === 모니터링 및 로깅 (19-20) ===
|
||||||
|
Monitoring -[#cyan,thickness=1,dashed]-> AppInsights : "19. 메트릭 수집"
|
||||||
|
Logging -[#cyan,thickness=1,dashed]-> AzureMonitor : "20. 로그 집계"
|
||||||
|
|
||||||
|
' === CI/CD 파이프라인 (21-25) ===
|
||||||
|
GitHub -[#teal,thickness=2]-> AzureDevOps : "21. 코드 푸시 트리거"
|
||||||
|
AzureDevOps -[#teal,thickness=2]-> ACR : "22. 컨테이너 빌드 & 푸시"
|
||||||
|
AzureDevOps -[#teal,thickness=2]-> GitHub : "23. 배포 YAML 업데이트"
|
||||||
|
GitHub -[#teal,thickness=2]-> aks : "24. GitOps 배포 동기화"
|
||||||
|
AzureDevOps -[#teal,thickness=2]-> AzureMonitor : "25. 배포 모니터링"
|
||||||
|
|
||||||
|
' === 백업 및 재해복구 (26-28) ===
|
||||||
|
PostgreSQL -[#purple,thickness=1,dashed]-> AzureBackup : "26. 데이터베이스 백업"
|
||||||
|
BlobStorage -[#purple,thickness=1,dashed]-> GeoStorage : "27. 파일 지역 복제"
|
||||||
|
aks -[#purple,thickness=1,dashed]-> SiteRecovery : "28. 클러스터 재해복구"
|
||||||
|
|
||||||
|
' === 주요 특징 및 처리 흐름 설명 ===
|
||||||
|
note top of FrontDoor
|
||||||
|
**글로벌 진입점**
|
||||||
|
• 전 세계 사용자 접근 최적화
|
||||||
|
• DDoS 보호 및 SSL 터미네이션
|
||||||
|
• 지연시간 최소화
|
||||||
|
end note
|
||||||
|
|
||||||
|
note top of AKS
|
||||||
|
**컨테이너 오케스트레이션**
|
||||||
|
• 마이크로서비스 자동 스케일링
|
||||||
|
• Service Mesh로 서비스 간 통신
|
||||||
|
• 무중단 배포 및 롤백
|
||||||
|
• 리소스 효율적 관리
|
||||||
|
end note
|
||||||
|
|
||||||
|
note top of PostgreSQL
|
||||||
|
**통합 데이터베이스**
|
||||||
|
• 단일 PostgreSQL로 모든 서비스 데이터 통합
|
||||||
|
• Flexible Server로 성능 최적화
|
||||||
|
• 자동 백업 및 포인트-인-타임 복구
|
||||||
|
• Read Replica로 읽기 성능 향상
|
||||||
|
end note
|
||||||
|
|
||||||
|
note top of Redis
|
||||||
|
**고성능 캐싱**
|
||||||
|
• Premium 티어로 99.9% 가용성
|
||||||
|
• 클러스터링으로 확장성 확보
|
||||||
|
• 영구 저장으로 데이터 보호
|
||||||
|
• 지역 복제로 재해 대비
|
||||||
|
end note
|
||||||
|
|
||||||
|
note top of ServiceBus
|
||||||
|
**신뢰성 있는 메시징**
|
||||||
|
• 이벤트 기반 아키텍처 구현
|
||||||
|
• 메시지 순서 보장
|
||||||
|
• Dead Letter Queue로 실패 처리
|
||||||
|
• Auto-scaling으로 부하 대응
|
||||||
|
end note
|
||||||
|
|
||||||
|
note top of KeyVault
|
||||||
|
**중앙 보안 관리**
|
||||||
|
• 모든 시크릿 중앙 관리
|
||||||
|
• 하드웨어 보안 모듈 (HSM)
|
||||||
|
• 액세스 정책 세밀 제어
|
||||||
|
• 감사 로그 자동 기록
|
||||||
|
end note
|
||||||
|
|
||||||
|
note bottom of AzureDevOps
|
||||||
|
**완전 자동화 CI/CD**
|
||||||
|
• 코드 커밋 → 자동 빌드
|
||||||
|
• 컨테이너 이미지 스캔
|
||||||
|
• 단계별 배포 승인
|
||||||
|
• Blue-Green 배포 지원
|
||||||
|
• 자동 롤백 기능
|
||||||
|
end note
|
||||||
|
|
||||||
|
note bottom of AzureMonitor
|
||||||
|
**통합 모니터링**
|
||||||
|
• 실시간 성능 메트릭
|
||||||
|
• 사용자 정의 대시보드
|
||||||
|
• 자동 알림 및 경고
|
||||||
|
• 로그 분석 및 검색
|
||||||
|
• 비용 최적화 인사이트
|
||||||
|
end note
|
||||||
|
|
||||||
|
note bottom of external
|
||||||
|
**외부 시스템 연동**
|
||||||
|
• Google OAuth: 안전한 사용자 인증
|
||||||
|
• Claude AI: 고품질 AI 분석
|
||||||
|
• 건강보험공단: 신뢰성 있는 건강 데이터
|
||||||
|
• Circuit Breaker 패턴으로 안정성 확보
|
||||||
|
end note
|
||||||
140
design/외부 시퀀스 다이어그램.txt
Normal file
140
design/외부 시퀀스 다이어그램.txt
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
!theme mono
|
||||||
|
|
||||||
|
skinparam sequenceArrowThickness 2
|
||||||
|
skinparam sequenceParticipantBorderThickness 2
|
||||||
|
skinparam sequenceActorBorderThickness 2
|
||||||
|
skinparam sequenceGroupBorderThickness 2
|
||||||
|
|
||||||
|
title HealthSync 역설계 - 외부 시퀀스 다이어그램 (개발 소스 기반)
|
||||||
|
|
||||||
|
participant "React 모바일 앱" as MobileApp
|
||||||
|
participant "API Gateway" as Gateway
|
||||||
|
participant "User Service" as UserSvc
|
||||||
|
participant "Health Service" as HealthSvc
|
||||||
|
participant "Goal Service" as GoalSvc
|
||||||
|
participant "Intelligence Service" as IntelSvc
|
||||||
|
participant "Google SSO" as GoogleSSO
|
||||||
|
participant "Claude API" as ClaudeAPI
|
||||||
|
participant "Azure Blob Storage" as BlobStorage
|
||||||
|
participant "Redis Cache" as Redis
|
||||||
|
participant "PostgreSQL" as DB
|
||||||
|
|
||||||
|
== 1. 사용자 인증 및 등록 ==
|
||||||
|
|
||||||
|
MobileApp -> Gateway: POST /api/users/auth/google-login (구글 로그인)
|
||||||
|
Gateway -> UserSvc: POST /auth/google-login
|
||||||
|
UserSvc -> GoogleSSO: OAuth 2.0 인증 요청
|
||||||
|
UserSvc -> DB: 사용자 조회/생성
|
||||||
|
UserSvc -> UserSvc: JWT 토큰 생성
|
||||||
|
|
||||||
|
MobileApp -> Gateway: POST /api/users/profile/complete (프로필 완료)
|
||||||
|
Gateway -> UserSvc: POST /profile/complete
|
||||||
|
UserSvc -> DB: 사용자 프로필 업데이트
|
||||||
|
|
||||||
|
MobileApp -> Gateway: GET /api/users/profile (프로필 조회)
|
||||||
|
Gateway -> UserSvc: GET /profile
|
||||||
|
UserSvc -> DB: 사용자 정보 조회
|
||||||
|
|
||||||
|
== 2. 건강검진 데이터 관리 ==
|
||||||
|
|
||||||
|
MobileApp -> Gateway: POST /api/health/checkup/sync (건강검진 연동)
|
||||||
|
Gateway -> HealthSvc: POST /checkup/sync
|
||||||
|
HealthSvc -> DB: 건강보험공단 원본 데이터 조회
|
||||||
|
HealthSvc -> DB: 성별별 정상치 기준 조회
|
||||||
|
HealthSvc -> DB: 정상치 비교 분석 후 저장
|
||||||
|
HealthSvc -> Redis: 분석 결과 캐싱
|
||||||
|
|
||||||
|
MobileApp -> Gateway: GET /api/health/checkup/history (건강검진 이력)
|
||||||
|
Gateway -> HealthSvc: GET /checkup/history
|
||||||
|
HealthSvc -> Redis: 캐시 조회
|
||||||
|
alt 캐시 미스
|
||||||
|
HealthSvc -> DB: 건강검진 이력 조회
|
||||||
|
HealthSvc -> Redis: 결과 캐싱
|
||||||
|
end
|
||||||
|
|
||||||
|
MobileApp -> Gateway: POST /api/health/checkup/upload (파일 업로드)
|
||||||
|
Gateway -> HealthSvc: POST /checkup/upload
|
||||||
|
HealthSvc -> BlobStorage: Azure Blob 파일 저장
|
||||||
|
HealthSvc -> DB: 파일 메타데이터 저장
|
||||||
|
|
||||||
|
== 3. AI 기반 건강 분석 ==
|
||||||
|
|
||||||
|
MobileApp -> Gateway: GET /api/intelligence/health/diagnosis (AI 3줄 요약)
|
||||||
|
Gateway -> IntelSvc: GET /health/diagnosis
|
||||||
|
IntelSvc -> HealthSvc: 건강검진 데이터 조회 (서비스 간 통신)
|
||||||
|
IntelSvc -> ClaudeAPI: AI 분석 요청
|
||||||
|
IntelSvc -> Redis: 분석 결과 캐싱
|
||||||
|
|
||||||
|
MobileApp -> Gateway: POST /api/intelligence/missions/recommend (미션 추천)
|
||||||
|
Gateway -> IntelSvc: POST /missions/recommend
|
||||||
|
IntelSvc -> HealthSvc: 건강상태 조회
|
||||||
|
IntelSvc -> UserSvc: 사용자 직업정보 조회
|
||||||
|
IntelSvc -> ClaudeAPI: 개인화 미션 추천 요청
|
||||||
|
|
||||||
|
== 4. 목표 설정 및 관리 ==
|
||||||
|
|
||||||
|
MobileApp -> Gateway: POST /api/goals/missions/select (미션 선택)
|
||||||
|
Gateway -> GoalSvc: POST /missions/select
|
||||||
|
GoalSvc -> DB: 목표 설정 저장
|
||||||
|
|
||||||
|
MobileApp -> Gateway: GET /api/goals/missions/active (활성 미션 조회)
|
||||||
|
Gateway -> GoalSvc: GET /missions/active
|
||||||
|
GoalSvc -> Redis: 활성 미션 캐시 조회
|
||||||
|
alt 캐시 미스
|
||||||
|
GoalSvc -> DB: 활성 미션 조회
|
||||||
|
GoalSvc -> Redis: 결과 캐싱
|
||||||
|
end
|
||||||
|
|
||||||
|
MobileApp -> Gateway: PUT /api/goals/missions/{missionId}/complete (미션 완료)
|
||||||
|
Gateway -> GoalSvc: PUT /missions/{missionId}/complete
|
||||||
|
GoalSvc -> DB: 미션 완료 처리
|
||||||
|
GoalSvc -> Redis: 관련 캐시 무효화
|
||||||
|
|
||||||
|
MobileApp -> Gateway: GET /api/goals/missions/history (미션 이력)
|
||||||
|
Gateway -> GoalSvc: GET /missions/history
|
||||||
|
GoalSvc -> DB: 미션 달성 이력 조회
|
||||||
|
|
||||||
|
== 5. AI 챗봇 상담 ==
|
||||||
|
|
||||||
|
MobileApp -> Gateway: POST /api/intelligence/chat/consultation (AI 채팅)
|
||||||
|
Gateway -> IntelSvc: POST /chat/consultation
|
||||||
|
IntelSvc -> DB: 채팅 히스토리 조회
|
||||||
|
IntelSvc -> ClaudeAPI: AI 챗봇 응답 요청
|
||||||
|
IntelSvc -> DB: 채팅 메시지 저장
|
||||||
|
IntelSvc -> Redis: 채팅 히스토리 캐싱
|
||||||
|
|
||||||
|
MobileApp -> Gateway: GET /api/intelligence/chat/history (채팅 이력)
|
||||||
|
Gateway -> IntelSvc: GET /chat/history
|
||||||
|
IntelSvc -> Redis: 캐시된 채팅 이력 조회
|
||||||
|
|
||||||
|
== 6. 알림 및 독려 시스템 ==
|
||||||
|
|
||||||
|
MobileApp -> Gateway: POST /api/intelligence/notifications/celebration (축하 메시지)
|
||||||
|
Gateway -> IntelSvc: POST /notifications/celebration
|
||||||
|
IntelSvc -> GoalSvc: 미션 달성 정보 조회
|
||||||
|
IntelSvc -> ClaudeAPI: 축하 메시지 생성 요청
|
||||||
|
|
||||||
|
Gateway -> IntelSvc: POST /batch/notifications (배치 알림 처리)
|
||||||
|
IntelSvc -> GoalSvc: 모든 사용자 진행상황 조회
|
||||||
|
IntelSvc -> ClaudeAPI: 개인화 독려 메시지 생성
|
||||||
|
IntelSvc -> Redis: 알림 결과 캐싱
|
||||||
|
|
||||||
|
== 7. 목표 재설정 ==
|
||||||
|
|
||||||
|
MobileApp -> Gateway: POST /api/goals/missions/reset (목표 재설정)
|
||||||
|
Gateway -> GoalSvc: POST /missions/reset
|
||||||
|
GoalSvc -> IntelSvc: 새로운 미션 추천 요청
|
||||||
|
GoalSvc -> DB: 기존 목표 비활성화 및 새 목표 설정
|
||||||
|
|
||||||
|
== 주요 특징 ==
|
||||||
|
note over MobileApp, DB
|
||||||
|
**개발 소스 기반 주요 특징**
|
||||||
|
• JWT 기반 인증 (모든 API)
|
||||||
|
• Redis 캐싱으로 성능 최적화
|
||||||
|
• Claude AI 연동 강화
|
||||||
|
• 정상치 기준 비교 분석
|
||||||
|
• Azure Blob 파일 저장
|
||||||
|
• 서비스 간 HTTP 통신
|
||||||
|
• 이벤트 기반 비동기 처리
|
||||||
|
• 예외 처리 및 회복 전략
|
||||||
|
end note
|
||||||
1534
design/클래스 설계서.txt
Normal file
1534
design/클래스 설계서.txt
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user