From 7e5e532ce44000beeaf182365217c5dbee823a69 Mon Sep 17 00:00:00 2001 From: Chanhae Lee Date: Sun, 18 Jan 2026 19:44:30 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(report):=20=EB=8B=B5=EB=B3=80=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20API=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET /answers/{answerId}와 GET /calendar/{date}에 대해 응답으로 질문 내용, 질문 카테고리, 답변 작성일, 답변 내용, 리포트 내용, 감정 상태를 반환하도록 변경, Repository에 findDetailByAnswerId, findDetailByUserAndDate 쿼리 추가, 사용하지 않는 메서드 제거 --- .../dailyreport/api/AnswerController.java | 45 ++++++++++--------- .../dto/response/AnswerDetailResponse.java | 39 ++++++++++++++++ .../application/AnswerQueryService.java | 15 +++++-- .../application/DailyReportQueryService.java | 21 --------- .../dailyreport/core/dto/AnswerDetailDto.java | 20 +++++++++ .../AnswerEntryQueryRepository.java | 28 ++++++++++-- 6 files changed, 118 insertions(+), 50 deletions(-) create mode 100644 src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/dto/response/AnswerDetailResponse.java create mode 100644 src/main/java/com/devkor/ifive/nadab/domain/dailyreport/core/dto/AnswerDetailDto.java diff --git a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/AnswerController.java b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/AnswerController.java index e7ddd2b..ce1d3ab 100644 --- a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/AnswerController.java +++ b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/AnswerController.java @@ -2,13 +2,11 @@ import com.devkor.ifive.nadab.domain.dailyreport.api.dto.request.GetMonthlyCalendarRequest; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.request.SearchAnswerEntryRequest; -import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.AnswerEntrySummaryResponse; +import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.AnswerDetailResponse; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.CalendarRecentsResponse; -import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.DailyReportResponse; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.MonthlyCalendarResponse; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.SearchAnswerEntryResponse; import com.devkor.ifive.nadab.domain.dailyreport.application.AnswerQueryService; -import com.devkor.ifive.nadab.domain.dailyreport.application.DailyReportQueryService; import com.devkor.ifive.nadab.domain.search.application.SearchHistoryCommandService; import com.devkor.ifive.nadab.global.core.response.ApiResponseDto; import com.devkor.ifive.nadab.global.core.response.ApiResponseEntity; @@ -34,7 +32,6 @@ @RequiredArgsConstructor public class AnswerController { - private final DailyReportQueryService dailyReportQueryService; private final AnswerQueryService answerQueryService; private final SearchHistoryCommandService searchHistoryCommandService; @@ -109,6 +106,9 @@ public ResponseEntity> searchAnswers( description = """ 답변 ID로 해당 답변의 상세 정보와 리포트를 조회합니다. + - 질문 내용 (questionText) + - 질문 카테고리 (interestCode) + - 답변 작성일 (answerDate) - 답변 내용 (answer) - 리포트 내용 (content) - 감정 상태 (emotion) @@ -119,36 +119,28 @@ public ResponseEntity> searchAnswers( responses = { @ApiResponse( responseCode = "200", - description = "리포트 조회 성공", - content = @Content(schema = @Schema(implementation = DailyReportResponse.class), mediaType = "application/json") + description = "상세 조회 성공", + content = @Content(schema = @Schema(implementation = AnswerDetailResponse.class), mediaType = "application/json") ), @ApiResponse( responseCode = "401", description = "인증 실패", content = @Content ), - @ApiResponse( - responseCode = "403", - description = """ - - ErrorCode: ANSWER_ACCESS_FORBIDDEN - 본인의 답변이 아님 - """, - content = @Content - ), @ApiResponse( responseCode = "404", description = """ - - ErrorCode: ANSWER_NOT_FOUND - 답변을 찾을 수 없음 - - ErrorCode: DAILY_REPORT_NOT_FOUND - 리포트가 생성되지 않았음 + - ErrorCode: ANSWER_NOT_FOUND - 답변을 찾을 수 없음 (또는 본인의 답변이 아님) """, content = @Content ) } ) - public ResponseEntity> getDailyReportByAnswerId( + public ResponseEntity> getAnswerDetailById( @AuthenticationPrincipal UserPrincipal principal, @PathVariable Long answerId ) { - DailyReportResponse response = dailyReportQueryService.getDailyReportByAnswerId(principal.getId(), answerId); + AnswerDetailResponse response = answerQueryService.getAnswerDetailById(principal.getId(), answerId); return ApiResponseEntity.ok(response); } @@ -216,11 +208,20 @@ public ResponseEntity> getCalendarRecent @GetMapping("/calendar/{date}") @PreAuthorize("isAuthenticated()") @Operation( - summary = "특정 날짜 답변 조회", + summary = "특정 날짜 답변 상세 조회", description = """ - 특정 날짜의 답변 미리보기를 조회합니다. + 특정 날짜의 답변 전체 정보를 조회합니다. + + 응답 데이터: + - 질문 내용 (questionText) + - 질문 카테고리 (interestCode) + - 답변 작성일 (answerDate) + - 답변 내용 (answer) + - 리포트 내용 (content) + - 감정 상태 (emotion) - 해당 날짜에 답변이 없으면 404 에러를 반환합니다. + - COMPLETED 상태의 리포트만 조회 가능합니다. - 날짜 형식: yyyy-MM-dd (예: 2026-01-30) """, security = @SecurityRequirement(name = "bearerAuth"), @@ -228,7 +229,7 @@ public ResponseEntity> getCalendarRecent @ApiResponse( responseCode = "200", description = "조회 성공", - content = @Content(schema = @Schema(implementation = AnswerEntrySummaryResponse.class)) + content = @Content(schema = @Schema(implementation = AnswerDetailResponse.class)) ), @ApiResponse(responseCode = "400", description = "ErrorCode: VALIDATION_FAILED - 잘못된 날짜 형식", content = @Content), @ApiResponse(responseCode = "401", description = "인증 실패", content = @Content), @@ -239,11 +240,11 @@ public ResponseEntity> getCalendarRecent ) } ) - public ResponseEntity> getAnswerByDate( + public ResponseEntity> getAnswerDetailByDate( @AuthenticationPrincipal UserPrincipal principal, @PathVariable LocalDate date ) { - AnswerEntrySummaryResponse response = answerQueryService.getAnswerByDate( + AnswerDetailResponse response = answerQueryService.getAnswerDetailByDate( principal.getId(), date ); diff --git a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/dto/response/AnswerDetailResponse.java b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/dto/response/AnswerDetailResponse.java new file mode 100644 index 0000000..0f9b1fd --- /dev/null +++ b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/dto/response/AnswerDetailResponse.java @@ -0,0 +1,39 @@ +package com.devkor.ifive.nadab.domain.dailyreport.api.dto.response; + +import com.devkor.ifive.nadab.domain.dailyreport.core.dto.AnswerDetailDto; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.time.LocalDate; + +@Schema(description = "답변 상세 조회 응답") +public record AnswerDetailResponse( + + @Schema(description = "질문 내용", example = "오늘 가장 기뻤던 순간은?") + String questionText, + + @Schema(description = "질문 카테고리 (관심분야 코드)", example = "EMOTION") + String interestCode, + + @Schema(description = "답변 작성일", example = "2025-12-25") + LocalDate answerDate, + + @Schema(description = "나의 답변") + String answer, + + @Schema(description = "리포트 내용") + String content, + + @Schema(description = "리포트 감정 상태", example = "ACHIEVEMENT") + String emotion +) { + public static AnswerDetailResponse from(AnswerDetailDto dto) { + return new AnswerDetailResponse( + dto.questionText(), + dto.interestCode() != null ? dto.interestCode().name() : null, + dto.answerDate(), + dto.answerContent(), + dto.reportContent(), + dto.emotionCode() != null ? dto.emotionCode().name() : null + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/application/AnswerQueryService.java b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/application/AnswerQueryService.java index 06a2a66..018abed 100644 --- a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/application/AnswerQueryService.java +++ b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/application/AnswerQueryService.java @@ -2,11 +2,13 @@ import com.devkor.ifive.nadab.domain.dailyreport.api.dto.request.GetMonthlyCalendarRequest; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.request.SearchAnswerEntryRequest; +import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.AnswerDetailResponse; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.AnswerEntrySummaryResponse; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.CalendarEntryResponse; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.CalendarRecentsResponse; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.MonthlyCalendarResponse; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.SearchAnswerEntryResponse; +import com.devkor.ifive.nadab.domain.dailyreport.core.dto.AnswerDetailDto; import com.devkor.ifive.nadab.domain.dailyreport.core.dto.MonthlyCalendarDto; import com.devkor.ifive.nadab.domain.dailyreport.core.dto.SearchAnswerEntryDto; import com.devkor.ifive.nadab.domain.dailyreport.core.entity.EmotionCode; @@ -123,11 +125,18 @@ public CalendarRecentsResponse getRecentAnswers(Long userId) { return CalendarRecentsResponse.from(items); } - public AnswerEntrySummaryResponse getAnswerByDate(Long userId, LocalDate date) { - SearchAnswerEntryDto dto = answerEntryQueryRepository.findByUserAndDate(userId, date) + public AnswerDetailResponse getAnswerDetailById(Long userId, Long answerId) { + AnswerDetailDto dto = answerEntryQueryRepository.findDetailByAnswerId(userId, answerId) .orElseThrow(() -> new NotFoundException(ErrorCode.ANSWER_NOT_FOUND)); - return AnswerEntrySummaryResponse.from(dto); + return AnswerDetailResponse.from(dto); + } + + public AnswerDetailResponse getAnswerDetailByDate(Long userId, LocalDate date) { + AnswerDetailDto dto = answerEntryQueryRepository.findDetailByUserAndDate(userId, date) + .orElseThrow(() -> new NotFoundException(ErrorCode.ANSWER_NOT_FOUND)); + + return AnswerDetailResponse.from(dto); } /** diff --git a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/application/DailyReportQueryService.java b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/application/DailyReportQueryService.java index f80904d..8dc9558 100644 --- a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/application/DailyReportQueryService.java +++ b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/application/DailyReportQueryService.java @@ -8,7 +8,6 @@ import com.devkor.ifive.nadab.domain.user.core.entity.User; import com.devkor.ifive.nadab.domain.user.core.repository.UserRepository; import com.devkor.ifive.nadab.global.core.response.ErrorCode; -import com.devkor.ifive.nadab.global.exception.ForbiddenException; import com.devkor.ifive.nadab.global.exception.NotFoundException; import com.devkor.ifive.nadab.global.shared.util.TodayDateTimeProvider; import com.devkor.ifive.nadab.global.shared.util.dto.TodayDateTimeRangeDto; @@ -44,24 +43,4 @@ public DailyReportResponse getDailyReport(Long id) { report.getEmotion().getCode().toString() ); } - - public DailyReportResponse getDailyReportByAnswerId(Long userId, Long answerId) { - // 답변 조회 및 권한 확인 - AnswerEntry entry = answerEntryRepository.findById(answerId) - .orElseThrow(() -> new NotFoundException(ErrorCode.ANSWER_NOT_FOUND)); - - if (!entry.getUser().getId().equals(userId)) { - throw new ForbiddenException(ErrorCode.ANSWER_ACCESS_FORBIDDEN); - } - - // 리포트 조회 - DailyReport report = dailyReportRepository.findByAnswerEntryIdAndCompleted(answerId) - .orElseThrow(() -> new NotFoundException(ErrorCode.DAILY_REPORT_NOT_FOUND)); - - return new DailyReportResponse( - entry.getContent(), - report.getContent(), - report.getEmotion().getCode().toString() - ); - } } diff --git a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/core/dto/AnswerDetailDto.java b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/core/dto/AnswerDetailDto.java new file mode 100644 index 0000000..9339990 --- /dev/null +++ b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/core/dto/AnswerDetailDto.java @@ -0,0 +1,20 @@ +package com.devkor.ifive.nadab.domain.dailyreport.core.dto; + +import com.devkor.ifive.nadab.domain.dailyreport.core.entity.EmotionCode; +import com.devkor.ifive.nadab.domain.user.core.entity.InterestCode; + +import java.time.LocalDate; + +/** + * 답변 상세 조회용 Projection DTO + * Repository 쿼리 결과를 담는 DTO + */ +public record AnswerDetailDto( + String questionText, + InterestCode interestCode, + LocalDate answerDate, + String answerContent, + String reportContent, + EmotionCode emotionCode +) { +} \ No newline at end of file diff --git a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/core/repository/AnswerEntryQueryRepository.java b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/core/repository/AnswerEntryQueryRepository.java index e4094da..18e4428 100644 --- a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/core/repository/AnswerEntryQueryRepository.java +++ b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/core/repository/AnswerEntryQueryRepository.java @@ -1,5 +1,6 @@ package com.devkor.ifive.nadab.domain.dailyreport.core.repository; +import com.devkor.ifive.nadab.domain.dailyreport.core.dto.AnswerDetailDto; import com.devkor.ifive.nadab.domain.dailyreport.core.dto.MonthlyCalendarDto; import com.devkor.ifive.nadab.domain.dailyreport.core.dto.SearchAnswerEntryDto; import com.devkor.ifive.nadab.domain.dailyreport.core.entity.AnswerEntry; @@ -100,12 +101,31 @@ List findRecentAnswers( Pageable pageable ); + /** - * 특정 날짜 답변 조회 + * 답변 ID로 상세 조회 */ @Query(""" - select new com.devkor.ifive.nadab.domain.dailyreport.core.dto.SearchAnswerEntryDto( - ae.id, ae.question.interest.code, e.code, ae.question.questionText, ae.content, ae.date + select new com.devkor.ifive.nadab.domain.dailyreport.core.dto.AnswerDetailDto( + ae.question.questionText, ae.question.interest.code, ae.date, ae.content, dr.content, e.code + ) + from AnswerEntry ae + left join DailyReport dr on dr.answerEntry = ae and dr.status = com.devkor.ifive.nadab.domain.dailyreport.core.entity.DailyReportStatus.COMPLETED + left join dr.emotion e + where ae.id = :answerId + and ae.user.id = :userId + """) + Optional findDetailByAnswerId( + @Param("userId") Long userId, + @Param("answerId") Long answerId + ); + + /** + * 날짜로 상세 조회 + */ + @Query(""" + select new com.devkor.ifive.nadab.domain.dailyreport.core.dto.AnswerDetailDto( + ae.question.questionText, ae.question.interest.code, ae.date, ae.content, dr.content, e.code ) from AnswerEntry ae left join DailyReport dr on dr.answerEntry = ae and dr.status = com.devkor.ifive.nadab.domain.dailyreport.core.entity.DailyReportStatus.COMPLETED @@ -113,7 +133,7 @@ List findRecentAnswers( where ae.user.id = :userId and ae.date = :date """) - Optional findByUserAndDate( + Optional findDetailByUserAndDate( @Param("userId") Long userId, @Param("date") LocalDate date ); From e35f2b46561d0d9bf06ffb9f9ed28bdb422e9f00 Mon Sep 17 00:00:00 2001 From: Chanhae Lee Date: Sun, 18 Jan 2026 21:05:18 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor(search):=20=EA=B2=80=EC=83=89=20AP?= =?UTF-8?q?I=EC=99=80=20=EA=B2=80=EC=83=89=EC=96=B4=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20API=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POST /api/v1/search/histories 엔드포인트를 추가 --- .../dailyreport/api/AnswerController.java | 9 +---- .../domain/search/api/SearchController.java | 38 +++++++++++++++++++ .../dto/request/SaveSearchHistoryRequest.java | 14 +++++++ 3 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/devkor/ifive/nadab/domain/search/api/dto/request/SaveSearchHistoryRequest.java diff --git a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/AnswerController.java b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/AnswerController.java index ce1d3ab..8d0f957 100644 --- a/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/AnswerController.java +++ b/src/main/java/com/devkor/ifive/nadab/domain/dailyreport/api/AnswerController.java @@ -7,7 +7,6 @@ import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.MonthlyCalendarResponse; import com.devkor.ifive.nadab.domain.dailyreport.api.dto.response.SearchAnswerEntryResponse; import com.devkor.ifive.nadab.domain.dailyreport.application.AnswerQueryService; -import com.devkor.ifive.nadab.domain.search.application.SearchHistoryCommandService; import com.devkor.ifive.nadab.global.core.response.ApiResponseDto; import com.devkor.ifive.nadab.global.core.response.ApiResponseEntity; import com.devkor.ifive.nadab.global.security.principal.UserPrincipal; @@ -33,7 +32,6 @@ public class AnswerController { private final AnswerQueryService answerQueryService; - private final SearchHistoryCommandService searchHistoryCommandService; @GetMapping @PreAuthorize("isAuthenticated()") @@ -71,9 +69,9 @@ public class AnswerController { - GET /api/v1/answers?keyword=행복&emotionCode=JOY&cursor=2025-12-06 ### 참고사항 - - 검색어는 자동으로 저장됩니다. - keyword와 emotionCode는 동시에 사용 가능합니다. - emotionCode만 사용 시 keyword는 생략 가능합니다. + - 검색어 저장은 POST /api/v1/search/histories 엔드포인트를 통해 별도로 수행할 수 있습니다. """, security = @SecurityRequirement(name = "bearerAuth"), responses = { @@ -90,12 +88,7 @@ public ResponseEntity> searchAnswers( @AuthenticationPrincipal UserPrincipal principal, @Valid @ModelAttribute SearchAnswerEntryRequest request ) { - // 검색 실행 SearchAnswerEntryResponse response = answerQueryService.searchAnswers(principal.getId(), request); - - // 검색어 저장 (동기) - searchHistoryCommandService.saveOrRefreshSearchHistory(principal.getId(), request.keyword()); - return ApiResponseEntity.ok(response); } diff --git a/src/main/java/com/devkor/ifive/nadab/domain/search/api/SearchController.java b/src/main/java/com/devkor/ifive/nadab/domain/search/api/SearchController.java index 8538744..b1f0e65 100644 --- a/src/main/java/com/devkor/ifive/nadab/domain/search/api/SearchController.java +++ b/src/main/java/com/devkor/ifive/nadab/domain/search/api/SearchController.java @@ -1,5 +1,6 @@ package com.devkor.ifive.nadab.domain.search.api; +import com.devkor.ifive.nadab.domain.search.api.dto.request.SaveSearchHistoryRequest; import com.devkor.ifive.nadab.domain.search.api.dto.response.SearchHistoryListResponse; import com.devkor.ifive.nadab.domain.search.application.SearchHistoryCommandService; import com.devkor.ifive.nadab.domain.search.application.SearchHistoryQueryService; @@ -13,6 +14,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -53,6 +55,42 @@ public ResponseEntity> getRecentSearch return ApiResponseEntity.ok(response); } + @PostMapping("/histories") + @PreAuthorize("isAuthenticated()") + @Operation( + summary = "검색어 저장", + description = """ + 사용자가 검색어를 확정했을 때 검색어를 저장합니다. + + - 엔터를 치거나 검색 버튼을 누르거나 답변 검색 결과를 클릭했을 때 호출됩니다. + - 이미 존재하는 검색어는 최신 순서로 갱신됩니다. + - 빈 문자열이나 공백만 있는 경우 저장되지 않습니다. + + ### 에러 처리 + - DB 장애 등으로 내부 오류가 발생해서 실제 저장이 실패하더라도 204를 반환합니다. + """, + security = @SecurityRequirement(name = "bearerAuth"), + responses = { + @ApiResponse( + responseCode = "204", + description = "저장 완료 (내부 오류 발생 시에도 204 반환)" + ), + @ApiResponse( + responseCode = "400", + description = "ErrorCode: VALIDATION_FAILED - 잘못된 요청 (키워드 누락 또는 길이 초과)", + content = @Content + ), + @ApiResponse(responseCode = "401", description = "인증 실패", content = @Content) + } + ) + public ResponseEntity> saveSearchHistory( + @AuthenticationPrincipal UserPrincipal principal, + @Valid @RequestBody SaveSearchHistoryRequest request + ) { + searchHistoryCommandService.saveOrRefreshSearchHistory(principal.getId(), request.keyword()); + return ApiResponseEntity.noContent(); + } + @DeleteMapping("/histories/{historyId}") @PreAuthorize("isAuthenticated()") @Operation( diff --git a/src/main/java/com/devkor/ifive/nadab/domain/search/api/dto/request/SaveSearchHistoryRequest.java b/src/main/java/com/devkor/ifive/nadab/domain/search/api/dto/request/SaveSearchHistoryRequest.java new file mode 100644 index 0000000..527e750 --- /dev/null +++ b/src/main/java/com/devkor/ifive/nadab/domain/search/api/dto/request/SaveSearchHistoryRequest.java @@ -0,0 +1,14 @@ +package com.devkor.ifive.nadab.domain.search.api.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +@Schema(description = "검색어 저장 요청") +public record SaveSearchHistoryRequest( + @Schema(description = "검색어", example = "행복했던 순간") + @NotBlank(message = "검색어는 필수입니다.") + @Size(min = 1, max = 100, message = "검색어는 1자 이상 100자 이하여야 합니다.") + String keyword +) { +} \ No newline at end of file