게시판 검색 기능 구현하기
1. 게시판 검색
2.
MyBatis의 동적 쿼리(1) - <sql>과 <include>
공통 부분을 <sql>로 정의하고 <include>로 포함시켜 재사용
MyBatis의 동적 쿼리(2) - <if>
if문으로 조건에 맞는 쿼리문을 실행함
그러나 단 한가지 조건이 아닌, 다른 조건도 true라면 실행됨 (if문만 쓰였기 때문에)
MyBatis의 동적 쿼리(3) - <choose> <when>
반대로 <choose><when>은 조건이 true면 그 해당되는 쿼리문만 실행함
그 뒤에 있는 조건문은 보지 않음.
* mySQL 의 와일드카드 %, _
%는 여러글자 (0~n)
_는 한 글자 (1)
ex)
'title%'은 title도 OK, title1도 OK
'title_'은 title은 X, title1은 OK
와일드카드를 사용할 땐 등호 사용 X, LIKE를 사용해야 함
MyBatis의 동적 쿼리(4) - <foreach>
where bno = 1 / (bno가 1인 경우만)
where bno in (1,2,3) / (bno가 1,2,3 인 경우)
배열인 array에 담겨있는 값을 괄호 안에 구분자( , ) 를 넣어서 쿼리문을 만듦
ex) 배열이 {1,2,3} 이면 WHERE bno IN (1,2,3)
package com.fastcampus.ch4.domain;
import org.springframework.web.util.UriComponentsBuilder;
public class SearchCondition {
private Integer page = 1;
private Integer pageSize = 10;
// private Integer offset = 0;
private String keyword = "";
private String option = "";
public SearchCondition () {}
public SearchCondition(Integer page, Integer pageSize, String keyword, String option) {
this.page = page;
this.pageSize = pageSize;
this.keyword = keyword;
this.option = option;
}
public String getQueryString(Integer page) {
// ?page=1&pageSize=10&option=T&keyword="title"
return UriComponentsBuilder.newInstance()
.queryParam("page", page)
.queryParam("pageSize", pageSize)
.queryParam("option", option)
.queryParam("keyword", keyword)
.build().toString();
}
public String getQueryString() {
return getQueryString(page);
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getOffset() {
return (page-1) * pageSize;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public String getOption() {
return option;
}
public void setOption(String option) {
this.option = option;
}
@Override
public String toString() {
return "SearchCondition{" +
"page=" + page +
", pageSize=" + pageSize +
", offset=" + getOffset() +
", keyword='" + keyword + '\'' +
", option='" + option + '\'' +
'}';
}
}
먼저 검색에 필요한 조건을 담은 SearchCondition 클래스를 생성함
@Override
public int searchResultCnt(SearchCondition sc) throws Exception {
return session.selectOne(namespace+"searchResultCnt", sc);
} // T selectOne(String statement, Object parameter)
@Override
public List<BoardDto> searchSelectPage(SearchCondition sc) throws Exception {
return session.selectList(namespace+"searchSelectPage", sc);
} // List<E> selectList(String statement, Object parameter)
BoardDaoImpl에 다음과 같이 쿼리를 실행할 메소드를 추가
@Override
public int getSearchResultCnt(SearchCondition sc) throws Exception {
return boardDao.searchResultCnt(sc);
} // 검색하여 얻은 게시물 수
@Override
public List<BoardDto> getSearchResultPage(SearchCondition sc) throws Exception {
return boardDao.searchSelectPage(sc);
} // 검색하여 얻은 게시물을 list에 담음
BoardServiceImpl에도 다음과 같이 메소드를 추가
<sql id="searchCondition">
<choose>
<when test='option=="T"'>
AND title LIKE concat('%', #{keyword}, '%')
</when>
<when test='option=="W"'>
AND writer LIKE concat('%', #{keyword}, '%')
</when>
<otherwise>
AND (title LIKE concat('%', #{keyword}, '%')
OR content LIKE concat('%', #{keyword}, '%'))
</otherwise>
</choose>
</sql>
<select id="searchSelectPage" parameterType="SearchCondition" resultType="BoardDto">
<include refid="selectFromBoard"/>
WHERE true
<include refid="searchCondition"/>
ORDER BY reg_date DESC, bno DESC
LIMIT #{offset}, #{pageSize}
</select>
<select id="searchResultCnt" parameterType="SearchCondition" resultType="int">
SELECT count(*)
FROM board
WHERE true
<include refid="searchCondition"/>
</select>
다음과 같은 쿼리문을 boardMapper.xml에 추가하였다.
중복되는 쿼리문은 따로 만들어서 간편하게 호출하여 사용할 수 있다.
@GetMapping("/list")
public String list(SearchCondition sc, Model m, HttpServletRequest request) {
if(!loginCheck(request))
return "redirect:/login/login?toURL="+request.getRequestURL(); // 로그인을 안했으면 로그인 화면으로 이동
// if(page==null) page=1;
// if(pageSize==null) pageSize=10;
try {
int totalCnt = boardService.getSearchResultCnt(sc);
m.addAttribute("totalCnt", totalCnt);
PageHandler pageHandler = new PageHandler(totalCnt, sc);
List<BoardDto> list = boardService.getSearchResultPage(sc);
m.addAttribute("list", list);
m.addAttribute("ph", pageHandler);
Instant startOfToday = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant();
m.addAttribute("startOfToday", startOfToday.toEpochMilli());
} catch (Exception e) {
e.printStackTrace();
m.addAttribute("msg", "LIST_ERR");
m.addAttribute("totalCnt", 0);
}
return "boardList"; // 로그인을 한 상태이면, 게시판 화면으로 이동
}
list 메소드와 pageHandler 클래스를 SearchCondition 객체를 사용하도록 수정한다.
SearchCondition 객체를 톰캣에서 자동으로 만들어준다(기본 생성자로)
REST API와 Ajax
1. JSON이란?
Java Script Object Notation - 자바 스크립트 객체 표기법
{ 속성명1: 속성값1, 속성명2: 속성값2, ... }
[{속성명: 속성값, ...}, { 속성명: 속성값, ...}, ...] // 객체 배열
{키1:{속성명: 속성값,...}, 키2: {속성명 : 속성값, ...}, ... } // Map
2. stringify()와 parse()
JS객체를 서버로 전송하려면, 직렬화(문자열로 변환)가 필요
서버가 보낸 데이터(JSON 문자열)를 JS객체로 변환할 때, 역직렬화가 필요
JSON.striingify() - 객체를 JSON 문자열로 변환(직렬화, JS객체->문자열)
JSON.parse() - JSON 문자열을 객체로 변환(역직렬화, 문자열->JS객체)
ex)
3. Ajax란?
Asynchronous javascript and XML - 요즘은 JSON을 주로 사용
비동기 통신으로 데이터를 주고 받기 위한 기술
웹페이지 전체(data+UI)가 아닌 일부(data)만 업데이트 가능
동기는 보낸 요청이 응답으로 돌아올 때까지 기다려야함
비동기는 요청이 끝나지 않아도 다른 요청을 할 수 있음(콜백으로 응답이 온 것을 확인함)
4. jQuery를 이용한 Ajax
%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
</head>
<body>
<h2>{name:"abc", age:10}</h2>
<button id="sendBtn" type="button">SEND</button>
<h2>Data From Server :</h2>
<div id="data"></div>
<script>
$(document).ready(function(){
let person = {name:"abc", age:10};
let person2 = {};
$("#sendBtn").click(function(){
$.ajax({
type:'POST', // 요청 메서드
url: '/ch4/send', // 요청 URI
headers : { "content-type": "application/json"}, // 요청 헤더
dataType : 'text', // 전송받을 데이터의 타입
data : JSON.stringify(person), // 서버로 전송할 데이터. stringify()로 직렬화 필요.
success : function(result){
person2 = JSON.parse(result); // 서버로부터 응답이 도착하면 호출될 함수
alert("received="+result); // result는 서버가 전송한 데이터
$("#data").html("name="+person2.name+", age="+person2.age);
},
error : function(){ alert("error") } // 에러가 발생했을 때, 호출될 함수
}); // $.ajax()
alert("the request is sent")
});
});
</script>
</body>
</html>
package com.fastcampus.ch4.controller;
import com.fastcampus.ch4.domain.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
@Controller
public class SimpleRestController {
@GetMapping("/ajax")
public String ajax() {
return "ajax";
}
@PostMapping("/send")
@ResponseBody
public Person test(@RequestBody Person p) {
System.out.println("p = " + p);
p.setName("ABC");
p.setAge(p.getAge() + 10);
return p;
}
}
5. Ajax요청과 응답 과정
JS객체를 문자열로 변환하여 POST 메소드로 요청을 보냄
jackson-databind가 문자열로 받은 데이터를 Java 객체로 만들어서 매개변수로 넘겨줌
작업을 하고 나서 객체를 반환하면 다시 jackson-databind가 문자열로 변환하여 응답을 함
@RequestBody 는 request(요청)의 body(내용)을 읽어와서 객체로 변환해주고
@ResponseBody는 response(응답)으로 jackson-databind에 의해 문자열로 변환하여 body로 보낸다.
6. @RestController
@ResponseBody 대신, 클래스에 @RestController 사용 가능
클래스 내부의 모든 메소드에 @ResponseBody가 붙게 된다.
package com.fastcampus.ch4.controller;
import com.fastcampus.ch4.domain.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
@RestController
public class SimpleRestController {
// @GetMapping("/ajax")
// public String ajax() {
// return "ajax";
// }
@PostMapping("/send")
// @ResponseBody
public Person test(@RequestBody Person p) {
System.out.println("p = " + p);
p.setName("ABC");
p.setAge(p.getAge() + 10);
return p;
}
@PostMapping("/send")
// @ResponseBody
public Person test2(@RequestBody Person p) {
System.out.println("p = " + p);
p.setName("ABC");
p.setAge(p.getAge() + 10);
return p;
}
}
7. REST란?
Roy Fielding이 제안한 웹서비스 디자인 아키텍쳐 접근 방식
프로토콜에 독립적이며, 주로 HTTP를 사용해서 구현
리소스 중심의 API 디자인 - HTTP메소드로 수행할 작업을 정의
PUT : 파일 update
DELETE : 파일 삭제
PATCH : 일부 수정
8. REST API란?
Representational State Transfer API - REST규약을 준수하는 API
REST is a set of architectural constraints, not a protocol or a standard.
API developers can implement REST in a variety of ways.
API(Application Programming Interface)
9. RESTful API 설계
출처 : 스프링의 정석 : 남궁성과 끝까지 간다
'Spring & SpringBoot > Spring' 카테고리의 다른 글
220516 Spring - Chapter 5. Spring MVC로 웹사이트 만들어보기 (0) | 2022.05.17 |
---|---|
220513 Spring - Chapter 4. MyBatis로 게시판 만들기(Part.5) (0) | 2022.05.14 |
220511 Spring - Chapter 4. MyBatis로 게시판 만들기(Part.3) (0) | 2022.05.12 |
220510 Spring - Chapter 4. MyBatis로 게시판 만들기(Part.2) (0) | 2022.05.11 |
220509 Spring - Chapter 4. MyBatis로 게시판 만들기 (0) | 2022.05.10 |