@startuml user-로그인
!theme mono

title User Service - 로그인 내부 시퀀스 (UFR-USER-020)

actor Client
participant "UserController" as Controller <<API Layer>>
participant "UserService" as Service <<Business Layer>>
participant "AuthenticationService" as AuthService <<Business Layer>>
participant "UserRepository" as UserRepo <<Data Layer>>
participant "PasswordEncoder" as PwdEncoder <<Utility>>
participant "JwtTokenProvider" as JwtProvider <<Utility>>
participant "Redis\nCache" as Redis <<E>>
participant "User DB\n(PostgreSQL)" as UserDB <<E>>

note over Controller, UserDB
**UFR-USER-020: 로그인**
- 입력: 이메일, 비밀번호
- 비밀번호 검증 (bcrypt compare)
- JWT 토큰 발급
- 세션 저장 (Redis)
- 최종 로그인 시각 업데이트
end note

Client -> Controller: POST /api/users/login\n{"email": "user@example.com",\n"password": "password123"}
activate Controller

Controller -> Controller: 입력값 검증\n(필수 필드, 이메일 형식 확인)

Controller -> AuthService: authenticate(email, password)
activate AuthService

== 1단계: 사용자 조회 ==

AuthService -> Service: findByEmail(email)
activate Service
Service -> UserRepo: findByEmail(email)
activate UserRepo
UserRepo -> UserDB: 이메일로 사용자 조회\n(사용자ID, 비밀번호해시, 역할,\n이름, 전화번호 조회)
activate UserDB
UserDB --> UserRepo: 사용자 정보 반환 또는 없음
deactivate UserDB
UserRepo --> Service: Optional<User>
deactivate UserRepo
Service --> AuthService: Optional<User>
deactivate Service

alt 사용자 없음
    AuthService --> Controller: throw AuthenticationFailedException\n("이메일 또는 비밀번호를 확인해주세요")
    Controller --> Client: 401 Unauthorized\n{"code": "AUTH_001",\n"message": "이메일 또는 비밀번호를\n확인해주세요"}
    deactivate AuthService
    deactivate Controller

else 사용자 존재

    == 2단계: 비밀번호 검증 ==

    AuthService -> PwdEncoder: matches(rawPassword, passwordHash)
    activate PwdEncoder
    PwdEncoder -> PwdEncoder: bcrypt compare\n(입력 비밀번호 vs 저장된 해시)
    PwdEncoder --> AuthService: boolean (일치 여부)
    deactivate PwdEncoder

    alt 비밀번호 불일치
        AuthService --> Controller: throw AuthenticationFailedException\n("이메일 또는 비밀번호를 확인해주세요")
        Controller --> Client: 401 Unauthorized\n{"code": "AUTH_001",\n"message": "이메일 또는 비밀번호를\n확인해주세요"}
        deactivate AuthService
        deactivate Controller

    else 비밀번호 일치

        == 3단계: JWT 토큰 생성 ==

        AuthService -> JwtProvider: generateToken(userId, role)
        activate JwtProvider
        JwtProvider -> JwtProvider: JWT 토큰 생성\n(Claims: userId, role=OWNER,\nexp=7일)
        JwtProvider --> AuthService: JWT 토큰
        deactivate JwtProvider

        == 4단계: 세션 저장 ==

        AuthService -> Redis: 세션 정보 저장\nKey: user:session:{token}\nValue: {userId, role}\nTTL: 7일
        activate Redis
        Redis --> AuthService: 저장 완료
        deactivate Redis

        == 5단계: 최종 로그인 시각 업데이트 (비동기) ==

        AuthService ->> Service: updateLastLoginAt(userId)
        activate Service
        note right of Service
        **비동기 처리**
        - @Async 어노테이션 사용
        - 로그인 응답 지연 방지
        - 별도 스레드풀에서 실행
        end note
        Service ->> UserRepo: updateLastLoginAt(userId)
        activate UserRepo
        UserRepo ->> UserDB: 사용자 최종 로그인 시각 갱신\n(현재 시각으로 업데이트)
        activate UserDB
        UserDB -->> UserRepo: 업데이트 완료
        deactivate UserDB
        UserRepo -->> Service: void
        deactivate UserRepo
        Service -->> AuthService: void (비동기 완료)
        deactivate Service

        note over AuthService, Service
        **비동기 화살표 설명**
        - `->>`: 비동기 호출 (호출 후 즉시 반환)
        - `-->>`: 비동기 응답 (별도 스레드에서 완료)
        end note

        == 6단계: 응답 반환 ==

        AuthService -> AuthService: 로그인 응답 DTO 생성
        AuthService --> Controller: LoginResponse\n{token, userId, userName, role, email}
        deactivate AuthService

        Controller --> Client: 200 OK\n{"token": "eyJhbGc...",\n"userId": 123,\n"userName": "홍길동",\n"role": "OWNER",\n"email": "hong@example.com"}
        deactivate Controller
    end
end

note over Controller, UserDB
**보안 처리**
- 비밀번호: bcrypt compare (원본 노출 안 됨)
- 에러 메시지: 이메일/비밀번호 구분 없이 동일 메시지 반환 (Timing Attack 방어)
- JWT 토큰: 7일 만료, 서버 세션과 동기화

**보안 강화 (향후 구현)**
- Rate Limiting: IP당 5분에 5회 로그인 실패 시 임시 차단 (15분)
- Account Lockout: 동일 계정 10회 실패 시 계정 잠금 (관리자 해제)
- MFA: 2단계 인증 추가 (SMS/TOTP)
- Anomaly Detection: 비정상 로그인 패턴 감지 (지역, 디바이스 변경)

**성능 최적화**
- 최종 로그인 시각 업데이트: 비동기 처리 (@Async)
- 평균 응답 시간: 0.5초 이내
- P95 응답 시간: 1.0초 이내
- Redis 세션 조회: 0.1초 이내

**에러 코드**
- AUTH_001: 인증 실패 (이메일 또는 비밀번호 불일치)
end note

@enduml
