Ajax 댓글 처리
REST 방식을 가장 많이 사용하는 형태는 역시 브라우저나 모바일 App 등에서 Ajax를 이용해서 호출하는 것이다,
예)
Ajax의 호출을 가정하고 웹페이지에서 사용하는 댓글 기능을 작성해보도록 한다.
데이터베이스 상에서 댓글은 전형적인 1:N의 곤계로 구성한다. 하나의 게시물에 여러 개의 댓글을 추가하는 형태로 구성하고, 화면은 조회 화면상에서 별도의 화면 이동 없이 처리하기 떄문에 Ajax를 이용해서 호출한다,
프로젝트의 구성
REST 처리를 위해서는 pom.xml에서 수정된 내용이 대부분이므로 Part 3 에서 사용된 src/main/java 폴더 아래 모든 Java 코드를 그대로 복사해서 사용한다.
src/main/resources에서는 log4jdbc-log4j2를 이용하기 위해서 log4jdbc.log4j2.properties 파일을 반드시 추가해야한다. 또한 MyBatis에서 이용할 XML 파일들 역시 추가한다.
WEB과 관련된 파일들 역시 Part3 예제의 모든 내용을 그대로 복사해서 사용한다
Resources 폴더와 views 폴더 아래 모든 내용을 복사해서 프로젝트에 추가한다.
Tomcat의 실행은 /경로를 이용해서 프로젝트가 정상적으로 동작하는지 확인한다.
댓글 처리를 위한 영속 영역
댓글을 추가하기 위해서 댓글 구조에 맞는 테이브를 설계한다. 댓글 테이블은 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_seply;
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 테이블을 참조하도록 설정한다.
ReplyVO 클래스의 추가
tbl_reply 테이블을 참고해서 org.codehows.domain 패키지 아래 ReplyVO 클래스를 추가한다.
// ReplyVO.java
package org.codehows.domain;
import java.util.Date;
import lombok.Data;
@Data
public class ReplyVO {
private Long rno;
private Long bno;
private String reply;
private String replyer;
private Date replyDate;
private Date updateDate;
}
ReplyMapper 클래스와 XML 처리
org.codehows.mapper 패키지에는 ReplyMapper 인터페이스를 처리하고, XML 파일 역시 생성해 준다.
// ReplyMapper.java
package org.codehows.mapper;
public interface ReplyMapper {
}
댓글에 대한 처리 역시 화면상에서 페이지 처리가 필요할 수 있으므로 Criteria를 이용해서 처리하도록 한다.
실제 SQL은 src/main/resources 폴더 아래 ReplyMapper.xml 파일을 작성해서 처리한다.
XML 에서는 tbl_reply 테이블에 필요한 SQL을 작성한다. 페이징 처리에서는 조금 더 신경 써야하는 내용이 있으므로 우선 특정 게시물 번호(bno)에 해당하는 모든 댓글을 가져오는 형태로 작성한다.
XML을 작성할 때에는 항상 namespace에 주의해서 작성해야한다.
ReplyMapper.xml
// ReplyMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.codehows.mapper.ReplyMapper">
</mapper>
CRUD 작업을 테스트하기 전에 tbl_reply 테이블이 tbl_board 테이블과 FK(외래키)의 관계로 처리되어 있다는 점을 기억해야한다.
tbl_reply가 tbl_board 테이블의 bno 값과 정확히 일치해야 하므로 테스트를 진행하기 전에 최신 bno 번호 몇 개를 예제로 확인해 두도록 한다.
select * from tbl_board where rownum < 10 order by bno desc
ReplyMapper 테스트
우선 ReplyMapper를 사용 가능한지에 대한 테스트 작업을 진행한다. 테스트를 위해서는 ReplyMapperTests 클래스를 작성해두도록 한다.
// ReplyMapperTests.java
package org.codehows.mapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class ReplyMapperTests {
@Setter(onMethod_ = @Autowired)
private ReplyMapper mapper;
@Test
public void testMapper() {
log.info(mapper);
}
}
testMapper()를 통해서 ReplyMapper 타입의 객체가 정상적으로 사용이 가능한지 확인한다.
CRUD 작업
ReplyMapper를 이용한 CRUD 작업은 단일 테이블에 대한 작업과 유사하므로 등록, 수정, 삭제, 조회 작업을 처리한다.
등록(create)
우선은 왜래키를 사용하는 등록 작업을 먼저 진행한다.
ReplyMapper 인터페이스
// ReplyMapper.java
package org.codehows.mapper;
import org.codehows.domain.ReplyVO;
public interface ReplyMapper {
public int insert(ReplyVO vo);
}
ReplyMapper 의 SQL 을 처리하는 XML의 내용은 다음과 같다.
// ReplyMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.codehows.mapper.ReplyMapper">
<insert id="insert">
insert into tbl_reply (rno, bno, reply, replyer)
values (seq_reply.nextval, #{bno}, #{reply}, #{replyer})
</insert>
</mapper>
테스트 코드는 기존에 존재하는 게시물 일부의 bno(게시물 번호)를 사용해서 ReplyVO를 작성한다.
ReplyMapperTests 클래스
// ReplyMapperTests.java
package org.codehows.mapper;
import java.util.stream.IntStream;
import org.codehows.domain.ReplyVO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class ReplyMapperTests {
//테스트 전에 해당 번호의 게시물이 존재하는지 반드시 확인할 것
private Long[] bnoArr = { 195L, 194L, 193L, 192L, 191L };
@Setter(onMethod_ = @Autowired)
private ReplyMapper mapper;
@Test
public void testCreate() {
IntStream.rangeClosed(1, 10).forEach(i -> {
ReplyVO vo = new ReplyVO();
// 게시물의 번호
vo.setBno(bnoArr[i % 5]);
vo.setReply("댓글 테스트 " + i);
vo.setReplyer("replyer" + i);
mapper.insert(vo);
});
}
@Test
public void testMapper() {
log.info(mapper);
}
}
ReplyMapperTests 내부의 bnoArr은 게시물 번호의 일부로 실제 데이터베이스에 있는 번호여야만 한다.
테스트가 정상적으로 실행되는지 최종적으로 데이터베이스의 tbl_reply의 상태를 확인한다.
select * from tbl_reply order by rno desc;
등록 작업이 온전히 처리되는 것을 확인한 후에 조회 작업을 처리한다.
조회(read)
ReplyMapper 인터페이스와 ReplyMapper.xml에 조회 처리를 추가한다.
ReplyMapper 인터페이스
package org.codehows.mapper;
import org.codehows.domain.ReplyVO;
public interface ReplyMapper {
public int insert(ReplyVO vo);
public ReplyVO read(Long rno); // 특정 댓글 읽기
}
ReplyMapper.xml 의 일부
// ReplyMapper.xml
... 생략 ...
<select id="read" resultType="org.codehows.domain.ReplyVO">
select * from tbl_reply where rno = #{rno}
</select>
... 생략 ...
테스트 코드는 tbl_reply에 있는 번호 중에서 하나를 이용해서 확인한다.
// ReplyMapperTests.java
... 생략 ...
@Test
public void testRead() {
Long targetRno = 5L;
ReplyVO vo = mapper.read(targetRno);
log.info(vo);
}
테스트 결과로 5번 댓글이 정상적으로 조회되는지 확인한다.
삭제(delete)
특정 댓글의 삭제는 댓글의 번호(rno)만으로 처리가 가능하다.
ReplyMapper.java
// ReplyMapper.java
... 생략 ...
public int delete (Long rno);
}
ReplyMapper.xml의 일부
// ReplyMapper.xml
... 생략 ...
<delete id="delete">
delete from tbl_reply where rno = #{rno}
</delete>
</mapper>
ReplyMapperTests 클래스의 일부
// ReplyMapperTests.java
... 생략 ...
@Test
public void testDelete() {
Long targetRno = 1L;
mapper.delete(targetRno);
}
}
수정(update)
댓글의 수정은 현재 tbl_reply 테이블의 구조에서는 댓글의 내용(reply)과 최종 수정 시간(updatedate)을 수정한다.
ReplyMapper 인터페이스의 일부
// ReplyMapper.java
... 생략 ...
public int update(ReplyVO reply);
}
ReplyMapper.xml
// ReplyMapper.xml
... 생략 ...
<update id="update">
update tbl_reply set reply = #{reply}, updatedate = sysdate where rno = #{rno}
</update>
</mapper>
ReplyMapperTests 클래스
// ReplyMapperTest.java
... 생략 ...
@Test
public void testUpdate() {
Long targetRno = 10L;
ReplyVO vo = mapper.read(targetRno);
vo.setReply("Update Reply");
int count = mapper.update(vo);
log.info("UPDATE COUNT: " + count);
}
}
@Param어노테이션과 댓글 목록
댓글의 목록과 페이징 처리는 기존의 게시물 페이징 처리와 유사하지만, 추가적으로 특정한 게시물의 댓글들만 대상으로 하기 떄문에 추가로 게시물 번호가 필요하게된다.
MyBatis는 두 개 이상이 데이터를 파라미터로 전달하기 위해서
- 별도의 객체로 구성
- Map을 이용하는 방식
- @Param을 이용해서 이름을 사용하는방식, 여러 방식중에 가장 간단하게 사용할 수 있는 방식이 @Param을 이용하는 방식, @Param의 속성값은 MyBatis에서 SQL을 이용할때 #{ } 의 이름으로 사용이 가능하다
페이징 처리는 기존과 동일하게 Criteria를 이용한다. 여기에 추가적으로 해당 게시물의 번호는 파라미터를 전달하도록 ReplyMapper를 구성한다.
ReplyMapper 인터페이스
// ReplyMapper.java
package org.codehows.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.codehows.domain.Criteria;
import org.codehows.domain.ReplyVO;
public interface ReplyMapper {
... 생략 ...
public List<ReplyVO> getListWithPaging(
@Param("cri") Criteria cri,
@Param("bno") Long bno);
}
XML로 처리할 때에는 지정된 cri 와 bno 를 모두 사용할 수 있다.
댓글도 페이징 처리를 해 줄 수 있는데, 조금 뒤쪽에서 이것을 처리하고 지금은 특정 게시물의 댓글로 가져오는 것을 작성한다.
ReplyMapper.xml
// ReplyMapper.xml
... 생략 ...
<select id="getListWithPaging"
resultType="org.codehows.domain.ReplyVO">
select rno, bno, reply, replyer, replyDate, updatedate
from tbl_reply
where bno = #{bno}
order by rno asc
</select>
... 생략 ...
XML 에서 #{bno} 가 @Param("bno")와 매칭되어서 사용되는 점에 주목해야한다.
테스트 코드에서 현재 데이터베이스에 추가되어 있는 댓글들의 게시물 번호로 확인한다.
ReplyMapperTests의 일부
// ReplyMapperTests.java
... 생략 ...
@Test
public void testList() {
Criteria cri = new Criteria();
List<ReplyVO> replies = mapper.getListWithPaging(cri, bnoArr[0]);
replies.forEach(reply -> log.info(reply));
}
}
https://rogi221.tistory.com/159
'Spring' 카테고리의 다른 글
스프링 - Part 4 - REST 방식과 Ajax를 이용하는 댓글 처리 02 (Ajax 댓글 처리 - 03) (0) | 2023.03.29 |
---|---|
스프링 - Part 4 - REST 방식과 Ajax를 이용하는 댓글 처리 02 (Ajax 댓글 처리 - 02) (0) | 2023.03.29 |
스프링 - Part 4 - REST 방식과 Ajax를 이용하는 댓글 처리 01 (REST 방식으로 전환) (0) | 2023.03.28 |
스프링 - Part 3 - 기본적인 웹 게시물 관리 09 (검색처리) (0) | 2023.03.28 |
스프링 - Part 3 - 기본적인 웹 게시물 관리 08 (페이징 화면 처리) (0) | 2023.03.27 |