Skip to content

[REFACTOR] RestClient 로깅 분리#17

Merged
unifolio0 merged 2 commits intodevelopfrom
refactor/#16
Feb 12, 2026
Merged

[REFACTOR] RestClient 로깅 분리#17
unifolio0 merged 2 commits intodevelopfrom
refactor/#16

Conversation

@unifolio0
Copy link
Contributor

@unifolio0 unifolio0 commented Feb 12, 2026

closed #16

작업 내용

참고 사항

Summary by CodeRabbit

  • 리팩토링
    • TossPayments API 호출 관련 로깅과 실행시간 측정이 중앙화되어 추적·모니터링이 개선되었습니다.
  • 신규 기능
    • 외부 결제 연동의 요청/응답 로깅이 자동화되어 문제 진단이 더 쉬워졌습니다.
  • 잡업(Chore)
    • 런타임에서의 AOP 기반 로깅 지원을 위한 빌드 설정이 추가되었습니다.

@unifolio0 unifolio0 self-assigned this Feb 12, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

external 모듈에 Spring AOP 의존성을 추가하고, TosspaymentsClient의 로깅/예외 처리 코드를 제거한 뒤 이를 대체하는 TosspaymentsLoggingAspect를 새로 도입하여 TosspaymentsClient의 공개 메서드를 AOP로 가로챕니다.

Changes

Cohort / File(s) Summary
의존성
external/build.gradle
org.springframework.boot:spring-boot-starter-aop 구현(dependency) 추가
새 AOP Aspect
external/src/main/java/com/samhap/kokomen/global/aop/TosspaymentsLoggingAspect.java
TosspaymentsClient의 public 메서드를 around 어드바이스로 인터셉트하여 요청/응답 로그 및 StopWatch 기반 실행시간 측정, 예외 전파 처리 구현
클라이언트 변경
external/src/main/java/com/samhap/kokomen/payment/external/TosspaymentsClient.java
클래스 수준 로깅/타이밍 애노테이션 및 메서드 내부의 로깅·HttpClientErrorException 처리 제거, confirmPayment/cancelPayment 본문 단순화

Sequence Diagram(s)

sequenceDiagram
  participant ClientApp as Client App
  participant TosspayClient as TosspaymentsClient
  participant Aspect as TosspaymentsLoggingAspect
  participant ExternalAPI as Toss API

  ClientApp->>TosspayClient: invoke confirmPayment(...)
  TosspayClient->>Aspect: around advice intercepts (ProceedingJoinPoint)
  Aspect->>Aspect: log request, start StopWatch
  Aspect->>TosspayClient: proceed()
  TosspayClient->>ExternalAPI: HTTP call
  ExternalAPI-->>TosspayClient: HTTP response
  TosspayClient-->>Aspect: return response
  Aspect->>Aspect: stop StopWatch, log response + elapsed
  Aspect-->>ClientApp: return response
Loading

Estimated code review effort

🎯 3 (중간) | ⏱️ ~20분

Possibly related PRs

Poem

🐰 토스 로그는 내가 맡을게,
스프링의 실로 깔끔히 감싸고,
클라이언트는 제일의 일만 하네,
바운스 한 번에 시간 재고,
당근보다 쫄깃한 코드 정리 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive 연결된 이슈 #16은 구체적인 요구사항이나 수용 기준이 없어 평가할 명시적 목표가 부족합니다. 이슈 #16에 구체적인 요구사항, 수용 기준, 또는 검증 가능한 목표를 추가하여 PR 변경사항과 비교할 기준을 제공하세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 주요 변경사항인 RestClient 로깅을 AOP 기반으로 분리하는 리팩토링을 명확하게 설명합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항(AOP 의존성 추가, 로깅 Aspect 구현, TosspaymentsClient 정리)이 RestClient 로깅 분리라는 범위 내에 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#16

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @unifolio0, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 TosspaymentsClient 내부에 분산되어 있던 토스페이먼츠 API 호출 관련 로깅 및 예외 처리 로직을 Spring AOP를 사용하여 별도의 Aspect로 분리하는 리팩토링을 수행합니다. 이를 통해 TosspaymentsClient의 코드를 더욱 간결하게 만들고, 로깅 로직의 재사용성과 유지보수성을 향상시키는 것을 목표로 합니다.

Highlights

  • AOP 의존성 추가: Spring AOP 기능을 활용하기 위해 spring-boot-starter-aop 의존성을 build.gradle 파일에 추가했습니다.
  • 토스페이먼츠 로깅 Aspect 구현: TosspaymentsLoggingAspect 클래스를 새로 추가하여 토스페이먼츠 API 호출에 대한 요청, 응답, 실패 로깅 및 실행 시간 측정을 중앙화했습니다.
  • RestClient 로깅 분리: TosspaymentsClient 클래스에서 직접적인 로깅 코드와 예외 처리 로직을 제거하고, 새로 생성된 AOP Aspect가 이를 처리하도록 변경하여 비즈니스 로직과 로깅 로직을 분리했습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • external/build.gradle
    • spring-boot-starter-aop 의존성이 추가되었습니다.
  • external/src/main/java/com/samhap/kokomen/global/aop/TosspaymentsLoggingAspect.java
    • 토스페이먼츠 API 호출 로깅을 위한 Aspect 클래스가 추가되었습니다.
  • external/src/main/java/com/samhap/kokomen/payment/external/TosspaymentsClient.java
    • 직접적인 로깅 코드, @Slf4j 어노테이션, @ExecutionTimer 어노테이션 및 HttpClientErrorException 처리 로직이 제거되었습니다.
Activity
  • 이 PR은 이슈 #16을 해결했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

github-actions bot commented Feb 12, 2026

Test Results

1 tests   1 ✅  0s ⏱️
1 suites  0 💤
1 files    0 ❌

Results for commit 607688d.

♻️ This comment has been updated with latest results.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This PR is a good refactoring that separates the logging logic of RestClient using AOP, making the TosspaymentsClient code cleaner through separation of concerns. However, the current implementation in TosspaymentsLoggingAspect logs full request arguments, response bodies, and error response bodies, which contain sensitive transaction identifiers (e.g., paymentKey) and Personally Identifiable Information (e.g., orderName). Logging such data poses a significant security and compliance risk. It is crucial to address this by making the exception handling logic more robust and ensuring sensitive information is not logged. Consider implementing redaction/masking for sensitive fields or logging only non-sensitive metadata.

String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();

log.info("[토스페이먼츠 API 요청] {} - args: {}", methodName, args);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The logging statement includes the full request arguments (args), which contain sensitive information such as paymentKey and orderId. Logging these identifiers can lead to the exposure of sensitive transaction data and violates security policies. It is recommended to log only non-sensitive metadata or to implement a masking mechanism for sensitive fields, perhaps by overriding the toString() method of related DTOs.

Suggested change
log.info("[토스페이먼츠 API 요청] {} - args: {}", methodName, args);
log.info("[토스페이먼츠 API 요청] {}", methodName);

Comment on lines 30 to 31
log.info("[토스페이먼츠 API 응답] {} - {}ms - response: {}",
methodName, stopWatch.getTotalTimeMillis(), result);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The logging statement includes the full response body (result), which contains Personally Identifiable Information (PII) such as orderName and sensitive transaction details like paymentKey. Logging such data can violate privacy regulations and increase the risk of exposure. It is recommended to avoid logging the entire response object and instead log only necessary, non-sensitive fields. Additionally, ensure the try-catch block is robust enough to handle all Throwables, not just HttpClientErrorException, to prevent logging omissions and ensure stopWatch always stops.

            log.info("[토스페이먼츠 API 응답] {} - {}ms", methodName, stopWatch.getTotalTimeMillis());

Comment on lines 35 to 37
log.error("[토스페이먼츠 API 실패] {} - {}ms - status: {}, body: {}",
methodName, stopWatch.getTotalTimeMillis(),
e.getStatusCode(), e.getResponseBodyAsString(), e);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The error logging statement includes the full error response body (e.getResponseBodyAsString()), which may contain PII or sensitive system information returned by the external API during a failure. Logging raw error responses can lead to unintended data leakage. It is recommended to log the status code and exception details without including the full response body.

            log.error("[토스페이먼츠 API 실패] {} - {}ms - status: {}",
                    methodName, stopWatch.getTotalTimeMillis(),
                    e.getStatusCode(), e);

Comment on lines 30 to 31
log.info("[토스페이먼츠 API 응답] {} - {}ms - response: {}",
methodName, stopWatch.getTotalTimeMillis(), result);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

응답 객체 전체를 로깅하면 민감한 정보가 포함될 수 있고 로그가 너무 길어질 수 있습니다. 요청 로깅과 마찬가지로, 응답 객체의 toString()을 재정의하여 필요한 정보만 로깅하거나 민감한 정보를 마스킹하는 것이 좋습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
`@external/src/main/java/com/samhap/kokomen/global/aop/TosspaymentsLoggingAspect.java`:
- Line 23: The logging in TosspaymentsLoggingAspect currently prints raw request
arguments and responses (args and result), which can expose sensitive payment
data; update the code to sanitize/redact sensitive fields before logging by
introducing or using a sanitizer utility (e.g., redactSensitiveFields(Object) or
a TosspaymentsLoggingSanitizer) and call it where methodName and args are logged
and where result is logged, ensuring sensitive keys (amounts, card numbers,
keys, tokens, PII) are masked or omitted and that you do not rely on raw
toString() of request/response objects.
- Around line 12-15: Reorder the class annotations so Spring annotations come
before Lombok: move `@Aspect`, `@Component` and `@Order`(1) to appear above `@Slf4j` in
the TosspaymentsLoggingAspect class; this ensures the Spring annotations
(`@Aspect`, `@Component`, `@Order`) are declared before the Lombok `@Slf4j` to comply
with the project coding guideline.
- Around line 27-39: The current TosspaymentsLoggingAspect catch only handles
HttpClientErrorException so server-side errors (HttpServerErrorException),
timeouts/connection failures (ResourceAccessException) and other
RestClientExceptions aren't logged and stopWatch.stop() may not run; update the
try/catch in the method where joinPoint.proceed() is invoked to ensure
stopWatch.stop() is always called (move stopWatch.stop() to a finally block) and
add a broader catch for RestClientException (or Exception) to log the error with
methodName, stopWatch.getTotalTimeMillis(), and relevant exception details
before rethrowing; reference the joinPoint.proceed(), stopWatch.stop(),
methodName, and HttpClientErrorException in your changes.

Comment on lines +12 to +15
@Slf4j
@Order(1)
@Aspect
@Component
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

어노테이션 순서: Spring 어노테이션을 Lombok 어노테이션보다 앞에 배치해주세요.

코딩 가이드라인에 따르면 Spring 어노테이션이 Lombok 어노테이션보다 먼저 위치해야 합니다.

♻️ 수정 제안
-@Slf4j
 `@Order`(1)
 `@Aspect`
 `@Component`
+@Slf4j
 public class TosspaymentsLoggingAspect {

As per coding guidelines, "Use Lombok for constructors, getters/setters with Spring annotations before Lombok annotations".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Slf4j
@Order(1)
@Aspect
@Component
`@Order`(1)
`@Aspect`
`@Component`
`@Slf4j`
🤖 Prompt for AI Agents
In
`@external/src/main/java/com/samhap/kokomen/global/aop/TosspaymentsLoggingAspect.java`
around lines 12 - 15, Reorder the class annotations so Spring annotations come
before Lombok: move `@Aspect`, `@Component` and `@Order`(1) to appear above `@Slf4j` in
the TosspaymentsLoggingAspect class; this ensures the Spring annotations
(`@Aspect`, `@Component`, `@Order`) are declared before the Lombok `@Slf4j` to comply
with the project coding guideline.

String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();

log.info("[토스페이먼츠 API 요청] {} - args: {}", methodName, args);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

결제 요청 인자(args)를 그대로 로깅하면 민감한 결제 정보가 노출될 수 있습니다.

args 배열에는 결제 금액, 키 등 민감 데이터가 포함될 수 있습니다. 마찬가지로 Line 31의 result 응답 객체도 민감 정보를 포함할 수 있습니다. 민감 필드를 마스킹하거나, toString()에서 민감 필드를 제외하는 방식을 고려해주세요.

🤖 Prompt for AI Agents
In
`@external/src/main/java/com/samhap/kokomen/global/aop/TosspaymentsLoggingAspect.java`
at line 23, The logging in TosspaymentsLoggingAspect currently prints raw
request arguments and responses (args and result), which can expose sensitive
payment data; update the code to sanitize/redact sensitive fields before logging
by introducing or using a sanitizer utility (e.g., redactSensitiveFields(Object)
or a TosspaymentsLoggingSanitizer) and call it where methodName and args are
logged and where result is logged, ensuring sensitive keys (amounts, card
numbers, keys, tokens, PII) are masked or omitted and that you do not rely on
raw toString() of request/response objects.

Comment on lines 27 to 39
try {
Object result = joinPoint.proceed();
stopWatch.stop();
log.info("[토스페이먼츠 API 응답] {} - {}ms - response: {}",
methodName, stopWatch.getTotalTimeMillis(), result);
return result;
} catch (HttpClientErrorException e) {
stopWatch.stop();
log.error("[토스페이먼츠 API 실패] {} - {}ms - status: {}, body: {}",
methodName, stopWatch.getTotalTimeMillis(),
e.getStatusCode(), e.getResponseBodyAsString(), e);
throw e;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

HttpClientErrorException만 캐치하면 서버 에러(5xx) 및 타임아웃이 로깅되지 않습니다.

HttpServerErrorException(5xx), ResourceAccessException(타임아웃/커넥션 에러) 등 다른 예외가 발생하면 로깅 없이 전파됩니다. 또한 이 경우 stopWatch.stop()도 호출되지 않습니다.

전체 예외를 로깅하려면 catch (Exception e) 또는 최소한 catch (RestClientException e)를 추가하거나, finally 블록에서 처리하는 것을 권장합니다.

🐛 수정 제안
         try {
             Object result = joinPoint.proceed();
             stopWatch.stop();
             log.info("[토스페이먼츠 API 응답] {} - {}ms - response: {}",
                     methodName, stopWatch.getTotalTimeMillis(), result);
             return result;
         } catch (HttpClientErrorException e) {
             stopWatch.stop();
             log.error("[토스페이먼츠 API 실패] {} - {}ms - status: {}, body: {}",
                     methodName, stopWatch.getTotalTimeMillis(),
                     e.getStatusCode(), e.getResponseBodyAsString(), e);
             throw e;
+        } catch (Exception e) {
+            if (stopWatch.isRunning()) {
+                stopWatch.stop();
+            }
+            log.error("[토스페이먼츠 API 실패] {} - {}ms", methodName, stopWatch.getTotalTimeMillis(), e);
+            throw e;
         }
🤖 Prompt for AI Agents
In
`@external/src/main/java/com/samhap/kokomen/global/aop/TosspaymentsLoggingAspect.java`
around lines 27 - 39, The current TosspaymentsLoggingAspect catch only handles
HttpClientErrorException so server-side errors (HttpServerErrorException),
timeouts/connection failures (ResourceAccessException) and other
RestClientExceptions aren't logged and stopWatch.stop() may not run; update the
try/catch in the method where joinPoint.proceed() is invoked to ensure
stopWatch.stop() is always called (move stopWatch.stop() to a finally block) and
add a broader catch for RestClientException (or Exception) to log the error with
methodName, stopWatch.getTotalTimeMillis(), and relevant exception details
before rethrowing; reference the joinPoint.proceed(), stopWatch.stop(),
methodName, and HttpClientErrorException in your changes.

@unifolio0 unifolio0 merged commit 4334b63 into develop Feb 12, 2026
2 of 3 checks passed
@unifolio0 unifolio0 deleted the refactor/#16 branch February 12, 2026 07:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] RestClient 로깅 분리

1 participant