[JSP]MVC Pattern 정리(1)
Jsp MVC패턴을 공부하면서 전체적인 흐름에 대해 공부한 것을 정리해보았다 !

[ 참고 ]
- 톰캣은 켜자마자 Servers 프로젝트안에 환경변수 문서부터 읽는다.
- *현재 MVC파일에서*
web.xml에서 Servlet은 2개다(main과 member 있기 때문)
Servlet 태그가 2개라는 것은? DispatcherServlet의 객체가 2개라는 것 !
그리고 init()가 각각 순서대로 2번 호출된다 !!
또 ApplicationContext 객체는 2개 - DispatcherServlet가 가지고 있는 메서드?
처음 init() 실행 → 요청이 오면 doGet() [ 또는 doPost() ] → 마지막에 사라지면 destroy() 실행 - 톰캣 켰을 때 init() 호출되게 하려면? web.xml에서 load-on-startup을 양수로 설정하기
- ServletConfig config는 web.xml 문서에서 init-param 데려옴
ServletContext application은 context-param 데려옴
#web.xml
<servlet>
<servlet-name>Test1</servlet-name>
<servlet-class>a.b.Test1</servlet-class>
<init-param>
<param-name>abc</param-name>
<param-value>def</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
----------------------------------------------------------------
ex) Test1의 init메서드에서 def 출력하는 문장
public class Test1 extends HttpServlet{
@Override
public void init(ServletConfig config) throws ServletException{
System.out.println(config.getInitParameter("abc"));
} //def
}
💡application.getRealPath
절대경로를 얻어온다. 톰캣버전이 바뀌어도 경로를 구해주기 때문에 유지보수에 좋다!
http://localhost:8282/ABC/a.jsp
<%=application.getRealPath("/def/a.txt") %>
실행하면 ? —> c:/tomcat/webapps/ABC/def/a.txt
// '/'를 적으면 프로젝트명 전까지 절대경로를 얻어온다
#web.xml
<servlet>
<servlet-name>main</servlet-name>
<servlet-class>kr.co.seoul.common.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>configFile</param-name>
<param-value>/WEB-INF/config/main.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
-----------------------------------------------------------------------------------
#DispatcherServlet.java
public class DispatcherServlet extends HttpServlet{
@Override
String fn=config.getInitParameter("configFile"); //param-name의 param-value얻어옴
String aPath=application.getRealPath(fn); //절대 경로, 리턴타입 : 문자열
FileInputStream fis=new FileInputStream(aPath);
}
}
/*init()에서 FileInputStream사용하면, FileNotFoundException이 발생함
→ application.getRealPath 사용하기*/
⚡톰캣을 키면 → sever.xml문서 읽어들이고 → 각각의 프로젝트 web.xml문서 읽어서 → web.xml의 DispatcherServlet같은 것을 객체로 만들어야함(메모리에 올려야함) → DispatcherServlet 객체를 2개 만드는데 init()가 자동으로 2번 호출된다.(load-on-startup은 양수로 1, 2로 해놨으니까)
*현재 main.properties, member.properties가 있음
Map<String,Object> map
map.put("urlFilenameViewController",new kr.co.seoul.common.servlet.mvc.UrlFilenameViewController);
//맵 객체가 2개 : properties가 2개니까
public void init(ServletConfig config) throws ServletException (
String fn=config.getlnitParameter ("configFile");
System.out.println(fn); // WEB-INF/config/main.properties
String aPath=application.getRealPath(fn);
FilelnputStream fis=new FilelnputStream(aPath);
//here
--------------------------------------------------------------------
Map<String,Object> map
map.put(키,new 클래스명());
public void init(ServletConfig config) throws ServletException {
String fn=config.getlnitParameter("configFile");
System.out.printin(fn); // WEB-INF/config/memeber.properties
String aPath=application.getRealPath(fn);
FilelnputStream fis=new FilelnputStream(aPath);
//here
//🤔그런데! init메서드에 이렇게 다 적으면 좋지않다.
//main.properties, memeber.properties을 대신 읽어주는 클래스가 있다
public class DispatcherServlet extends HttpServlet{
ApplicationContext applicationContext;
/*main.properties, member.properties를 불러오는
applicationContext 멤버변수 등록*/
public void init(ServletConfig config) throws ServletException{
ServletContext application=this.getServletContext(); //context-param 데려옴
applicationContext=new ApplicationContext();
//init()에 ApplicationContext 객체 생성
applicationContext.메서드(config,application);
//config와 application 전달
--------------------------------------------------------------------
public class ApplicationContext{
Map<String,Object> map; //Map에 등록
메서드(ServletConfig config, ServletContext application)
//config와 application 받음
String fn=config.getlnitParameter("configFile");
String aPath=application.getRealPath(fn);
FilelnputStream fis=new FilelnputStream(aPath);
Properties p=new.... //등록하는 작업
p.load(fis);
for(String key : p.stringPropertyNames()){ //키 값 가져오고
String cn=p.getProperty(key); //키를 주면 클래스 네임 얻어옴
map.put(key, Class.forName(cn).newlnstance() );
//클래스 이름을 주고 newlnstance로 객체 생성
}
}
}
- 톰캣을 키면 HDD에 있는 properties를 메모리에 올리는 작업을 함. 이것들의 위치를 알려줘야함. 그 위치는 web.xml 문서에 있음(이렇게 하는 것이 유지보수상 좋음, 만약 java안에 있으면 경로가 바뀌면 수정해야하는 번거로움)
- main.properties, member.properties를 불러오는 것은 applicationContext → 그래서 멤버변수에 등록되어있다 → ApplicationContext는 init()에서 객체 생성하면된다 → Map에 등록하기 → 등록하는 작업해야함
- 클래스 이름 2개 기억하기: DispatcherServlet, ApplicationContext
- main(DispatcherServlet) , ApplicationContext(main.properties)
- member(DispatcherServlet) , ApplicationContext(member.properties)
- 즉, properties파일은 Map이다
/menu.html=urIFilenameViewController
/member/list.do=memberListController
/emp/list.do=empListController
/item/list.do=itemListController
/error.html=urlFilenameViewController
=> 이렇게 실행한다
http://localhost:8282/MVC1/menu.html
http://localhost:8282/MVC1/member/list.do
http://localhost.8282/MVC1/emp/list.do
<servlet-name>main</servlet-name>
<url-pattern>*.do</url-pattern>
<servlet-name>main</servlet-name>
<url-pattern>*.html</url-pattern>
<servlet-name>member</servlet-name>
<url-pattern>/member/*</url-pattern>
--------------------------------------------------------------------
http://localhost:8282/MVC1/menu.html //카테고리 없다
http://localhost:8282/MVC1/member/list.do //카테고리 있다
//서블릿이 메인과 멤버 각각 2개
//디스패처서블릿이 객체가 각각 2개 - 위, 아래는 다른 것임
//맵 객체도 2개 있다
<servlet-name>main</servlet-name>
<url-pattern>*.html</url-pattern>
<servlet-name>member</servlet-name>
<url-pattern>/member/*</url-pattern>
--------------------------------------------------------------------------
/*main.properties(Map<String,Object>), member.properties(Map<String,Object>)
아래와 같이 요청했을 때 어떤 클래스의 메서드가 호출됩니까?
http://localhost:8282/MVC1/menu.html
DispatcherServlet(doGet메서드) main.properties
아래와 같이 요청했을 때 어떤 클래스의 메서드가 호출됩니까?
http://localhost.8282/MVC1/member/list.do
DispatcherServlet(doGet메서드) member.*/
- context-param은 모든 서블릿이 공유하는 것 (ServletContext application으로 얻어옴)
유지보수를 카테고리별로 하려면 init-param에 등록해야함 (ServletConfig config으로 얻어옴) - urlmapping.properties는 카테고리 구분되어 있지않음
DispatcherServlet 멤버변수
private ApplicationContext( member.properties, main.properties )
private SimpleUrlHandlerMapping ( urlmapping.properties )
public class DispatcherServlet extends HttpServlet{
@Override
public void init(ServletConfig config){
super(config);
ServletContext.application=this.getServletContext();
applicationContext=new ApplicationContext(application,config);
simpleUrlHandlerMapping=SimpleUrIHandlerMapping.getInstance(application);
- main.properties, member.properties 2개니까 init()가 각각 호출되는데, new ApplicationContext(), new SimpleUrIHandlerMapping()으로 로직을 짜면 객체 2개 만들어지니까 1개 만들기 위해 싱글톤패턴으로 만듬 !
- application Context.메서드(application,config); 이런식으로 로직짜지 않고 생성자에 전달해서 메서드 호출하지 않음(리팩토링)
- SimpleUrIHandlerMapping은 urlmapping.properties를 얻어올건데, 이것은 web.xml에 세팅 되어있고, context-param임. 얻어오려면 application있으니까 전달함
=> 톰캣켰을 때 일어나는 일들..
1️⃣ 톰캣이 켜지면 properties파일들을 메모리에 로드하는 일이 일어난다.
DispatcherServlet 멤버변수
private ApplicationContext( member.properties, main.properties )
private SimpleUrlHandlerMapping ( urlmapping.properties )
private InternalResourceViewResolver ( path.properties )
⚡3개의 멤버변수는 각각()안에 있는 것 들고있다.
⚡DispatcherServlet은 멤버변수로 private하게 3개를 가지고 있다.
⚡톰캣 켰을 때 init()에서 이 클래스들을 객체 생성한다 -> properties파일이 Map형태로 메모리에 로드된다.
⚡path.properties 가지고 있는 것는 InternalResourceViewResolver(화면처리자)
2️⃣ 요청하기
아래와 같이 요청하면 자동으로 실행되는 메서드는 무엇인가요? doGet
http://localhost:8282/MVC1/menu.html
http://localhost:8282/MVC1/member/list.do
http://localhost:8282/MVC1/error.html
⚡doGet에서 request, response 매개변수로 받아온다.
a.jsp를 만들면 -> 자동으로 톰캣엔진이 a_jsp.java만듬 -> 이걸 컴파일하면 a_jsp.class(톰캣이 자동으로 만든 서블릿 클래스) 생김,
a_jsp.class가 _jspService()메서드 가지고 있음, _jspService()은 매개변수로 request, response 있음
⚡bean객체는 controller다.
⚡bean객체가 등록되어 있는 Map은 총 4개인데, 어떤 클래스의 멤버변수인가?
http://localhost:8282/MVC1/menu.html 이렇게 요청했을 때 ? ApplicationContext
- DispatcherServlet
- ApplicationContext(main.properties)
- SimipleUrlHandlerMapping
- InternalResourceViewResolver
http://localhost:8282/MVC1/member.html 이렇게 요청했을 때? ApplicationContext(member.properties)
⚡controller는 interface, 즉 handleRequest은 추상메서드
⚡urlmapping에 이름 등록되어 있다. ( /menu.html ), 그 뒤에 있는 것은 bean의 이름 (urlFilenameViewController )
💡1. url-path에서 url이름을 얻은 뒤 simpleUrlHandlerMapping에서 빈이름을 얻습니다.
💡2. bean객체 얻기 applicationContext에서 빈이름을 주면 빈객체를 얻어올 수 있습니다.
http://localhost:8282/MVC1/menu.html 요청했을 때
=> 이렇게 요청했을 때 DispatcherServlet의 doGet() 호출
=> /menu.html 이름을 얻어야함 -> bean 이름을 얻을 수 있음 -> bean 객체를 얻을 수 있음
public class DispatcherServlet extends HttpServlet{
public void doGet(request,response){
Controller controller=simpleUrlHandlerMapping.getController();
//controller가 bean객체
String url=request.getRequestURI();
System.out.println(uri);// /MVC1/menu.html : url을 얻어와야함, 프로젝트 이름부터 싹 가져옴
String contextPath=request.getContextPath();
System.out.println(contextPath);// /MVC1 : Context경로 얻기(프로젝트명)
String name=url.substring(contextPath.length());
System.out.println(name);// /menu.html
}
}
// /menu.html 을 urlmapping.properties가 가지고 있음.
// simpleUrlHandlerMapping에서 getController객체를 하나 만든다.
3️⃣ http://localhost:8282/MVC1/menu.html 이라고 요청했을 때 !
public class DispatcherServlet extends HttpServlet{
private ApplicationContext applicationContext;
private SimipleUrlHandlerMapping simipleUrlHandlerMapping;
private InternalResourceViewResolver internalResourceViewResolver;
public void doGet(request,response){
1. bean객체얻기
Controller controller=simpleUrlHandlerMapping
.getController(request,applicationContext);
//bean객체 가지고 있는 것 applicationContext인데 simpleUrlHandlerMapping 먼저 호출 이유?
//bean객체의 이름이 없고 url주소만 있어서
//applicationContext주소 전달
//💡즉 !! main.properties에서 bean의 이름 urlFilenameViewController은 어떤 객체다?
//urlFilenameViewController다, 객체 주소를 얻어옴~
2.
ModelAndView mav=controller.handleRequest(request,response);
//Controller는 interface, bean이 가진 추상메서드 handleRequest(요청처리)
//controller.handleRequest의 리턴형 ModelAndView
3.
internalResourceViewResolver.resolveView(mav, request, response);
//화면해결자 : 화면처리
//ModelAndView 받아올꺼니까 mav, forward할거니까 request, response 전달
//forward한다는 것? (request주소값이 다음 페이지에 연결된다)
//즉, request.setAttribute하겠다는 것, db연동해서 data있으면 여기서 세팅 !
}
}
⚡ .html은 DB연동X / .do는 DB연동O (이 파일에서 정한 것)
urlFilenameViewController DB연동X, 화면만 이동하겠다는 것 !
⭐ModelAndView mav : DB연동해서 다음 화면에 응답할 데이터 *data를 map에 등록해서 여기 담음
⚡View는 jsp → 누르면 DispatcherServlet에 가서 Cotroller bean객체 얻어옴, doGet호출해서
회원리스트보여줘~ 모델단에 가서 data를 가져옴(DTO,VO) → 다음화면 jsp가 응답
⚡ServiceFacade는 모델단의 입구
3-1
public class SimpleUrlHandlerMapping{ //object가 부모
private Map<String,String> map;
//키,값을 가짐
public Controller getController(request,applicationContext){
//반환형 Controller, bean객체를 주는 getController
String key=request.getRequestURI()
.substring(
request.getContextPath().length()
);
return (Controller)applicationContext.getBean(
map.get(key) //"urlFilenameViewController"(bean의 이름)
);
//형변환해야함 : getController 리턴타입이 Controller
//Map이 들고있는 key는 /menu.html이다. 그 value를 들고 있는 것이 bean의 이름
}
}
3-2
public class ApplicationContext{
private Map<String,Object> map //이게 bean
public Object getBean( String beanName ){ //반환형이 Object
return map.get(beanName); //bean이름 있어, bean객체를 줘, object줘
}
}
3-3 UrlFilenameViewController
public class UrlFilenameViewController implements Controller{
public ModelAndView handlRequest(request, ){ //반환형 ModelAndView
/*사용자가 아래와 같이 요청했을때 화면이름부분에 어떤값이 들어가야하나요? menu
http://localhost:8282/MVC1/menu.html*/
String uri=request.getRequestURI(); // /MVC1/menu.html
String contextPath=request.getContextPath(); // /MVC1
String str=uri.substring( contextPath.length()+1 ); // str=menu.html
//+1을 해야 /빼고 menu.html 얻어옴
return new ModelAndView( str.split("[.]")[0] ,null );
//화면이름 str.split("[.]")[0] 꺼내옴 --> menu
//.html은 db안가니까 전달할게 없어서 null
//정규표현식에서 .은 특수문자이기 때문에 .으로 사용하고 싶으면 []에 해야함
}
}
3-4
@AllArgsConstructor //생성자
public class ModelAndView{
private String viewName;
private Map<String,Object> map;
}
3-5
public class InternalResourceViewResolver
public void resolveView(){
화면이동하기
}
/menu.html=kr.co.seoul.common.controller.urlFilenameViewController
/member/list.do=kr.co.seoul.member.controller.memberListController
/error.html=kr.co.seoul.common.controller.ErrorController
path.properties에서
prefix =/WEB-INF/jsp ⇒ 접두어
postfix=.jsp ⇒ 접미어
http://localhost:8282/MVC1/menu.html 이라고 요청했을 때 !
결론 : 다음 화면은 메뉴 ! web-inf의 menu.jsp가 응답했다.