코딩기록 저장소 🐕/기능구현연습

10/30 블로그 게시글 쓰고 보기🐻

kimkongmom 2023. 10. 30. 09:11

 

 

 

 

🐻 오늘은 게시글을 확인하고 게시글에 조회수 올리기! 그다음 게시글에 댓글 달기! 🐻

 

쿼리문 돌린후 

 

🦦 mapper.xml

 

getBlogCount: 블로그 게시물의 총 수를 가져오는 쿼리입니다. 결과 유형은 정수(int)입니다.

getBlogList: 블로그 게시물 목록을 가져오는 쿼리입니다. 

begin과 end 매개 변수를 사용하여 페이지네이션을 지원하며, BlogMap resultMap을 사용하여 결과를 매핑합니다.

updateHit: 블로그 게시물의 조회수(HIT)를 업데이트하는 쿼리입니다. 

blogNo 매개 변수를 사용하여 특정 게시물을 식별합니다.

getBlog: 특정 블로그 게시물의 정보를 가져오는 쿼리입니다.

 blogNo를 사용하여 게시물을 식별하고 BlogMap resultMap을 사용하여 결과를 매핑합니다.

insertComment: 댓글을 삽입하는 쿼리입니다. CommentDto 매개 변수를 사용하여 댓글 내용 및 관련 정보를 삽입합니다.

getCommentCount: 특정 블로그 게시물에 대한 댓글 수를 가져오는 쿼리입니다. 

blogNo를 사용하여 게시물을 식별하고 결과 유형은 정수(int)입니다.

getCommentList: 특정 블로그 게시물의 댓글 목록을 가져오는 쿼리입니다. 

begin과 end 매개 변수를 사용하여 페이지네이션을 지원하며, CommentMap resultMap을 사용하여 결과를 매핑합니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 
  <select id="getBlogCount" resultType="int">
    SELECT COUNT(*)
      FROM BLOG_T
  </select>
  
  <select id="getBlogList" parameterType="Map" resultMap="BlogMap">
    SELECT A.BLOG_NO, A.TITLE, A.CONTENTS, A.USER_NO, A.HIT, A.IP, A.CREATED_AT, A.MODIFIED_AT, A.EMAIL
      FROM (SELECT ROW_NUMBER() OVER(ORDER BY B.BLOG_NO DESC) AS RN, B.BLOG_NO, B.TITLE, B.CONTENTS, B.USER_NO, B.HIT, B.IP, B.CREATED_AT, B.MODIFIED_AT, U.EMAIL
              FROM USER_T U INNER JOIN BLOG_T B
                ON B.USER_NO = U.USER_NO) A
     WHERE A.RN BETWEEN #{begin} AND #{end}
  </select>
  
  <update id="updateHit" parameterType="int">
    UPDATE BLOG_T
       SET HIT = HIT + 1
     WHERE BLOG_NO = #{blogNo}  
  </update>
  
  <select id="getBlog" parameterType="int" resultMap="BlogMap">
    SELECT B.BLOG_NO, B.TITLE, B.CONTENTS, B.HIT, B.IP, B.CREATED_AT, B.MODIFIED_AT, U.USER_NO, U.EMAIL, U.NAME
      FROM USER_T U, BLOG_T B
     WHERE U.USER_NO = B.USER_NO
       AND B.BLOG_NO = #{blogNo}
  </select>
  
  <insert id="insertComment" parameterType="CommentDto">
    INSERT INTO COMMENT_T (
        COMMENT_NO
      , CONTENTS
      , USER_NO
      , BLOG_NO
      , CREATED_AT
      , STATUS
      , DEPTH
      , GROUP_NO
    ) VALUES (
        COMMENT_SEQ.NEXTVAL
      , #{contents}
      , #{userDto.userNo}
      , #{blogNo}
      , TO_CHAR(SYSDATE, 'YYYY-MM-DD HH:MI:SS')
      , 1
      , 0
      , COMMENT_SEQ.CURRVAL
    )
  </insert>
  
  <select id="getCommentCount" parameterType="int" resultType="int">
    SELECT COUNT(*)
      FROM COMMENT_T
     WHERE BLOG_NO = #{blogNo}
  </select>
 
  <select id="getCommentList" parameterType="Map" resultMap="CommentMap">
    SELECT A.COMMENT_NO, A.CONTENTS, A.BLOG_NO, A.CREATED_AT, A.STATUS, A.DEPTH, A.GROUP_NO, A.USER_NO, A.NAME
      FROM (SELECT ROW_NUMBER() OVER(ORDER BY GROUP_NO DESC, DEPTH ASC, COMMENT_NO DESC) AS RN, C.COMMENT_NO, C.CONTENTS, C.BLOG_NO, C.CREATED_AT, C.STATUS, C.DEPTH, C.GROUP_NO, U.USER_NO, U.NAME
              FROM USER_T U INNER JOIN COMMENT_T C
                ON U.USER_NO = C.USER_NO
             WHERE C.BLOG_NO = #{blogNo}) A
     WHERE A.RN BETWEEN #{begin} AND #{end}
  </select>
  
cs

 

 

🦦 BlogDto

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.gdu.myhome.dto;
 
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class BlogDto {
  private int blogNo;
  private String title;
  private String contents;
  private int hit;
  private String ip;  
  private String createdAt;
  private String modifiedAt;
  private String email;
  private UserDto userDto;  // private int userNo;
}
 
cs

 

🦦 CommentDto

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.gdu.myhome.dto;
 
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class CommentDto {
  private int commentNo;
  private String contents;
  private int blogNo;
  private String createdAt;
  private int status;
  private int depth;
  private int groupNo;
  private UserDto userDto; // private int userNo;
}
 
cs

 

 

🦦 mapper.xml에 추가사항

BlogMap: 이 resultMap은 BlogDto의 필드와 데이터베이스 테이블의 열 간의 매핑을 정의합니다. BlogDto 내에 userDto 필드가 있으며, javaType="UserDto" 및 property="userDto"를 사용하여 이 필드에 대한 매핑 정보를 정의합니다. 결과 매핑 정보를 사용하여 데이터베이스 결과를 BlogDto 객체로 변환합니다.

CommentMap: 이 resultMap은 CommentDto의 필드와 데이터베이스 테이블의 열 간의 매핑을 정의합니다. CommentDto도 userDto 필드가 있으며, javaType="UserDto" 및 property="userDto"를 사용하여 이 필드에 대한 매핑 정보를 정의합니다. 결과 매핑 정보를 사용하여 데이터베이스 결과를 CommentDto 객체로 변환합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 
  <resultMap type="BlogDto"       id="BlogMap">
    <id      column="BLOG_NO"     property="blogNo" />
    <result  column="TITLE"       property="title" />
    <result  column="CONTENTS"    property="contents" />
    <result  column="HIT"         property="hit" />
    <result  column="IP"          property="ip" />
    <result  column="CREATED_AT"  property="createdAt" />
    <result  column="MODIFIED_AT" property="modifiedAt" />
    <association javaType="UserDto"      property="userDto">
      <id        column="USER_NO"        property="userNo"/>
      <result    column="EMAIL"          property="email" />
      <result    column="PW"             property="pw" />
      <result    column="NAME"           property="name" />
      <result    column="GENDER"         property="gender" />
      <result    column="MOBILE"         property="mobile" />
      <result    column="POSTCODE"       property="postcode" />
      <result    column="ROAD_ADDRESS"   property="roadAddress" />
      <result    column="JIBUN_ADDRESS"  property="jibunAddress" />
      <result    column="DETAIL_ADDRESS" property="detailAddress" />
      <result    column="AGREE"          property="agree" />
      <result    column="STATE"          property="state" />
      <result    column="PW_MODIFIED_AT" property="pwModifiedAt" />
      <result    column="JOINED_AT"      property="joinedAt" />
    </association>
  </resultMap>
 
  <resultMap type="CommentDto"    id="CommentMap">
    <id      column="COMMENT_NO"  property="commentNo"/>
    <result  column="CONTENTS"    property="contents"/>
    <result  column="BLOG_NO"     property="blogNo"/>
    <result  column="CREATED_AT"  property="createdAt"/>
    <result  column="STATUS"      property="status"/>
    <result  column="DEPTH"       property="depth"/>
    <result  column="GROUP_NO"    property="groupNo"/>
    <association javaType="UserDto"      property="userDto">
      <id        column="USER_NO"        property="userNo"/>
      <result    column="EMAIL"          property="email" />
      <result    column="PW"             property="pw" />
      <result    column="NAME"           property="name" />
      <result    column="GENDER"         property="gender" />
      <result    column="MOBILE"         property="mobile" />
      <result    column="POSTCODE"       property="postcode" />
      <result    column="ROAD_ADDRESS"   property="roadAddress" />
      <result    column="JIBUN_ADDRESS"  property="jibunAddress" />
      <result    column="DETAIL_ADDRESS" property="detailAddress" />
      <result    column="AGREE"          property="agree" />
      <result    column="STATE"          property="state" />
      <result    column="PW_MODIFIED_AT" property="pwModifiedAt" />
      <result    column="JOINED_AT"      property="joinedAt" />
    </association>
  </resultMap>
 
</mapper>
cs

 

 

 

🦦 BlogMapper 인터페이스

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.gdu.myhome.dao;
 
@Mapper
public interface BlogMapper {
  public int getBlogCount();
  public List<BlogDto> getBlogList(Map<String, Object> map);
  public int updateHit(int blogNo);
  public BlogDto getBlog(int blogNo);
  
  public int insertComment(CommentDto comment);
  public int getCommentCount(int blogNo);
  public List<CommentDto> getCommentList(Map<String, Object> map);
}
 
cs

 

 

🦦 BlogService 인터페이스

 

1
2
3
4
5
6
7
8
9
10
package com.gdu.myhome.service;
 
public interface BlogService {
  public void loadBlogList(HttpServletRequest request, Model model);
  public int increseHit(int blogNo);
  public BlogDto getBlog(int blogNo);
  
  public Map<String, Object> addComment(HttpServletRequest request);
  public Map<String , Object> loadCommentList(HttpServletRequest request);
}
cs

 

 

 

🦦 BlogServiceImpl

loadBlogList 메서드: 블로그 게시물 목록을 불러오는 메서드입니다. 요청(request)에서 페이지 번호를 받아와서, 해당 페이지에 표시할 블로그 게시물 목록을 데이터베이스에서 가져옵니다. 페이징 처리를 위한 정보를 생성하고, 이를 모델(Model)에 추가하여 뷰로 전달합니다.

increseHit 메서드: 블로그 게시물의 조회수(hit)를 증가시키는 메서드입니다. 게시물의 번호를 받아와서 해당 게시물의 조회수를 증가시킵니다.

getBlog 메서드: 특정 블로그 게시물을 가져오는 메서드입니다. 게시물의 번호를 받아와서 해당 게시물 정보를 데이터베이스에서 가져옵니다.

addComment 메서드: 댓글을 추가하는 메서드입니다. 요청에서 댓글 내용, 사용자 번호, 게시물 번호를 받아와서, 이 정보를 사용하여 CommentDto 객체를 생성하고 데이터베이스에 댓글을 추가합니다. 댓글이 추가된 후, 해당 게시물의 댓글 목록을 다시 불러옵니다.

loadCommentList 메서드: 특정 블로그 게시물의 댓글 목록을 불러오는 메서드입니다. 요청에서 게시물 번호와 페이지 번호를 받아와서, 해당 게시물의 댓글 목록을 데이터베이스에서 가져오고 페이징 처리를 수행합니다. 그리고 댓글 목록과 페이징 정보를 반환합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.gdu.myhome.service;
 
@Transactional
@RequiredArgsConstructor
@Service
public class BlogServiceImpl implements BlogService {
 
  private final BlogMapper blogMapper;
  private final MyFileUtils myFileUtils;
  private final MyPageUtils myPageUtils;
  
  @Transactional(readOnly=true)
  @Override
  public void loadBlogList(HttpServletRequest request, Model model) {
  
    Optional<String> opt = Optional.ofNullable(request.getParameter("page"));
    int page = Integer.parseInt(opt.orElse("1"));
    int total = blogMapper.getBlogCount();
    int display = 10;
    
    myPageUtils.setPaging(page, total, display);
    
    Map<String, Object> map = Map.of("begin", myPageUtils.getBegin()
                                   , "end", myPageUtils.getEnd());
    
    List<BlogDto> blogList = blogMapper.getBlogList(map);
    
    model.addAttribute("blogList", blogList);
    model.addAttribute("paging", myPageUtils.getMvcPaging(request.getContextPath() + "/blog/list.do"));
    model.addAttribute("beginNo", total - (page - 1* display);
    
  }
  
  @Override
  public int increseHit(int blogNo) {
    return blogMapper.updateHit(blogNo);
  }
  
  @Override
  public BlogDto getBlog(int blogNo) {
    return blogMapper.getBlog(blogNo);
  }
 
  @Override
  public Map<String, Object> addComment(HttpServletRequest request) {
 
    String contents = request.getParameter("contents");
    int userNo = Integer.parseInt(request.getParameter("userNo"));
    int blogNo = Integer.parseInt(request.getParameter("blogNo"));
    
    CommentDto comment = CommentDto.builder()
                          .contents(contents)
                          .userDto(UserDto.builder()
                                    .userNo(userNo)
                                    .build())
                          .blogNo(blogNo)
                          .build();
    
    int addCommentResult = blogMapper.insertComment(comment);
    
    Map<String, Object> map = Map.of("blogNo", blogNo
                                    , "begin", myPageUtils.getBegin()
                                    , "end", myPageUtils.getEnd());
    
    List<CommentDto> commentList = blogMapper.getCommentList(map);
    
    return Map.of("addCommentResult", addCommentResult);
    
  }
 
@Override
    public Map<String, Object> loadCommentList(HttpServletRequest request) {
  
      int blogNo = Integer.parseInt(request.getParameter("blogNo"));
      
      int page = Integer.parseInt(request.getParameter("page"));
      int total = blogMapper.getCommentCount(page);
      int display = 10;
      
      myPageUtils.setPaging(page, total, display);
      Map<String, Object> map = Map.of("blogNo", blogNo
          , "begin", myPageUtils.getBegin()
          , "end", myPageUtils.getEnd());
 
      List<CommentDto> commentList = blogMapper.getCommentList(map);
      String paging = myPageUtils.getAjaxPaging();
      
      Map<String, Object> result = new HashMap<String, Object>();
      result.put("commentList", commentList);
      result.put("paging", paging);     
      
      return result;
    } 
}
cs

 

 

🦦 BlogController

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.gdu.myhome.controller;
 
@RequestMapping("/blog")
@RequiredArgsConstructor
@Controller
public class BlogController {
 
  private final BlogService blogService;
  
  @GetMapping("/increseHit.do")
  public String increseHit(@RequestParam(value="blogNo", required=false, defaultValue="0"int blogNo) {
    int increseResult = blogService.increseHit(blogNo);
    if(increseResult == 1) {
      return "redirect:/blog/detail.do?blogNo=" + blogNo;
    } else {
      return "redirect:/blog/list.do";
    }
  }
  
  @GetMapping("/detail.do")
  public String detail(@RequestParam(value="blogNo", required=false, defaultValue="0"int blogNo
                     , Model model) {
    BlogDto blog = blogService.getBlog(blogNo);
    model.addAttribute("blog", blog);
    return "blog/detail";
  }
    
  @ResponseBody
  @PostMapping(value="/addComment.do", produces="application/json")
  public Map<String, Object> addComment(HttpServletRequest request) {
    return blogService.addComment(request);
  }
  
  @ResponseBody
  @GetMapping(value="/commentList.do", produces="application/json")
  public Map<String, Object> commentList(HttpServletRequest request){
    return blogService.loadCommentList(request);
  }
  
  
  
  
}
cs

 

 

🦦 list.jsp

 ${blogList}는 JSP 모델에 저장된 블로그 목록을 나타냅니다. var 속성을 사용하여 각 블로그 항목을 b라는 변수에 할당하고, varStatus 속성을 사용하여 반복 상태를 vs라는 변수에 할당합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
 
  <div>
    <a href="${contextPath}/blog/write.form">
      <button type="button" class="btn btn-primary">새글작성</button>
    </a>
  </div>
  
  <hr>
    <div>
    <table border="1">
      <thead>
        <tr>
          <td>순번</td>
          <td>제목</td>
          <td>조회수</td>
          <td>작성자</td>
          <td>작성일자</td>
        </tr>
      </thead>
      <tbody>
        <c:forEach items="${blogList}" var="b" varStatus="vs">
          <tr>
            <td>${beginNo - vs.index}</td>
            <td>
              <!-- 내가 작성한 블로그의 조회수는 증가하지 않는다. -->
              <c:if test="${sessionScope.user.userNo == b.userDto.userNo}">
                <a href="${contextPath}/blog/detail.do?blogNo=${b.blogNo}">${b.title}</a>
              </c:if>
              <!-- 내가 작성하지 않았다면 조회수를 증가시킨 뒤 상세보기 요청을 한다. -->
              <c:if test="${sessionScope.user.userNo != b.userDto.userNo}">
                <a href="${contextPath}/blog/increseHit.do?blogNo=${b.blogNo}">${b.title}</a>
              </c:if>
            </td>
            <td>${b.hit}</td>
            <td>${b.userDto.email}</td>
            <td>${b.createdAt}</td>
          </tr>
        </c:forEach>
      </tbody>
      <tfoot>
        <tr>
          <td colspan="5">${paging}</td>
        </tr>
      </tfoot>
    </table>
  </div>
cs

 

 

🦦 detail.jsp

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
 
  <!-- 블로그 상세보기 -->
  <div>
    <h1>${blog.title}</h1>
    <div>작성자 : ${blog.userDto.name}</div>
    <div>조회수 : ${blog.hit}</div>
    <div>작성IP : ${blog.ip}</div>
    <div>작성일 : ${blog.createdAt}</div>
    <div>수정일 : ${blog.modifiedAt}</div>
    <div>
      버튼구역
    </div>
    <div>${blog.contents}</div>
  </div>
  
  <hr>
  
  <!-- 댓글 작성 화면 -->
  <div>
    <form id="frm_comment_add">
      <textarea rows="3" cols="50" name="contents" id="contents" placeholder="댓글을 작성해 주세요"></textarea>
      <input type="hidden" name="userNo" value="${sessionScope.user.userNo}">
      <input type="hidden" name="blogNo" value="${blog.blogNo}">
      <button type="button" id="btn_comment_add">작성완료</button>
    </form>
  </div>
  
  <!-- 블로그 댓글 목록 -->
  <div style="width: 100%; border-bottom: 1px solid gray;"></div>
  <div id="comment_list"></div>
  <div id="paging"></div>
  
  <script>
  
    const fnRequiredLogin = () => {        
      // 로그인을 안하고 작성을 시도하면 로그인 페이지로 보내기
      $('#contents, #btn_comment_add').click(() => {
        if('${sessionScope.user}' === ''){
          if(confirm('로그인이 필요한 기능입니다. 로그인할까요?')){
            location.href = '${contextPath}/user/login.form';
          } else {
            return;
          }
        }
      })
    }
    
    const fnCommentAdd = () => {
      $('#btn_comment_add').click(() => {
        $.ajax({
          // 요청
          type: 'post',
          url: '${contextPath}/blog/addComment.do',
          data: $('#frm_comment_add').serialize(),
          // 응답
          dataType: 'json',
          success: (resData) => {  // {"addCommentResult": 1}
            if(resData.addCommentResult === 1){
              alert('댓글이 등록되었습니다.');
              $('#contents').val('');
              fnCommentList();
            }
          }
        })
      })
    }
    
    // 전역 변수
    var page = 1;
    
    const fnCommentList = () => {
      $.ajax({
        // 요청
        type: 'get',
        url: '${contextPath}/blog/commentList.do',
        data: 'page=' + page + '&blogNo=${blog.blogNo}',
        // 응답
        dataType: 'json',
        success: (resData) => {  // resData = {"commentList": [], "paging": "<div>...</div>"}
            $('#comment_list').empty();
            $('#paging').empty();
          if(resData.commentList.length === 0){
              $('#comment_list').text('첫 번째 댓글의 주인공이 되어 보세요');
              $('#paging').text('');
              return;
          }
          $.each(resData.commentList, (i, c) => {
              let str = '';
              if(c.depth === 0){
                  str += '<div style="width: 100%; border-bottom: 1px solid gray;">';
              } else {
                  str += '<div style="width: 100%; border-bottom: 1px solid gray; margin-left: 32px;">';
              }
              str += '  <div>' + c.userDto.name + '</div>';
              str += '  <div>' + c.contents + '</div>';
              str += '  <div style="font-size: 12px;">' + c.createdAt + '</div>';
              str += '</div>';
              $('#comment_list').append(str);
          })
          $('#paging').append(resData.paging);  // fnAjaxPaging() 함수가 호출되는 곳
        }
      })
    }
    
    const fnAjaxPaging = (p) => {
        page = p;
        fnCommentList();
    }
    
    fnRequiredLogin();
    fnCommentAdd();
    fnCommentList();
    
  </script>
 
</div>
 
cs

 

 

조회수올라가쥬