Skip to content

feat: 토스 페이먼츠 결제 승인 외부 api 멱등키 헤더 추가 및 게스트 티켓 양식 수정 #228#229

Merged
sjk4618 merged 8 commits into
devfrom
feat/#228
Jan 31, 2026
Merged

feat: 토스 페이먼츠 결제 승인 외부 api 멱등키 헤더 추가 및 게스트 티켓 양식 수정 #228#229
sjk4618 merged 8 commits into
devfrom
feat/#228

Conversation

@sjk4618
Copy link
Copy Markdown
Member

@sjk4618 sjk4618 commented Jan 31, 2026

🔥Pull requests

⛳️ 작업한 브랜치

👷 작업한 내용

  • 토스 페이먼츠 api 멱등키 추가
  • 게스트 티켓 이메일 양식 수정

🚨 참고 사항

@sjk4618 sjk4618 self-assigned this Jan 31, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 31, 2026

📝 Walkthrough

Summary by CodeRabbit

릴리스 노트

  • Bug Fixes

    • 이메일 제목과 템플릿의 이벤트 타입 표시를 개선해 Gmail 스레드 분할 문제 완화 및 표시명 일관화
  • New Features

    • 이벤트 이미지 일괄(배치) 조회로 조회 성능 및 안정성 향상
    • 결제 요청에 Idempotency-Key 헤더 추가로 중복 처리 방지
  • Chores

    • 로드테스트 관련 경로를 .gitignore에 추가
    • 보안 설정 코드 재포매팅

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

토스 페이먼츠 API에 Idempotency-Key 헤더가 추가되고, 게스트 티켓 이메일의 이벤트 타입 표시와 제목 포맷이 변경되었으며, 이벤트 썸네일 조회가 개별 호출에서 배치 조회(Map)로 전환되고 관련 시그니처/레포지토리 메서드가 추가 및 조정되었습니다. 일부 코드 정렬·포맷 변경도 포함됩니다.

Changes

Cohort / File(s) Summary
토스 페이먼츠 멱등키
src/main/java/com/permitseoul/permitserver/domain/payment/api/client/TossPaymentClient.java, src/main/java/com/permitseoul/permitserver/domain/payment/api/service/PaymentService.java
Feign 클라이언트의 purchaseConfirm/cancelPayment@RequestHeader("Idempotency-Key") 파라미터 추가; PaymentService에서 해당 헤더 전달을 위해 호출 시그니처에 orderId/paymentKey 인자 추가.
게스트 티켓 이메일
src/main/java/com/permitseoul/permitserver/domain/admin/util/GuestTicketEmailSender.java
이메일 템플릿 컨텍스트 및 제목에서 eventType.name()eventType.getDisplayName()(Lombok-generated)으로 변경하고, 제목 앞에 제로폭 공백(U+200B) 삽입.
이벤트 이미지 배치 조회
src/main/java/com/permitseoul/permitserver/domain/event/api/service/EventService.java, src/main/java/com/permitseoul/permitserver/domain/eventimage/core/component/EventImageRetriever.java, src/main/java/com/permitseoul/permitserver/domain/eventimage/core/repository/EventImageRepository.java
개별 썸네일 호출을 배치 조회로 전환: EventImageRetriever.findAllThumbnailsByEventIds(List<Long>) 추가, Repository에 findAllThumbnailsByEventIds(...) 쿼리 추가, EventService.getAllVisibleEvents()에서 이벤트 ID 집합을 이용해 한 번에 썸네일 맵을 조회하도록 리팩터링 및 관련 메서드 시그니처 변경.
이벤트 타입 및 조회자
src/main/java/com/permitseoul/permitserver/domain/event/core/domain/EventType.java, src/main/java/com/permitseoul/permitserver/domain/event/core/component/EventRetriever.java
EventType enum에 displayName 필드 및 Lombok 어노테이션(@Getter, @RequiredArgsConstructor) 추가. EventRetriever에 일부 임포트와 final 키워드/정렬 변경(비기능적).
저장소 메서드 확장
src/main/java/com/permitseoul/permitserver/domain/eventimage/core/repository/EventImageRepository.java
기존 메서드 파라미터에 final 추가 및 배치 조회용 findAllThumbnailsByEventIds(@Param("eventIds") List<Long>) 메서드 추가.
설정·포맷·기타
src/main/java/com/permitseoul/permitserver/global/config/SecurityConfig.java, .gitignore
SecurityConfig 포맷/정렬 변경(무결성 유지), 로드 테스트/퍼포먼스 테스트용 소스 파일들을 .gitignore에 추가.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant EventService as EventService
    participant ImageRetriever as EventImageRetriever
    participant ImageRepo as EventImageRepository
    participant DB as Database

    Client->>EventService: getAllVisibleEvents()
    EventService->>EventService: collect eventIds
    EventService->>ImageRetriever: findAllThumbnailsByEventIds(eventIds)
    ImageRetriever->>ImageRepo: findAllThumbnailsByEventIds(eventIds)
    ImageRepo->>DB: SELECT ... WHERE eventId IN (...) AND sequence = 0
    DB-->>ImageRepo: EventImageEntity List
    ImageRepo-->>ImageRetriever: EventImageEntity List
    ImageRetriever->>ImageRetriever: collect to Map<Long, EventImage>
    ImageRetriever-->>EventService: Map<Long, EventImage>
    EventService->>EventService: filteringEventByEventType(events, type, thumbnailMap)
    EventService-->>Client: List<EventAllResponse.EventInfo> (with thumbnails)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
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.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 토스 페이먼츠 멱등키 헤더 추가와 게스트 티켓 양식 수정이라는 변경사항의 핵심 내용을 명확하게 반영하고 있습니다.
Description check ✅ Passed PR 설명은 토스 페이먼츠 API 멱등키 추가와 게스트 티켓 이메일 양식 수정이라는 변경사항과 관련이 있습니다.
Linked Issues check ✅ Passed PR의 변경사항이 이슈 #228의 요구사항을 충족합니다: (1) TossPaymentClient에 Idempotency-Key 헤더 추가, (2) GuestTicketEmailSender에서 게스트 티켓 이메일 양식 수정, (3) 이벤트 없을 경우 방어 로직 추가 (EventService, EventRetriever).
Out of Scope Changes check ✅ Passed EventType에 displayName 필드 추가와 EventImageRetriever의 배치 썸네일 조회 메서드는 이메일 양식 개선을 위한 event type 표시 방식 변경과 성능 최적화와 관련된 범위 내 변경으로 보입니다.

✏️ 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 feat/#228

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.

Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/com/permitseoul/permitserver/domain/payment/api/service/PaymentService.java (1)

331-336: ⚠️ Potential issue | 🔴 Critical

Toss Payments 취소 API의 멱등키 사용 규칙을 수정해야 합니다.

Toss Payments 공식 API 스펙에 따르면 Idempotency-Key요청마다 고유한 랜덤 값으로 생성해야 하며, 최대 300자, 유효 기간 15일입니다. 현재 코드는 paymentKey를 직접 멱등키로 사용하고 있는데, 이는 명시적으로 권장되지 않는 방식입니다.

  • 문제: 동일 결제에 대해 다른 취소 사유나 재시도가 필요한 경우, 동일한 멱등키를 재사용하면 이전 취소 응답이 반환되거나 요청이 거부될 수 있습니다.
  • 해결책: 각 취소 요청마다 고유한 멱등키를 생성하고 저장하는 로직이 필요합니다. orderId 또는 고유한 취소 ID 기반으로 일관성 있게 생성하되, 단순히 paymentKey를 재사용하지 마세요.
🤖 Fix all issues with AI agents
In
`@src/main/java/com/permitseoul/permitserver/domain/eventimage/core/component/EventImageRetriever.java`:
- Around line 28-39: findAllThumbnailsByEventIds currently throws
EventImageNotFoundException even when the input eventIds list is empty; change
the method (EventImageRetriever.findAllThumbnailsByEventIds) to return an empty
map immediately if eventIds is null or empty instead of querying, and only throw
EventImageNotFoundException when eventIds was provided (non-empty) but the
repository call (eventImageRepository.findAllThumbnailsByEventIds) returned an
empty result; use a null/empty check on the eventIds parameter and return
Collections.emptyMap() (or Map.of()) for empty input, leaving the existing
collect/exception logic for the non-empty case.
🧹 Nitpick comments (2)
.gitignore (1)

47-54: 중복된 Load Tests 섹션을 하나로 정리해 주세요.

### Load Tests ### 블록과 load-tests/ 항목이 두 번 반복되어 있습니다. 중복을 제거하고 새 경로만 해당 블록에 포함시키면 유지보수성이 좋아집니다.

♻️ 정리 예시
 ### Load Tests ###
 load-tests/
 
-### Load Tests ###
-load-tests/
 src/main/java/com/permitseoul/permitserver/domain/reservation/api/controller/PerformanceTestController.java
 src/main/java/com/permitseoul/permitserver/domain/reservation/api/service/ReservationDBLockTestService.java
 src/main/java/com/permitseoul/permitserver/domain/reservation/api/service/ReservationPerformanceTestService.java
src/main/java/com/permitseoul/permitserver/domain/admin/util/GuestTicketEmailSender.java (1)

58-58: ZWSP 리터럴을 이스케이프 표기로 명시해 가독성 개선 권장.
보이지 않는 문자라 유지보수 시 혼란이 생길 수 있어 \u200B 표기가 안전합니다.

♻️ 제안 변경
-            helper.setSubject("​[" + eventType.getDisplayName() + "] Guest Ticket Info"); //Gmail thread subject가 맨앞 "[]" 태그를 무시/정규화하는 경우가 있어, Zero-width space(U+200B)로 패턴 인식을 회피
+            helper.setSubject("\u200B[" + eventType.getDisplayName() + "] Guest Ticket Info"); //Gmail thread subject가 맨앞 "[]" 태그를 무시/정규화하는 경우가 있어, Zero-width space(U+200B)로 패턴 인식을 회피

Comment on lines +28 to +39
@Transactional(readOnly = true)
public Map<Long, EventImage> findAllThumbnailsByEventIds(final List<Long> eventIds) {
final Map<Long, EventImage> eventImageMap = eventImageRepository.findAllThumbnailsByEventIds(eventIds).stream()
.collect(Collectors.toMap(
EventImageEntity::getEventId,
EventImage::fromEntity,
(a, b) -> a)
);
if(ObjectUtils.isEmpty(eventImageMap)) {
throw new EventImageNotFoundException();
}
return eventImageMap;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

eventIds가 비어 있을 때 예외가 발생할 수 있습니다.
빈 입력에서도 map이 empty라 EventImageNotFoundException이 발생해, 이벤트 0건 상태에서 404가 날 수 있습니다. 빈 입력은 빈 맵 반환으로 처리하는 편이 안전합니다.

🐛 제안 수정
     `@Transactional`(readOnly = true)
     public Map<Long, EventImage> findAllThumbnailsByEventIds(final List<Long> eventIds) {
+        if (ObjectUtils.isEmpty(eventIds)) {
+            return Map.of();
+        }
         final Map<Long, EventImage> eventImageMap = eventImageRepository.findAllThumbnailsByEventIds(eventIds).stream()
                 .collect(Collectors.toMap(
                         EventImageEntity::getEventId,
                         EventImage::fromEntity,
                         (a, b) -> a)
                 );
         if(ObjectUtils.isEmpty(eventImageMap)) {
             throw new EventImageNotFoundException();
         }
         return eventImageMap;
     }
🤖 Prompt for AI Agents
In
`@src/main/java/com/permitseoul/permitserver/domain/eventimage/core/component/EventImageRetriever.java`
around lines 28 - 39, findAllThumbnailsByEventIds currently throws
EventImageNotFoundException even when the input eventIds list is empty; change
the method (EventImageRetriever.findAllThumbnailsByEventIds) to return an empty
map immediately if eventIds is null or empty instead of querying, and only throw
EventImageNotFoundException when eventIds was provided (non-empty) but the
repository call (eventImageRepository.findAllThumbnailsByEventIds) returned an
empty result; use a null/empty check on the eventIds parameter and return
Collections.emptyMap() (or Map.of()) for empty input, leaving the existing
collect/exception logic for the non-empty case.

@sjk4618 sjk4618 merged commit d57a53c into dev Jan 31, 2026
2 checks passed
@sjk4618 sjk4618 deleted the feat/#228 branch January 31, 2026 08:41
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.

feat: 토스 페이먼츠 결제 승인 외부 api 멱등키 헤더 추가 및 게스트 티켓 양식 수정

1 participant