1. DAO 메소드에 여러 타입의 매개변수가 있는 경우
: mapper XML에서 @MapperScan, @Param 사용하여 구분할 수 있다.
2. STS15_MyBatis, @MapperScan, @Param 사용 예제
** [src/main/java] com.lec.sts15_mybatis.board.beans > IWriteDAO.java
package com.lec.sts15_mybatis.board.beans;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.mybatis.spring.annotation.MapperScan;
@MapperScan
public interface IWriteDAO {
public List<BWriteDTO> select();
public int insert(final BWriteDTO dto);
public int insert(String subject, String content, String name);
//public BWriteDTO readByUid(final int uid);
public int incViewCnt(final int uid); // 조회수 증가
public BWriteDTO selectByUid(final int uid);
public int update(final BWriteDTO dto);
public int update(int uid, @Param("a") BWriteDTO dto);
public int deleteByUid(final int uid);
public BWriteDTO searchBySubject(String subject);
}
** [src/main/java] com.lec.sts15_mybatis.mapper > IWriteDAO.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lec.sts15_mybatis.board.beans.IWriteDAO">
<!-- list.do -->
<select id="select" resultType="com.lec.sts15_mybatis.board.beans.BWriteDTO">
SELECT
wr_uid "uid",
wr_subject subject,
wr_content content,
wr_name name,
wr_viewcnt viewcnt,
wr_regdate regdate
FROM
test_write
ORDER BY
wr_uid DESC
</select>
<!-- view.do -->
<select id="selectByUid" resultType="com.lec.sts15_mybatis.board.beans.BWriteDTO">
SELECT
wr_uid "uid", wr_subject subject, wr_content content, wr_name name,
wr_viewcnt viewcnt, wr_regdate regDate
FROM test_write
WHERE wr_uid=#{uid}
</select>
<!-- 조회수 증가 -->
<update id="incViewCnt" flushCache="true">
UPDATE test_write
SET wr_viewcnt = wr_viewcnt + 1
WHERE wr_uid = #{param1}
</update>
<!-- update -->
<!--
<update id="update" flushCache="true"
parameterType="com.lec.sts15_mybatis.board.beans.BWriteDTO">
UPDATE test_write
SET wr_subject = #{subject}, wr_content = #{content}
WHERE wr_uid = #{uid}
</update>
-->
<update id="update" flushCache="true"
parameterType="com.lec.sts15_mybatis.board.beans.BWriteDTO">
UPDATE test_write
SET wr_subject = #{a.subject}, wr_content = #{a.content}
WHERE wr_uid = #{param1}
</update>
<!-- delete.do -->
<delete id="deleteByUid" flushCache="true">
DELETE FROM test_write WHERE wr_uid = #{uid}
</delete>
<!-- searchBySubject, 플러그인 다운 후 어시스트 확인을 위해 만들음 -->
<!--
<select id="searchBySubject" resultType="com.lec.sts15_mybatis.board.beans.BWriteDTO"></select>
-->
</mapper>
3. 이클립스 MyBatis 플러그인
1) 설치
: Help > Eclipse Marketplace ... 클릭

> mybatipse 검색 후 Install 클릭

> I accept the terms of the license agreement 선택 후 Finish 클릭 > MyBatis 플러그인 다운로드가 진행됨...!!

2) 기능
① DAO 와 매퍼 XML 이동 편리


② 어시스트


③ MyBatis XML Mapper 파일 생성 가능
: 패키지 선택 후 우클릭 > NEW > Other ... 클릭

> MyBatis XML Mapper 선택 후 Next 클릭

> 파일 이름 설정 후 > Finish 클릭

> MyBatis XML Mapper 파일 생성 완료...!!

4. Mapper 파일 내 특수기호 처리
** mapper 파일의 부등호 등의 문제
: XML 파일이 ML(Markup Language)이다 보니 <, > 등의 기호 표현 불가(즉, 쿼리문에서 부등식 표현 불가)
[불가] SELECT * FROM 테이블명 WHERE 속성명 < 10
[가능] SELECT * FROM 테이블명 WHERE 속성명 < 10
[해결책] mapper 파일 쿼리문 특수문자 CDATA 활용 : <![CDATA[ 쿼리문 ]]>
<![CDATA[SELECT * FROM 테이블명 WHERE 속성명 < 10]]>
5. MyBatis 로 generated key 값 받아오기(새로 INSERT된 id 값 콘솔창으로 출력하기)
: 매퍼파일에 keyProperty="uid" useGeneratedKeys="true" keyColumn="wr_uid" 명시하면
generated된 key 값을 dto에 받아 올 수 있다.
** [src/main/java] com.lec.sts19_rest.mapper > IWriteDAO2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lec.sts15_mybatis.board.beans.IWriteDAO">
<!-- write -->
<insert id="insert" flushCache="true"
parameterType="com.lec.sts15_mybatis.board.beans.BWriteDTO"
keyProperty="uid" useGeneratedKeys="true" keyColumn="wr_uid"
>
INSERT INTO test_write
(wr_uid, wr_subject, wr_content, wr_name)
VALUES
(test_write_seq.nextval, #{subject}, #{content}, #{name})
</insert>
<!--
<insert id="insert" flushCache="true">
INSERT INTO test_write
(wr_uid, wr_subject, wr_content, wr_name)
VALUES
(test_write_seq.nextval, #{param1}, #{param2}, #{param3})
</insert>
-->
</mapper>
** [src/main/java] com.lec.sts15_mybatis.board.command > BWriteCommand.java
package com.lec.sts15_mybatis.board.command;
import java.util.Map;
import org.springframework.ui.Model;
import com.lec.sts15_mybatis.board.C;
import com.lec.sts15_mybatis.board.beans.BWriteDTO;
import com.lec.sts15_mybatis.board.beans.IWriteDAO;
public class BWriteCommand implements BCommand {
@Override
public void execute(Model model) {
// Model 안에 있는 값(attribute) 꺼내기
Map<String, Object> map= model.asMap();
BWriteDTO dto = (BWriteDTO)map.get("dto");
// BWriteDAO dao = new BWriteDAO();
// int result = dao.insert(dto);
// model.addAttribute("result", result);
// MyBatis 사용
IWriteDAO dao = C.sqlSession.getMapper(IWriteDAO.class);
model.addAttribute("result", dao.insert(dto));
System.out.println("생성된 uid는 " + dto.getUid());
// model.addAttribute("result",
// dao.insert(dto.getSubject(), dto.getContent(), dto.getName()));
} // end execute
} // end Command
6. MyBatis와 스프링 트랜잭션
1) 설정 파일에 PlatformTransactionManager 빈 생성
[주의] TransactionManager에 사용된 dataSource와 SqlSessionFactoryBean에 사용된 dataSource는 반드시 동일,
동일하지 않으면 트랜잭션이 제대로 동작하지 않는다.
2) TicketService 작성(트랜잭션 정의)
: MyBatis의 SqlSession을 사용하여 동작하는 다양한 트랜잭션들 정의 가능,
일전의 Command 객체들을 만든 것이 바로 서비스 단에 해당
** [src/main/java]
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.lec.sts15_mybatis" />
<!-- spring-jdbc 빈 객체 생성 -->
<!-- DataSource 객체 -->
<beans:bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<beans:property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<beans:property name="username" value="scott0316"/>
<beans:property name="password" value="tiger0316"/>
</beans:bean>
<!-- MyBatis 설정 -->
<!-- SqlSessionFactoryBean 생성 -->
<beans:bean name="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="mapperLocations" value="classpath:com/lec/sts15_mybatis/mapper/*.xml"/>
</beans:bean>
<!-- SqlSessionTemplate 생성 -->
<beans:bean name="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<beans:constructor-arg index="0" ref="SqlSessionFactory"/>
</beans:bean>
<!-- Transaction -->
<beans:bean name="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<beans:bean name="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<beans:property name="transactionManager" ref="transactionManager"/>
</beans:bean>
<beans:bean name="ticketService"
class="com.lec.sts15_mybatis.ticket.TicketService">
</beans:bean>
</beans:beans>
** [src/main/java] TicketDTO
package com.lec.sts15_mybatis.ticket;
public class TicketDTO {
private String userId;
private int ticketCount;
public final String getUserId() {
return userId;
}
public final void setUserId(String userId) {
this.userId = userId;
}
public final int getTicketCount() {
return ticketCount;
}
public final void setTicketCount(int ticketCount) {
this.ticketCount = ticketCount;
}
}
** [src/main/java] com.lec.sts15_mybatis.ticket > ITicketDAO.java
package com.lec.sts15_mybatis.ticket;
public interface ITicketDAO {
// 아래 2개의 동작이 하 나의 트랜잭션으로 처리 되어야 한다.
int insertCard(String userId, int buyAmount);
int insertTicket(String userId, int ticketCount);
}
** [src/main/java] com.lec.sts15_mybatis.mapper > ITicketDAO.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lec.sts15_mybatis.ticket.ITicketDAO">
<insert id='insertCard' flushCache="true">
INSERT INTO test_card VALUES (#{param1}, #{param2})
</insert>
<insert id="insertTicket" flushCache="true">
INSERT INTO test_ticket VALUES (#{param1}, #{param2})
</insert>
</mapper>
** [src/main/java] com.lec.sts15_mybatis.ticket > TicketService.java
package com.lec.sts15_mybatis.ticket;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.lec.sts15_mybatis.board.C;
public class TicketService {
// MyBatis
private SqlSession sqlSession;
@Autowired
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
// TransactionTemplate 사용
TransactionTemplate transactionTemplate;
@Autowired
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
// 하나의 트랜잭션 정의
public void buyTicket(final TicketDTO dto) {
// MyBatis 를 사용하여 이 트랜잭션 안의 여러 동작(쿼리들) 실행
// 중간에 실패하면 트랜잭션 실패하고 자동으로 rollback 된다.
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
ITicketDAO dao = C.sqlSession.getMapper(ITicketDAO.class);
dao.insertCard(dto.getUserId(), dto.getTicketCount());
dao.insertTicket(dto.getUserId(), dto.getTicketCount());
}
});
} // end buyTicket()
} // end Service
** [src/main/java] com.lec.sts15_mybatis.ticket > TicketController.java
package com.lec.sts15_mybatis.ticket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/ticket")
public class TicketController {
@Autowired
TicketService ticketService;
@RequestMapping("/buy_ticket")
public String buy_ticket() {
return "ticket/buy_ticket";
}
// 티켓 구매 처리 (트랜잭션)
@RequestMapping("/buy_ticket_card")
public String buy_ticket_card(TicketDTO dto, Model model) {
String page = "ticket/buy_ticket_done";
try {
ticketService.buyTicket(dto); // 트랜잭션 수행!
model.addAttribute("ticketInfo", dto);
} catch(Exception e) {
e.printStackTrace();
page = "ticket/buy_ticket_fail"; // 트랜잭션 오류 발생시..
}
return page;
}
}
7. 인증(Authentication)
: 시스템 접근시, 등록된 사용자인지 여부를 확인하는 것
예) 로그인
8. 인가(Authorization)
: 접근후, 인증된 사용자에게 권한을 부여하는 것,
권한에따라 사용 가능한 기능이 제한됨
예) 사용자 등급(일반/VIP/관리자)
9. 웹 어플리케이션에서 인증이나 인가가 적용된 페이지를 작성하면,
해당 URL request 발생시마다 체크해주어야 한다.
10. 스프링 MVC에서의 로그인 처리
: 각 URL request 처리마다 해당측 url request를 처리하는 컨트롤러 핸들러에서
로그인을 체크하는 코드가 공통코드로 동작되어야 한다.
[이때!] AOP를 이용하여 공통코드를 처리 가능하나
스프링 MVC에서는 Interceptor를 제공함으로 편리하게 다룰 수 있다.
11. 스프링에서 인터셉터 지원하는 주요 객체 2가지
HandlerInterceptor 인터페이스
└─ HandlerInterceptorAdapter 클래스
12. HandlerInterceptorAdapter의 메소드를 이용, 3가지 시점에 공통기능을 넣을 수 있다.

1) 컨트롤러(핸들러) 실행전
: 컨트롤러로 request가 들어가기 전에 수행
[리턴값] true : 컨트롤러 uri로 이동, false : 컨트롤러로 요청 안감
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
** 매개변수 handler는 preHandle() 수행 후 수행될 컨트롤러 메소드에 대한 정보를 담고 있다.
2) 컨트롤러(핸들러) 실행 후, 그러나 아직 뷰 실행 전
: 컨트롤러의 핸들러 처리가 끝나 return 되고, 뷰(화면)을 response 되기 직전에 이 메서드가 수행 void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)
** ModelAndView에는 뷰에 넘어갈 데이터들이 있다. 뷰 직전에 처리할 내용 있으면 여기서 처리한다.
3) 뷰 실행 후
: 뷰(화면)을 response 끝난 뒤 수행
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
13. STS16_Interceptor, 기존 게시판에 로그인 기능 넣기
** 예제 시나리오
① /board/list.do는 비회원에게도 노출
② 나머지는 로그인한 회원만 노출
③ /board/list.do 제외, 비로그인 상태에서 URI request 발생하면 Interceptor가 로그인페이지 /user/login로 redirect
④ 로그인 정보는 세션 객체에 담긴다(즉, attribute는 id)
** src > main > webapp > WEB-INF > spring > sppServlet > servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.lec.sts16_interceptor" />
<!-- spring-jdbc 빈 객체 생성 -->
<!-- DataSource 객체 -->
<beans:bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<beans:property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<beans:property name="username" value="scott0316"/>
<beans:property name="password" value="tiger0316"/>
</beans:bean>
<!-- JdbcTemplate -->
<beans:bean name="template" class="org.springframework.jdbc.core.JdbcTemplate">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<!-- Interceptor 빈(bean) 생성 -->
<beans:bean name="loginInterceptor"
class="com.lec.sts16_interceptor.board.controller.LoginInterceptor"/>
<!-- Interceptor 등록/설정 -->
<interceptors>
<interceptor>
<!-- 일일히 지정 -->
<!--
<mapping path="/board/list.do"/>
<mapping path="/board/write.do"/>
-->
<!-- 모든 URL 적용 -->
<!--
<mapping path="/**"/>
-->
<!-- 특정 url 만 제외 가능 -->
<mapping path="/board/*.do"/>
<exclude-mapping path="/board/list.do"/>
<beans:ref bean="loginInterceptor"/>
</interceptor>
</interceptors>
</beans:beans>
** [src/main/java] com.lec.sts16_interceptor.board.controller > LoginInterceptor.java
package com.lec.sts16_interceptor.board.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// session 객체 가져오기
HttpSession session = request.getSession();
String id = (String)session.getAttribute("id");
// 컨트롤러 실행전 (Handler 실행전)
System.out.println("[preHandle] " + id);
if(id == null) {
// 만약 로그인이 되어 있지 않다면, 로그인 페이지로, redirect
// 직전 요청 url을 세션에 기록
String urlPrior =
request.getRequestURL().toString() + "?" + request.getQueryString();
request.getSession().setAttribute("url_prior_login", urlPrior);
response.sendRedirect(request.getContextPath() + "/user/login");
return false; // ※ 더이상 컨트롤러 핸들러 진행하지 않도록 false 리턴
}
return true; // ★ true 리턴하면 컨트롤 핸들러 진행
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 컨트롤러 실행후(Handler 실행직후), 뷰 직전
System.out.println("[postHandle]");
super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// 뷰 response 후
System.out.println("[afterCompletion]");
super.afterCompletion(request, response, handler, ex);
}
}
** [src/main/java] com.lec.sts16_interceptor.user.controller > UserController.java
package com.lec.sts16_interceptor.user.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/user")
public class UserController {
// 예제: 테스트용 계정
public static final String ADMIN_ID = "admin";
public static final String ADMIN_PW = "1234";
@RequestMapping("/login")
public String logIn() {
return "user/login";
}
@RequestMapping(value="/loginOk", method=RequestMethod.POST)
public String loginOk(String id, String pw, HttpSession session) {
String returnURL = "";
if(session.getAttribute("id") != null) {
// 기존에 id 세션값이 존재한다면 (즉, 로그인 세션정보가 있는 상태라면)
session.removeAttribute("id");// 일단 이전 로그인 세션 정보 삭제
}
// ※ 실제 회원 DB 테이블을 쿼리해야 하는 부분.
if(ADMIN_ID.equals(id) && ADMIN_PW.equals(pw)) {
// 로그인 성공
session.setAttribute("id", id);
// 원래 가고자 했던 url 이 있었다면
String priorUrl = (String)session.getAttribute("url_prior_login");
if(priorUrl != null) {
returnURL = "redirect:" + priorUrl;
session.removeAttribute("url_prior_login");
} else {
// 없었다면, 디폴트로 list.do 로 이동.
returnURL = "redirect:/board/list.do";
}
} else {
// 로그인 실패
returnURL = "user/logfail"; // history.back()
}
return returnURL;
} // end loginOk()
@RequestMapping("/logout")
public String logOut(HttpSession session) {
session.removeAttribute("id");
return "user/logout";
} // end logOut()
} // end Controller
** src > main > webapp > WEB-INF > views > user > login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>로그인</title>
</head>
<body>
<h2>로그인</h2>
<form action="loginOk" method="POST">
id : <input name="id" required/><br>
pw : <input name="pw" required/><br>
<input type="submit" value="로그인"/>
</form>
</body>
</html>
** src > main > webapp > WEB-INF > views > user > logfail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<script>
alert("로그인 실패");
history.back();
</script>
** src > main > webapp > WEB-INF > views > user > logout.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<script>
alert("로그 아웃 되었습니다!");
location.href="${pageContext.request.contextPath}/board/list.do"
</script>
14. REST(REpresentational State Transfer)
: 하나의 URI은 하나의 고유한 리소스를 갖도록 설계된 아키텍쳐,
특정 URI를 통해 사용자가 원하는 정보 제공 받는 형식,
REST 방식으로 서비스 제공이 가능한 것을 RESTful하다고 한다.
15. REST 웹 서비스
: 웹의 모든 리소스를 URI로 표현하고 이를 구조적이고 유기적으로 연결하여 비상태 지향적인 방법,
일관된 method를 사용하여 리소스를 사용하는 웹 서비스 디자인 표준
** 반드시 4가지 속성을 지원해야 한다.
1) Addressablilty : 제공하는 모든 정보를 URI로 표시할 수 있어야 한다.
2) Connectedness : 하나의 리소스들은 서로 주변의 연관 리소스들과 연결되어 표현(Presentation)
3) Statelessness : 현재 클라이언트의 상태를 절대로 서버에서 관리하지 않아야 한다.
(REST에서는 상태가 서버가 아니라 클라이언트에 유지되며 매 요청마다 필요한 정보를 서버에 보낸다)
- 모든 요청은 일회성의 성격을 가지며 이전의 요청에 영향을 받지 말아야 한다.
- 세션을 유지 하지 않기 때문에 서버 로드 밸런싱이 매우 유리
- URI에 현재 state를 표현할 수 있어야 한다.
4) Homogeneous Interface(동일한 인터페이스)
- HTTP에서 제공하는 기본적인 4가지의 method와 추가적인 2가지의 method를 이용해서
리소스의 모든 동작을 정의(GET, POST, PUT, DELETE ) + (HEAD, OPTION)
- 대부분의 리소스 조작은 6가지의 method를 이용하여 대부분 처리 가능
(만일 이것들로만 절대로 불가능한 액션이 필요할 경우에는 POST를 이용하여 추가 액션을 정의할 수 있다)
16. 오늘날은 주로 XML, JSON 등을 response 하는 기술에 많이 적용된다.
자연스럽게 REST와 AJAX는 밀접하게 연계되고, API 서비스에서 많이 채용하는 설계방식
(이를 REST API 라고도 함, 이 서비스는 모바일앱 서비스 제작에도 매우 널리 사용)
17. @RestController를 이용하여 데이터를 response하는 3가지 방법
1) TEXT response
: @Controller로 생성한 컨트롤러 클래스의 경우, handler가 리턴하는 문자열은 뷰 파일이 된다.
2) JSON response
: 스프링에서 자바객체에서 JSON으로 변환해주는 라이브러리 설치 필요
3) XML response
: JAXB(Java Architecture for XML Binding) 기술 사용 필요
18. STS19_REST
** 메이븐 설정파일 pom.xml에서 jackson-databind, jackson-annotations, jackson-core 추가 작업 필요
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lec</groupId>
<artifactId>STS19_rest</artifactId>
<name>STS19_REST</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.6</java-version>
<org.springframework-version>5.2.1.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
<!-- Maven 빌드를 사용하는 Spring 에서 오라클 라이브러리 추가하기 -->
<!--dependencies 위에 설정 -->
<repositories>
<repository>
<id>oracle</id>
<name>ORACLE JDBC Repository</name>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
</repositories>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<!-- dependencies 안쪽 에 설정 -->
<!-- ojdbc6 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<!-- spring jdbc 사용 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- mybatis spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- Jackson databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
[추가] jackson-databind, jackson-annotations, jackson-core : https://mvnrepository.com 이용하기



> 스프링에서는 필요한 연관 파일을 자동 다운로드 됨, 따라서 아래와 그림과 같이 다운이 확인될 것임

> 만약 jackson-databind, jackson-annotations, jackson-core 중 하나라도 다운이 받아지지 않았다면
아래의 Compile Dependencies를 이용하여 다운이 안된 파일만 추가해주면 됨

** [src/main/java] com.lec.sts19_rest.controller > MyRestController.java
package com.lec.sts19_rest.controller;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.lec.sts19_rest.board.C;
import com.lec.sts19_rest.board.beans.BWriteDTO;
import com.lec.sts19_rest.board.beans.EmployeeListVO;
import com.lec.sts19_rest.board.beans.EmployeeVO;
import com.lec.sts19_rest.board.beans.IWriteDAO;
@RestController
@RequestMapping("/MyRest")
public class MyRestController {
@RequestMapping("/")
public String helloTEXT() {
return "Hello REST";
} // end helloTEXT()
@RequestMapping("/helloJSON")
public BWriteDTO helloJSON() {
BWriteDTO dto
= new BWriteDTO(100, "안녕하세요", "REST", "JSON이당!", 123, new Timestamp(100000));
return dto;
} // end helloJSON()
// JSON 데이터 <-- 자바 List<>
@RequestMapping("/listSJON")
public List<BWriteDTO> listJSON() {
IWriteDAO dao = C.sqlSession.getMapper(IWriteDAO.class);
return dao.select();
} // end listJSON()
// JSON 데이터 <-- 자바 배열
@RequestMapping("/arrJSON")
public BWriteDTO[] arrJSON() {
IWriteDAO dao = C.sqlSession.getMapper(IWriteDAO.class);
List<BWriteDTO> list = dao.select();
BWriteDTO[] arr = new BWriteDTO[list.size()];
return list.toArray(arr);
}
// JSON 데이터 <-- 자바 Map<k, v>
@RequestMapping("/mapJSON")
public Map<Integer, BWriteDTO> mapJSON() {
IWriteDAO dao = C.sqlSession.getMapper(IWriteDAO.class);
List<BWriteDTO> list = dao.select();
Map<Integer, BWriteDTO> map = new HashMap<Integer, BWriteDTO>();
for(BWriteDTO dto : list) {
map.put(dto.getUid(), dto);
}
return map;
} // end mapJSON()
// XML데이터 <-- 자바객체
@RequestMapping("/helloXML")
public EmployeeVO helloXML() {
return new EmployeeVO(100, "홍길동", 200, new int[] {10, 20, 30}, 34.2);
} // end EmployeeVO()
// XML데이터 <-- 자바 List<>
@RequestMapping("/listXML")
public EmployeeListVO listXML() {
EmployeeListVO employees = new EmployeeListVO();
EmployeeVO emp1 = new EmployeeVO(1, "김재현", 24, new int[] {78, 67, 92}, 34.2);
EmployeeVO emp2 = new EmployeeVO(2, "킹재현", 25, new int[] {22, 55, 88}, 34.2);
EmployeeVO emp3 = new EmployeeVO(3, "재현 킴", 27, new int[] {100, 200, 300}, 34.2);
employees.getEmployees().add(emp1);
employees.getEmployees().add(emp2);
employees.getEmployees().add(emp3);
return employees;
} // end listXML()
@RequestMapping("/read/{uid}")
public ResponseEntity<BWriteDTO> read(@PathVariable("uid") int uid) {
IWriteDAO dao = C.sqlSession.getMapper(IWriteDAO.class);
// 없는 값도 Status Code가 200이란 점이 문제!
// 없는 값은 404 에러가 발생할 수 있도록 처리 필요
BWriteDTO dto = dao.selectByUid(uid);
// 실패
if(dto == null) {return new ResponseEntity(HttpStatus.NOT_FOUND);} // 404 에러
// 성공
return new ResponseEntity<BWriteDTO>(dto, HttpStatus.OK); // 200
} // end read()
} // end Controller
** [src/main/java] com.lec.sts19_rest.board.beans > EmployeeVO.java
package com.lec.sts19_rest.board.beans;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "employee") // <employee>
public class EmployeeVO {
@XmlAttribute // "id" attribute
private Integer id;
@XmlElement // <name> element
private String name;
@XmlElement // <age> element
private int age;
@XmlElement // <score> elements(즉, element 들)
private int[] score;
// 어노테이션 없으면 XML변환에 포함 안됨
private double point;
public EmployeeVO() {}
public EmployeeVO(Integer id, String name, int age, int[] score, double point) {
super();
this.id = id;
this.name = name;
this.age = age;
this.score = score;
this.point = point;
}
// getter만 제공 : read-only 속성, immutable
public final Integer getId() {
return id;
}
public final String getName() {
return name;
}
public final int getAge() {
return age;
}
public final int[] getScore() {
return score;
}
public final double getPoint() {
return point;
}
}
** [src/main/java] com.lec.sts19_rest.board.beans > EmployeeListVO.java
package com.lec.sts19_rest.board.beans;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "employees") // <employees> ~ </employees>
public class EmployeeListVO {
// List 멤버 --> <emp>~</emp> .. 들로 만들어 진다.
@XmlElement
private List<EmployeeVO> emp = new ArrayList<EmployeeVO>();
public List<EmployeeVO> getEmployees() {
return emp;
}
}'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글
| 2020.07.06 (0) | 2020.07.06 |
|---|---|
| 2020.07.03 (0) | 2020.07.03 |
| 2020.07.01 (0) | 2020.07.01 |
| 2020.06.30 (0) | 2020.06.30 |
| 2020.06.29 (0) | 2020.06.29 |