Spring은 Controller, View 등을 사용하기 편하도록 MVC 패턴을 모두 구현을 해 두었다.
Servlet/JSP에서는 웹 요청 처리와 통제를 Controller 역할로 지정해 주었지만, Spring MVC에서는 Controller라는 이름이 전용으로 있으므로 이를 사용한다.
🌿Controller
Ex01Controller.java: jsp 연결
package com.test.spring.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class Ex01Controller implements Controller {
//doGet/doPost == handleRequest
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//업무 진행..
//뷰 호출(JSP)
//RequestDispatcher > forward()
//ModelAndView 사용
//- Model > 데이터 전송
//- View > JSP
ModelAndView mv = new ModelAndView(); //jsp를 부르기 위한 객체 생성
//mv.setViewName("JSP 경로");
//mv.setViewName("/WEBAPP/views/ex01.jsp");
mv.setViewName("ex01");
return mv;
}
}
이 컨트롤러는 서블릿은 아니지만 서블릿의 컨트롤러 같은 역할을 한다.
Spring은 컨트롤러 역할을 하는 것을 Controller를 상속받아서 사용하도록 하는데, 서블릿이 아니기 때문에 가상주소 어노테이션이 없다.
전부 스프링 환경 하에서 관리되므로 <bean>으로 관리해야 하며, 이를 위해 xml이 필요하다.
컨트롤러는 웹 요청과 관련되어 있기 때문에 servlet-context.xml에서 작업한다.
servlet-context.xml: jsp 연결
<?xml version="1.0" encoding="UTF-8"?>
<!-- beans:beans == beans 빈과 동일하지만 네임스페이스를 안 적으면 안 되는 상황으로 생각하면 된다. -->
<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>
<!--
InternalResourceViewResolver > ViewResolver
-->
<context:component-scan base-package="com.test.spring" />
<!-- 컨트롤러 인식 + 가상 주소 매핑 -->
<beans:bean name="/ex01.do" class="com.test.spring.controller.Ex01Controller"></beans:bean>
</beans:beans>
id는 안 쓰고 name을 쓰는데, 이때 가상 주소를 붙인다. 이게 컨트롤러의 가상 주소를 만드는 방법이다. 이제 다시 컨트롤러로 간다.
Multiple 에러 발생
Multiple Contexts have a path of "/spring".
현재 아파치 톰캣에 동일한 Path가 2개가 있어서 Multiple 오류가 발생한다.
처음 MVC 프로젝트를 만들 때 메인 패키지를 적도록 했다. 그런데 하필이면 두 프로젝트 모두 com.test.spring이라고 적었는데, 마지막 3번째 단어가 Root Context Path로 적히기 때문에 오류가 발생한 것이다.
HTTP 상태 404-찾을 수 없음
- http://localhost:8090/spring/WEB-INF/classes/com/test/spring/controller/Ex01Controller.java
컨트롤러 그대로 자바 주소가 뜬다. 스프링은 자동으로 가상 주소가 붙지 않기 때문이다.
그래서 spring/뒤를 모두 지우고 http://localhost:8090/spring/ex01.do로 직접 주소를 작성해야 한다.
주소 변경
//mv.setViewName("/WEBAPP/views/ex01.jsp");
mv.setViewName("ex01");
- http://localhost:8090/spring/ex01.do
그런데 이번에는 확장자가 이상해서 오류가 발생한다.
스프링은 장치가 하나 더 들어 있어서 이름을 ex01로 파일 이름만 작성해 주어야 한다.
이제 ex01.jsp 파일이 제대로 실행된다.
InternalResourceViewResolver > ViewResolver
<!-- 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>
<!--
InternalResourceViewResolver > ViewResolver
-->
스프링이 이름 앞에 ViewResolver를 통해서 "/WEB-INF/views/" +"ex01" + ".jsp"로 접두어를 붙이기 때문에 이러한 에러가 발생했던 것이다.
이 기능은 필수로 사용해야 하는 기능이 아니므로, 사용하지 않을 경우 주석 처리하면 된다.
위 그림에서 Ex01Controller는 제일 앞에 있는 컨트롤러라고 해서 Front-Controller라고도 부른다.
클라이언트로부터 어떤 주소가 하나 들어오면 그것과 똑같이 생긴 <bean>태그를 Server에서 찾는다.
요청이 들어온 /ex01.do와 똑같이 생긴 <bean>을 호출하면 해당 클래스 인스턴스를 찾아서 호출하며, 실행의 권한이 컨트롤러 클래스로 넘어가게 된다.
JSP로 호출할 때 방식은 request를 가지고 데이터를 담아서 전송해도 되지만, Spring 방식으로 데이터를 담는 역할과 전송하는 역할을 동시에 하는 ModelAndView로 return mv() forsward 한다
이 과정은 면접에서 자주 나오는 질문이기도 하다.
전체 코드
Ex01Controller.java
package com.test.spring.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class Ex01Controller implements Controller {
//doGet/doPost == handleRequest
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//업무 진행..
//뷰 호출(JSP)
//RequestDispatcher > forward()
//ModelAndView 사용
//- Model > 데이터 전송
//- View > JSP
ModelAndView mv = new ModelAndView(); //jsp를 부르기 위한 객체 생성
//mv.setViewName("JSP 경로");
//mv.setViewName("/WEBAPP/views/ex01.jsp");
mv.setViewName("ex01");
request.setAttribute("name", "Isaac");
mv.addObject("age", "23"); //권장
//기존 방식으로도 전달은 되지만 ModelAndView를 사용하는 것을 권장한다.
return mv;
}
}
ex01.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Ex01</h1>
<div>이름: ${name}</div>
<div>나이: ${age}</div>
</body>
</html>
servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- beans:beans == beans 빈과 동일하지만 네임스페이스를 안 적으면 안 되는 상황으로 생각하면 된다. -->
<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>
<!--
InternalResourceViewResolver > ViewResolver
-->
<context:component-scan base-package="com.test.spring" />
<!-- 컨트롤러 인식 + 가상 주소 매핑 -->
<beans:bean name="/ex01.do" class="com.test.spring.controller.Ex01Controller"></beans:bean>
</beans:beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 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> <!-- 서블릿이라는 객체가 xml객체를 읽어들였기 때문에 어떤 작업을 했는지 알고 있다. -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern> <!-- 모든 url이라는 의미이다. -->
</servlet-mapping>
</web-app>
🌿DB 작업
Ex02Controller.java
package com.test.spring.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.test.spring.persistence.SpringDAO;
import com.test.spring.persistence.SpringDAOImpl;
public class Ex02Controller implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//업무 중 일부 > DB 작업 > DAO 위임
SpringDAO dao = new SpringDAOImpl();
int count = dao.getCount();
ModelAndView mv = new ModelAndView();
mv.addObject("count", count);
mv.setViewName("ex02");
return mv; //null > mv
}
}
servlet-context.xml
<beans:bean name="/ex02.do" class="com.test.spring.controller.Ex02Controller"></beans:bean>
SpringDAO.java
package com.test.spring.persistence;
public interface SpringDAO {
int getCount();
}
SpringDAOImpl.java
package com.test.spring.persistence;
public class SpringDAOImpl implements SpringDAO {
@Override
public int getCount() {
return 100; //select 한 값
}
}
ex02.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Ex02</h1>
<div>count: ${count}</div>
</body>
</html>
🍃Ex02Controller.java
package com.test.spring.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class Ex01Controller implements Controller {
//doGet/doPost == handleRequest
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//업무 진행..
//뷰 호출(JSP)
//RequestDispatcher > forward()
//ModelAndView 사용
//- Model > 데이터 전송
//- View > JSP
ModelAndView mv = new ModelAndView(); //jsp를 부르기 위한 객체 생성
//mv.setViewName("JSP 경로");
//mv.setViewName("/WEBAPP/views/ex01.jsp");
mv.setViewName("ex01");
request.setAttribute("name", "Isaac");
mv.addObject("age", "23"); //권장
//기존 방식으로도 전달은 되지만 ModelAndView를 사용하는 것을 권장한다.
return mv;
}
}
DAO를 직접 만들고 있었던 것을 Spring에게 위임하여 Spring이 만들게 한다.
멤버 변수와 생성자를 만든 뒤, 의존 주입을 하도록 한다.
🍃servlet-context.xml
<beans:bean id="dao" class="com.test.spring.persistence.SpringDAOImpl"></beans:bean>
<beans:bean name="/ex02.do" class="com.test.spring.controller.Ex02Controller">
<beans:constructor-arg ref="dao"></beans:constructor-arg>
</beans:bean>
xml을 고치면 반드시 서버를 재시작해야 한다.