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이 가장 먼저 로드
- root-context 설정 (service , persistence layer 설정)
- 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에서 조건을 주고 맞는지 확인하기 위해 사용
//오늘의 숙제
복습