HealthSync_BE/design/내부 시퀀스 설계서 - User Service.txt

177 lines
6.2 KiB
Plaintext

!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