REST (JSON형식의 텍스트 데이터로 응답)

woom·2023년 3월 6일
0

Framework

목록 보기
19/20
post-thumbnail

🌼 REST

  • REST(Representational State Transfer) : 자원(Resource)의 표현(Representation)에 의한 상태(State)를 전달(Transfer)하는 것을 의미

    • 페이지 요청에 대한 처리결과를 클라이언트에게 XML 이나 JSON 형식의 텍스트 데이타로 응답
    • Restful API : REST 기능을 사용하여 두 컴퓨터의 시스템이 안전하게 값을 주고 받기 위한 프로그램
    • 스마트기기의 프로그램(앱) 정보를 전달받아 저장하거나 검색결과를 제공하여 출력하기 위해 사용

📕 RestfulController

  • @ResponseBody : 요청 처리 메소드의 반환값(문자열)을 리스폰즈 메세지 몸체부에 저장하여 클라이언트에게 직접 텍스트 데이타로 응답되도록 설정하는 어노테이션
    • 반환되는 문자열을 ViewResolver 객체를 사용하여 뷰로 변환하여 응답하지 않고 리스폰즈 메세지 몸체부에 저장하여 Front Controller가 직접 응답 처리
    • Java 객체는 리스폰즈 메세지 몸체부에 저장 불가능하므로 응답 불가능
  • @ResponseBody 어노테이션을 사용하여 요청 처리 메소드의 반환값(Member 객체)을 리스폰즈 메세지의 몸체부에 저장하여 텍스트 데이타의 회원정보를 클라이언트에게 전달하여 응답 처리

    • 문제점 : 리스폰즈 메세지의 몸체부에 Java 객체를 저장 불가능 → 에러 발생(406)
    • 해결법 : 반환되는 Java 객체를 텍스트 데이타(XML 또는 JSON)의 문자열로 자동 변환되도록 설정하여 응답 처리 가능
    • jackson-databind 라이브러리를 프로젝트에 빌드 처리하면 자동으로 Java 객체가 JSON형식의 텍스트 데이타로 변환하여 반환 (메이븐 : pom.xml)
    • @ResponseBody 어노테이션을 사용하여 요청 처리 메소드의 반환값(List 객체)을 리스폰즈 메세지의 몸체부에 저장하여 텍스트 데이타의 회원목록을 클라이언트에게 전달하여 응답 처리
    • jackson-databind 라이브러리에 의해 List/Map 객체가 JavaScript Array 객체로 변환되어 응답
  • 모든 전달값이 Map 객체의 엔트리에 저장되어 매개변수로 제공받아 사용 가능
    • 모든 전달값을 Map 객체로 제공받기 위해 반드시 @RequestParam 어노테이션 사용
  • @RequestBody : 리퀘스트 메세지 몸체부에 저장되어 모든 전달값을 문자열로 제공받기 위한 어노테이션
    • POST, PUT, PATCH, DELETE 등의 요청방식에 의해 리퀘스트 메세지 몸체부에 저장되어 전달된 모든 값을 문자열로 제공받아 사용 가능
    • GET 방식은 리퀘스트 메세지 몸체부가 비어 있으므로 @RequestBody 어노테이션 사용 불가능
    • 리퀘스트 메세지 몸체부에 저장되어 전달된 JSON 형식(application/json)의 값은 매개변수에 Java 객체로 제공받아 사용 가능 (전달값은 객체 필드에 저장)

package xyz.itwill10.controller;

@Controller
public class RestfulController {
	@RequestMapping(value = "/rest", method = RequestMethod.GET)
	public String rest() {
		return "rest/input";
	}
	
	/*
	//@RequestParam 어노테이션을 사용하여 하나의 전달값을 하나의 매개변수에 저장받아 사용
	@RequestMapping(value = "/rest", method = RequestMethod.POST)
	public String rest(@RequestParam String id, @RequestParam String name, Model model) {
		model.addAttribute("id", id);
		model.addAttribute("name", name);
		//반환되는 문자열(ViewName)을 이용하여 ViewResolver 객체가 뷰로 완성하여 응답 처리
		return "rest/output";
	}
	*/
	
	//@ResponseBody : 요청 처리 메소드의 반환값(문자열)을 리스폰즈 메세지 몸체부에 저장하여
	//클라이언트에게 직접 텍스트 데이타로 응답되도록 설정하는 어노테이션
	// => 반환되는 문자열을 ViewResolver 객체를 사용하여 뷰로 변환하여 응답하지 않고 리스폰즈
	//메세지 몸체부에 저장하여 Front Controller가 직접 응답 처리
	// => Java 객체는 리스폰즈 메세지 몸체부에 저장 불가능하므로 응답 불가능
	//@RequestBody : 리퀘스트 메세지 몸체부에 저장되어 모든 전달값을 문자열로 제공받기 위한 어노테이션
	// => POST, PUT, PATCH, DELETE 등의 요청방식에 의해 리퀘스트 메세지 몸체부에 저장되어 전달된 
	//모든 값을 문자열로 제공받아 사용 가능 
	// => GET 방식은 리퀘스트 메세지 몸체부가 비어 있으므로 @RequestBody 어노테이션 사용 불가능
	// => 리퀘스트 메세지 몸체부에 저장되어 전달된 JSON 형식(application/json)의 값은 매개변수에 
	//Java 객체로 제공받아 사용 가능 - 전달값은 객체 필드에 저장 
	@RequestMapping(value = "/rest", method = RequestMethod.POST)
	@ResponseBody
	public String rest(@RequestBody String input) {
		return input;
	}

	//@ResponseBody 어노테이션을 사용하여 요청 처리 메소드의 반환값(Member 객체)을 리스폰즈 메세지의
	//몸체부에 저장하여 텍스트 데이타의 회원정보를 클라이언트에게 전달하여 응답 처리
	//문제점)리스폰즈 메세지의 몸체부에 Java 객체를 저장 불가능 - 에러 발생(406)
	//해결법)반환되는 Java 객체를 텍스트 데이타(XML 또는 JSON)의 문자열로 자동 변환되도록 
	//설정하여 응답 처리 가능
	// => jackson-databind 라이브러리를 프로젝트에 빌드 처리하면 자동으로 Java 객체가 JSON
	//형식의 텍스트 데이타로 변환하여 반환 - 메이븐 : pom.xml
	@RequestMapping("/rest_member")
	@ResponseBody
	public Member restMember() {
		Member member=new Member();
		member.setId("abc123");
		member.setPasswd("123456");
		member.setName("홍길동");
		member.setEmail("abc@itwill.xyz");
		
		//jackson-databind 라이브러리에 의해 Member 객체가 JavaScript Object 객체로 변환되어 응답
		return member;
	}

	//@ResponseBody 어노테이션을 사용하여 요청 처리 메소드의 반환값(List 객체)을 리스폰즈 메세지의
	//몸체부에 저장하여 텍스트 데이타의 회원목록을 클라이언트에게 전달하여 응답 처리
	@RequestMapping("/rest_list")
	@ResponseBody
	public List<Member> restMemberList() {
		List<Member> memberList=new ArrayList<Member>();
		
		Member member1=new Member();
		member1.setId("abc123");
		member1.setPasswd("123456");
		member1.setName("홍길동");
		member1.setEmail("abc@itwill.xyz");
		memberList.add(member1);
		
		Member member2=new Member();
		member2.setId("xyz789");
		member2.setPasswd("789456");
		member2.setName("임꺽정");
		member2.setEmail("xyz@itwill.xyz");
		memberList.add(member2);
				
		//jackson-databind 라이브러리에 의해 List 객체가 JavaScript Array 객체로 변환되어 응답
		return memberList;
	}

	@RequestMapping("/rest_map")
	@ResponseBody
	public Map<String, Object> restMap() {
		Map<String, Object> map=new HashMap<String, Object>();
		map.put("id", "abc123");
		map.put("passwd", "123456");
		map.put("name", "홍길동");
		map.put("email", "abc@itwill.xyz");
				
		//jackson-databind 라이브러리에 의해 Map 객체가 JavaScript Object 객체로 변환되어 응답
		return map;
	}

	@RequestMapping(value = "/rest/map", method = RequestMethod.GET)
	public String restInput() {
		return "rest/input";
	}
	
	//모든 전달값이 Map 객체의 엔트리에 저장되어 매개변수로 제공받아 사용 가능
	// => 모든 전달값을 Map 객체로 제공받기 위해 반드시 @RequestParam 어노테이션 사용
	@RequestMapping(value = "/rest/map", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, Object> restOutput(@RequestParam Map<String, Object> map) {
		return map;
	}
}






📌 pom.xml

  • jackson-databind : Java 객체를 JSON 형식의 텍스트 데이타로 변환하기 위한 라이브러리

		<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-databind</artifactId>
		    <version>2.14.2</version>
		</dependency>
	</dependencies>




🐣 앱실행 (input)


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SPRING</title>
</head>
<body>
	<h1>입력페이지</h1>
	<hr>
	<form method="post">
	<table>
		<tr>
			<td>아이디</td>
			<td><input type="text" name="id"></td>
		</tr>
		<tr>
			<td>이름</td>
			<td><input type="text" name="name"></td>
		</tr>
		<tr>
			<td colspan="2"><button type="submit">입력완료</button></td>
		</tr>
	</table>
	</form>
</body>
</html>





🐣 앱실행 (output)


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SPRING</title>
</head>
<body>
	<h1>출력페이지</h1>
	<hr>
	<p>아이디 = ${id }</p>
	<p>이름 = ${name }</p>
</body>
</html>





🌼 웹프로그램 작성 순서

  1. table
  2. DTO Class
  3. XML Mapper
  4. Interface Mapper
  5. DAO Interface
  6. DAO Class
  7. Service Interface
  8. Service Class
  9. Controller 클래스 (Model)
  10. HTML 문서를 JSP 문서로 변환

📕 1. table 생성


CREATE TABLE RESTBOARD(NUM NUMBER PRIMARY KEY, WRITER VARCHAR2(50), CONTENT VARCHAR2(100), REGDATE DATE);
CREATE SEQUENCE RESTBOARD_SEQ;


📕 2. DTO Class

package xyz.itwill10.dto;

import lombok.Data;

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



📙 3. XML Mapper


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xyz.itwill10.mapper.RestBoardMapper">
	<insert id="insertRestBoard">
		<selectKey resultType="int" keyProperty="num" order="BEFORE">
			select restboard_seq.nextval from dual
		</selectKey>
		insert into restboard values(#{num},#{writer},#{content},sysdate)
	</insert>
	
	<update id="updateRestBoard">
		update restboard set writer=#{writer},content=#{content} where num=#{num}
	</update>
	
	<delete id="deleteRestBoard">
		delete from restboard where num=#{num}
	</delete>
	
	<select id="selectRestBoardCount" resultType="int">
		select count(*) from restboard
	</select>	
	
	<select id="selectRestBoard" resultType="RestBoard">
		select * from restboard where num=#{num}
	</select>
	
	<select id="selectRestBoardList" resultType="RestBoard">
		select * from (select rownum rn, board.* from (select * from restboard 
			order by num desc) board) where rn between #{startRow} and #{endRow}
	</select>
</mapper>





📙 4. Interface Mapper


package xyz.itwill10.mapper;

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





📒 5. DAO Interface


package xyz.itwill10.dao;

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




📒 6. DAO Class


package xyz.itwill10.dao;

@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 num) {
		return sqlSession.getMapper(RestBoardMapper.class).deleteRestBoard(num);
	}

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

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

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

}





📗 7. Service interface



package xyz.itwill10.service;

public interface RestBoardService {
	void addRestBoard(RestBoard restBoard);
	void modifyRestBoard(RestBoard restBoard);
	void removeRestBoard(int num);
	int getRestBoardCount();
	RestBoard getRestBoard(int num);
	List<RestBoard> getRestBoardList(Map<String, Object> map);
}





📗 8. Service class


package xyz.itwill10.service;

@Service
@RequiredArgsConstructor
public class RestBoardServiceImpl implements RestBoardService {
	private final RestBoardDAO restBoardDAO;
	
	@Transactional
	@Override
	public void addRestBoard(RestBoard restBoard) {
		restBoardDAO.insertRestBoard(restBoard);
	}

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

	@Transactional
	@Override
	public void removeRestBoard(int num) {
		restBoardDAO.deleteRestBoard(num);
	}

	@Override
	public int getRestBoardCount() {
		return restBoardDAO.selectRestBoardCount();
	}
	
	@Override
	public RestBoard getRestBoard(int num) {
		return restBoardDAO.selectRestBoard(num);
	}

	@Override
	public List<RestBoard> getRestBoardList(Map<String, Object> map) {
		return restBoardDAO.selectRestBoardList(map);
	}

}





📌 REST API 테스트 프로그램

  • REST 기능을 제공하는 페이지를 요청하여 요청 처리 메소드가 정상적으로 실행되는지 확인하기 위해 Advanced REST Client 크롬앱을 설치하여 사용 (REST API 테스트 프로그램)
  1. google에 advanced rest client 검색하여 클릭 → Chrome에 추가

  1. 추가 완료 후 앱 실행

  1. ARC 실행하여 요청 처리 메소드가 정상적으로 실행되는지 확인
    • Method(요청방식), Request URL 입력 후 SEND를 클릭하여 확인 가능

  • 게시글 내용 수정 및 확인


🌸 REST 참고

  • REST 기능을 제공하는 요청 처리 메소드의 페이지 요청방식

    • @RequestMapping 어노테이션의 method 속성값을 사용하여 구분 - @GetMapping, @PostMapping 등의 어노테이션 사용 가능
    • 페이지 요청방식 : GET(검색), POST(삽입), PUT(전체 변경), PATCH(부분 변경), DELETE(삭제) 등
    • PUT, PATCH, DELETE 등의 요청방식은 POST 요청방식에서 파생된 요청방식 (리퀘스트 메세지의 몸체부에 전달값 저장
      )
  • @RestController : REST 기능을 제공하는 요청 처리 메소드로만 작성된 Controller 클래스를 Spring Bean으로 등록하기 위한 어노테이션

    • REST 기능을 제공하는 요청 처리 메소드에 @ResponseBody 어노테이션을 사용하지 않아도 반환값을 텍스트 데이타로 응답 처리 가능
    • jsp파일로 응답하는 메소드가 하나라도 있을 경우 사용 불가

📘 9. Controller class

  • 페이지 번호를 전달받아 RESTBOARD 테이블에 저장된 게시글 목록 중 해당 페이지 번호의 범위에 게시글 목록 검색하여 JSON 형식의 데이타로 응답하는 요청 처리 메소드

    • 게시글 목록을 페이징 처리하여 출력하기 위해 요청 페이지 번호의 게시글 목록과 페이지 번호 출력 관련 값이 저장된 객체를 Map 객체의 엔트리로 저장하여 JSON 형식의 데이타로 응답 처리되도록 반환
  • 게시글을 전달받아 RESTBOARD 테이블에 삽입하고 처리결과를 일반 텍스트로 응답하는 요청 처리 메소드
    • [application/json] 형식으로 전달된 값을 Java 객체로 제공받기 위해 매개변수에 @RequestBody 어노테이션 사용
    • HtmlUtils.htmlEscape(String str) : 전달받은 문자열에서 HTML 태그 관련 문자를 회피
    • 문자(Escape Character)로 변환하여 반환하는 메소드 (XSS 공격에 대한 방어)
  • 글번호를 전달받아 RESTBOARD 테이블에 저장된 해당 글번호의 게시글을 검색하여 JSON 형식의 텍스트 데이타로 응답하는 요청 처리 메소드

  • 게시글을 전달받아 RESTBOARD 테이블에 저장된 해당 게시글을 변경하고 처리결과를 일반텍스트로 응답하기 위한 요청 처리 메소드

    • 페이지 요청 방식을 여러개로 설정할 경우 @RequestMapping 어노테이션의 method 속성값으로 배열 사용 가능
  • 요청 URL 주소의 글번호를 전달받아 RESTBOARD 테이블에 저장된 해당 글번호의 게시글을 삭제하고 처리결과를 일반 텍스트로 응답하기 위한 요청 처리 메소드

    • 요청 URL 주소를 사용하여 전달된 값은 @RequestMapping 어노테이션의 value 속성값에서 {이름} 형식으로 표현 가능
    • @PathVariable 어노테이션을 사용하여 전달값을 매개변수에 저장
  • @PathVariable : URL 주소로 제공된 값을 요청 처리 메소드의 매개변수에 저장하기 위한 어노테이션
    • 요청 URL 주소에서 표현된 이름과 매개변수의 이름이 반드시 같아야만 매개변수에 값 저장 가능
    • public String restBoardRemove(@PathVariable int num) {}
    • @PathVariable 어노테이션에 value 속성값을 사용하여 요청 URL 주소에서 표현된 이름으로 제공된 값을 얻어와 매개변수에 저장 가능



package xyz.itwill10.controller;

@Controller
@RequiredArgsConstructor
public class RestBoardController {
	private final RestBoardService restBoardService;
	
	@RequestMapping("/board")
	public String restBoard() {
		return "rest/board";
	}
	
	//페이지 번호를 전달받아 RESTBOARD 테이블에 저장된 게시글 목록 중 해당 페이지 번호의 범위에
	//게시글 목록을 검색하여 JSON 형식의 데이타로 응답하는 요청 처리 메소드
	// => 게시글 목록을 페이징 처리하여 출력하기 위해 요청 페이지 번호의 게시글 목록과 페이지 
	//번호 출력 관련 값이 저장된 객체를 Map 객체의 엔트리로 저장하여 JSON 형식의 데이타로 응답
	//처리되도록 반환
	@RequestMapping(value="/board_list", method = RequestMethod.GET)
	@ResponseBody
	public Map<String, Object> restBoardList(@RequestParam(defaultValue = "1") int pageNum) {
		//System.out.println("pageNum = "+pageNum);
		
		//RESTBOARD 테이블에 저장된 모든 게시글의 갯수를 반환하는 메소드 호출
		int totalBoard=restBoardService.getRestBoardCount();
		int pageSize=5;//하나의 페이지에 출력될 게시글의 갯수 저장
		int blockSize=5;//하나의 블럭에 출력될 페이지 번호의 갯수 저장
		
		//페이징 처리 관련 값이 필드에 저장된 Pager 객체 생성 
		Pager pager=new Pager(pageNum, totalBoard, pageSize, blockSize);
		
		//RestBoardService 클래스의 getRestBoardList() 메소드를 호출하기 위해 Map 객체 생성
		// => Map 객체에는 요청 페이지의 시작 행번호와 종료 행번호를 엔트리로 저장하여 SQL 명령에서 사용
		Map<String, Object> pageMap=new HashMap<String, Object>();
		pageMap.put("startRow", pager.getStartRow());
		pageMap.put("endRow", pager.getEndRow());
		//요청 페이지 번호에 대한 게시글 목록을 List 객체로 반환받아 저장
		List<RestBoard> restBoardList=restBoardService.getRestBoardList(pageMap);
		
		//처리결과를 저장하여 반환하기 위한 Map 객체 생성 
		// => Map 객체에는 게시글 목록(List 객체)과 페이징 처리 관련 값이 저장된 객체(Pager 객체)를 엔트리로 저장
		Map<String, Object> resultMap=new HashMap<String, Object>();
		resultMap.put("restBoardList", restBoardList);//list객체
		resultMap.put("pager", pager);//array객체
		
		return resultMap;//Map 객체를 반환하여 JSON 형식의 데이타로 응답
	}
	
	//게시글을 전달받아 RESTBOARD 테이블에 삽입하고 처리결과를 일반 텍스트로 응답하는 요청 처리 메소드
	// => [application/json] 형식으로 전달된 값을 Java 객체로 제공받기 위해 매개변수에 @RequestBody 어노테이션 사용
	@RequestMapping(value="/board_add", method = RequestMethod.POST)
	@ResponseBody
	public String restBoardAdd(@RequestBody RestBoard restBoard) {
		//HtmlUtils.htmlEscape(String str) : 전달받은 문자열에서 HTML 태그 관련 문자를 회피
		//문자(Escape Character)로 변환하여 반환하는 메소드 - XSS 공격에 대한 방어
		restBoard.setContent(HtmlUtils.htmlEscape(restBoard.getContent()));
		restBoardService.addRestBoard(restBoard);
		return "success";
	}
	
	//글번호를 전달받아 RESTBOARD 테이블에 저장된 해당 글번호의 게시글을 검색하여 JSON 형식의
	//텍스트 데이타로 응답하는 요청 처리 메소드
	@RequestMapping(value="/board_view", method = RequestMethod.GET)
	@ResponseBody
	public RestBoard restBoardView(@RequestParam int num) {
		return restBoardService.getRestBoard(num);
	}
	
	//게시글을 전달받아 RESTBOARD 테이블에 저장된 해당 게시글을 변경하고 처리결과를 일반
	//텍스트로 응답하기 위한 요청 처리 메소드
	// => 페이지 요청 방식을 여러개로 설정할 경우 @RequestMapping 어노테이션의 method 속성값으로 배열 사용 가능
	@RequestMapping(value="/board_modify", method = {RequestMethod.PUT, RequestMethod.PATCH})
	@ResponseBody
	public String restBoardModify(@RequestBody RestBoard restBoard) {
		restBoardService.modifyRestBoard(restBoard);
		return "success";
	}
	
	//요청 URL 주소의 글번호를 전달받아 RESTBOARD 테이블에 저장된 해당 글번호의 게시글을 
	//삭제하고 처리결과를 일반 텍스트로 응답하기 위한 요청 처리 메소드
	// => 요청 URL 주소를 사용하여 전달된 값은 @RequestMapping 어노테이션의 value 속성값에서
	//{이름} 형식으로 표현 가능 - @PathVariable 어노테이션을 사용하여 전달값을 매개변수에 저장
	//@PathVariable : URL 주소로 제공된 값을 요청 처리 메소드의 매개변수에 저장하기 위한 어노테이션
	// => 요청 URL 주소에서 표현된 이름과 매개변수의 이름이 반드시 같아야만 매개변수에 값 저장 가능
	// => @PathVariable 어노테이션에 value 속성값을 사용하여 요청 URL 주소에서 표현된 이름으로
	//제공된 값을 얻어와 매개변수에 저장 가능
	@RequestMapping(value="/board_remove/{num}", method = RequestMethod.DELETE)
	@ResponseBody
	public String restBoardRemove(@PathVariable int num) {
		restBoardService.removeRestBoard(num);
		return "success";
	}
}





📌 페이지 처리 클래스



package xyz.itwill10.util;

import lombok.Data;

//페이징 처리 관련 값을 필드에 저장하기 위한 클래스
@Data
public class Pager {
	//생성자를 이용하여 초기값을 전달받아 필드에 저장
	private int pageNum;//요청 페이지의 번호
	private int totalBoard;//전체 게시글의 갯수
	private int pageSize;//하나의 페이지에 출력될 게시글의 갯수
	private int blockSize;//하나의 블럭에 출력될 페이지 번호의 갯수

	//생성자로 초기화된 필드값을 계산하여 결과값을 필드에 저장
	private int totalPage;//전체 페이지의 갯수
	private int startRow;//요청 페이지에 출력될 게시글의 시작 행번호
	private int endRow;//요청 페이지에 출력될 게시글의 종료 행번호
	private int startPage;//현재 블럭에 출력될 시작 페이지 번호
	private int endPage;//현재 블럭에 출력될 종료 페이지 번호
	private int prevPage;//이전 블럭에 출력될 시작 페이지 번호
	private int nextPage;//다음 블럭에 출력될 시작 페이지 번호
	
	public Pager(int pageNum, int totalBoard, int pageSize, int blockSize) {
		super();
		this.pageNum = pageNum;
		this.totalBoard = totalBoard;
		this.pageSize = pageSize;
		this.blockSize = blockSize;
		
		calcPage();
	}
	
	//계산 결과값을 필드에 저장하는 메소드 - 생성자에서 호출하여 사용
	private void calcPage() {
		totalPage=(int)Math.ceil((double)totalBoard/pageSize);
		if(pageNum<=0 || pageNum>totalPage) {
			pageNum=1;
		}
		
		startRow=(pageNum-1)*pageSize+1;
 		endRow=pageNum*pageSize;
		if(endRow>totalBoard) {
			endRow=totalBoard;
		}
		
		startPage=(pageNum-1)/blockSize*blockSize+1;
		endPage=startPage+blockSize-1;
		if(endPage>totalPage) {
			endPage=totalPage;
		}
		
		prevPage=startPage-blockSize;
		nextPage=startPage+blockSize;
	}
	
}





🎀 10. view (board)

  • RESTBOARD 테이블에 저장된 게시글 목록을 페이징 처리하여 검색하고 처리결과를 JSON 형식의 데이타로 응답하는 페이지(board_list)를 AJAX 기능으로 요청

    • 응답받은 JSON 형식의 데이타로 게시글 목록 출력 태그를 변경
  • headers : 요청정보가 저장된 리퀘스트 메세지의 머릿부(Header)를 변경하기 위한 속성

    • 리퀘스트 메세지의 머릿부에서 몸체부에 저장된 전달값에 대한 문서형식(MimeType)을 변경
    • headers: {"contentType":"application/json"},
  • contentType : 리퀘스트 몸체부에 저장된 전달값에 대한 문서형식을 변경하기 위한 속성

    • 리퀘스트 몸체부에 저장된 전달값을 JSON 형식의 텍스트 데이타로 전달
    • 요청 처리 메소드의 매개변수에서 @RequestBody 어노테이션을 사용하여 모든 전달값을 Java 객체로 제공받아 사용 (전달값은 Java 객체의 필드값으로 저장)
    • contentType: "application/json",
  • JSON.stringify(object) : JavaScript 객체를 전달받아 JSON 형식의 텍스트 데이터로 변환하는 메소드


<%@ 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>
	
	<script type="text/javascript">
	//현재 요청 페이지의 번호를 저장하기 위한 전역변수 
	// => 모든 함수에서 사용 가능하며 프로그램 종료 전까지 값을 유지
	var page=1;
	
	//특정 페이지 번호의 게시글 목록을 출력하는 함수 호출
	boardListDisplay(page);
	
	//RESTBOARD 테이블에 저장된 게시글 목록을 페이징 처리하여 검색하고 처리결과를 JSON 형식의
	//데이타로 응답하는 페이지(board_list)를 AJAX 기능으로 요청 
	// => 응답받은 JSON 형식의 데이타로 게시글 목록 출력 태그를 변경
	function boardListDisplay(pageNum) {
		page=pageNum;
		$.ajax({
			type: "get",
			url: "${pageContext.request.contextPath}/board_list?pageNum="+pageNum,
			dataType: "json",
			success: function(result) {
				//alert(result);//[object Object] >> 자바스크립트 Object 객체
				
				//매개변수로 제공받은 자바스크립트 객체를 HTML로 변경하여 게시글 목록 출력 태그 변경
				if(result.restBoardList.length==0) {//검색된 게시글이 없는 경우
					var html="<table id='restBoardTable'>";
					html+="<tr>";
					html+="<th width='800' colspan='6'>검색된 게시글이 없습니다.</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() {//게시글 목록을 반복 처리
					html+="<tr>";
					html+="<td align='center'>"+this.num+"</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' onclick='modify("+this.num+");'>변경</button></td>";
					html+="<td align='center'><button type='button' onclick='remove("+this.num+");'>삭제</button></td>";
					html+="</tr>";
				});
				html+="</table>";
				
				$("#restBoardListDiv").html(html);
				
				//페이지 번호를 출력하는 함수 호출
				pageNumDisplay(result.pager)
			},
			error: function(xhr) {
				alert("에러코드(게시글 목록 검색) = "+xhr.status)
			}
		});
	}
	
	//페이지 번호를 출력하는 태그를 변경하는 함수 - 페이지 번호 출력
	function pageNumDisplay(pager) {
		var html="";
		
		if(pager.startPage > pager.blockSize) {
			html+="<a href='javascript:boardListDisplay("+pager.prevPage+")'>[이전]</a>";
		}
		
		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>";
		}
		
		$("#pageNumDiv").html(html);
	}
  • 게시글 목록과 페이징 처리 확인
	//[글쓰기] 태그를 클릭한 경우 호출되는 이벤트 처리 함수
	$("#writeBtn").click(function() {
		//변경 게시글을 입력받기 위한 태그 초기화
		$(".update").val("");//입력태그 초기화
		$("#updateDiv").hide();//태그 숨김
		
		//신규 게시글을 입력받기 위한 태그 출력
		$("#insertDiv").show();
	});

  • 글쓰기를 클릭했을 경우

	//신규 게시글을 입력받기 위한 태그에서 [저장] 태그를 클릭한 경우 호출되는 이벤트 처리 함수
	// => 사용자 입력값을 얻어와 RESTBOARD 테이블에 삽입하는 페이지를 AJAX 기능으로 요청하여
	//처리결과를 제공받아 응답 처리
	$("#insertBtn").click(function() {
		var writer=$("#insertWriter").val();
		var content=$("#insertContent").val();
		
		if(writer=="") {
			alert("작성자를 입력해 주세요.");
			return;
		}
		
		if(content=="") {
			alert("내용 입력해 주세요.");
			return;
		}
		
		$.ajax({
			type: "post",
			url: "${pageContext.request.contextPath}/board_add",
			//headers : 요청정보가 저장된 리퀘스트 메세지의 머릿부(Header)를 변경하기 위한 속성
			// => 리퀘스트 메세지의 머릿부에서 몸체부에 저장된 전달값에 대한 문서형식(MimeType)을 변경
			//headers: {"contentType":"application/json"},
			//contentType : 리퀘스트 몸체부에 저장된 전달값에 대한 문서형식을 변경하기 위한 속성
			// => 리퀘스트 몸체부에 저장된 전달값을 JSON 형식의 텍스트 데이타로 전달
			// => 요청 처리 메소드의 매개변수에서 @RequestBody 어노테이션을 사용하여 모든 전달값을
			//Java 객체로 제공받아 사용 - 전달값은 Java 객체의 필드값으로 저장
			contentType: "application/json",
			//JSON.stringify(object) : JavaScript 객체를 전달받아 JSON 형식의 텍스트 데이타로 변환하는 메소드
			data: JSON.stringify({"writer":writer,"content":content}),
			dataType: "text",
			success: function(result) {
				if(result=="success") {
					//신규 게시글을 입력받기 위한 태그 초기화
					$(".insert").val("");//입력태그 초기화
					$("#insertDiv").hide();//태그 숨김
					
					//특정 페이지 번호의 게시글 목록을 출력하는 함수 호출
					boardListDisplay(page);
				}
			}, 
			error: function(xhr) {
				alert("에러코드(게시글 삽입) = "+xhr.status)
			}
		});
	});
	
	//신규 게시글을 입력받기 위한 태그에서 [취소] 태그를 클릭한 경우 호출되는 이벤트 처리 함수
	$("#cancelInsertBtn").click(function() {
		//신규 게시글을 입력받기 위한 태그 초기화
		$(".insert").val("");//입력태그 초기화
		$("#insertDiv").hide();//태그 숨김
	});
  • 변경 및 삭제를 위한 num값 전달 결과 확인
	//게시글의 [변경] 태그를 클릭한 경우 호출되는 이벤트 처리 함수
	// => 글번호를 전달받아 RESTBOARD 테이블에 저장된 해당 글번호의 게시글을 검색하여 반환하는
	//페이지를 AJAX 기능으로 요청하여 처리결과를 JSON 형식의 데이타로 응답받아 변경 게시글을 
	//입력받기 위한 태그의 입력값으로 초기화 처리
	function modify(num) {
		//alert(num);
		
		//신규 게시글을 입력받기 위한 태그 초기화
		$(".insert").val("");//입력태그 초기화
		$("#insertDiv").hide();//태그 숨김
		
		//변경 게시글을 입력받기 위한 태그 출력
		$("#updateDiv").show();
		
		$.ajax({
			type: "get",
			//페이지 요청시 질의문자열(QueryString)을 사용하여 값 전달
			// => 요청 처리 메소드의 매개변수에 @RequestParam 어노테이션을 사용하여 값을 제공받아 사용
			url: "${pageContext.request.contextPath}/board_view?num="+num,
			dataType: "json",
			success: function(result) {
				$("#updateNum").val(result.num);
				$("#updateWriter").val(result.writer);
				$("#updateContent").val(result.content);
			}, 
			error: function(xhr) {
				alert("에러코드(게시글 검색) = "+xhr.status)
			}
		});
	}
	
	//변경 게시글을 입력받기 위한 태그에서 [변경] 태그를 클릭한 경우 호출되는 이벤트 처리 함수
	// => 사용자 입력값을 얻어와 RESTBOARD 테이블에 저장된 게시글 변경하는 페이지를 AJAX 
	//기능으로 요청하여 처리결과를 제공받아 응답 처리
	$("#updateBtn").click(function() {
		var num=$("#updateNum").val();
		var writer=$("#updateWriter").val();
		var content=$("#updateContent").val();
		
		if(writer=="") {
			alert("작성자를 입력해 주세요.");
			return;
		}
		
		if(content=="") {
			alert("내용 입력해 주세요.");
			return;
		}
		
		$.ajax({
			type: "put",
			url: "${pageContext.request.contextPath}/board_modify",
			contentType: "application/json",
			data: JSON.stringify({"num":num,"writer":writer,"content":content}),
			dataType: "text",
			success: function(result) {
				if(result=="success") {
					//변경 게시글을 입력받기 위한 태그 초기화
					$(".update").val("");//입력태그 초기화
					$("#updateDiv").hide();//태그 숨김
					
					//특정 페이지 번호의 게시글 목록을 출력하는 함수 호출
					boardListDisplay(page);
				}
			}, 
			error: function(xhr) {
				alert("에러코드(게시글 변경) = "+xhr.status)
			}
		});
	});
	
	//변경 게시글을 입력받기 위한 태그에서 [취소] 태그를 클릭한 경우 호출되는 이벤트 처리 함수
	$("#cancelUpdateBtn").click(function() {
		//변경 게시글을 입력받기 위한 태그 초기화
		$(".update").val("");//입력태그 초기화
		$("#updateDiv").hide();//태그 숨김
	});
	//게시글의 [삭제] 태그를 클릭한 경우 호출되는 이벤트 처리 함수
	// => 글번호를 전달받아 RESTBOARD 테이블에 저장된 해당 글번호의 게시글을 삭제하는 페이지를
	//AJAX 기능으로 요청하여 처리결과를 일반 텍스트로 제공받아 응답 처리 
	function remove(num) {
		//alert(num);
		
		if(confirm("게시글을 삭제 하시겠습니까?")) {
			$.ajax({
				type: "delete",
				//페이지 요청시 요청 URL 주소를 사용하여 값 전달
				// => 요청 처리 메소드의 매개변수에 @PathVariable 어노테이션을 사용하여 값을 제공받아 사용
				url: "${pageContext.request.contextPath}/board_remove/"+num,
				dataType: "text",
				success: function(result) {
					if(result=="success") {
						//특정 페이지 번호의 게시글 목록을 출력하는 함수 호출
						boardListDisplay(page);
					}
				}, 
				error: function(xhr) {
					alert("에러코드(게시글 삭제) = "+xhr.status)
				}
			});
		}
	}
	</script>
</body>
</html>





📌 ajax 메소드

  • $.ajax(setting) : AJAX 기능을 사용하여 요청과 응답을 처리하기 위한 메소드

    • 매개변수에 AJAX 기능을 구현하기 위한 정보를 Object 객체로 전달
    • 매개변수에 전달되는 Object 객체의 요소의 이름은 정해져 있으며 값 또는 함수로 요소값 표현
    • $.get() 또는 $.post() 메소드를 사용하여 AJAX 기능 구현 가능
    $.ajax({
			type: "post",//요청방식 설정
			url: "welcome_two.jsp",//요청 웹프로그램의 URL 주소 설정
			data: "name="+name,//요청 웹프로그램에 값을 전달하기 위한 이름과 값을 설정
			dataType: "html",//응답결과에 대한 문서형식 설정 - 요소값 : text, html, script, xml, json 등
			//정상적인 응답결과(200)를 제공받아 처리하기 위한 함수의 등록 설정
			// => 처리함수의 매개변수에는 응답결과가 자동으로 저장되어 제공
			success: function(result) {
				$("#message").html(result);
			},
			//비정상적인 응답결과(4XX or 5XX)를 제공받아 처리하기 위한 함수의 등록 설정
			// => 처리함수의 매개변수에는 XMLHttpRequest 객체가 자동으로 저장되어 제공
			error: function(xhr) {
				alert("에러코드 = "+xhr.status);
			})
            
            
            

profile
Study Log 📂

0개의 댓글