JSP 강의 Day07

주세환·2023년 4월 7일
0

JSP

목록 보기
7/16

Api

준비단계

pom.xml

<dependency>
	<groupId>com.google.code.gson</groupId>
	<artifactId>gson</artifactId>
	<version>2.10</version>
</dependency>

pom.xml에 gson코드를 추가하고


restcontroller 생성

restcontroller 패키지를 생성하여 컨트롤러를 만든다.

@WebServlet(urlPatterns = {"/api/board/select.json"})
public class RestBoardSelectController extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private Gson gson = new Gson(); // 라이브러리를 이용한 객체 생성
	
    public RestBoardSelectController() {
        super();
    }
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 전달받는 문자로 된 페이지 정보
		String page = request.getParameter("page");
		
		// 10개씩 가져오기 위한 시작위치와 종료위치 계산 
		int start = Integer.parseInt(page)*10-9;// 페이지네이션 시작
		int end = Integer.parseInt(page)*10;	// 페이지네이션 종료
		
		// mapper를 이용한 결과 조회
		List<Board> list = MybatisContext.getSqlSession().getMapper(BoardMapper.class).selectBoardListPage(start,end);
		
		// 전송하는 데이터 타입 설정 (json 문서) <= text/html을
		response.setContentType("application/json");
		
		// list 객체를 json으로 변환
		String jsonString = gson.toJson(list);
		
		// 호출된 곳으로 값 전송
		PrintWriter out = response.getWriter();
		out.print(jsonString);
		out.flush();
	}
}

RestBoardController.java이다.
주석으로 각 줄의 설명을 적어두었다.


postman 설치 및 세팅

오늘은 postman을 사용할 것이다.

Postman 이란?

API 개발을 빠르고 쉽게, 개발된 API 테스트
팀원들간 공유 기능
API를 만들수도,
공유할수도, 테스트에 문서화까지 가능하며,
API로 리퀘스트해서 나오는 응답까지 확인할 수 있는 툴

postman 다운로드

위 링크에서 postman을 다운받자.

좌측상단의 + 버튼을 눌러 게시판관련, 회원관련을 생성한다 (폴더개념)

각 폴더의 ...을 눌러 request를 추가하자.


연동

게시글 목록

게시글목록을 확인할 것이니 URL에 링크를 작성하자.

자바에서 서버를 start 하고, Query Params에 키와 값을 입력하자.
페이지를 지정하였으니 키에는 page, 값에는 페이지 수를 입력한다.
아래에 코드가 생성되면 성공이다.

// list 객체를 json으로 변환
Map<String, Object> map = new HashMap<>();
map.put("list", list);
map.put("pages", (cnt-1) / 10 + 1);
String jsonString = gson.toJson(map);

RestBoardSelectController를 위처럼 수정하자.

이렇게 페이지에 해당하는 게시글만 나오게 된다.


연동

조회수 증가

// 조회수 증가 ( 글번호가 오면 해당 글번호만 조회수 1 증가)
@Update({
	" UPDATE board SET hit=hit+1 WHERE no=#{no} "
})
public int updateBoardHit( @Param("no") long no );

우선 mapper를 추가하고

// 변경 PUT
@WebServlet(urlPatterns = {"/api/board/updatehit.json"})
public class RestBoardUpdateHitController extends HttpServlet{
	private static final long serialVersionUID = 1L;
	private Gson gson = new Gson(); // 라이브러리를 이용한 객체 생성
	
	@Override
	protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		// 글번호 받기
		String strNo = request.getParameter("no");		
		// 형변환
		Long no = Long.parseLong(strNo);
		// 결과받기
		int ret = MybatisContext.getSqlSession().getMapper(BoardMapper.class).updateBoardHit(no);
		
		// 전달할 값을 map타입으로 설정
		Map<String, Integer> map = new HashMap<>();
		map.put("result", ret);		
		
		response.setContentType("application/json");
		String res = gson.toJson(map);
		PrintWriter out = response.getWriter();
		out.print(res);
		out.flush();
	}
}

restcontroller 패키지에 RestBoardUpdateHitController.java를 생성한다.

postman에서 put으로 게시글 조회수 증가를 생성하고 위처럼 입력하면

글번호 36 글의 조회수가 1 늘어났다.


데이터 확인

@WebServlet(urlPatterns = {"/board/select1.do"})
public class BoardSelectController1 extends HttpServlet {
	private static final long serialVersionUID = 1L;

    public BoardSelectController1() {
        super();
    }
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.getRequestDispatcher("/WEB-INF/board_select1.jsp").forward(request, response);
		
	}
}    

BoardSelectController1.java 를 생성하고

<body>
	<div class="container">
		
		<table class="table table-hover">
  			<thead>
    			<tr>
					<th scope="col">글번호</th>
					<th scope="col">제목</th>
					<th scope="col">작성자</th>
					<th scope="col">조회수</th>
					<th scope="col">날짜</th>
			    </tr>
  			</thead>
			<tbody>
				<tr>
					<td scope="row">${obj.no}</td>
					<td>${obj.title}</td>
					<td>${obj.writer}</td>
					<td>${obj.hit}</td>
					<td>${obj.regdate}</td>
				</tr>
			</tbody>
		</table>
		
		<div class="row justify-content-center">
			<div class="col-4">
				<ul id="pagination-demo" class="pagination"></ul>
			</div>
		</div>
	</div>
	
	<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.5/axios.min.js"></script>
	<script src="${pageContext.request.contextPath}/resources/js/jquery-3.6.4.min.js"></script>
	<script src="${pageContext.request.contextPath}/resources/js/jquery.twbsPagination.min.js"></script>
	<script>
	async function ajaxBoardList(page){
		// 같은 서버내에서는 http://127.0.0.1:8080/web02 생략가능
		const url = '${pageContext.request.contextPath}/api/board/select.json?page=' + page;
		const headers = {"Content-Type":"application/json"};
		
		/*
		axios.get(url, {headers}).then(function (response){
			console.log(response);
			console.log('A');
		});
		console.log('B'); // 순서보장 X
		*/
		
		const response = await axios.get(url, {headers}); // 완료될 때까지 다음 명령 X
		console.log(response); // 순서보장 O
	};
	
	ajaxBoardList(1); // 페이지 번호
	
	
	$(function(){
		
	});
	</script>

</body>

board_select.jsp를 복사하여 board_select1.jsp를 생성하고 위처럼 수정한다.

http://127.0.0.1:8080/web02/board/select1.do 에서 확인하면 해당 페이지에 대한 정보가 나온다.

const {data} = await axios.get(url, {headers});
console.log(data); 로 변경하면

data만 나온다.


<body>
	...
			<tbody id="tbody">
			</tbody>
		</table>
		
		<div class="row justify-content-center">
			<div class="col-4">
				<ul id="pagination-demo" class="pagination"></ul>
			</div>
		</div>
	</div>
	
	<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.5/axios.min.js"></script>
	<script src="${pageContext.request.contextPath}/resources/js/jquery-3.6.4.min.js"></script>
	<script src="${pageContext.request.contextPath}/resources/js/jquery.twbsPagination.min.js"></script>
	<script>
	async function ajaxBoardList(page){
		// 같은 서버내에서는 http://127.0.0.1:8080/web02 생략가능
		const url = '${pageContext.request.contextPath}/api/board/select.json?page=' + page;
		const headers = {"Content-Type":"application/json"};		
		const {data} = await axios.get(url, {headers}); // 완료될 때까지 다음 명령 X
		console.log( data ); 
		
		const tbody = document.getElementById('tbody');
        tbody.innerHTML = '';
		for (let tmp of data.list) {
			console.log(tmp.no, tmp.title, tmp.writer);
			tbody.innerHTML +=
				`<tr>` + 
					'<td scope="row">' + tmp.no + '</td>' +
					'<td>' + tmp.title + '</td>' +
					'<td>' + tmp.writer + '</td>' +
					'<td>' + tmp.hit + '</td>' +
					'<td>' + tmp.regdate + '</td>' +
				`</tr>`;
		}
	};
	
	ajaxBoardList(1); // 페이지 번호
	
	
	$(function(){
		
	});
	</script>

</body>

이렇게 수정해보자.

사진처럼 나온다면 성공. undefined는 글을 등록할 때 제목과 작성자를 적지 않았던 글들이다.


$(function(){
	$('#pagination-demo').twbsPagination({
		  totalPages	: 5,
		  visiblePages	: 10,
		  first 		: '≪',
		  last 			: '≫',
		  prev 			: '<',
		  next 			: '>',
		  onPageClick	: function (event, page) {
				ajaxBoardList(page);
		  }
	});	
});
</script>

function에 위처럼 추가한다.

아래 페이지 넘어가는 버튼이 생기고, 다른 페이지로 넘어갈 때마다 콘솔에 해당 페이지의 정보가 출력된다.

하지만 아래 페이지 버튼의 수는 임의로 설정한 것이다. 페이지수를 게시글 수에 맞게 바꿔보자.

<script>
$(function(){

	// 1. restful 호출해서 목록을 받아오는 것.
	async function ajaxBoardList(page){
		// 같은 서버내에서는 http://127.0.0.1:8080/web02 생략가능
		const url = '${pageContext.request.contextPath}/api/board/select.json?page=' + page;
		const headers = {"Content-Type":"application/json"};		
		const {data} = await axios.get(url, {headers}); // 완료될 때까지 다음 명령 X
		console.log( data ); 
		
		// 2. 함수를 pages 정보를 넘겨서 생성시킴
		initPagination( Number(data.pages) );
		
		const tbody = document.getElementById('tbody');
		tbody.innerHTML = '';
		for (let tmp of data.list) {
			console.log(tmp.no, tmp.title, tmp.writer);
			tbody.innerHTML +=
				`<tr>` + 
					'<td scope="row">' + tmp.no + '</td>' +
					'<td>' + tmp.title + '</td>' +
					'<td>' + tmp.writer + '</td>' +
					'<td>' + tmp.hit + '</td>' +
					'<td>' + tmp.regdate + '</td>' +
				`</tr>`;
		}
	};
	
	ajaxBoardList(1); // 페이지 번호
	
	// 3. 페이지네이션 생성하는 함수
	function initPagination( pages ) {
		$('#pagination-demo').twbsPagination({
			  totalPages	: pages,
			  visiblePages	: 10,
			  first 		: '≪',
			  last 			: '≫',
			  prev 			: '<',
			  next 			: '>',
			  onPageClick	: function (event, page) {
				  // 1번 함수호출
				  ajaxBoardList(page);
			  }
		});	
	};
});
</script>

script 안의 함수를 수정해보자.

게시글이 하나밖에 없는 4번 페이지 모습이다. 비어있는 5번페이지가 사라졌다.


글 제목을 눌러 조회수 증가

<tbody>
	<c:forEach var="obj" items="${list}">
	<tr>
		<td scope="row">${obj.no}</td>
		<td><a href="selectone.do?no=${obj.no}">${obj.title}</a></td>
		<td>${obj.writer}</td>
		<td>${obj.hit}</td>
		<td>${obj.regdate}</td>
	</tr>
	</c:forEach>
</tbody>

...

<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.5/axios.min.js"></script>
	<script src="${pageContext.request.contextPath}/resources/js/jquery-3.6.4.min.js"></script>
	<script src="${pageContext.request.contextPath}/resources/js/jquery.twbsPagination.min.js"></script>
	<script>
	async function ajaxUpdateHit(no){
		// 1. restful을 이용해서 조회수를 증가
		const url = '${pageContext.request.contextPath}/api/board/updatehit.json?no=' + no;
		const headers = {"Content-Type":"application/json"};
		
		//await axios.get(url, {headers});
		//await axios.post(url, body, {headers});
		//await axios.put(url, body, {headers});
		//await axios.delete(url, {headers:{ }, data:{ }});
		const { data } = await axios.put(url, {}, {headers});
		console.log(data);
		if(data.result === 1) {
			// 2. 상세화면페이지로 전환	
			window.location.href='selectone.do?no=' + no;
		}
	}

board_select.jsp를 수정한다.

35번을 누르면

404 에러가 뜨고

조회수가 1 증가하였다.

이제 selectone을 만들어보자.


// 1개 조회
@Select({
	" SELECT b.* FROM board b WHERE no = #{no} "
})
public Board selectBoardOne(@Param("no") long no);

BoardMapper.java에 추가

@WebServlet(urlPatterns = {"/board/selectone.do"})
public class BoardSelectOneController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 1. 주소창의 no값 가져오기
		String no = request.getParameter("no");
		// 2. no값이 없으면 목록으로 보내기
		if( no == null ) {
			response.sendRedirect("select.do");
			return; // 메소드 종료시키기
		}
		// 3. no값을 이용해서 mapper호출 후 결과 받기
		Board obj = MybatisContext.getSqlSession().getMapper(BoardMapper.class).selectBoardOne( Long.parseLong(no));    
		
		// 4. view로 전달
		request.setAttribute("obj", obj);
		
		// 5. view 표시
		request.getRequestDispatcher("/WEB-INF/board_selectone.jsp").forward(request, response);
	}
}

BoardSelectOneController를 생성하고 작성

<%@ 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 charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>게시판내용</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/bootstrap.min.css" />
</head>

<body>
	<div class="container">
		<div style="width:700px; border:1px solid #cccccc; margin :0px auto;padding:40px;">
			<div class="mb-3 row">
		    	<label for="staticEmail" class="col-sm-2 col-form-label">글번호</label>
		    	<div class="col-sm-10">
		      		<input type="text" class="form-control" value="${obj.no}" readonly />
		    	</div>
		  	</div>
		  
		  	<div class="mb-3 row">
		    	<label for="inputPassword" class="col-sm-2 col-form-label">제목</label>
		    	<div class="col-sm-10">
		      		${obj.title}
		    	</div>
		  	</div>
		  	
		  	<div class="mb-3 row">
		    	<label for="inputPassword" class="col-sm-2 col-form-label">내용</label>
		    	<div class="col-sm-10">
		      		${obj.content}
		    	</div>
		  	</div>
		  	
		  	<div class="mb-3 row">
		    	<label for="inputPassword" class="col-sm-2 col-form-label">작성자</label>
		    	<div class="col-sm-10">
		      		${obj.writer}
		    	</div>
		  	</div>
		  	
		  	<div class="mb-3 row">
		    	<label for="inputPassword" class="col-sm-2 col-form-label">조회수</label>
		    	<div class="col-sm-10">
		      		${obj.hit}
		    	</div>
		  	</div>
		  	
		  	<div class="mb-3 row">
		    	<label for="inputPassword" class="col-sm-2 col-form-label">날짜</label>
		    	<div class="col-sm-10">
		      		${obj.regdate}
		    	</div>
		  	</div>
		  	
		  	<div class="mb-3 row">
		  		<div class="col-sm-10">
		    	<a class="btn btn-primary" href="select.do">목록으로</a>
		    	<button class="btn btn-primary">글수정</button>
		    	<button class="btn btn-primary">글삭제</button>
		    	<button class="btn btn-primary">이전글</button>
		    	<button class="btn btn-primary">다음글</button>
		    	</div>
		  	</div>
		  	
		</div> 
	</div>	 
	
	<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.5/axios.min.js"></script>
	<script src="${pageContext.request.contextPath}/resources/js/jquery-3.6.4.min.js"></script>

	<script>

	</script>

</body>
</html>

board_selectone.jsp도 만들어준다.

36번 제목이 a인 글을 누르면

그 글의 정보가 나오면서 조회수가 1이 올라간다.

목록으로 버튼을 누르면 다시 목록으로 오는 모습을 볼 수 있다.


이전글, 다음글

// 다음글
@Select({
	" SELECT NVL(MIN(no),0) FROM board WHERE no > #{no}"
})
public long nextBoardOne(@Param("no") long no);

// 이전글
@Select({
	" SELECT NVL(MAX(no),0) FROM board WHERE no < #{no}"
})
public long prevBoardOne(@Param("no") long no);

BoardMapper에 추가해준다.

// 3.1 이전글, 다음글
long next = MybatisContext.getSqlSession().getMapper(BoardMapper.class).nextBoardOne(Long.parseLong(no));
long prev = MybatisContext.getSqlSession().getMapper(BoardMapper.class).prevBoardOne(Long.parseLong(no));
		
// 4. view로 전달
request.setAttribute("next", next);
request.setAttribute("prev", prev);

BoardSelectOneController에 위 코드를 추가한다.

<a href="#" class="btn btn-primary" onclick="prevBoardOne('${prev}')">이전글</a>
<a href="#" class="btn btn-primary" onclick="prevBoardOne('${prev}')">이전글</a>

...

<script>

async function prevBoardOne(no) {
	const url = '${pageContext.request.contextPath}/api/board/updatehit.json?no=' + no;
	const headers = {"Content-Type":"application/json"};
	const { data } = await axios.put(url, {}, {headers});
	console.log(data);
	if(data.result === 1) {
		// 2. 상세화면페이지로 전환	
		window.location.href='selectone.do?no=' + no;
	}
}	
</script>

을 추가하면

31에서 다음글을 눌렀을 때

다음글인 32로 이동한다.


<script>
async function prevBoardOne(no) {
	if(Number(no) === 0) {
		alert('이동할 수 없습니다.');
		return false;
	}

board_selectone.jsp의 스크립트에 위 조건문을 추가한다.

마지막글인 36번에서 다음글을 누르면

이동할 수 없다는 알림이 뜬다.

0개의 댓글