반응형
- View template 수정
- BackEnd
- Posts List Dto 생성
- Controller, Service, Repository Query 생성
View
- Template 수정
- Posts를 보여주기위한 table 구성
index.mustache
- Mustache 문법
- {{#posts}}
- posts라는 List를 순회
- Java의 for문과 동일하게 작동
- {{id}}등의 {{변수명}}
- List에서 뽑아낸 객체의 필드를 사용
- {{#posts}}
{{>layout/header}}
<h1>스프링 부트로 시작하는 웹 서비스 No. 3</h1>
<div class="col-md-12">
<div class="row">
<div class="col-md-6">
<a href = "/posts/save" role="button" class="btn btn-primary">
글 등록
</a>
</div>
</div>
<br>
<!-- 목록 출력 영역 -->
<table class="table table-horizontal table-bordered">
<thead class="thead-strong">
<tr>
<th>게시글번호</th>
<th>제목</th>
<th>작성자</th>
<th>최종수정일</th>
</tr>
</thead>
<tbody id="tbody">
{{#posts}}
<tr>
<td>{{id}}</td>
<td><a href="/posts/update/{{id}}">{{title}}</a></td>
<td>{{author}}</td>
<td>{{modifiedDate}}</td>
</tr>
{{/posts}}
</tbody>
</table>
</div>
{{>layout/footer}}
BackEnd
PostsListResponseDto
- PostsListResponseDto
- Posts의 Entity를 이용하여 필요한 필드만 Dto로 구성
@Getter
public class PostsListResponseDto {
private Long id;
private String title;
private String author;
private LocalDateTime modifiedDate;
public PostsListResponseDto(Posts entity) {
this.id = entity.getId();
this.title = entity.getTitle();
this.author = entity.getAuthor();
this.modifiedDate = entity.getModifiedDate();
}
}
- PostsRepository
- JpaRepository 인터페이스에 Entity에 접근하여 가져오는 findAll() 메서드가 있다.
- Spring Data JPA에서 제공하지 않는 메서드를 사용하기 위해서 @Query를 사용하는 방법도 있다.
- findAllDesc을 새로 작성하여 PostsListResponseDto라는 Controller와 Service에서 접근가능한 Dto를 사용하여 호출
- @Query가 가독성이 더 좋을 수도 있다.
- Entity 클래스만으로 처리하기 어려운 경우
- querydsl, jooq, MyBatis 등 프레임워크를 추가하여 조회용으로 사용할 수 있다.
- 기본적인 등록 / 수정 / 삭제는 Spring Data JPA만으로도 충분히 가능하다.
- querydsl을 추천하는 이유
- 타입의 안정성
- 단순한 문자열로 쿼리를 생성하는 것이 아니라, 메서드를 기반으로 쿼리를 생성
- 오타나 존재하지 않는 컬럼명을 명시하는 경우 IDE에서 자동으로 검증이 가능하다.
- 타입의 안정성
public interface PostsRepository extends JpaRepository<Posts, Long> {
@Query("SELECT p FROM Posts p ORDER BY p.id DESC")
List<Posts> findAllDesc();
}
- PostsService
- PostsRepository에서 구현한 findAllDesc() 메서드를 호출
- List<Posts> 를 PostsListResponseDto로 매핑하여 List<PostsResponseDto>로 리턴
- findAllDesc() 메서드의 트랜잭션 어노테이션에 readOnly = true라는 옵션을 추가- 이는 트랜잭션 범위는 유지하되, 조회 기능만 남겨두어 조회 속도를 개선할 수 있다.
- 등록, 수정, 삭제 기능이 전혀없는 서비스 메서드에서 사용하는 것을 추천
- 람다 설명- .map(PostsListResponseDto::new) - .map(posts -> new PostsListResponseDto(posts)) - postsRepository 결과로 넘어온 Posts의 Stream을 map을 통해 PostsListReponseDto로 변환 -> List로 반환하는 메서드
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;
// ... save, update, findById
@Transactional(readOnly = true)
public List<PostsListResponseDto> findAllDesc() {
return postsRepository.findAll().stream()
.map(PostsListResponseDto::new)
.collect(Collectors.toList());
}
}
- IndexController
- Service Layer에서 List<PostsListResponseDto> 를 반환하는 findAllDesc()를 model에 담아 View로 전달한다.
- Model
- 서버 템플릿 엔진에서 사용할 수 있는 객체를 저장
- postsService.findAllDesc()로 가져온 결과를 posts로 index.mustache에 전달
@RequiredArgsConstructor
@Controller
public class IndexController {
private final PostsService postsService;
@GetMapping("/")
public String index(Model model) {
model.addAttribute("posts", postsService.findAllDesc());
return "index";
}
//... save
}
게시글 화면 확인