feat : 역설계 파일 추가

This commit is contained in:
hehe 2025-06-20 06:54:13 +00:00
parent 409d7abdc6
commit 04c32417e7
13 changed files with 3995 additions and 0 deletions

26
design/API 설계서.txt Normal file
View 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}

View 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

View 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

View 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

View 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

View 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

View 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
}

View 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
}

View 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
}

View 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
}

View 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

View 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

File diff suppressed because it is too large Load Diff