이번 강의 시간에는 배운 것들을 총점검하는 시간으로 게시판 만들기 과제가 주어졌다. 만들면서 배웠던 것을 복기하고 어떤 시행착오가 있는지 간단히 정리하려 한다.
한 페이지당 글 개수 10개.
@GetMapping("pagination")
public String pagination(Model model, @RequestParam(defaultValue = "1") int page) {
int startPage = (page-1) / 10 * 10 + 1;
int endPage = startPage + 9;
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
model.addAttribute("page", page);
return "pagination";
}
Pageable 이용Page<T> 객체로 페이지 정보 및 데이터 제공@GetMapping("/board/list")
public String boardList(
@RequestParam(defaultValue = "0") int page,
Model model) {
Pageable pageable = PageRequest.of(page, 10, Sort.by("id").descending());
Page<Board> boardPage = boardRepository.findAll(pageable);
model.addAttribute("boardPage", boardPage);
model.addAttribute("currentPage", page);
return "board/list";
}
두 방식의 비교
| 항목 | 전통 페이징 | Spring JPA 페이징 |
|---|---|---|
| 페이징 처리 | 수동(offset 계산) | 자동(Pageable) |
| 블럭 페이징 (1~10 등) | 가능 (직접 계산) | 가능 (계산 필요) |
| 총 페이지 수 계산 | 직접 필요 | 자동 처리 |
| 검색/정렬 연동 | 직접 구현 | 메서드명 or @Query |
| 유지보수 | 어려움 | 쉬움 |
| 실무 적합도 | 낮음 | 👍 높음 |
따라서, jpa 방식을 더 많이 사용한다고 함!
코드를 작성하면서 Model 객체로 넘기는 코드는 필수인데, 개념이 명확하지 않아 헷갈릴 때가 많았다.
Model model이란?
MVC에서 controller -> view로 데이터를 전달하는 객체.
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("name", "둘리");
return "hello";
-> hello.html에서 ${name} 으로 접근 가능
}
*
뷰에서 사용 코드 :
<p th:text="${name}">기본값</p>
나는 보통 @Autowired UserRepository userRepository; 이런식으로 필드를 주입해서 작성하는데 나의 gpt 선생님께서는 생성자를 생성해서 코드를 작성하길래 궁금해서 차이를 정리해보았다.
| 항목 | 필드 주입 (@Autowired) | 생성자 주입 (final, 생성자) |
|---|---|---|
| ⭐ 권장도 | ❌ 비추천 (테스트 어려움) | ✅ Spring 공식 권장 방식 |
| 불변성 보장 | 불가능 (final 아님) | 가능 (final 필드로 설정) |
| 테스트 용이성 | Mock 주입 어려움 | ✅ 테스트에서 생성자 통해 직접 주입 가능 |
| 컴파일 시점 체크 | ❌ 주입 누락 시 런타임 오류 | ✅ 컴파일 시점에 누락 확인 가능 |
| 가독성 | 코드 짧지만 의존성 불투명 | 명확한 의존성 구조 보장 |
| 리팩토링 안정성 | 변경 시 영향 범위 넓음 | 안정적인 리팩토링 가능 |
@Autowired 방식은 테스트 코드에서 의존성 주입을 Mock으로 하기가 어렵기 때문에 테스트할때 좋지 않다.
그와 달리 생성자 주입 방식은
private final UserRepository userRepository;
-> final이 붙어서 반드시 초기화
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
---
테스트에서
UserService service = new UserService(mockUserRepository);
이렇게 테스트에서 직접 주입 가능 하고,
이것이 DI에 가장 잘 부합하는 방법이다.
결론은, 필드 주입 방식보다 생성자 + final 방식으로 작성하는 것이 유지 보수, 테스트 용이 등을 생각하면 훨씬 권장되는 방식이라고 한다.
또한, @RequiredArgsConstructor 를 사용하면 생성자 주입을 더 간결하게 작성 할 수 있다.

UserRepository에서 자꾸 오류가 나서 오류코드를 분석해보니,
No property 'password' found for type 'User' 이런 문구가 났다. User entity에서 정의한 password를 pwd라고 작성한 것을 잊어버렸다가 다르게 작성해서 오류가 난 것이다.
오타와의 전쟁
가령 entity에 속성을 created라고 해놨는데 repository 에서 create로 쿼리메소드를 작성했다던지.. 그런 작은 오타가 큰 오류로 돌아온다. ㅎ 꼼꼼하게 처음부터 잘 작성해야 한다!
post 후 실패 시 돌아올 때
HTML의