Seojoo21 2022. 2. 25. 17:35

* 이 카테고리의 글은 <코드로 배우는 스프링 웹 프로젝트> 에서 각 파트별로 진행하는 프로젝트의 흐름을 보기 위해 각 장의 내용을 간단히 요약한 것이다.

* 출처: 코드로 배우는 스프링 웹 프로젝트 개정판, 구멍가게 코딩단, 남가람북스 

 

Ch 17. Ajax 댓글 처리

- REST 방식을 가장 많이 사용하는 형태는 역시 브라우저나 모바일 앱등에서 Ajax를 이용해서 호출하는 것이다.

- 예제에서는 Ajax의 호출을 가정하고 웹페이지에서 사용하는 댓글 기능을 작성해 보도록 하자.

- 데이터베이스 상에서 댓글은 전형적인 1:N의 관계로 구성한다. 하나의 게시물에 여러 개의 댓글을 추가하는 형태로 구성하고, 화면은 조회 화면상에서 별도의 화면 이동 없이 처리하기 때문에 Ajax를 이용해서 호출한다.

 

* 이번 예제는 이전 PART3의 예제에 추가하는 형태로 작성한다. 프로젝트 설정은 이전 장의 내용을 참고해서 구성한다.

 

17.1 프로젝트의 구성 

- PART3의 프로젝트인 ex02를 새 프로젝트인 ex03으로 옮겨오고, pom.xml 내 라이브러리 또한 ex02에서 추가했던 것과 동일하게 ex03에서도 추가해준다. 

 

17.2 댓글 처리를 위한 영속 영역 

- 댓글을 추가하기 위해서 댓글 구조에 맞는 테이블을 Oracle SQL Developer에서 설계한다. 

- 댓글 테이블은 tbl_reply라는 이름의 테이블로 지정해서 생성한다. 

create table tbl_reply (
    rno number(10,0),
    bno number(10,0) not null,
    reply varchar2(1000) not null,
    replyer varchar2(50) not null,
    replyDate date default sysdate,
    updateDate date default sysdate
);

create sequence seq_reply;

alter table tbl_reply add constraint pk_reply primary key (rno);

alter table tbl_reply add constraint fk_reply_board
foreign key (bno) references tbl_board (bno);

- tbl_reply 테이블은 bno라는 칼럼을 이용해서 해당 댓글이 어떤 게시물의 댓글인지를 명시하도록 한다. 

- 댓글 자체는 단독으로 CRUD가 가능하므로 별도의 PK를 부여하고 외래키(FK) 설정을 통해 tbl_board 테이블을 참조하도록 설정한다.

 

17.2.1 ReplyVO 클래스의 추가 

- tbl_reply 테이블을 참고해서 org.zerock.domain 패키지 아래 ReplyVO 클래스를 추가한다.

 

17.2.2 ReplyMapper 클래스와 XML 처리 

- org.zerock.mapper 패키지에는 ReplyMapper 인터페이스를 처리하고, XML 파일 역시 생성해준다.

- 댓글에 대한 처리 역시 화면상에서 페이지 처리가 필요할 수 있으므로 Criteria를 이용해서 처리하도록 한다. 

 

- 실제 SQL은 'src/main/resources' 폴더 아래 ReplyMapper.xml 파일을 작성해서 처리한다.

- XML에서는 tbl_reply 테이블에 필요한 SQL을 작성한다. 페이징 처리에서는 조금 더 신경 써야 하는 내용이 있으므로 우선은 특정 게시물 번호(bno)에 해당하는 모든 댓글을 가져오는 형태로 작성한다. 

- XML을 작성할 때는 항상 namespace에 주의해서 작성한다. 

 

- CRUD 작업을 테스트하기 전에 tbl_reply 테이블이 tbl_board 테이블과 FK(외래키)의 관계로 처리되어 있다는 점을 기억해야만 한다. 

- tbl_reply가 tbl_board 테이블의 bno 값과 정확히 일치해야 하므로 테스트를 진행하기 전에 최신 bno 번호 몇 개를 예제로 확인해 두도록 한다. 

 

1) ReplyMapper 테스트 

- 개발 초기에는 테스트 코드를 작성하는 것이 번거롭게 느껴지지만 가능하면 팀 전체가 테스트 코드를 습관적으로 작성하는 노력을 해야한다.

- 우선 ReplyMapper를 사용 가능한지에 대한 테스트 작업을 진행한다. 테스트를 위해서는 ReplyMapperTests 클래스를 작성해두도록 한다.  

- ReplyMappterTests 클래스에 만든 testMapper()를 통해서 ReplyMapper 타입의 객체가 정상적으로 사용이 가능한지 확인한다. 

 

17.2.3 CRUD 작업

- ReplyMapper를 이용한 CRUD 작업은 단일 테이블에 대한 작업과 유사하므로 등록, 수정, 삭제, 조회 작업을 처리한다. 

 

1) 등록(create) 

- 우선은 외래키를 사용하는 등록 작업을 먼저 진행해본다. 

- ReplyMapper 인터페이스에 외래키 사용 등록을 위한 추상 메서드를 추가하고, ReplyMapper의 SQL을 처리하는 SQL 코드를 ReplyMapper.xml에 추가한다.

- 그리고 테스트 코드를 ReplyMapperTests 클래스에 기존에 존재하는 게시물 일부의 bno(게시물 번호)를 사용해서 ReplyVO를 작성한다.  

- 테스트가 정상적으로 실행되는지 스프링에서 1차로 확인하고, 최종적으로 SQL Developer에서 데이터베이스의 tbl_reply의 상태를 확인한다.  

 

2) 조회(read)

- ReplyMapper 인터페이스와 ReplyMapper.xml에 조회 처리를 추가한다. 

- ReplyMapperTests 클래스에서 테스트 코드는 tbl_reply에 있는 번호 중에서 하나를 이용해서 확인한다.

 

3) 삭제(delete)

- 특정 댓글의 삭제는 댓글의 번호(bno)만으로 처리가 가능하다.

 

4) 수정(update)

- 댓글의 수정은 현재의 tbl_reply 테이블의 구조에서는 댓글의 내용(reply)과 최종 수정 시간(updatedate)을 수정한다. 

- ReplyMapper 인터페이스, ReplyMapper.xml 에 댓글 수정 처리를 위한 코드를 추가하고, ReplyMapperTests 클래스에 테스트용 코드를 추가하여 테스트를 실행한다. 

 

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

- 댓글의 목록과 페이징 처리는 기존의 게시물 페이징 처리와 유사하지만, 추가적으로 특정한 게시물의 댓글들만을 대상으로 하기 때문에 추가로 게시물의 번호가 필요하게 된다.

- MyBatis는 두 개 이상의 데이터를 파라미터로 전달하기 위해 아래와 같은 방식을 사용한다. 

1) 별도의 객체를 구성하는 방식 

2) Map을 이용하는 방식

3) @Param을 이용해서 이름을 사용하는 방식 

- 이 방식들 중 가장 간단하게 사용할 수 있는 방식이 @Param을 이용하는 방식이다. @Param의 속성값은 MyBatis에서 SQL을 이용할 때 '#{ }' 의 이름으로 사용이 가능하다.

 

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

- ReplyMapper.xml 에서 #{bno}가 ReplyMapper 인터페이스의 @Param("bno")와 매칭되어서 사용되는 점에 주목해야 한다. 

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

 

17.3 서비스 영역과 Controller 처리

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

 

17.3.1 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에서 표현하는 방식을 사용한다. 

 

- ReplyController는 ReplyService 타입의 객체인 ReplyServiceImpl 객체를 주입받도록 설계한다. 

 

17.3.2 등록 작업과 테스트 

- REST 방식으로 처리할 때 주의해야 하는 점은 브라우저나 외부에서 서버를 호출할 때 데이터의 포맷과 서버에서 보내주는 데이터의 타입을 명확히 설계해야 하는 것이다. 

- 예를 들어 댓글 등록의 경우 브라우저에서는 JSON 타입으로 된 댓글 데이터를 전송하고, 서버에서는 댓글의 처리 결과가 정상적으로 되었는지 문자열로 결과를 알려 주도록 한다. 

 

- ReplyController에 댓글 등록 작업을 위해 create() 메서드를 생성한다.

* 코드와 내용 설명 주석은 깃헙 참고   

- 테스트 시에는 크롬 브라우저에 설치해놓은 Talend API Tester를 이용해 POST 방식으로 진행한다.

* Content-Type은 application/json

* 실제 전송되는 데이터는 존재하는 게시물 번호(bno)와 댓글 내용(reply), 댓글 작성자(replyer)를 JSON 문법에 맞게 작성하도록 주의

{"bno":507915, "reply":"댓글 등록 테스트", "replyer":"user00"}

* 게시물의 번호는 기존에 존재하는 번호여야 하므로 주의가 필요

- 작성된 댓글이 데이터베이스에 정상적으로 추가 되었는지 SQL Devolper를 통해 확인한다. 

 

테스트 결과) 

 

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

- ReplyController 클래스 안에 특정 게시물의 댓글 목록을 확인하는 작업을 위한 코드를 작성한다. 

* 코드와 내용 설명 주석은 깃헙 참고 

- 브라우저에서 아래와 같이 테스트를 해본다. 테스트 결과는 xml 타입으로 댓글 데이터들이 나온다. 만일 JSON 타입을 원한다면 마지막에 '.json' 을 추가한다. 

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

 

17.3.4 댓글 삭제/조회

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

 

17.3.5 댓글 수정 

- 댓글 수정은 JSON 형태로 전달되는 데이터와 파라미터로 전달되는 댓글번호(rno)를 처리한다.

- 따라서 JSON 데이터를 특정 타입으로 변환해주는 @RequestBody와 URL에 있는 댓글번호(rno)를 파라미터로 처리해주는 @PathVariable를 모두 사용해서 코드를 작성한다.

- 댓글 수정은 'PUT'방식이나 'PATCH'방식을 이용하도록 처리하기 위해 아래와 같이 @RequestMapping 내 method 방식을 지정해준다. 

  @RequestMapping(method = {RequestMethod.PUT, RequestMethod.PATCH}, 
		  value = "/{rno}",
		  consumes = "application/json",
		  produces = {MediaType.TEXT_PLAIN_VALUE})

- 그리고 @RequestBody로 처리되는 데이터는 일반 파라미터나 @PathVariable 파라미터를 처리할 수 없기 때문에 직접 처리해 주는 부분을 주의해야 한다. 

테스트 예)