0. 지연

손가락을 다쳤고 쓰고 있던 글의 임시 저장이 안 되어 새로 작성.

 

1. 게시판 목록 기능 구현

1-1) form

<tbody>
    <c:forEach items="${list}" var="post" varStatus="status">
      <tr role="row" class="<c:if test=" ${status.count % 2 == 0}">odd</c:if>
        <c:if test="${status.count % 2 != 0}">even</c:if>">
        <td class="sorting_1">
          <c:out value="${post.pst_no }"></c:out>
        </td>
        <td>
          <a class="move" href="<c:out value=" ${post.pst_no }"></c:out>">
            <c:out value="${post.pst_title }"></c:out>
          </a>
        </td>
        <td>
          <c:out value="${post.pst_writer }"></c:out>
        </td>
        <td>
          <fmt:formatDate value="${post.pst_wr_date }" pattern="yyyy-MM-dd" />
        </td>
        <td>
          <c:out value="${post.pst_hit }"></c:out>
        </td>
        <td>
          <c:out value="${post.pst_cmt_cnt }"></c:out>
        </td>
        <td>
          <c:out value="${post.pst_itr }"></c:out>
        </td>
      </tr>
    </c:forEach>
  </tbody>

게시물 여러 개를 보이기 위해 collection 반복문 성격인 forEach문 사용.

 

1-2) Service, ServiceImpl, Controller

// 게시판 글 목록
@GetMapping("/list")
public void list(Model model) {

    List<PostVO> list = service.getList();
    model.addAttribute("list", list);

}

model parameter를 이용하여 list의 data를 jsp에서 보여 주도록 함.

 

<select id="getList" resultType="com.comunity.domain.PostVO">
    SELECT pst_no, pst_title, pst_content, pst_writer, pst_wr_date, pst_update_date, pst_hit, pst_itr, pst_cmt_cnt FROM post_tb
</select>

a) 목록이 나오지 않는 오류가 있었는데 캐시 비우기+새로고침 이후 정상적으로 노출이 됨.

 

2. Paging 및 검색 기능

2-1) PageDTO 객체, Criteria 객체 생성

public class Criteria {
	
	private int pageNum; // 선택한 page로 이동
	private int amount; // page마다 출력할 게시물 개수
	
	private String type; // 검색 종류
	private String keyword; // 검색어
	
	// /post/list 주소 request 시 자동 호출
    // 기본 값 1 page, 출력 게시물 수 10개 지정
	public Criteria() {
		this(1, 10);
	}

	public Criteria(int pageNum, int amount) {
		// TODO Auto-generated constructor stub
		this.pageNum = pageNum;
		this.amount = amount;
	}
	
	// 검색 조건 배열
	public String[] getTypeArr() {
		return type == null? new String[] {} : type.split("");
	}
	
	public String getListLink() {
		
		UriComponentsBuilder builder = UriComponentsBuilder.fromPath("")
				.queryParam("pageNum", this.pageNum)
				.queryParam("amount", this.getAmount())
				.queryParam("type", this.getType())
				.queryParam("keyword", this.getKeyword());
		
		return builder.toUriString();

	}
}

Criteria는 page 자체를 관리하는 객체.

url이 parameter를 토대로 만들어 지기 때문에 (ex.?pageNum=7&amount=10&type=W&keyword=user2) redirect하게 되면 그때마다 parameter를 addAttribute로 받아야 함. 번거롭기 때문에, uriComponentsBuilder를 이용하면 해당 method가 호출될 때마다 queryParam을 통해 자동으로 URL 형태로 만들어 줌.

 

public PageDTO(Criteria cri, int total) {

    this.cri = cri;
    this.total = total;

    int pageSize = 10;
    int endPageInfo = pageSize - 1;

    this.endPage = (int) (Math.ceil(cri.getPageNum() / (double) pageSize)) * pageSize;

    this.startPage = this.endPage - endPageInfo;

    int realEnd = (int) (Math.ceil((total * 1.0) / cri.getAmount()));

    if(realEnd <= this.endPage) {
        this.endPage = realEnd;
    }

    this.prev = this.startPage > 1;

    this.next = this.endPage < realEnd;

}

PageDTO는 Paging 관련 변수.

 

endPage = 현재 pageNum / 한 page에 출력할 게시물 개수 * 페이지에 출력할 게시물 개수

소숫점이 남지 않도록 ceil method 이용.

하지만 위는 총 게시물 수가 10 page를 넘지 않는 경우가 있지만 현재 page에 1을 넣고 계산하면 10이 나오기 때문에 이를 보완해야 함.

realEnd = 총 게시물 * 1.0 / 총 게시물 수

 

2-2) mapper sql문

<sql id="criteria">
    <trim prefix="(" suffix=") AND " prefixOverrides="OR">
        <foreach collection="typeArr" item="type">
            <trim prefix="OR">
                <choose>
                    <when test="type == 'T'.toString()">
                        title like '%' || #{keyword} || '%'
                    </when>
                    <when test="type == 'C'.toString()">
                        content like '%' || #{keyword} || '%'
                    </when>
                    <when test="type == 'W'.toString()">
                        writer like '%' || #{keyword} || '%'
                    </when>
                </choose>
            </trim>
        </foreach>
    </trim>
</sql>

<sql> tag. 다른 구문에서 재사용이 가능한 SQL 구문을 정의할 때 사용. 주의할 점은 해당 query를 재사용하고 싶다면 재사용할 query의 상단에 선언되어야 함. 제작하면서 강의 내용 다시 보다가 왜 가장 위 순서에 있는지 의문스러웠는데 그런 주의점이 있었다. 다시 상기.

 

그리고 해당 query를 재사용(호출)하는 방법은 아래 query 중간의 <include> tag.

 

<select id="getListWithPaging" parameterType="com.comunity.domain.Criteria" resultType="com.comunity.domain.PostVO">
    <![CDATA[
        SELECT pst_no, pst_title, pst_content, pst_writer, pst_wr_date, pst_update_date, pst_hit, pst_itr, pst_cmt_cnt
        FROM (
            SELECT /*+ index_desc(post_tb pk_post) */ rownum rn, pst_no, pst_title, pst_content, pst_writer, pst_wr_date, pst_update_date, pst_hit, pst_itr, pst_cmt_cnt
            FROM post_tb
            WHERE
    ]]>

    <include refid="criteria"></include>

    <![CDATA[
        rownum <= (#{pageNum} * #{amount})
        )
        WHRER rn > ((#{pageNum) - 1) * #{amount})
    ]]>
</select>

<select id="getTotalCount" resultType="int">
    SELECT count(*) FROM post_tb WHERE

    <include refid="criteria"></include>

    bno > 0
</select>

 

Criteria에 검색 조건이 있므로 WHERE을 쓰고 include로 criteria를 호출시킴.

 

2-3) Controller 수정

@GetMapping("/list")
public void list(Criteria cri, Model model) {

    // 1) list.jsp(view) 목록 data
    List<PostVO> list = service.getList();
    model.addAttribute("list", list);

    // list.jsp(view) paging
    int total = service.getTotalCount(cri);

    PageDTO pageDTO = new PageDTO(cri, total);
    model.addAttribute("pageMaker", pageDTO);

}

paging query: 총 게시물 수를 통해 page 개수를 가져옴.

pageMaker: startPage, endPage 등 field를 jsp에서 사용하도록 하는 query. view에서 사용할 수 있도록 pageMaker 속성에 pageDTO 변수들을 저장.

 

2-4) list form 수정

<div class="row">
    <div class="col-sm-5 dataTables_info">
        <form id="searchForm" action="/post/list" method="get">
            <select name="type">
                <option value=""
                    <c:out value="${pageMaker.cri.type == null? 'selected':'' }" />>--</option>
                <option value="T"
                    <c:out value="${pageMaker.cri.type eq 'T'? 'selected':'' }" />>제목</option>
                <option value="C"
                    <c:out value="${pageMaker.cri.type eq 'C'? 'selected':'' }" />>내용</option>
                <option value="W"
                    <c:out value="${pageMaker.cri.type eq 'W'? 'selected':'' }" />>작성자</option>
                <option value="TC"
                    <c:out value="${pageMaker.cri.type eq 'TC'? 'selected':'' }" />>제목 or 내용</option>
                <option value="TW"
                    <c:out value="${pageMaker.cri.type eq 'TW'? 'selected':'' }" />>제목 or 작성자</option>
                <option value="TWC"
                    <c:out value="${pageMaker.cri.type eq 'TWC'? 'selected':'' }" />>제목 or 작성자 or 내용</option>
            </select>
            <input type="text" name="keyword" value="<c:out value="${ pageMaker.cri.keyword}" />">
            <input type="hidden" name="pageNum" value="${pageMaker.cri.pageNum}">
            <input type="hidden" name="amount" value="${pageMaker.cri.amount}">
            <button class="btn btn-primary">Search</button>
        </form>
        <!--
        <div class="dataTables_info" id="example2_info" role="status"
            aria-live="polite">Showing 1 to 10 of 57 entries</div>
            -->
    </div>
    <div class="col-sm-7">
        <div class="dataTables_paginate paging_simple_numbers"
            id="example2_paginate">
            <ul class="pagination">
            <c:if test="${pageMaker.prev }">
                <li class='paginate_button previous ${pageMaker.prev ? "": "disabled" }'
                    id="example2_previous"><a href="${pageMaker.startPage - 1}"
                    aria-controls="example2" data-dt-idx="0" tabindex="0">Previous</a></li>
            </c:if>
            <c:forEach begin="${pageMaker.startPage }" end="${pageMaker.endPage }" var="num">	
                <li class='paginate_button ${pageMaker.cri.pageNum == num ? "active":"" }'><a href="${num}"
                    aria-controls="example2" data-dt-idx="1" tabindex="0">${num}</a></li>
            </c:forEach>
            <c:if test="${pageMaker.next }">	
                <li class="paginate_button next" id="example2_next"><a
                    href="${pageMaker.endPage + 1}" aria-controls="example2" data-dt-idx="7"
                    tabindex="0">Next</a></li>
            </c:if>
            </ul>
        </div>
    </div>
    <!--prev,page number, next 를 클릭하면 아래 form이 작동된다.-->
    <form id="actionForm" action="/board/list" method="get">
        <!--list.jsp 가 처음 실행되었을 때 pageNum의 값을 사용자가 선택한 번호의 값으로 변경-->
        <input type="hidden" name="pageNum" value="${pageMaker.cri.pageNum}">
        <input type="hidden" name="amount" value="${pageMaker.cri.amount}">
        <input type="hidden" name="type" value="${pageMaker.cri.type}">
        <input type="hidden" name="keyword" value="${pageMaker.cri.keyword}">
        <!--글번호추가-->
    </form>

</table>

<!-- list nav bar -->
<div class="row page-list">
  <div class="col-sm-6">
    <div class="dataTables_info" id="example2_info" role="status" aria-live="polite">Showing 1 to 10 of 57
      entries</div>
  </div>
  <div class="col-sm-6">
    <div class="dataTables_paginate paging_simple_numbers" id="example2_paginate">
      <ul class="pagination">
        <li class="paginate_button previous disabled" id="example2_previous"><a href="#"
            aria-controls="example2" data-dt-idx="0" tabindex="0">Previous</a></li>
        <li class="paginate_button active"><a href="#" aria-controls="example2" data-dt-idx="1"
            tabindex="0">1</a></li>
        <li class="paginate_button "><a href="#" aria-controls="example2" data-dt-idx="2"
            tabindex="0">2</a></li>
        <li class="paginate_button "><a href="#" aria-controls="example2" data-dt-idx="3"
            tabindex="0">3</a></li>
        <li class="paginate_button "><a href="#" aria-controls="example2" data-dt-idx="4"
            tabindex="0">4</a></li>
        <li class="paginate_button "><a href="#" aria-controls="example2" data-dt-idx="5"
            tabindex="0">5</a></li>
        <li class="paginate_button "><a href="#" aria-controls="example2" data-dt-idx="6"
            tabindex="0">6</a></li>
        <li class="paginate_button next" id="example2_next"><a href="#" aria-controls="example2"
            data-dt-idx="7" tabindex="0">Next</a></li>
      </ul>
    </div>
  </div>

  <form id="actionForm" action="/board/list" method="get">
        <!--list.jsp 가 처음 실행되었을 때 pageNum의 값을 사용자가 선택한 번호의 값으로 변경-->
        <input type="hidden" name="pageNum" value="${pageMaker.cri.pageNum}">
        <input type="hidden" name="amount" value="${pageMaker.cri.amount}">
        <input type="hidden" name="type" value="${pageMaker.cri.type}">
        <input type="hidden" name="keyword" value="${pageMaker.cri.keyword}">
        <!--글번호추가-->
    </form>
</div>

 

a) 500번 mapper에서 sql문 오류. 오타가 있어서 수정.

b) vscode랑 동시에 쓰다가 위 내용 저장 전으로 덮어 씌워져서 다시 작업.

c) 10개씩 보이게 만들었는데 게시글 전체가 나옴...

service getList>getListWithPaging(cri)로 수정. list는 paging query 없이 그냥 전부 가져만 오는 거고 ListWithPaging이 Paging query가 들어간 method임

 

2-5) 게시글 읽기(get) 및 게시글 수정, 삭제

// 게시물 읽기, 수정 form
@GetMapping({"/get", "/modify"})
public void get(@RequestParam("pst_no") Long pst_no, @ModelAttribute("cri") Criteria cri, Model model) {

    log.info("get..." + pst_no);

    PostVO post = service.get(pst_no);
    model.addAttribute("post", post);

}

@PostMapping("/modify") 
public String modify(PostVO post, Criteria cri, RedirectAttributes rttr) {

    log.info("modify: " + post);

    service.modify(post);

    return "redirect:/post/list" + cri.getListLink();
}

@PostMapping("/remove")   //   /board/remove
public String remove(@RequestParam("pst_no") Long pst_no, Criteria cri, RedirectAttributes rttr) {

    service.delete(pst_no);

    return "redirect:/post/list" + cri.getListLink();
}

1보 1오류 경험 중. controller에 수정, 삭제 추가. 읽기 코드 일부 수정.

 

a) 게시글을 클릭했을 때 조회가 안 됨 pst_no를 받아 올 수 없다는 400번 오류.

Criteria에서 pageNum 부분 getPageNum 아닌 그냥 pageNum Parameter로 가져와서 잘못된 건 줄 알았는데 아님.

 

b) 500번 오류 There is no getter for property named 'pageNum) - 1) * #{amount' in 'class com.comunity.domain.Criteria'

amount랑 pagenum 둘 다 못 가져옴 쿼리에서 오타 난 것 수정

 

c) 10개 노출은 되나 페이지 이동이 안 됨 404에러... list script 작업이 안 돼서 그런 것 같음

$(document).ready(function() {

    let actionForm = $("#actionForm");

    $(".move").on("click", function(e) {

        e.preventDefault();

        let bno = $(this).attr("href");
        console.log("글번호" + bno);

        actionForm.append("<input type='hidden' name='bno' value='" + $(this).attr("href") + "'>");
        actionForm.attr("action", "/board/get");

        actionForm.submit();

    });

    $(".paginate_button a").on("click", function(e){
        e.preventDefault();

        actionForm.find("input[name='pageNum']").val($(this).attr("href"));

        console.log("click");

        actionForm.submit();
    });

}); // paging end

 

복사했습니다!