2022.10.17 [MON]
- Day 50, 51-
수업 주요 목차
- Member 예제 Update, register 추가
- Semi project 조 짜기+첫 회의
- Interceptor(Day 50)
- Ajax - intro (Day 50)
🤖Review
🔎 Eclipse 실습 내용
1. Update
- MemberDAO에 추가된 method
public void update(String id, String name, String password, String address) throws SQLException {
Connection con=null;
PreparedStatement pstmt=null;
try {
con=getConnection();
String sql="UPDATE MEMBER SET name=?,password=?,address=? WHERE id=?";
pstmt=con.prepareStatement(sql);
pstmt.setString(1, name);
pstmt.setString(2, password);
pstmt.setString(3, address);
pstmt.setString(4, id);
pstmt.executeUpdate();
} finally {
closeAll(pstmt,con);
}
}
- index.jsp에 추가된 코드
<form action="UpdateMemberFormController.do">
<button type="submit">회원정보 수정</button><br>
</form>
- UpdateMemberFormController
package org.kosta.myproject.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UpdateMemberFormController implements Controller {
@Override
public String handlerRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
return "redirect:update-form.jsp";
}
}
- update-form.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원정보 수정</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<style type="text/css">
body{
background: linear-gradient(to right, rgba(115, 209, 250), rgba(115, 169, 255));
color: white;
text-align:center;
}
input{
background-color: transparent;
border: 2px solid white;
border-radius: 9999em;
}
button{
color: gray;
background-color: white;
border: 2px solid white;
border-radius: 50px;
}
a{
color:white;
}
</style>
</head>
<body>
<div class="container pt-3">
<form action="UpdateController.do" method="post">
<h5>↓수정할 회원의 아이디↓</h5>
<input type="text" name="memberId" value=" ${sessionScope.member.id}" readonly="readonly"><br><br>
<h5>↓수정할 정보↓</h5>
이름 <br> <input type="text" name="memberName" value=" ${sessionScope.member.name}"><br><br>
비밀번호 <br><input type="text" name="memberPassword" value=" ${sessionScope.member.password}"><br><br>
주소 <br><input type="text" name="memberAddress" value=" ${sessionScope.member.address}"><br><br><br>
<button type="submit">수정</button>
</form>
<br><br>
<a href="index.jsp">홈으로 돌아가기</a>
</div>
</body>
</html>
value에 저장된 값을 readonly로 설정하면 사용자가 변경할 수 없지만 controller에서 getParameter로 가져올 수 있다.
- UpdateController
package org.kosta.myproject.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.kosta.myproject.model.MemberDAO;
import org.kosta.myproject.model.MemberVO;
public class UpdateController implements Controller {
@Override
public String handlerRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// MemberVO mvo=(MemberVO) request.getSession(false).getAttribute("member");
// String id=mvo.getId(); // 세션에 저장된 정보로 id가져오는 방법
String id=(String) request.getParameter("memberId");
String password=(String) request.getParameter("memberPassword");
String name=(String) request.getParameter("memberName");
String address=(String) request.getParameter("memberAddress");
MemberDAO.getInstance().update(id,name,password,address);
MemberVO mvo=new MemberVO(id, password, name, address);
request.getSession().setAttribute("member", mvo);
return "redirect:update-result.jsp";
}
}
method를 통해 변경된 내용을 session에 setAttribute로 저장을 해줘야 바로 적용된다.
- update-result.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Update-result</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<style type="text/css">
body{
background: linear-gradient(to right, rgba(115, 209, 250), rgba(115, 169, 255));
color: white;
text-align:center;
font-family: IBM Plex Sans KR;
}
a{
color:white;
}
table{
margin-top: 30px;
background-color: rgba(255,255,255,0.1);
text-align:center;
}
td{
width:20%;
color: white;
}
th{
height:50px;
color: gray;
font-weight: 500;
background-color: rgba(255,255,255,0.3);
}
</style>
</head>
<body>
<div class="container pt-3">
<h2>회원정보 수정 결과</h2>
<table class="table table-bordered table-hover">
<thead>
<tr>
<td>아이디</td>
<td>패스워드</td>
<td>이름</td>
<td>주소</td>
</tr>
</thead>
<tbody>
<tr>
<td>${sessionScope.member.id}</td>
<td>${sessionScope.member.password}</td>
<td>${sessionScope.member.name}</td>
<td>${sessionScope.member.address}</td>
</tr>
</tbody>
</table>
<br><br><br>
<a href="index.jsp">홈으로 돌아가기</a>
</div>
</body>
</html>
↓
2. Register
- index.jsp 에 추가된 코드
<a href="register-form.jsp">회원가입</a>
- register-form.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<style type="text/css">
body{
background: linear-gradient(to right, rgba(115, 209, 250), rgba(115, 169, 255));
color: white;
text-align:center;
}
input{
background-color: transparent;
border: 2px solid white;
border-radius: 9999em;
}
button{
color: gray;
background-color: white;
border: 2px solid white;
border-radius: 50px;
}
a{
color:white;
}
</style>
</head>
<body>
<div class="container pt-3">
<%--
index.jsp -- register-form.jsp -- FrontControllerServlet -- HandlerMapping -- RegisterController -- MemberDAO -- DB
| ㄴ register(memberVO)
| redirect
|
register-result.jsp
회원가입 개발단계
1. sql test
2. MemberDAO : model 구현
3. TestCase
4. Controller
5. View 구현
--%>
<form action="RegisterController.do" method="post">
<h3>회원가입</h3><br>
<input type="text" name="memberId" placeholder="아이디" required="required"><br><br>
<input type="password" name="memberPassword" placeholder="패스워드" required="required"><br><br>
<input type="text" name="memberName" placeholder="이름" required="required"><br><br>
<input type="text" name="memberAddress" placeholder="주소" required="required"><br><br>
<button type="submit">회원가입</button><br><br>
<a href="index.jsp">홈으로 돌아가기</a>
</form>
</div>
</body>
</html>
- RegisterController
package org.kosta.myproject.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.kosta.myproject.model.MemberDAO;
import org.kosta.myproject.model.MemberVO;
public class RegisterController implements Controller {
@Override
public String handlerRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
String path=null;
String id=request.getParameter("memberId");
String pw=request.getParameter("memberPassword");
String name=request.getParameter("memberName");
String address=request.getParameter("memberAddress");
MemberDAO.getInstance().register(new MemberVO(id,pw,name,address));
path="redirect:register-result.jsp";
return path;
}
}
- register-result.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<style type="text/css">
body{
background: linear-gradient(to right, rgba(115, 209, 250), rgba(115, 169, 255));
color: white;
text-align:center;
}
a{
color:white;
}
</style>
</head>
<body>
<div class="container pt-3">
회원가입을 축하합니다! <br><br>
<a href="index.jsp">홈으로 돌아가기</a>
</div>
</body>
</html>
↓
Session이 30분이 지나서, 또는 다른 이유로 끊겼을 때(위 예제의 경우 로그인 상태가 풀리거나 세션이 끊겼을 때) 회원정보 수정, 검색 등의 기능을 이용하지 못하게 하기 위한 인증절차를 모든 controller 첫부분에 아래 코드를 삽입하는 방법으로 Day 49 수업이 마무리 됐었다.
HttpSession session=request.getSession(false);
if(session==null||session.getAttribute("member")==null) {
return "redirect:index.jsp";
}
그리고 오늘 이 반복되는 코드를 refactoring해주기 위해 FrontController의 doDispatch()메소드에 추가하려고 했으나 이미 doDispatch()는 할일이 너무 많다. 이 때 사용하는 것이 Interceptor이다.
Interceptor : 클라이언트의 요청을 가로채서 미리 인증절차를 진행시키는 것
FrontControllerServlet --- HandlerMapping --- Controller --- Model(DAO) --- DB | | | FindIdController, CheckLoginInterceptor UpdateMemberController ... |
각 Controller에서 중복되는 인증절차를 FrontControllerServlet에서 우선적으로 처리하도록 FrontControllerServlet의 doDispatch() method 에 CheckLoginInterceptor의 리턴값을 통해 false(진행불가; 인증 필요서비스인데 비인증 사용자 요청)면 로그인페이지로 이동, true(진행; 인증상태)면 계속 진행하도록 설정할 수 있다.
🧚♂️
Front Controller Design Pattern : 모든 클라이언트의 요청을 하나의 진입점으로 모아 공통정책을 효과적으로 수행, 예외처리, 보안(인증)
🔎 Eclipse 실습 내용
1. CheckLoginInterceptor
* 현 시스템에서 인증이 필요없는 서비스 컨트롤러 목록 정의
* 인증이 필요한 서비스컨트롤러에 대해 인증 체크 후 인증상태이면 true를 반환, 아니면 false를 반환
package org.kosta.myproject.controller;
import java.util.ArrayList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class CheckLoginInterceptor {
private static CheckLoginInterceptor instance=new CheckLoginInterceptor();
//인증이 필요없는 컨트롤러 리스트
private ArrayList<String> permitAllList=new ArrayList<>();
private CheckLoginInterceptor() {
permitAllList.add("TestController");
permitAllList.add("LoginController");
permitAllList.add("RegisterController");
}
public static CheckLoginInterceptor getInstance() {
return instance;
}
public boolean checkLogin(HttpServletRequest request, String controllerName) {
boolean result=true;
if(permitAllList.contains(controllerName)==false) { // 인증이 필요한 서비스 컨트롤러이면 인증을 체크한다.
HttpSession session=request.getSession(false); // 세션이 없으면 null, 있으면 기존 Session 반환
if(session==null || session.getAttribute("member")==null){ // 비인증 상태이면 false 리턴
System.out.println("**"+getClass().getName()+" "+controllerName+" 서비스 상태이므로 로그인해야 함**");
result=false;
}
}
return result;
}
}
2. FrontControllerServlet
🧚♂️
FrontControllerServlet : 모든 클라이언트 요청의 단일한 진입점 -> 공통정책을 효과적으로 적용 (인코딩, 예외처리, 보안 등)
package org.kosta.myproject.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("*.do")
public class FrontControllerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String servletPath=request.getServletPath(); //서블렛 이름 (~~.do)
String controllerName=servletPath.substring(1, servletPath.lastIndexOf(".")); // (.do 제외한 이름)
boolean checkResult=CheckLoginInterceptor.getInstance().checkLogin(request, controllerName);
if(checkResult==false) { // 인증이 필요한 서비스임에도 클라이언트가 비인증 상태이면
response.sendRedirect("index.jsp"); // 로그인 폼이 있는 페이지로 redirect
return; // 현 doDispatch 메서드 실행을 중단한다.
}
Controller controller=HandlerMapping.getInstance().create(controllerName); // 객체생성요청
String viewPath=controller.handlerRequest(request, response); //controller 실행
if(viewPath.startsWith("redirect:")) {
response.sendRedirect(viewPath.trim().substring(9));
} else {
request.getRequestDispatcher(viewPath).forward(request, response);
}
} catch (Exception e) {
e.printStackTrace();
response.sendRedirect("error.jsp");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doDispatch(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
doDispatch(request,response);
}
}
Ajax: JavaScript와 XML을 이용하여 Web Client와 Server간의 비동기적 처리를 지원하는 프로그래밍 기법
- Google의 Google Map이 AJAX를 이용하여 구축된 이후 각광 받음.
- Web 2.0의 Rich Client 기술로 인식되어 Web의 많은 분야에서 적용되는 추세
- AJAX는 그 자체가 하나의 기술이라기 보다는 패턴을 의미
- 기존 Web Browser가 제공하는 기능을 활용하는 프로그래밍 패턴.
- 기존 기술인 JavaScript와 XML등을 이용한 프로그래밍 패턴
🔎 Eclipse 실습 내용
1. 동기적(AJAX 이전)
- jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>동기적방식</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
<form action="SynServlet">
<input type="text" name="id" required="required" placeholder="아이디">
<button type="submit">동기방식테스트</button>
</form>
<br><br>
<textarea rows="50" cols="50"></textarea>
</div>
</body>
</html>
- Servlet
package step1;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/SynServlet")
public class SynServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>동기적 방식</title>");
out.println("</head>");
out.println("<body bgcolor=yellow>");
out.println("SynServlet이 응답했습니다");
out.println("</body>");
out.println("</html>");
out.close();
}
}
↓
(페이지 변동, 기존 작성 내용 유지X)
2.비동기적(AJAX 적용)
- jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ajax basic</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container pt-3">
<form>
<input type="text" name="id" required="required" placeholder="아이디">
<button type="button" onclick="startAjax()">비동기Ajax방식테스트</button>
</form>
<br><br>
<span id="result"></span>
<br><br>
<textarea rows="50" cols="50"></textarea>
</div>
<script type="text/javascript">
let xhr;
function startAjax(){
xhr=new XMLHttpRequest(); //Ajax 통신을 위한 자바스크립트 객체
//ajax 요청에 대한 서버의 응답이 완료될 때 실행 될 함수 : callback 함수
//ajax 콜백 함수를 등록
xhr.onreadystatechange=callback;
//alert("startAjax"+xhr);
xhr.open("get", "AsynServlet");
xhr.send();
}
//서버가 정상적으로 응답하면 동작될 함수
function callback() {
//readystate==4 -> 서버로부터 응답이 완료된 상태
//status==200 -> 응답 상태 코드 200은 정상 수행
if(xhr.readyState==4&&xhr.status==200){
//alert(xhr.responseText); //responseText : 서버가 응답한 데이터
// result id span 영역에 서버에서 응답받은 데이터를 입력한다.
document.getElementById("result").innerHTML=xhr.responseText;
}
}
</script>
</body>
</html>
-Servlet
package step2;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/AsynServlet")
public class AsynServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private int count;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
++count;
/*
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
//Ajax 통신의 응답은 페이지가 아니라 필요한 데이터만 응답한다.
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
System.out.println("AsynServlet ajax 요청에 대한 응답");
out.print("hello ajax count: "+count);
out.close();
}
}
↓
//Semi project 진행상황
https://www.notion.so/babmuk/d8566c3387e343ceb5bbd3cdb83c17a7
//오늘의 숙제
복습