웹_프론트_백엔드/JAVA프레임윅기반_풀스택

2020.07.02

shine94 2020. 7. 2. 09:34

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 속성명 &lt; 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