organize/프로젝트

justBoard13 댓글 구현(3) 데이터

001cloudid 2024. 10. 13. 11:57
728x90

화면까지 구성했고 어떤 식으로 주소매핑을 해야 할지 정했다.

이번에는 주소매핑에서 mapper까지 설정해 보도록 하겠다.

 

이전글에서 Update로 되어있는 부분은 Insert로 수정하였다.

 

댓글을 작성하고 작성 버튼을 누르면 /board/replyInsertPro 여기서 댓글을 작성하는 처리과정을 거치게 된다. 이 과정부터 시작해 보도록 하자

 

BoardController

	@PostMapping("/replyInsertPro")
	public String replyInsertPro(ReplyDTO replyDTO, Model model) {
		System.out.println("BoardController replyInsertPro()");
		BoardDTO boardDTO = new BoardDTO();
		boardService.replyUpdatePro(replyDTO);
		return "redirect:/board/content?no="+boardDTO.getNo();
	}

 

작성하고 나니 굳이 BoardDTO 객체를 안에서 작성할 필요는 없을 것 같다는 생각이 들었다.

	@PostMapping("/replyInsertPro")
	public String replyInsertPro(ReplyDTO replyDTO,BoardDTO boardDTO, Model model) {
		System.out.println("BoardController replyInsert()");
		boardService.replyInsert(replyDTO);
		return "redirect:/board/content?no="+boardDTO.getNo();
	}

 

Controller에서 return 값을 저렇게 써본적은 처음인데 잘 될 거라고 생각한다.

 

BoardService

	public void replyInsert(ReplyDTO replyDTO) {
		System.out.println("BoardService replyInsert()");
		replyDTO.setNum(boardDAO.getMaxNum()+1); //댓글 번호(PK) 값 설정
		replyDTO.setReplyWriteTime(new Timestamp(System.currentTimeMillis())); //댓글 작성 시간
		boardDAO.replyInsert(replyDTO);
	}

 댓글 번호는 PK라 어디 보이지 않아도 반드시 필요한 값, 댓글 작성 시간도 따로 값을 지정해주지 않았기 때문에 Service에서 설정이 필요함. 비밀글 처리를 jsp에서 해줘야 할지 Service에서 해야 할지 잘 모르겠다. 

 

BoardDAO

	public void replyInsert(ReplyDTO replyDTO) {
		System.out.println("BoardDAO replyInsert()");
		sqlSession.insert(namespace+".replyInsert", replyDTO);
	}
	
	public int getMaxNum() {
		System.out.println("BoardDAO getMaxNum()");
		return sqlSession.selectOne(namespace+".getMaxNum");
	}

 

boardMapper

	<insert id="replyInsert">
		insert into reply(num,id,replyContent,replyHidden,replyWriteTime,re_ref,re_lev,re_sql)
		values(#{num},#{id},#{replyContent},#{replyHidden},#{replyWriteTime},#{re_ref},#{re_lev},#{re_sql})
	</insert>
	
	<select id="getMaxNum" resultType="java.lang.Integer">
		select max(num) from reply
	</select>

 

뭔가 실행하면 오류가 날 것 같다만 일단 실행해보도록 해야겠다.

역시 오류가 뜬다. 어디의 값이 문제인지 toString을 오버라이딩해서 알아보도록 하자

ReplyDTO

	@Override
	public String toString() {
		return "ReplyDTO [id=" + id + ", replyContent=" + replyContent + ", replyHidden=" + replyHidden
				+ ", replyWriteTime=" + replyWriteTime + ", re_ref=" + re_ref + ", re_lev=" + re_lev + ", re_seq="
				+ re_seq + ", num=" + num + "]";
	}

 

BoardController

	@PostMapping("/replyInsertPro")
	public String replyInsertPro(ReplyDTO replyDTO,BoardDTO boardDTO, Model model) {
		System.out.println("BoardController replyInsert()");
		boardService.replyInsert(replyDTO);
		replyDTO.toString();
		return "redirect:/board/content?no="+boardDTO.getNo();
	}

 

그럼에도 계속해서 똑같은 오류가 난다.

 

content.jsp에서 댓글form에 num을 hidden으로 넣어보았다. 아마 num값이 넘어오지 않아서 그런가 싶다는 생각이 들었다.

<hr>
댓글<br>
<form action="${pageContext.request.contextPath}/board/replyInsertPro" method="post">
<input type="hidden" name="num">
<input type="text" name="id" value="${sessionScope.id }" readonly="readonly"><br>
<textarea rows="5" cols="30" style="width: 792px; height: 60px" placeholder="내용을 작성해주세요." name="replyContent"></textarea><br>
<span style="float: right;"><input type="checkbox" name="replyHidden">비밀글 <input type="submit" value="작성"></span>
</form>
댓글 목록<br>
이곳에 댓글 목록이 나타남. 작성자 - 댓글 내용 순으로
</div> <%-- content영역 div --%>

이러한 문제를 직면했다. 전에 프로젝트할 때 이러한 상황을 경험했다. 타입이 맞지 않아서 생긴 걸로 기억하는데 자세히 알아보기 위해 검색해 보았다.

 

HTTP 에러 코드

  • 400 : 잘못된 요청으로 문법상 오류가 있어서 서버가 요청을 이해하지 못한 경우 → 잘못입력한 url인 경우가 많음
  • 404 : 클라이언트가 요청한 문서를 찾지 못한 경우 → url이나 캐시 삭제
  • 405 : request라인에 명시된 메소드를 수행하기 위한 해당 자원의 이용이 허용되지 않았을 경우
  • 415 : 지원되지 않는 형식으로 클라이언트가 요청해서 서버가 요청에 대한 승인을 거부한 경우
  • 500 : 웹 서버가 요청사항을 수행할 수 없는 경우

참고 : https://donggu1105.tistory.com/145

 

댓글을 작성했을 때 콘솔창은

replyInsertPro()까지 가지 않을 것을 확인할 수 있다.

 

이전 글을 확인해 보니 빠진 부분이 있었다. content.jsp 주소매핑과정에서 ReplyDTO도 추가해줘야 한다는 것이다.

	@GetMapping("/content")
	public String content(BoardDTO boardDTO, ReplyDTO replyDTO, Model model) {
		System.out.println("BoardController Content()");
		boardDTO = boardService.getBoard(boardDTO); //글에 대한 정보
		boardService.readCount(boardDTO); //조회수
		model.addAttribute("boardDTO",boardDTO);
		model.addAttribute("replyDTO",replyDTO);
		return "board/content";
	}

 

이러거나 저러거나 400 오류가 발생함 해당 오류가 발생할 수 있는 이유에 대해서 조금 더 구체적으로 알아보았다.

400 오류가 발생하는 이유

  1. 필수 입력 누락
  2. 잘못된 URL 요청
  3. HTTP 메소드 문제
  4. 서버 세션 유효성 문제
  5. 폼 데이터 불일치
  6. 잘못된 데이터 타입
  7. Ajax 요청 시 처리 문제
  8. CORS 정책 문제

이걸 찾아보니 아무래도 비밀글 체크박스와 관련되어 문제가 생긴 것 같다. true 또는 false가 넘어가야 하고, db에는 0 또는 1로 받아져야 하기 때문이다.

 

이를 체크박스로 해결하려고 하니 어떻게 해야 할지 좀 막막했다. 그래서 알아보니

content.jsp에

<input type="checkbox" name="replyHidden" value="true">비밀글
<input type="hidden" name="replyHidden" value="false">

이렇게 하면 체크박스가 체크되었을 때는 true가 전송되고, 체크되지 않았을 때 false가 전송되게 할 수 있다.

 

BoardController

	@PostMapping("/replyInsertPro")
	public String replyInsertPro(ReplyDTO replyDTO,BoardDTO boardDTO, Model model) {
		System.out.println("BoardController replyInsertPro()");
		
		//replyHidden을 boolean으로 변환
		if("true".equals(replyDTO.isReplyHidden())) {
			replyDTO.setReplyHidden(true);
		} else {
			replyDTO.setReplyHidden(false);
		}
		
		boardService.replyInsert(replyDTO);
		return "board/content?no="+boardDTO.getNo();
	}

replyHidden값을 처리...

이렇게 했음에도 여전히 문제가 생긴다.

 

원인을 찾지 못했다. 원인이 될 부분만 찾아보았다.

  1. 폼 데이터 문제
    → num이 비어있으므로 번호를 제대로 설정
  2. DTO 데이터 처리
    → isReplyHidden() 메소드는 boolean값을 반환해야 함
  3. 리다이렉션 return
    → "board/content?no=" 는 단순히 URL을 반환
  4. DAO 및 매퍼 설정

content.jsp

<hr>
댓글<br>
<form action="${pageContext.request.contextPath}/board/replyInsertPro" method="post">
<input type="hidden" name="num" value="${replyDTO.num }">
<input type="text" name="id" value="${sessionScope.id }" readonly="readonly"><br>
<textarea rows="5" cols="30" style="width: 792px; height: 60px" placeholder="내용을 작성해주세요." name="replyContent"></textarea><br>
<span style="float: right;"><input type="checkbox" name="replyHidden" value="true">비밀글 <input type="hidden" name="replyHidden" value="false"><input type="submit" value="작성"></span>
</form>
댓글 목록<br>
이곳에 댓글 목록이 나타남. 작성자 - 댓글 내용 순으로
</div> <%-- content영역 div --%>
<jsp:include page="../inc/footer.jsp" />
</div> <%-- container 영역 div --%>
</body>
</html>

 

BoardController

	@PostMapping("/replyInsertPro")
	public String replyInsertPro(ReplyDTO replyDTO,BoardDTO boardDTO, Model model) {
		System.out.println("BoardController replyInsertPro()");
		
		//replyHidden을 boolean으로 변환
		if("true".equals(replyDTO.getReplyHidden())) {
			replyDTO.setReplyHidden(true);
		} else {
			replyDTO.setReplyHidden(false);
		}
		
		boardService.replyInsert(replyDTO);
		return "redirect:/board/content?no="+boardDTO.getNo();
	}

 

BoardService, BoardDAO, boardMapper는 그대로 놔둠.

 

댓글 작성하니 이번엔 

 

문법오류인가? 일단 검색해 보니 Project - Clean 했을 경우 문제가 해결된다고 한다.

혹시 몰라서 Tomcat까지 Clean을 하고 restart 하고 댓글 작성하니

다시 NullPointerException 발생. 그래도 희망적인 사실은 400 오류 떴을 때보다 콘솔에서 더 많이 진행됨을 확인할 수 있었다.

num값이 넘어오지 못하는 것 같다. 가장 쉬운 방법은 db에서 auto_increment 속성을 추가해 주면 될 것 같은데.. 그렇다면 auto_increment 속성을 추가해 보자. 어차피 reply 테이블의 num 컬럼은 테이블의 기본키를 위한 의미 없는 컬럼이기 때문에 바꿔도 상관없을 것 같다.

추가적으로도 NullPointerException이 발생하였다. 이 과정에서 auto_increment 속성을 걸어뒀는데 getMaxNum()이 필요할까? content.jsp에 num값을 넘겨줘야 할까? BoardController에 replyHidden을 또 boolean으로 변환하는 것이 필요할까?

즉 아래와 같이 변경했다.

content.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>그냥 게시판</title>
</head>
<style>
.container{
	align-content : center;
	width: 1200px;
	height: 600px;
}
.content{
	width: 1000px;
	height: 600px;
}
</style>
<body>
<div class="container"> <%-- container 영역 div --%>

<jsp:include page="../inc/top.jsp" />

<jsp:include page="../inc/sidebar.jsp" />

<div class="content"> <%-- content영역 div --%>
<h1>글 읽기</h1>
<table >
<tr><td>글번호</td><td>${boardDTO.no}</td></tr>
<tr><td>글쓴이</td><td>${boardDTO.id}</td></tr>
<tr><td>조회수</td><td>${boardDTO.readcount}</td></tr>
<tr><td>작성일</td><td>${boardDTO.writetime}</td></tr>
<tr><td>글제목</td><td>${boardDTO.subject}</td></tr>
<tr><td>글내용</td><td>${boardDTO.content}</td></tr>
</table>
<div style="display: center">
<c:if test="${!empty sessionScope.id }">
	<c:if test="${sessionScope.id eq boardDTO.id}">
<a href="${pageContext.request.contextPath}/board/update?no=${boardDTO.no}"><input type="button" value="글 수정"></a> <a href="${pageContext.request.contextPath}/board/delete?no=${boardDTO.no}"><input type="button" value="글 삭제"></a>
	</c:if>
</c:if>
<a href="${pageContext.request.contextPath}/board/list"><input type="button" value="글 목록"></a>
</div>
<hr>
댓글<br>
<form action="${pageContext.request.contextPath}/board/replyInsertPro" method="post">
<input type="text" name="id" value="${sessionScope.id }" readonly="readonly"><br>
<textarea rows="5" cols="30" style="width: 792px; height: 60px" placeholder="내용을 작성해주세요." name="replyContent"></textarea><br>
<span style="float: right;"><input type="checkbox" name="replyHidden" value="true">비밀글 <input type="hidden" name="replyHidden" value="false"><input type="submit" value="작성"></span>
</form>
댓글 목록<br>
이곳에 댓글 목록이 나타남. 작성자 - 댓글 내용 순으로
</div> <%-- content영역 div --%>
<jsp:include page="../inc/footer.jsp" />
</div> <%-- container 영역 div --%>
</body>
</html>

 

BoardController

	@PostMapping("/replyInsertPro")
	public String replyInsertPro(ReplyDTO replyDTO,BoardDTO boardDTO, Model model) {
		System.out.println("BoardController replyInsertPro()");
		//replyHidden을 boolean으로 변환
//		if("true".equals(replyDTO.getReplyHidden())) {
//			replyDTO.setReplyHidden(true);
//		} else {
//			replyDTO.setReplyHidden(false);
//		}
		boardService.replyInsert(replyDTO);
		return "redirect:/board/content?no="+boardDTO.getNo();
	}

 

BoardSerivce

	public void replyInsert(ReplyDTO replyDTO) {
		System.out.println("BoardService replyInsert()");
//		replyDTO.setNum(boardDAO.getMaxNum()+1); //댓글 번호(PK) 값 설정
		replyDTO.setReplyWriteTime(new Timestamp(System.currentTimeMillis())); //댓글 작성 시간
		boardDAO.replyInsert(replyDTO);
	}

 

BoardDAO

	public void replyInsert(ReplyDTO replyDTO) {
		System.out.println("BoardDAO replyInsert()");
		sqlSession.insert(namespace+".replyInsert", replyDTO);
	}
	
//	public int getMaxNum() {
//		System.out.println("BoardDAO getMaxNum()");
//		return sqlSession.selectOne(namespace+".getMaxNum");
//	}

 

boardMapper

	<insert id="replyInsert">
		insert into reply(num,id,replyContent,replyHidden,replyWriteTime)
		values(#{num},#{id},#{replyContent},#{replyHidden},#{replyWriteTime})
	</insert>
	
<!-- 	<select id="getMaxNum" resultType="java.lang.Integer"> -->
<!-- 		select max(num) from reply -->
<!-- 	</select> -->

 

이렇게 하고 댓글을 작성했다.

여전히 오류가 발생하지만 발생하는 이유는 주소줄에서 no=0이라 그런 것 같다. 기존의 no값을 넘겨갈 줄 알았는데 아닌 것 같다. 그래도 값은 들어간 것 같아서 확인해 봤다.

content.jsp에 jstl을 이용해서 나타내기를 하면 될 것 같고, no값이 제대로 받아올 수 있게 수정하면 얼추 될 것 같다.

 

일단 몇몇 수정

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>그냥 게시판</title>
</head>
<style>
.container{
	align-content : center;
	width: 1200px;
	height: 600px;
}
.content{
	width: 1000px;
	height: 600px;
}
</style>
<body>
<div class="container"> <%-- container 영역 div --%>

<jsp:include page="../inc/top.jsp" />

<jsp:include page="../inc/sidebar.jsp" />

<div class="content"> <%-- content영역 div --%>
<h1>글 읽기</h1>
<table >
<tr><td>글번호</td><td>${boardDTO.no}</td></tr>
<tr><td>글쓴이</td><td>${boardDTO.id}</td></tr>
<tr><td>조회수</td><td>${boardDTO.readcount}</td></tr>
<tr><td>작성일</td><td>${boardDTO.writetime}</td></tr>
<tr><td>글제목</td><td>${boardDTO.subject}</td></tr>
<tr><td>글내용</td><td>${boardDTO.content}</td></tr>
</table>
<div style="display: center">
<c:if test="${!empty sessionScope.id }">
	<c:if test="${sessionScope.id eq boardDTO.id}">
<a href="${pageContext.request.contextPath}/board/update?no=${boardDTO.no}"><input type="button" value="글 수정"></a> <a href="${pageContext.request.contextPath}/board/delete?no=${boardDTO.no}"><input type="button" value="글 삭제"></a>
	</c:if>
</c:if>
<a href="${pageContext.request.contextPath}/board/list"><input type="button" value="글 목록"></a>
</div>
<hr>
댓글<br>
<form action="${pageContext.request.contextPath}/board/replyInsertPro" method="post">
<input type="text" name="id" value="${sessionScope.id }" readonly="readonly"><br>
<textarea rows="5" cols="30" style="width: 792px; height: 60px" placeholder="내용을 작성해주세요." name="replyContent"></textarea><br>
<span style="float: right;"><input type="checkbox" name="replyHidden" value="true">비밀글 <input type="hidden" name="replyHidden" value="false"><input type="submit" value="작성"></span>
</form>
댓글 목록<br>
<c:forEach var="replyDTO" items="${replyList}">
<tr>
<td>${replyDTO.id}</td><td>${replyDTO.replyContent}</td>
</tr>
</c:forEach>
</div> <%-- content영역 div --%>
<jsp:include page="../inc/footer.jsp" />
</div> <%-- container 영역 div --%>
</body>
</html>

 

BoardController

	@PostMapping("/replyInsertPro")
	public String replyInsertPro(ReplyDTO replyDTO,BoardDTO boardDTO, Model model) {
		System.out.println("BoardController replyInsertPro()");
		//replyHidden을 boolean으로 변환
//		if("true".equals(replyDTO.getReplyHidden())) {
//			replyDTO.setReplyHidden(true);
//		} else {
//			replyDTO.setReplyHidden(false);
//		}
		boardService.replyInsert(replyDTO);
		return "redirect:/board/content?no="+boardDTO.getNo();
	}
    
	@GetMapping("/content")
	public String content(BoardDTO boardDTO, ReplyDTO replyDTO, Model model) {
		System.out.println("BoardController Content()");
		boardDTO = boardService.getBoard(boardDTO); //글에 대한 정보
		boardService.readCount(boardDTO); //조회수
		
		List<ReplyDTO> replyList = boardService.getReplyList();
		
		model.addAttribute("boardDTO",boardDTO);
		model.addAttribute("replyDTO",replyDTO);
		return "board/content";
	}

 

BoardSerivce

	public void replyInsert(ReplyDTO replyDTO) {
		System.out.println("BoardService replyInsert()");
//		replyDTO.setNum(boardDAO.getMaxNum()+1); //댓글 번호(PK) 값 설정
		replyDTO.setReplyWriteTime(new Timestamp(System.currentTimeMillis())); //댓글 작성 시간
		boardDAO.replyInsert(replyDTO);
	}

	public List<ReplyDTO> getReplyList() {
		System.out.println("BoardService getReplyList()");
		return boardDAO.getReplyList();
	}

 

BoardDAO

	public void replyInsert(ReplyDTO replyDTO) {
		System.out.println("BoardDAO replyInsert()");
		sqlSession.insert(namespace+".replyInsert", replyDTO);
	}

	
//	public int getMaxNum() {
//		System.out.println("BoardDAO getMaxNum()");
//		return sqlSession.selectOne(namespace+".getMaxNum");
//	}

	public List<ReplyDTO> getReplyList() {
		System.out.println("BoardDAO getReplyList()");
		return sqlSession.selectList(namespace+".getReplyList");
	}

 

boardMapper

	<insert id="replyInsert">
		insert into reply(num,id,replyContent,replyHidden,replyWriteTime)
		values(#{num},#{id},#{replyContent},#{replyHidden},#{replyWriteTime})
	</insert>
	
<!-- 	<select id="getMaxNum" resultType="java.lang.Integer"> -->
<!-- 		select max(num) from reply -->
<!-- 	</select> -->

	<select id="getReplyList" resultType="com.mystory001.domain.ReplyDTO">
		select *
		from reply
		order by replyWriteTime desc
	</select>

 

문뜩 테이블 설계가 잘 못되었음을 느낀다.

현재 이와같은 ERD인데, 테이블 board와 reply는 no로 연결되어야할 것 같다.

reply 테이블에 no 컬럼을 추가하고 외래키를 지정해주자

다음 ReplyDTO에 no 객체 생성

	private int no;
	
	
	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

 

728x90

'organize > 프로젝트' 카테고리의 다른 글

justBoard15 댓글 구현(5) 댓글 페이지 처리  (0) 2024.10.20
justBoard14 댓글 구현(4)  (0) 2024.10.15
justBoard12 댓글 구현(2) 화면  (1) 2024.10.12
justBoard11 댓글 구현(1) DB  (6) 2024.10.10
justBoard10 마무리  (5) 2024.09.30