Full Stack Web Developer.
Syaku (샤쿠)

Java, JS (ES6+), Spring, Spring security, jQuery, Reactjs, Bootstrap.

        

07-03 04:38


Spring Boot 2 보기 Front-end 보기 DevOps 보기 Spring 3 보기 Spring Security 3 보기

#부록 스프링 검색 및 조회수 올리기 , 스프링 한글깨짐 - 스프링 프레임워크 게시판 : 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


  • lee at 2014.08.17 22:27

    정말 많은 도움이 되고 있습니다. 이제 신입개발자의 길로 들어섰는데 회사의 프로젝트를 따라가지 못해 게시판부터 차근히 만들고 있었는데 환경설정이나 개발스타일이 제가 하는 기본 스타일과 맞아서 가뭄의 단비처럼 갈증이 해소 되고 있습니다.
    죄송하지만 바쁘시겠지만 혹시 게시판 페이징 처리 부분도 올려주실수 있으신지요.
    저같은 경우에 워낙 초보라 .jsp에다 쌩 코딩으로만 해봤습니다만.
    스프링이나 마이바티스와 db단에서 한다든가 최근 개발방법론으로 접근하기는 너무나 힘들더군요.
    혹시 가능하시다면 꼭좀 부탁좀 드리겠습니다.

    • 샤쿠 syaku at 2014.08.18 09:25 신고

      페이지네비게이션은 오래된 로직이라 쉽게 소스를 찾을 수 있을 겁니다~
      게시물 총 수를 읽어와서 처리하면 되는 부분이라 해보시고~ 안되는 부분이 있으면 질문해주세요

  • hwang at 2014.12.09 00:02

    spring 공부중에 정말 많은 도움이 되고 있습니다. 감사합니다.
    따라하는 도중에 에러가 발생하여서 그런데
    데이터 베이스에 read_count column 추가 하고 해야하는 부분이죠??
    Unknown column 'read_count' in 'field list' <-- 이오류 입니다.

  • at 2014.12.09 15:19

    비밀댓글입니다

    • 샤쿠 syaku at 2014.12.09 15:42 신고

      따라했으면 큰문제는 없을 텐데요~~
      혹시 오타가 있는 지 확인해보시겠어요~
      html input 태그 name 에 뛰어쓰기라든지~
      자바 컨트롤러 소스에 request.getParameter(이름); 파라메터하는 부분에 뛰어쓰기가 들어가있던지~

  • hwang at 2014.12.09 22:28

    bbs.list.jsp 소스에서 jQuery('#form_search #sch_type option').val('${mapSearch.sch_type}');
    이부분에 #sch_type option을 value로 바꾸니까 잘 되네요~~ 이렇게요~~
    jQuery('#form_search #sch_type value').val('${mapSearch.sch_type}');
    많이 배우고 갑니다. 감사합니다~! 새해복많이 받으세요~

    • 샤쿠 syaku at 2014.12.09 22:52 신고

      문제해결능력까지 가지셨네요~ 생각해보니 그부분에 버그가 있었던 것 같아요~;;; 샘플소스 다양하게 만들다보면 짬뽕이 되는 경우가 종종있어서~ ^^ 감사합니다!!!

  • dldldldl at 2015.01.23 15:53

    한글을 입력하면 검색이안되고 꺠져보이네여 한글설정햇는데 ㅎㅎ;

  • heyjude at 2015.07.16 21:39

    이거저거 손보고 서버에 런칭했는데,
    org.apache.commons.logging.impl.Jdk14Logger] to check against the @HandlesTypes 라는 에러가 뜨네요.
    뭐가 문제일까요...

    • 샤쿠 syaku at 2015.07.17 09:20 신고

      톰캣에 있는 jar 라이브러리가 등록되지 않아서 발생하는 문제인 것 같네용~
      Java Build Path 설정에 보면 라이브러리스에서 톰캣에 jsp-api.jar 가 있는 지 확인해보세요~

      참고링크: http://stackoverflow.com/questions/12300287/spring-tomcat-handletypes-annotation-error

댓글 남기기
◀ PREV 1···949596979899100101102···313 NEXT ▶