[#48] Tosspay mocking 서버 구현을 위한 멀티모듈 전환 #49

Merged
pricelees merged 39 commits from feat/#48 into main 2025-09-30 00:39:14 +00:00
4 changed files with 53 additions and 85 deletions
Showing only changes of commit 1cbece032f - Show all commits

View File

@ -14,6 +14,10 @@ enum class HttpStatus(
INTERNAL_SERVER_ERROR(500)
;
fun isClientError(): Boolean {
return code in 400..<500
}
fun value(): Int {
return code
}

View File

@ -1,9 +1,7 @@
package com.sangdol.common.log.web
package com.sangdol.common.web.asepct
import com.sangdol.common.log.config.LogType
import com.sangdol.common.log.message.ApiLogMessageConverter
import com.sangdol.common.log.message.ConvertResponseMessageRequest
import com.sangdol.common.log.message.getEndpoint
import com.sangdol.common.log.constant.LogType
import com.sangdol.common.web.support.log.WebLogMessageConverter
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.servlet.http.HttpServletRequest
@ -13,7 +11,6 @@ import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.MethodSignature
import org.slf4j.MDC
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestBody
@ -25,7 +22,7 @@ private val log: KLogger = KotlinLogging.logger {}
@Aspect
class ControllerLoggingAspect(
private val messageConverter: ApiLogMessageConverter,
private val messageConverter: WebLogMessageConverter,
) {
@Pointcut("execution(* com.sangdol..web..*Controller*.*(..))")
@ -34,10 +31,8 @@ class ControllerLoggingAspect(
@Around("allController()")
fun logAPICalls(joinPoint: ProceedingJoinPoint): Any? {
val startTime: Long = MDC.get("startTime").toLongOrNull() ?: System.currentTimeMillis()
val controllerPayload: Map<String, Any> = parsePayload(joinPoint)
val servletRequest: HttpServletRequest = servletRequest()
val controllerPayload: Map<String, Any> = parseControllerPayload(joinPoint)
log.info {
messageConverter.convertToControllerInvokedMessage(servletRequest, controllerPayload)
@ -45,29 +40,22 @@ class ControllerLoggingAspect(
try {
return joinPoint.proceed()
.also { logSuccess(servletRequest.getEndpoint(), startTime, it) }
.also { logSuccess(servletRequest, it as ResponseEntity<*>) }
} catch (e: Exception) {
throw e
}
}
private fun logSuccess(endpoint: String, startTime: Long, result: Any) {
val responseEntity = result as ResponseEntity<*>
var convertResponseMessageRequest = ConvertResponseMessageRequest(
type = LogType.CONTROLLER_SUCCESS,
endpoint = endpoint,
httpStatus = responseEntity.statusCode.value(),
startTime = startTime,
private fun logSuccess(servletRequest: HttpServletRequest, result: ResponseEntity<*>) {
val body: Any? = if (log.isDebugEnabled()) result.body else null
val logMessage = messageConverter.convertToResponseMessage(
type = LogType.SUCCEED,
servletRequest = servletRequest,
httpStatusCode = result.statusCode.value(),
responseBody = body,
)
if (log.isDebugEnabled()) {
convertResponseMessageRequest = convertResponseMessageRequest.copy(
body = responseEntity.body
)
}
val logMessage = messageConverter.convertToResponseMessage(convertResponseMessageRequest)
log.info { logMessage }
}
@ -75,14 +63,16 @@ class ControllerLoggingAspect(
return (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request
}
private fun parsePayload(joinPoint: JoinPoint): Map<String, Any> {
private fun parseControllerPayload(joinPoint: JoinPoint): Map<String, Any> {
val signature = joinPoint.signature as MethodSignature
val args = joinPoint.args
val payload = mutableMapOf<String, Any>()
payload["controller_method"] = joinPoint.signature.toShortString()
val payload = mutableMapOf<String, Any>(
"controller_method" to joinPoint.signature.toShortString()
)
val requestParams: MutableMap<String, Any> = mutableMapOf()
val pathVariables: MutableMap<String, Any> = mutableMapOf()
signature.method.parameters.forEachIndexed { index, parameter ->
val arg = args[index]
@ -97,9 +87,10 @@ class ControllerLoggingAspect(
parameter.getAnnotation(RequestParam::class.java)?.let {
requestParams[parameter.name] = arg
}
}.also {
if (pathVariables.isNotEmpty()) payload["path_variable"] = pathVariables
if (requestParams.isNotEmpty()) payload["request_param"] = requestParams
}
if (pathVariables.isNotEmpty()) payload["path_variable"] = pathVariables
if (requestParams.isNotEmpty()) payload["request_param"] = requestParams
return payload
}

View File

@ -1,19 +1,15 @@
package com.sangdol.roomescape.common.exception
package com.sangdol.common.web.exception
import com.sangdol.common.log.constant.LogType
import com.sangdol.common.types.exception.CommonErrorCode
import com.sangdol.common.types.exception.ErrorCode
import com.sangdol.common.types.exception.RoomescapeException
import com.sangdol.common.types.web.CommonErrorResponse
import com.sangdol.common.types.web.HttpStatus
import com.sangdol.roomescape.auth.exception.AuthException
import com.sangdol.roomescape.common.log.ApiLogMessageConverter
import com.sangdol.roomescape.common.log.ConvertResponseMessageRequest
import com.sangdol.roomescape.common.log.LogType
import com.sangdol.roomescape.common.log.getEndpoint
import com.sangdol.common.web.support.log.WebLogMessageConverter
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.servlet.http.HttpServletRequest
import org.slf4j.MDC
import org.springframework.http.ResponseEntity
import org.springframework.http.converter.HttpMessageNotReadableException
import org.springframework.web.bind.MethodArgumentNotValidException
@ -23,8 +19,8 @@ import org.springframework.web.bind.annotation.RestControllerAdvice
private val log: KLogger = KotlinLogging.logger {}
@RestControllerAdvice
class ExceptionControllerAdvice(
private val messageConverter: ApiLogMessageConverter
class GlobalExceptionHandler(
private val messageConverter: WebLogMessageConverter
) {
@ExceptionHandler(value = [RoomescapeException::class])
fun handleRoomException(
@ -35,14 +31,7 @@ class ExceptionControllerAdvice(
val httpStatus: HttpStatus = errorCode.httpStatus
val errorResponse = CommonErrorResponse(errorCode)
val type = if (e is AuthException) LogType.AUTHENTICATION_FAILURE else LogType.APPLICATION_FAILURE
logException(
type = type,
servletRequest = servletRequest,
httpStatus = httpStatus.value(),
errorResponse = errorResponse,
exception = e
)
logException(servletRequest, httpStatus, errorResponse, e)
return ResponseEntity
.status(httpStatus.value())
@ -54,26 +43,21 @@ class ExceptionControllerAdvice(
servletRequest: HttpServletRequest,
e: Exception
): ResponseEntity<CommonErrorResponse> {
val message: String = if (e is MethodArgumentNotValidException) {
if (e is MethodArgumentNotValidException) {
e.bindingResult.allErrors
.mapNotNull { it.defaultMessage }
.joinToString(", ")
} else {
e.message!!
}.also {
log.warn { "[ExceptionControllerAdvice] Invalid Request Value Exception occurred: $it" }
}
log.debug { "[ExceptionControllerAdvice] Invalid Request Value Exception occurred: $message" }
val errorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE
val httpStatus: HttpStatus = errorCode.httpStatus
val errorResponse = CommonErrorResponse(errorCode)
logException(
type = LogType.APPLICATION_FAILURE,
servletRequest = servletRequest,
httpStatus = httpStatus.value(),
errorResponse = errorResponse,
exception = e
)
logException(servletRequest, httpStatus, errorResponse, e)
return ResponseEntity
.status(httpStatus.value())
@ -91,13 +75,7 @@ class ExceptionControllerAdvice(
val httpStatus: HttpStatus = errorCode.httpStatus
val errorResponse = CommonErrorResponse(errorCode)
logException(
type = LogType.UNHANDLED_EXCEPTION,
servletRequest = servletRequest,
httpStatus = httpStatus.value(),
errorResponse = errorResponse,
exception = e
)
logException(servletRequest, httpStatus, errorResponse, e)
return ResponseEntity
.status(httpStatus.value())
@ -105,25 +83,21 @@ class ExceptionControllerAdvice(
}
private fun logException(
type: LogType,
servletRequest: HttpServletRequest,
httpStatus: Int,
httpStatus: HttpStatus,
errorResponse: CommonErrorResponse,
exception: Exception
) {
val commonRequest = ConvertResponseMessageRequest(
type = type,
endpoint = servletRequest.getEndpoint(),
httpStatus = httpStatus,
startTime = MDC.get("startTime")?.toLongOrNull(),
body = errorResponse,
)
val type = if (httpStatus.isClientError()) LogType.APPLICATION_FAILURE else LogType.UNHANDLED_EXCEPTION
val actualException: Exception? = if (errorResponse.message == exception.message) null else exception
val logMessage = if (errorResponse.message == exception.message) {
messageConverter.convertToResponseMessage(commonRequest)
} else {
messageConverter.convertToResponseMessage(commonRequest.copy(exception = exception))
}
val logMessage = messageConverter.convertToResponseMessage(
type = type,
servletRequest = servletRequest,
httpStatusCode = httpStatus.value(),
responseBody = errorResponse,
exception = actualException
)
log.warn { logMessage }
}

View File

@ -1,13 +1,13 @@
package com.sangdol.common.log.web
package com.sangdol.common.web.servlet
import com.sangdol.common.log.message.ApiLogMessageConverter
import com.sangdol.common.utils.MdcPrincipalIdUtil
import com.sangdol.common.utils.MdcStartTimeUtil
import com.sangdol.common.web.support.log.WebLogMessageConverter
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.slf4j.MDC
import org.springframework.web.filter.OncePerRequestFilter
import org.springframework.web.util.ContentCachingRequestWrapper
import org.springframework.web.util.ContentCachingResponseWrapper
@ -15,7 +15,7 @@ import org.springframework.web.util.ContentCachingResponseWrapper
private val log: KLogger = KotlinLogging.logger {}
class HttpRequestLoggingFilter(
private val messageConverter: ApiLogMessageConverter
private val messageConverter: WebLogMessageConverter
) : OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
@ -27,14 +27,13 @@ class HttpRequestLoggingFilter(
val cachedRequest = ContentCachingRequestWrapper(request)
val cachedResponse = ContentCachingResponseWrapper(response)
val startTime = System.currentTimeMillis()
MDC.put("startTime", startTime.toString())
MdcStartTimeUtil.setCurrentTime()
try {
filterChain.doFilter(cachedRequest, cachedResponse)
cachedResponse.copyBodyToResponse()
} finally {
MDC.remove("startTime")
MdcStartTimeUtil.clear()
MdcPrincipalIdUtil.clear()
}
}