mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2025-12-06 22:46:24 +00:00
✨ 주요 기능 - Azure 기반 물리아키텍처 설계 (개발환경/운영환경) - 7개 마이크로서비스 물리 구조 설계 - 네트워크 아키텍처 다이어그램 작성 (Mermaid) - 환경별 비교 분석 및 마스터 인덱스 문서 📁 생성 파일 - design/backend/physical/physical-architecture.md (마스터) - design/backend/physical/physical-architecture-dev.md (개발환경) - design/backend/physical/physical-architecture-prod.md (운영환경) - design/backend/physical/*.mmd (4개 Mermaid 다이어그램) 🎯 핵심 성과 - 비용 최적화: 개발환경 월 $143, 운영환경 월 $2,860 - 확장성: 개발환경 100명 → 운영환경 10,000명 (100배) - 가용성: 개발환경 95% → 운영환경 99.9% - 보안: 다층 보안 아키텍처 (L1~L4) 🛠️ 기술 스택 - Azure Kubernetes Service (AKS) - Azure Database for PostgreSQL Flexible - Azure Cache for Redis Premium - Azure Service Bus Premium - Application Gateway + WAF 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
451 lines
13 KiB
Plaintext
451 lines
13 KiB
Plaintext
@startuml
|
|
!theme mono
|
|
|
|
title User Service 클래스 다이어그램 (상세)
|
|
|
|
' ====================
|
|
' 공통 컴포넌트 (참조)
|
|
' ====================
|
|
package "com.kt.event.common" <<rectangle>> {
|
|
abstract class BaseTimeEntity {
|
|
- createdAt: LocalDateTime
|
|
- updatedAt: LocalDateTime
|
|
}
|
|
|
|
interface ErrorCode {
|
|
+ getCode(): String
|
|
+ getMessage(): String
|
|
}
|
|
|
|
class BusinessException extends RuntimeException {
|
|
- errorCode: ErrorCode
|
|
+ getErrorCode(): ErrorCode
|
|
}
|
|
|
|
interface JwtTokenProvider {
|
|
+ createAccessToken(): String
|
|
+ validateToken(): boolean
|
|
+ getExpirationFromToken(): Date
|
|
}
|
|
}
|
|
|
|
package "com.kt.event.user" {
|
|
|
|
' ====================
|
|
' Presentation Layer
|
|
' ====================
|
|
package "controller" {
|
|
class UserController {
|
|
- userService: UserService
|
|
- authenticationService: AuthenticationService
|
|
|
|
' UFR-USER-010: 회원가입
|
|
+ register(request: RegisterRequest): ResponseEntity<RegisterResponse>
|
|
|
|
' UFR-USER-020: 로그인
|
|
+ login(request: LoginRequest): ResponseEntity<LoginResponse>
|
|
|
|
' UFR-USER-040: 로그아웃
|
|
+ logout(authHeader: String): ResponseEntity<LogoutResponse>
|
|
|
|
' UFR-USER-030: 프로필 관리
|
|
+ getProfile(principal: UserPrincipal): ResponseEntity<ProfileResponse>
|
|
+ updateProfile(principal: UserPrincipal, request: UpdateProfileRequest): ResponseEntity<ProfileResponse>
|
|
+ changePassword(principal: UserPrincipal, request: ChangePasswordRequest): ResponseEntity<Void>
|
|
}
|
|
}
|
|
|
|
' ====================
|
|
' Business Layer (Service)
|
|
' ====================
|
|
package "service" {
|
|
interface UserService {
|
|
+ register(request: RegisterRequest): RegisterResponse
|
|
+ getProfile(userId: UUID): ProfileResponse
|
|
+ updateProfile(userId: UUID, request: UpdateProfileRequest): ProfileResponse
|
|
+ changePassword(userId: UUID, request: ChangePasswordRequest): void
|
|
+ updateLastLoginAt(userId: UUID): void
|
|
}
|
|
|
|
interface AuthenticationService {
|
|
+ login(request: LoginRequest): LoginResponse
|
|
+ logout(token: String): LogoutResponse
|
|
}
|
|
}
|
|
|
|
package "service.impl" {
|
|
class UserServiceImpl implements UserService {
|
|
- userRepository: UserRepository
|
|
- storeRepository: StoreRepository
|
|
- passwordEncoder: PasswordEncoder
|
|
- jwtTokenProvider: JwtTokenProvider
|
|
- redisTemplate: RedisTemplate<String, Object>
|
|
|
|
' UFR-USER-010: 회원가입
|
|
+ register(request: RegisterRequest): RegisterResponse
|
|
|
|
' UFR-USER-030: 프로필 관리
|
|
+ getProfile(userId: UUID): ProfileResponse
|
|
+ updateProfile(userId: UUID, request: UpdateProfileRequest): ProfileResponse
|
|
+ changePassword(userId: UUID, request: ChangePasswordRequest): void
|
|
|
|
' UFR-USER-020: 로그인 시각 업데이트
|
|
+ updateLastLoginAt(userId: UUID): void
|
|
|
|
' 내부 메소드
|
|
- saveSession(token: String, userId: UUID, role: String): void
|
|
}
|
|
|
|
class AuthenticationServiceImpl implements AuthenticationService {
|
|
- userRepository: UserRepository
|
|
- storeRepository: StoreRepository
|
|
- passwordEncoder: PasswordEncoder
|
|
- jwtTokenProvider: JwtTokenProvider
|
|
- userService: UserService
|
|
- redisTemplate: RedisTemplate<String, Object>
|
|
|
|
' UFR-USER-020: 로그인
|
|
+ login(request: LoginRequest): LoginResponse
|
|
|
|
' UFR-USER-040: 로그아웃
|
|
+ logout(token: String): LogoutResponse
|
|
|
|
' 내부 메소드
|
|
- saveSession(token: String, userId: UUID, role: String): void
|
|
}
|
|
}
|
|
|
|
' ====================
|
|
' Data Access Layer
|
|
' ====================
|
|
package "repository" {
|
|
interface UserRepository extends JpaRepository {
|
|
+ findByEmail(email: String): Optional<User>
|
|
+ findByPhoneNumber(phoneNumber: String): Optional<User>
|
|
+ existsByEmail(email: String): boolean
|
|
+ existsByPhoneNumber(phoneNumber: String): boolean
|
|
+ updateLastLoginAt(userId: UUID, lastLoginAt: LocalDateTime): void
|
|
}
|
|
|
|
interface StoreRepository extends JpaRepository {
|
|
+ findByUserId(userId: UUID): Optional<Store>
|
|
}
|
|
}
|
|
|
|
' ====================
|
|
' Domain Layer
|
|
' ====================
|
|
package "entity" {
|
|
class User extends BaseTimeEntity {
|
|
- id: UUID
|
|
- name: String
|
|
- phoneNumber: String
|
|
- email: String
|
|
- passwordHash: String
|
|
- role: UserRole
|
|
- status: UserStatus
|
|
- lastLoginAt: LocalDateTime
|
|
- store: Store
|
|
|
|
' 비즈니스 로직
|
|
+ updateLastLoginAt(): void
|
|
+ changePassword(newPasswordHash: String): void
|
|
+ updateProfile(name: String, email: String, phoneNumber: String): void
|
|
+ setStore(store: Store): void
|
|
}
|
|
|
|
enum UserRole {
|
|
OWNER
|
|
ADMIN
|
|
}
|
|
|
|
enum UserStatus {
|
|
ACTIVE
|
|
INACTIVE
|
|
LOCKED
|
|
WITHDRAWN
|
|
}
|
|
|
|
class Store extends BaseTimeEntity {
|
|
- id: UUID
|
|
- name: String
|
|
- industry: String
|
|
- address: String
|
|
- businessHours: String
|
|
- user: User
|
|
|
|
' 비즈니스 로직
|
|
+ updateInfo(name: String, industry: String, address: String, businessHours: String): void
|
|
~ setUser(user: User): void
|
|
}
|
|
}
|
|
|
|
' ====================
|
|
' DTO Layer
|
|
' ====================
|
|
package "dto.request" {
|
|
class RegisterRequest {
|
|
- name: String
|
|
- phoneNumber: String
|
|
- email: String
|
|
- password: String
|
|
- storeName: String
|
|
- industry: String
|
|
- address: String
|
|
- businessHours: String
|
|
}
|
|
|
|
class LoginRequest {
|
|
- email: String
|
|
- password: String
|
|
}
|
|
|
|
class UpdateProfileRequest {
|
|
- name: String
|
|
- email: String
|
|
- phoneNumber: String
|
|
- storeName: String
|
|
- industry: String
|
|
- address: String
|
|
- businessHours: String
|
|
}
|
|
|
|
class ChangePasswordRequest {
|
|
- currentPassword: String
|
|
- newPassword: String
|
|
}
|
|
}
|
|
|
|
package "dto.response" {
|
|
class RegisterResponse {
|
|
- token: String
|
|
- userId: UUID
|
|
- userName: String
|
|
- storeId: UUID
|
|
- storeName: String
|
|
}
|
|
|
|
class LoginResponse {
|
|
- token: String
|
|
- userId: UUID
|
|
- userName: String
|
|
- role: String
|
|
- email: String
|
|
}
|
|
|
|
class LogoutResponse {
|
|
- success: boolean
|
|
- message: String
|
|
}
|
|
|
|
class ProfileResponse {
|
|
- userId: UUID
|
|
- userName: String
|
|
- phoneNumber: String
|
|
- email: String
|
|
- role: String
|
|
- storeId: UUID
|
|
- storeName: String
|
|
- industry: String
|
|
- address: String
|
|
- businessHours: String
|
|
- createdAt: LocalDateTime
|
|
- lastLoginAt: LocalDateTime
|
|
}
|
|
}
|
|
|
|
' ====================
|
|
' Exception Layer
|
|
' ====================
|
|
package "exception" {
|
|
enum UserErrorCode {
|
|
USER_DUPLICATE_EMAIL
|
|
USER_DUPLICATE_PHONE
|
|
USER_NOT_FOUND
|
|
AUTH_FAILED
|
|
AUTH_INVALID_TOKEN
|
|
AUTH_TOKEN_EXPIRED
|
|
AUTH_UNAUTHORIZED
|
|
PWD_INVALID_CURRENT
|
|
PWD_SAME_AS_CURRENT
|
|
|
|
- errorCode: ErrorCode
|
|
+ getCode(): String
|
|
+ getMessage(): String
|
|
}
|
|
}
|
|
|
|
' ====================
|
|
' Configuration Layer
|
|
' ====================
|
|
package "config" {
|
|
class SecurityConfig {
|
|
- jwtTokenProvider: JwtTokenProvider
|
|
- allowedOrigins: String
|
|
|
|
+ filterChain(http: HttpSecurity): SecurityFilterChain
|
|
+ corsConfigurationSource(): CorsConfigurationSource
|
|
+ passwordEncoder(): PasswordEncoder
|
|
}
|
|
|
|
class RedisConfig {
|
|
- redisHost: String
|
|
- redisPort: int
|
|
|
|
+ redisConnectionFactory(): RedisConnectionFactory
|
|
+ redisTemplate(): RedisTemplate<String, Object>
|
|
}
|
|
|
|
class AsyncConfig {
|
|
+ taskExecutor(): Executor
|
|
}
|
|
|
|
class SwaggerConfig {
|
|
+ customOpenAPI(): OpenAPI
|
|
}
|
|
}
|
|
}
|
|
|
|
' ====================
|
|
' Layer 간 의존성 관계
|
|
' ====================
|
|
|
|
' Controller → Service
|
|
UserController --> UserService : uses
|
|
UserController --> AuthenticationService : uses
|
|
|
|
' Service → Repository
|
|
UserServiceImpl --> UserRepository : uses
|
|
UserServiceImpl --> StoreRepository : uses
|
|
AuthenticationServiceImpl --> UserRepository : uses
|
|
AuthenticationServiceImpl --> StoreRepository : uses
|
|
AuthenticationServiceImpl --> UserService : uses
|
|
|
|
' Service → Entity (도메인 로직 호출)
|
|
UserServiceImpl ..> User : creates/updates
|
|
UserServiceImpl ..> Store : creates/updates
|
|
AuthenticationServiceImpl ..> User : reads
|
|
|
|
' Repository → Entity
|
|
UserRepository --> User : manages
|
|
StoreRepository --> Store : manages
|
|
|
|
' Service → DTO
|
|
UserServiceImpl ..> RegisterRequest : receives
|
|
UserServiceImpl ..> UpdateProfileRequest : receives
|
|
UserServiceImpl ..> ChangePasswordRequest : receives
|
|
UserServiceImpl ..> RegisterResponse : returns
|
|
UserServiceImpl ..> ProfileResponse : returns
|
|
AuthenticationServiceImpl ..> LoginRequest : receives
|
|
AuthenticationServiceImpl ..> LoginResponse : returns
|
|
AuthenticationServiceImpl ..> LogoutResponse : returns
|
|
|
|
' Controller → DTO
|
|
UserController ..> RegisterRequest : receives
|
|
UserController ..> LoginRequest : receives
|
|
UserController ..> UpdateProfileRequest : receives
|
|
UserController ..> ChangePasswordRequest : receives
|
|
UserController ..> RegisterResponse : returns
|
|
UserController ..> LoginResponse : returns
|
|
UserController ..> LogoutResponse : returns
|
|
UserController ..> ProfileResponse : returns
|
|
|
|
' Entity 관계
|
|
User "1" -- "0..1" Store : has >
|
|
User +-- UserRole
|
|
User +-- UserStatus
|
|
|
|
' Exception
|
|
UserServiceImpl ..> UserErrorCode : throws
|
|
AuthenticationServiceImpl ..> UserErrorCode : throws
|
|
UserErrorCode --> ErrorCode : wraps
|
|
|
|
' Configuration
|
|
SecurityConfig --> JwtTokenProvider : uses
|
|
SecurityConfig ..> PasswordEncoder : creates
|
|
UserServiceImpl --> PasswordEncoder : uses
|
|
AuthenticationServiceImpl --> PasswordEncoder : uses
|
|
|
|
' Common 컴포넌트 사용
|
|
User --|> BaseTimeEntity
|
|
Store --|> BaseTimeEntity
|
|
UserServiceImpl ..> JwtTokenProvider : uses
|
|
AuthenticationServiceImpl ..> JwtTokenProvider : uses
|
|
UserServiceImpl ..> BusinessException : throws
|
|
AuthenticationServiceImpl ..> BusinessException : throws
|
|
|
|
' Notes
|
|
note top of UserController
|
|
<b>Presentation Layer</b>
|
|
- REST API 엔드포인트 제공
|
|
- 요청/응답 DTO 변환
|
|
- 인증 정보 추출 (UserPrincipal)
|
|
- Swagger 문서화
|
|
end note
|
|
|
|
note top of UserService
|
|
<b>Business Layer</b>
|
|
- 비즈니스 로직 처리
|
|
- 트랜잭션 관리
|
|
- 도메인 객체 조작
|
|
- 검증 및 예외 처리
|
|
end note
|
|
|
|
note top of UserRepository
|
|
<b>Data Access Layer</b>
|
|
- JPA 기반 데이터 액세스
|
|
- CRUD 및 커스텀 쿼리
|
|
- 트랜잭션 경계
|
|
end note
|
|
|
|
note top of User
|
|
<b>Domain Layer</b>
|
|
- 핵심 비즈니스 엔티티
|
|
- 도메인 로직 포함
|
|
- 불변성 및 일관성 보장
|
|
end note
|
|
|
|
note right of UserServiceImpl
|
|
<b>핵심 기능</b>
|
|
|
|
1. 회원가입 (register)
|
|
- 중복 검증 (이메일, 전화번호)
|
|
- 비밀번호 해싱
|
|
- User/Store 생성
|
|
- JWT 토큰 발급
|
|
- Redis 세션 저장
|
|
|
|
2. 프로필 관리
|
|
- 프로필 조회/수정
|
|
- 비밀번호 변경 (현재 비밀번호 검증)
|
|
|
|
3. 로그인 시각 업데이트
|
|
- 비동기 처리 (@Async)
|
|
end note
|
|
|
|
note right of AuthenticationServiceImpl
|
|
<b>핵심 기능</b>
|
|
|
|
1. 로그인 (login)
|
|
- 이메일/비밀번호 검증
|
|
- JWT 토큰 발급
|
|
- Redis 세션 저장
|
|
- 최종 로그인 시각 업데이트
|
|
|
|
2. 로그아웃 (logout)
|
|
- JWT 토큰 검증
|
|
- Redis 세션 삭제
|
|
- JWT Blacklist 추가
|
|
end note
|
|
|
|
note bottom of User
|
|
<b>User-Store 관계</b>
|
|
|
|
- OneToOne 양방향 관계
|
|
- User가 Store를 소유
|
|
- Cascade ALL, Orphan Removal
|
|
- Lazy Loading
|
|
end note
|
|
|
|
@enduml
|