> Hello World !!!

     

@syaku

#부록 스프링 검색 및 조회수 올리기 , 스프링 한글깨짐 - 스프링 프레임워크 게시판 : Spring Framework Cookie

written by Seok Kyun. Choi. 최석균


스프링 프레임워크 연재 포스팅

2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #1 STS 설치 및 스프링 프로젝트 만들기 : Spring Framework Hello, World!!!
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #2 스프링 프로젝트 만들기 : Spring Framework Create Project
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #3 스프링 MyBatis 설정하기 및 로그출력 : Spring Framework MyBatis Log4jdbc
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #4 스프링 XML , 스프링 유효성검사 : Spring Framework Hibernate Validator XML Marshaller
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #5 스프링 트랜잭션 : Spring Framework Transaction
2014/07/21 - [개발노트/Spring] - 스프링 프레임워크 게시판 #6 스프링 파일업로드 : Spring Framework FileUpload
2014/07/28 - [개발노트/Spring] - 스프링 프레임워크 게시판 #부록 스프링 검색 및 조회수 올리기 , 스프링 한글깨짐: Spring Framework Cookie


개발 환경

Mac OS X 10.9.4
JAVA 1.6
Apache Tomcat 7.x
MySQL 5.x
Spring 3.1.1
Spring Tool Suite 3.5.1
Maven 2.5.1
myBatis 3.2.7
jQuery 1.11.0

2014.07.19 Written by 최석균 (Syaku)


소스파일 : source-7.zip

부록. 게시물 검색과 스프링 쿠키를 사용한 조회수 올리기

원하는 게시물을 검색할 수 있는 UI와 기능을 추가한다.
목록 페이지에 검색 UI 소스를 추가하고, 검색 처리를 위한 jQuery 함수를 추가한다.

@소스 bbs.list.jsp

  <form id="form_search" method="get" action="./">
  <select id="sch_type" name="sch_type">
       <option value="subject" selected="selected">제목</option>
       <option value="content">내용</option>
       <option value="user_name">작성자</option>
  </select>
  <input type="text" id="sch_value" name="sch_value" />
  <button type="button" onclick="search();">검색</button>
  </form>
  <script>
       function search() {
            var sch_value = jQuery('#form_search #sch_value').val();
            if (sch_value == '') { alert('검색어를 입력하세요.'); }
            else {
                 jQuery('#form_search').submit();
            }
       }
  </script>

검색 함수는 sch_value 항목에 검색어를 입력하지 않으면 전송하지 않는 다.
검색 요청을 처리하기 위해 컨트롤러 소스를 아래와 같이 업데이트한다.

@소스 ViewController.java

@RequestMapping(value = "/", method = RequestMethod.GET)
    public String dispBbsList(HttpServletRequest request, Model model) {
    logger.info("display view BBS list");

    // 검색을 위한
    String sch_type = request.getParameter("sch_type");
    String sch_value = request.getParameter("sch_value");
    Map mapSearch = new HashMap();
    mapSearch.put("sch_type", sch_type);
    mapSearch.put("sch_value", sch_value);
    // 검색 값을 뷰에 넘겨줌
    model.addAttribute("mapSearch", mapSearch);

    List<BbsVo> list = this.bbsDao.getSelect(mapSearch);
    model.addAttribute("list", list);

    logger.info("totcal count" + list.size() );

    return "bbs.list";
}

Request Parameter 는 HttpSerletRequest 객체를 상수로 받아 사용할 수 있다. 검색 값을 HashMap 담아 MyBatis 맵퍼에 넘겨주어 결과를 받는 다.
기존에 검색기능이 없기때문에 MyBatis 맵퍼에 검색 값을 넘겨줘도 처리할 수 없다. 그래서 MyBatis 맵퍼에 검색 기능을 추가한다.

@소스 BbsDao.java

public List<BbsVo> getSelect(Map map) {
    return this.bbsMapper.select(map);
}

getSelect 메서드에 map 객체를 받을 수 있게 수정한다.

@소스 BbsMapper.java

List<BbsVo> select(Map map);

@소스 BbsMapper.xml

<select id="select" parameterType="hashmap" resultMap="bbsMap">
    SELECT  * FROM bbs
    <where>
         <!-- SQL Injection -->
         <!--
         <if test="sch_type != null and sch_value != null">
         AND ${sch_type} LIKE '%${sch_value}%'
         </if>
         -->

          <choose>
          <when test="sch_type == 'subject'">
          AND subject like CONCAT('%', #{sch_value} , '%')
          </when>
          <when test="sch_type == 'content'">
          AND content like CONCAT('%', #{sch_value} , '%')
          </when>
          <when test="sch_type == 'user_name'">
          AND user_name like CONCAT('%', #{sch_value} , '%')
          </when>
          </choose>

    </where>
</select>

마지막으로 select 맵퍼를 위와 같이 수정한다. 여기서 주의해야할 점은 SQL Injection 부분이다. SQL Injection 처럼 쉽게 코딩할 수 있지만, Sql 보안 취약점이 발생한다. 검색어를 입력하지 않고 쿼리문을 입력하면 그대로 받아들이는 문제가 있으니 꼭 문자열을 사용할 때는 $ 보다는 #을 사용하여 코딩하는 것이 좋다.

그래서 choose 구문을 사용하여 동적 쿼리문을 완성하였다. choose 는 swutch 와 유사한 역활을 한다.
CONCAT 는 MySQL 에서 사용하는 구문이고 Oracle 에서는 || 와 같은 기능을 한다.

이제 검색어를 입력하고 게시물이 검색되는 지 확인한다.

작성자중에 3인 게시물을 검색한 결과이다. 그런데 검색 후 입력한 값들이 모두 사라졌다.
원래 입력한 값들을 적용해보자.

bbs.list.jsp 파일을 열어 아래와 같이 수정한다.

@소스 bbs.list.jsp

<form id="form_search" method="get" action="./">
<select id="sch_type" name="sch_type">
   <option value="subject" selected="selected">제목</option>
   <option value="content">내용</option>
   <option value="user_name">작성자</option>
</select>
<input type="text" id="sch_value" name="sch_value" value="${mapSearch.sch_value}"/>
<button type="button" onclick="search();">검색</button>
</form>
<script>
   function search() {
        var sch_value = jQuery('#form_search #sch_value').val();
        if (sch_value == '') { alert('검색어를 입력하세요.'); }
        else {
             jQuery('#form_search').submit();
        }
   }
   //jQuery('#form_search #sch_type option').val('${mapSearch.sch_type}');
   jQuery('#form_search #sch_type value').val('${mapSearch.sch_type}'); // 댓글 제보로 수정...(댓글 참조)
</script>

추가된 소스를 알기쉽게 빨간색으로 표시하였다. 이전에 ViewController 에서 mapSearch 데이터를 뷰에 넘기는 작업을 했었다. 그래서 mapSearch.sch_value 변수를 사용하여 검색어를 노출한 것이다. 하지만 selectbox 는 형식이 틀리기 때문에 jQuery 를 이용하여 선택한 값을 표시할 수 있다.

스프링 쿠키를 사용한 게시물 조회수 올리기

게시물을 열람하고 열람한 수를 기록하는 작업이다. 하지만 중복적으로 열람하게 되면 무수한 조회수가 올라가기 때문에 쿠키를 이용하여 중복된 조회수를 막는 작업까지 같이하겠다. 여기서 핵심은 게시물 조회수 올리기가 아니라 스프링에서 쿠키를 어떻게 사용하는 지 방법을 이해하기 위한 작업이다.

세션은 쿠키보다 보안이 좋지만 서버 자원을 낭비하기 때문에 보안이 중요하지 않는 자료는 쿠키를 사용하는 것이 바람직하다. 쿠키는 로컬에서 관리되고, 세션은 서버에서 관리된다.

테이블 항목에 조회가 없기 때문에 조회 항목을 추가하고, MyBatis 에도 항목을 모두 추가한다. 조회 항목명은 read_count 이고 타입은 BIGINT 나 INT 로 하면된다.

@소스 BbsVo.java

private Integer read_count;

public Integer getRead_count() {
     return this.read_count;
}
public void setRead_count(Integer read_count) {
     this.read_count = read_count;
}

VO에 read_count 를 추가하고 MyBatis 맵퍼에 조회 항목을 추가하고 조회수를 올려주는 update 문도 추가한다.

@소스 BbsMapper.xml

<resultMap id="bbsMap" type="com.syaku.bbs.dao.BbsVo">
생략 ...
<result property="read_count"       column="read_count" />
</resultMap>

<update id="updateReadCount">
UPDATE bbs SET read_count = IFNULL(read_count,0) + 1 WHERE idx = #{idx} LIMIT 1
</update>

마지막으로 맵퍼 인터페이스와 DAO에 updateReadCount 를 추가한다.

스프링은 DAO를 Service 라는 이노테이션을 사용한다. 그래서 헤깔릴 경우 DAO 라고 하기보다 Service 라고 명명하는 것도 좋다. 예) BbsService

BbsMapper.java 파일을 열어 void updateReadCount(int idx) 를 추가하고 DAO 도 아래와 같이 추가한다.

@소스 BbsDao.java

public void updateReadCount(int idx) {
   this.bbsMapper.updateReadCount(idx);
}

bbs.view.jsp 파일을 열어 조회항목을 추가하고 조회수가 노출되는 지 확인한다.

<p>조회수 : ${object.read_count}</p>

이제 컨트롤러에 조회수를 올려주는 기능을 프로그램한다. 조회수 중복을 방지하기 위해 쿠키에는 게시물 번호를 기록한다. 쿠키를 불러오고 저장하는 소스 코드가 좀 길어, 반복적으로 사용할 경우 클래스를 만들어 사용하는 것이 좋다.

@소스 ViewController.java

@RequestMapping("/{idx}")
public String dispBbsView(HttpServletResponse response, HttpServletRequest request, @PathVariable int idx, Model model) {
     logger.info("display view BBS view idx = {}", idx);

     // 저장된 쿠키 불러오기
     Cookie cookies[] = request.getCookies();
     Map mapCookie = new HashMap();
    if(request.getCookies() != null){
      for (int i = 0; i < cookies.length; i++) {
        Cookie obj = cookies[i];
        mapCookie.put(obj.getName(),obj.getValue());
      }
    }

    // 저장된 쿠키중에 read_count 만 불러오기
     String cookie_read_count = (String) mapCookie.get("read_count");
     // 저장될 새로운 쿠키값 생성
    String new_cookie_read_count = "|" + idx;

    // 저장된 쿠키에 새로운 쿠키값이 존재하는 지 검사
     if ( StringUtils.indexOfIgnoreCase(cookie_read_count, new_cookie_read_count) == -1 ) {
          // 없을 경우 쿠키 생성
          Cookie cookie = new Cookie("read_count", cookie_read_count + new_cookie_read_count);
          //cookie.setMaxAge(1000); // 초단위
          response.addCookie(cookie);

          // 조회수 업데이트
          this.bbsDao.updateReadCount(idx);
     }

     BbsVo object = this.bbsDao.getSelectOne(idx);

     model.addAttribute("object", object);
     return "bbs.view";
}

쿠키를 사용하기 위해 response, request 가 필요하다. 저장할때는 response 를 불러올때는 request 사용한다.

우선 저장된 쿠키를 가져와서 현재 게시물 idx 를 포함한 쿠키와 비교하여 존재하지 않을 경우 기존에 쿠키와 현재 idx를 포함한 쿠키로 새로운 쿠키를 생성하여 저장한다. 그리고 조회수를 업데이트한다. 현재 게시물 idx를 포함한 쿠키라면 조회수를 업데이트하지 않게 된다.

스프링에서 쿠키사용은 기본 자바 프로그램과 다른 점이 없는 것 같다. 세션 또한 특별히 다른점이 없다.

HttpSession session = request.getSession(); 호출하여 사용하면 된다.

한글깨짐 해결

한글이 깨질 경우에 아래의 작업을 모두 진행하면 된다.

jsp(뷰) 파일 언어셋
jsp 파일의 언어셋을 utf-8로 변경한다. STS 소스 편집 화면에서 메뉴를 활성화 시킨다.


Preferences... 메뉴를 선택한다. 그리고 아래와 같이 UTF-8로 변경하고 적용한다.


web.xml 언어셋

@소스 web.xml

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

server.xml 언어셋
Apache Tomcat을 사용할 경우 conf 폴더 아래 server.xml 열어 수정하고, STS 기본 서버를 사용할 경우 Server 폴더 아래 server.xml 파일을 열어 저장한다.

<Connector URIEncoding="UTF-8" (생략).../>

Connector 설정에 URIEncoding="UTF-8" 을 추가하면 된다.


posted syaku blog

Syaku Blog by Seok Kyun. Choi. 최석균.

http://syaku.tistory.com