@startuml
!theme mono

title 공통 컴포넌트 클래스 다이어그램

package "com.kt.event.common" {

    package "entity" {
        abstract class BaseTimeEntity {
            - createdAt: LocalDateTime
            - updatedAt: LocalDateTime
        }
    }

    package "dto" {
        class "ApiResponse<T>" {
            - success: boolean
            - data: T
            - errorCode: String
            - message: String
            - timestamp: LocalDateTime
            + success(data: T): ApiResponse<T>
            + success(): ApiResponse<T>
            + error(errorCode: String, message: String): ApiResponse<T>
        }

        class ErrorResponse {
            - success: boolean
            - errorCode: String
            - message: String
            - timestamp: LocalDateTime
            - details: Map<String, Object>
            + of(errorCode: ErrorCode): ErrorResponse
            + of(errorCode: ErrorCode, details: Map<String, Object>): ErrorResponse
        }

        class "PageResponse<T>" {
            - content: List<T>
            - totalElements: long
            - totalPages: int
            - number: int
            - size: int
            - first: boolean
            - last: boolean
            + of(content: List<T>, pageable: Pageable, total: long): PageResponse<T>
        }
    }

    package "exception" {
        interface ErrorCode {
            + getCode(): String
            + getMessage(): String
        }

        enum CommonErrorCode implements ErrorCode {
            COMMON_001
            COMMON_002
            COMMON_003
            COMMON_004
            COMMON_005
            NOT_FOUND
            INVALID_INPUT_VALUE
            AUTH_001 ~ AUTH_005
            USER_001 ~ USER_005
            EVENT_001 ~ EVENT_005
            JOB_001 ~ JOB_004
            AI_001 ~ AI_004
            CONTENT_001 ~ CONTENT_004
            DIST_001 ~ DIST_004
            PART_001 ~ PART_008
            ANALYTICS_001 ~ ANALYTICS_003
            EXTERNAL_001 ~ EXTERNAL_003
            DB_001 ~ DB_004
            REDIS_001 ~ REDIS_003
            KAFKA_001 ~ KAFKA_003
            - code: String
            - message: String
            + getCode(): String
            + getMessage(): String
        }

        class BusinessException extends RuntimeException {
            - errorCode: ErrorCode
            - details: String
            + BusinessException(errorCode: ErrorCode)
            + BusinessException(errorCode: ErrorCode, message: String)
            + BusinessException(errorCode: ErrorCode, message: String, details: String)
            + BusinessException(errorCode: ErrorCode, cause: Throwable)
            + BusinessException(errorCode: ErrorCode, message: String, details: String, cause: Throwable)
            + getErrorCode(): ErrorCode
            + getDetails(): String
        }

        class InfraException extends RuntimeException {
            - errorCode: ErrorCode
            - details: String
            + InfraException(errorCode: ErrorCode)
            + InfraException(errorCode: ErrorCode, message: String)
            + InfraException(errorCode: ErrorCode, cause: Throwable)
            + getErrorCode(): ErrorCode
            + getDetails(): String
        }
    }

    package "util" {
        class ValidationUtil {
            + requireNonNull(object: Object, errorCode: ErrorCode): void
            + requireNonNull(object: Object, errorCode: ErrorCode, message: String): void
            + requireNotBlank(str: String, errorCode: ErrorCode): void
            + requireNotBlank(str: String, errorCode: ErrorCode, message: String): void
            + require(condition: boolean, errorCode: ErrorCode): void
            + require(condition: boolean, errorCode: ErrorCode, message: String): void
            + requireValidPhoneNumber(phoneNumber: String, errorCode: ErrorCode): void
            + requireValidEmail(email: String, errorCode: ErrorCode): void
            + requireValidBusinessNumber(businessNumber: String, errorCode: ErrorCode): void
            + requirePositive(value: long, errorCode: ErrorCode): void
            + requireNonNegative(value: long, errorCode: ErrorCode): void
            + requireInRange(value: long, min: long, max: long, errorCode: ErrorCode): void
        }

        class StringUtil {
            + isBlank(str: String): boolean
            + isNotBlank(str: String): boolean
            + hasText(str: String): boolean
            + isEmpty(str: String): boolean
            + isNotEmpty(str: String): boolean
            + isValidEmail(email: String): boolean
            + isValidPhoneNumber(phoneNumber: String): boolean
            + isValidBusinessNumber(businessNumber: String): boolean
            + maskEmail(email: String): String
            + maskPhoneNumber(phoneNumber: String): String
            + generateRandomString(length: int): String
            + removeSpecialCharacters(str: String): String
        }

        class DateTimeUtil {
            + now(): LocalDateTime
            + nowZoned(): ZonedDateTime
            + toEpochMilli(dateTime: LocalDateTime): long
            + fromEpochMilli(epochMilli: long): LocalDateTime
            + format(dateTime: LocalDateTime, pattern: String): String
            + parse(dateTimeString: String, pattern: String): LocalDateTime
            + isAfter(dateTime1: LocalDateTime, dateTime2: LocalDateTime): boolean
            + isBefore(dateTime1: LocalDateTime, dateTime2: LocalDateTime): boolean
            + isDateInRange(target: LocalDateTime, start: LocalDateTime, end: LocalDateTime): boolean
            + getDaysBetween(start: LocalDateTime, end: LocalDateTime): long
        }

        class EncryptionUtil {
            + encrypt(plainText: String): String
            + decrypt(encryptedText: String): String
            + hash(plainText: String): String
            + matches(plainText: String, hashedText: String): boolean
            + generateSalt(): String
            + encryptWithSalt(plainText: String, salt: String): String
        }
    }

    package "security" {
        class JwtAuthenticationFilter extends OncePerRequestFilter {
            - jwtTokenProvider: JwtTokenProvider
            - userDetailsService: UserDetailsService
            + doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain): void
            - extractTokenFromRequest(request: HttpServletRequest): String
            - authenticateUser(token: String): void
        }

        interface JwtTokenProvider {
            + generateToken(userDetails: UserDetails): String
            + validateToken(token: String): boolean
            + getUsernameFromToken(token: String): String
            + getExpirationDateFromToken(token: String): Date
        }
    }
}

' 관계 정의
BusinessException --> ErrorCode : uses
InfraException --> ErrorCode : uses
ValidationUtil --> ErrorCode : uses
ValidationUtil --> StringUtil : uses
ErrorResponse --> ErrorCode : uses

note top of BaseTimeEntity : JPA Auditing을 위한 기본 엔티티\n모든 도메인 엔티티가 상속
note top of "ApiResponse<T>" : 모든 API 응답을 감싸는\n표준 응답 포맷
note top of CommonErrorCode : 시스템 전체에서 사용하는\n표준 에러 코드
note top of ValidationUtil : 비즈니스 로직에서 사용하는\n공통 유효성 검증 기능

@enduml