diff --git a/src/main/java/com/example/umc9th/config/AppConfig.java b/src/main/java/com/example/umc9th/config/AppConfig.java index 80e9239..f55cd8e 100644 --- a/src/main/java/com/example/umc9th/config/AppConfig.java +++ b/src/main/java/com/example/umc9th/config/AppConfig.java @@ -1,16 +1,26 @@ package com.example.umc9th.config; +import com.example.umc9th.global.resolver.PageArgumentResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.web.client.RestTemplate; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; @Configuration @EnableAsync -public class AppConfig { +public class AppConfig implements WebMvcConfigurer { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new PageArgumentResolver()); + } } diff --git a/src/main/java/com/example/umc9th/domain/mission/controller/MissionController.java b/src/main/java/com/example/umc9th/domain/mission/controller/MissionController.java index 1bb7699..0300bd9 100644 --- a/src/main/java/com/example/umc9th/domain/mission/controller/MissionController.java +++ b/src/main/java/com/example/umc9th/domain/mission/controller/MissionController.java @@ -2,36 +2,38 @@ import com.example.umc9th.domain.mission.dto.MissionRequestDto; import com.example.umc9th.domain.mission.dto.MissionResponseDto; +import com.example.umc9th.domain.mission.entity.Mission; import com.example.umc9th.domain.mission.service.MissionService; -import com.example.umc9th.domain.review.dto.ReviewRequestDto; -import com.example.umc9th.domain.review.dto.ReviewResponseDto; +import com.example.umc9th.global.annotation.CheckPage; import com.example.umc9th.global.apiPayload.ApiResponse; import com.example.umc9th.global.apiPayload.code.status.GeneralSuccessCode; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; +import com.example.umc9th.global.dto.PageResponseDto; import lombok.RequiredArgsConstructor; +import org.hibernate.annotations.Check; import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @RequestMapping("/api/missions") -@Tag(name = "미션") -public class MissionController { +public class MissionController implements MissionControllerDocs{ private final MissionService missionService; - @Operation( - summary = "미션 추가", - description = "관리자용") @PostMapping("/admin") - public ApiResponse addMission(@RequestBody MissionRequestDto.CreateMission req) { + public ApiResponse createMission(@RequestBody MissionRequestDto.CreateMission req) { return ApiResponse.onSuccess(GeneralSuccessCode._CREATED, missionService.createMission(req)); } - @Operation( - summary = "사용자 미션 추가", - description = "사용자가 가게 미션을 본인의 도전 미션에 추가합니다.") @PostMapping("/{mission-id}") - public ApiResponse addMission(@RequestParam @PathVariable("mission-id")Long missionId) { + public ApiResponse addMission(@PathVariable("mission-id")Long missionId) { return ApiResponse.onSuccess(GeneralSuccessCode._CREATED, missionService.addUserMission(missionId)); } + + @GetMapping("/{store-id}") + public ApiResponse> getMissions(@PathVariable("store-id")Long storeId, + @CheckPage Integer page, + Integer size) { + return ApiResponse.onSuccess(GeneralSuccessCode._OK, missionService.getStoreMissions(storeId, page, size)); + } + + } diff --git a/src/main/java/com/example/umc9th/domain/mission/controller/MissionControllerDocs.java b/src/main/java/com/example/umc9th/domain/mission/controller/MissionControllerDocs.java new file mode 100644 index 0000000..63f86a9 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/mission/controller/MissionControllerDocs.java @@ -0,0 +1,31 @@ +package com.example.umc9th.domain.mission.controller; + +import com.example.umc9th.domain.mission.dto.MissionRequestDto; +import com.example.umc9th.domain.mission.dto.MissionResponseDto; +import com.example.umc9th.domain.mission.entity.Mission; +import com.example.umc9th.global.apiPayload.ApiResponse; +import com.example.umc9th.global.dto.PageResponseDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +@Tag(name = "미션") +public interface MissionControllerDocs { + + @Operation( + summary = "미션 추가", + description = "관리자용") + public ApiResponse createMission(MissionRequestDto.CreateMission req); + + @Operation( + summary = "사용자 미션 추가", + description = "사용자가 가게 미션을 본인의 도전 미션에 추가합니다.") + public ApiResponse addMission(Long missionId); + + @Operation( + summary = "특정 가게 미션 목록", + description = "특정 가게 미션 목록을 조회합니다.") + public ApiResponse> getMissions(Long storeId, Integer page, Integer size); + + + +} diff --git a/src/main/java/com/example/umc9th/domain/mission/converter/MissionConverter.java b/src/main/java/com/example/umc9th/domain/mission/converter/MissionConverter.java new file mode 100644 index 0000000..6625cf3 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/mission/converter/MissionConverter.java @@ -0,0 +1,29 @@ +package com.example.umc9th.domain.mission.converter; + +import com.example.umc9th.domain.mission.dto.MissionRequestDto; +import com.example.umc9th.domain.mission.dto.MissionResponseDto; +import com.example.umc9th.domain.mission.dto.UserMissionDTO; +import com.example.umc9th.domain.mission.entity.Mission; +import com.example.umc9th.domain.mission.entity.UserMission; +import com.example.umc9th.domain.user.entity.User; + +public class MissionConverter { + public static MissionResponseDto.MissionInfo toMissionInfo(Mission mission){ + return MissionResponseDto.MissionInfo.builder() + .missionId(mission.getId()) + .content(mission.getContent()) + .createdAt(mission.getCreatedAt().toString()) + .build(); + } + + public static UserMissionDTO toUserMissionDto(UserMission um){ + return UserMissionDTO.builder(). + id(um.getMission().getId()) + .point(um.getMission().getPoint()) + .storeName(um.getMission().getStore().getName()) + .missionContent(um.getMission().getContent()) + .isCompleted(um.getIsCompleted()) + .build(); + } + +} diff --git a/src/main/java/com/example/umc9th/domain/mission/dto/MissionResponseDto.java b/src/main/java/com/example/umc9th/domain/mission/dto/MissionResponseDto.java index 5564066..b30ec60 100644 --- a/src/main/java/com/example/umc9th/domain/mission/dto/MissionResponseDto.java +++ b/src/main/java/com/example/umc9th/domain/mission/dto/MissionResponseDto.java @@ -10,7 +10,7 @@ private MissionResponseDto() {} @Builder @Getter @AllArgsConstructor - public static class AddUserMission{ + public static class MissionInfo{ private Long missionId; private String content; private String createdAt; diff --git a/src/main/java/com/example/umc9th/domain/mission/dto/UserMissionDTO.java b/src/main/java/com/example/umc9th/domain/mission/dto/UserMissionDTO.java index fb40d9c..9e9f744 100644 --- a/src/main/java/com/example/umc9th/domain/mission/dto/UserMissionDTO.java +++ b/src/main/java/com/example/umc9th/domain/mission/dto/UserMissionDTO.java @@ -1,10 +1,12 @@ package com.example.umc9th.domain.mission.dto; +import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor +@Builder public class UserMissionDTO { private final Long id; private final Integer point; diff --git a/src/main/java/com/example/umc9th/domain/mission/entity/UserMission.java b/src/main/java/com/example/umc9th/domain/mission/entity/UserMission.java index fd80d75..a5bc05b 100644 --- a/src/main/java/com/example/umc9th/domain/mission/entity/UserMission.java +++ b/src/main/java/com/example/umc9th/domain/mission/entity/UserMission.java @@ -32,4 +32,7 @@ public class UserMission extends BaseEntity { @Builder.Default private Boolean isCompleted = false; + public void complete() { + this.isCompleted = true; + } } diff --git a/src/main/java/com/example/umc9th/domain/mission/repository/MissionRepository.java b/src/main/java/com/example/umc9th/domain/mission/repository/MissionRepository.java index 574cb69..b4aacb8 100644 --- a/src/main/java/com/example/umc9th/domain/mission/repository/MissionRepository.java +++ b/src/main/java/com/example/umc9th/domain/mission/repository/MissionRepository.java @@ -2,6 +2,7 @@ import com.example.umc9th.domain.mission.dto.MissionInAreaDTO; import com.example.umc9th.domain.mission.entity.Mission; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -17,4 +18,6 @@ public interface MissionRepository extends JpaRepository { "WHERE s.address LIKE CONCAT(:regionName, '%') AND m.createdAt < :lastCreatedAt " + "ORDER BY m.createdAt DESC") Slice findMissionsInArea(@Param("regionName") String regionName, @Param("lastCreatedAt") LocalDateTime lastCreatedAt, Pageable pageable); + + Page findByStoreId(Long storeId, Pageable pageable); } diff --git a/src/main/java/com/example/umc9th/domain/mission/repository/UserMissionRepository.java b/src/main/java/com/example/umc9th/domain/mission/repository/UserMissionRepository.java index 03e0052..ba76ba1 100644 --- a/src/main/java/com/example/umc9th/domain/mission/repository/UserMissionRepository.java +++ b/src/main/java/com/example/umc9th/domain/mission/repository/UserMissionRepository.java @@ -2,6 +2,7 @@ import com.example.umc9th.domain.mission.dto.UserMissionDTO; import com.example.umc9th.domain.mission.entity.UserMission; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -22,4 +23,7 @@ public interface UserMissionRepository extends JpaRepository "WHERE um.user.id = :userId AND um.isCompleted = :isCompleted AND um.id < :lastMissionId " + "ORDER BY um.id DESC") Slice findUserMissionsByIsCompleted(@Param("userId") Long userId, @Param("isCompleted") Boolean isCompleted, @Param("lastMissionId") Long lastMissionId, Pageable pageable); + + Page findByUserIdAndIsCompleted(Long userId, Boolean isCompleted, Pageable pageable); + } \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/domain/mission/service/MissionService.java b/src/main/java/com/example/umc9th/domain/mission/service/MissionService.java index 477391e..5dc6246 100644 --- a/src/main/java/com/example/umc9th/domain/mission/service/MissionService.java +++ b/src/main/java/com/example/umc9th/domain/mission/service/MissionService.java @@ -2,8 +2,11 @@ import com.example.umc9th.domain.mission.dto.MissionRequestDto; import com.example.umc9th.domain.mission.dto.MissionResponseDto; +import com.example.umc9th.domain.mission.entity.Mission; +import com.example.umc9th.global.dto.PageResponseDto; public interface MissionService { String createMission(MissionRequestDto.CreateMission req); - MissionResponseDto.AddUserMission addUserMission(Long missionId); + MissionResponseDto.MissionInfo addUserMission(Long missionId); + PageResponseDto getStoreMissions(Long storeId, Integer page, Integer size); } diff --git a/src/main/java/com/example/umc9th/domain/mission/service/MissionServiceImpl.java b/src/main/java/com/example/umc9th/domain/mission/service/MissionServiceImpl.java index 5519327..6a60c9d 100644 --- a/src/main/java/com/example/umc9th/domain/mission/service/MissionServiceImpl.java +++ b/src/main/java/com/example/umc9th/domain/mission/service/MissionServiceImpl.java @@ -1,5 +1,6 @@ package com.example.umc9th.domain.mission.service; +import com.example.umc9th.domain.mission.converter.MissionConverter; import com.example.umc9th.domain.mission.dto.MissionRequestDto; import com.example.umc9th.domain.mission.dto.MissionResponseDto; import com.example.umc9th.domain.mission.entity.Mission; @@ -8,7 +9,11 @@ import com.example.umc9th.domain.mission.repository.UserMissionRepository; import com.example.umc9th.domain.store.repository.StoreRepository; import com.example.umc9th.domain.user.repository.UserRepository; +import com.example.umc9th.global.dto.PageResponseDto; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @Service @@ -33,7 +38,7 @@ public String createMission(MissionRequestDto.CreateMission req) { } @Override - public MissionResponseDto.AddUserMission addUserMission(Long missionId) { + public MissionResponseDto.MissionInfo addUserMission(Long missionId) { UserMission um = UserMission.builder() .user(userRepository.getReferenceById(1L)) .mission(missionRepository.getReferenceById(missionId)) @@ -41,10 +46,17 @@ public MissionResponseDto.AddUserMission addUserMission(Long missionId) { userMissionRepository.save(um); - return MissionResponseDto.AddUserMission.builder() + return MissionResponseDto.MissionInfo.builder() .missionId(missionId) .content(um.getMission().getContent()) .createdAt(um.getMission().getCreatedAt().toString()) .build(); } + + @Override + public PageResponseDto getStoreMissions(Long storeId, Integer page, Integer size) { + Page result = missionRepository.findByStoreId(storeId, PageRequest.of(page, size)); + Page missionInfoPage = result.map(MissionConverter::toMissionInfo); + return new PageResponseDto<>(missionInfoPage); + } } diff --git a/src/main/java/com/example/umc9th/domain/user/controller/UserController.java b/src/main/java/com/example/umc9th/domain/user/controller/UserController.java index abc66f5..ec8c7c1 100644 --- a/src/main/java/com/example/umc9th/domain/user/controller/UserController.java +++ b/src/main/java/com/example/umc9th/domain/user/controller/UserController.java @@ -1,9 +1,12 @@ package com.example.umc9th.domain.user.controller; +import com.example.umc9th.domain.mission.dto.UserMissionDTO; import com.example.umc9th.domain.user.service.UserService; +import com.example.umc9th.global.annotation.CheckPage; import com.example.umc9th.global.apiPayload.ApiResponse; import com.example.umc9th.global.apiPayload.code.BaseSuccessCode; import com.example.umc9th.global.apiPayload.code.status.GeneralSuccessCode; +import com.example.umc9th.global.dto.PageResponseDto; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -11,12 +14,24 @@ @RestController @RequiredArgsConstructor @RequestMapping("/users") -public class UserController { - +public class UserController implements UserControllerDocs { private final UserService userService; @DeleteMapping("/{userId}") public ApiResponse deleteUser(@PathVariable("userId") Long userId) { return ApiResponse.onSuccess(GeneralSuccessCode._DELETED, userService.deleteUser(userId)); } + + @Override + @GetMapping("/missions/{isCompleted}") + public ApiResponse> getMyMissions(@PathVariable("isCompleted")Boolean isCompleted, + @CheckPage Integer page, + Integer size) { + return ApiResponse.onSuccess(GeneralSuccessCode._OK, userService.getUserMissions(isCompleted, page, size)); + } + + @PatchMapping("/{userMissionId}") + public ApiResponse completeMission(@PathVariable("userMissionId")Long userMissionId) { + return ApiResponse.onSuccess(GeneralSuccessCode._OK, userService.completeMission(userMissionId)); + } } diff --git a/src/main/java/com/example/umc9th/domain/user/controller/UserControllerDocs.java b/src/main/java/com/example/umc9th/domain/user/controller/UserControllerDocs.java new file mode 100644 index 0000000..a0c8816 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/user/controller/UserControllerDocs.java @@ -0,0 +1,23 @@ +package com.example.umc9th.domain.user.controller; + +import com.example.umc9th.domain.mission.dto.MissionRequestDto; +import com.example.umc9th.domain.mission.dto.MissionResponseDto; +import com.example.umc9th.domain.mission.dto.UserMissionDTO; +import com.example.umc9th.global.apiPayload.ApiResponse; +import com.example.umc9th.global.dto.PageResponseDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +@Tag(name = "사용자") +public interface UserControllerDocs { + @Operation( + summary = "내가 진행중인 미션 목록", + description = "isCompleted에 따라 진행중/완료된 미션 목록을 조회합니다") + public ApiResponse> getMyMissions(Boolean isCompleted, Integer page, Integer size); + + @Operation( + summary = "미션 완료", + description = "진행중인 사용자 미션을 진행 완료로 변경합니다") + public ApiResponse completeMission(Long userMissionId); + +} diff --git a/src/main/java/com/example/umc9th/domain/user/service/UserService.java b/src/main/java/com/example/umc9th/domain/user/service/UserService.java index 1c61a92..8326907 100644 --- a/src/main/java/com/example/umc9th/domain/user/service/UserService.java +++ b/src/main/java/com/example/umc9th/domain/user/service/UserService.java @@ -1,5 +1,10 @@ package com.example.umc9th.domain.user.service; +import com.example.umc9th.domain.mission.dto.UserMissionDTO; +import com.example.umc9th.global.dto.PageResponseDto; + public interface UserService { String deleteUser(Long userId); + PageResponseDto getUserMissions(Boolean isCompleted, Integer page, Integer size); + String completeMission(Long userMissionId); } diff --git a/src/main/java/com/example/umc9th/domain/user/service/UserServiceImpl.java b/src/main/java/com/example/umc9th/domain/user/service/UserServiceImpl.java index 57b9a3d..7c7231a 100644 --- a/src/main/java/com/example/umc9th/domain/user/service/UserServiceImpl.java +++ b/src/main/java/com/example/umc9th/domain/user/service/UserServiceImpl.java @@ -1,10 +1,21 @@ package com.example.umc9th.domain.user.service; import com.example.umc9th.domain.inqury.repository.InquiryRepository; +import com.example.umc9th.domain.mission.converter.MissionConverter; +import com.example.umc9th.domain.mission.dto.UserMissionDTO; +import com.example.umc9th.domain.mission.entity.Mission; +import com.example.umc9th.domain.mission.entity.UserMission; +import com.example.umc9th.domain.mission.repository.MissionRepository; import com.example.umc9th.domain.mission.repository.UserMissionRepository; import com.example.umc9th.domain.review.repository.ReviewRepository; import com.example.umc9th.domain.user.repository.*; +import com.example.umc9th.global.apiPayload.code.status.GeneralErrorCode; +import com.example.umc9th.global.apiPayload.code.status.MissionErrorCode; +import com.example.umc9th.global.apiPayload.exception.GeneralException; +import com.example.umc9th.global.dto.PageResponseDto; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,4 +46,26 @@ public String deleteUser(Long userId) { return "유저" + userId.toString() + "이 삭제되었습니다."; } + + @Override + public PageResponseDto getUserMissions(Boolean isCompleted, Integer page, Integer size) { + Page missions = userMissionRepository.findByUserIdAndIsCompleted(1L, isCompleted, PageRequest.of(page, size)); + Page result = missions.map(MissionConverter::toUserMissionDto); + return new PageResponseDto<>(result); + } + + @Override + @Transactional + public String completeMission(Long userMissionId) { + UserMission um = userMissionRepository.findById(userMissionId) + .orElseThrow(()-> new GeneralException(GeneralErrorCode.NO_RESULT)); + + if(um.getIsCompleted()) { + throw new GeneralException(MissionErrorCode.ALREADY_COMPLETED); + }else{ + um.complete(); + } + + return um.getId().toString() + "번 미션이 완료되었습니다"; + } } diff --git a/src/main/java/com/example/umc9th/global/annotation/CheckPage.java b/src/main/java/com/example/umc9th/global/annotation/CheckPage.java new file mode 100644 index 0000000..478dba3 --- /dev/null +++ b/src/main/java/com/example/umc9th/global/annotation/CheckPage.java @@ -0,0 +1,9 @@ +package com.example.umc9th.global.annotation; + +import java.lang.annotation.*; + +@Documented +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface CheckPage { +} diff --git a/src/main/java/com/example/umc9th/global/apiPayload/code/status/GeneralErrorCode.java b/src/main/java/com/example/umc9th/global/apiPayload/code/status/GeneralErrorCode.java index c18cdf3..bccd113 100644 --- a/src/main/java/com/example/umc9th/global/apiPayload/code/status/GeneralErrorCode.java +++ b/src/main/java/com/example/umc9th/global/apiPayload/code/status/GeneralErrorCode.java @@ -30,6 +30,8 @@ public enum GeneralErrorCode implements BaseErrorCode { INVALID_DATE_FORMAT(HttpStatus.BAD_REQUEST, "COMMON_004", "요청한 날짜/시간 형식이 올바르지 않습니다. 형식을 확인해주세요."), DUPLICATE_UNIQUE_KEY(HttpStatus.CONFLICT, "COMMON_005", "이미 처리된 요청입니다."), + PAGE_NUMBER_UNDER_ZERO(HttpStatus.BAD_REQUEST, "PAGE_4001", "페이지 번호는 1보다 커야 합니다."), + // s3 사진 첨부 에러 FILE_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "FILE_001", "파일 업로드에 실패했습니다."); diff --git a/src/main/java/com/example/umc9th/global/apiPayload/code/status/MissionErrorCode.java b/src/main/java/com/example/umc9th/global/apiPayload/code/status/MissionErrorCode.java new file mode 100644 index 0000000..9f0fcd7 --- /dev/null +++ b/src/main/java/com/example/umc9th/global/apiPayload/code/status/MissionErrorCode.java @@ -0,0 +1,16 @@ +package com.example.umc9th.global.apiPayload.code.status; + +import com.example.umc9th.global.apiPayload.code.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum MissionErrorCode implements BaseErrorCode { + ALREADY_COMPLETED(HttpStatus.BAD_REQUEST,"USER_MISSION4001","이미 완료된 미션입니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; +} diff --git a/src/main/java/com/example/umc9th/global/resolver/PageArgumentResolver.java b/src/main/java/com/example/umc9th/global/resolver/PageArgumentResolver.java new file mode 100644 index 0000000..f89bdca --- /dev/null +++ b/src/main/java/com/example/umc9th/global/resolver/PageArgumentResolver.java @@ -0,0 +1,30 @@ +package com.example.umc9th.global.resolver; + +import com.example.umc9th.global.annotation.CheckPage; +import com.example.umc9th.global.apiPayload.code.status.GeneralErrorCode; +import com.example.umc9th.global.apiPayload.exception.GeneralException; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +public class PageArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(CheckPage.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + String page = webRequest.getParameter("page"); + int pageInt = (page == null) ? 1 : Integer.parseInt(page); + + if (pageInt <= 0) { + throw new GeneralException(GeneralErrorCode.PAGE_NUMBER_UNDER_ZERO); + } + return pageInt-1; // 프론트 1-based -> 백 0-based + } +}