본문 바로가기

학원/복기

[Spring] @RestController - 수정

@RestController 

  • REST 기능을 제공하는 요청 처리 메소드(Restful API) 만 선언된 Controller 클래스를 Spring Bean 으로 등록하는 어노테이션
  • 요청 처리 메소드에 @ResponseBody 어노테이션을 사용하지 않아도 문자열로 응답 처리가 가능하다.
  • REST 기능을 제공하는 요청 처리 메소드는 @RequestMapping 어노테이션 대신 @GetMapping, @PostMapping, @PutMapping, @PatchMapping, @DeleteMapping 어노테이션을 사용하여 요청 페이지를 요청 처리 메소드와 매핑 처리하는 것을 권장한다.
    • 요청 방식 : GET(검색), POST(삽입), PUT(전체 변경), PATCH(부분 변경), DELETE(삭제) 등

 

 

REST 이용해 JSON으로 응답받아 처리할 수 있도록 해보자.

 

예제)

 

 

 

board.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SPRING</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<style type="text/css">
#btnDiv {
	margin: 10px;
}

#restBoardTable {
	border: 1px solid black;
	border-collapse: collapse; 
}

#restBoardTable td, #restBoardTable th {
	border: 1px solid black;
	padding: 3px;
}

.inputDiv {
	width: 240px;
	height: 80px;
	border: 2px solid black;
	background-color: gray;
	position: absolute;
	top: 50%;
	left: 50%;
	margin-top: -40px;
	margin-left: -120px;
	padding: 5px;
	z-index: 100;
	display: none;
}
</style>
</head>
<body>
	<h1>RestBoard</h1>
	<hr>
	<div id="btnDiv">
		<button type="button" id="writeBtn">글쓰기</button>
	</div>
	
	<%-- 게시글 목록을 출력하는 태그 --%>
	<div id="restBoardListDiv"></div>

	<%-- 페이지 번호를 출력하는 태그 --%>
	<div id="pageNumDiv"></div>
	
	<%-- 신규 게시글을 입력받기 위한 태그 --%>
	<div id="insertDiv" class="inputDiv">
		<table>
			<tr>
				<td>작성자</td>
				<td><input type="text" id="insertWriter" class="insert"></td>
			</tr>
			<tr>
				<td>내용</td>
				<td><input type="text" id="insertContent" class="insert"></td>
			</tr>
			<tr>
				<td colspan="2">
					<button type="button" id="insertBtn">저장</button>
					<button type="button" id="cancelInsertBtn">취소</button>
				</td>
			</tr>
		</table>
	</div>
	
	<%-- 변경 게시글을 입력받기 위한 태그 --%>
	<div id="updateDiv" class="inputDiv">
		<input type="hidden" id="updateNum">
		<table>
			<tr>
				<td>작성자</td>
				<td><input type="text" id="updateWriter" class="update"></td>
			</tr>
			<tr>
				<td>내용</td>
				<td><input type="text" id="updateContent" class="update"></td>
			</tr>
			<tr>
				<td colspan="2">
					<button type="button" id="updateBtn">변경</button>
					<button type="button" id="cancelUpdateBtn">취소</button>
				</td>
			</tr>
		</table>
	</div>
</body>
</html>

 

테이블, 시퀀스 생성

//테이블 생성
create table restboard(idx number primary key, writer varchar2(50)
    , content varchar2(100), regdate date); 

//시퀀스 생성
create sequence restboard_seq;

 

DTO 클래스 선언

@Data
public class RestBoard {
	private int idx;
	private String writer;
	private String content;
	private String regdate;
}

 

 

Mapper 생성

 

RestBoardMapper.xml

<mapper namespace="xyz.itwill10.mapper.RestBoardMapper">
	<insert id="insertRestBoard">
		<selectKey resultType="int" keyProperty="idx" order="BEFORE">
			select restboard_seq.nextval from dual
		</selectKey>
		insert into restboard values(#{idx}, #{writer}, #{content}, sysdate)
	</insert>

	<update id="updateRestBoard">
		update restboard set writer=#{writer}, content=#{content} where idx=#{idx}
	</update>
	
	<delete id="deleteBoard">
		delete from restboard where idx=#{idx}
	</delete>
	
	<select id="selectRestBoard" resultType="RestBoard">
		select idx, writer, content, regdate from restboard where idx=#{idx}
	</select>
	
	<select id="selectRestBoardCount" resultType="int">
		select count(*) from restboard
	</select>
	
	<select id="selectRestBoardList" resultType="RestBoard">
		select * from (select rownum rn, board.* from (select idx, writer, content, regdate 
			from restboard oder by idx desc) board) where rn between #{startRow} and #{endRow}
	</select>
</mapper>

 

RestBoardMapper

public interface RestBoardMapper {
	int insertRestBoard(RestBoard restBoard);
	int updateRestBoard(RestBoard restBoard);
	int deleteRestBoard(int idx);
	RestBoard selectRestBoard(int idx);
	int selectRestBoardCount();
	List<RestBoard> selectRestBoardList(Map<String, Object> map);
}

 

DAO

public interface RestBoardDAO {
	int insertRestBoard(RestBoard restBoard);
	int updateRestBoard(RestBoard restBoard);
	int deleteRestBoard(int idx);
	RestBoard selectRestBoard(int idx);
	int selectRestBoardCount();
	List<RestBoard> selectRestBoardList(Map<String, Object> map);
}


@Repository
@RequiredArgsConstructor
public class RestBoardDAOImpl implements RestBoardDAO {
	private final SqlSession sqlSession;

	@Override
	public int insertRestBoard(RestBoard restBoard) {
		return sqlSession.getMapper(RestBoardMapper.class).insertRestBoard(restBoard);
	}

	@Override
	public int updateRestBoard(RestBoard restBoard) {
		return sqlSession.getMapper(RestBoardMapper.class).updateRestBoard(restBoard);
	}

	@Override
	public int deleteRestBoard(int idx) {
		return sqlSession.getMapper(RestBoardMapper.class).deleteRestBoard(idx);
	}

	@Override
	public RestBoard selectRestBoard(int idx) {
		return sqlSession.getMapper(RestBoardMapper.class).selectRestBoard(idx);
	}

	@Override
	public int selectRestBoardCount() {
		return sqlSession.getMapper(RestBoardMapper.class).selectRestBoardCount();
	}

	@Override
	public List<RestBoard> selectRestBoardList(Map<String, Object> map) {
		return sqlSession.getMapper(RestBoardMapper.class).selectRestBoardList(map);
	}
}

 

 

Service 

 

인터페이스

public interface RestBoardService {
	void addRestBoard(RestBoard restBoard);
	void modifyRestBoard(RestBoard restBoard);
	void removeBoard(int idx);
	RestBoard getRestBoard(int idx);
	Map<String, Object> getRestBoardList(int pageNum);
}

 

서비스 클래스

@Service
@RequiredArgsConstructor
public class RestBoardServiceImpl implements RestBoardService {
	private final RestBoardDAO restBoardDAO;

	@Override
	public void addRestBoard(RestBoard restBoard) {
		restBoardDAO.insertRestBoard(restBoard);
	}

	@Override
	public void modifyRestBoard(RestBoard restBoard) {
		/*
		if(restBoardDAO.selectRestBoard(restBoard.getIdx()) == null) {
			throw new Exception("해당 게시글을 찾을 수 없습니다.");
		}
		*/
		
		restBoardDAO.updateRestBoard(restBoard);
	}

	@Override
	public void removeBoard(int idx) {
		restBoardDAO.deleteRestBoard(idx);

	}

	@Override
	public RestBoard getRestBoard(int idx) {
		return restBoardDAO.selectRestBoard(idx);
	}

	@Override
	public Map<String, Object> getRestBoardList(int pageNum) {
		int totalBoard=restBoardDAO.selectRestBoardCount();
		
		Pager pager=new Pager(pageNum, totalBoard, 5, 5);
		
		Map<String, Object> pageMap=new HashMap<String, Object>();
		pageMap.put("startRow", pager.getStartRow());
		pageMap.put("endRow", pager.getEndRow());
		
		List<RestBoard> restBoardList=restBoardDAO.selectRestBoardList(pageMap);
		
		Map<String, Object> resultMap=new HashMap<String, Object>();
		resultMap.put("restBoardList", restBoardList);
		resultMap.put("pager", pager);

		return resultMap;
	}
}

 

 

컨트롤러

 

페이지 번호를 전달받아 RESTBOARD 테이블에 저장된 게시글  페이지 번호에 출력될 게시글 목록을 검색하여 JSON 형식의 문자열로 응답하는 요청 처리 메소드를 선언해보자

 

REST 기능을 제공하는 요청 처리 메소드는 @RequestMapping 어노테이션 대신 @GetMapping, @PostMapping, @PutMapping, @PatchMapping, @DeleteMapping 어노테이션을 사용하여 요청 페이지를 요청 처리 메소드와 매핑 처리하는 것을 권장한다.

  • 요청 방식 : GET(검색), POST(삽입), PUT(전체 변경), PATCH(부분 변경), DELETE(삭제) 등 

 

RestBoardController

@RestController
@RequestMapping("/rest")
@RequiredArgsConstructor
public class RestBoardController {
	private final RestBoardService restBoardService;
	
	@GetMapping("/board_list")
	public Map<String, Object> restBoardList(@RequestParam(defaultValue = "1") int pageNum) {
		//자바스크립트의 Object 객체 형식의 문자열로 반환 
		return restBoardService.getRestBoardList(pageNum);
	}
}

 

 

Restful 기능을 제공하는 요청 처리 메소드가 정상적으로 실행되는지 확인하기 위해 Restful API 테스트 프로그램인 Advanced REST Client 크롬앱을 설치하여 사용해보자. 

 

https://www.advancedrestclient.com/

 

install

 

 

 

 

 


게시글을 전달받아 RESTBOARD 테이블에 게시글을 삽입 처리하고 실행결과를 문자열로 응답하는 요청 처리 메소드를 만들어보자

[application/json] 형식의 문자열로 전달된 게시글을 Java 객체로 제공받아 매개변수에 저장하기 위해 @RequestBody 어노테이션을 사용할 것이다.

 

 

HtmlUtils.htmlEscape(String str)

  • 매개변수로 전달받은 문자열에 저장된 HTML 태그 관련 문자를 회피문자(Escape Character)로 변환하여 반환하는 메소드
  • XSS 공격에 대한 방어를 위해 사용한다.
	@PostMapping("/board_add")
	public String restBoardAdd(@RequestBody RestBoard restBoard) {
		//htmlEscape() 메소드로 매개변수로 전달받은 문자열에 저장된 HTML 태그 관련 문자를 회피문자로 변환하여 반환
		restBoard.setContent(HtmlUtils.htmlEscape(restBoard.getContent()));
		
		restBoardService.addRestBoard(restBoard);
		return "success";
	}

 

 

 

성공

 

 

 

 

 

 


쿼리스트링으로 전달된 글번호를 매개변수로 제공받아 사용하는 경우를 보자

 

 

글번호를 전달받아 RESTBOARD 테이블에 저장된 게시글에서 해당 글번호의 게시글을 검색하여 JSON 형식의 문자열로 응답하는 요청 처리 메소드를 선언해보자

-> 질의 문자열로 전달된 글번호를 매개변수로 제공받아 사용할 것이다.

 

	@GetMapping("/board_view")
	public RestBoard restBoardView(@RequestParam int idx) {
		return restBoardService.getRestBoard(idx);
	}

 

 

 

이번엔 요청 URL 주소로 전달된 글번호를 매개변수로 제공받아 사용하는 메소드드를 선언해보자

 

요청 URL 주소로 값을 전달하기 위해서는 @GetMapping 어노테이션(@PostMapping도 가능)의 value 속성값에서 {이름} 형식으로 요청 URL 주소를 표현해야 한다.

 

요청 URL 주소로 값은 매개변수에 @PathVariable 어노테이션을 사용하여 전달값을 저장한다. 

 

@PathVariable : 요청 URL 주소로 표현된 값을 요청 처리 메소드의 매개변수에 저장하기 위한 어노테이션 

  • @GetMapping 어노테이션의 value 속성값으로 설정된 이름과 매개변수의 이름이 같도록 작성해주어야 한다.
  • 전댤값의 이름과 매개변수의 이름이 다른 경우 @PathVariable 어노테이션의 value 속성을 사용하여 요청 URL 주소로 표현하여 전달된 값을 매개변수에 저장할 수 있다.

 

	@GetMapping("/board_view/{idx}")
	public RestBoard restBoardView(@PathVariable int idx) {
		return restBoardService.getRestBoard(idx);
	}
	
	/*
	//전달값의 이름과 매개변수의 이름이 다른 경우 
	@GetMapping("/board_view/{num}")
	public RestBoard restBoardView(@PathVariable("num") int idx) {
		return restBoardService.getRestBoard(idx);
	}
	*/

 

 


게시글을 전달받아 RESTBOARD 테이블에 저장된 게시글을 변경하고 실행결과를 문자열로 응답하는 요청 처리 메소드 선언

=> [application/json] 형식의 문자열로 전달된 게시글을 Java 객체로 제공받아 매개변수에 저장하기 위해 @RequestBody 어노테이션을 사용

 

	@PutMapping("/board_modify")
	public String restBoardModify(@RequestBody RestBoard restBoard) {
		restBoardService.modifyRestBoard(restBoard);
		return "success";
	}

 

 

 


글번호를 전달받아 RESTBOARD 테이블에 저장된 게시글에서 해당 글번호의 게시글을 삭제하고 실행결과를 문자열로 응답하는 요청 처리 메소드

=> 요청 URL 주소로 전달된 글번호를 매개변수로 제공받아 사용

 

	@DeleteMapping("/board_remove/{idx}") 
	public String restBoardRemove(@PathVariable int idx) {
		restBoardService.removeBoard(idx);
		return "success";
	}

 


board.jsp

 

 

<script type="text/javascript">
	//현재 요청 페이지 번호를 저장하기 위한 전역변수
	// => 모든 함수에서 사용 가능하며 프로그램 종료 전까지 값 유지
	var page=1;

	//게시글 목록을 제공받아 출력하는 함수 호출
	boardListDisplay(page);
	
	//매개변수로 요청 페이지 번호을 전달받아 페이지 번호에 대한 게시글 목록을 JSON 형식의 
	//문자열로 제공받아 HTML 태그로 변환하여 출력하는 함수
	// => 게시글 목록을 JSON 형식의 문자열로 제공하는 Restful API를 비동기식으로 요청하여 응답받아 처리
	function boardListDisplay(pageNum) {
		page=pageNum;
		$.ajax({
			type: "get",
			url: "<c:url value="/rest/board_list"/>",
			data: {"pageNum":pageNum},		
			dataType: "json",
			success: function(result) {
				//alert(result);//[object Object] >> Map 객체가 변환되어 응답된 결과
				
				//매개변수로 제공받은 자바스크립트의 Object 객체를 HTML 태그로 변환
				if(result.restBoardList.length == 0) {//검색된 게시글이 없는 경우
					var html="<table id='restBoardTable'>";
					html+="<tr>";
					html+="<th width='800'>검색된 게시글이 없습니다.</th>";
					html+="</tr>";
					html+="</table>";
					$("#restBoardListDiv").html(html);
					return;
				}
				
				var html="<table id='restBoardTable'>";
				html+="<tr>";
				html+="<th width='50'>번호</th>";
				html+="<th width='100'>작성자</th>";
				html+="<th width='350'>내용</th>";
				html+="<th width='200'>작성일</th>";
				html+="<th width='50'>변경</th>";
				html+="<th width='50'>삭제</th>";
				html+="</tr>";
				$(result.restBoardList).each(function() {//게시글 목록(Array 객체)에 대한 반복 처리
					html+="<tr>";
					html+="<td align='center'>"+this.idx+"</td>";
					html+="<td align='center'>"+this.writer+"</td>";
					html+="<td>"+this.content+"</td>";
					html+="<td align='center'>"+this.regdate+"</td>";
					html+="<td align='center'><button type='button'>변경</button></td>";
					html+="<td align='center'><button type='button'>삭제</button></td>";
					html+="</tr>";
				});
				html+="</table>";

				$("#restBoardListDiv").html(html);
				
				//페이지 번호를 출력하는 함수 호출
				pageNumDisplay(result.pager);
			},
			error: function(xhr) {
				alert("에러코드(게시글 목록 검색) = "+xhr.stauts);
			}
		});
	}
	
	//매개변수로 페이징 처리 관련 정보(Object 객체)를 전달받아 HTML 태그로 변환하여 출력하는 함수
	function pageNumDisplay(pager) {
		var html="";
		
		if(pager.startPage > pager.blockSize) {
			html+="<a href='javascript:boardListDisplay("+pager.prevPage+");'>[이전]</a>";
		} else {
			html+="[이전]";
		}
		
		for(i = pager.startPage ; i <= pager.endPage ; i++) {
			if(pager.pageNum != i) {
				html+="<a href='javascript:boardListDisplay("+i+");'>["+i+"]</a>";
			} else {
				html+="["+i+"]";
			}
		}
		
		if(pager.endPage != pager.totalPage) {
			html+="<a href='javascript:boardListDisplay("+pager.nextPage+");'>[다음]</a>";
		} else {
			html+="[다음]";
		}
		
		$("#pageNumDiv").html(html);
	}
	
	//[글쓰기] 태그를 클릭한 경우 호출되는 이벤트 처리 함수 등록
	$("#writeBtn").click(function() {
		//변경 게시글을 입력받기 위한 태그 초기화
		$(".update").val("");//입력태그 초기화
		$("#updateDiv").hide();//태그 숨김
		
		//신규 게시글을 입력받기 위한 태그 출력
		$("#insertDiv").show();
	});
	
	//신규 게시글을 입력받기 위핸 태그에서 [저장] 태그를 클릭한 경우 호출되는 이벤트 처리 함수 등록
	// => 사용자 입력값을 반환받아 RESTBOARD 테이블에 삽입 처리하는 Restful API를 비동기식을 요청하여
	//실행결과를 제공받아 출력 처리
	$("#insertBtn").click(function() {
		var writer=$("#insertWriter").val();
		var content=$("#insertContent").val();
		
		if(writer == "") {
			alert("작성자를 입력해 주세요.");
			return;
		}
		
		if(content == "") {
			alert("내용을 입력해 주세요.");
			return;
		}
		
		$.ajax({
			type: "post",
			url: "<c:url value="/rest/board_add"/>",
			//headers : 요청정보가 저장된 리퀘스트 메세지의 머릿부(Header)를 변경하기 위한 속성
			// => 리퀘스트 메세지 몸체부에 저장되어 전달될 값의 파일형식(MimeType)을 변경
			//headers: {"contentType":"application/json"},
			//contentType : 리퀘스트 메세지 몸체부에 저장되어 전달될 값의 파일형식(MimeType)을 변경하기 위한 속성
			// => 리퀘스트 메소드 몸체부에 JSON 형식의 문자열로 값 전달
			// => 요청 처리 메소드의 매개변수에서 @RequestBody 어노테이션을 사용하여 JSON 형식의
			//문자열을 Java 객체로 전달받아 사용 - 값을 Java 객체의 필드값으로 저장되어 제공
			contentType: "application/json",
			//JSON.stringify(object) : 자바스트립트 객체를 JSON 형식의 문자값으로 변환하여 반환하는 메소드
			data: JSON.stringify({"writer":writer, "content":content}),
			dateType: "text",
			success: function(result) {
				if(result == "success") {
					//신규 게시글을 입력받기 위한 태그 초기화
					$(".insert").val("");//입력태그 초기화
					$("#insertDiv").hide();//태그 숨김
					
					//게시글 목록을 제공받아 출력하는 함수 호출
					boardListDisplay(page);
				}
			},
			error: function(xhr) {
				alert("에러코드(게시글 삽입) = "+xhr.stauts);
			}
		});
	});
	</script>

 


 

0818 변경/삭제

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

참고: https://dncjf64.tistory.com/288