현재 게시판에서는

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

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

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


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

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

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

영어와 숫자를 제외한 문자는 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;
    }
}




+ Recent posts