-
Notifications
You must be signed in to change notification settings - Fork 2
feat: admin 타임테이블 최초 등록 api 구현 - #157 #158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughSummary by CodeRabbit릴리스 노트
Walkthrough타임테이블의 Area→Stage 전환과 Notion 연동 도입, 관리자 초기 타임테이블 등록 API 추가 및 외부 연동(Google/Kakao/Notion/Discord) 패키지 재배치와 보안·로깅·오류코드·설정 변경을 포함한 대규모 도메인 및 인프라 변경입니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Admin
participant Controller as AdminTimetableController
participant Service as AdminTimetableService
participant Notion as NotionProvider
participant Validator as NotionRelationValidator
participant Facade as AdminTimetableFacade
participant Savers as Savers (Timetable/Stage/Category/Block/Media)
Admin->>Controller: POST /api/admin/events/{eventId}/timetables/initial\n(payload: start/end + datasourceIds)
Controller->>Service: saveInitialTimetableInfo(eventId, ...)
Service->>Notion: getNotionStageDatasource(id)
Service->>Notion: getNotionCategoryDatasource(id)
Service->>Notion: getNotionTimetableDatasource(id)
Service->>Validator: validateNotionRelationIds(timetable, stage, category)
Service->>Facade: saveInitialTimetableInfos(eventId, times, ids, notionResponses)
activate Facade
Facade->>Savers: saveTimetable()
Facade->>Savers: saveAllTimetableStages(...)
Facade->>Savers: saveAllTimetableCategoryEntities(...)
Facade->>Savers: saveAllTimetableBlocks(...)
Facade->>Savers: saveAllBlockMedia(...)
deactivate Facade
Service-->>Controller: void
Controller-->>Admin: 200 OK (BaseResponse)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🧹 Nitpick comments (14)
src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordDevHttpLogSender.java (1)
19-19: 옵션: ObjectMapper 빈 주입 고려현재 static ObjectMapper는 정상적으로 작동하지만 (Jackson의 ObjectMapper는 읽기 작업에 대해 스레드 안전함), 테스트 용이성과 설정 커스터마이징을 위해 Spring 빈으로 주입하는 것을 고려할 수 있습니다.
src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketController.java (1)
24-29: PR 범위와의 연관성 확인 필요이 변경사항은 티켓 정보 조회 API의 경로 수정에 관한 것인데, PR 제목과 설명은 "admin 타임테이블 최초 등록 API 구현"으로 되어 있습니다.
- 이 변경사항이 타임테이블 기능 구현의 일부인 경우, PR 설명에 티켓 API 경로 변경에 대한 내용을 추가하는 것이 좋습니다
- 만약 관련이 없는 변경사항이라면, 별도의 PR로 분리하여 변경 이력을 명확히 하는 것을 권장합니다
src/main/java/com/permitseoul/permitserver/global/external/kakao/client/KakaoKAuthClient.java (1)
11-17: 폼 인코딩 명시로 상호운용성 강화 제안Kakao 토큰 엔드포인트는 x-www-form-urlencoded를 기대합니다. consumes를 명시해 예측 가능성을 높이는 것을 권장합니다.
- @PostMapping("/oauth/token") + @PostMapping(value = "/oauth/token", consumes = "application/x-www-form-urlencoded") KakaoAccessTokenResponse getKakaoAccessToken( @RequestParam("grant_type") String grantType, @RequestParam("client_id") String clientId, @RequestParam("redirect_uri") String redirectUri, @RequestParam("code") String code );src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/domain/entity/TimetableStageEntity.java (1)
25-29: sequence 제약 및 조회 인덱스 권장음수 방지를 위한 @min(0) 및 조회 빈번 컬럼(timetable_id, notion_stage_row_id) 인덱스를 권장합니다.
예시:
// 상단 import import jakarta.validation.constraints.Min; import jakarta.persistence.Index; @Table(name = "event_timetable_stage", indexes = { @Index(name = "idx_event_timetable_stage_tid", columnList = "timetable_id"), @Index(name = "uk_event_timetable_stage_notion_id", columnList = "notion_stage_row_id", unique = true) }) // ... @Min(0) @Column(name = "sequence", nullable = false) private int sequence;src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/domain/TimetableCategory.java (1)
15-25: 식별자 필드 네이밍 일관성 제안도메인은 categoryNotionId, 엔티티는 notionCategoryRowId로 상반된 접두/접미어 순서를 사용합니다. 유지보수성을 위해 동일한 네이밍(not ionCategoryRowId 또는 notionCategoryId 등)으로 통일을 권장합니다.
이 필드가 외부 API 응답/요청에 노출된다면 변경 영향도를 확인해 주세요.
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/exception/TimetableStageCoreException.java (1)
3-4: 표준 예외 생성자 추가를 권장합니다.기본 추상 클래스 구조는 적절하지만, 에러 메시지와 원인을 전달할 수 있는 표준 생성자들을 추가하면 더 유연한 예외 처리가 가능합니다.
다음과 같이 생성자를 추가하는 것을 고려해보세요:
public abstract class TimetableStageCoreException extends RuntimeException { + protected TimetableStageCoreException() { + super(); + } + + protected TimetableStageCoreException(String message) { + super(message); + } + + protected TimetableStageCoreException(String message, Throwable cause) { + super(message, cause); + } + + protected TimetableStageCoreException(Throwable cause) { + super(cause); + } }src/main/java/com/permitseoul/permitserver/domain/admin/timetable/category/core/component/AdminTimetableCategorySaver.java (1)
10-18: 일관된 구조로 잘 구현되었습니다.배치 저장 컴포넌트가
AdminTimetableStageSaver와 일관된 패턴으로 구현되었습니다.메서드 이름을 간소화하는 것을 고려해보세요:
- public void saveAllTimetableCategoryEntities(final List<TimetableCategoryEntity> timetableCategoryEntities) { + public void saveAll(final List<TimetableCategoryEntity> timetableCategoryEntities) { timetableCategoryRepository.saveAll(timetableCategoryEntities); }컴포넌트 이름이 이미 역할을 명확히 하므로 메서드 이름에서 "TimetableCategoryEntities" 반복을 줄일 수 있습니다.
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/blockmedia/core/component/AdminTimetableBlockMediaSaver.java (1)
3-3: 사용하지 않는 import를 제거하세요.Line 3의
TimetableBlockEntityimport는 코드에서 사용되지 않습니다.다음 diff를 적용하여 불필요한 import를 제거하세요:
-import com.permitseoul.permitserver.domain.eventtimetable.block.core.domain.entity.TimetableBlockEntity; import com.permitseoul.permitserver.domain.eventtimetable.blockmedia.domain.entity.TimetableBlockMediaEntity;src/main/java/com/permitseoul/permitserver/global/external/notion/NotionProvider.java (1)
17-21: 헤더 주입은 Feign RequestInterceptor로 통합 권장토큰/버전/컨텐츠 타입을 메서드마다 인자로 전달하지 말고, RequestInterceptor에서 공통 헤더로 주입하면 중복이 줄고 안전합니다. 또한 NotionProperties의 값(@notblank 등) 유효성 검증도 함께 구성해 주세요.
예시:
@Bean public RequestInterceptor notionAuthInterceptor(NotionProperties props) { return tpl -> { tpl.header("Authorization", "Bearer " + props.privateApiToken()); tpl.header("Notion-Version", props.notionVersion()); tpl.header("Content-Type", MediaType.APPLICATION_JSON_VALUE); }; }src/main/java/com/permitseoul/permitserver/global/external/notion/dto/NotionStageDatasourceResponse.java (1)
1-8: DTO 역직렬화 탄력성 확보를 위해 unknown 필드 무시 설정 권장Notion 응답 필드가 추가되어도 파싱 실패가 없도록 ignoreUnknown을 추가해 주세요. 다른 Notion DTO에도 동일 적용을 권장합니다.
package com.permitseoul.permitserver.global.external.notion.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.util.List; -public record NotionStageDatasourceResponse( +@JsonIgnoreProperties(ignoreUnknown = true) +public record NotionStageDatasourceResponse( List<StagePage> results ) {src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableService.java (2)
22-26: 미사용 의존성 제거AdminTimetableStageSaver 필드는 이 클래스에서 사용되지 않습니다. 제거해 주세요.
27-33: 시간 유효성 검증 추가 권장timetableStartAt이 timetableEndAt 이후인 경우를 사전에 차단해 주세요. 잘못된 데이터 저장을 방지합니다.
예시:
if (!timetableStartAt.isBefore(timetableEndAt)) { throw new AdminApiException(ErrorCode.INVALID_REQUEST); }src/main/java/com/permitseoul/permitserver/domain/eventtimetable/block/core/domain/entity/TimetableBlockEntity.java (1)
25-29: Notion ID 검증 권장timetableCategoryNotionId와 timetableStageNotionId가 String 타입으로 변경되었습니다. 이 필드들이 null이나 빈 문자열이 될 수 없도록 생성자나 팩토리 메서드에서 검증을 추가하는 것을 권장합니다.
생성자에 검증 로직을 추가할 수 있습니다:
private TimetableBlockEntity( long timetableId, String timetableCategoryNotionId, String timetableStageNotionId, LocalDateTime startAt, LocalDateTime endAt, String blockName, String artist, String information, String blockInfoRedirectUrl ) { validateDateTime(startAt, endAt); + validateNotionIds(timetableCategoryNotionId, timetableStageNotionId); this.timetableId = timetableId; this.timetableCategoryNotionId = timetableCategoryNotionId; this.timetableStageNotionId = timetableStageNotionId; this.startAt = startAt; this.endAt = endAt; this.blockName = blockName; this.artist = artist; this.information = information; this.blockInfoRedirectUrl = blockInfoRedirectUrl; } + + private void validateNotionIds(final String categoryNotionId, final String stageNotionId) { + if (categoryNotionId == null || categoryNotionId.isBlank()) { + throw new IllegalArgumentException("Category Notion ID cannot be null or empty"); + } + if (stageNotionId == null || stageNotionId.isBlank()) { + throw new IllegalArgumentException("Stage Notion ID cannot be null or empty"); + } + }src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java (1)
59-72: 기본 색상값 검토backgroundColor와 lineColor가 비어 있을 경우 빈 문자열("")로 API 응답에 반영됩니다. UI 렌더링이나 클라이언트 로직에서 유효한 CSS 색상값을 기대한다면, 기본 색상(예: "#FFFFFF")을 지정하거나 null 처리 로직을 추가하세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (60)
src/main/java/com/permitseoul/permitserver/PermitServerApplication.java(2 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/controller/AdminTimetableController.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/dto/req/TimetableInitialPostRequest.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableService.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/core/components/AdminTimetableSaver.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/block/core/component/AdminTimetableBlockSaver.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/blockmedia/core/component/AdminTimetableBlockMediaSaver.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/category/core/component/AdminTimetableCategorySaver.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/stage/core/AdminTimetableStageSaver.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/util/QrCodeUtil.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/auth/core/strategy/GoogleLoginStrategy.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/auth/core/strategy/KakaoLoginStrategy.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/component/TimetableAreaRetriever.java(0 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/domain/TimetableArea.java(0 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/domain/entity/TimetableAreaEntity.java(0 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/exception/TimetableAreaCoreException.java(0 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/exception/TimetableAreaNotFoundException.java(0 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/repository/TimetableAreaRepository.java(0 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/block/core/domain/TimetableBlock.java(2 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/block/core/domain/entity/TimetableBlockEntity.java(4 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/blockmedia/domain/entity/TimetableBlockMediaEntity.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/component/TimetableCategoryRetriever.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/domain/TimetableCategory.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/domain/entity/TimetableCategoryEntity.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/repository/TimetableCategoryRepository.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/component/TimetableStageRetriever.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/domain/TimetableStage.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/domain/entity/TimetableStageEntity.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/exception/TimetableStageCoreException.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/exception/TimetableStageNotFoundException.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/repository/TimetableStageRepository.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/api/dto/TimetableResponse.java(3 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/api/service/TimetableService.java(8 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/core/domain/Timetable.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/core/domain/entity/TimetableEntity.java(2 hunks)src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketController.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/config/SecurityConfig.java(2 hunks)src/main/java/com/permitseoul/permitserver/global/exception/PermitIllegalStateException.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordDevHttpLogSender.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/discord/client/DiscordFeignClient.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/google/GoogleProperties.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/google/client/GoogleClient.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/google/dto/GoogleTokenResponse.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/kakao/KakaoProperties.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/kakao/client/KakaoKApiClient.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/kakao/client/KakaoKAuthClient.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/kakao/dto/KakaoATInfoResponse.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/kakao/dto/KakaoAccessTokenResponse.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/notion/NotionProperties.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/notion/NotionProvider.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/notion/NotionRelationValidator.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/notion/client/NotionClient.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/notion/dto/NotionCategoryDatasourceResponse.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/notion/dto/NotionStageDatasourceResponse.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/notion/dto/NotionTimetableDatasourceResponse.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/response/code/ErrorCode.java(2 hunks)src/main/resources/logback/logback-dev-loki.xml(1 hunks)src/test/java/com/permitseoul/permitserver/PermitServerApplicationTests.java(1 hunks)
💤 Files with no reviewable changes (6)
- src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/exception/TimetableAreaNotFoundException.java
- src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/domain/entity/TimetableAreaEntity.java
- src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/domain/TimetableArea.java
- src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/component/TimetableAreaRetriever.java
- src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/repository/TimetableAreaRepository.java
- src/main/java/com/permitseoul/permitserver/domain/eventtimetable/area/core/exception/TimetableAreaCoreException.java
🧰 Additional context used
🧬 Code graph analysis (15)
src/main/java/com/permitseoul/permitserver/global/external/notion/NotionRelationValidator.java (1)
src/main/java/com/permitseoul/permitserver/global/exception/PermitIllegalStateException.java (1)
PermitIllegalStateException(3-4)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/blockmedia/core/component/AdminTimetableBlockMediaSaver.java (3)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.java (1)
Component(27-69)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/core/components/AdminTimetableSaver.java (1)
Component(11-32)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/block/core/component/AdminTimetableBlockSaver.java (1)
Component(11-21)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/category/core/component/AdminTimetableCategorySaver.java (1)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/stage/core/AdminTimetableStageSaver.java (1)
Component(10-18)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/core/components/AdminTimetableSaver.java (3)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.java (1)
Component(27-69)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/block/core/component/AdminTimetableBlockSaver.java (1)
Component(11-21)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/blockmedia/core/component/AdminTimetableBlockMediaSaver.java (1)
Component(11-19)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/component/TimetableStageRetriever.java (4)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/exception/TimetableStageNotFoundException.java (1)
TimetableStageNotFoundException(3-4)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/domain/TimetableStage.java (1)
RequiredArgsConstructor(7-26)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/api/service/TimetableService.java (1)
RequiredArgsConstructor(35-195)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/component/TimetableCategoryRetriever.java (1)
Component(13-36)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/controller/AdminTimetableController.java (1)
src/main/java/com/permitseoul/permitserver/global/response/ApiResponseUtil.java (1)
ApiResponseUtil(7-29)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.java (4)
src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java (1)
NotionResponseMapper(19-142)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/core/components/AdminTimetableSaver.java (1)
Component(11-32)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/block/core/component/AdminTimetableBlockSaver.java (1)
Component(11-21)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/blockmedia/core/component/AdminTimetableBlockMediaSaver.java (1)
Component(11-19)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/block/core/component/AdminTimetableBlockSaver.java (3)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.java (1)
Component(27-69)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/core/components/AdminTimetableSaver.java (1)
Component(11-32)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/blockmedia/core/component/AdminTimetableBlockMediaSaver.java (1)
Component(11-19)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/domain/TimetableStage.java (3)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/block/core/domain/TimetableBlock.java (1)
Getter(9-37)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/domain/TimetableCategory.java (1)
RequiredArgsConstructor(7-28)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/api/service/TimetableService.java (1)
RequiredArgsConstructor(35-195)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/domain/entity/TimetableStageEntity.java (4)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/block/core/domain/TimetableBlock.java (1)
Getter(9-37)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/block/core/domain/entity/TimetableBlockEntity.java (1)
Entity(11-91)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/blockmedia/domain/entity/TimetableBlockMediaEntity.java (1)
Entity(10-41)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/domain/entity/TimetableCategoryEntity.java (1)
Entity(8-53)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/component/TimetableCategoryRetriever.java (1)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/exception/TimetableCategoryNotfoundException.java (1)
TimetableCategoryNotfoundException(3-4)
src/main/java/com/permitseoul/permitserver/global/external/notion/NotionProvider.java (1)
src/main/java/com/permitseoul/permitserver/global/Constants.java (1)
Constants(3-24)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/stage/core/AdminTimetableStageSaver.java (1)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/category/core/component/AdminTimetableCategorySaver.java (1)
Component(10-18)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableService.java (2)
src/main/java/com/permitseoul/permitserver/global/exception/PermitIllegalStateException.java (1)
PermitIllegalStateException(3-4)src/main/java/com/permitseoul/permitserver/global/external/notion/NotionRelationValidator.java (1)
NotionRelationValidator(11-40)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/api/service/TimetableService.java (1)
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/exception/TimetableStageNotFoundException.java (1)
TimetableStageNotFoundException(3-4)
🔇 Additional comments (44)
src/main/resources/logback/logback-dev-loki.xml (1)
6-6: 호스트 변경 동작 확인 필요dev 환경에서
http://loki:3100호스트명이 실제로 DNS 해석되고 접근 가능한지 다시 확인 부탁드립니다. 기존localhost대비 네트워크 경로가 달라 잠재적 접속 실패 가능성이 있습니다.src/main/java/com/permitseoul/permitserver/global/external/discord/client/DiscordFeignClient.java (1)
1-1: 패키지 재구성 승인Discord 관련 클라이언트를
client서브패키지로 분리한 것은 코드 구조를 개선하는 좋은 변경입니다. 관련 의존성들이 모두 업데이트되었습니다.src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordDevHttpLogSender.java (1)
5-5: LGTM!패키지 재구성에 따른 import 경로 업데이트가 정확합니다.
src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketController.java (1)
24-24: 엔드포인트/api/tickets/{eventId}→/api/tickets/info/{eventId}로 변경됨
SecurityConfig에서 이미/api/tickets/info/*로 갱신된 것은 확인했습니다. 프론트엔드·모바일 앱 등 외부 클라이언트와 API 문서(Swagger 등)가 새 경로로 모두 반영되었는지 검증해 주세요.src/main/java/com/permitseoul/permitserver/global/config/SecurityConfig.java (2)
56-56: 인증 범위 변경 검토 필요SecurityConfig에서
/api/tickets/**→/api/tickets/user로 변경되어 아래 엔드포인트가 인증 제외되었습니다.
- GET
/api/tickets/info/{eventId}- POST
/api/tickets/confirm해당 경로들이 의도적으로 공개된 것이 맞는지, 혹은 인증이 필요하다면 설정을 조정해주세요.
38-38: 패턴 중복 아님
/api/events/*/timetables은 이벤트 ID로 타임테이블 목록 조회,/api/events/timetables/*은 블록 ID로 상세 조회에 매핑되어 있어 중복이 아닙니다.Likely an incorrect or invalid review comment.
src/main/java/com/permitseoul/permitserver/global/external/kakao/client/KakaoKApiClient.java (1)
1-14: 패키지 정리/이관 LGTM동작/시그니처 변화 없이 패키지·임포트만 정리된 것으로 보이며 문제 없습니다.
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/block/core/component/AdminTimetableBlockSaver.java (1)
16-20: LGTM일괄 저장 후 도메인 매핑 흐름이 명확합니다. 트랜잭션은 상위 Facade @transactional 범위에서 커버되므로 현재 형태로 충분합니다.
src/main/java/com/permitseoul/permitserver/global/external/kakao/KakaoProperties.java (1)
1-9: 패키지 이관 LGTM프로퍼티 프리픽스/레코드 구조 그대로 유지되어 호환성 문제 없어 보입니다.
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/domain/entity/TimetableStageEntity.java (1)
8-10: 일관된 테이블/컬럼 네이밍 사용
@Table(name = "event_timetable_stages")→@Table(name = "event_timetable_stage")- 외래키 어노테이션(
@JoinColumn(name = "event_timetable_id")) →@JoinColumn(name = "timetable_id")으로 변경- 마이그레이션/DDL에서도 동일한 네이밍이 적용되었는지 확인 바랍니다.
src/test/java/com/permitseoul/permitserver/PermitServerApplicationTests.java (1)
4-5: 패키지 재구성과 캐싱 설정이 적절합니다.Google과 Kakao 속성 클래스의 import 경로가
global.external패키지로 일관되게 업데이트되었고, 테스트에서@EnableCaching을 활성화하여 캐시 의존 기능을 테스트할 수 있도록 했습니다.Also applies to: 9-9, 11-11
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/block/core/domain/TimetableBlock.java (1)
27-28: Notion ID 필드명 변경이 일관되게 적용되었습니다.
fromEntity메서드가 새로운 Notion ID 필드명을 올바르게 사용하도록 업데이트되었습니다.src/main/java/com/permitseoul/permitserver/domain/auth/core/strategy/KakaoLoginStrategy.java (1)
6-9: 패키지 재구성이 일관되게 적용되었습니다.Kakao 관련 클래스들의 import 경로가
global.external.kakao패키지로 올바르게 업데이트되었으며, 로직 변경은 없습니다.src/main/java/com/permitseoul/permitserver/global/exception/PermitIllegalStateException.java (1)
1-4: 마커 예외 클래스가 적절하게 구현되었습니다.
PermitIllegalStateException은 Notion 관계 검증에서 사용되는 도메인 특화 예외입니다. 추가 동작 없이PermitGlobalException을 상속하는 마커 예외 패턴은 적절합니다.src/main/java/com/permitseoul/permitserver/global/external/google/GoogleProperties.java (1)
1-1: 패키지 재구성이 적절합니다.
GoogleProperties가global.external.google패키지로 이동되어 외부 통합 구성을 전역 패키지에 일관되게 배치했습니다.src/main/java/com/permitseoul/permitserver/global/external/google/client/GoogleClient.java (1)
1-1: 패키지 재구성이 일관되게 적용되었습니다.
GoogleClient와 관련 DTO의 import가global.external.google네임스페이스로 올바르게 업데이트되었습니다. 기능적 변경은 없습니다.Also applies to: 3-3
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/exception/TimetableStageNotFoundException.java (1)
1-4: 도메인 특화 예외가 적절하게 구현되었습니다.
TimetableStageNotFoundException은 타임테이블 스테이지 조회 실패 시 사용되는 명확한 의미의 예외입니다.TimetableStageCoreException을 상속하는 구조가 적절합니다.src/main/java/com/permitseoul/permitserver/global/external/google/dto/GoogleTokenResponse.java (1)
1-1: 패키지 재구성이 적절합니다.
GoogleTokenResponse가global.external.google.dto패키지로 이동되어 DTO 클래스들을 일관된 위치에 배치했습니다.src/main/java/com/permitseoul/permitserver/domain/auth/core/strategy/GoogleLoginStrategy.java (1)
9-10: LGTM!Google 통합 클래스들의 패키지 경로가
global.external.google로 정상적으로 재구성되었습니다.src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/repository/TimetableCategoryRepository.java (1)
14-14: LGTM!Notion 행 ID 기반 조회 메서드가 올바르게 추가되었습니다. Spring Data JPA 명명 규칙을 준수하고 있으며,
Optional반환 타입이 적절합니다.src/main/java/com/permitseoul/permitserver/global/external/kakao/dto/KakaoAccessTokenResponse.java (1)
1-1: LGTM!Kakao DTO의 패키지 경로가
global.external.kakao.dto로 적절하게 재구성되었습니다.src/main/java/com/permitseoul/permitserver/global/external/kakao/dto/KakaoATInfoResponse.java (1)
1-1: LGTM!Kakao DTO의 패키지 경로가
global.external.kakao.dto로 일관되게 재구성되었습니다.src/main/java/com/permitseoul/permitserver/domain/eventtimetable/blockmedia/domain/entity/TimetableBlockMediaEntity.java (1)
38-40: LGTM!팩토리 메서드가 올바르게 추가되었습니다. 생성 로직을 중앙화하고 private 생성자로 캡슐화를 유지하고 있습니다.
src/main/java/com/permitseoul/permitserver/global/external/notion/NotionProperties.java (1)
5-9: 민감한 Notion API 토큰 관리 검증 필요
.properties,.yml,.yaml파일에서 토큰 하드코딩 항목이 발견되지 않았습니다.- 환경 변수 기반 설정 및 로그 마스킹 적용 여부를 수동으로 검토해주세요.
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/blockmedia/core/component/AdminTimetableBlockMediaSaver.java (1)
1-19: LGTM! 일관된 패턴을 따릅니다.다른 Saver 컴포넌트들(AdminTimetableBlockSaver, AdminTimetableCategorySaver)과 동일한 패턴으로 구현되어 있습니다. 배치 저장 로직이 올바르게 구현되었습니다.
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/controller/AdminTimetableController.java (1)
1-35: LGTM! RESTful 설계가 잘 되어 있습니다.컨트롤러 구현이 적절합니다:
- RESTful 엔드포인트 설계 (
POST /api/admin/events/{eventId}/timetables/initial)@Valid어노테이션으로 요청 검증 활성화- 표준화된 응답 형식 사용 (
ApiResponseUtil.success)- 비즈니스 로직을 서비스 계층에 위임
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/stage/core/AdminTimetableStageSaver.java (1)
1-18: LGTM! 다른 Saver 컴포넌트와 일관성이 있습니다.AdminTimetableCategorySaver와 동일한 패턴으로 구현되어 배치 저장 로직이 명확하고 일관성 있게 작성되었습니다.
src/main/java/com/permitseoul/permitserver/PermitServerApplication.java (2)
8-9: LGTM! 패키지 구조 개선입니다.외부 통합 관련 Properties 클래스들을
global.external패키지로 이동한 것은 합리적인 리팩토링입니다. GoogleProperties와 KakaoProperties를 global 패키지로 이동하여 도메인 계층과 분리했습니다.Also applies to: 13-13
30-31: LGTM! Notion 통합을 위한 설정 추가.NotionProperties를 @EnableConfigurationProperties에 추가하여 Notion API 통합을 지원합니다. 다른 외부 Properties들과 일관성 있게 등록되었습니다.
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/core/domain/Timetable.java (1)
17-19: LGTM! Notion 데이터소스 ID 필드 추가.Timetable 도메인 모델에 Notion 데이터소스 식별자를 추가하여 Notion 통합을 지원합니다. fromEntity 메서드에서도 새 필드들을 올바르게 매핑하고 있습니다.
Also applies to: 26-29
src/main/java/com/permitseoul/permitserver/global/external/notion/NotionRelationValidator.java (1)
11-39: LGTM! Notion 관계 검증 로직이 올바릅니다.Notion 데이터소스 간의 관계 ID를 검증하는 로직이 정확하게 구현되었습니다:
- Stage와 Category의 유효한 ID Set을 생성
- Timetable의 관계 ID들이 유효한 Set에 포함되는지 검증
- 유효하지 않은 관계가 발견되면 PermitIllegalStateException 발생
Stream API를 적절히 사용하여 검증 로직이 명확합니다.
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/repository/TimetableStageRepository.java (1)
11-16: LGTM! Repository 메서드 정의가 적절합니다.Spring Data JPA의 쿼리 메서드 네이밍 규칙을 따라
findAllByTimetableId와findByNotionStageRowId메서드가 올바르게 선언되었습니다.src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/core/domain/entity/TimetableEntity.java (2)
30-37: LGTM! Notion 데이터소스 ID 컬럼 추가.새로운 Notion 데이터소스 식별자 컬럼들이 올바르게 정의되었습니다:
nullable=false로 데이터 무결성 보장length=100은 Notion ID에 적절한 길이- 명확한 컬럼명 사용
39-71: LGTM! 생성자와 팩토리 메서드가 일관되게 업데이트되었습니다.private 생성자와 public static create 메서드가 새로운 Notion 데이터소스 ID 파라미터를 포함하여 일관되게 확장되었습니다. 모든 필드 할당이 올바르게 수행됩니다.
src/main/java/com/permitseoul/permitserver/global/external/notion/NotionProvider.java (1)
23-48: 위임 메서드 구성은 적절합니다헤더/버전/컨텐츠 타입 전달과 클라이언트 위임 구조가 명확합니다.
src/main/java/com/permitseoul/permitserver/global/external/notion/dto/NotionStageDatasourceResponse.java (1)
16-19: data_source_id 사용이 최신 Notion API 응답 스키마와 일치합니다.
API 버전 2025-09-03 기준 Parent 객체는 "data_source_id" 필드를 반환하므로 database_id로 변경할 필요가 없습니다.src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/api/dto/TimetableResponse.java (2)
27-35: 변경 사항 확인 완료Area에서 Stage로의 네이밍 변경과 함께 areaId(long)에서 stageNotionId(String)로 타입 변경이 적절하게 이루어졌습니다. Notion 기반 식별자로의 전환이 일관되게 적용되었습니다.
46-46: 변경 사항 확인 완료Block 레코드의 blockAreaId(long)에서 blockStageNotionId(String)로의 변경이 적절합니다. Stage 기반 Notion ID 참조로 일관되게 업데이트되었습니다.
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/domain/TimetableStage.java (1)
1-26: 구현 확인 완료TimetableStage 도메인 객체가 적절하게 구현되었습니다. 불변 객체 패턴과 엔티티 변환 팩토리 메서드가 프로젝트의 다른 도메인 객체들(TimetableBlock, TimetableCategory)과 일관된 방식으로 작성되었습니다.
src/main/java/com/permitseoul/permitserver/global/external/notion/dto/NotionCategoryDatasourceResponse.java (1)
1-48: DTO 구조 확인 완료NotionCategoryDatasourceResponse가 Notion API 응답 구조를 적절하게 반영하고 있습니다. 중첩된 레코드 구조와 Jackson 애노테이션 매핑이 올바르게 작성되었습니다.
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/api/service/TimetableService.java (2)
105-106: Notion ID 기반 조회로 변경 확인 완료카테고리와 스테이지 조회가 Notion ID 기반으로 변경되었습니다. TimetableBlock의 Notion ID 필드와 일관되게 작동합니다.
148-194: 매핑 메서드 업데이트 확인 완료카테고리 색상 맵이 Notion ID 기반(String)으로 변경되었고, Stage 매핑 메서드가 추가되었으며, 블록 매핑이 Notion ID를 사용하도록 업데이트되었습니다. 모든 변경사항이 일관되게 적용되었습니다.
src/main/java/com/permitseoul/permitserver/global/external/notion/dto/NotionTimetableDatasourceResponse.java (1)
1-89: Notion DTO 구조 확인 완료NotionTimetableDatasourceResponse가 Notion API의 복잡한 응답 구조를 적절하게 표현하고 있습니다. 중첩된 레코드 구조와 Jackson 애노테이션이 올바르게 작성되었으며, 한국어 주석으로 각 필드 타입이 명확하게 문서화되어 있습니다.
src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java (1)
35-39: 시퀀스 기본값(-1) 검증 필요
result.properties().sequence()가null일 때-1을 할당하고 있습니다. 이 기본값이 정렬 및 다른 처리 로직에서 의도대로 작동하는지 확인하세요.
...m/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.java
Show resolved
Hide resolved
.../permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableService.java
Show resolved
Hide resolved
| public Timetable saveTimetable(final long eventId, | ||
| final LocalDateTime timetableStartAt, | ||
| final LocalDateTime timetableEndAt, | ||
| final String notionTimetableDataSourceId, | ||
| final String notionTimetableStageDataSourceId, | ||
| final String notionTimetableCategoryDataSourceId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
파라미터 순서 혼동 위험 및 실제 호출부에서 순서 오류 감지됨
AdminTimetableFacade에서 saveTimetable 호출 시 stage/category ID 순서가 뒤바뀌어 전달되고 있습니다. 즉시 정정 필요. 또한 순서 오류를 방지하려면 커맨드 객체(예: TimetableCreateCommand)로 묶는 것을 권장합니다.
호출부 점검 스크립트:
권장 호출 형태(예시):
adminTimetableSaver.saveTimetable(
eventId,
timetableStartAt,
timetableEndAt,
notionTimetableDataSourceId,
notionTimetableStageDataSourceId,
notionTimetableCategoryDataSourceId
);장기적으로는:
public record TimetableCreateCommand(
long eventId,
LocalDateTime startAt,
LocalDateTime endAt,
String notionTimetableDataSourceId,
String notionStageDataSourceId,
String notionCategoryDataSourceId
) {}🏁 Script executed:
#!/bin/bash
# saveTimetable 호출부와 인자 순서 확인
rg -nP -C3 --type=java '\bsaveTimetable\s*\('Length of output: 2364
🏁 Script executed:
#!/bin/bash
sed -n '45,65p' src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.javaLength of output: 1225
인자 순서 오류: stage↔category ID 교환 필요
- AdminTimetableFacade.java:47 saveTimetable 호출부에서 5번째 인자를 notionStageDataSourceId, 6번째 인자를 notionCategoryDataSourceId로 교정하세요.
- 파라미터 혼동 방지를 위해 TimetableCreateCommand 레코드로 묶는 리팩토링을 권장합니다.
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/core/components/AdminTimetableSaver.java
around lines 16-21, the method parameter order for stage and category data
source IDs is reversed relative to the caller (AdminTimetableFacade.java:47).
Fix this by swapping the last two parameters so the signature becomes (...
String notionTimetableCategoryDataSourceId, String
notionTimetableStageDataSourceId) to match call-site argument order, and update
all internal usages accordingly; alternatively, refactor by introducing a
TimetableCreateCommand record that groups these parameters (eventId,
timetableStartAt, timetableEndAt, notionTimetableDataSourceId,
notionTimetableStageDataSourceId, notionTimetableCategoryDataSourceId) and
change the method to accept that record to avoid future ordering mistakes.
src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java
Outdated
Show resolved
Hide resolved
| for (int i = 0; i < savedBlocks.size(); i++) { | ||
| final TimetableBlock block = savedBlocks.get(i); | ||
| final List<NotionTimetableDatasourceResponse.FilesProperty.FileItem> files = notionTimetableDatasourceResponse.results().get(i).properties().media().files(); | ||
| if (files == null) continue; | ||
|
|
||
| for (int seq = 0; seq < files.size(); seq++) { | ||
| String mediaUrl = files.get(seq).file().url(); | ||
| mediaEntities.add(TimetableBlockMediaEntity.create( | ||
| block.getTimetableBlockId(), | ||
| seq, | ||
| mediaUrl | ||
| )); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리스트 동기화 위험 - 중요
savedBlocks와 notionTimetableDatasourceResponse.results()의 크기와 순서가 일치한다고 가정하고 있습니다. 이는 매우 위험한 가정입니다:
- 두 리스트의 크기가 다르면 IndexOutOfBoundsException이 발생합니다.
- 순서가 일치하지 않으면 잘못된 블록에 미디어가 연결됩니다.
- 저장 과정에서 일부 블록이 실패하거나 필터링되면 인덱스가 맞지 않게 됩니다.
Notion ID를 키로 사용하여 매칭하도록 수정하세요:
public static List<TimetableBlockMediaEntity> mapToTimetableBlockMediaEntities(final List<TimetableBlock> savedBlocks,
final NotionTimetableDatasourceResponse notionTimetableDatasourceResponse) {
final List<TimetableBlockMediaEntity> mediaEntities = new ArrayList<>();
+
+ // Notion ID를 키로 하는 맵 생성
+ final Map<String, TimetableBlock> blocksByNotionId = savedBlocks.stream()
+ .collect(Collectors.toMap(
+ block -> /* Notion ID 추출 로직 필요 */,
+ block -> block
+ ));
- for (int i = 0; i < savedBlocks.size(); i++) {
- final TimetableBlock block = savedBlocks.get(i);
- final List<NotionTimetableDatasourceResponse.FilesProperty.FileItem> files = notionTimetableDatasourceResponse.results().get(i).properties().media().files();
+ for (final NotionTimetableDatasourceResponse.NotionPage page : notionTimetableDatasourceResponse.results()) {
+ final String notionId = /* page에서 ID 추출 */;
+ final TimetableBlock block = blocksByNotionId.get(notionId);
+ if (block == null) continue; // 매칭되는 블록이 없으면 스킵
+
+ final List<NotionTimetableDatasourceResponse.FilesProperty.FileItem> files = page.properties().media().files();
if (files == null) continue;
for (int seq = 0; seq < files.size(); seq++) {
- String mediaUrl = files.get(seq).file().url();
+ final String mediaUrl = files.get(seq).file().url();
+ if (mediaUrl == null || mediaUrl.isEmpty()) continue;
mediaEntities.add(TimetableBlockMediaEntity.create(
block.getTimetableBlockId(),
seq,
mediaUrl
));
}
}
return mediaEntities;
}참고: TimetableBlock에 Notion ID가 포함되어 있지 않은 것으로 보입니다. 이 경우 매핑 로직을 재검토하거나 TimetableBlock 도메인 객체에 Notion ID를 추가해야 할 수 있습니다.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java
around lines 125 to 138, the code assumes savedBlocks and
notionTimetableDatasourceResponse.results() are same size and order, which can
cause IndexOutOfBounds or incorrect mappings; fix by matching on a stable key
(Notion ID): ensure TimetableBlock contains the Notion ID (or add it), build a
Map from notion results keyed by their Notion ID, then iterate savedBlocks and
for each block lookup the corresponding notion result by block.getNotionId();
skip or log if no match, guard against null files, and create media entities
only from the matched result so order/size differences are handled safely.
| private final String timetableCategoryNotionId; | ||
| private final String timetableStageNotionId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
데이터 모델 변경에 대한 마이그레이션을 검증하세요.
타임테이블 블록의 ID 필드가 long 타입에서 String 타입의 Notion ID로 변경되었습니다:
timetableCategoryId→timetableCategoryNotionIdtimetableAreaId→timetableStageNotionId
이는 중요한 데이터 모델 변경으로, 다음 사항을 확인해야 합니다:
- 데이터베이스 스키마 마이그레이션이 준비되었는지
- 기존 데이터 처리 방안이 마련되었는지
- 모든 의존 코드가 새 필드명을 사용하도록 업데이트되었는지
다음 스크립트를 실행하여 마이그레이션 관련 파일과 이전 필드명의 잔여 사용을 확인하세요:
🏁 Script executed:
#!/bin/bash
# 데이터베이스 마이그레이션 파일 검색
echo "=== 마이그레이션 파일 검색 ==="
fd -e sql -e xml | head -20
# 이전 필드명 사용 확인
echo -e "\n=== timetableCategoryId 사용 검색 ==="
rg -n --type=java 'timetableCategoryId' -g '!TimetableBlock.java'
echo -e "\n=== timetableAreaId 사용 검색 ==="
rg -n --type=java 'timetableAreaId'Length of output: 585
데이터 모델 변경 관련 마이그레이션 및 코드 반영 필요
- 데이터베이스 마이그레이션(SQL/XML) 파일 미발견: DDL 또는 Liquibase/Flyway changelog 추가 필요
- 기존
timetableCategoryId가 아직 사용 중 (
– src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/domain/TimetableCategory.java:10
– src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/domain/entity/TimetableCategoryEntity.java:17
) timetableAreaId참조 위치는 없으나timetableStageNotionId로 전역 변경 반영 필요
위 사항 해결을 위해 마이그레이션 스크립트 작성, 기존 데이터 변환 로직 구현, 필드명 일괄 교체를 수행하세요.
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/block/core/domain/TimetableBlock.java
around lines 14-15, the model added timetableCategoryNotionId and
timetableStageNotionId but there is no DB migration or code changes to migrate
existing timetableCategoryId/timetableAreaId usage; create a database migration
(Liquibase/Flyway changelog or SQL) that adds the new columns, migrates data
from the old columns (populate timetableCategoryNotionId from
timetableCategoryId and timetableStageNotionId from timetableAreaId or
appropriate source), and drops or marks deprecated the old columns if desired;
update and refactor all Java references still using timetableCategoryId (e.g.,
TimetableCategory.java and TimetableCategoryEntity.java) to the new naming,
search-and-replace timetableAreaId -> timetableStageNotionId globally, add a
short data migration routine or script in the application startup if runtime
conversion is needed, and add tests and changelog entries to validate schema and
data migration.
| @Column(name = "notion_category_row_id", nullable = false) | ||
| private String notionCategoryRowId; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
새 컬럼 추가에 따른 마이그레이션/제약 보완 필요
- 운영 DB에 NOT NULL 컬럼 추가는 기존 데이터가 있으면 실패합니다. 마이그레이션 스크립트(추가→백필→NOT NULL/UNIQUE 적용) 포함해 주세요.
- Notion Row ID는 유일해야 할 가능성이 높습니다. unique + 길이 명시 및 인덱스를 권장합니다.
권장 변경 예:
- @Column(name = "notion_category_row_id", nullable = false)
- private String notionCategoryRowId;
+ @Column(name = "notion_category_row_id", nullable = false, unique = true, length = 64)
+ private String notionCategoryRowId;DB 마이그레이션에서는 UNIQUE 인덱스도 함께 생성해 주세요.
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/category/core/domain/entity/TimetableCategoryEntity.java
around lines 31-33, the new notionCategoryRowId column is declared NOT NULL but
needs migration and constraint hardening: create a DB migration that 1) adds the
column as nullable, 2) backfills existing rows with a generated or lookup value
(or a placeholder) so no NULLs remain, 3) alters the column to NOT NULL and 4)
creates a UNIQUE index on the column; in the entity add length metadata (e.g.
appropriate varchar length) and mark uniqueness (either @Column(length=...,
unique=true) or add an @Index on the field) so the JPA mapping matches the DB,
and ensure the migration script also creates the UNIQUE index to enforce
uniqueness at the DB level.
...seoul/permitserver/domain/eventtimetable/stage/core/repository/TimetableStageRepository.java
Outdated
Show resolved
Hide resolved
| "/api/tickets/info/*", | ||
| "/api/tickets/confirm" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
티켓 엔드포인트의 공개 접근 허용에 대한 보안 검증이 필요합니다.
/api/tickets/info/*와 /api/tickets/confirm을 인증 없이 접근 가능하도록 변경하면 다음과 같은 보안 위험이 발생할 수 있습니다:
/api/tickets/info/*: 티켓 정보 조회 시 개인정보(구매자 정보, 결제 정보 등)가 노출될 가능성/api/tickets/confirm: 인증 없이 티켓 확인/검증이 가능하면 무단 사용이나 티켓 상태 조작의 위험
다음 사항을 확인해주세요:
- 이 엔드포인트들이 노출하는 데이터에 민감 정보가 포함되어 있지 않은지
/api/tickets/confirm이 티켓 상태를 변경하는 경우, 인증 없이 허용해도 안전한지- Rate limiting이나 기타 보안 장치가 구현되어 있는지
다음 스크립트로 해당 엔드포인트의 구현을 확인하여 보안 위험을 검증하세요:
🏁 Script executed:
#!/bin/bash
# Description: 티켓 관련 컨트롤러와 서비스의 구현을 확인하여 데이터 노출 및 권한 검증 로직을 분석
# 티켓 컨트롤러에서 /info/* 및 /confirm 엔드포인트 구현 찾기
echo "=== 티켓 엔드포인트 구현 찾기 ==="
ast-grep --pattern $'@GetMapping($$$"/info/"$$$)
$$$
public $$$ $FUNC($$$) {
$$$
}'
ast-grep --pattern $'@PostMapping($$$"/confirm"$$$)
$$$
public $$$ $FUNC($$$) {
$$$
}'
# 티켓 정보 조회 및 확인 메서드에서 반환하는 DTO 확인
echo -e "\n=== 티켓 DTO 클래스 찾기 ==="
fd -e java -x rg -l "class.*Ticket.*Response|class.*Ticket.*Info|class.*Ticket.*Confirm" {} \; | head -10
# 티켓 서비스에서 개인정보 포함 여부 확인
echo -e "\n=== 티켓 서비스 메서드 확인 ==="
ast-grep --pattern $'class TicketService {
$$$
public $$$ getTicketInfo($$$) {
$$$
}
$$$
}'Length of output: 68
다음 스크립트를 실행하여 컨트롤러·DTO·서비스 구현을 재탐색해주세요:
🏁 Script executed:
#!/bin/bash
echo "=== TicketController 파일 위치 ==="
fd -e java -I TicketController.java
echo -e "\n=== /info 및 /confirm 매핑 검색 ==="
rg -nP --type java '@(GetMapping|PostMapping)\s*\(\s*"/api/tickets/(info|confirm)[^"]*"' -C3 src/main/java
echo -e "\n=== ticket.dto 패키지 내 DTO 클래스 ==="
rg -n 'package .*ticket\.dto' -l src/main/java
echo -e "\n=== DTO 클래스 목록 (Ticket 포함) ==="
fd -e java -g '*Ticket*.java' src/main/java/com/permitseoul/permitserver/**/dto | head -20
echo -e "\n=== TicketService 클래스 및 메서드 ==="
rg -nP --type java 'class\s+TicketService\b' -C3 src/main/java
rg -n 'TicketService' -C2 src/main/javaLength of output: 12298
다음 스크립트를 실행해 TicketController 내 매핑을 정확히 확인해주세요:
🏁 Script executed:
#!/bin/bash
FILE=src/main/java/com/permitseoul/permitserver/domain/ticket/api/controller/TicketController.java
echo "=== @GetMapping 어노테이션 ==="
rg -n '@GetMapping' -C2 $FILE
echo -e "\n=== @PostMapping 어노테이션 ==="
rg -n '@PostMapping' -C2 $FILE
echo -e "\n=== 'info' 키워드 검색 ==="
rg -n 'info' -C2 $FILE
echo -e "\n=== 'confirm' 키워드 검색 ==="
rg -n 'confirm' -C2 $FILELength of output: 1250
다음 스크립트로 confirmTicket 메서드 구현을 확인해주세요:
🏁 Script executed:
#!/bin/bash
rg -n 'confirmTicket' -C5 src/main/java/com/permitseoul/permitserver/domain/ticket/api/service/TicketService.javaLength of output: 541
다음 스크립트로 confirmTicket 메서드 내부의 상태 변경 로직을 확인해주세요:
🏁 Script executed:
#!/bin/bash
rg -nP --type java 'public void confirmTicket' -C20 src/main/java/com/permitseoul/permitserver/domain/ticket/api/service/TicketService.javaLength of output: 2215
/api/tickets/confirm 인증 및 권한 검사 적용 필요
confirmTicket메서드가ticketUpdater.updateTicketStatus로 상태 변경을 수행하므로 인증·인가 로직이 반드시 필요합니다./api/tickets/info/{eventId}가 노출하는 데이터에 민감 정보(구매자·결제 정보 등)가 없는지 확인하고, 필요 시 인증 처리 검토를 권장합니다.
🤖 Prompt for AI Agents
In src/main/java/com/permitseoul/permitserver/global/config/SecurityConfig.java
around lines 39-40, the endpoint "/api/tickets/confirm" is currently not
protected; add it to the list of routes requiring authentication/authorization
so that confirmTicket cannot be called anonymously and is subject to role checks
(e.g., requires authenticated user or specific role like ROLE_TICKET_UPDATER).
Also review "/api/tickets/info/*" for sensitive buyer/payment data and, if any
sensitive fields are returned, move that pattern from public to authenticated
(or add finer-grained authorization) so only authorized clients can access it.
| NOT_FOUND_TIMETABLE_STAGE(HttpStatus.NOT_FOUND, 40418, "타임테이블 장소를 찾을 수 없습니다."), | ||
| NOT_FOUND_TIMETABLE_CATEGORY(HttpStatus.NOT_FOUND, 40419, "타임테이블 카테고리를 찾을 수 없습니다."), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
메시지 용어 불일치(Area→Stage 전환 반영 필요)
상수명은 STAGE로 바뀌었지만, 메시지는 “장소”로 남아 있습니다. 사용자 혼동을 줄이기 위해 “무대”로 통일하세요.
- NOT_FOUND_TIMETABLE_STAGE(HttpStatus.NOT_FOUND, 40418, "타임테이블 장소를 찾을 수 없습니다."),
+ NOT_FOUND_TIMETABLE_STAGE(HttpStatus.NOT_FOUND, 40418, "타임테이블 무대를 찾을 수 없습니다."),📝 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.
| NOT_FOUND_TIMETABLE_STAGE(HttpStatus.NOT_FOUND, 40418, "타임테이블 장소를 찾을 수 없습니다."), | |
| NOT_FOUND_TIMETABLE_CATEGORY(HttpStatus.NOT_FOUND, 40419, "타임테이블 카테고리를 찾을 수 없습니다."), | |
| NOT_FOUND_TIMETABLE_STAGE(HttpStatus.NOT_FOUND, 40418, "타임테이블 무대를 찾을 수 없습니다."), | |
| NOT_FOUND_TIMETABLE_CATEGORY(HttpStatus.NOT_FOUND, 40419, "타임테이블 카테고리를 찾을 수 없습니다."), |
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/global/response/code/ErrorCode.java
around lines 78-79, the enum entries NOT_FOUND_TIMETABLE_STAGE and
NOT_FOUND_TIMETABLE_CATEGORY still use the Korean word "장소" ("place") in their
messages; update both message strings to use "무대" ("stage") to match the STAGE
naming and avoid user confusion. Keep the rest of the enum values untouched and
ensure the messages remain grammatically consistent with existing phrasing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/main/java/com/permitseoul/permitserver/global/response/code/ErrorCode.java (1)
79-79: 메시지 용어 불일치 (Area→Stage 전환 반영 필요)상수명은
NOT_FOUND_TIMETABLE_STAGE로 변경되었지만 메시지는 여전히 "타임테이블 장소를 찾을 수 없습니다."로 되어 있습니다. "무대"로 통일하는 것이 사용자 혼동을 줄일 수 있습니다.다음 diff를 적용하여 메시지를 수정하세요:
- NOT_FOUND_TIMETABLE_STAGE(HttpStatus.NOT_FOUND, 40418, "타임테이블 장소를 찾을 수 없습니다."), + NOT_FOUND_TIMETABLE_STAGE(HttpStatus.NOT_FOUND, 40418, "타임테이블 무대를 찾을 수 없습니다."),
🧹 Nitpick comments (2)
src/main/java/com/permitseoul/permitserver/global/external/notion/exception/NotFoundNotionResponseException.java (1)
5-6: 예외 클래스에 생성자 추가를 권장합니다.예외 클래스가 메시지와 원인(cause)을 받을 수 있는 생성자를 제공하지 않고 있습니다. 디버깅과 로깅을 위해 컨텍스트 정보를 제공할 수 있는 생성자를 추가하는 것이 좋습니다.
다음 diff를 적용하여 표준 생성자를 추가하세요:
public class NotFoundNotionResponseException extends PermitGlobalException { + public NotFoundNotionResponseException() { + super(); + } + + public NotFoundNotionResponseException(String message) { + super(message); + } + + public NotFoundNotionResponseException(String message, Throwable cause) { + super(message, cause); + } }src/main/java/com/permitseoul/permitserver/domain/admin/util/exception/PermitListSizeNotMatchException.java (1)
5-6: 예외 클래스에 생성자 추가를 권장합니다.예외 클래스에 메시지와 원인을 전달할 수 있는 생성자가 없습니다. 어떤 리스트의 크기가 맞지 않는지 등의 컨텍스트 정보를 제공할 수 있도록 생성자를 추가하면 디버깅에 도움이 됩니다.
다음 diff를 적용하여 표준 생성자를 추가하세요:
public class PermitListSizeNotMatchException extends AdminBaseException { + public PermitListSizeNotMatchException() { + super(); + } + + public PermitListSizeNotMatchException(String message) { + super(message); + } + + public PermitListSizeNotMatchException(String message, Throwable cause) { + super(message, cause); + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableService.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/stage/core/AdminTimetableStageSaver.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/util/exception/PermitListSizeNotMatchException.java(1 hunks)src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/repository/TimetableStageRepository.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/notion/NotionProvider.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/notion/exception/NotFoundNotionResponseException.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/response/code/ErrorCode.java(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/java/com/permitseoul/permitserver/domain/admin/timetable/stage/core/AdminTimetableStageSaver.java
- src/main/java/com/permitseoul/permitserver/global/external/notion/NotionProvider.java
🧰 Additional context used
🧬 Code graph analysis (5)
src/main/java/com/permitseoul/permitserver/global/external/notion/exception/NotFoundNotionResponseException.java (1)
src/main/java/com/permitseoul/permitserver/global/exception/PermitGlobalException.java (1)
PermitGlobalException(3-4)
src/main/java/com/permitseoul/permitserver/domain/admin/util/exception/PermitListSizeNotMatchException.java (1)
src/main/java/com/permitseoul/permitserver/domain/admin/base/AdminBaseException.java (1)
AdminBaseException(3-4)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableService.java (5)
src/main/java/com/permitseoul/permitserver/domain/admin/util/exception/PermitListSizeNotMatchException.java (1)
PermitListSizeNotMatchException(5-6)src/main/java/com/permitseoul/permitserver/global/exception/DateFormatException.java (1)
DateFormatException(4-4)src/main/java/com/permitseoul/permitserver/global/exception/PermitIllegalStateException.java (1)
PermitIllegalStateException(3-4)src/main/java/com/permitseoul/permitserver/global/external/notion/NotionRelationValidator.java (1)
NotionRelationValidator(11-40)src/main/java/com/permitseoul/permitserver/global/external/notion/exception/NotFoundNotionResponseException.java (1)
NotFoundNotionResponseException(5-6)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.java (5)
src/main/java/com/permitseoul/permitserver/domain/admin/timetable/stage/core/AdminTimetableStageSaver.java (1)
Component(10-18)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/block/core/component/AdminTimetableBlockSaver.java (1)
Component(11-21)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/core/components/AdminTimetableSaver.java (1)
Component(11-32)src/main/java/com/permitseoul/permitserver/domain/admin/timetable/blockmedia/core/component/AdminTimetableBlockMediaSaver.java (1)
Component(11-19)src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java (1)
NotionResponseMapper(23-162)
src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java (3)
src/main/java/com/permitseoul/permitserver/domain/admin/util/exception/PermitListSizeNotMatchException.java (1)
PermitListSizeNotMatchException(5-6)src/main/java/com/permitseoul/permitserver/global/exception/DateFormatException.java (1)
DateFormatException(4-4)src/main/java/com/permitseoul/permitserver/global/exception/PermitIllegalStateException.java (1)
PermitIllegalStateException(3-4)
🔇 Additional comments (10)
src/main/java/com/permitseoul/permitserver/global/response/code/ErrorCode.java (1)
31-31: 새로운 에러 코드가 적절하게 추가되었습니다.Notion 통합 기능에 필요한 에러 코드들(BAD_REQUEST_MISMATCH_LIST_SIZE, NOT_FOUND_NOTION_RELATION_ID, NOT_FOUND_NOTION_DATABASE_SOURCE, INTERNAL_NOTION_FEIGN_ERROR)이 잘 정의되었습니다.
Also applies to: 87-88, 128-128
src/main/java/com/permitseoul/permitserver/domain/eventtimetable/stage/core/repository/TimetableStageRepository.java (1)
10-15: 리포지토리 구현이 적절합니다.JPA 리포지토리가 올바르게 정의되었으며,
findAllByTimetableId와findByNotionStageRowId메서드는 Spring Data JPA의 네이밍 규칙을 따르고 있습니다.src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableFacade.java (1)
39-71: 트랜잭션 관리 및 순차적 저장 흐름이 적절합니다.
@Transactional어노테이션을 사용하여 전체 저장 프로세스의 원자성을 보장하고 있으며, timetable → stages → categories → blocks → block media 순서로 의존성을 고려한 순차적 저장이 올바르게 구현되었습니다.src/main/java/com/permitseoul/permitserver/domain/admin/timetable/base/api/service/AdminTimetableService.java (2)
37-47: Notion 데이터 소스 조회 및 검증 로직이 적절합니다.각 데이터 소스 ID가 올바른 메서드로 전달되고 있으며,
NotionRelationValidator를 통한 연관 관계 검증도 적절히 수행되고 있습니다.
62-72: 예외 처리 매핑이 잘 구조화되어 있습니다.각 예외 유형을 적절한 에러 코드와 매핑하여
AdminApiException으로 변환하고 있습니다. Feign 통신 오류, 관계 ID 누락, 날짜 형식 오류, 리스트 크기 불일치 등 다양한 시나리오를 처리하고 있습니다.src/main/java/com/permitseoul/permitserver/domain/admin/util/NotionResponseMapper.java (5)
26-48: Stage 엔티티 매핑 로직이 적절합니다.Notion 응답에서 stage name, sequence 추출 시
findFirst()와orElse()를 사용하여 null-safe하게 처리하고 있으며, sequence가 null인 경우 -1을 기본값으로 설정하는 것도 적절합니다.
50-87: Category 엔티티 매핑 로직이 적절합니다.Category name, background color, line color를 Notion 응답에서 안전하게 추출하며, 빈 값에 대한 적절한 기본값 처리가 구현되어 있습니다.
106-111: 날짜 파싱 예외 처리가 적절하게 추가되었습니다.
LocalDateTime.parse()에서 발생할 수 있는DateTimeParseException을 catch하여DateFormatException으로 변환하고 있어, 잘못된 날짜 형식에 대한 적절한 오류 처리가 구현되었습니다.
113-118: Relation 검증 로직이 적절하게 추가되었습니다.
relation().isEmpty()체크를 통해get(0)호출 전 안전성을 확보하고 있으며, 누락된 relation에 대해PermitIllegalStateException을 발생시켜 적절히 처리하고 있습니다.
138-161: saveAll()가 입력 순서를 유지합니다
Spring Data JPA의 saveAll()는 전달된 Iterable 순서를 그대로 반환하므로, savedBlocks와 Notion 결과 간 인덱스 기반 매핑은 안전합니다.
🔥Pull requests
⛳️ 작업한 브랜치
👷 작업한 내용
🚨 참고 사항