
👉 우선 MVC가 나오게된 유래부터 생각해보자면, 기존에는 하나의 주소를 가진 JSP 를 통한 홈페이지 화면 송출밖에 없었지만, 이후 홈페이지를 변경해야할때 주소도 변경해야하고 JSP 자체를 변경해야하는 에로사항 때문에 MVC가 생겨나기 시작했다.
👉 브라우저에서 고정된 주소를 호출하게 되면 서버안에 있는 서블릿과 jsp중에서 서블릿이 반응하게되는데, 여기서 서블릿은 Controller역할을 한다. 해당 역할로는 여러개의 jsp(화면)을 구성해놓고, 원하는 화면을 송출하고싶을때마다 컨트롤러에서 원하는 jsp로 변경한다. 여기서 jsp가 view의 역할을 한다.
👉 그렇게 되면 홈페이지 화면을 변경할때마다 사용자는 고정된 주소를 사용하지만, 송출되는 jsp만 변경하면 되므로 주소를 새로 만들필요가 없어진다.
👉 여기서 Model 서비스인 POJO와 관련된 것이 Model 이다.
👉 servlet 내부에는 화면 출력에 관련된 내용이 없는데도 jsp에서 화면 송출만 가능하고, java담당자는 서블릿만 담당하고 화면 담당은 jsp만 하면된다.
👉 하나의 경로(Servlet = Controller)로 get/post 분리해서 처리가 가능.
@WebServlet(value = {"/bmi"}) //실제로 사용자가 url에 쳐야하는 주소, *jsp파일이 어느 폴더에있든 아무상관이없음* localhost:8080/bmi
public class BMIController extends HttpServlet { //서블릿 상속하기
@Override //오버라이드해서 doGET 을 가져오기
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
req.getRequestDispatcher("/WEB-INF/bmiInput.jsp").forward(req, resp); //jsp파일의 진짜 위치
} catch(Exception e){
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try { //오버라이드해서 doPost 을 가져오기
req.getRequestDispatcher("/WEB-INF/bmiResult.jsp").forward(req, resp);
} catch(Exception e){
e.printStackTrace();
}
}
}
👆 코드를 분석해보자
👉 Servlet은 컨트롤러 역할로 jsp파일중에 어떤 파일을 송출시킬지 정하는 역할 및 실제 url 주소를 가지는 역할
👉 req.getRequestDispatcher 메소드는 jsp파일이 html형식으로 변환되어 클라이언트에 전송할 준비를 하는것.
👉 여기서는 try-catch와 같은 예외처리를 했지만, helloServelt과 같은 기본 servelt에서는 하지만 우리는 override를 하기 때문에 할 필요없음.
👉 "/WEB-INF/bmiInput.jsp" 에서 WEB-INF 디렉토리는 직접적으로 클라이언트에 접근하지못하는 위치이고, JSP폴더가 저장되어있는 곳임.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>BMI Input</h1>
<form method="post"> //form의 defult method는 get
<button>SEND</button>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>BMI Result</h1>
</body>
</html>
👆 코드를 분석해보자
📌 bmi input이나 bmi result나 get이고 post고 불문하고 localhost:8080/bmi/bmiInput과 같은 방법으로 들어갈수없다는것을 인지해야함.
📌 들어갈수있는 주소는 서블릿 주소이니까.
📌 target ="_blank"
<form action="/todo/register" method="post" target="_blank">
📌 update, delete는 항상 where절과 같이 쓰이는것을 연습해야한다.
📌 dml : insert, update, delete 의 공통점 - 결과가 숫자
-> 몇개의 ROW가 변경이 되었다.
📌 query : select - 결과가 집합
1. 프로젝트 세팅 -> 톰캣의 한글 확인
2. JSTL 라이브러리 세팅
3. 롬복 라이브러리 세팅
4. 모델2 MVC 구조 동작여부 확인 -> bmi예제
5. postman확인
6. 와이어프레임 설계 -> 엑셀, 그림 ->
7. 설계한 대로 컨트롤러
8. 화면 설계
9. include
10. db연결
11. DB 더미값 연결 (한글확인)
📌 톰캣을 사용했을때 한글이 깨지지 않는지 확인작업을 거쳐서 개발도중에 한글이 깨져 어떤 문제인지 찾는 과정을 거치지 않도록 미리 한글이 출력되는지 확인을 한다.
<body>
<h1> 한글 </h1>
</body>
👉 코드를 실행해서 실제 코드에 잘 출력되는지 확인
📌 개발에 필요한 Log4j2, lombok, JSTL등 주요 라이브러리를 build.gradle 내부에 세팅후 시작한다.
`compileOnly 'org.projectlombok:lombok:1.18.34'`
`testCompileOnly 'org.projectlombok:lombok:1.18.34'`
`annotationProcessor 'org.projectlombok:lombok:1.18.34'`
`testAnnotationProcessor 'org.projectlombok:lombok:1.18.34'`
`implementation 'org.apache.logging.log4j:log4j-core:2.23.1'`
`implementation 'org.apache.logging.log4j:log4j-api:2.23.1'`
`implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.0'`
`implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl:3.0.1'`
`implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.3'`
`implementation 'org.slf4j:slf4j-simple:2.0.13'`
📌 해당 Servlet과 jsp파일을 대략적으로 작성한 후에 동작이 되는지만 확인한다.
(jsp 파일은 생략)
@WebServlet(value = {"/bmi"}) //실제로 사용자가 url에 쳐야하는 주소, *jsp파일이 어느 폴더에있든 아무상관이없음* localhost:8080/bmi
public class BMIController extends HttpServlet { //서블릿 상속하기
@Override //오버라이드해서 doGET 을 가져오기
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
req.getRequestDispatcher("/WEB-INF/bmiInput.jsp").forward(req, resp); //jsp파일의 진짜 위치
} catch(Exception e){
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try { //오버라이드해서 doPost 을 가져오기
req.getRequestDispatcher("/WEB-INF/bmiResult.jsp").forward(req, resp);
} catch(Exception e){
e.printStackTrace();
}
}
}
📌 postman을 통해서 실제 브라우저에 들어가지 않아도 간편하게 확인이 가능하다.
📌 GET 메소드인 경우에는 브라우저에서 확인이 가능하지만, POST 메소드는 확인이 어렵다.
그렇기 때문에 log.info("get"), log.info("post") 와 같이 메소드가 실행이 되면 log를 통해서 알 수 있도록 구조하는것도 좋다.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info("Todo List GET");

📌 프로젝트 개발의 핵심
📌 프로젝트가 어떻게 흘러가는지 구상도를 그리고, 어떠한 URL, Method를 사용할지 미리 정한다.


📌 기능 : 해당 번호에 해당되는 화면 및 처리 기능
📌 URL : 실제 사용자가 입력하는 주소 / Servlet value
📌 Method : GET 또는 POST를 선택해주는데, 조회 및 확인은 GET, 등록 및 처리는 POST
📌 Controller : Servlet 클래스의 이름
📌 View : GET메소드를 가지고 있을때의 송출해야하는 JSP 파일명
📌 Params : 해당 화면에 필요한 매개변수
ex) 조회를 하기 위해선 번호가 필요하다.
📌 Redirect : POST메소드 사용시 데이터 입력 이후 도배의 위험을 막기위해 자동으로 화면을 변경해야하므로, 자동 이동될 화면주소
(Servlet 내부 오버라이드에서 사용됨)
@WebServlet(value = "/todo/list") //목록페이지
@Log4j2
public class TodoListController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info("Todo List GET");
req.getRequestDispatcher("/WEB-INF/todo/list.jsp").forward(req, resp); //view
}
}
@WebServlet(value = "/todo/register")
@Log4j2
public class TodoRegisterController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info("todo register doGet");
req.getRequestDispatcher("/WEB-INF/todo/register.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info("todo register doPost");
}
}
📌 와이어프레임을 설계했던대로 화면을 구조한다.
📌 Include는 header.jsp와 footer.jsp로 나눠 화면마다 공통되는 부분은 미리 부트스트랩을 통해서 미리 구조해놓고 화면마다 달라지는 부분만 별개로 작성한다.
📌 되는지 확인부터 다 한다음에 Include를 하면 시간이 너무 지체됨.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="../includes/header.jsp"%>
<h1>todo list page</h1>
<%@include file="../includes/footer.jsp"%>
<%@include file="../includes/header.jsp"%>
<div class="card" style="margin: 3em">
<p>Todo Register Page</p>
<div class="card-body">
<form action="/todo/register" method="post">
<div class="mb-3">
<label class="form-label">Title</label>
<input type="text" name="title" class="form-control" placeholder="name@example.com">
</div>
<div class="mb-3">
<label class="form-label">Writer</label>
<input type="text" name="writer" class="form-control" placeholder="user">
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">SAVE</button>
</div>
</form>
</div>
</div>
<%@include file="../includes/footer.jsp"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<h1>Example heading <span class="badge text-bg-secondary">New</span></h1>
<button type="button" class="btn btn-link">Link</button>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
📌 사용할 DB를 연결하고 Table을 작성한다.
create table tbl_todo (
tno int auto_increment primary key,
title varchar(500) not null,
writer varchar(100) not null,
regdate timestamp default now(),
moddate timestamp default now(),
delflag boolean default false
);
📌 Test - java 파일내에 새로운 패키지를 생성하여 DBTests 파일을 생성후 DB에 해당되는 더미값을 입력하여 실제 DB내에 들어가는지 확인한다.
📌 DB와 인텔리제이를 연결하기 위해 sql의 url, username, password를 선언.
@Log4j2
public class DBTests {
@Test
public void testInsert() throws Exception {
String sql = "insert into tbl_todo (title, writer)" +
" values (? , ?)";
// SQL에서 필수적으로 넣어야하는 코드이면서, 더미데이터를 넣을 코드
String url = "jdbc:mariadb://localhost:13306/webdb";
String username ="webdbuser";
String password ="webdbuser";
Class.forName("org.mariadb.jdbc.Driver");
// 해당코드는 JDBC를 사용하여 DB와 연결하는 코드이고,
// 메서드 내부 코드는 MariaDB 데이터베이스용 JDBC 드라이버 클래스명이다.
//동적할당
try(
Connection con = DriverManager.getConnection(url, username, password);
//JDBC를 사용하여 DB에 실제로 연결하는 코드
PreparedStatement ps = con.prepareStatement(sql);
){
// execute(실행)함수를 진행하기전에 ? 에 해당하는 값을 준비하는것.
ps.setString(1, "해야하는일 "); //첫번재 매개변수에 해당 쿼리에 문자열을 넣는것.
ps.setString(2, "writer ");
int count = ps.executeUpdate(); //1건에 대한 내용이 카운트된다.
//executeUpdate는 dml쿼리를 실행해서 데이터베이스에 영향을 미친 행의 수를 반환 하는것.
log.info("Count: "+count);
// 행의 수가 몇개인지 반환하고
//테스트의 성공 조건
Assertions.assertEquals(count,1);
// 더미데이터로 한세트를 넣었기 때문에 영향을 미친 행의수는 1개이므로
//count의 값이 1이 아니면 테스트를 실패시키는 코드
}catch(Exception e){
log.error(e);
}
}
}
📌 4-4번에서 jsp와 servlet을 연결하는 지점에서 controller 클래스의 webservlet value값은 내가 실제로 url에 입력하는 내용이므로 jsp의 파일이 어디있는 관련이 없다.
📌 하지만 override를 진행했을때, getRequestDispatcher의 메소드안에 들어가는 url값은 실제 jsp의 파일이 어디있는지 작성해야한다.
📌 그렇다면 value 값을 "/bmi" 로 작성했다면 url에서 http://localhost:8080/bmi 를 작성하면 GET값에 들어가게 되고, post의 값을 보고싶으면 button을 만들거나, postman을 활용하는것이다.