2025-10-22 14:13:57 +09:00

13 KiB

User Service 내부 시퀀스 설계서

문서 정보


개요

User Service의 4가지 주요 시나리오에 대한 내부 처리 흐름을 상세히 정의합니다.

시나리오 목록

번호 파일명 유저스토리 주요 처리 내용
1 user-회원가입.puml UFR-USER-010 기본 정보 검증, 사업자번호 검증(국세청 API), 트랜잭션 처리, JWT 발급
2 user-로그인.puml UFR-USER-020 비밀번호 검증(bcrypt), JWT 발급, 세션 저장, 최종 로그인 시각 업데이트
3 user-프로필수정.puml UFR-USER-030 기본 정보 수정, 매장 정보 수정, 비밀번호 변경, 트랜잭션 처리
4 user-로그아웃.puml UFR-USER-040 JWT 검증, 세션 삭제, Blacklist 추가

아키텍처 구조

Layered Architecture

┌─────────────────────────────────────┐
│      API Layer (Controller)         │  - HTTP 요청/응답 처리
│                                      │  - DTO 변환 및 검증
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│   Business Layer (Service)          │  - 비즈니스 로직 처리
│   - UserService                      │  - 트랜잭션 관리
│   - AuthenticationService            │  - 외부 API 연동
│   - BusinessValidator                │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│   Data Layer (Repository)            │  - 데이터베이스 접근
│   - UserRepository                   │  - JPA/MyBatis
│   - StoreRepository                  │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│   External Systems                   │  - 국세청 API
│                                      │  - Redis Cache
│                                      │  - PostgreSQL DB
└─────────────────────────────────────┘

주요 컴포넌트

API Layer

  • UserController: 사용자 관련 REST API 엔드포인트
    • POST /api/users/register: 회원가입
    • POST /api/users/login: 로그인
    • PUT /api/users/profile: 프로필 수정
    • POST /api/users/logout: 로그아웃

Business Layer

  • UserService: 사용자 정보 관리 비즈니스 로직
  • AuthenticationService: 인증 및 세션 관리 로직
  • BusinessValidator: 사업자번호 검증 로직 (Circuit Breaker 적용)

Data Layer

  • UserRepository: users 테이블 CRUD
  • StoreRepository: stores 테이블 CRUD

Utility

  • PasswordEncoder: bcrypt 해싱 (Cost Factor 10)
  • JwtTokenProvider: JWT 토큰 생성/검증 (만료 7일)

시나리오별 상세 설명

1. 회원가입 (user-회원가입.puml)

처리 단계

  1. 입력 검증: @Valid 어노테이션으로 DTO 검증
  2. 중복 사용자 확인: 전화번호 기반 중복 체크
  3. 사업자번호 검증:
    • Redis 캐시 확인 (TTL 7일)
    • 캐시 MISS: 국세청 API 호출 (Circuit Breaker 적용)
    • 캐시 HIT: 0.1초, MISS: 5초
  4. 비밀번호 해싱: bcrypt (Cost Factor 10)
  5. 사업자번호 암호화: AES-256
  6. 데이터베이스 트랜잭션:
    • User INSERT
    • Store INSERT
    • COMMIT (실패 시 자동 Rollback)
  7. JWT 토큰 생성: Claims(userId, role=OWNER, exp=7일)
  8. 세션 저장: Redis (TTL 7일)

Resilience 패턴

  • Circuit Breaker: 국세청 API (실패율 50% 초과 시 Open)
  • Retry: 최대 3회 (지수 백오프: 1초, 2초, 4초)
  • Timeout: 5초
  • Fallback: 사업자번호 검증 스킵 (수동 확인 안내)

응답 시간

  • 캐시 HIT: 1초 이내
  • 캐시 MISS: 5초 이내

2. 로그인 (user-로그인.puml)

처리 단계

  1. 입력 검증: 필수 필드 확인
  2. 사용자 조회: 전화번호로 사용자 검색
  3. 비밀번호 검증: bcrypt compare
  4. JWT 토큰 생성: Claims(userId, role=OWNER, exp=7일)
  5. 세션 저장: Redis (TTL 7일)
  6. 최종 로그인 시각 업데이트: 비동기 처리 (@Async)

보안 처리

  • 에러 메시지: 전화번호/비밀번호 구분 없이 동일 메시지 반환
  • 비밀번호: bcrypt compare (원본 노출 안 됨)

성능 최적화

  • 최종 로그인 시각 업데이트: 비동기 처리로 응답 시간 단축
  • 응답 시간: 0.5초 목표

3. 프로필 수정 (user-프로필수정.puml)

처리 단계

  1. JWT 인증: @AuthenticationPrincipal로 userId 추출
  2. 사용자 조회: userId로 기존 정보 조회
  3. 비밀번호 변경 처리 (선택적):
    • 현재 비밀번호 검증 (bcrypt compare)
    • 새 비밀번호 해싱 (bcrypt)
  4. 기본 정보 업데이트: 이름, 전화번호, 이메일
  5. 매장 정보 업데이트: 매장명, 업종, 주소, 영업시간
  6. 데이터베이스 트랜잭션:
    • User UPDATE
    • Store UPDATE
    • COMMIT (실패 시 자동 Rollback)
  7. 캐시 무효화: 프로필 캐시 삭제 (선택적)

보안 처리

  • 비밀번호 변경: 현재 비밀번호 확인 필수
  • 권한 검증: 본인만 수정 가능

향후 개선사항

  • 전화번호 변경: SMS/이메일 재인증 구현
  • 이메일 변경: 이메일 인증 구현

4. 로그아웃 (user-로그아웃.puml)

처리 단계

  1. JWT 인증: @AuthenticationPrincipal로 userId 추출
  2. JWT 토큰 검증: 서명 및 만료 시간 확인
  3. Redis 세션 삭제: DEL user:session:{token}
  4. JWT Blacklist 추가 (선택적):
    • 만료되지 않은 토큰 강제 무효화
    • Redis에 Blacklist 추가 (TTL: 남은 만료 시간)
  5. 로그아웃 로그 기록: userId, timestamp

보안 처리

  • JWT Blacklist: 만료 전 토큰 강제 무효화
  • 멱등성 보장: 중복 로그아웃 요청에 안전

클라이언트 측 처리

  • LocalStorage 또는 Cookie에서 JWT 토큰 삭제
  • 로그인 화면으로 리다이렉트

성능 최적화

  • Redis 삭제 연산: O(1) 시간 복잡도
  • 응답 시간: 0.1초 이내

데이터 모델

User Entity

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;

    @Column(nullable = false, length = 100)
    private String name;

    @Column(nullable = false, unique = true, length = 20)
    private String phoneNumber;

    @Column(nullable = false, unique = true, length = 255)
    private String email;

    @Column(nullable = false, length = 60)
    private String passwordHash;  // bcrypt hash

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private UserRole role;  // OWNER, CUSTOMER

    @Column(name = "last_login_at")
    private LocalDateTime lastLoginAt;

    @Column(name = "created_at", nullable = false)
    private LocalDateTime createdAt;

    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
}

Store Entity

@Entity
@Table(name = "stores")
public class Store {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long storeId;

    @Column(name = "user_id", nullable = false)
    private Long userId;

    @Column(nullable = false, length = 200)
    private String storeName;

    @Column(length = 100)
    private String industry;

    @Column(columnDefinition = "TEXT")
    private String address;

    @Column(name = "business_number_encrypted", length = 255)
    private String businessNumberEncrypted;  // AES-256 encrypted

    @Column(name = "business_hours", length = 500)
    private String businessHours;

    @Column(name = "created_at", nullable = false)
    private LocalDateTime createdAt;

    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
}

캐싱 전략

Redis 캐시 키 구조

캐시 키 패턴 데이터 타입 TTL 용도
user:session:{token} String (JSON) 7일 JWT 세션 정보 (userId, role)
user:business:{사업자번호} String (JSON) 7일 사업자번호 검증 결과 (valid, status)
jwt:blacklist:{token} String 남은 만료 시간 로그아웃된 JWT 토큰 Blacklist
user:profile:{userId} String (JSON) 1시간 사용자 프로필 정보 (선택적)

Cache-Aside 패턴

  1. Application → Redis 확인 (Cache HIT/MISS)
  2. Cache MISS → Database/External API 조회
  3. Database/External API → Redis 캐싱 (TTL 설정)
  4. Redis → Application 반환

에러 처리

주요 예외 클래스

예외 클래스 HTTP 상태 발생 시점
DuplicateUserException 400 이미 가입된 전화번호
BusinessNumberInvalidException 400 사업자번호 검증 실패
AuthenticationFailedException 401 로그인 실패 (전화번호/비밀번호 불일치)
InvalidTokenException 401 JWT 토큰 무효
UserNotFoundException 404 사용자 없음
InvalidPasswordException 400 현재 비밀번호 불일치 (프로필 수정)

에러 응답 형식

{
  "error": "에러 메시지",
  "code": "ERROR_CODE",
  "timestamp": "2025-10-22T10:30:00Z"
}

보안 고려사항

1. 비밀번호 보안

  • 해싱 알고리즘: bcrypt (Cost Factor 10)
  • 원본 노출 방지: 비밀번호는 해시로만 저장, 평문 로깅 금지
  • 에러 메시지: 전화번호/비밀번호 구분 없이 동일 메시지 반환 (보안 강화)

2. JWT 보안

  • 만료 시간: 7일 (Refresh Token 별도 구현 가능)
  • 서명 알고리즘: HS256 또는 RS256
  • Blacklist 관리: 로그아웃 시 Redis Blacklist에 추가

3. 민감 정보 암호화

  • 사업자번호: AES-256 암호화 저장
  • 전송 보안: HTTPS 강제 적용

4. 세션 관리

  • 세션 저장: Redis (서버 재시작에도 유지)
  • 세션 만료: 7일 후 자동 삭제 (TTL)
  • 동시 세션: 동일 사용자 다중 세션 허용 (필요 시 제한 가능)

성능 최적화

1. 캐싱 효과

  • 사업자번호 검증: 5초 → 0.1초 (98% 개선)
  • 세션 조회: Redis 사용으로 DB 부하 감소

2. 비동기 처리

  • 최종 로그인 시각 업데이트: @Async로 비동기 처리
  • 응답 시간 개선: 0.5초 목표 달성

3. 데이터베이스 최적화

  • 인덱스: phone_number, email 컬럼에 Unique Index
  • Connection Pool: HikariCP 사용 (최소 10개, 최대 50개)

테스트 전략

Unit Test

  • UserService, AuthenticationService 단위 테스트
  • Mock 객체: UserRepository, Redis, 국세청 API

Integration Test

  • Controller → Service → Repository 통합 테스트
  • 실제 Redis 및 PostgreSQL 사용 (Testcontainers)

E2E Test

  • Postman 또는 REST Assured로 전체 플로우 테스트
  • 회원가입 → 로그인 → 프로필 수정 → 로그아웃

향후 개선사항

Phase 2

  1. Refresh Token 구현: Access Token 만료 시 갱신 메커니즘
  2. 소셜 로그인: 카카오, 네이버, 구글 OAuth 2.0 연동
  3. 2FA (Two-Factor Authentication): SMS 또는 TOTP 기반 2단계 인증
  4. 비밀번호 재설정: 이메일/SMS를 통한 비밀번호 재설정 기능

Phase 3

  1. 계정 잠금 정책: 로그인 5회 실패 시 계정 잠금
  2. 세션 관리 고도화: 동시 세션 수 제한, 세션 활성 기록
  3. 감사 로그: 민감 작업(로그인, 비밀번호 변경) 감사 로그 저장
  4. Rate Limiting: 로그인 API에 Rate Limiting 적용 (사용자당 5회/분)

참고 문서


문서 버전: 1.0 최종 수정일: 2025-10-22 작성자: System Architect 변경 사항: User Service 내부 시퀀스 4개 시나리오 초안 작성 완료