Spring_boot/Project

[Spring] REST 방식의 댓글 처리와 JPA 처리

달의요정루나 2021. 8. 19. 19:13

지난 웹 애플리케이션 제작과 연결된다.

https://julian5383.tistory.com/42

 

[Spring] 웹 애플리케이션 제작

1. 프로젝트 구조 생성 - Spring Starter Project로 프로젝트 생성하고 라이브러리 생성 2. pom.xml에 라이브러리 추가 - Add를 눌러 라이브러리를 추가한다.(querydsl-apt, querydsl-jpa, thymeleaf-layout-dia..

julian5383.tistory.com

 

1. REST 방식과 @RestController

- GET 방식 데이터를 보여주거나 다른 사람들에게 알리는 방식:

기본적으로 정보확산 목적, 인터넷상의 URL은 하나의 고유 데이터를 찾는 이름이나 태그가 됨

- POST 방식 데이터를 이용해서 특별한 작업을 처리하는 방식:

정보 가공이 목적, 정확한 목적을 가지고 특정수업을 수행하기 위해 사용

 

- REST 방식:

전송 방식 역할
GET 특정 리소스를 조회(read)하는 용도로 사용 ex) /products/123
POST 특정 리소스를 생성(create)하는 용도로 사용 ex) /products/ 혹은 /member/123
PUT 특정 리소스 수정
DELETE 특정 리소스 삭제

- REST 방식 설계위한 어노테이션:

@RequestBody 클라이언트가 보내는 JSON 데이터의 수집 및 가공
@ResponseBody 클라이언트에게 전송되는 데이터에 맞게 MINE 타입 결정
@PathVariable URL의 경로에 포함된 정보 누출
@RestController 컨트롤러의 모든 메소드 리턴 타입으로 @ResponseBody를 기본으로 지정

2. JPA 설계 및 연관관계 설정

댓글과 관련된 기능인 WebReply클래스 추가

package org.zerock.domain;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import com.fasterxml.jackson.annotation.JsonIgnore;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@Entity
@Table(name="tbl_webreplies")
@EqualsAndHashCode(of="rno")
@ToString
public class WebReply {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long rno;
	
	private String replyText;
	
	private String replyer;
	
	@CreationTimestamp
	private Timestamp regdate;
	@UpdateTimestamp
	private Timestamp updatedate;
	
	@JsonIgnore
    	/*양방향의 경우 이러한 변환이 상호 호출되기 때문에
    	무한히 반복해서 생성하는 문제가 생길수 있어서 특정 속성은 
    	JSON으로 변환되지 않도록 @JsonIgnore 적용*/
	
    	@ManyToOne(fetch = FetchType.LAZY)
    	//WebBoard와 WebReply는 양방향 관계로 설정해서 WebReply는 다대일로 설정
	private WebBoard board;
}

- WebReply.java

package org.zerock.domain;

import java.sql.Timestamp;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@Entity
@Table(name="tbl_webboards")
@EqualsAndHashCode(of="bno")
@ToString
public class WebBoard {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long bno;
	private String title;
	
	private String writer;
	
	private String content;
	
	@CreationTimestamp
	private Timestamp regdate;
	@UpdateTimestamp
	private Timestamp updatedate;
	
	@OneToMany(mappedBy = "board", fetch = FetchType.LAZY)
	private List<WebReply> replies;
	// WebReply에는 WebBoard를 @ManyToOne 관계로 설정하고, WebBoard에는 @OneToMany 관계를 설정
	// 연관관계의 설정
}

- WebBoard.java

 

3. WebReplyRepository 설계

package org.zerock.persistence;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.zerock.domain.WebBoard;
import org.zerock.domain.WebReply;

public interface WebReplyRepository extends JpaRepository<WebReply, Long>{

	@Query("SELECT r FROM WebReply r WHERE r.board = ?1 "+" AND r.rno > 0 ORDER BY r.rno ASC")
	public List<WebReply> getRepliesOfBoard(WebBoard webBoard);
}

4. WebReplyController 설계

- 각 작업을 위한 URL 설계(REST 방식)

기능 전송 방식 URI 예
특정 게시물의 댓글 추가 POST /replies/게시물 번호
특정 게시물의 댓글 삭제 DELETE /replies/게시물 번호/댓글 번호
특정 게시물의 댓글 수정 PUT /replies/게시물 번호
특정 게시물의 댓글 댓글 GET /replies/게시물 번호
package org.zerock.controller;

import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;

import javax.annotation.PostConstruct;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.zerock.domain.WebBoard;
import org.zerock.domain.WebReply;
import org.zerock.persistence.WebReplyRepository;

@RestController
@RequestMapping("/replies/")
public class WebReplyController {

	@Autowired
	WebReplyRepository repo;
	
	@PostConstruct
	public void init() {
		Long arr[] = {304L, 303L, 300L};
		//tbl_webboards 테이블에서 304, 303, 300 번호를 입력
		
		Arrays.stream(arr).forEach(num->{
			
			WebBoard board = new WebBoard();
			board.setBno(num);
			
			IntStream.range(0, 10).forEach(i->{
				//지정된 게시물의 번호를 이용해서 댓글을 10개씩 추가
				
				WebReply reply = new WebReply();
				reply.setReplyText("REPLY ..."+i);
				reply.setReplyer("replyer"+i);
				reply.setBoard(board);
				
				repo.save(reply);
			});
		});
	}
	
    // 댓글 등록 후 목록 처리
	@Transactional //addReply는 WebRepository에 save()작업과 findBoard()를 연속 호출해서 생성
	@PostMapping("/{bno}")
	public ResponseEntity<List<WebReply>> addReply(@PathVariable("bno")Long bno, @RequestBody WebReply reply){
		
		WebBoard board = new WebBoard();
		board.setBno(bno);
		
		reply.setBoard(board);
		
		repo.save(reply);
		
		return new ResponseEntity<>(getListByBoard(board), HttpStatus.CREATED);
		//ResponseEntity는 코드를 이용해서 직접 Http Response의 상태 코드와 데이터를 직접 제어해서 처리할 수 있음.
		
	} // 댓글 추가하는 기능을 한다.
	
	private List<WebReply> getListByBoard(WebBoard board)throws RuntimeException {
		return repo.getRepliesOfBoard(board);
	}
	
    //댓글 삭제
	@Transactional
	@DeleteMapping("/{bno}/{rno}") //삭제 처리는 다음과 같이 변경
	public ResponseEntity<List<WebReply>> remove(@PathVariable("bno")Long bno, @PathVariable("rno")Long rno){
		
		repo.deleteById(rno);
		
		WebBoard board = new WebBoard();
		board.setBno(bno);
		
		return new ResponseEntity<>(getListByBoard(board), HttpStatus.OK);
	}
    
	//댓글 수정	
	@Transactional
	@PutMapping("/{bno}")
	public ResponseEntity<List<WebReply>> modify(@PathVariable("bno")Long bno, @RequestBody WebReply reply) {
		
		repo.findById(reply.getRno()).ifPresent(origin -> {
			origin.setReplyText(reply.getReplyText());
			repo.save(origin);
		});
		
		WebBoard board = new WebBoard();
		board.setBno(bno);
		
		return new ResponseEntity<List<WebReply>>(getListByBoard(board), HttpStatus.CREATED);
	}
	
}

5. REST 방식 테스트

- 크롬 앱스토어에서 Yet Another REST Client 다운로드

1) 댓글 등록 처리 호출

- http://localhost:8080/replies/303을 입력하고 옆에 POST로 바꾼후 Payload에 위의 사진에 있는댈 입력하고 상태코드가 201이 뜨면 성공한 것이다.

 

2) 댓글 등록 후 목록 처리

- 프로젝트를 실행하고 YARC를 실행해 보면 댓글이 등록되고, 댓글들의 목록이 JSON 형태의 데이터로 반환

 

3) 댓글 삭제

- YARC에서 게시물 번호 300과 댓글 번호 25를 DELETE 방식으로 호출

- 자세히 보면 rno(댓글번호) 25가 지워져 있다.

 

4) 댓글 수정

- 화면 304번 게시물의 10번 댓글의 내용을 수정

- 댓글번호 10의 내용이 수정되었다.