본문 바로가기
Spring공부/1-REST

REST방식(7)-Ajax댓글처리

by 으노으뇨 2021. 10. 7.
728x90
반응형
SMALL

@Param어노테이션과 댓글 목록

댓글도 일반 게시판 조회할때 처럼 페이징기능이 있는것을 볼 수가 있다. 기존의 게시물 페이징 처리와 유사하지만, 추가적으로 특정한 게시물의 댓글들만을 대상으로 하기 때문에 추가로 게시물의 번호가 필요하게 된다. 

즉, 게시글고유번호를 통해 댓글들을 쭈욱 불러오고 그걸 페이징한다는 개념!

MyBatis는 두개 이상이 데이터를 파라미터로 전달하기 위해서

1. 별도의 객체로 구성

2. Map을 이용하는 방식

3. @Param을 이용해서 이름을 사용하는 방식이 있다.

여러 방식중 간단하게 사용할 수 있는 방식이 @Param을 이용하는 방식이다. 

페이징 처리를 하기 위해서 Criteria를 이용한다. 여기에 추가적으로 해당 게시물의 번호는 파라미터를 전달하도록 ReplyMapper를 구성한다.

package org.study.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Param;
import org.study.domain.Criteria;
import org.study.domain.ReplyVO;

public interface ReplyMapper {

	public int insert(ReplyVO vo);

	public ReplyVO read(Long bno);

	public int delete(Long bno);

	public int update(ReplyVO reply);
    
    //이후 추가된

	public List<ReplyVO> getListWithPaging(@Param("cri") Criteria cri, @Param("bno") Long bno);

	public int getCountByBno(Long bno);
}

XML로 처리할 때에는 지정된 cri와 bno를 모두 사용할 수 있다.

<select id="getListWithPaging"
		resultType="orgs.study.domain.ReplyVO">
		select rno, bno, reply, replyer, replyDate, updatedate from
		tbl_reply where
		bno = #{bno} order by rno asc
	</select>

그런데 여기서 XML #{bno}가 @Param("bno")와 매칭되어서 사용되는 점에 주목해야 합니다. 

테스트 코드에서는 현재 데이터베이스에 추가되어 있는 댓글들의 게시물 번호로 확인합니다.

ReplyMappingTests일부

...
@Test
	public void testList() {

		Criteria cri = new Criteria();

		// 4L
		List<ReplyVO> replies = mapper.getListWithPaging(cri, bnoArr[0]);

		replies.forEach(reply -> log.info(reply));

	}


서비스 영역과 Controller 처리

서비스 영역과 Controller의 처리는 기존의 BoardService와 동일하게 ReplyService인터페이스ReplyServiceImlp클래스를 작성합니다.

ReplyService인터페이스의 내용

package org.study.service;

import java.util.List;

import org.study.domain.Criteria;
import org.study.domain.ReplyVO;

public interface ReplyService {

	public int register(ReplyVO vo);

	public ReplyVO get(Long rno);

	public int modify(ReplyVO vo);

	public int remove(Long rno);

	public List<ReplyVO> getList(Criteria cri, Long bno);

}

ReplyServiceImpl 클래스에도 내용을 작성한다.

package org.study.service;

import java.util.List;

import org.springframework.stereotype.Service;
import org.study.domain.Criteria;
import org.study.domain.ReplyVO;
import org.study.mapper.ReplyMapper;

import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;

@Service
@Log4j
@AllArgsConstructor
public class ReplyServiceImpl implements ReplyService {

	private ReplyMapper mapper;

	@Override
	public int register(ReplyVO vo) {
		log.info("register......" + vo);
		return mapper.insert(vo);
	}

	@Override
	public ReplyVO get(Long rno) {
		log.info("get......" + rno);
		return mapper.read(rno);
	}

	@Override
	public int modify(ReplyVO vo) {
		log.info("modify......" + vo);
		return mapper.update(vo);
	}

	@Override
	public int remove(Long rno) {
		log.info("remove...." + rno);
		return mapper.delete(rno);
	}

	@Override
	public List<ReplyVO> getList(Criteria cri, Long bno) {
		log.info("get Reply List of a Board " + bno);
		return mapper.getListWithPaging(cri, bno);
	}
}

ReplyController의 설계

ReplyController는 앞으로 예제에서 SampleController와 유사하게 @RestController 어노테이션을 이용해서 설계하며 다음과 같으 URL을 기준으로 동작할 수 있게 작성한다.

작업 URL HTTP전송방식
등록 /replies/new POST
조회 /replies/:rno GET
삭제 /replies/:rno DELETE
수정 /replies/:rno PUT or PATCH
페이지 /replies/pages/:bno/:page GET

Rest방식으로 동작하는 URL을 설계할 때는 PK를 기준으로 작성하는 좋다. -댓글에서는 댓글번호겠지?

PK만으로 조회, 수정, 삭제가 가능하기 때문이다. 그러나 댓글 목록은 PK를 사용할 수 없기 때문에 파라미터로 필요한 게시물의 번호(BNO)와 페이지 번호(page)정보들을 URL에서 표현하는 방식을 사용한다.

package org.study.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.study.service.ReplyService;

import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;

@RequestMapping("/replies/")
@RestController
@Log4j
@AllArgsConstructor
public class ReplyController {
	private ReplyService service;
}

@Setter주입을 이용하거나 위의 코드같이 @ALLArgsConstructor를 이용해서 ReplyService타입의 객체를 필요로 하는 생성자를 만들어서 사용한다.


등록 작업과 테스트

Rest방식으로 처리할 때 주의해야 하는 점은 브라우저나 외부에서 서버를 호출할 때

데이터의 포맷과 서버에서 보내주는 데이터의 타입을 명확히 설계해야한다.

예를 들어 댓글 등록의 경우 브라우저에서는 JSON타입으로 된 댓글 데이터를 전송하고,

서버에서는 댓글의 처리 결과가 정상적으로 되었는지 문자열로 결과를 알려주도록 한다.

	@PostMapping(value = "/new", consumes = "application/json", produces = { MediaType.TEXT_PLAIN_VALUE })
	public ResponseEntity<String> create(@RequestBody ReplyVO vo) {

		log.info("ReplyVO: " + vo);

		int insertCount = service.register(vo);

		log.info("Reply INSERT COUNT: " + insertCount);

		return insertCount == 1 ? new ResponseEntity<>("success", HttpStatus.OK)
				: new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
	}

CREATE()는 @PostMapping으로 POST방식으로만 동작하도록 설계하고, consumes와 produces를 이용해서 JSON 방식의 데이터만 처리한다.

문자열을 반환하도록 설계하자, create() 의 파라미터는 @RequestBody를 적용해서 JSON 데이터를 ReplyVO 타입으로 변환하도록 지정한다.

create() 내부적으로 ReplyServiceImpl을 호출해서 register()를 호출하고, 댓글이 추가된 숫자를 확인해서 브라우저에서 '200OK' 혹은 500errror 를 반환하도록 한다. 

테스트를 위해 톰캣 서버 프로젝트 경로를 / 로 지정하고 크롬확장 프로그램들을 이용해 테스트를 진행하자.


테스트

테스트를 시작전에 POST방식으로 값을 넘겨주어야한다. 그런데 get처럼 대놓고 URL을 만져서 값을 얻을 수 있는것과는 다르게 웹상에서 테스트를 진행하기는 어렵다.

따라서 크롬에서 확장프로그램을 설치해서 진행하도록하자

구글 -> 크롬확장프로그램 설치 검색 -> 크롬웹스토어 -> REST CLIENT 검색 -> 해당 프로그램 설치

테스트 시에는 포스트 방식으로 전송하고 Countent-Type은 application/json으로 지정해야한다. 

실제 전송되는 데이터는 존재하는 게시물 번호와 댓글 내용, 작성자를 json문법에 맞게 작성해야한다.

이때 게시물번호는 이미 존재하는 번호로 테스트를 진행하자

보는바와 같이 설정한다.

RequestURL 에는

http://localhost:8080/replies/new 

headers에는 

Content-Type ,  apllicatgion/json 

Body에는

{"bno":8,"reply":"hello Reply","replyer":"uno"}

를 작성하고 테스트를 실시한다.

이클립스 콘솔창의 일부분은 이렇게 나타나고 전체적인 내용은

INFO : org.study.controller.ReplyController - ReplyVO: ReplyVO(rno=null, bno=8, reply=hello Reply, replyer=uno, replyDate=null, updateDate=null)
INFO : org.study.controller.ReplyController - Reply INSERT COUNT: 1

으로 나타난다. 즉 8번게시물에 "hello Reply" 라는 글과, 작성자는 uno 라는 글이 올라왔다는 것을 알 수 있고 오라클에서도 확인해보자

맨하단에 입력한 모든정보가 정상적으로 입력되었음을 알 수 있다.


특정 게시물의 댓글 목록 확인

내가 만약 글을 누르면 해당 글에대한 댓글들도 주룩 떠야한다. 따라서 이번엔

특정 게시물의 댓글 목록을 확인하는 작업을 해보자

ReplyController에 이 내용을 추가한다.

	@GetMapping(value = "/pages/{bno}/{page}", produces = { MediaType.APPLICATION_XML_VALUE,
			MediaType.APPLICATION_JSON_UTF8_VALUE })
	public ResponseEntity<List<ReplyVO>> getList(@PathVariable("page") int page, @PathVariable("bno") Long bno) {

		Criteria cri = new Criteria(page, 10);

		log.info("get Reply List bno: " + bno);

		log.info("cri:" + cri);

		return new ResponseEntity<>(service.getList(cri, bno), HttpStatus.OK);
	}

ReplyController의 getList()는 Criteria를 이용해서 파라미터를 수집한다. '{bno}/{page}의 page값은 Criteria를 생성해서 직접 처리해야한다. 게시물의 번호는  @PathVariable를 이용해서 파라미터로 처리하고 브라우저에서 아래와 같이 테스트 한다.

8번 게시물을 본다는 예시

http://localhost:8080/replies/pages/8/1

테스트결과로 XML타입으로 댓글 데이터들이 나오는 것을 볼 수 있다. 만일 JSON타입을 원하면 동일하게 마지막에 .json을 붙이면 된다. http://localhost:8080/replies/pages/8/1.json


댓글 삭제/수정

지금까지 등록하고, 조회하는 기능만했다. 이제 삭제하고 수정하는 기능도 구현 해보자

삭제

RestController의 댓글의 수정/삭제/조회는 위와 유사한 방식으로 JSON이나 문자열을 반환하도록 설계한다.

@DeleteMapping(value = "/{rno}", produces = { MediaType.TEXT_PLAIN_VALUE })
public ResponseEntity<String> remove(@PathVariable("rno") Long rno) {

log.info("remove: " + rno);

return service.remove(rno) == 1 
? new ResponseEntity<>("success", HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);

}

유의할 점은 @Delete ... 애너테이션을 사용했다. 잠시 이 애너테이션의 원문을 보자면 겟매핑, 포스트매핑과 큰차이가없이 @RequestMapping(method = RequestMethod. <- 부분만 다를뿐이다. 지금은 삭제기능을 나타내주는 애너테이션이라고만 생각하자

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.DELETE)
public @interface DeleteMapping {

	/**

삭제도 삭제할 rno 넘버(댓글 고유남바)를 받아서 삭제하는 기능을 수행하도록 했다.

수정

Json형태로 전달되는 데이터와 파라미터로 전달되는 댓글 번호를 처리하기 때문에 아래처럼 처리한다.

	@RequestMapping(method = { RequestMethod.PUT,
			RequestMethod.PATCH }, value = "/{rno}", consumes = "application/json", produces = {
					MediaType.TEXT_PLAIN_VALUE })
	public ResponseEntity<String> modify(@RequestBody ReplyVO vo, @PathVariable("rno") Long rno) {

		vo.setRno(rno);

		log.info("rno: " + rno);
		log.info("modify: " + vo);

		return service.modify(vo) == 1 ? new ResponseEntity<>("success", HttpStatus.OK)
				: new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);

	}

댓글 수정은 PUT 방식이나 PATCH방식을 이용하도록 처리하고, 실제 수정되는 데이터는 JSON 포맷이므로 @RequestBody를 이용해서 처리합니다. @RequestBody로 처리되는 데이터는 일반 파라미터나 @PathVariable 파라미터를 처리할 수 없기 때문에, 직접 처리해 주는 부분을 주의해야한다.

--구동화면

메서드는 POST가아닌 !! PUT으로 ! 그리고 URL 에 아까 내가 생성했던 댓글인 댓글번호 13번을 지칭하는 13을 써준다.

그리고 그 댓글의 글번호도 같이 남겨서 올려준다.

http://localhost:8080/replies/13

{"bno":8,"reply":"댓글을 수정합니다."}

이렇게 입력해서 결과값이 나왔다 결과값을 한번보자

이클립스(STS)에 정상적으로 나왔다. rno라는 13 댓글넘버가 수정이 되었다고 로그가 정상적으로 찍힘으로 알 수 있다.

INFO : org.study.controller.ReplyController - rno: 13
INFO : org.study.controller.ReplyController - modify: ReplyVO(rno=13, bno=8, reply=댓글을 수정합니다., replyer=null, replyDate=null, updateDate=null)

위는 로그창의 전체내용이다. 

오라클도 정상적으로 변경되었음을 볼 수 있다.

그런데 이상하게 replyer 즉 작성자, 수정자를 입력하지않았다. 그런데도 수정되는데 이상이없었다. 

그 이유는 쿼리문과 받아내는 파라미터값에 언급조차 안했기에 빼고 넣더라도 문제가없이 처리가 됨을 알 수 가 있다.

728x90
반응형
LIST

'Spring공부 > 1-REST' 카테고리의 다른 글

REST방식(9)-이벤트처리  (0) 2021.10.08
REST방식(8)-Ajax  (0) 2021.10.08
REST방식(6)-Ajax댓글처리  (2) 2021.10.07
REST방식(5)  (0) 2021.10.07
REST방식(4)  (0) 2021.10.06

댓글