지난번 등록 화면까지는 만들었지만 버튼을 눌러도 실제로 등록이 되질 않았다. 이번에는 실제 등록이 될 수 있도록 자바스크립트 파일을 사용한다.
src/main/resource에 static/js/app 디렉토리를 생성해주고
해당 디렉토리에 index.js 파일을 생성해준다.
index.js
var main = {
init : function (){
var _this = this;
$('#btn-save').on('click', function (){
_this.save();
});
$('#btn-update').on('click', function (){
_this.update();
})
},
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이라는 코드를 만들어 그 안에 함수를 선언해주었다.
footer에 index.js 파일을 연결해주고 테스트를 해보자
다음과 같이 테스트를 위한 정보를 적어주고 등록 버튼을 눌러주면
http://localhost:8080/h2-console 에 데이터가 저장되는 것을 확인할 수 있다.
전체 조회를 위해 index.mustache 파일을 수정한다.
index.mustache
{{>layout/header}}
<h1>스프링부트로 시작하는 웹 서비스 Ver.2</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}}
<!--posts라는 List를 순회한다. 자바의 for문과 동일-->
<tr>
<td>{{id}}</td>
<td>{{title}}</td>
<td>{{author}}</td>
<td>{{modifiedDate}}</td>
</tr>
{{/posts}}
</tbody>
</table>
</div>
{{>layout/footer}}
이제 전체 조회를 해주기 위해 각 파일들을 수정해준다.
PostsRepository
public interface PostsRepository extends JpaRepository<Posts, Long> {
@Query("SELECT p FROM Posts p ORDER BY p.id DESC")
List<Posts> findAllDesc();
}
PostsRepository에 쿼리가 추가 되었는데 SpringDataJpa에서 제공하지 않는 메소드는 위처럼 직접 쿼리로 작성할 수도 있다.
PostsService
@Transactional(readOnly = true)
public List<PostsListResponseDto> findAllDesc(){
return postsRepository.findAllDesc().stream()
.map(PostsListResponseDto::new)
.collect(Collectors.toList());
}
readOnly = ture : 트랜잭션 범위는 유지하되, 조회 기능만 남겨두어 조회 속도가 개선된다.
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();
}
}
Controller
@RequiredArgsConstructor
@Controller
public class IndexController {
private final PostsService postsService;
@GetMapping("/")
public String index(Model model){ //Model: 서버 템플릿 엔진에서 사용할 수 있는 객체를 저장할 수 있다.
//여기서는 postsService.findAllDesc()로 가져온 결과를 posts로 index.mustache에 전달한다.
model.addAttribute("posts", postsService.findAllDesc());
return "index";
}
}
이렇게 작성해주고 Application의 main 메소드를 실행시켜보면
이렇게 등록한 글을 출력하는 것을 확인할 수 있다.
수정을 위한 api는 기존에 이미 만들어 놓았기에 수정을 위한 화면을 만들어준다.
posts-update.mustache
{{>layout/header}}
<h1>게시글 수정</h1>
<div class="col-md-12">
<div class="col-md-4">
<form>
<div class="form-group">
<label for="title">글 번호</label>
<input type="text" class="form-control" id="id" value="{{post.id}}" readonly>
<!--post.id : 머스테치는 객체의 필드 접근 시 점으로 구분한다.
즉, post 클래스의 id에 대한 접근은 post.id로 사용할 수 있다.
-->
</div>
<div class="form-group">
<label for="title">제목</label>
<input type="text" class="form-control" id="title" value="{{post.title}}">
</div>
<div class="form-group">
<label for="author"> 작성자 </label>
<input type="text" class="form-control" id="author" value="{{post.author}}" readonly>
</div>
<div class="form-group">
<label for="content"> 내용 </label>
<textarea class="form-control" id="content">{{post.content}}</textarea>
</div>
</form>
<a href="/" role="button" class="btn btn-secondary">취소</a>
<button type="button" class="btn btn-primary" id="btn-update">수정 완료</button>
<button type="button" class="btn btn-danger" id="btn-delete">삭제</button>
</div>
</div>
{{>layout/footer}}
버튼 클릭시 수정 기능을 호출할 수 있도록 index.js 파일에도 update function을 만들어준다.
index.js
update : function (){
var data = {
title : $('#title').val(),
content: $('#content').val()
};
var id = $('#id').val();
$.ajax({
type : 'PUT',
url : '/api/v1/posts/' +id,
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));
});
}
전체 목록에서 수정 페이지로 이동할 수 있게 페이지 이동 기능을 추가해준다.
이렇게 a 태그를 활용해서 제목을 클릭하면 수정 페이지로 이동할 수 있게 만들어준다.
수정 화면을 controller로 연결할 수 있게 수정해준다.
IndexController
@GetMapping("/posts/update/{id}")
public String postsUpdate(@PathVariable Long id, Model model){
PostsReponseDto dto = postsService.findById(id);
model.addAttribute("post", dto);
return "posts-update";
}
글의 제목을 눌러서 수정 화면으로 이동하면
다음과 같은 화면이 출력되는 것을 확인할 수 있고
제목과 내용을 수정한 후 수정 완료 버튼을 눌러보면
메인 페이지로 이동후 수정한 데이터를 확인할 수 있다.