현재 게시판에서는

게시물의 제목이 긴 경우 두 줄 이상으로 출력된다.

이 경우 지저분해 보일 수 있기 때문에

다음 그림과 같이 지정된 글자수로 한 줄로 잘라서 보여주는 경우가 많다.


화면에 보여줄 때 제목의 글자수(길이)가 지정된 글자수보다 많은지 확인하고

많으면 자르는 방식으로 구현하면 된다.

다만, 자르려고 하는 글자가 한글일 때 문제가 발생하게 된다.

영어와 숫자를 제외한 문자는 2바이트(EUC-KR)내지 3바이트(UTF-8)로 처리된다.

따라서 별도의 처리가 필요하지만 본 예제에서는 utiletc.java 파일에 getShortString 함수로 구현되어 있다.

몇 년 전 다른 사람 것을 가져다 사용 중 인데(출처가 기억안나서…) 그냥 사용하길 바란다.

본 예제에서는 데이터를 뿌려줄 때 (boardList.jsp) 글자를 자르는 메소드(getShortString)를 호출해서 사용하도록 했다. 

화면에 맞추어 최대 35만 보이도록 표현했다.

<c:forEach var="listview" items="${listview}" varStatus="status">   
    <c:url var="link" value="board3Read">
        <c:param name="brdno" value="${listview.brdno}" />
    </c:url>       
                                           
    <tr>
        <td>
            <c:out value="${searchVO.totRow-((searchVO.page-1)*searchVO.displayRowCount + status.index)}"/>                   
        </td>
        <td><a href="${link}"><c:out value="${listview.getShortTitle(35)}"/></a></td>
        <td><c:out value="${listview.brdwriter}"/></td>
        <td><c:out value="${listview.brddate}"/></td>
        <td><c:out value="${listview.brdhit}"/></td>
    </tr>
</c:forEach>

boardList.jsp

JSP에서는 boardVO에 있는 함수를 호출(getShortTitle)하고, getShortTitle는 실제 글자를 자르는 함수를 호출한다.

글자를 자르기 위해서는 getShortString 함수를 호출한다는 것만 기억하자.

public class boardVO {
    private String brdno, brdtitle, brdwriter, brdmemo, brddate, brdhit, brddeleteflag;


    public String getShortTitle(Integer len){
        return utiletc.getShortString(brdtitle, len);
    }
  ~~ 생략 ~~
}

boardVO.java


getShortString 함수는 UTF-8로 한글을 자른다.

따라서 영어나 숫자가 있을 때와 없을 때(한글이 많을 때) 출력된 모습이 다른 걸 알 수 있다.

한글이 많을수록 짧게 표현된다. 

UTF-8은 3바이트로 처리하고 화면에 출력되는 것은 2 바이트(EUC-KR)로 처리되어서 그런 것으로 추측되는데

EUC-KR처리하면 바꿔야 하는 다른 것들이 많다(UTF-8이 거의 표준).


이것을 프로그램으로 처리하면 이상과 같이 다소 어려움이 있다.

하지만, 다음과 같이 디자인에서 사용하는 CSS를 이용하면 아주 쉽게 처리할 수 있다.

<td style="border: 1px solid black; max-width: 100px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">

<a href="${link}"><c:out value="${listview.brdtitle}"/></a>

</td>

이 코드는 글자를 출력하다 지정된 너비(width)를 넘어가면 줄여서 표시하게된다.

자세한 내용은 찾아보길 바란다.


실행후 F12를 눌러서 DOM 탐색기로 보면

다음과 같이 글 제목의 전체 내용이 다 있지만

브라우저에서는 적절하게 잘려서 출력되는 걸 볼 수 있다.

IE 7 이하에서는 적용되지 않지만

대부분 IE 9 이상을 사용하기 때문에 CSS로 사용하는 것이 더 좋을 것 같다.







그림과 같이 사용자가 게시물 중 원하는 게시물을 빨리 찾기 위한 검색 기능을 구현해 본다.


검색의 기본 개념은 데이터 베이스에 저장된 많은 데이터 중에서

특정 필드를 대상으로 어떤 값을 가진 데이터를 조회하도록 DBMS에게 시키는 것이다.

예로, 게시물 중에서 제목이나 내용에 “게시판”이라는 글자가 있는 데이터를 모두 찾는 것이다.

이 처리를 위해서는 


1.    리스트에서 찾고자 하는 필드(제목, 내용)와 찾고자 하는 검색어(게시판)를 입력할 수 있도록 해줘야 한다.

2.    그리고 이 값을 SQL로 전달해서 실행하면 된다.

3.    실제로 리스트를 가져오는 SELECT문에 WHERE만 추가해주면 끝이다.


위 3가지만 수정하면 되지만 선행 작업이 필요하다. 

먼저, 다음 코드와 같이 PageVO를 상속 받아서 SearchVO를 생성한다.

위 그림에서 검색하고자 하는 필드(제목, 내용)을 저장하는 searchType,

사용자가 찾으려는 검색어를 저장하는 searchKeyword를 추가해서 생성한다.

PageVO에 이 두 필드를 추가해서 사용해도 되지만

상속받아서 별도의 클래스를 만드는 이유는

실제 사이트 개발시 많이 사용되기 때문에

페이징과 관계된 기능을 PageVO에 두고 상속받아 사용하는 것이 편리하다.

따라서 PageVO와 pageVO를 사용한 것들을 모두 수정해 줘야 한다.

public class SearchVO extends  PageVO  {

            private String searchKeyword = "";           // 검색 키워드

           private String searchType = "";                // 검색 필드: 제목, 내용 

           private String[] searchTypeArr;                // 검색 필드를 배열로 변환

          

           public String getSearchKeyword() {

                     return searchKeyword;

           }

           public void setSearchKeyword(String searchKeyword) {

                     this.searchKeyword = searchKeyword;

           }

           public String getSearchType() {

                     return searchType;

           }

           public void setSearchType(String searchType) {

                     this.searchType = searchType;

           }

           public String[] getSearchTypeArr() {

                     return searchType.split(",");

           }

 }

/common/SearchVO.java


다음 코드 중 Form 테그의 내용은 검색 기능을 위해 추가된 내용이다.

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>
<%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

~~ 생략 ~~
<c:forEach var="listview" items="${listview}" varStatus="status">   
<c:url var="link" value="board3Read">
    <c:param name="brdno" value="${listview.brdno}" />
</c:url>       
<tr>
    <td>
    <c:out value="${searchVO.totRow-((searchVO.page-1)*searchVO.displayRowCount + status.index)}"/>    </td>
    <td><a href="${link}"><c:out value="${listview.brdtitle}"/></a></td>
    <td><c:out value="${listview.brdwriter}"/></td>
    <td><c:out value="${listview.brddate}"/></td>
    <td><c:out value="${listview.brdhit}"/></td>
</tr>
</c:forEach>
</tbody>
</table>
<c:if test="${searchVO.totPage>1}">
<div class="paging">
    <c:forEach var="i" begin="${searchVO.pageStart}" end="${searchVO.pageEnd}" step="1">
<c:url var="pageLink" value="board3List">
        <c:param name="page" value="${i}" />
        </c:url>
        <c:choose>
            <c:when test="${i eq searchVO.page}">
            <c:out value="${i}"/>
            </c:when>
            <c:otherwise>
                 <a href="${pageLink}"><c:out value="${i}"/></a>
            </c:otherwise>
        </c:choose>
    </c:forEach>
</div>
<br/>
</c:if>
<form id="form1" name="form1"  method="post">
<div>
    <input type="checkbox" name="searchType" value="brdtitle" <c:if test="${fn:indexOf(searchVO.searchType, 'brdtitle')!=-1}">checked="checked"</c:if>/>
    <label class="chkselect" for="searchType1">제목</label>
    <input type="checkbox" name="searchType" value="brdmemo" <c:if test="${fn:indexOf(searchVO.searchType, 'brdmemo')!=-1}">checked="checked"</c:if>/>
    <label class="chkselect" for="searchType2">내용</label>
    <input type="text" name="searchKeyword" style="width:150px;" maxlength="50" value='<c:out value="${searchVO.searchKeyword}"/>' onkeydown="if(event.keyCode == 13) { fn_formSubmit();}">
    <input name="btn_search" value="검색" class="btn_sch" type="button" onclick="fn_formSubmit()" />
</div>
/form>   
</body>
</html>


board3List.jsp

체크 박스(searchType)를 이용해 사용자가 검색하고자 하는 필드를 선택할 수 있도록 했다.

제목(brdtitle)과 내용(brdmemo)이 동일한 이름(searchType)을 가지기 때문에

서버로 전달될 때 searchType 변수에 콤마(,)로 구분되어 전달된다.

즉, 사용자가 제목을 체크하면 searchType에 “brdtitle”,

둘 다 체크하면 “brdtitle, brdmemo”이 저장되어 전달 된다.

폼 테그의 action에 url이 지정되지 않았기 때문에 자기 자신(boardList)을 호출한다.


JSP 파일 시작 부분에 테그 라이브러리 선언(<%@ taglib prefix="fn" uri="~~"%>)을 넣어 주고,

fn:indexOf를 사용하였다.

검색 값을 넣고 검색하면 선택된 필드가 계속 선택된 상태로 보여야 한다.

사용자가 선택한 필드는 searchType에 넣어져 서버로 전송되고,

서버에서 처리 후 다시 JSP 파일로 전달 된다.

JSP에서 HTML 생성시 searchType에 각 체크 박스의 값이 있는지 (IndexOF) 확인하여,

있으면 체크(checked="checked")하여 값이 유지되도록 처리한다.


사용자가 선택한 필드와 검색어가 컨트롤과 서비스를 통해 다음과 같이 전달된다.

@RequestMapping(value = "/board3List")
public String boardList(SearchVO searchVO, ModelMap modelMap) throws Exception {
        searchVO.pageCalculate( boardSvc.selectBoardCount(searchVO) );

        List<?> listview   = boardSvc.selectBoardList(searchVO);
       
        modelMap.addAttribute("listview", listview);
        modelMap.addAttribute("searchVO", searchVO);

        return "board3/boardList";
}

board3Ctr.java

public Integer selectBoardCount(SearchVO param) throws Exception {
    return sqlSession.selectOne("selectBoard3Count", param);
}
public List<?> selectBoardList(SearchVO param) throws Exception {
    return sqlSession.selectList("selectBoard3List", param);
}

board3Svc.java

넘겨 받은 SearchVO의 값을 가지고 WHERE 절을 생성한다.

다만, 데이터 개수를 세는 selectBoard3Count, selectBoard3List 에 동일한 조건식이 적용되어야 한다.

동일한 코드를 두 번 입력하면 귀찮고 유지보수 문제가 있어서

다음과 같이 공통으로 빼고 식별자(includeBoard)를 부여한 뒤

이 것을 각 SQL문에서 include하여 사용하는 것이 좋다.

<sql id="includeBoard">
    WHERE BRDDELETEFLAG='N'
    <if test="searchKeyword!=null and searchKeyword!=''">
        <foreach item="item" index="index" collection="searchTypeArr">
            AND ${item} LIKE CONCAT('%', #{searchKeyword},'%' )
         </foreach>
    </if>
</sql>

<select id="selectBoard3Count" resultType="Integer" parameterType="gu.common.SearchVO">
    SELECT COUNT(*)
      FROM TBL_BOARD
     <include refid="includeBoard"/>
</select>

<select id="selectBoard3List" resultType="gu.board3.boardVO" parameterType="gu.common.SearchVO">
    SELECT BRDNO, BRDTITLE, ~~ 생략 ~~
      FROM TBL_BOARD
     <include refid="includeBoard"/>
     ORDER BY BRDNO DESC
     LIMIT ${rowStart-1}, 10
</select>

baord3.xml

검색어가 없으면 조건을 사용하지 않고

전체 리스트를 보여주기 위해 IF문이 사용되었고,

검색어가 있으면 검색 대상 필드의 개수만큼 돌면서(foreach)서 조건을 생성하도록 했다. 

searchTypeArr는 searchType을 콤마(,)를 기준으로 잘라서 배열로 반환하는 것으로

foreach문은 배열 개수만큼 반복하여 다음과 같은 코드가 생성한다.

AND brdtitle LIKE CONCAT('%', '게시판','%')
AND brdmemo LIKE CONCAT('%', '게시판','%')

위의 조건절은 다소 잘 못 된 것으로 두 개의 필드가 선택된 경우

AND가 아닌 OR가 되어야 한다.

즉, 제목과(AND) 내용에 해당 검색어가 있는 것이 아니라

제목이나(OR) 내용에 해당 검색어가 있는 데이터를 찾는 것이다.

즉 다음과 같이 생성되어야 한다.

Mybatis의 foreach문은 보다 더 다양한 기능을 가지고 있으니

잘 활용해서 다음과 같이 작성해 보길 바란다.

AND (
        brdtitle LIKE CONCAT('%', '게시판','%')
   OR brdmemo LIKE CONCAT('%', '게시판','%')
)

검색 필드로 작성자도 추가해 보자.

두 번째 작업을 위해 board1 폴더를 복사해서 board2로 폴더 명을 바꾸어 준다.

Java와 xml 파일에서 board1로 명명된 명칭을 board2로 바꾸어 진행한다.

첫 글자가 대문자로 Board1으로 되어 있는 것은 Board2로 바꾸어야 하니 주의해서 변경하기 바란다.

개인적으로 모든 파일의 "oard1"을 "oard2"로 바꾸기를 실행하여 처리했다.

샘플을 다운 받아 사용해도 되며 설치가 제대로 되었다면 http://localhost:8080/board/board2List으로 실행해 볼 수 있다.


이 단계를 진행하기 위해 조회수(BRDHIT), 삭제여부(BRDDELETEFLAG) 필드를 추가하고 기존 데이터를 위해 값을 설정해줘야 한다.

다음 SQL문을 실행해야 한다.

ALTER TABLE TBL_BOARD ADD (BRDHIT INT, BRDDELETEFLAG CHAR(1));

UPDATE TBL_BOARD SET BRDHIT=0, BRDDELETEFLAG='N' WHERE BRDNO IS NOT NULL;


테이블과 같이 boardVO 클래스도 수정해 준다.

package gu.board2;

public class boardVO {

    private String brdno, brdtitle, brdwriter, brdmemo, brddate, brdhit, brddeleteflag;

    ~~ 생략 ~~

    public String getBrdhit() {
        return brdhit;
    }

    public void setBrdhit(String brdhit) {
        this.brdhit = brdhit;
    }

    public String getBrddeleteflag() {
        return brddeleteflag;
    }

    public void setBrddeleteflag(String brddeleteflag) {
        this.brddeleteflag = brddeleteflag;
    }
}




글쓰기와 수정은 기본적으로 동일하다.

차이가 있다면 사용자 입력 폼을 호출할 때,

글쓰기는 빈 입력 상자들만 출력하고,

글 수정은 지정된 글번호에 대한 데이터를 조회하여

입력상자들의 초기값으로 지정하여 출력한다.


저장 처리에 있어서는 SQL문만 다를 뿐 동일하다.

즉, 폼에서는 글번호 값이 있으면 데이터를 가져오는 서비스를 호출하고

없으면 그냥 JSP파일을 호출한다.

JSP에서는 값이 없으면 텍스트 박스에 빈 값이 들어가기 때문에 새 글을 입력하게 된다.

따라서 글쓰기 컨트롤(boardForm)에

글수정 컨트롤(boardUpdate) 내용을 복사해서 다음과 같이 넣어주면 된다.

두 컨트롤을 합쳐서 글 번호(brdno) 값이 있으면 해당 데이터를 불러와서 수정으로,

없으면(null)  그냥 입력 폼을 호출하게 된다.

public String boardForm(HttpServletRequest request, ModelMap modelMap) throws Exception {
        String brdno = request.getParameter("brdno");
        if (brdno!=null) {
            boardVO boardInfo = boardSvc.selectBoardOne(brdno);
             modelMap.addAttribute("boardInfo", boardInfo);
        }
       
        return "board2/boardForm";
}

board2Ctr.java

JSP 파일은 글 수정 파일(boardUpdate.jsp)의 내용을 글쓰기(boardUpdate.jsp)에 넣어준다.

글 수정과 관련된 컨트롤과 jsp 파일은 삭제 한다.


저장 컨트롤에서도 Insert와 Update를 글번호 값(brdno) 존재 여부에 따라

다음 코드와 같이 각각 필요한 것을 호출해 준다.

public String boardSave(@ModelAttribute boardVO boardInfo) throws Exception {
       
        if (boardInfo.getBrdno()==null)
               boardSvc.insertBoard(boardInfo);
        else boardSvc.updateBoard(boardInfo);

        return "redirect:/board2List";
}

board2Ctr.java

여기에서는 두 개의 서비스를 호출했는데

if문을 서비스(board2Svc.java)에 넣어서 처리하는 것이 보다 효율적일 수 있다.

하나의 서비스에서 다른 2개의 SQL문을 상황에 따라 호출하는 것이 더 좋을 것이다.

직접 해보길 바란다.


다음과 같이 boardForm.jsp의 폼(form) 테그 method를 Post로 지정하는 것이 좋다.

<form name="form1" action="board2Save" method="post">

별도의 지정이 없는 경우 GET 이 기본(METHOD="GET")으로 지정된다.

GET 방식은 다음과 같이 URL 뒤에 변수이름과 값이 전송되는 형태이다.

http://localhost/board1/board1List?searchType=brdname&searchKeyword=홍

호출되는 URL의 물음표(?)뒤에 변수명과 값이 이퀄(=)로 구분되고, 

다수의 변수는 &로 구분되어 전송된다.

예에서는 searchType와 searchKeyword 두개의 변수가 사용되었다.


POST 방식은 지정된 방식에 의해 인코딩한 다음 서버로 전송되는 것으로

브라우저의 주소 입력란에 내용이 나타나지 않는다. 

일정한 크기 이상의 데이터를 전송할 때, 

GET은 내용이 잘릴 수 있지만 POST 방식은 모두 전송되기 때문에 

사용자가 입력하는 게시물 내용이나 첨부 파일 같이 전송되는 데이터가 많을 경우 POST방식을 사용한다.




'Java > 게시판 2' 카테고리의 다른 글

1. 게시판 확장 - 준비  (0) 2016.03.28
3. 게시판 확장 - 조회수  (5) 2016.03.28
4. 게시판 확장 - 삭제에서 숨기기로  (0) 2016.03.28
5. 게시판 확장 - 페이징  (10) 2016.03.20

조회수(BRDHIT)는 사용자가 글을 읽은 회수를 의미하며,

구현은 글 읽기가 호출되면 해당 글의 조회수를 1씩 증가(BRDHIT = BRDHIT + 1)시켜서 구현한다.

그리고, 리스트에서 그 값을 볼 수 있게 해주면 된다.




먼저, 조회수를 증가 시켜주는 다음과 같은 SQL문과 서비스를 작성한다(board2Svc.java).

<update id="updateBoard2Read" parameterType="String">
        UPDATE TBL_BOARD
              SET BRDHIT = BRDHIT + 1
         WHERE BRDNO=#{brdno}
</update>

Board2.xml

public void updateBoard2Read(String param) throws Exception {
    sqlSession.update("updateBoard2Read", param);
}

board2Svc.java

이 서비스(SQL)를 아래와 같이 글 읽기 컨트롤에서 호출하면 된다.

@RequestMapping(value = "/board2Read")
public String boardSave(HttpServletRequest request, ModelMap modelMap) throws Exception {
    String brdno = request.getParameter("brdno");
       
    boardSvc.updateBoard2Read(brdno);
    boardVO boardInfo = boardSvc.selectBoardOne(brdno);
    modelMap.addAttribute("boardInfo", boardInfo);
       
    return "board2/boardRead";
}

board2Ctr.java

이렇게 하여 사용자가 글을 읽을 때 마다 조회수를 증가시키게 된다.

이렇게 하여 증가된 조회수는 리스트에 다음의 코드를 추가하여 볼 수 있다.

즉 SELECT문으로 조회수(BRDHIT) 값을 가져오고,

<select id="selectBoard2List" resultType="gu.board2.boardVO" parameterType="gu.common.PageVO">
    SELECT BRDNO, BRDTITLE, BRDWRITER, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE, BRDHIT
      FROM TBL_BOARD
     WHERE BRDDELETEFLAG='N'
     ORDER BY BRDNO DESC
</select>

board2.xml

사용자에게 보여 주는 JSP에서 다음과 같이 출력하면 된다.

<thead>

                                <tr>

                                          <th>번호</th>

                                          <th>제목</th>

                                          <th>등록자</th>

                                          <th>등록일</th>

                                          <th>조회수</th>

                                </tr>

                     </thead>

                     <tbody>

                                <c:forEach var="listview" items="${listview}" varStatus="status">      

                                          <c:url var="link" value="board2Read">

                                                     <c:param name="brdno" value="${listview.brdno}" />

                                          </c:url>           

                                          <tr>

                                                     <td><c:out value="${listview.brdno}"/></td>

                                                     <td><a href="${link}"><c:out value="${listview.brdtitle}"/></a></td>

                                                     <td><c:out value="${listview.brdwriter}"/></td>

                                                     <td><c:out value="${listview.brddate}"/></td>

                                                     <td><c:out value="${listview.brdhit}"/></td>

                                          </tr>

                                </c:forEach>

                     </tbody>

boardList.jsp

이상의 과정으로 조회수 기능 추가는 끝인데, 이렇게만 두면 오작동할 수 있다.

다음과 같이 글 작성(Inset)시 추가한 두 개의 필드에 대한 값을 처리해 줘야 한다.

즉, 처음 쓰는 글이니 조회수는 0, 삭제되지 않았으니 N(no)을 넣어 준다.

테이블 생성시 Default 값으로 처리하는 것이 편하지만,

개인적으로 직관적인 것을 선호해서 Default처리를 잘 하지 않는다.

<insert id="insertBoard2" parameterType="gu.board2.boardVO" >
        INSERT INTO TBL_BOARD(BRDTITLE, BRDWRITER, BRDMEMO, BRDDATE, BRDHIT, BRDDELETEFLAG)
    VALUES (#{brdtitle}, #{brdwriter}, #{brdmemo}, NOW(), 0, 'N' )
</insert>

좀 더 나은 조회수 처리는 중복 데이터 방지일 것이다.

즉, 동일한 사람이 두 번 이상 읽은 경우 한번으로 처리하는 것이다.

별도의 테이블을 이용하여 해당 글을 읽은 사람의 이름(ID)를 저장하면 된다.

이 기능은 사용자 ID가 필요한데

본 예제에서는 사용자 관련 기능을 다루지 않기 때문에 구현하지 않는다.

각자 구현해 본다면 실력 향상에 도움이 될 것이다.







데이터 베이스에 저장된 데이터는 삭제 하지 않는 것이 좋다.

차후에 발생하는 여러 가지 문제를 막기 위해 실제로는 삭제하지 않고 개발한다.

즉, 실제로는 삭제하지 않고 삭제한 것처럼 사용한다는 의미로,

삭제 여부를 확인하는 필드(BRDDELETEFLAG)를 추가하여

삭제이면 안 보이게 처리하는 방식을 사용한다.

구현 방법은 다음과 같이 Delete문을 update문으로 수정하면 된다.

즉 삭제 필드(BRDDELETEFLAG)가 N이면 사용, Y이면 삭제가 되는 것이다.

<delete id="deleteBoard2One" parameterType="String">
        UPDATE TBL_BOARD
              SET BRDDELETEFLAG='Y'
         WHERE BRDNO=#{brdno}
</delete>

Board2.xml

데이터를 이용하는 SQL문도 다음과 같이 수정해 줘야 한다.

리스트의 경우 데이터를 가져올 때

삭제 되지 않은 데이터(WHERE BRDDELETEFLAG='N')만 가져오도록 수정한다.

<select id="selectBoard2List" resultType="gu.board2.boardVO" >
        SELECT BRDNO, BRDTITLE, BRDWRITER, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE, BRDHIT
          FROM TBL_BOARD
         WHERE BRDDELETEFLAG='N'
</select>

~~ 생략 ~~
   
<update id="updateBoard2" parameterType="gu.board2.boardVO">
        UPDATE TBL_BOARD
              SET BRDTITLE=#{brdtitle}
                   , BRDWRITER=#{brdwriter}
                   , BRDMEMO=#{brdmemo}
         WHERE BRDDELETEFLAG='N'
              AND BRDNO=#{brdno}
</update>
       
<select id="selectBoard2One" parameterType="String" resultType="gu.board2.boardVO">
        SELECT BRDNO, BRDTITLE, BRDWRITER, BRDMEMO, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE
           FROM TBL_BOARD
         WHERE BRDDELETEFLAG='N'
              AND BRDNO=#{brdno}
</select>


Board2.xml

글수정과 글읽기에서도 BRDDELETEFLAG 조건을 사용해야 한다.

이상의 SQL문에서

사용자가 글을 수정하고 저장(updateBoard2) 할 때

조건절에 BRDDELETEFLAG을 사용하였다.

글 읽기에서 데이터를 가져오는 SQL(selectBoard2One)에도

조건절에서 BRDDELETEFLAG을 사용하였다.

간단하게 생각하면 리스트에서 안보이니

굳이 여기에서 처리할 필요가 없을 것 같지만

북마크 하고, 시간이 지난 뒤 다시 찾아올 수 있다.

즉 주소를 직접 입력해서 접근할 수 있고,

이때, 삭제된 데이터는 수정하거나 읽을 수 없게 해야 한다.

여기에서는 구현하지 않았지만

삭제된 데이터라는 메시지를 준다면 더욱 좋을 것이다.


'Java > 게시판 2' 카테고리의 다른 글

1. 게시판 확장 - 준비  (0) 2016.03.28
2. 게시판 확장 - 글쓰기와 수정을 하나로  (0) 2016.03.28
3. 게시판 확장 - 조회수  (5) 2016.03.28
5. 게시판 확장 - 페이징  (10) 2016.03.20

첫 번째 단계로 Spring MVC와 게시판의 기본 개념에 대한 이해를 돕기 위한 내용으로 구성하였다.

따라서, 기본 기능과 아주 짧은 코드로 작성하였다.

board1 폴더에 관련 소스가 있다.

설치가 제대로 되었다면 http://localhost:8080/board/board1List으로 실행해 볼 수 있다.


예제를 실행하기 위해 다음과 같이 게시판용 테이블(TBL_BOARD)을 생성해야 한다.

CREATE TABLE TBL_BOARD (

  BRDNO int(11) NOT NULL AUTO_INCREMENT,         -- 번호

  BRDTITLE varchar(255),                         -- 제목

  BRDWRITER varchar(20),                         -- 작성자

  BRDMEMO   varchar(4000),                       -- 내용

  BRDDATE     datetime,                          -- 작성일자

  PRIMARY KEY (BRDNO)

) ;

테이블에서 작성자(BRDWRITER), 글제목(BRDTITLE), 내용(BRDMEMO)는 웹 상에서 사용자가 값을 입력하는 필드이다. 

작성자는 회원제일 경우 아이디가 저장되겠지만 예제에서는 관련 기능이 없기 때문에 이름을 직접 입력하게 구현하였다.

글번호(BRDNO)는 MariaDB가 자동으로 고유값(AUTO_INCREMENT)을 부여하도록 하였다. 

이 글번호를 이용하여 데이터를 식별하고 처리하게 된다 (Primary Key, PK). 

작성일자(BRDDATE)는 사용자가 값을 입력하고 저장한 날짜와 시간을 의미하는 것으로

데이터 베이스에 저장(Insert)할 때 시스템에서 읽어와서 같이 저장하도록 제작한다.


테이블을 생성했으면, 이 테이블과 동일하게 boardVO클래스를 생성한다.

테이블의 내용을 boardVO클래스에 담아서 사용하기 때문에

테이블 필드명과 boardVO클래스 변수명은 동일하게 일치 시켜준다. 


public class boardVO {

    private String brdno, brdtitle, brdwriter, brdmemo, brddate;

           public String getBrdno() {

                     return brdno;

           }

           public void setBrdno(String brdno) {

                     this.brdno = brdno;

           }

            ~~ 생략 ~~

}

위 내용을 모두 입력하지 말고 이클립스에서 "private String brdno, brdtitle, brdwriter, brdmemo, brddate" 행의 아무 곳이나 선택하고

마우스 오른쪽 버튼을 눌러 컨텍스트 메뉴를 실행한다.

source > generater Getters and Setters 메뉴를 선택하여 실행하면 한번에 생성된다.


'Java > 게시판 1' 카테고리의 다른 글

2. 기본게시판 - 리스트  (0) 2016.03.28
3. 기본게시판 - 글 쓰기  (4) 2016.03.28
4. 기본게시판 - 글 읽기  (0) 2016.03.28
5. 기본게시판 - 글 수정  (2) 2016.03.28
6. 기본게시판 - 글 삭제  (0) 2016.03.28

게시판 리스트는 게시판 테이블(TBL_BOARD)의 내용을 가져와 보여주는 페이지로,

SQL문의 SELECT를 실행하여 반환 받은 데이터 집합(Record Set, Result Set, List)을 화면에 출력한다.

리스트의 MVC 개념은 게시판 첫 글에서 설명하니 다시 확인하면 좋을 것이다.


먼저, 컨트롤 파일인 board1Ctr.java 파일에 다음 내용을 입력한다.

@Controller
public class board1Ctr {
    @Autowired
    private board1Svc boardSvc;

    @RequestMapping(value = "/board1List")
    public String boardList(ModelMap modelMap) throws Exception {
        List<?> listview   = boardSvc.selectBoardList();
       
        modelMap.addAttribute("listview", listview);
        return "board1/boardList";
    }
}

board1Ctr.java

사용자가 게시판 리스트를 선택하면 board1List 컨트롤이 호출되고,

컨트롤은 서비스인 boardSvc클래스의 selectBoardList 메소드(함수)를 호출한다.

selectBoardList SELECT문을 실행하여

데이터 집합(Record Set)을 동적 집합인 List 클래스로 반환한다.

반환되는 데이터의 개수() 5개면 List의 크기도 5개가 되며, 각 행은 boardVO 형태로 구성된다.

따라서 실제로는 다음 코드에서 List<?> 대신 List<boardVO> 로 사용하는 것이 맞지만

개인적인 편의성 때문에 이렇게 사용하고 있다.

반환 받은 리스트(listview)ModelMap을 이용하여 JSP로 전달된다.

, 리스트는 listview 변수에 담기게 되고 listview라는 이름으로 넘어가게 된다.

마지막으로 화면을 구성할 HTML이 있는 boardList.jsp 파일의 위치와 이름을 반환하게 된다.


주의] 실제 명명규칙에서는 컨트롤명(board1List), 컨트롤의 함수명(boardList),

JSP 파일명(boardList.jsp)이 일치하는 것이 좋지만,

다양한 게시판을 하나에서 처리하려다 보니 조금 다르게 부여 되었으니 혼동하지 말길….

☆ 어노테이션(Annotation)

자바(스프링)에서 클래스 사용을 쉽게 하기 위해 제공하는 방법으로 다음 4가지가 빈번하게 사용된다.

@Controller: 해당 파일이 컨트롤 파일이라고 명시

@Service: 해당 파일이 서비스 파일이라고 명시

@Autowired: 자동으로 매핑 시켜주는 것으로 컨트롤에서 서비스를 찾을 때 등에 사용

@RequestMapping: 실제 액션(URL)을 의미

상세한 내용은 Spring MVC를 찾아서 익혀두길 바란다.


다음으로 컨트롤에서 호출되는 서비스는

board1Svc.java 파일에 board1Svc 클래스 메소드(selectBoardList)로 내용은 다음과 같다.

SQL문을 호출하여 처리한 후 반환하는 역할을 하는 것으로

selectBoard1List 이름(ID)을 가진 SQL 문을 찾아서 실행하게 된다.

sqlSession Spring 설정(webapp/WEB-INF/applicationContext.xml)에서

지정된 SqlSessionTemplate을 사용하기 위한 변수(인스턴스)

반환 데이터가 하나일 때는 selectOne을,

2개 이상의 집합(Result Set, List)일 때는 selectList메소드를 사용한다.

@Service
public class board1Svc {
    @Autowired
    private SqlSessionTemplate sqlSession;   

    public List<?> selectBoardList() throws Exception {
        return sqlSession.selectList("selectBoard1List");
    }
}

board1Svc.java

applicationContext.xml에 데이터 베이스에 대한 설정과

SQL문이 입력된 XML 파일 정보 등이 SqlSessionTemplate에 지정되어 있다.


서비스에서 호출되는 selectBoard1List는 SQL의 ID를 의미하고

 board1.xml파일에 있으며 다음과 같다.

resultType이 boardVO로 되어 있다. boardVO는 반환 되는 데이터의 열(column)에 대한 정보를 의미한다.

<mapper namespace="baord1">
    <select id="selectBoard1List" resultType="gu.board1.boardVO" >
        SELECT BRDNO, BRDTITLE, BRDWRITER, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE
          FROM TBL_BOARD
         ORDER BY BRDNO DESC
    </select>
</mapper>

board1.xml

이상에서 사용된 SELECT 문은 게시판 테이블(TBL_BOARD)의 모든 내용을 별다른 조건 없이 가져오고 있다.

필요한 데이터만 추출하는 것은 차차 진행할 것이다.

다만 글 번호를 큰 값 순으로 정렬(DESC)한 것은

최신 글(마지막에 작성한 글)이 리스트에서 가장 먼저 보이게 하기 위한 것이다.


[주의] 개인적인 명명 규칙으로 SQL문을 호출하는 서비스의 함수명(selectBoard1List)과

SQL의 명칭(selectBoardList)은 가급적 동일하게 부여 한다.

다만, 예제 구성상 SQL 명칭에 "1"자가 없이 작성되었다.

클래스 명들도 유사하게 명명되었는데,

앞으로 진행할 단계(board1, board2, board3 … )별로 차이가 있지만

기본적으로 같은 기능을 한다는 걸 표시하기 위한 것이다.


Mybatis의 Select 테그(<select>)는 SQL SELECT 실행을 위한 것으로

SQL의 INSERT문은 <insert> 테그를 사용해야 한다.

UPDATE와 DELETE도 동일하나

실제로는 INSERT, UPDATE, DELETE는 아무 테그나 사용해도 결과가 같다.

즉, 반환 값이 있는 것은 <select> 테그를,

없으면 <insert> 테그 등 아무 것이나 사용해도 된다.

resultType은 반환되는 클래스 타입을 나타내는 것이고

여기서 사용되지 않았지만 많이 사용되는 parameterType은

SQL문을 생성하기 위해 파라메터로 넘겨주는 클래스를 의미한다.

이외에도 많은 개념이 있지만 이 정도만 기억해 두면 될 것 같다.

SQL ID(selectBoard1List)는 프로젝트 전체에서 사용하는 고유한 이름으로

서비스에서 이 ID를 호출하여 SQL문을 실행한다.


반환되는 데이터의 구조는 boardVO.java 파일에 있으며

데이터가 저장되는 테이블(TBL_BOARD)과 유사한 구조로 되어 있다.

글번호(brdno)가 테이블에서는 숫자형이지만 boardVO에서는 문자열(String)이라는 차이가 있는데,

숫자형으로 일치시켜도 되고, 글 번호를 이용한 별도의 연산이 없어 편의를 위해 문자열로 사용하였다.


마지막으로 이렇게 추출된 데이터를

사용자에게 적절한 디자인으로 만들어 보여주는 JSP(HTML) 파일이 있다.

테그 라이브러리(taglib)인 forEach문은 주어진 데이터의 개수만큼 반복해서 실행하는 명령어로

컨트롤에서 보내온 데이터(listview)의 개수만큼 반복하여 실행한다.

즉, HTML 테이블(table) 테그의 TR을 데이터 행 개수만큼 생성하게 되고,

각 행의 열은 boardVO의 변수(brdno, brdtitle등)들을 호출하여 값을 출력(c:out)한다.


<%@ 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 http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>board1</title>
</head>
<body>
<a href="board1Form">글쓰기</a>
                   
<table border="1" style="width:600px">
    <caption>게시판</caption>
    <colgroup>
        <col width='8%' />
        <col width='*%' />
        <col width='15%' />
        <col width='15%' />
    </colgroup>
    <thead>
        <tr>
        <th>번호</th>
        <th>제목</th>
        <th>등록자</th>
        <th>등록일</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach var="listview" items="${listview}" varStatus="status">   
            <c:url var="link" value="board1Read">
                <c:param name="brdno" value="${listview.brdno}" />
            </c:url>       
            <tr>
                <td><c:out value="${listview.brdno}"/></td>
                <td><a href="${link}"><c:out value="${listview.brdtitle}"/></a></td>
                <td><c:out value="${listview.brdwriter}"/></td>
                <td><c:out value="${listview.brddate}"/></td>
           </tr>
        </c:forEach>
    </tbody>
</table>   
</body>
</html>

boardList.jsp

테그 라이브러리인 c:url은 글읽기 컨트롤(board1Read)의 주소와 글을 읽어오는데

필요한 글번호를 파라메터로하여 링크를 생성하고,

글 제목을 클릭하면 이동하도록 링크를 추가하였다.

테그 라이브러리에서 제공되는 명령어는 많지만 본 예제에서는 다음과 같이 몇 가지만 주로 사용한다.

명령어

기능

 forEach

 주어진 데이터 개수 만큼 반복

 set

 변수를 선언하고 값 할당

 out

 변수 값을 화면에 출력

 if

 조건절

 url

 URL 주소 생성


이상의 코드가 실행되면 HTML 생성되고 사용자에게 보여지게 된다. 

웹 브라우저에서 http://localhost:8080/board/board1List를 입력하여 실행 결과를 확인하자.



웹 브라우저에서 디버그 모드(F12키 실행)로 확인하면

그림과 같이 forEach 문이 있던 자리에 HTML이 생성된 것을 확인할 수 있다.




'Java > 게시판 1' 카테고리의 다른 글

1. 기본게시판 - 준비 & 시작  (5) 2016.03.28
3. 기본게시판 - 글 쓰기  (4) 2016.03.28
4. 기본게시판 - 글 읽기  (0) 2016.03.28
5. 기본게시판 - 글 수정  (2) 2016.03.28
6. 기본게시판 - 글 삭제  (0) 2016.03.28

글쓰기는 두 개의 기능으로 구성되었기 때문에 두 개의 컨트롤이 필요하다.

하나는 글쓰기 폼을 보여주고 사용자가 입력하게 하는 페이지(board1Form) 컨트롤과

사용자가 입력 후 저장 버튼을 누르면 저장하는 컨트롤(board1FormSave)이다.

먼저 글쓰기 폼은 다음 코드와 같이

사용자가 입력할 폼(HTML)이 있는 JSP 파일의 위치만 반환하면 된다.

@RequestMapping(value = "/board1Form")
public String boardForm() throws Exception {
        return "board1/boardForm";
}

board1Ctr.java

반환된 boardForm.jsp에는 다음과 같은 코드가 구성되어 있다.

Form 테그에서 action명이board1Save 컨트롤을 가르키고 있기 때문에

사용자가 저장 버튼을 누르면 글 내용을 저장하는 board1Save이 실행된다.

<%@ 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 http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>board1</title>
</head>
<body>
    <form name="form1" action="board1Save">
        <table border="1" style="width:600px">
            <caption>게시판</caption>
            <colgroup>
                <col width='15%' />
                <col width='*%' />
            </colgroup>
            <tbody>
                <tr>
                    <td>작성자</td>
                    <td><input type="text" name="brdwriter" size="20" maxlength="20"></td>
                </tr>
                <tr>
                    <td>제목</td>
                    <td><input type="text" name="brdtitle" size="70" maxlength="250"></td>
                </tr>
                <tr>
                    <td>내용</td>
                    <td><textarea name="brdmemo" rows="5" cols="60"></textarea></td>
                </tr>
            </tbody>
        </table>   
        <a href="#" onclick="form1.submit()">저장</a>
    </form>   
</body>
</html>

board1Form.jsp

글 내용을 저장하기 위해 board1Save 컨트롤이 실행된다.

boardSave 함수가 실행되면서 파라메터로 boardVO 클래스가 사용된다.

@ModelAttribute는 사용자가 입력한 내용들의 이름과 지정된 클래스 (boardVO)의 변수 명이 일치하면 자동으로 그 내용을 담아서 반환해 준다.

사용자 입력 HTML에서 텍스트 입력 상자의 이름을 확인해 보면 brdwriter(작성자), brdtitle(제목), brdmemo(글내용)으로 명명되어 있다.

이 명칭은 모두 boardVO에 선언되어 사용되는 것으로 @ModelAttribute이 자동으로 바인딩하여 넘겨 준다.

@ModelAttribute을 생략하고 클래스명만 지정해서 사용할 수 있다.

@ModelAttribute을 사용하지 않으면 request를 사용하며 하나씩 값을 받아와야 한다.

@RequestMapping(value = "/board1Save")
public String boardSave(@ModelAttribute boardVO boardInfo) throws Exception {
       
    boardSvc.insertBoard(boardInfo);
       
    return "redirect:/board1List";
}

board1Ctr.java

사용자가 입력한 내용을 boardVO로 받아와서 저장(Insert)하기 위한 서비스(insertBoard)로 넘겨 준다.
저장되고 난 후 반환 값이 JSP 파일이 아니라 redirect 이다. 즉, 글을 저장하고 난 뒤 게시판의 리스트(board1List)로 돌아가라는 의미이다.

[주의] 입력 폼의 컴포넌트(텍스트 박스들 등)명칭과 boardVO 클래스, 테이블(TBL_BOARD)의 필드명이 모두 동일하다.

개발의 편의성과 가독성을 위한 것으로 필수는 아니지만 지키는 것이 좋다.


서비스에서는 INSERT문을 실행한다.

public void insertBoard(boardVO param) throws Exception {
        sqlSession.insert("insertBoard1", param);
}

board1Svc.java

<insert id="insertBoard1" parameterType="gu.board1.boardVO" >
        INSERT INTO TBL_BOARD(BRDTITLE, BRDWRITER, BRDMEMO, BRDDATE)
        VALUES (#{brdtitle}, #{brdwriter}, #{brdmemo}, NOW() )
</insert>

board1.xml

여기에서도 서비스명(insertBoard)과 SQL 매핑 ID(insertBoard1)에 1만 다르고

다른 이름들은 같다.

사소하지만 이러한 명명 규칙이 프로젝트가 커질 수록 수정에 용이하여

때로는 중요한 역할을 하기도 한다.



'Java > 게시판 1' 카테고리의 다른 글

1. 기본게시판 - 준비 & 시작  (5) 2016.03.28
2. 기본게시판 - 리스트  (0) 2016.03.28
4. 기본게시판 - 글 읽기  (0) 2016.03.28
5. 기본게시판 - 글 수정  (2) 2016.03.28
6. 기본게시판 - 글 삭제  (0) 2016.03.28

글 읽기는 리스트에서 사용자가 선택한 글에 대한 내용을 보여주는 페이지로

다음과 같은 코드를 가진다.

먼저, 사용자가 선택한 글의 번호(brdno)을 파라메터로 받아온다.

앞서 글쓰기에서는 @ModelAttribute를 이용하여 boardVO 클래스로 받아왔지만,

여기에서는 파라메터가 한개라서 request로 받아온다.

이 파라메터는 글읽기로 넘어 올 때는 리스트에서 글 제목에 board1Read?brdno=1와 같은 url이 링크로 생성되었고

물음표(?) 뒤의 값이 파라메터로 넘어오게 되는 것이다.

데이터 베이스에서 글번호에 해당하는 내용을 가져오기 위해 서비스로 넘겨준다.

SQL이 실행되어 하나의 글(행)이 반환되면 ModelMap을 이용하여

JSP에 값을 넘겨서 적절한 모습으로 사용자에게 보여 주게 된다.

@RequestMapping(value = "/board1Read")
public String boardRead(HttpServletRequest request, ModelMap modelMap) throws Exception {
       
        String brdno = request.getParameter("brdno");
       
        boardVO boardInfo = boardSvc.selectBoardOne(brdno);
       
        modelMap.addAttribute("boardInfo", boardInfo);
       
        return "board1/boardRead";
}

board1Ctr.java

글읽기 페이지(HTML)는 다음 코드로,

글 쓰기 폼(HTML)에서 텍스트 입력 박스 위치에 c:out테그로 각각의 값을 출력해서 작성했다.

컨트롤에서 boardInfo로 넘겨줬기 때문에 JSP 파일에서도

boardInfo.brdwriter, boardInfo.brdtitle 등으로 값을 가져와 사용한다.

<%@ 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 http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>board1</title>
</head>
<body>
        <table border="1" style="width:600px">
            <caption>게시판</caption>
            <colgroup>
                <col width='15%' />
                <col width='*%' />
            </colgroup>
            <tbody>
                <tr>
                    <td>작성자</td>
                    <td><c:out value="${boardInfo.brdwriter}"/></td>
                </tr>
                <tr>
                    <td>제목</td>
                    <td><c:out value="${boardInfo.brdtitle}"/></td>
                </tr>
                <tr>
                    <td>내용</td>
                    <td><c:out value="${boardInfo.brdmemo}"/></td>
                </tr>
            </tbody>
        </table>   
        <a href="#" onclick="history.back(-1)">돌아가기</a>
        <a href="board1Delete?brdno=<c:out value="${boardInfo.brdno}"/>">삭제</a>
        <a href="board1Update?brdno=<c:out value="${boardInfo.brdno}"/>">수정</a>
</body>
</html>

다시 리스트로 돌아가기 위한 링크,

글 삭제와 글 수정을 할 수 있는 링크가 추가 되어 있다.

글 읽기, 수정, 삭제는 모두 대상 글에 대한 고유 값인 글 번호 (brdno)가 필요하기 때문에

항상파라메터로 넘어가는 것을 볼 수 있다. 

리스트에서 

글읽기로 넘어 갈 때는 board1Read?brdno=1
글삭제로 넘어 갈 때는 board1Delete?brdno=1
글수정으로 넘어 갈 때는 board1Update?brdno=1




'Java > 게시판 1' 카테고리의 다른 글

2. 기본게시판 - 리스트  (0) 2016.03.28
3. 기본게시판 - 글 쓰기  (4) 2016.03.28
5. 기본게시판 - 글 수정  (2) 2016.03.28
6. 기본게시판 - 글 삭제  (0) 2016.03.28
7. 기본게시판 - 예전의 개발 코드  (1) 2016.03.19

+ Recent posts