본문 바로가기

Project

[Project] Lv_4 스케줄 프로젝트

 


 

 

요구사항

설명

  • ✅ 많은 양의 데이터를 효율적으로 표시하기 위해 데이터를 여러 페이지로 나눕니다.
    • ✅ 페이지 번호와 페이지 크기를 쿼리 파라미터로 전달하여 요청하는 항목을 나타냅니다.
    • ✅ 전달받은 페이지 번호와 크기를 기준으로 쿼리를 작성하여 필요한 데이터만을 조회하고 반환

조건

  • ✅ 등록된 일정 목록을 페이지 번호와 크기를 기준으로 모두 조회
  • ✅ 조회한 일정 목록에는 작성자 이름이 포함
  • ✅ 범위를 넘어선 페이지를 요청하는 경우 빈 배열을 반환
  • ✅ Paging 객체를 활용할 수 있음

 


 

요구 구현

가독성을 위해 다른 구현 코드는 넣지 않고 페이지네이션 기능을하는 코드만 넣었습니다.

 

ScheduleController

더보기
@GetMapping("/pages")
@ResponseBody
public List<ScheduleResponseDto> pagination (
    @RequestParam("pageNum") int pageNum,
    @RequestParam("pageSize") int pageSize
) {
    Paging paging = new Paging(pageNum, pageSize);
    return scheduleService.pagination(paging);
}
  • 쿼리 파라미터로 값을 전달받기 위해 @RequestParam을 사용하였습니다. ("pageNum", "pageSize" : 생략 가능)
  • 페이지 번호와 크기 2가지를 받아야하므로, `pageNum`과 `pageSize` 2개의 변수를 활용하였습니다.
  • 여러 개의 데이터를 반환받아야하므로 반환타입은 List입니다.

 

 

ScheduleService

더보기
public interface ScheduleService {

    List<ScheduleResponseDto> pagination (Paging paging);
}
  • 추후 다른 Service를 고려하여, 인터페이스를 통해 서비스 클래스가 구현하도록 설계했습니다. (객체지향 다형성)

 

 

ScheduleServiceImpl

더보기
@Service
public class ScheduleServiceImpl implements ScheduleService{

    private final ScheduleRepository scheduleRepository;

    public ScheduleServiceImpl(ScheduleRepository scheduleRepository) {
        this.scheduleRepository = scheduleRepository;
    }

    @Override
    public List<ScheduleResponseDto> pagination(Paging paging) {
        return scheduleRepository.pagination(paging);
    }
}
  • 처리해야할 서비스 로직이 없으므로, repository로 `Paging` 객체를 넘겨줍니다.

 

 

ScheduleRepository

더보기
public interface ScheduleRepository {

    List<ScheduleResponseDto> pagination(Paging paging);
}

 

 

JdbcTemplateScheduleRepository

더보기
@Repository
public class JdbcTemplateScheduleRepository implements ScheduleRepository{

    private final JdbcTemplate jdbcTemplate;

    public JdbcTemplateScheduleRepository(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public List<ScheduleResponseDto> pagination(Paging paging) {
        String sql = "select * from schedule ORDER BY updateAt DESC LIMIT ? OFFSET ?";
        int pageLimit = paging.getPageSize();
        int pageOffset = (paging.getPageNum()-1) * paging.getPageSize();
        return jdbcTemplate.query(sql, scheduleRowMapper(), pageLimit, pageOffset);
    }
}
  • 몇 번 페이지 : `(pageNum - 1) * pageSize`
  • 몇 개의 데이터 : `pageSize`

 


 

트러블 슈팅

1. BeanCreationException 오류

설명

@RestController
@RequestMapping("/schedules")
public class ScheduleController {
...
    @GetMapping
    public List<ScheduleResponseDto> findAllSchedule() {
        return scheduleService.findAllSchedules();
    }
    
    ...
    
    @GetMapping
    @ResponseBody
    public List<ScheduleResponseDto> pagination (
        @RequestParam("pageNum") int pageNum,
        @RequestParam("pageSize") int pageSize
    ) {
        Paging paging = new Paging(pageNum, pageSize);
        return scheduleService.pagination(paging);
    }

다음과 같이 동일 url("/schedules")에서 동일한 `@GetMapping`을 사용하여, 서버를 작동시켰을 때, 다음과 같이`BeanCreationException` 오류가 발생하였습니다.

 

BeanCreationException

 

 

문제

 

Spring MVC는 내부적으로 `RequestMappingHandlerMapping`이라는 컴포넌트를 사용하여 각 컨트롤러의 `@RequestMapping`, `@GetMapping`, `@PostMapping` 등의 정보를 등록하고 관리합니다.

 

Spring은 애플리케이션이 시작될 때, 모든 `@Controller` 혹은 `@RestController` 빈을 스캔합니다.

각 메서드에 붙은 `@RequestMapping`, `@GetMapping` 등의 어노테이션 정보를 기반으로, URL + HTTP Method + 조건(예: params) 조합을 키로 하여 내부 매핑 테이블을 구성합니다.

@GetMapping("/schedules")
public List<ScheduleResponseDto> findAllSchedule()

@GetMapping("/schedules")
public List<ScheduleResponseDto> pagination(@RequestParam int pageNum, ...)

둘 다 `GET /schedules`로 매핑되므로, 내부적으로는 같은 키가 두 번 등록되며 충돌이 발생합니다.

URL 매핑 충돌로 인해 `BeanCreationException`이 발생하였습니다.

 

 

해결

다른 하나의 메서드의 경로("/pages)를 추가하여 문제를 해결하였습니다.