-
[Back End] Layered Architecture and Rest ControllerWeb Development/부스트코스 - Back-End(Java) 2020. 4. 6. 14:56
✔️ Controller의 중복요소
◾ 웹 페이지는 중복으로 개발되는 요소가 존재한다. ex) 웹 페이지 어디서나 보이는 로그인 상태 정보
◾ 2개의 URL이 있다. 두 URL은 다르지만, 각 URL에 해당하는 웹 페이지를 보여주기 위해서 중복으로 실행되는 부분이 존재한다. 이 중복을 없애려면 어떻게 해야할까? 중복되는 부분을 별도의 객체 또는 메서드로 분리하면된다.
◾ 예를들어, 쇼핑몰에서 '문의 게시판'에서도 '회원 정보'를 보여주고, '상품 목록 보기'에서도 '회원정보'를 보여줘야 한다면 회원정보를 읽어오는 코드는 어떻게 해야할까?
◾ '문의 게시판'을 보여주는 컨트롤러와 '상품 목록 보기'를 보여주는 보여주는 컨트롤러를 각각 별도로 구현한다.
◾ '회원 정보'를 보여주는 것을 별도의 객체로 만들고 각 컨트롤러는 이 객체를 이용하면 된다.
◾ 컨트롤러에서 중복적으로 호출되는 부분들을 별도의 Service 객체에서 구현하도록하고 컨트롤러들을 Service 객체를 사용하도록 해주면 된다.
◾ Service에서도 DB 작업시 중복되는 코드가 있는데, 이것도 역시 별도의 객체나 메서드로 구현한다. ( Repository Layer = dao )
✔️서비스( Service ) 객체
◾ 서비스 객체는 보통 업무와 관련된 메서드를 가지고 있다. 이를 비지니스 로직이라고 한다.
◾ 보통 하나의 비지니스 로직은 하나의 트랜잭션으로 동작한다.
◾ 위 그림을 보면 컨트롤러 1,3은 상품 Service 객체를 각각 구현하는 것이 아니라, 가져다 쓴다.
✔️ 트랜잭션 ( Transaction )
◾ 하나의 논리적인 작업을 의미한다. 트랜잭션은 다음과 같은 4가지 특성이 있다.
◾ 원자성 ( Atomicity ) : 트랜잭션은 DB에 완전히 반영되거나 ( commit ), DB에 아예 반영되지 않거나 해야한다. ( rollback)
◾ 일관성 ( Consistency ) : 트랜잭션의 작업 처리 결과가 항상 일관성이 있어야 한다.
◾ 독립성 ( Isolation ) : 어떤 트랜잭션이 다른 트랜잭션의 연산을 끼어들 수 없다.
◾ 지속성 ( Durability ) : 트랜잭션이 성공적으로 완료됐을 경우, 결과는 영구적으로 반영되어야 한다.
◾ JDBC는 auto commit 설정이 기본적으로 되어있다. ( Connection 객체의 파라미터 조정으로 commit 메서드로 commit을 할 수 있음 )
◾ Spring JDBC에서는 Spring Java Config 파일에서 @EnableTransactionManagement 어노테시션을 사용해 드랜잭션을 활성화할 수 있다.
✔️서비스( Service )의 중복요소
◾ 서비스 객체들은 비지니스 로직을 가지고 있다. 하나의 비즈니스 로직은 하나의 트랜잭션 단위로 작업이 처리가 된다.
◾ 하나의 트랜잭션은 여러개의 DB 작업이 수행될 수 있다. 이 때 비지니스 메서드마다 중복되는 기능을 호출할 수 있다.
◾ 서비스에서의 중복요소도 별도의 객체나 메서드로 분리해서 사용하면 중복을 없앨 수 있다.
✔️ 레이어드 아키텍쳐 ( Layered Architecture )
◾ Persentation Layer : 컨트롤러 객체가 동작 한다.
◾ Service Layer : 비지니스 메서드를 가지고 있는 서비스 객체가 동작한다.
◾ Repository Layer : DB 작업을 수행한다.
◾ Service 객체는 Repository Layer에 있는 DAO 객체를 사용한다.
◾ Persentation Layer 은 웹, 앱, 윈도우 프로그램 등으로 만들 수 있다. 이 때 Service Layer, Repository Layer를 재사용할 수 있다. -> 재사용, 유지보수 측면에서 Presentation Layer와 Service, Repository Layer 설정 파일을 분리하는 것이 좋다.
✔️ 설정의 분리
◾ Spring 설정 파일을 프리젠테이션 레이어쪽과 나머지를 분리할 수 있다.
◾ web.xml 파일에서 프리젠테이션 레이어에 대한 스프링 설정은 DispathcerServlet이 읽도록 하고, 그 외의 설정은 ContextLoaderListener를 통해서 읽도록 한다.
◾ ContextLoaderListener와 DispatcherServlet은 각각 ApplicationContext를 생성하는데, ContextLoaderListener가 생성하는 ApplicationContext가 root컨텍스트가 되고 DispatcherServlet이 생성한 인스턴스는 root컨텍스트를 부모로 하는 자식 컨텍스트가 된다. 참고로, 자식 컨텍스트들은 root컨텍스트의 설정 빈을 사용할 수 있다. ( 상속 )
✔️ 레이어드 아키텍쳐 실습
1️⃣ 설정( Configuration )
💾 WebMvcContextConfiguration.java : Spring DispatcherServlet 설정 클래스
더보기package kr.or.connect.guestbook.config; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.TransactionManagementConfigurer; @Configuration @EnableTransactionManagement //트랜잭션과 관련된 설정을 자동으로 해주는 애노테이션 public class DBConfig implements TransactionManagementConfigurer { private String driverClassName = "com.mysql.jdbc.Driver"; private String url = "jdbc:mysql://localhost:3306/connectdb?useUnicode=true&characterEncoding=utf8"; private String username = "root"; private String password = "0000"; @Bean public DataSource dataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } @Override public PlatformTransactionManager annotationDrivenTransactionManager() { return transactionManger(); } @Bean public PlatformTransactionManager transactionManger() { return new DataSourceTransactionManager(dataSource()); } }
💾 DBConfig.java : DB 관련 설정 클래스
더보기package kr.or.connect.guestbook.config; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.TransactionManagementConfigurer; //DB 설정 클래스. TransactionManagementConfigurer인터페이스를 구현한다. @Configuration @EnableTransactionManagement //트랜잭션과 관련된 설정을 자동으로 해주는 애노테이션 public class DBConfig implements TransactionManagementConfigurer { private String driverClassName = "com.mysql.jdbc.Driver"; private String url = "jdbc:mysql://localhost:3306/connectdb?useUnicode=true&characterEncoding=utf8"; private String username = "root"; private String password = "0000"; //DB연결을 위한 dataSouce 등록 @Bean public DataSource dataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } // 트랜잭션을 처리할 transactionManger을 반환하는 메서드 @Override public PlatformTransactionManager annotationDrivenTransactionManager() { return transactionManger(); } @Bean public PlatformTransactionManager transactionManger() { return new DataSourceTransactionManager(dataSource()); } }
💾 ApplicationConfig.java : 스프링 IoC/DI 컨네이너 설정을 위한 클래스 (dao, service 컴포넌트스캔 )
더보기package kr.or.connect.guestbook.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @ComponentScan(basePackages = { "kr.or.connect.guestbook.dao", "kr.or.connect.guestbook.service"}) @Import({ DBConfig.class }) public class ApplicationConfig { }
💾 web.xml
더보기<?xml version="1.0" encoding="UTF-8"?> <web-app> <display-name>Spring JavaConfig Sample</display-name> <context-param> <param-name>contextClass</param-name> <!-- AnnotationConfigWebApplicationContext 사용 설정 --> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </context-param> <context-param> <!-- ApplicationConfig.java를 참고하도록 설정 --> <param-name>contextConfigLocation</param-name> <param-value>kr.or.connect.guestbook.config.ApplicationConfig </param-value> </context-param> <listener> <!-- ContextLoaderListener을 통해 service layer와 repository layer을 설정!! --> <!-- ContextLoaderListener는 context-param을 참고한다.--> <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>mvc</servlet-name> <!-- 모든요청을 DispatcherServlet이 받는다. (front servlet으로 등록하는 것, presentaition layer 설정!!) --> <servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextClass</param-name> <!-- AnnotationConfigWebApplicationContext 사용 설정 --> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </init-param> <init-param> <param-name>contextConfigLocation</param-name> <!-- 디스패처 설정 클래스 등록. WebMvcContextConfiguration.java를 참고해서 설정해라! --> <param-value>kr.or.connect.guestbook.config.WebMvcContextConfiguration </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc</servlet-name> <!-- 모든 요청을 받음 --> <url-pattern>/</url-pattern> </servlet-mapping> <!-- filter는 요청, 응답이 수행되기전 실행되는 것들. 한글 인코딩 설정을 해준다. --> <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> </web-app>
2️⃣ Repository Layer 구현
💾 Guestbook.java : Guestbook DTO
더보기package kr.or.connect.guestbook.dto; import java.util.Date; public class Guestbook { private Long id; private String name; private String content; private Date regdate; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Date getRegdate() { return regdate; } public void setRegdate(Date regdate) { this.regdate = regdate; } @Override public String toString() { return "Guestbook [id=" + id + ", name=" + name + ", content=" + content + ", regdate=" + regdate + "]"; } }
💾 Log.java : Log DTO
더보기package kr.or.connect.guestbook.dto; import java.util.Date; public class Log { private Long id; private String ip; private String method; private Date regdate; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public Date getRegdate() { return regdate; } public void setRegdate(Date regdate) { this.regdate = regdate; } @Override public String toString() { return "Log [id=" + id + ", ip=" + ip + ", method=" + method + ", regdate=" + regdate + "]"; } }
💾 GuestbookDao.java : Guestbook DAO
더보기package kr.or.connect.guestbook.dao; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.sql.DataSource; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; import kr.or.connect.guestbook.dto.Guestbook; import static kr.or.connect.guestbook.dao.GuestbookDaoSqls.*; //DAO에는 @Respasitory 어노테이션을 붙여준다. @Repository public class GuestbookDao { private NamedParameterJdbcTemplate jdbc; private SimpleJdbcInsert insertAction; private RowMapper<Guestbook> rowMapper = BeanPropertyRowMapper.newInstance(Guestbook.class); public GuestbookDao(DataSource dataSource) { this.jdbc = new NamedParameterJdbcTemplate(dataSource); this.insertAction = new SimpleJdbcInsert(dataSource) .withTableName("guestbook") .usingGeneratedKeyColumns("id"); } public List<Guestbook> selectAll(Integer start, Integer limit) { Map<String, Integer> params = new HashMap<>(); params.put("start", start); params.put("limit", limit); return jdbc.query(SELECT_PAGING, params, rowMapper); } public Long insert(Guestbook guestbook) { SqlParameterSource params = new BeanPropertySqlParameterSource(guestbook); return insertAction.executeAndReturnKey(params).longValue(); } public int deleteById(Long id) { Map<String, ?> params = Collections.singletonMap("id", id); return jdbc.update(DELETE_BY_ID, params); } public int selectCount() { return jdbc.queryForObject(SELECT_COUNT, Collections.emptyMap(), Integer.class); } }
💾 GuestbookDaoSqls.java
더보기package kr.or.connect.guestbook.dao; public class GuestbookDaoSqls { public static final String SELECT_PAGING = "SELECT id, name, content, regdate FROM guestbook ORDER BY id DESC limit :start, :limit"; public static final String DELETE_BY_ID = "DELETE FROM guestbook WHERE id = :id"; public static final String SELECT_COUNT = "SELECT count(*) FROM guestbook"; }
💾 LogDao.java
더보기package kr.or.connect.guestbook.dao; import javax.sql.DataSource; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; import kr.or.connect.guestbook.dto.Log; @Repository //dao에 붙이는 애노테이션 public class LogDao { private NamedParameterJdbcTemplate jdbc; private SimpleJdbcInsert insertAction; public LogDao(DataSource dataSource) { this.jdbc = new NamedParameterJdbcTemplate(dataSource); this.insertAction = new SimpleJdbcInsert(dataSource) .withTableName("log") .usingGeneratedKeyColumns("id"); //primary key인 id 자동 입력! } public Long insert(Log log) { SqlParameterSource params = new BeanPropertySqlParameterSource(log); return insertAction.executeAndReturnKey(params).longValue(); // insert문을 내부적으로 생성해서 실행후 생성된 id값 반환 } }
3️⃣ Service Layer 구현
💾 GuestbookService.java : 서비스 인터페이스
더보기package kr.or.connect.guestbook.service; import java.util.List; import kr.or.connect.guestbook.dto.Guestbook; public interface GuestbookService { public static final Integer LIMIT = 5; public List<Guestbook> getGuestbooks(Integer start); public int deleteGuestbook(Long id, String ip); public Guestbook addGuestbook(Guestbook guestbook, String ip); public int getCount(); }
💾 GuestbookServiceImpl.java : 서비스 인터페이스 구현체
더보기package kr.or.connect.guestbook.service.impl; import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import kr.or.connect.guestbook.dao.GuestbookDao; import kr.or.connect.guestbook.dao.LogDao; import kr.or.connect.guestbook.dto.Guestbook; import kr.or.connect.guestbook.dto.Log; import kr.or.connect.guestbook.service.GuestbookService; // Service Layer은 @Service 어노테이션을 붙인다. @Service public class GuestbookServiceImpl implements GuestbookService{ //스프링 컨테이너가 알아서 guestbookDao 주입 시켜줌 @Autowired GuestbookDao guestbookDao; // 스프링 컨테이너가 알아서 logDao 주입 시켜줌 @Autowired LogDao logDao; //게스트북 목록 가져오기 @Override @Transactional //읽기만 하는 메소드는 @Transactional 어노테이션을 붙여주면 Read only 커넥션을 사용한다. public List<Guestbook> getGuestbooks(Integer start) { List<Guestbook> list = guestbookDao.selectAll(start, GuestbookService.LIMIT); //start부터 LIMIT(5개)만큼 가져온다. return list; } //게스트북 삭제 @Override @Transactional(readOnly=false) //이 메소드는 ReadOnly가 아니므로 false 처리 public int deleteGuestbook(Long id, String ip) { int deleteCount = guestbookDao.deleteById(id); Log log = new Log(); // 삭제 로그 남기기 log.setIp(ip); log.setMethod("delete"); log.setRegdate(new Date()); logDao.insert(log); return deleteCount; } //게스트북 삽입 @Override @Transactional(readOnly=false) //이 메소드는 ReadOnly가 아니므로 false 처리 public Guestbook addGuestbook(Guestbook guestbook, String ip) { guestbook.setRegdate(new Date()); Long id = guestbookDao.insert(guestbook); guestbook.setId(id); // 이 메서드는 @Transactional이 붙어있다. 즉! 이 메서드는 트랜잭션이다. 즉! 이 메서드 전체 로직은 나눌 수 없는 하나의 단위이다. // 이 메서드의 모든 부분이 성공해야 DB에 반영이된다. // 아래는 트랜잭션을 배우기 위한 강제 예외처리 코드 ( insert 이후 강제로 예외가 발생하게 한다. 윗 라인에서 insert가 정상적으로 될지라도 이 메서드는 트랜잭션이므로 DB에 반영이 안됨 즉, rollbakc 된다.) /* if(1 == 1) throw new RuntimeException("test exception"); */ Log log = new Log(); // 삽입 로그 남기기 log.setIp(ip); log.setMethod("insert"); log.setRegdate(new Date()); logDao.insert(log); return guestbook; } @Override public int getCount() { return guestbookDao.selectCount(); } }
4️⃣ Persentation Layer 구현 ( Controller 구현)
💾 GuestbookController.java
더보기package kr.or.connect.guestbook.controller; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import kr.or.connect.guestbook.dto.Guestbook; import kr.or.connect.guestbook.service.GuestbookService; //컨트롤러 클래스 @Controller public class GuestbookController { //서비스 객체 주입 @Autowired GuestbookService guestbookService; //@PathVariable은 URL경로에 변수를 넣는것 이다. RESTful API에서 사용한다. ex)127.0.0.1:8080/abcd/abcd //@RequestParam은 URL 파라미터로 값을 넘기는 방식이다. ex)127.0.0.1:8080?a=b&c=d @GetMapping(path="/list") // 페이지네이션을 위한 strat 파라미터. 디폴트 값은 0. public String list(@RequestParam(name="start", required=false, defaultValue="0") int start, ModelMap model) { // start로 시작하는 방명록 목록 구하기 List<Guestbook> list = guestbookService.getGuestbooks(start); // 전체 페이지수 구하기 int count = guestbookService.getCount(); int pageCount = count / GuestbookService.LIMIT; if(count % GuestbookService.LIMIT > 0) pageCount++; // 페이지 수만큼 start의 값을 리스트로 저장 // 예를 들면 페이지수가 3이면 // 0, 5, 10 이렇게 저장된다. // list?start=0 , list?start=5, list?start=10 으로 링크가 걸린다. List<Integer> pageStartList = new ArrayList<>(); for(int i = 0; i < pageCount; i++) { pageStartList.add(i * GuestbookService.LIMIT); } model.addAttribute("list", list); model.addAttribute("count", count); model.addAttribute("pageStartList", pageStartList); return "list"; } @PostMapping(path="/write") public String write(@ModelAttribute Guestbook guestbook, HttpServletRequest request) { String clientIp = request.getRemoteAddr(); System.out.println("clientIp : " + clientIp); guestbookService.addGuestbook(guestbook, clientIp); return "redirect:list"; } }
💾 list.jsp : View
더보기<%@ 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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>방명록 목록</title> </head> <body> <h1>방명록</h1> <br> 방명록 전체 수 : ${count } <br> <br> <c:forEach items="${list}" var="guestbook"> ${guestbook.id }<br> ${guestbook.name }<br> ${guestbook.content }<br> ${guestbook.regdate }<br> </c:forEach> <br> <c:forEach items="${pageStartList}" var="pageIndex" varStatus="status"> <a href="list?start=${pageIndex}">${status.index +1 }</a> </c:forEach> <br> <br> <form method="post" action="write"> name : <input type="text" name="name"><br> <textarea name="content" cols="60" rows="6"></textarea> <br> <input type="submit" value="등록"> </form> </body> </html>
👀 결과
✔️ Rest Controller
◾ Spring 4에서 Rest API 또는 Web API를 개발하기 위해 등장한 어노테이션
◾ MessageConvertor는 외부에서 전달받은 JSON을 자바 내부에서 사용할 수 있는 객체로 변환해주거나, 컨트롤러에서 리턴한 객체를 JSON으로 변환해서 클라이언트에 전달하는 역할을 한다. ( JSON으로 변환하기 위해서는 jackson 라이브러리가 필요하다. )
💾 GuestbookApiController.java
더보기package kr.or.connect.guestbook.controller; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import kr.or.connect.guestbook.dto.Guestbook; import kr.or.connect.guestbook.service.GuestbookService; //Rest 컨트롤러이다. @RestController @RequestMapping(path="/guestbooks") // 이렇게 해주면 잎에 "/guestbooks"을 붙여야함. public class GuestbookApiController { // 서비스 객체 주입 @Autowired GuestbookService guestbookService; // guest book 리스트 반환 // 반환형이 Map 객체이다. 디스패처 서블릿은 json 메세지 컨버터를 내부적으로 사용해서 List Map객체를 json으로 변환해서 전송한다. @GetMapping public Map<String, Object> list(@RequestParam(name="start", required=false, defaultValue="0") int start) { List<Guestbook> list = guestbookService.getGuestbooks(start); int count = guestbookService.getCount(); int pageCount = count / GuestbookService.LIMIT; if(count % GuestbookService.LIMIT > 0) pageCount++; List<Integer> pageStartList = new ArrayList<>(); for(int i = 0; i < pageCount; i++) { pageStartList.add(i * GuestbookService.LIMIT); } Map<String, Object> map = new HashMap<>(); // json으로 만들 데이터를 map 만들어준다. map.put("list", list); map.put("count", count); map.put("pageStartList", pageStartList); return map; } // 여기도 Guestbook 객체로 반환되는데, 이를 디스패처 서블릿이 json 메세지 컨버터를 내부적으로 사용해서 json으로 변환해서 전송한다. // geuest book 작성 @PostMapping public Guestbook write(@RequestBody Guestbook guestbook, HttpServletRequest request) { String clientIp = request.getRemoteAddr(); // id가 입력된 guestbook이 반환된다. Guestbook resultGuestbook = guestbookService.addGuestbook(guestbook, clientIp); return resultGuestbook; } //guest book 삭제 @DeleteMapping("/{id}") //결과 정보를 담은 map 객체로 반환되는데,이를 디스패처 서블릿이 json 메세지 컨버터를 내부적으로 사용해서 json으로 변환해서 전송한다. public Map<String, String> delete(@PathVariable(name="id") Long id, HttpServletRequest request) { String clientIp = request.getRemoteAddr(); int deleteCount = guestbookService.deleteGuestbook(id, clientIp); return Collections.singletonMap("success", deleteCount > 0 ? "true" : "false"); } }
👀 GET 결과
👀 POST 결과
👀 DELETE 결과
'Web Development > 부스트코스 - Back-End(Java)' 카테고리의 다른 글
[Back End] 상태유지기술 - Session (1) 2020.05.18 [Back End] 상태유지기술 - Cookie (0) 2020.05.11 [Back End] Spring MVC (0) 2020.04.03 [Back End] Spring JDBC (0) 2020.03.30 [Back End] Spring Core (0) 2020.03.25