@startuml !theme mono title 공통 컴포넌트 클래스 다이어그램 package "com.kt.event.common" { package "entity" { abstract class BaseTimeEntity { - createdAt: LocalDateTime - updatedAt: LocalDateTime } } package "dto" { class "ApiResponse" { - success: boolean - data: T - errorCode: String - message: String - timestamp: LocalDateTime + success(data: T): ApiResponse + success(): ApiResponse + error(errorCode: String, message: String): ApiResponse } class ErrorResponse { - success: boolean - errorCode: String - message: String - timestamp: LocalDateTime - details: Map + of(errorCode: ErrorCode): ErrorResponse + of(errorCode: ErrorCode, details: Map): ErrorResponse } class "PageResponse" { - content: List - totalElements: long - totalPages: int - number: int - size: int - first: boolean - last: boolean + of(content: List, pageable: Pageable, total: long): PageResponse } } 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" : 모든 API 응답을 감싸는\n표준 응답 포맷 note top of CommonErrorCode : 시스템 전체에서 사용하는\n표준 에러 코드 note top of ValidationUtil : 비즈니스 로직에서 사용하는\n공통 유효성 검증 기능 @enduml