{{>layout/header}}
<h1>스프링 부트로 시작하는 웹 서비스</h1>
</body>
{{>layout/footer}}
<!--{{>}}는 현재 머스테치 파일(index.mustache)을 기준으로 다른 파일을 가져옴-->
var main = {
init: function () {
var _this = this;
$('#btn-save').on('click', function () {
_this.save();
});
},
save: function () {
var data = {
title: $('#title').val(),
author: $('#author').val(),
content: $('#content').val()
};
$.ajax({
type: 'POST',
url: '/api/v1/posts',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data)
}).done(function () {
alert('글이 등록되었습니다.');
window.location.href = '/';
}).fail(function (error) {
alert(JSON.stringify(error));
});
}
};
main.init();
여러 사람이 참여하는 프로젝트에서는 중복된 함수(function)이름은 자주 발생할 수 있음. 그래서 모든 function 이름을 확인하면서 만들 수는 없기 때문에 index.js만의 유효범위(scope)를 만들어서 사용
전체 조회 화면 만들기
<!--목록 출력 영역 -->
<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>{{title}}</td>
<td>{{author}}</td>
<td>{{modifiedDate}}</td>
</tr>
{{/posts}}
</tbody>
</table>
- {{#posts}}
- posts 라는 List를 순회
- Java의 for문과 동일하게 생각
- {{id}} 등의 {{변수명}}
- List에서 뽑아낸 객체의 필드를 사용
Controller, Service, Repository 코드를 작성
기존에 있던 PostsRepository 인터페이스에 쿼리를 추가
public interface PostsRepository extends JpaRepository<Posts, Long> {
// Query 가 가독성이 좋아 선택해서 사용.
// Query 없이 SpringDataJpa 에서 제공하는 기본 메소드만으로도 해결 가능
@Query("SELECT p from Posts p ORDER BY p.id DESC ")
List<Posts> findAllDesc();
}
Entity 클래스만으로 처리가 어려운경우 조회용 프레임워크를 추가로 사용
- querydsl
- jooq
- MyBatis
Querydsl을 사용하는걸 추천하는데 그이유는
- 타입 안정성이 보장됨
- 단순한 문자열로 쿼리를 생성하는 것이 아니라, 메소드를 기반으로 쿼리를 생성하기 때문에 오타나 존재하지 않는 컬럼명을 명시할 경우 IDE에서 자동으로 검출됨. 이 장점은 Jooq에서도 지원하는 장점이지만, MyBatis에서는 지원하지 않음
- 국내 많은 회사에서 사용중
- 쿠팡,배민 등 JPA를 적극적으로 사용하는 회사에서는 Querydsl를 적극적으로 사용중
- 레퍼런스가 많음
- 앞 2번의 장점에서 이어지는 것인데, 많은 회사와 개발자들이 사용하다보니 그만큼 국내 자료가 많음. 어떤 문제가 발생했을 때 여러 커뮤니티에 질문하고 그에대한 답변을 들을 수 있다는것은 큰 장점
PostsService에 코드를 추가
//readOnly = true 트랜잭션 범위는 유지, 조회 기능만 남겨두어 조회속도가 개선됨
//등록, 수정, 삭제 기능이 전혀 없는 메소드에서 사용하는 것을 추천
@Transactional(readOnly = true)
public List<PostsListResponseDto> findAllDesc() {
return postsRepository.findAllDesc()
.stream()
.map(PostsListResponseDto::new) // map(posts -> new PostsListResponseDto(posts)) 람다
.collect(Collectors.toList());
}
PostsListResponseDto 생성
@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();
}
}
IndexController 변경
@Controller
@RequiredArgsConstructor
public class IndexController {
private final PostsService postsService;
@GetMapping("/")
public String index(Model model) {
model.addAttribute("posts", postsService.findAllDesc());
return "index";
}
@GetMapping("/posts/save")
public String postsSave() {
return "posts-save";
}
}
- 참고자료
스프링 부트와 AWS로 혼자 구현하는 웹 서비스
'FrameWork > Spring' 카테고리의 다른 글
로그인 기능 개선 (0) | 2021.08.21 |
---|---|
스프링 시큐리티/ OAuth2.0 로그인 기능 구현 (0) | 2021.08.21 |
머스테치 (0) | 2021.08.21 |
JPA Auditing으로 생성시간/수정시간 자동화하기 (0) | 2021.08.20 |
등록/수정/조회 API (0) | 2021.08.19 |