여러 요청을 하나의 컨트롤러에서 처리하는 구조를 구현해볼것이다.
기본적으로 MVC구조이며, 전체 상품 목록을 보여주고 상품을 선택하면 세부 정보를 보여주도록 구현할것이다.
1️⃣ 뷰 구현: productList
뷰는 아래의 정보를 보여주는 화면으로 구성된다.
- 전체 상품 목록
- 상품의 세부 정보
상품 목록 화면
[ch08]에 productList.jsp를 새로 만들어주자.
생성된 코드는 아래와 같다.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
</body>
</html>
코드를 아래와 같이 바꿔주자.
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상품 목록</title>
</head>
<body>
<h2>상품 목록</h2>
<hr>
<table border="1">
<tr> <th>번호</th> <th>상품명</th> <th>가격</th> </tr>
<c:forEach var="p" varStatus="i" items="${products}">
<tr>
<td>${i.count}</td>
<td><a href="/pcontrol?action=info&id=${p.id}">${p.name}</a></td>
<td>${p.price}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
상품 정보 출력 화면
[ch08]에 productInfo.jsp를 만들어주자.
생성된 코드는 아래와 같다.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
</body>
</html>
코드를 다음과 같이 바꿔주자.
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상품정보 조회</title>
</head>
<body>
<h2>상품정보 조회</h2>
<hr>
<ul>
<li>상품코드: ${p.id}</li>
<li>상품명: ${p.name}</li>
<li>제조사: ${p.maker}</li>
<li>가격: ${p.price}</li>
<li>등록일: ${p.date}</li>
</ul>
</body>
</html>
2️⃣ 모델 구현: Product, ProductService
모델 영역은 아래 두개의 클래스로 구현된다
- 상품 정보를 표현하기 위한 DO 객체인 Product 클래스
- DB없이 샘플 데이터를 제공하기 위한 ProductService 클래스
Product 클래스
[src/main/java]의 [ch08]패키지에 Product 클래스를 생성하고
화면에 제공할 정보를 참조해 아래와 같이 작성해주자.
package ch08;
public class Product {
private String id;
private String name;
private String maker;
private int price;
private String date;
//데이터 생성을 쉽게 하기 위한 생성자
public Product(String id, String name, String maker, int price, String date)
{
this.id = id;
this.name = name;
this.maker = maker;
this.price = price;
this.date = date;
}
//getter, setter 메서드
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 getMaker() {
return maker;
}
public void setMaker(String maker) {
this.maker = maker;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
ProductService 클래스
DB없이 샘플 데이터를 Map형태로 저장하고
전체 상품 목록과 특정 상품의 정보를 컨트롤러에
제공하기 위한 서비스 클래스를 만들것이다.
이번에 만들 서비스 클래스 ProductService는 컨트롤러 서빌릇의 init() 메서드에서 초기화 되기 때문에
서블릿이 실행되는 동안에는 데이터가 유지되나 서블릿이 다시 시작하거나 톰캣을 종료하면 데이터는 초기화된다.
따라서 샘플 데이텉 이외에 별도 기능 구현을 통해 추가한 데이터가 있다면 삭제될 위험이 있으니 주의.
데이터 추가기능은 구현하지 않을것이다.
[src/main/java]의 [ch08] 패키지에 ProductService 클래스를 생성하고 다음과 같이 코딩해주자.
package ch08;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ProductService {
Map<String, Product> products = new HashMap<>();
public ProductService() {
Product p = new Product("101", "애플사과폰 12", "애플전자", 1200000, "2020.12.12");
products.put("101", p);
p = new Product("102", "삼전우주폰 21", "삼전전자", 1300000, "2021.2.2");
products.put("102", p);
p = new Product("103", "엘스듀얼폰", "엘스전자", 1500000, "2021.3.2");
products.put("103", p);
}
public List<Product> findAll() {
return new ArrayList<>(products.values());
}
public Product find(String id) {
return products.get(id);
}
}
- 생성자에서 샘플데이터 3개를 생성해서 Map에 추가함.
- findAll() 메서드는 Map의 리스트 형태로 변환해서 리턴
- find() 메서드는 인자로 받은 키값으로 상품을 검색해서 Product 객체를 반환한다.
필요시 수정, 삭제 메서드도 추가 구현이 가능하다. 하지만 패스
3️⃣ 컨트롤러 구현: ProductController
여러 요청을 하나의 컨트롤러 서블릿에서 처리하는 경우
기본 생성 서블릿 구조를 굳이 사용할 필요가 없다.
doGet, doPost 메서드를 오버라이딩 하지않고
service메서드를 오버라이딩하여 컨트롤러 기본 구조를 구현하고
각각의 요청은 action 파라미터를 비교해 메서드를 호출한 다음
뷰로 이동하는 구조로 구현할것이다.
ProductController 서블릿
[src/main/java]의 [ch08] 패키지에서 ProductController 서블릿을 생성해주자.
생성된 코드는 아래와같다.
package ch08;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class ProductController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
이를 여러 과정에 거쳐 수정할 것이다. 일단 아래처럼 코딩해주자.
Init()와 service 메서드는 generate로 자동생성이 가능하다.
package ch08;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/pcontrol")
public class ProductController extends HttpServlet {
private static final long serialVersionUID = 1L;
ProductService service;
public ProductController() {
service = new ProductService();
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
service = new ProductService();
}
@Override
public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
super.service(req, res);
}
}
- ProductSErvice 객체는 생성자에서 초기화된다. 모든 사용자요청에 대해 동일 인스턴스가 사용된다.
다음으로 클라이언트 요청을 구분하고 처리 메서드를 호출한 다음
뷰로 이동하는 구조를 service() 메서드에 작성할것이다.
action 파리미터가 null인 경우,
즉 서블릿이 action 파라 미터 없이 호출된 경우 컨트롤러에 action파라미터를 넣어 포워딩 할것이다.
MVC구조에서는 JSP화면을 바로 실행하는 것이 아니라
컨트롤러를 통해 JSP로 이동해야 하기 때문에
실행의 편의를 위해서 만들어두면 좋다.
만약 이런 구조를 만들지 않은 상황에서 서블릿(컨트롤러)를 실행하면 null pointer exception이 발생한다.
따라서 직접 action 파리미터를 브라우저에 넣어줘야 한다.
이제 service 코드를 아래와 같이 수정해주자.
@Override
public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String action = req.getParameter("action");
String view = "";
if(req.getParameter("action") == null) {
getServletContext().getRequestDispatcher("/pcontrol?action=list")
.forward(req, res);
}
else {
switch(action) {
case "list": view = list(req, res); break;
case "info": view = info(req, res); break;
}
}
getServletContext().getRequestDispatcher("/ch08/"+view)
.forward(req, res);
}
action 파라미터 값에 따라 list, info 메서드를 호출한다.
이때 request는 반드시 전달해야 파라미터를 읽어올수 있으며,
response는 굳이 필요없지만 필요한 경우를 대비해 함께 전달해주자.
이렇게 작성하고 나면 view = list, view = info 가 에러가 날것이다.
아래와 같이 추가 코딩해주자.
private String list(HttpServletRequest req, HttpServletResponse res)
{
req.setAttribute("products", service.findAll());
return "productList.jsp";
}
private String info(HttpServletRequest req, HttpServletResponse res)
{
req.setAttribute("p", service.find(req.getParameter("p")));
return "productInfo.jsp";
}
- list() 메서드 : 전체 데이터를 보여 주기 위한 메서드. service객체의 findAll() 메서드를 호출한 결과를 request scope에 저장하고 productList.jsp로 이동하면 된다.
- info() 메서드 : productList.jsp에서 특정 상품을 선택했을때 호출. 특정 상품에 대한 정보를 가져와 productInfo.jsp로 이동해 보여주게된다.
- products, p 등 request scope에 저장되는 속성의 이름에 주의하자.
코드는 다음과 같다.
package ch08;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/pcontrol")
public class ProductController extends HttpServlet {
private static final long serialVersionUID = 1L;
ProductService service;
public ProductController() {
service = new ProductService();
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
service = new ProductService();
}
@Override
public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String action = req.getParameter("action");
String view = "";
if(req.getParameter("action") == null) {
getServletContext().getRequestDispatcher("/pcontrol?action=list")
.forward(req, res);
}
else {
switch(action) {
case "list": view = list(req, res); break;
case "info": view = info(req, res); break;
}
}
getServletContext().getRequestDispatcher("/ch08/"+view)
.forward(req, res);
}
private String info(HttpServletRequest req, HttpServletResponse res)
{
req.setAttribute("p", service.find(req.getParameter("id")));
return "productInfo.jsp";
}
private String list(HttpServletRequest req, HttpServletResponse res)
{
req.setAttribute("products", service.findAll());
return "productList.jsp";
}
}
이제 톰캣서버를 실행해 확인해보자.
애플 사과폰을 눌러보자.
이것으로 MVC는 마무리.
[메인으로 돌아가기]
'Java Spring > 책공부 1 (JSP와 스프링)' 카테고리의 다른 글
16. JDBC 종합 : 학생정보 조회와 등록 (0) | 2022.07.13 |
---|---|
15. SQL : 학생정보 목록 생성 (0) | 2022.07.12 |
13. 컨트롤러 기초 : 계산기 구현 (0) | 2022.07.11 |
12. JSTL과 EL 종합 예제 (0) | 2022.07.11 |
11. 액션 종합: 계산기 구현 (0) | 2022.07.09 |