MVC 패턴
MVC는 Model View Controller로 애플리케이션을 세 가지 역할로 구분하여 개발하는 방법론이다.
스프링 MVC에서 Model, View, Controller
Model
Controller에서 View로 객체를 전달하는데 사용된다. Controller 메소드에 input으로 값을 넘겨 주면 된다. 스프링에서 기본적으로 제공하는 Model 인터페이스가 있는데 이는 Map<String, Object> 형식이다. 아래와 같이 Event 클래스와 List<Event>를 return하는 EventService 클레스가 있다.
import lombok.*;
import java.time.LocalDateTime;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Event {
private String name;
private int limitOfEnrollment;
private LocalDateTime startDateTime;
private LocalDateTime endDateTime;
}
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class EventService {
public List<Event> getEvents() {
Event event1 = Event.builder()
.name("영어 스터디")
.limitOfEnrollment(5)
.startDateTime(LocalDateTime.of(2020, 1, 10, 10, 0))
.endDateTime(LocalDateTime.of(2020, 1, 10, 12, 0))
.build();
Event event2 = Event.builder()
.name("수학 스터디")
.limitOfEnrollment(5)
.startDateTime(LocalDateTime.of(2020,1,17,10,0))
.endDateTime(LocalDateTime.of(2020, 1, 17,12,0))
.build();
return List.of(event1, event2);
}
}
이 때 모델의 사용법은 Controller의 메소드에 input으로 Model을 넣고 이를 메소드에서 key 와 value 형식으로 넣어주면된다.
@GetMapping("/event")
public String events(Model model) { //model은 map이라고 생각 key, value
model.addAttribute("events", eventService.getEvents());
return "events";
}
Controller
@Controller annotation을 이용해 bean으로 등록한다. @GetMapping()을 통해 해당 url로 요청이 들어오면 해당 메소드가 실행된다.
package me.jiho.springmvcdemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class EventController {
@Autowired
EventService eventService;
//@RequestMapping(value = "/event", method = RequestMethod.GET)
@GetMapping("/event")
public String events(Model model) { //model은 map이라고 생각 key, value
model.addAttribute("events", eventService.getEvents());
return "events";
}
}
View
스프링부트에서 Controller의 메소드에 return 하는 String과 같은 값의 html 파일을 resources 폴더에서 찾는다 동적은 View는 resources/template 에서 찾으므로 해당 디렉토리에 events.html을 다음과 같이 만든다. 다음은 타임리프를 사용한 예시이다.
<!DOCTYPE html>
<html lang="en" xmlns:th="<http://www.thymeleaf.org>">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>이밴트 목록</h1>
<table>
<tr>
<th>이름</th>
<th>참가 인원</th>
<th>시작</th>
<th>종료</th>
</tr>
<tr th:each="event: ${events}">
<td th:text="${event.name}"></td>
<td th:text="${event.limitOfEnrollment}"></td>
<td th:text="${event.startDateTime}"></td>
<td th:text="${event.endDateTime}"></td>
</tr>
</table>
</body>
</html>

서블릿(Servlet)
서블릿 엔진 또는 서블릿 컨테이너
서블릿 스팩을 준수하는 컨테이너로 톰캣, 제티, 언더토 등 이 있다. 세션관리, 네트워크 서비스, MIME 기반 메시지 인코딩 디코딩, 서블릿 생명주기 관리 등의 역할을 한다. 서블릿 어플리케이션은 서블릿 컨테이너가 실행시킬수 있기 때문에 서블릿 컨테이너를 사용해야한다.
서블릿 생명주기
서블릿 컨테이너가 서블릿을 실행하는 방법이다.
서블릿 컨테이너가 서블릿 인스턴스의 init() 메소드를 호출하여 초기화 한다.
서블릿이 초기화 된 다음부터 클라이언트의 요청을 처리할 수 있다. 각 요청은 별도의 쓰레드로 처리하고 이때 서블릿 인스턴스의 service() 메소드를 호출한다.
HTTP 요청을 받고 클라이언트로 보낼 HTTP 응답을 만든다.
service() 메소드는 HTTP 메소드에 따라 doGet() doPost() 등으로 처리를 위임한다
서블릿컨테이너 판단에 따라 해당 서블릿을 메모리에서 내려야 할 시점에 destroy()를 호출한다.
프로젝트 생성
스프링이 아닌 서블릿으로 어플리케이션을 개발하는 예시이다. 프로젝트 생성시 스프링이 아닌 maven의 archetype으로 프로젝트를 생성

pom.xml로 들어가서 의존성으로 sevlet을 추가 시킨다. 여기서 scope의 의미는 test인 경우 test를 실행하는 경우에만 사용가능, provided인경우 런타임 시점에는 빠지고 컴파일 시점에서는 사용 패키징시 제외되고 실행시에는 컨테이너에서 제공된다.
<!-- <https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api> -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
main에 java라는 폴더를 만들고 Sources로 지정해준다

다음으로 패키지 폴더를 생성해 주면 된다.
서블릿 구현
패키지 폴더 안에 다음과 같이 서블릿을 구현해 준다. init, doGet, destry안에 각각 언제 호출되는지 알아보기 위해 문구를 넣었다.
package me.jiho;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("init");
}
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("doGet");
resp.getWriter().println("<html>");
resp.getWriter().println("<head>");
resp.getWriter().println("</head>");
resp.getWriter().println("<body>");
resp.getWriter().println("<h1>Hello Servlet</h1>");
resp.getWriter().println("</body>");
resp.getWriter().println("</html>");
}
@Override
public void destroy() {
System.out.println("destroy");
}
}
서블릿 실행
서블릿은 실행시에는 서블릿 컨테이너에 배포를 해야 실행이 가능하다. 따라서 AddConfiguration을 눌러 +를 눌러 tomcat을 넣어준다.


Deployment war를 war exploded로 바꾸어 준다. war의 압축을 풀은 상태로 tomcat에 배포한다.


서블릿 등록하기
서블릿을 등록하기 위해서는 webapp/WEB-INF/web.xml에 서블릿을 추가하면 된다 servlet 태그를 이용해 hello 라는 이름으로 HelloServlet을 등록하고 <servlet-mapping>으로 url과 서블릿을 매핑시킨다.
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>me.jiho.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>

다음 화면을 띄우고 새로고침을 수행하였다. 이때 init은 처음에 한번만 수행되고 다음에는 doget만 실행되는 것을 확인 할 수 있다.

서블릿 리스너
웹 어플리케이션에서 발생하는 주요 이벤트를 감지하고 각 이벤트에 특별한 작업이 필요한 경우에 사용할 수 있다. 예시로는 서블릿 컨테이너 실행시 데이터베이스 커넥션을 만들어 놓고 여러 서블릿에서 이를 사용할 수 있다.
서블릿 리스너의 종류
서블릿 리스너 생성
MyLister 클래스를 다음과 같이 만들어 준다.
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Context Init");
sce.getServletContext().setAttribute("name", "jiho");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Context Destroy");
}
}
web.xml에 Listener를 등록해 준다.
<listener>
<listener-class>me.jiho.MyListener</listener-class>
</listener>
기존의 서블릿에서 리스너의 값을 가지고 오게 변경한다.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("doGet");
resp.getWriter().println("<html>");
resp.getWriter().println("<head>");
resp.getWriter().println("</head>");
resp.getWriter().println("<body>");
resp.getWriter().println(
"<h1>Hello " +getServletContext().getAttribute("name") + "</h1>");
resp.getWriter().println("</body>");
resp.getWriter().println("</html>");
}