7 (게시판 만들기)
2023.06.16 수업
교재 : 자바 웹 개발 워크북
* 스프링 : jsp는 숨기는 스타일
* 앞으로 메소드를 실행할때 확인법
1) 디버그 실행
2) 로그 2개 생성
(클래스 이름, 메소드 이름)
(진행중인 위치 표시)
▶ todoList 만들기 실습(67p)
2023.06.21 - [백엔드/JSP & Servlet] - JSP / Servlet - 6(MVC 방식 + 게시판 만들기)
JSP / Servlet - 6(MVC 방식 + 게시판 만들기)
2023.06.14 ~ 2023.06.15 수업 - 자바 웹 개발 워크북 참고 45p~ ● MVC 구조와 서블릿/JSP - 서블릿 코드의 경우 자바 코드를 그대로 이용할 수 있지만 HTML처리는 번거로움 - JPS의 경우 HTML코드를 바로 사용
dustj0824.tistory.com
이어서
▷TodoRegisterController의 구현
서블릿 만들기
TodoRegisterController
- register.jsp를 호출
package org.zerock.w1.todo;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class TodoRegisterController
*/
@WebServlet(
name = "todoRegisterController",
urlPatterns = "/todo/register")
public class TodoRegisterController extends HttpServlet {
// 서블릿이 요청한 view jsp 페이지를 대신 실행한다.(간접적 실행)
// 파라미터 :
// jspFilePath - String ; 실행할 jsp파일 위치
// req - HttpServletRequest ; 요청객체
// res - HttpServletResponse ; 응답객체
private void forwardJsp(
String jspFilePath,
HttpServletRequest req,
HttpServletResponse res
) throws ServletException, IOException {
// 메소드 본체
RequestDispatcher rd = req.getRequestDispatcher(jspFilePath);
rd.forward(req, res);
}
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
// jsp를 불러내기 위한 서블릿임을 알 수 있음
System.out.println("TodoRegisterController1...doGet...");
System.out.println("Method " + req.getMethod() + "입력화면을 볼 수 있습니다.");
String jspPath = "/WEB-INF/todo/register.jsp";
// 실제 jsp 실행결과를 마지막으로 처리하는 것이 서블릿임을 확인하기
// - 서블릿 객체를 jsp가 빌려서 사용함(jsp가 사용하는 것을 숨김, 책임자는 서블릿)
// 확인용 tmpReq와 tmpRes객체 변수를 선언
// 기존에는 다이렉트로 넘겼는데 임시객체에 담아서 넘기는 방법 사용
HttpServletRequest tmpReq = req;
HttpServletResponse tmpRes = res;
forwardJsp(jspPath, tmpReq, tmpRes);
}
}
▷ jsp 만들기
- register.jsp
- 등록처리
- form태그를 이용하여 post 방식으로 호출
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Todo 등록</title>
</head>
<body>
<form action="/todo/register2" method="post">
<input type="text" name="num1" value="1000" />
<input type="text" name="num2" value="2000" />
<button type="submit">등록처리</button>
</form>
</body>
</html>
- 웹브라우저에서 /todo/register의 주소를 요청하면 WAS에서 todoRegisterController서블릿이 TodoRegisterController를 실행시키고 RequestDispatcher에 의해 /WEB-INF/todo/register.jsp가 호출
- register.jsp를 처리하기 위해 register2.jsp를 호출함
서블릿이 발주업체 jsp는 하청업체와 같음
-> 서블릿이 요청 받지만 세부처리는 jsp에서 함
* 디버그
서블릿
TodoRegisterController
중단점
RequestDispatcher rd = req.getRequestDispatcher(jspFilePath);
HttpServletRequest tmpReq = req;
forward는 실행 1회
Redirect는 실행 2회
▷ 서블릿 만들기
TodoRegisterContoller2
- result.jsp를 호출
package org.zerock.w1.todo;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class TodoRegisterContoller2
*/
@WebServlet( // 이름이 중복되면 톰캣에서 문제 발생(name을 소문자로 변경하기)
name = "todoRegisterController2",
urlPatterns = "/todo/register2")
public class TodoRegisterController2 extends HttpServlet {
private static final long serialVersionUID = 1L;
// 서블릿으로 들어온 요청을 대신(위임)처리하는 view 기능 호출
private void forwardJSP(
String jspFilePath, // 경로
HttpServletRequest req, // 요청변수
HttpServletResponse res // 응답변수
) throws ServletException, IOException {
RequestDispatcher rd =
req.getRequestDispatcher(jspFilePath);
rd.forward(req, res);
}
@Override
// 서블릿의 doGet()을 상속받음 - req, res를 가져야함
// 외부에서 접근 불가하지만 상속이기에 protected
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
System.out.println("TodoRegisterController2 ... doGet() ...");
System.out.println("Method " + req.getMethod() + "입력화면을 볼 수 있습니다.");
// view jsp파일의 위치
String jspPath = "/WEB-INF/todo/result.jsp";
forwardJSP(jspPath, req, res);
// String redirectUrl = "/todo/list";
// res.sendRedirect(redirectUrl);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
System.out.println("TodoRegisterController2 ... doPost() ...");
System.out.println("Method " + req.getMethod() + "입력화면 볼 수 있음");
String jspPath = "/WEB-INF/todo/result.jsp";
forwardJSP(jspPath, req, res);
}
}
▷ jsp 만들기
- result.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>NUM1 ${param.num1}</h1>
<h1>NUM2 ${param.num2}</h1>
<h1>SUM ${
Integer.parseInt(param.num1) +
Integer.parseInt(param.num2)
}</h1>
</body>
</html>
* requestDispatcher는 부모호출방식을 따라감
서블릿에서 post로 부르면 jsp에서도 post로 처리,
서블릿에서 get로 부르면 jsp에서도 get로 처리
TodoRegisterController가 부모임
● 모델(model) 74p
- 컨트롤러에서 화면에서 필요한 데이터를 화면쪽으로 전달해주는데 이런 역할을 하는 객체
- 데이터베이스를 담당하는 영역(영속 계층) / 비즈니스 로직을 처리하는 영역(서비스 계층)
* Persistant 계층 = Repository 계층 = Mapper 계층
데이터베이스와 연결하는 다양한 방식
JDBC > JPA(DB맵핑) > MyBatis (쿼리맵핑)
* 컨트롤러는 라우터와 서비스로 나뉨
서비스에 의해 데이터가 확정이 되고 데이터는 모델로 전달됨
* 모델
- 서비스 계층(로직처리), 영속 계층(데이터 처리)
* MVC구조를 제외한 3티어 구조
view 계층(view, controller), 서비스 계층(로직처리), 영속 계층(데이터 처리)
* DTO(Data Transfer Object)
여러개의 데이터를 묶어서 하나의 객체로 전달하는 것
주로 Java Beans의 형태로 구성함
* Java Beans
- 생성자가 없거나 반드시 파라미터가 없는 생성자 함수를 가지는 상태
-> 데이터를 넣기위한 빈 공간을 만듦
- 속성(멤버변수)는 private로 작성
- getter/setter를 제공할 것
▶ 실습76p
▷ DTO
- 컨트롤러는 DTO를 구성해서 서비스 계층을 호출하기도 하고, 반대로 서비스 계층에서 DTO를 받기도 함
-> 서비스 계층 구성 전 DTO를 위한 클래스를 먼저 구성하기
패키지 : org.zerock.w1.todo.dto
클래스 : TodoDTO
- 변수선언
- 속성함수
- toString
package org.zerock.w1.todo.dto;
import java.time.LocalDate;
public class TodoDTO {
private long tno; // 일련변호
private String title; // 할 일 제목
private LocalDate dueDate; // 작성일자
private boolean finished; // 완료여부
public long getTno() {
return tno;
}
public void setTno(long tno) {
this.tno = tno;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public LocalDate getDueDate() {
return dueDate;
}
public void setDueDate(LocalDate dueDate) {
this.dueDate = dueDate;
}
public boolean isFinished() {
return finished;
}
public void setFinished(boolean finished) {
this.finished = finished;
}
@Override
public String toString() {
return "TodoDTO [tno=" + tno + ", title=" + title + ", dueDate=" + dueDate + ", finished=" + finished + "]";
}
}
▷ 서비스 객체
* CRUD 기능들은 서비스 객체에 모아서 구현됨
- 서비스객체 : 기능(로직)들의 묶음, 프로그램이 구현해야하는 기능들의 실제 처리
- sysout과 같이 출력하면 안되는 존재, 디버깅 용도로 사용 가능
* 기호상수가 객체로 진화
패키지 : org.zerock.w1.todo.service
enum : TodoService
- Todo의 서비스객체 TodoService
- 컨트롤러가 이용하는 객체
*enum 클래스
- 상수
- 객체라 equals 연산 불가
* enum 타입으로 클래스를 작성하는 경우 정해진 수만큼 객체를 생성할 수 있다
- INSTANCE : 객체의 개수를 결정하는 부분
- 한개만 지정하면 하나의 객체만 생성하여 사용하게 됨
-> 싱글톤 패턴
* register()
- TodoDTO객체를 받아 확인할 수 있는 것
* getList()
- 10개의 TodoDTO객체를 만들어 반환
package org.zerock.w1.todo.service;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.zerock.w1.todo.dto.TodoDTO;
// 상수 클래스
public enum TodoService {
// 각각의 식별할 변수이름
INSTANCE; // 각각의 값에 대한 식별키(변수), 값을 적지 않으면 순차적 부여
// 한개의 할일을 디비에 저장한다
public void register(TodoDTO todoDTO) {
System.out.println("[DEBUG]....... " + todoDTO);
}
// 디비에 저장된 할일 목록을 가져온다
// Generic(제네릭) 타입 = 데이터처리의 일반화
// -> List 인터페이스의 메소드만 사용 가능
public List<TodoDTO> getList() {
// todoDTO의 목록이 리턴되어야 함
// 이 데이터를 생성시키기위해 스트림클래스를 사용하여 TodoDTO객체를
// 간단히 반복하면서 만들어 내고 있다
// 현재 1개의 데이터가 만들어짐
// 스트림은 일종의 자료구조를 의미(연속적으로 흐르는 구조)
// 업캐스팅
List<TodoDTO> todoDTOS =
IntStream.range(0, 10).mapToObj(i -> {
TodoDTO dto = new TodoDTO();
dto.setTno((long)i);
dto.setTitle("Todo ..." + i);
dto.setDueDate(LocalDate.now());
return dto;
// collect를 통해 모아서 list 함수로 변환해라
}).collect(Collectors.toList());
return todoDTOS; // getList()로 돌아감
}
}
* IntStream
- 말로 모든 것을 풀어나감
-> for는 프로그램 구조를 보는데 불편하므로 빌더패턴을 활용
// 기존 방식
for (int i = 0; i < 10; i++) {
TodoDTO dto = new TodoDTO();
dto.setTno((long)i);
dto.setTitle("Todo ..." + i);
dto.setDueDate(LocalDate.now());
return dto;
}
* 빌더패턴(선언적 프로그래밍)
-> 과정은 중요하지 않고 결과만 보여주기
IntStream : 숫자가 규칙을 가지고 나열된다.
.range(시작, 반복크기) : 숫자가 반복된다, 루프를 돌겠다
.mapToObj(람다함수) : 자료구조는 map(키와 값을 가짐), 자료는 Obj형식
-> 구현법
구체적 절차 :
1개의 데이터 생성, 순서는 일반화 i 사용
return으로 데이터 담음
.collect()
※ 람다함수 = 한줄 함수
- 이름이 없음 (매개변수만 남음)
- 매개변수 i
- 익명함수를 통해 함수정의와 호출을 한번에 함
절차적 프로그래밍 vs 선언적 프로그래밍
▷ 컨트롤러에서 모델 처리하기
ToDoListController 수정
패키지 : org.zerock.w1.todo
- TodoService에서 제공하는 List<TodoDTO>를 가져와 JSP로 전달하는 코드 만들기
- 웹브라우저에서 보내준 정보 (request)
- setAttribute를 통해 우리가 만든 정보 getList에 List<TodoDTO>객체 더하기
package org.zerock.w1.todo;
import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.zerock.w1.todo.dto.TodoDTO;
import org.zerock.w1.todo.service.TodoService;
@WebServlet(name = "toDoListController", urlPatterns = "/todo/list")
public class ToDoListController extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
System.out.println("/todo/list");
// (추가)서비스를 이용해서 모델 데이터 만들기
List<TodoDTO> dtoList = TodoService.INSTANCE.getList();
// (추가)위 데이터를 req객체에 저장
req.setAttribute("list", dtoList);
// 정석
RequestDispatcher rd =
req.getRequestDispatcher("/WEB-INF/todo/list.jsp");
rd.forward(req, res);
}
}
▷ list.jsp 수정
- req.setAttribute으로부터 저장한 키 값을 얻음
- 뷰로 보여줌
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>제목은 여기에</title>
</head>
<body>
<h1>List Page</h1>
<%-- req.setAttribute으로부터 저장한 키 값을 적음 --%>
${ list }
</body>
</html>
ToDoListController에서 실행
* Controller ---> Service
컨트롤러 클래스에서 명령을 요청하면 서비스 클래스에서 처리하게 함
- 서비스 클래스가 한번만 실행되게 하는 것 (싱글톤 패턴)
* TodoService 개선하기
- register()지우고 log() 로 이름 변경
// 기존
public void register(TodoDTO todoDTO) {
System.out.println("[DEBUG]....... " + todoDTO);
}
// 변경 0619
// toString()의 가장 가까운 메소드, 구현하지 않았다면 부모의 것을 가져옴
public void log(TodoDTO todoDTO) {
System.out.println("DEBUG.........." + todoDTO.toString());
}
- getList()
i는 range로부터 옴