Coding Note

게시글 조회/수정/삭제 기능 구현하기 본문

SpringBoot/AWS_PJ

게시글 조회/수정/삭제 기능 구현하기

jinnkim 2022. 3. 9. 17:57

 

 

 

전 게시물 등록 화면 구현에 이어서 조회/수정/삭제 화면 및 기능을 구현해봅시다!

 

대규모 프로젝트 시 조회는 조회용 프레임워크를 사용함.

등록/수정/삭제 등은 SpringDataJpa를 통해 진행함.

 

책 속 저자는 조회 시 Querydsl을 추천한다고 한다.

이유는 다음과 같다.

1. 타입 안정성이 보장됨

2. 국내 많은 회사에서 사용 중

3. 래퍼런스가 많음

 


 

 

1. 조회 

전체 조회를 위해 index.mustache 코드 추가

 

- index.mustache

   <!-- 목록 출력 영역 -->
    <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>

 

머스테치의 문법 사용함.

 

< 간단한 코드 설명 >

1.  {{#posts}}

  - posts라는 Lists를 순회함.

  - java의 for문과 동일함

 

2.  {{id}} 등의 {{변수명}}

  - List에서 뽑아낸 객체의 필드를 사용함.

 

 

 

- 기존 PostsRepository 인터페이스에 쿼리 추가

@Query("SELECT p FROM Posts p ORDER BY p.id DESC")
List<Posts> findAllDesc();

JPA에서 사용하지 않는 메소드는 위처럼 쿼리로 작성 

@Query - 가독성 좋음

 

 

 

- PostsService

기존 코드에 아래 코드 추가하기

 @Transactional(readOnly = true)
    public List<PostsListResponseDto> findAllDesc() {
        return postsRepository.findAllDesc().stream()
                .map(PostsListResponseDto::new)//.map(posts -> new PostsListResponseDto(posts))
                .collect(Collectors.toList());
    }

 

(readOnly = true)

- 트랜잭션 범위는 유지하되, 조회 기능만 남겨두어 조회 속도가 개선되기 때문에 등록, 수정, 삭제 기능이 없는 서비스 메소드에서 사용하는 것을 추천!

 

 

- PostsListResponseDto

package com.bs.book.springboot.web.dto;

import com.bs.book.springboot.domain.posts.Posts;
import lombok.Getter;
import java.time.LocalDateTime;

@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

 @GetMapping("/")
    public String index(Model model) {
        model.addAttribute("posts", postsService.findAllDesc());
        return "index";
    }

Model

- 서버 템플릿 엔진에서 사용할 수 있는 객체를 저장할 수 있음

 

 

<결과>


 

 

2. 수정

수정 머스테치 파일 생성하기 

 

- 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>
            </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>
        //수정 완료 버튼 클릭시 기능이 작동하도록 js파일에 기능 추가하기
    </div>
</div>

{{>layout/footer}}

 

< 코드 정리하기 >

1. {{post.객체}}

- 머스테치는 객체의 필드 접근 시 점으로 구분

2. readonly

- input 태그에 읽기 기능만 허용하는 속성

 

기존 index.js 파일에 코드 추가

 

- index.js

//btn-update id의 버튼 클릭시 수정 기능
$('#btn-update').on('click', function () {
            _this.update();
        });
 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));
        });
    }

 

 

- index.mustache

 <tbody id="tbody">
        {{#posts}}
            <tr>
                <td>{{id}}</td>
                //<a> 태그를 통해 페이지 이동
                <td><a href="/posts/update/{{id}}">{{title}}</a></td>
                <td>{{author}}</td>
                <td>{{modifiedDate}}</td>
            </tr>
        {{/posts}}
</tbody>

 

 

- indexController

  //수정
@GetMapping("/posts/update/{id}")
public String postsUpdate(@PathVariable Long id, Model model) {
    PostsResponseDto dto = postsService.findById(id);
    model.addAttribute("post", dto);
    return "posts-update";
}

 

 

- PostsService

 //수정
@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto) {
    Posts posts = postsRepository.findById(id)
            .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id ="+id));
    posts.update(requestDto.getTitle(), requestDto.getContent());

    return id;
}

 

- PostsApiController

 //수정
@PutMapping("/api/v1/posts/{id}")
public Long Update(@PathVariable Long id, @RequestBody PostsUpdateRequestDto requestDto) {
    return postsService.update(id, requestDto);
}

 

 

 

< 수정 기능 결과 >

 

 

 

 

3. 삭제

posts-update.mustache 파일에 삭제 버튼 추가하기

 <button type="button" class="btn btn-danger" id="btn-delete">삭제</button>

 

- index.js

삭제 기능 추가

 $('#btn-delete').on('click', function () {
            _this.delete();
        });

삭제 버튼 클릭 시 아래 삭제 기능을 시현함.

  delete : function () {
        var id = $('#id').val();

        $.ajax({
            type: 'DELETE',
            url: '/api/v1/posts/'+id,
            dataType: 'json',
            contentType:'application/json; charset=utf-8'
        }).done(function() {
            alert('글이 삭제되었습니다.');
            window.location.href = '/';
        }).fail(function (error) {
            alert(JSON.stringify(error));
        });
    }

 

 

- PostsService

 //삭제
@Transactional
public void delete (Long id) {
    Posts posts = postsRepository.findById(id)
            .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id=" + id));

    postsRepository.delete(posts);
}

 

< 코드 정리하기 >

 

1. postsRepository.delete(posts);

- 존재하는 posts인지 조회 후 삭제함.

- 엔티티를 파라미터로 삭제할 수 있고, deleteById메소드를 이용해 삭제할 수 있음.

 

 

- PostsApiController

//삭제
@DeleteMapping("/api/v1/posts/{id}")
public Long delete(@PathVariable Long id) {
    postsService.delete(id);
    return id;
}

 

 

 

< 삭제 기능 결과 >

 

 

 

 

 

블로그에 정리하고 보면 왜 헤맸나 싶다

어쨌든 오늘도 완료!

 

Comments