Spring

[IoC] MVC패턴 이해 실습_2 : Spring제공 클래스 사용

amoomar 2022. 3. 31. 14:28
반응형

 

포스팅의 목차는 다음과 같다. 이전 포스팅에서는 모든 클래스들을 직접 생성하여 이해하며 작업했는데, 이번 포스팅에서는 이전 작업물을 참고하여, Spring framwork에서 지원해주는 클래스로 교체해보는 작업을 할 예정이다.

1. Filter(encoding)
2. Dispatcher Servlet(DS)
3. 의존성 주입(DI)
   1) C
   2) HM
   3) VR

 


 

1. Filter(encoding)

Filter파일을 별도로 생성해주어도 되지만, 스프링 프레임워크에서 기본 제공 해주기 때문에 파일의 생성보다는 어떤 파일을 적용할지 설정해 주는 것이 더 나은 방식이라고 할 수 있다. 어노테이션을 활용하여 어떤 요청에 대해 해당 filter를 거쳐야하는지를 설정했었는데, 즉 xml파일에서 설정을 해주면 된다는 의미이다.

 

WEB-INF하위의 web.xml을 수정하여 서블릿 컨테이너가 이 곳에서 filter설정을 보고 인코딩을 해줄 수 있도록 지정해준다.

파일 위치

 

 

 

 

아래의 코드를 web.xml에 추가하여 작성해주면 된다. 해당 코드를 통해 스프링 프레임워크가 지원해주는 클래스파일 중 encFilter라는 이름의 필터파일을 사용하고, init메서드 사용시 encoding이라는 이름의 초기화 매개변수에 UTF-8이라는 값을 담아 전달하도록 지정할 수 있게 된다. 이때 filter-mapping태그를 통해 어떠한 경로요청일때 어떤 필터파일을 만나게 할 것인지(인코딩 할 것인지) 지정할 수 있다.

 	<filter>
 		<filter-name>encFilter</filter-name>
 		<filter-class>org.springframwork.web.filter.CharacterEncodingFilter</filter-class>
 		<init-param>
 			<param-name>encoding</param-name>
 			<param-value>UTF-8</param-value>
 		</init-param>
 	</filter>
 	<!-- 알럿 띄우고 싶으면 아래에 mapping(*.jsp)하나 더 추가 해야한다. -->
 	<filter-mapping>
 		<filter-name>encFilter</filter-name>
 		<url-pattern>*.do</url-pattern>
 	</filter-mapping>

 

 


 

2. Dispatcher Servlet(DS)

동작의 순서에 대해 먼저 짚어보자면, 

web.xml을 참고하여 서블릿 컨테이너가 동작하고, 이때 컨테이너는 객체의 생성 및 관리를 한다.
*.do요청에 대해 DS라는 이름의 서블릿파일을 매핑하고, 어떤 이름의 어디에 있는 클래스인지를 xml에서 지정해주고 있다.

이러한 과정이 바로 DS를 new해주는 것이고, 컨테이너가 new를 대신하고 있으므로 즉, IoC이다.

 

DS가 위치하고 있는 자리(class)를 Spring이 지원하는 class로 참조해주면 된다.

 

 

변경 전, 직접 만든 클래스를 참조

 

변경 후, spring에서 지원해주는 class를 참조

 

 

서블릿 지원 클래스의 경로는 아래와 같다.

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

 

해당 작업을 통해 직접 생성했던 DispatcherServlet.java파일은 지워도 무관하다.

 

구조상 서블릿 컨테이너의 동작시 init()메서드가 자동 실행 되도록 기본 설정 되어있다.

 

DS는 HM, VR를 멤버변수로 가지고 있다. 즉 의존관계이기 때문에, init()메서드의 호출시 HM, VR를 초기화(DI)해줄 필요가 있다.

IoC라는 특성상 초기화는 컨테이너가 대신 해줄 예정인데, 그러기 위해서는 컨테이너가 참고할 설명서(설정파일:xml)가 필요하다.

 

각 컨테이너들(서블릿컨테이너, 스프링컨테이너)은 각기 다른 xml파일을 참고하여 보고 동작하게 되는데, 스프링 컨테이너가 Dispatcher Servlet파일을 생성할때 참고할 수 있는 xml파일이 없기 때문에 에러 발생을 유의하여 별도 xml파일을 생성해주어야한다.

 

 

 

파일의 생성 위치는 WEB-INF하위이다.

파일 생성 위치와 파일명

 

 

스프링 컨테이너가 참고할 xml파일의 생성

파일 종류

 

 

생성할 파일 명

파일명은 서블릿 파일의 이름+"-servlet.xml"이다.



 


 

 

3. 의존성 주입(DI)

 

1) Controller

모든 controller류(요청처리 로직) 객체들은 Controller(상위객체)를 implements해야한다. 이전 포스팅에서는 직접 interface클래스 (Controller)를 생성하여 해당 클래스를 implements 하였지만, spring framwork에서 지원해주는 Controller(interface클래스)가 존재했음을 알고있다.

 

따라서 spring framwork에서 지원해주는 Controller를 implements하여 지원해주는 추상메서드를 사용해야할 필요가 있다.

 

한개의 로직처리 객체의  내용을 참고해보면 코드는 다음과 같다.

package com.test.app.controller.board;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import com.test.app.board.BoardVO;
import com.test.app.board.impl.BoardDAO;

public class MainController implements Controller {

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
		// 검색 로직 추가할 예정
		BoardVO vo=new BoardVO();
		vo.setSearchCondition(request.getParameter("searchCondition"));
		vo.setSearchKeyword(request.getParameter("searchKeyword"));
		BoardDAO boardDAO=new BoardDAO();
		List<BoardVO> datas=boardDAO.getBoardList(vo);
		
		ModelAndView mav=new ModelAndView();
		mav.addObject("datas", datas); // Model을 이용하여 전달할 정보를 저장!
		mav.setViewName("main");
		return mav;
	}

}

 

import를 잘 확인해보면, Spring framwork가 제공하는 Controller클래스와 ModelAndView클래스를 참조하고 있다.

이때 ModelAndView은 output으로 사용되는 클래스로, 해당 클래스는 어디로 가야하는지(경로)와 뭘 갖다줘야하는지(정보:dao를 통해 나온 결과물)를 담는 자료형이다.

 

 

해당 객체로 setViewName메서드를 사용을 통해 view resolver에게 이동해야할 view의 이름을 전달할 수 있으며,

addObject메서드 사용을 통해 model로 반환받은 데이터를 view resolver에게 전달할 수 있게 된다.

자세한 내용은 view resolver설명시 함께 첨부 예정이다.

 



2) Handler Mapping

프로그램 실행시 Dispatcher Servlet으로 인해 스프링 컨테이너는 DispatcherServlet-servlet.xml파일에 들러 해당 DS의 의존관계나 기타 설정 사항들을 참고할텐데, DS는 HM과 VR를 멤버변수로 두고 있으니(의존관계) 두 클래스의 의존성 주입을 행할 필요가 있다.

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

<!-- DS를 위한 스프링 컨테이너를 위한 설정파일. init()이 자동 호출될때 필요하다. -->
<!-- HM과 VR를 DI해줄 것이다. init에 해당되는 내용들을 여기서 설정해준다는 의미이다. -->

<!-- //////////////////////////////////////////HM의 초기화와 DI///////////////////////////////////////// -->
<!-- HM을 초기화, 이때 이 친구를 콕 집어서 부를 일이 없으므로 id는 생략! -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<!-- 멤버변수 map을 사용할거니까, DI진행 -->
	<property name="mappings">
		<props>
			<!-- key가 String, value가 controller -->
			<prop key="/login.do">login</prop>
			<prop key="/logout.do">logout</prop>
			<prop key="/main.do">main</prop>
			<prop key="/insertBoard.do">insertBoard</prop>
			<prop key="/deleteBoard.do">deleteBoard</prop>
			<prop key="/updateBoard.do">updateBoard</prop>
			<prop key="/board.do">board</prop>
		</props>
	</property>
</bean>

<!-- 위에서 매핑한애들은 다 컨트롤러의 하위 클래스들이다! -->
<!-- 저 친구들은 우리가 생성해주면 실제하게 되는데, 그 친구들을 모두 초기화 해주자! -->
<bean class="com.test.app.controller.member.LoginController" id="login"/>
<bean class="com.test.app.controller.member.LogoutController" id="logout" />
<bean class="com.test.app.controller.board.BoardController" id="board" />
<bean class="com.test.app.controller.board.InsertBoardController" id="insertBoard" />
<bean class="com.test.app.controller.board.DeleteBoardController" id="deleteBoard" />
<bean class="com.test.app.controller.board.UpdateBoardController" id="updateBoard" />
<bean class="com.test.app.controller.board.MainController" id="main" />
<!-- //////////////////////////////////////////////////////////////////////////////////////////////// -->

</beans>

 

작업 단계는 다음과 같다.

 

- Spring framwork가 제공해주는 클래스를 참조하여, HM을 초기화 해준다. 이때 개발자에 의해 객체가 사용될 일은 없으므로 id는 지정하지 않아도 무관하다.
- 해당 객체는 멤버변수(map)을 가지고 있으므로 DI를 진행한다. 이때 map컬렉션인 멤버변수는 mappings라는 이름으로 사용되고 있다.
- key가 String, value가 controller이다.
- 이후 각 value들은 그저 문자열이 아닌 controller의 하위 개념(객체)므로, 초기화가 필요하다.

 

 



3) View Resolver

마찬가지로 DS의 멤버변수 중 하나이므로 DI를 진행한다.

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

<!-- DS를 위한 스프링 컨테이너를 위한 설정파일. init()이 자동 호출될때 필요하다. -->
<!-- HM과 VR를 DI해줄 것이다. init에 해당되는 내용들을 여기서 설정해준다는 의미이다. -->

<!-- //////////////////////////////////////////VR의 초기화와 DI///////////////////////////////////////// -->
<!-- ID를 바꾸어서는 안된다! 각자 참조에 용이하도록 일부러 id를 설정한것이기 때문에. -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
   <property name="prefix" value="/WEB-INF/" />
   <property name="suffix" value=".jsp" />
</bean>
<!-- //////////////////////////////////////////////////////////////////////////////////////////////// -->

<!-- //////////////////////////////////////////HM의 초기화와 DI///////////////////////////////////////// -->
<!-- HM을 초기화, 이때 이 친구를 콕 집어서 부를 일이 없으므로 id는 생략! -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<!-- 멤버변수 map을 사용할거니까, DI진행 -->
	<property name="mappings">
		<props>
			<!-- key가 String, value가 controller -->
			<prop key="/login.do">login</prop>
			<prop key="/logout.do">logout</prop>
			<prop key="/main.do">main</prop>
			<prop key="/insertBoard.do">insertBoard</prop>
			<prop key="/deleteBoard.do">deleteBoard</prop>
			<prop key="/updateBoard.do">updateBoard</prop>
			<prop key="/board.do">board</prop>
		</props>
	</property>
</bean>

<!-- 위에서 매핑한애들은 다 컨트롤러의 하위 클래스들이다! -->
<!-- 저 친구들은 우리가 생성해주면 실제하게 되는데, 그 친구들을 모두 초기화 해주자! -->
<bean class="com.test.app.controller.member.LoginController" id="login"/>
<bean class="com.test.app.controller.member.LogoutController" id="logout" />
<bean class="com.test.app.controller.board.BoardController" id="board" />
<bean class="com.test.app.controller.board.InsertBoardController" id="insertBoard" />
<bean class="com.test.app.controller.board.DeleteBoardController" id="deleteBoard" />
<bean class="com.test.app.controller.board.UpdateBoardController" id="updateBoard" />
<bean class="com.test.app.controller.board.MainController" id="main" />
<!-- //////////////////////////////////////////////////////////////////////////////////////////////// -->

</beans>

 

view resolver는 페이지를 hiding을 할 수 있다는 특징이 있다.

 

WEB-INF폴더의 하위에 페이지를 두게 되면 현재까지의 방식으로는 url을 통한 직접 접근이나, 페이지를 통한 이동의 접근이 절대 불가능 했는데, view resolver를 거치게 되면 prefix라는 멤버변수 덕에 WEB-INF하위의 페이지에 접근이 가능해지는 점을 활용한 특징이다. Controller류를 경유하여 가져올 데이터가 있는 페이지들은 WEB-INF폴더 하위에 배치함으로써 url을 활용한 직접 접근을 막을 수 있게 된다.

 

web-inf하위에 별도로 기타 폴더(폴더명:view)를 생성해서 거기다가 jsp파일을 배치하고싶다면, prefix의 value를 /WEB-INF/view/로 작성하면 된다.


데이터의 전달 또한 view resolver를 통해 가능해진다. (req와 res의 활용)


앞서 언급된 ModelAndView에서 addObject()를 통해 Model을 이용하여 전달할 정보를 저장할 수 있는데, 스스로 ModelAndView객체 안에 전달된 M의 정보(datas)를 추출해낼 수 있다. 이 추출된 정보를 Request객체에 저장해서 jsp에 그대로 전달한다. (pageContext.forward방식)

이때 xml파일의 설정때문에 VR는 전달된 경로(viewName)에앞에 무조건 /web/, 뒤에 .jsp를 붙이는데,

그게 싫거나 리다이렉트 방식으로 가고싶거나, .do를 이용하고싶다면

 

 

"redirect: 어쩌구.jsp"를 통해, response.sendRedirect방식을 사용할 수 있다.

"redirect: 어쩌구.do"를 통해 view로 이동 전 다른 controller에 거쳐야하는 경우를 표시할 수 있다.

 

 

즉, "redirect: 어쩌구.저쩌구""나 지금 forward방식 안써, 나서지마 OR 나 아직 view로 갈 타이밍 아니야"라는 의미로 사용된다.

 

 

사용 예시는 다음과 같다.

package com.test.app.controller.member;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import com.test.app.member.MemberVO;
import com.test.app.member.impl.MemberDAO;

public class LoginController implements Controller{

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {

		// 1. 사용자가 입력한 정보들을 추출
		String id=request.getParameter("id");
		String password=request.getParameter("password");
		
		// 2. vo -> dao == DB 연동
		MemberVO vo=new MemberVO();
		vo.setId(id);
		vo.setPassword(password);
		
		MemberDAO dao=new MemberDAO();
		vo=dao.getMember(vo);
		
		// 3. 결과화면으로 이동
		ModelAndView mav=new ModelAndView();
		if(vo==null){
			mav.setViewName("redirect:login.jsp"); // 경로를 반환 -> VR의 viewName으로 전달됨
		}
		else{
			HttpSession session=request.getSession();
			session.setAttribute("member", vo);
			mav.setViewName("redirect:main.do"); // 경로를 반환 -> VR의 viewName으로 전달됨
		}
		return mav;
	}

}

 

 


 

반응형