177 lines
6.2 KiB
Plaintext
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 |