본문 바로가기
☁︎KOSTA/☂KOSTA-SPRING

[KOSTA] Spring 기반 Cloud 서비스 구현 개발자 양성 (Day 82,83) - jQuery/Ajax, SpringMVC(xml, java config)

by 하_센세 2022. 11. 30.

2022.11.29 [TUE,WED]

- Day 82,83- 

 

수업 주요 목차

  • jQuery/Ajax
  • SpringMVC
    • xml 설정방식
    • java config 설정방식

🤖Review

 

jQuery/Ajax 복습 예제 1

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<title>serialize</title>
</head>
<body>
<div class="container pt-3">

<form id="f1" action="front">
  이름 :<input type="text" id="memberName" name="memberName">
   <input type="hidden" id="command" name="command" value="register"> 
   <select id="tool" name="tool">
   <option value="notepad">메모장</option>
   <option value="editplus">에디트플러스</option>
   <option value="eclipse">이클립스</option>
  </select> 
  <br>
  <input type="checkbox"  name="menu" value="beer">맥주<br>
   <input type="checkbox" name="menu" value="pizza">피자<br> 
   <input type="button" value="test" id="test">
 </form>
 
 <!--
		ajax 방식으로 전송시 
		data:"name=장기하&tool=메모장&menu=맥주&menu=피자  와 같은 query string 을 만들어 
		전송해야 한다 
		위와 같은 form data 의 query string을 간편하게 만들수 있는 jquery의 serialize() 함수를 테스트해본다
		data:$("#f1").serialize() 처럼 query string을 만들어 전송하면 됨 
 -->
</div>
<script type="text/javascript">
	$(function() {
		$("#test").click(function() {
			//alert("ajax 요청시 서버로 전송할 데이터는 \n query string 형식으로 전송해야 함");
			//form의 query string을 생성해준다.
			//alert($("#f1").serialize());
			//아래는 ajax 방식으로 서버에 데이터를 전송할 때 form 입력양식 요소별로 각각 query string을 만들어서 전송하는 방식
			/*
			$.ajax({
				type:"post",
				url:"AjaxTestServlet",
				data:"memberName="+$("#memberName").val()+"&command="+$("#command").val(),
				success:function(result){
					alert(result);
				}
			});//ajax
			*/
			//serialize() 함수를 이용하는 방식
			$.ajax({
				type:"post",
				url:"AjaxTestServlet",
				data:	$("#f1").serialize(),
				success:function(result){
					alert(result);
				}
			});//ajax
		});//click
	});//ready
</script>
</body>
</html>

 

serialize() 함수를 사용하지 않을 경우 query string이 길어지고 복잡해진다.

폼태그 작성을 신경써서 잘 해주면 serialize() 함수를 통해 간결하게 모든 정보를 한번에 넘길 수 있다.

 

 


 

🔎 jQuery/Ajax Eclipse 실습 내용 

 

1. 동적으로 생성된 요소에 대해 이벤트 처리를 등록

  • 아래는 이벤트 처리에 대한 동작이 되지 않는다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<title>jquery-on</title>
</head>
<body>
<div class="container pt-3">
<!-- 아래 버튼을 누르면 testDiv 영역에 동적으로 버튼을 생성한다 -->
<button type="button" id="createBtn">버튼생성</button>
<hr>
<div id="testDiv"></div>
</div>
<script type="text/javascript">
	$(function() { //document ready 시점 (document 객체가 준비되면 - 이미지, 동영상 로드와 상관없음)
		//위의 익명함수가 한번 실행된다. 이 때 현 체이지의 요소의 이벤트 처리를 등록(바인딩)
		//아래는 id createBtn이 클릭되면 동적으로 testDiv id 영역에 버튼을 생성한다.
		$("#createBtn").click(function() {
			//alert($(this).html());
			$("#testDiv").empty();
			$("#testDiv").append("<button type='button' class='testBtn'>동적버튼</button>");
		});//click
		$(".testBtn").click(function() {
			alert("동적버튼클릭");
		});//click
	});//ready
</script>
</body>
</html>
  • 이유는 document가 ready되는 시점에 존재하지 않는 요소이기 때문에(동적으로 생성된 요소에 대한 이벤트 처리는 jQuery의 on으로 처리하면 된다.)

 

  • jQuery on: 동적으로 생성되는 요소에 대한 이벤트 처리를 위한 함수
  • syntax  $(셀렉터-동적요소를 가진 부모요소).on("이벤트타입","셀렉터",익명함수);
		$("#testDiv").on("click", ".testBtn",function() {
			alert("동적 버튼 클릭");
		}); //on

 

2. jQuery data

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<title>jquery data 이용</title>

</head>
<body>
<div class="container pt-3">
<span id="member"  data-id="javaking" data-age="13">회원정보</span> <!-- span 에다가 데이터를 심어두는 것  -->
<br><br>
<button type="button" id="testBtn1">data 테스트1 속성 정보를 get하는 버튼</button><br>
<button type="button" id="testBtn2">data 테스트2 속성 정보를 set하는 버튼</button><br>
<button type="button" id="testBtn3">data 테스트3 customer id 영역에 data 정보를 set</button><br>
<button type="button" id="testBtn4">data 테스트4 customer id 영역에 data 정보를 get</button><br><br>
<span id="customer">고객정보</span>
<script type="text/javascript">
	$(function() {
		$("#testBtn1").click(function() {
			alert($("#member").data("id"));
			alert($("#member").data("age"));
		}); // click
		$("#testBtn2").click(function() {
			$("#member").data("id","spring").data("age","20");
			alert($("#member").data("id"));
			alert($("#member").data("age"));
		}); // click
		$("#testBtn3").click(function(){
			// data 속성에 json 정보를 할당할 수도 있다.
			$("#customer").data("id","springking").data("detailInfo",{age:19,nick:"손흥민"});
		});// click
		$("#testBtn4").click(function(){
			alert($("#customer").data("id"));
			//data 속성에 저장된 json 정보를 받아와 출력해본다.
			let customer=$("#customer").data("detailInfo");
			alert(customer.age+" "+customer.nick);
		});// click
	}); // ready
</script>
</div>
</body>
</html>

 

 

Spring MVC: 웹 어플리케이션을 위한 spring 기술

  • 주요 디자인 패턴으로는 MVC 패턴, frontcontroller 디자인 패턴이 적용되어 있다.

Legacy : 전통적인 스타일

 

DispatcherServlet
Front Controller 로 모든 클라이언트의 요청을 수신하고 처리를 제어
HandlerMapping
클라이언트의 요청을 처리할 담당 컨트롤러를 결정
HandlerAdapter
담당 컨트롤러 handler 메서드의 호출을 담당
Controller
클라이언트의 요청을 처리 , Model 계층과 연동 후 결과 정보를 ModelAndView 객체에 저장해서 반환
ViewResolver
클라이언트에게 응답하기 위한 View를 선택하는 방식을 제공
View
클라이언트에게 응답하는 로직을 정의
ModelAndView
응답할 View 정보와 View에 전달하기 위한 데이터를 가지고 있는 객체

 

🧚‍♂️ WEB-INF는 url로 직접 접근이 불가능하다

  • WHY? Front Controller pattern을 위하여 (모든 요청의 진입점, 공통정책을 위해)

 

🔎 Spring MVC 실습 내용 1

 

1. Dynamic Web Project 생성

2. Configure -> Maven Project로 변경

3. pom.xml에 dependency 추가

<dependencies>
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-webmvc</artifactId>
		<version>5.3.20</version>
	</dependency>
	<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>jstl</artifactId>
		<version>1.2</version>
	</dependency>
</dependencies>

 

4. web.xml 설정

  • DispatcherServlet의 name 을 이용해 spring에서 자신의 설정 파일을 로드한다.
  • [서블릿이름]-servlet.xml => spring 설정파일 아래 설정에 의해 spring 설정파일은 dispatcher-servlet.xml이 된다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
	id="WebApp_ID" version="4.0">
	<display-name>spring17-mvc-basic</display-name>
	<welcome-file-list>
		<welcome-file>home.do</welcome-file>
	</welcome-file-list>
	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
	<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>

 

5.  dispatcher-servlet.xml 설정

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Annotation 기반 spring IOC DI 설정  -->
<context:component-scan base-package="org.kosta"/>
<!-- HanddlerMapping : 명시하지 않으면 기본적으로 지원하지만 존재를 확인하기 위해 직접 명시해본다. -->
<!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean> -->
<!-- 
	  SpringMVC 설정 : 어노테이션 MVC 를 지원 , @RequestMapping @GetMapping @PostMapping @ResponseBody 등을 지원 
-->
<mvc:annotation-driven/>
<!-- 
		ViewResolver : client에게 응답하는 방식을 설정
		Controller에서 view name을 ok 로 반환하면  /WEB-INF/views/ok.jsp가 실행되어 클라이언트에게 응답한다 
		WEB-INF 아래 디렉토리에 view jsp들을 배치하는 이유는 모든 클라이언트는 Front 인 DispatcherServlet 을 
		통해서 서비스를 받도록 하기 위해서임 
		WEB-INF 아래 배치하면 클라이언트가 직접 접근할 수 없음 
		===> 모든 클라이언트의 요청을 하나의 진입점으로 집중시켜 공통정책을 수행하는 FrontController Design Pattern을 
			  위해서임 
 -->
 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
 	<property name="prefix" value="/WEB-INF/views/" />
 	<property name="suffix" value=".jsp" />
 </bean>
</beans>

 

 

6. WEB-INF 아래  'views' 폴더 생성 후 index.jsp 파일 생성

<%@ 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>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
 <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
<h3>spring MVC study</h3><br>
${testInfo}
<form action="findMemberById.do">
<input type="text" name="memberId" placeholder="회원아이디" required="required">
<button type="submit">검색</button>
</form>
<br><br>
<form action="findMemberById2.do">
<input type="text" name="memberId2" placeholder="회원아이디" required="required">
<button type="submit">검색</button>
</form>
<br><br>
<form action="registerMember.do" method="post">
<!-- MemberVO의 인스턴스 변수명과 입력양식 name을 일치시킨다 -->
<input type="text" name="id" placeholder="회원아이디" required="required"><br>
<input type="password" name="password" placeholder="패스워드" required="required"><br>
<input type="text" name="name" placeholder="이름" required="required"><br>
<input type="text" name="address" placeholder="주소" required="required"><br>
<button type="submit">가입</button>
</form>
<br><br>
<a href="paramTest.do?nick=조규성&age=25">Controller test1</a><br><br>
<a href="paramTest.do?nick=아이유&age=12">Controller test2</a><br><br>
</div>
</body>
</html>

7. VO 생성

package org.kosta.myproject.controller;

public class MemberVO {
	private String id;
	private String password;
	private String name;
	private String address;
	public MemberVO() {
		super();
	}
	public MemberVO(String id, String password, String name, String address) {
		super();
		this.id = id;
		this.password = password;
		this.name = name;
		this.address = address;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	@Override
	public String toString() {
		return "회원정보 [id=" + id + ", password=" + password + ", name=" + name + ", address=" + address + "]";
	}
}

 

8. Controller 생성 + JSP 생성

  • HomeController
    • home() method의 return 값은 view name이다.
    • spring mvc 설정(dispatcher-servlet.xml) ViewResolver에 의해 WEB-INF/views/index.jsp로 응답한다.
package org.kosta.myproject.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HomeController {
	@RequestMapping(value={"/","home.do"})
	public String home() {
		System.out.println("HomeController home");
		return "index"; 
	}
}

위의 method를 아래와 같이 표현할 수 있다(~17년도까지 아래 방식으로 주로 사용)

@RequestMapping(value={"/","home.do"}) //url 패턴을 지정
public ModelAndView home() {
	// request.setAttribute("testInfo","공유정보"); -> 아래처럼 처리할 수 있다.
	return new ModelAndView("index","testInfo","컨트롤러에서 공유한 정보"); //view name을 setting
}

 

  • MemberController
package org.kosta.myproject.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MemberController {
	@RequestMapping("findMemberById.do")
	public ModelAndView findMemberById(String memberId) { //request.getParameter() 처리
		System.out.println("findMemberById 검색아이디: "+memberId);
		//database에 회원아이디로 spring이 존재한다고 가정
		ModelAndView mv=new ModelAndView();
		if(memberId.equals("spring")) {
			//viewResolver에 의해 /WEB-INF/views/findMemberById-ok.jsp
			mv.setViewName("findMemberById-ok");
			//request.setAttribute 역할
			mv.addObject("memberInfo", "이강인, 마요르카");
		} else {
			//viewResolver에 의해 /WEB-INF/views/findMemberById-fail.jsp
			mv.setViewName("findMemberById-fail");						
		}
		return mv;
	}
	// 위가 전통적이고 기초적인 방식, 아래가 최근 스타일
	@RequestMapping("findMemberById2.do")
	public String findMemberBuId2(String memberId2, Model model) {
		String viewName=null;
		System.out.println(memberId2);
		if(memberId2.equals("spring")) {
			model.addAttribute("memberInfo","컨트롤러에서 공유한 회원정보2");
			viewName="findMemberById-ok";
		} else {
			viewName="findMemberById-fail";
		}
		return viewName;
	}
	//HandlerAdapter가 클라이언트가 전송한 폼데이터를 매개변수에 정의한 MemberVO 객체로 생성해서 전달해준다.
	//@RequestMapping("registerMember.do")
	//Post만 가능하게 처리
	@PostMapping("registerMember.do")
	public String registerMember(MemberVO memberVO) {
		System.out.println("registerMember "+memberVO);
		//redirect방식: 응답을 받은 브라우저가 지정한 url로 이동해서 페이지를 응답받는다. 
		return "redirect:registerResult.do";
	}
	
	@RequestMapping("registerResult.do")
	public String registerResult(MemberVO memberVO) {
		// ViewResolver에 의해 WEB-INF/views/register-result.jsp로 응답
		return "register-result"; 
	}
}

 

🧚‍♂️ 막간 리뷰

  • forward : 기존 request, response 유지 , 웹컨테이너 상에서 이동, 새로고침시 재동작 가능 (검색/조회에 주로 사용)
  • redirect : 기존 request, response 유지 X, url 지정하여 이동명령, 새로고침시 재동작 불가능 (등록,삭제에 주로 사용)

  • MvcTestController
package org.kosta.myproject.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MvcTestController {
	@RequestMapping("paramTest.do")
	public String paramTest(String nick,int age) {
		System.out.println(nick);
		if(age>19) {
			System.out.println("성인 "+age+"세 "+nick);
		} else {
			System.out.println("미성년");			
		}
		return "param-result";
	}
}
  • 아이디 검색 OK JSP
<%@ 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>
<html>
<head>
<meta charset="UTF-8">
<title>OK</title>
 <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
<a href="home.do">Home 으로 돌아가기</a><br><br>
검색결과 : ${memberInfo}
</div>
</body>
</html>
  • 아이디 검색 FAIL JSP
<%@ 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>
<html>
<head>
<meta charset="UTF-8">
<title>FAIL</title>
 <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
<script type="text/javascript">
	alert("회원정보가 존재하지 않습니다.");
	location.href="home.do";
</script>
</div>
</body>
</html>
  • 회원가입 result JSP
<%@ 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>
<html>
<head>
<meta charset="UTF-8">
<title>register</title>
 <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
<a href="home.do">Home으로 돌아가기</a><br>
회원가입을 축하합니다!
</div>
</body>
</html>
  • param-result JSP
<%@ 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>
<html>
<head>
<meta charset="UTF-8">
<title>param-result</title>
 <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
Spring MVC Controller parameter test result page<br><br>
</div>
</body>
</html>

1. 회원찾기 1

2. 회원찾기 2

3. 회원가입

4. Controller test

 


 

 

 

 

프로그램 시작시 web.xml이 가장 먼저 로드

  1. root-context 설정 (service , persistence layer 설정)
  2. servlet-context 설정 (view 와 conroller 설정)

 

 

 

🔎 Spring MVC 실습 내용 2 (Spring Legacy XML 설정방식)

1. 환경설정

- maven(pom.xml)

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>spring18-mvc-mybatis-junit-xmlconfig</groupId>
	<artifactId>spring18-mvc-mybatis-junit-xmlconfig</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<release>11</release>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.2.3</version>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<!-- springmvc , slf4j,logback 사용을 위해 commons logging은 제외  -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>5.3.20</version>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- db 연동을 위한 mybatis 설정  -->
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<version>21.5.0.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
			<version>2.8.0</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.6</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>5.3.20</version>
		</dependency>
		<!--  view jstl  -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<!--  AOP -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.1</version>
		</dependency>
		<!--  Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>1.7.25</version>
		</dependency>
		<!-- ajax/json -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.10.0</version>
		</dependency>
		<!-- spring junit test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.13</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>5.3.20</version>
		</dependency>
	</dependencies>
</project>

 

-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
	id="WebApp_ID" version="4.0">
	<!-- The definition of the Root Spring Container shared by all Servlets 
		and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>

	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<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>

프로그램이 시작되면 가장 먼저 Context를 로드하게 되고 위의 경우 /WEB-INF/spring/root-context.xml 이다. 그래서 이 경로에 root-context.xml을 생성했다.

 

- root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

	<!-- Root Context: defines shared resources visible to all other web components -->
	<context:component-scan
		base-package="org.kosta.myproject.model" />
	<!-- dbcp -->
	<bean id="dbcp" class="org.apache.commons.dbcp2.BasicDataSource">
		<property name="driverClassName"	value="oracle.jdbc.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@13.209.97.194:1521:xe" />
		<property name="username" value="scott" />
		<property name="password" value="tiger" />
	</bean>
	<!-- spring과 mybatis framework 연동설정 -->
	<bean id="sqlSessionFactory" 	class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dbcp" />
		<property name="typeAliasesPackage"	value="org.kosta.myproject.model.vo" />
		<!-- db의 underscore와 application의 camelcase를 자동매핑(컬럼명:director_id, 변수명:directorId를 자동매핑) -->
		<property name="configuration">
			<bean class="org.apache.ibatis.session.Configuration">
				<property name="mapUnderscoreToCamelCase" value="true" />
			</bean>
		</property>
	</bean>
	<!-- SqlSessionTemplate : 반복적인 db 연동 작업( openSession() ~ commit() ~ close())을 
	Template에서 지원 AOP 기반 트랜잭션 처리를 지원 영속성 계층 DAO에서 DI 방식으로 주입받아 사용할 Bean -->
	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg ref="sqlSessionFactory" />
	</bean>
	<!-- MyBatis Proxy 설정 -->
	<mybatis-spring:scan base-package="org.kosta.myproject.model.mapper" />
</beans>

MyBatis/DB 관련 설정을 해주고 필요한 각각의 패키지를 생성해줬다. 그리고 그 후에 DispatcherServlet 설정파일이 로드되기 때문에 위치에 맞게 servlet-context.xml을 생성해줬다. 그리고 url을 /로 설정해주어 모든 request가 DispatcherServlet을 거치도록 설정해준다.(다른 jsp파일 또는 이미지에 직접 접근이 불가능하다) 

 

- 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>
	<!-- model 영역은 root-context.xml에서 설정했으므로 controller 영역만 component scan 하도록 설정  -->
	<context:component-scan base-package="org.kosta.myproject.controller" />
</beans:beans>

 

<annotation-driven /> : @Controller annotation을 사용할 수 있게 해주는 설정 (mvc를 따로 붙여주지 않은 이유는 위 beans xmlns에 이미 작성되어 있기 때문이다.

 

<resources mapping="/resources/**" location="/resources/" /> : 이제 모든 request가 다 Dispatcher Servlet을 거쳐야하기 때문에 직접적인 접근이 가능하도록 하기 위해 매핑을 해준다. (front controller 패턴 적용으로 mapping이 된 디렉토리 아래에 있는 파일만 url로 접근 가능하다.)

 

 

그 아래는 ViewResolver가 디렉토리명, 확장자명을 자동으로 붙여주도록 설정하는 것

 

-logback.xml, sql 파일 등 src/main/resources 디렉토리에 생성

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
	scan="true" scanPeriod="30 seconds" 
	주기적으로 configuration 파일을 읽어 Logback의 설정을 재구성
	Application실행중일때 별도의 재시작없이 설정 파일을 수정하고 적용가능 
	scanPeriod  default 1분 
 -->
<configuration scan="true" scanPeriod="30 seconds">
<!-- 
        콘솔 로깅
        encoder  Pattern로그 출력 포맷
		%d{HH:mm}은 로그 출력 시간 포맷
		%-5level은 로그 레벨  5 고정폭  출력
		%logger는 logger의 이름 출력 { }  length
		%msg    사용자 메시지 출력
		%n  줄바꿈
  -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</Pattern>
        </encoder>
    </appender>
<!-- 
    파일 로깅 
	RollingFileAppender : 일별로 로그 파일을 백업하면서 로깅
	maxHistory : 30일 지나면 오래된 순서부터 로그파일을 지워준다 
 -->
 
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>report.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>report-%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</Pattern>
        </encoder>
    </appender>
  <!--  특정 패키지 이하 로깅 레별을 별도로 설정 가능  -->
    <logger name="org.springframework" level="warn"/>
    <logger name="org.kosta.myproject" level="debug"/>

    <root level="info">
        <appender-ref ref="CONSOLE"/>
<!--         <appender-ref ref="FILE"/> -->
    </root>
</configuration>

 

2. home.jsp 생성

<%@ 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>
<html>
<head>
<meta charset="UTF-8">
<title>spring legacy mvc</title>
 <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
<h4>Spring Legacy Project Test(IOC/DI MVC MyBatis JUnit)</h4>
<br>
전체 고객수 ${totalCustomerCount} 명
<br><br>
<form action="findCustomerById">
	<input type="text" name="customerId" placeholder="고객 아이디" required="required">
	<button type="submit">검색</button>
</form>
<br><br><br><br>
<img src="resources/springmvc.png" width=80%>
</div>
</body>
</html>

 

3. CustomerVO 생성

package org.kosta.myproject.model.vo;

public class CustomerVO {
	private String id;
	private String name;
	private String address;
	
	public CustomerVO() {
		super();
	}

	public CustomerVO(String id, String name, String address) {
		super();
		this.id = id;
		this.name = name;
		this.address = address;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "CustomerVO [id=" + id + ", name=" + name + ", address=" + address + "]";
	}
}

 

4. HomeController 생성

package org.kosta.myproject.controller;

import org.kosta.myproject.model.mapper.CustomerMapper;
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
public class HomeController {
	private CustomerMapper customerMapper;
	
	@Autowired
	public HomeController(CustomerMapper customerMapper) {
		super();
		this.customerMapper=customerMapper;
	}
	//context-path(어플리케이션명) : /, home => 여러개 지정할 수 있음
	@RequestMapping(value= {"/","home"}) 
	public String home(Model model) { //Model은 DI로 주입받는 것
		model.addAttribute("totalCustomerCount",customerMapper.getTotalCustomerCount());
		return "home"; //WEB-INF/views/home.jsp로 응답
	}
}

 

5. CustomerMapper.java

package org.kosta.myproject.model.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.kosta.myproject.model.vo.CustomerVO;

@Mapper
public interface CustomerMapper {

	int getTotalCustomerCount();

	CustomerVO findCustomerById(String id);
	
}

 

6. CustomerMapper.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="org.kosta.myproject.model.mapper.CustomerMapper">
<select id="getTotalCustomerCount" resultType="int">
	SELECT COUNT(*) FROM spring_customer
</select>
<select id="findCustomerById" parameterType="string" resultType="CustomerVO">
	SELECT id,name,address FROM spring_customer WHERE id=#{value}
</select>
</mapper>

 

 

 

 

예제 요구사항

CustomerController 정의 생성자를 통한 DI (CustomerMapper) 컨트롤러 메서드 정의 

고객정보가 DB에 존재하지 않으면 findCustomerById-fail.jsp -> alert(고객 정보가 존재하지 않는다. home.jsp로 이동)
(viewresolver에 의해 WEB-INF/views/findCustomerById-fail.jsp로 응답)


<a href="home">HOME</a>

존재하면 findCustomerById-ok.jsp 로 응답 (아이디: java 이름 : 아이유 주소 : 오리) 

 

- CustomerController

package org.kosta.myproject.controller;

import org.kosta.myproject.model.mapper.CustomerMapper;
import org.kosta.myproject.model.vo.CustomerVO;
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
public class CustomerController {
	private CustomerMapper customerMapper;
	@Autowired
	public CustomerController(CustomerMapper customerMapper) {
		super();
		this.customerMapper = customerMapper;
	}
	
	@RequestMapping("findCustomerById")
	public String findCustomerById(String customerId ,Model model) {
		CustomerVO customerVO=customerMapper.findCustomerById(customerId);
		String viewName=null;
		if(customerVO!=null) { 
			model.addAttribute("customerVO",customerVO);
			viewName="findCustomerById-ok";
		} else {
			viewName= "findCustomerById-fail";
		}
			return viewName;
	}
}

 

- findCustomerById-fail.jsp

<%@ 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>
<html>
<head>
<meta charset="UTF-8">
<title>FAIL</title>
 <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
	<script type="text/javascript">
		alert("고객 정보가 존재하지 않는다");
		location.href="home";
	</script>
</div>
</body>
</html>

 

- findCustomerById-ok.jsp

<%@ 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>
<html>
<head>
<meta charset="UTF-8">
<title>OK</title>
 <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
	아이디: ${customerVO.id} 이름: ${customerVO.name} 주소: ${customerVO.address}
	<br><br>
	<a href="home">HOME</a>
</div>
</body>
</html>

 

CustomerController if문 조건에 customerVO!=null을 사용할지 customerVO.getId.equals(customerId) 이걸 사용할지 긴가민가할 때 민가에 걸었더니 널포인터익셉션이 떴었음! 생각을 좀 해보면 비교할 때 customerVO.getId 자체가 null이기 때문에 비교가 불가능하지...멍츙멍츙 

 

 

🔎 Spring MVC 실습 내용 3 (Spring Legacy JavaConfig 설정방식)

 

- WebAppConfig

package org.kosta.myproject.config;

import javax.servlet.Filter;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/*
 *   springmvc project를 java Config 스타일로 설정하기 위한 클래스 
 *   기존 SprnigMVC의 web.xml 역할을 한다 
 *   -> FrontController인 spring의 DispatcherServlet 설정 , url-pattern 정의 
 *   	 CharacterEncodingFilter를 이용한 한글처리 설정  
 */
public class WebAppConfig extends AbstractAnnotationConfigDispatcherServletInitializer{

	@Override
	protected Class<?>[] getRootConfigClasses() {		
		return new Class[] {RootContextConfig.class};// 프로젝트 model 영역 설정 ( dbcp , mybatis , mybatis mapper 등 ) 
	}
	@Override
	protected Class<?>[] getServletConfigClasses() {		
		return new Class[] {ServletContextConfig.class};// springmvc web 영역 설정( view 와 controller 설정 ) 
	}
	@Override
	protected String[] getServletMappings() {		
		return new String[] {"/"};// DispatcherServlet에 대한 url pattern 지정 
	}
	@Override
	protected Filter[] getServletFilters() {		// 인코딩 설정 
		CharacterEncodingFilter encodingFilter=new CharacterEncodingFilter();
		encodingFilter.setEncoding("utf-8");
		return new Filter[] {encodingFilter};
	}
}

 

- RootContextConfig

package org.kosta.myproject.config;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/*
 *  기존 springmvc에서 root-context.xml 설정 역할을 하는 클래스
 *  web을 제외한 service , persistence layer 설정 (  dbcp , mybatis 연동 설정 , MapperScan 설정 )  
 */
@Configuration
@MapperScan(value = "org.kosta.myproject.model.mapper") // MyBatis MapperProxy 를 위한 설정 
public class RootContextConfig {
	@Bean // 메서드에서 반환하는 객체를 스프링 컨테이너에 bean으로 등록하고 관리하게 하는 어노테이션 
	public DataSource dataSource() {
		BasicDataSource dataSource=new BasicDataSource();
		dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
		dataSource.setUrl("jdbc:oracle:thin:@13.209.97.194:1521:xe");
		dataSource.setUsername("scott");
		dataSource.setPassword("tiger");
		return dataSource;
	}
	
	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception{
		SqlSessionFactoryBean sessionFactory=new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource);		
		// class 명으로 자동 별칭을 정해주는 옵션 -> 만약 이 설정을 하지 않으면 Mapper.xml의 parameterType 과 
		// resultType에 모두 resultType="org.kosta.myproject.model.ProductVO" 와 같이 모든 패키지명을 기술해야 한다 
		sessionFactory.setTypeAliasesPackage("org.kosta.myproject.model.vo");				
		// db 는 underscore로 합성어를 표현 (product_no) , java 는 camelcase로 합성어를 표현 (productNo)
		// db의 underscore와 java의 camelcase를 자동 연결하는 설정 
		org.apache.ibatis.session.Configuration conf=new org.apache.ibatis.session.Configuration();
		conf.setMapUnderscoreToCamelCase(true);
		sessionFactory.setConfiguration(conf);		
		return sessionFactory.getObject();
	}  
	// Spring과 MyBatis 연동 
	// SqlSessionTemplate : AOP 기반 Transaction 을 지원 
	@Bean
	public SqlSessionTemplate sqlSession(SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

 

- ServletContextConfig

package org.kosta.myproject.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/*
 *  기존 springmvc 의 servlet-context.xml 설정 역할을 하는 클래스 
 *  web( view 와 conroller ) 관련 설정을 한다 ( controller bean , annotation 기반 springmvc 설정 , viewResolver 설정 , 정적 자원에 대한 설정 )
 */
@Configuration
@EnableWebMvc // 어노테이션 기반 SpringMVC 컨트롤러 설정( <mvc:annotation-driven/> 설정 역할 )
@ComponentScan("org.kosta.myproject.controller") // bean 생성 등록 관리 
public class ServletContextConfig implements WebMvcConfigurer{
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {	//servlet-context.xml의 viewResolver 설정 	
		WebMvcConfigurer.super.configureViewResolvers(registry);
		registry.jsp("/WEB-INF/views/", ".jsp");
	}
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {		
		WebMvcConfigurer.super.addResourceHandlers(registry);
		//<resources mapping="/resources/**" location="/resources/" />
		registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
	}
}

 

나머지는 xml방식과 동일하고(복사&붙여넣기 함) 동일하게 동작된다.

 


JUnit : Java 단위테스트 Framework

- TDD(Test Driven Development)를 위한 프레임워크

- Spring과 연동하여 IOC/DI 기반의 테스트를 할 수 있다.

package org.kosta.myproject.test;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kosta.myproject.model.mapper.CustomerMapper;
import org.kosta.myproject.model.vo.CustomerVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml") // model만 로딩해서 단위테스트 가능
public class UnitTestCustomer {
	@Autowired
	private CustomerMapper customerMapper; //4버전에서는 필드주입
	
	@Test
	public void testDI() {
		//System.out.println(customerMapper+" DI");
		Assert .assertNotNull(customerMapper); // 이 객체가 not null일 것으로 예상함
		Assert .assertNull(customerMapper); // 이 객체가 not null일 것으로 예상함
	}
	
	@Test
	public void getTotalCustomerCount() {
		int count=customerMapper.getTotalCustomerCount();
		System.out.println("junit test customer count: "+count);
		Assert.assertEquals(2, count); // 첫번째 매개변수는 예상값, 두번째 매개변수는 실제값
	}
	
	@Test
	public void findCustomerById() {
		String id="java";
		CustomerVO customerVO=customerMapper.findCustomerById(id);
		Assert.assertNotNull(customerVO);
	}	
}

 

@Test → junit 단위 테스트를 위한 Annotation

Assert → junit에서 조건을 주고 맞는지 확인하기 위해 사용

 

 


//오늘의 숙제

복습