diff --git a/src/main/java/com/moplus/moplus_server/domain/problemset/controller/ProblemSetSearchController.java b/src/main/java/com/moplus/moplus_server/domain/problemset/controller/ProblemSetSearchController.java index 044c567..0b65a47 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problemset/controller/ProblemSetSearchController.java +++ b/src/main/java/com/moplus/moplus_server/domain/problemset/controller/ProblemSetSearchController.java @@ -28,11 +28,9 @@ public class ProblemSetSearchController { ) public ResponseEntity> search( @RequestParam(value = "problemSetTitle", required = false) String problemSetTitle, - @RequestParam(value = "problemTitle", required = false) String problemTitle, - @RequestParam(value = "conceptTagNames", required = false) List conceptTagNames + @RequestParam(value = "problemTitle", required = false) String problemTitle ) { - List problemSets = problemSetSearchRepository.search(problemSetTitle, problemTitle, - conceptTagNames); + List problemSets = problemSetSearchRepository.search(problemSetTitle, problemTitle); return ResponseEntity.ok(problemSets); } @@ -44,11 +42,10 @@ public ResponseEntity> search( ) public ResponseEntity> confirmSearch( @RequestParam(value = "problemSetTitle", required = false) String problemSetTitle, - @RequestParam(value = "problemTitle", required = false) String problemTitle, - @RequestParam(value = "conceptTagNames", required = false) List conceptTagNames + @RequestParam(value = "problemTitle", required = false) String problemTitle ) { List problemSets = problemSetSearchRepository.confirmSearch(problemSetTitle, - problemTitle, conceptTagNames); + problemTitle); return ResponseEntity.ok(problemSets); } } diff --git a/src/main/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetSearchRepositoryCustom.java b/src/main/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetSearchRepositoryCustom.java index 9faa3f1..b93d688 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetSearchRepositoryCustom.java +++ b/src/main/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetSearchRepositoryCustom.java @@ -22,8 +22,7 @@ public class ProblemSetSearchRepositoryCustom { private final JPAQueryFactory queryFactory; - public List search(String problemSetTitle, String problemTitle, - List conceptTagNames) { + public List search(String problemSetTitle, String problemTitle) { return queryFactory .from(problemSet) .leftJoin(problem).on(problem.id.in(problemSet.problemIds)) // 문제 세트 내 포함된 문항과 조인 @@ -31,8 +30,7 @@ public List search(String problemSetTitle, String p .leftJoin(publish).on(publish.problemSetId.eq(problemSet.id)) // 문제 세트와 발행 데이터 조인 .where( containsProblemSetTitle(problemSetTitle), - containsProblemTitle(problemTitle), - containsConceptTagNames(conceptTagNames) + containsProblemTitle(problemTitle) ) .distinct() .transform(GroupBy.groupBy(problemSet.id).list( @@ -51,8 +49,7 @@ public List search(String problemSetTitle, String p )); } - public List confirmSearch(String problemSetTitle, String problemTitle, - List conceptTagNames) { + public List confirmSearch(String problemSetTitle, String problemTitle) { return queryFactory .from(problemSet) .leftJoin(problem).on(problem.id.in(problemSet.problemIds)) // 문제 세트 내 포함된 문항과 조인 @@ -61,8 +58,7 @@ public List confirmSearch(String problemSetTitle, S .where( problemSet.confirmStatus.eq(CONFIRMED), containsProblemSetTitle(problemSetTitle), - containsProblemTitle(problemTitle), - containsConceptTagNames(conceptTagNames) + containsProblemTitle(problemTitle) ) .distinct() .transform(GroupBy.groupBy(problemSet.id).list( @@ -89,8 +85,4 @@ private BooleanExpression containsProblemSetTitle(String problemSetTitle) { private BooleanExpression containsProblemTitle(String problemTitle) { return (problemTitle == null || problemTitle.isEmpty()) ? null : problem.memo.containsIgnoreCase(problemTitle); } - - private BooleanExpression containsConceptTagNames(List conceptTagNames) { - return (conceptTagNames == null || conceptTagNames.isEmpty()) ? null : conceptTag.name.in(conceptTagNames); - } } \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/global/response/ResponseDto.java b/src/main/java/com/moplus/moplus_server/global/response/ResponseDto.java new file mode 100644 index 0000000..545e075 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/global/response/ResponseDto.java @@ -0,0 +1,20 @@ +package com.moplus.moplus_server.global.response; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.moplus.moplus_server.global.error.ErrorResponse; +import org.springframework.http.HttpStatus; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public record ResponseDto( + T data, + String message, + HttpStatus status +) { + public static ResponseDto success(final T data) { + return new ResponseDto<>(data, null, null); + } + + public static ResponseDto fail(ErrorResponse errorResponse) { + return new ResponseDto<>(null, errorResponse.getMessage(), errorResponse.getStatus()); + } +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/global/response/ResponseDtoAdvice.java b/src/main/java/com/moplus/moplus_server/global/response/ResponseDtoAdvice.java new file mode 100644 index 0000000..af24122 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/global/response/ResponseDtoAdvice.java @@ -0,0 +1,39 @@ +package com.moplus.moplus_server.global.response; + +import com.moplus.moplus_server.global.error.ErrorResponse; +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +@RestControllerAdvice( + basePackages = "com.moplus.moplus_server" +) + +public class ResponseDtoAdvice implements ResponseBodyAdvice { + + @Override + public boolean supports(MethodParameter returnType, Class converterType) { + return !(returnType.getParameterType() == ResponseDto.class) + && MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType); + } + + @Override + public Object beforeBodyWrite( + Object body, + MethodParameter returnType, + MediaType selectedContentType, + Class selectedConverterType, + ServerHttpRequest request, + ServerHttpResponse response + ) { + if (body instanceof ErrorResponse) { + return ResponseDto.fail((ErrorResponse) body); + } + return ResponseDto.success(body); + } +} diff --git a/src/test/java/com/moplus/moplus_server/domain/member/controller/MemberControllerTest.java b/src/test/java/com/moplus/moplus_server/domain/member/controller/MemberControllerTest.java index 2ce90a3..3d3622d 100644 --- a/src/test/java/com/moplus/moplus_server/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/moplus/moplus_server/domain/member/controller/MemberControllerTest.java @@ -70,9 +70,9 @@ public void setMockMvc() throws Exception { .contentType("application/json") .header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)) .andExpect(status().isOk()) // 200 응답 확인 - .andExpect(jsonPath("$.id").exists()) // MemberGetResponse의 필드 확인 - .andExpect(jsonPath("$.name").exists()) - .andExpect(jsonPath("$.email").exists()); + .andExpect(jsonPath("$.data.id").exists()) // MemberGetResponse의 필드 확인 + .andExpect(jsonPath("$.data.name").exists()) + .andExpect(jsonPath("$.data.email").exists()); } } } \ No newline at end of file diff --git a/src/test/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetSearchRepositoryCustomTest.java b/src/test/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetSearchRepositoryCustomTest.java index bffb84d..33bc144 100644 --- a/src/test/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetSearchRepositoryCustomTest.java +++ b/src/test/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetSearchRepositoryCustomTest.java @@ -36,7 +36,7 @@ public class ProblemSetSearchRepositoryCustomTest { @Test void 문항세트_타이틀_일부_포함_검색() { // when - List result = problemSetSearchRepository.search("고2 모의고사", null, null); + List result = problemSetSearchRepository.search("고2 모의고사", null); // then assertThat(result).hasSize(1); @@ -46,18 +46,7 @@ public class ProblemSetSearchRepositoryCustomTest { @Test void 문항타이틀_포함_검색() { // when - List result = problemSetSearchRepository.search(null, "설명", null); - - // then - assertThat(result).hasSize(2); - assertThat(result.get(0).getProblemSetTitle()).isEqualTo("2025년 5월 고2 모의고사 문제 세트"); - assertThat(result.get(1).getProblemSetTitle()).isEqualTo("2025년 5월 고3 모의고사 문제 세트"); - } - - @Test - void 개념태그_하나라도_포함되면_조회() { - // when - List result = problemSetSearchRepository.search(null, null, List.of("미분 개념")); + List result = problemSetSearchRepository.search(null, "설명"); // then assertThat(result).hasSize(2); @@ -68,7 +57,7 @@ public class ProblemSetSearchRepositoryCustomTest { @Test void 모두_적용된_검색() { // when - List result = problemSetSearchRepository.search("고2", "설명 1", List.of("미분 개념")); + List result = problemSetSearchRepository.search("고2", "설명 1"); // then assertThat(result).hasSize(1); @@ -78,7 +67,7 @@ public class ProblemSetSearchRepositoryCustomTest { @Test void 아무_조건도_없으면_모든_데이터_조회() { // when - List result = problemSetSearchRepository.search(null, null, null); + List result = problemSetSearchRepository.search(null, null); // then assertThat(result).hasSize(2); @@ -87,7 +76,7 @@ public class ProblemSetSearchRepositoryCustomTest { @Test void 문항_여러개_문항세트_검색_조회() { // when - List result = problemSetSearchRepository.search("고2 모의고사", null, null); + List result = problemSetSearchRepository.search("고2 모의고사", null); // then assertThat(result).hasSize(1); @@ -111,7 +100,7 @@ public class ProblemSetSearchRepositoryCustomTest { @Test void 발행되지_않은_문항세트는_NOT_CONFIRMED_테스트() { // when - List result = problemSetSearchRepository.search("고2 모의고사", null, null); + List result = problemSetSearchRepository.search("고2 모의고사", null); // then assertThat(result).hasSize(1); @@ -128,7 +117,7 @@ public class ProblemSetSearchRepositoryCustomTest { publishSaveService.createPublish(new PublishPostRequest(publishDate, 2L)); // when - List result = problemSetSearchRepository.search("고3 모의고사", null, null); + List result = problemSetSearchRepository.search("고3 모의고사", null); // then assertThat(result).hasSize(1); @@ -146,8 +135,7 @@ public class ProblemSetSearchRepositoryCustomTest { // when: publishSearch 실행 (CONFIRMED 상태만 검색되어야 함) List result = problemSetSearchRepository.confirmSearch( "고", - "설명", - List.of("미분 개념") + "설명" ); // then @@ -164,7 +152,6 @@ public class ProblemSetSearchRepositoryCustomTest { // when: 발행된 문제 세트만 조회하는 publishSearch 실행 List result = problemSetSearchRepository.confirmSearch( - null, null, null ); @@ -182,7 +169,6 @@ public class ProblemSetSearchRepositoryCustomTest { // when List result = problemSetSearchRepository.confirmSearch( "고3 모의고사", - null, null );