## 63. 쿠키와 ServletContext 보관소 활용하기
- 쿠키를 이용하여 로그인 할 때 입력한 전화번호을 보관하기
- 서블릿들이 공통으로 사용하는 객체를 ServletContext 보관소에 보관하기
ㄴ BoardFormServlet.java 파일 복사하기
=>
ㄴ 복사한 파일을 같은 위치에 LoginFormServlet 이라는 이름으로 붙여넣기
=>
LoginFormServlet.java
ㄴ board -> auth 로 변경해주기
=>
LoginFormServlet.java
ㄴ 게시글 -> 로그인 으로 변경해주기
=>
webapp > auth > form.html
ㄴ 해당 코드 복사하기
=>
LoginFormServlet.java
ㄴ 복사한 코드 붙여넣기
=>
LoginFormServlet.java
ㄴ out.println 형식으로 변경해주기
=>
LoginFormServlet.java
ㄴ " -> ' 로 변경해주기
=>
HeaderServlet.java
ㄴ /auth/form.html -> /auth/form 으로 변경하기
=>
ㄴ auth 폴더 자체를 삭제
=>
ㄴ LoginFormServlet 은 BoardFormServlet 과 다르게 category 가 필요 없음
=>
ㄴ App 실행
=>
ㄴ 로그인 선택
=>
ㄴ [로그인] 선택
=>
쿠키를 이용하여 로그인 할 때 입력한 전화번호을 보관하는 기능 추가하기
LoginFormServlet.java
ㄴ 이메일 저장 체크박스 추가
=>
=>
LoginServlet.java
ㄴ javax.servlet.http 의 Cookie 클래스 import 하기
=>
LoginServlet.java
ㄴ saveEmail 파라미터
=>
LoginServlet.java
ㄴ 넘어온 파라미터 값 중 savePhone 이 있다면 쿠키 생성하여 서블릿에서 클라이언트로 쿠키를 전송하도록 함
=>
LoginServlet.java
ㄴ 쿠키의 유효 시간을 0으로 설정하여 해당 쿠키를 바로 삭제하도록 함
=>
LoginServlet.java
ㄴ 쿠키 저장하는 코드 추가
=>
LoginFormServlet.java
ㄴ 쿠키 하나씩 꺼낼 수 없어 배열에 담아서 가져오도록 함
=>
LoginFormServlet.java
ㄴ println -> printf 로 변경
ㄴ phone 을 value 값으로 설정해주기
=>
ㄴ App 실행
=>
ㄴ 전화번호 저장 체크박스 체크하지 않고 [로그인] 선택하여 로그인하기
=>
ㄴ [로그아웃] 선택
=>
ㄴ 다시 [로그인] 선택 시 전화번호는 저장되어있지 않음을 확인
ㄴ 전화번호 저장 체크박스 체크한 후 [로그인] 선택하여 로그인하기
=>
ㄴ [로그아웃] 선택하여 로그아웃 하기
=>
ㄴ [로그인] 선택
=>
ㄴ 로그인 할 때 입력했던 전화번호가 저장되어 있음을 확인
서블릿들이 공통으로 사용하는 객체를 ServletContext 보관소에 보관하기
ㄴ listener 패키지에 ContextLoaderListener 라는 이름의 새로운 클래스 파일 생성
=>
InitServlet.java
ㄴ 해당 코드 복사하기
=>
ContextLoaderListener.java
=>
ContextLoaderListener.java
ㄴ contextInitialized 메서드 내부에서 ServletContext 객체를 얻고 이 객체를 통해 웹 애플리케이션의 설정 정보나 공유 데이터에 접근할 수 있음
=>
ContextLoaderListener.java
ㄴ 위쪽에서 복사한 코드 붙여넣기
=>
ContextLoaderListener.java
ㄴ InitServlet 대신 해당 객체들을 ContextLoaderListener 에서 생성해주도록 함
=>
ContextLoaderListener.java
=>
ContextLoaderListener.java
ㄴ mybatis-config.xml 의 경로 복사
=>
ContextLoaderListener.java
ㄴ <context-param> 태그의 <param-name> 에 mybatis-config 추가하기
ㄴ <context-param> 태그의 <param-value> 에 복사한 경로 붙여넣기
=> 웹 애플리케이션의 web.xml 파일에 <context-param> 요소를 추가하여 웹 애플리케이션의 초기 설정 정보를 정의하는 역할을 함
=>
ContextLoaderListener.java
ㄴ mybatis-config.xml 설정 파일을 읽어오도록 함
ㄴ SqlSessionFactoryBuilder의 build 메서드를 호출하여 SqlSessionFactory 인스턴스를 생성함
ㄴ 생성된 SqlSessionFactory 인스턴스를 SqlSessionFactoryProxy로 감싸서 반환함
=>
ContextLoaderListener.java
ㄴ 리스너 클래스를 서블릿 컨테이너에 등록하기
=> 해당 리스너가 이벤트를 처리하게 되며, 따로 web.xml 파일에 리스너를 등록할 필요가 없어짐
=>
ContextLoaderListener.java
ㄴ 공통 객체 준비가 완료되면 해당 문구 출력하도록 코드 추가
LoginServlet.java
ㄴ 서블릿 컨텍스트에서 "memberDao"라는 이름으로 저장된 속성(Attribute) 값을 가져오는 부분
ㄴ getServletContext() 메서드는 서블릿에서 제공하는 메서드로, 서블릿 컨텍스트 객체를 반환함
ㄴ 서블릿 컨텍스트는 웹 애플리케이션 전체에서 공유되는 데이터 저장소이며, 서블릿 간 데이터 공유에 사용됨
=>
ㄴ App 실행 시 공통 객체 준비 완료됨을 확인
=>
=>
ㄴ 로그인을 정상적으로 실행함을 확인
=>
ㄴ InitServlet.java 파일 삭제
=>
ㄴ InitServlet.java 삭제하자마자 수정한 코드를 제외하고 모두 에러 발생
=>
BoardAddServlet.java
=>
BoardAddServlet.java
ㄴ 현재 필요한 객체들 모두 생성해주기
=>
BoardAddServlet.java
ㄴ InitServlet 제거해주기
=>
나머지도 같은 방식으로 수정해주기
BoardListServlet.java
ㄴ 현재 필요한 객체 생성시키도록 함
=>
ㄴ InitServlet 모두 제거해주기
BoardUpdateServlet.java
ㄴ 현재 필요한 객체 생성시키도록 함
=>
BoardUpdateServlet.java
ㄴ InitServlet 모두 제거해주기
MemberAddServlet.java
ㄴ 현재 필요한 객체 생성시키도록 함
=>
MemberAddServlet.java
ㄴ InitServlet 모두 제거해주기
MemberDeleteServlet.java
ㄴ 현재 필요한 객체 생성시키도록 함
=>
MemberDeleteServlet.java
ㄴ InitServlet 모두 제거해주기
MemberDetailServlet.java
ㄴ 현재 필요한 객체 생성시키도록 함
=>
MemberDetailServlet.java
ㄴ InitServlet 모두 제거해주기
MemberListServlet.java
ㄴ 현재 필요한 객체 생성시키도록 함
=>
MemberListServlet.java
ㄴ InitServlet 모두 제거해주기
MemberUpdateServlet.java
ㄴ 현재 필요한 객체 생성시키도록 함
=>
MemberUpdateServlet.java
ㄴ InitServlet 모두 제거해주기
BoardDeleteServlet.java
ㄴ 현재 필요한 객체 생성시키도록 함
=>
BoardDeleteServlet.java
ㄴ InitServlet 모두 제거해주기
BoardFileDeleteServlet.java
ㄴ 현재 필요한 객체 생성시키도록 함
=>
BoardFileDeleteServlet.java
ㄴ InitServlet 모두 제거해주기
ReportServletRequestListener.java
ㄴ 삭제한 InitServlet 대신 객체를 생성해주도록 함
=>
ㄴ App 실행
=>
=>
=>
ㄴ [새 회원] 선택
=>
ㄴ 값 입력 후 [등록] 선택
=>
ㄴ 생성한 회원의 이름 링크 선택
=>
=>
ㄴ 값 변경 후 [변경] 선택
=>
ㄴ 값이 변경됨을 확인
=>
ㄴ [삭제] 선택
=>
ㄴ 삭제가 정상적으로 처리됨을 확인
ㄴ [새 글] 선택
=>
ㄴ [등록] 선택
=>
ㄴ 생성한 게시글 제목 링크 선택
=>
ㄴ 첨부파일 링크 선택
=>
=>
ㄴ 첨부파일 > [삭제] 선택
=>
ㄴ 첨부파일이 정상적으로 삭제됨을 확인
=>
ㄴ 값 변경 후 [변경] 선택
=>
ㄴ 변경 값이 리스트에 정상적으로 출력됨을 확인
=>
ㄴ 첨부파일이 존재하는 상태에서 [삭제] 선택
=>
ㄴ 실행 오류 발생
=>
ㄴ 첨부파일을 모두 삭제하고 [삭제] 선택
=>
ㄴ 정상적으로 삭제됨을 확인
Auto Import 설정
ㄴ Editor > General > Auto Import 선택 > Optimize imports on the fly 체크박스를 체크 > [OK] 선택
64. JSP를 이용하여 MVC 모델1 구조로 변경하기
ㄴ 기존에 있던 webapp > board > form.jsp 삭제
=>
ㄴ 새로운 jsp 파일 생성
=>
webapp > board > form.jsp
ㄴ JSP 페이지의 속성을 설정하는 부분 (꼭 써줘야 함)
=>
webapp > board > form.jsp
ㄴ BoardFormServlet.java 파일의 일부분을 복사하여 붙여넣기
=>
webapp > board > form.jsp
<%@ page
language="java"
pageEncoding="UTF-8"
contentType="text/html;charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>비트캠프</title>
</head>
<body>
<div style='height: 50px;background-color:#c2e0f0;'>
<img src='https://www.ncloud.com/public/img/logo-m.png' style='height: 40px'>
<a href='/member/list'>회원</a>
<a href='/board/list?category=1'>게시글</a>
<a href='/board/list?category=2'>공지사항</a>
<a href='/auth/form'>로그인</a>
</div>
<h1>게시글(JSP)</h1>
<form action='/board/add' method='post' enctype='multipart/form-data'>
제목 <input type='text' name='title'><br>
내용 <textarea name='content'></textarea><br>
파일 <input type='file' name='files' multiple><br>
<input type='hidden' name='category' value='1'>
<button>등록</button>
</form>
<div style='text-align:center;background-color:gray;color:white;padding:10px;'>
<p style='font-size:90%;margin: 0px;'>비트캠프 + 매직에꼴 + 네이버클라우드@2023</p>
<address style='font-size: x-small; font-style:italic;'>서울시 강남구 강남대로 94길 20, 삼오빌딩 5층</address>
</div>
</body>
</html>
ㄴ header 와 footer 를 하드코딩하여 붙여넣기
=>
ㄴ url 에서 /board/form 뒤에 .jsp 를 직접 붙여주기
=>
ㄴ 페이지 소스 보기 선택
=>
ㄴ 하드코딩 된 header 와 footer 를 확인
=>
ㄴ url 에서 form 뒤에 .jsp 붙이기
=>
ㄴ JSP 파일이 정상적으로 노출됨을 확인
=>
ㄴ WEB-INF 에 새로운 header.jsp 파일 생성
=>
header.jsp
ㄴ jsp 설정코드와 webapp > board > form.jsp 의 헤더 부분의 코드를 잘라내서 붙여넣기
=>
ㄴ header.jsp 를 복사하여 footer.jsp 라는 파일 생성하기
=>
footer.java
ㄴ webapp > board > form.jsp 의 헤더 부분의 코드를 잘라내서 붙여넣기
=>
https://docs.oracle.com/cd/E13226_01/workshop/docs81/pdf/files/workshop/JSPTagsReference.pdf
ㄴ 여기서 용어는 jsp 는 namespace 라고 하고 include 는 element 라고 함
=>
webapp > board > form.jsp
ㄴ <jsp:include page="../header.jsp"></jsp:include> 은 <jsp:include page="../header.jsp"/> 로 간단하게 할 수 있음
=>
webapp > board > form.jsp
ㄴ footer 도 header 와 동일하게 적용해주기
=>
webapp > board > form.jsp
ㄴ jsp:include Action Tag를 이용한 것임을 확인하기 위해 적어줌
=>
ㄴ jsp:include Action Tag를 이용한 것임을 확인
=>
ㄴ 페이지 소스 보기 선택
=>
ㄴ header.jsp 와 footer.jsp 의 내용이 포함되어 출력됨을 확인
MemberListServlet.java
ㄴ 해당 이미지 링크 복사하기
=>
header.jsp
=>
HeaderServlet.java
ㄴ 해당 코드 복사
=>
header.jsp
ㄴ 복사한 코드 붙여넣은 후 expression element 태그를 이용하여 묶어주기
HeaderServlet.java
ㄴ 해당 import 복사
=>
Servlet.jsp
ㄴ import 를 jsp 형식으로 해주기
https://javaee.github.io/javaee-spec/javadocs/
ㄴ java.servlet.jsp > JspWriter
=> jsp 는 JspWriter 를 사용함
header.jsp
ㄴ println 으로 구조를 변경
ㄴ String.format() 사용하기
=>
header.jsp
<%@ page
language="java"
pageEncoding="UTF-8"
contentType="text/html;charset=UTF-8"%>
<%@ page import ="bitcamp.report.vo.Member"%>
<div style='height: 50px;background-color:#c2e0f0;'>
<img src='https://www.ncloud.com/public/img/logo-m.png' style='height: 40px'>
<a href='/member/list'>회원</a>
<a href='/board/list?category=1'>게시글</a>
<a href='/board/list?category=2'>공지사항</a>
<%
Member loginUser = (Member) request.getSession().getAttribute("loginUser");
if (loginUser == null) {
out.println("<a href='/auth/form'>로그인</a>");
} else {
if (loginUser.getPhoto() == null) {
out.println("<img style='height:40px' src='/images/avatar.png'>");
} else {
out.println(String.format(
"<img src='http://urnfabxxeceu19010753.cdn.ntruss.com/member/%s?type=f&w=30&h=40&faceopt=true&ttype=jpg'>",
loginUser.getPhoto()));
}
out.println(String.format("%s <a href='/auth/logout'>로그아웃</a>", loginUser.getName()));
}
%>
</div>
=>
form.jsp
ㄴ Scriptlet 사용함을 나타내주기 위해 추가
=>
ㄴ 정상적으로 출력됨을 확인
ㄴ BoardFormServlet.java 파일 삭제하기 (jsp 파일로 변경했기 때문)
BoardListServelet.java
ㄴ /board/form -> /board/form.jsp 로 변경하기 (이제 새 글 링크에서 jsp 파일로 이동 가능하도록 함)
=>
ㄴ [새 글] 선택
=>
ㄴ 새 글 /board/form.jsp 로 이동됨을 확인
ㄴ webapp/board/form.jsp 파일 복사
=>
ㄴ list.jsp 라는 이름으로 같은 위치에 붙여넣기
=>
form.jsp 와 같은 방식으로 나머지 XxxServlet.java 파일도 모두 Xxx.jsp 로 변경해주기
=>
list.jsp
ㄴ 한 줄에 import 문을 2개 작성할 수 있음
=>
list.jsp
ㄴ servlet 관련 import 는 알아서 해주므로 제거
=>
list.jsp
ㄴ <%! ... %>는 JSP 페이지 내에서 선언적인 코드 블록을 나타냄 (declaration element : 필드와 메서드 선언)
=>
list.jsp
ㄴ 해당 코드는 위쪽의 jsp 설정인 directive element 에서 설정해주므로 제거
=>
list.jsp
ㄴ jsp 에서는 JspWriter 를 사용하므로 제거
=>
list.jsp
ㄴ 아래쪽 코드 대신 위쪽의 <jsp:include> 태그를 사용하여 JSP 페이지에서 다른 JSP 페이지를 포함하거나 삽입하도록 함
=>
list.jsp
ㄴ 표현식 태그 이용하기
=>
list.jsp
ㄴ 자바 코드는 <% %> (스크립트릿)으로 묶어주기
=>
list.jsp
<%@ page
language="java"
pageEncoding="UTF-8"
contentType="text/html;charset=UTF-8"%> <%-- directive element --%>
<%@ page import="java.io.IOException"%>
<%@ page import="java.text.SimpleDateFormat"%>
<%@ page import="java.util.List"%>
<%@ page import="bitcamp.report.dao.BoardDao"%>
<%@ page import="bitcamp.report.vo.Board"%>
<%!
// declaration element
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
%>
<%
// scriptlet (scripting element)
int category = Integer.parseInt(request.getParameter("category"));
%>
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>게시글</title>
</head>
<body>
<jsp:include page="../header.jsp"/>
<h1>게시글 목록</h1>
<div style='margin:5px;'>
<a href='/board/form.jsp?category=<%=category%>'>새 글</a>
</div>
<table border='1'>
<thead>
<tr><th>번호</th> <th>제목</th> <th>작성자</th> <th>조회수</th> <th>등록일</th></tr>
</thead>
<%
BoardDao boardDao = (BoardDao) this.getServletContext().getAttribute("boardDao");
List<Board> list = boardDao.findAll(category);
out.println("<tbody>");
for (Board board : list) {
out.printf(
"<tr>"
+ " <td>%d</td>"
+ " <td><a href='/board/detail?category=%d&no=%d'>%s</a></td>"
+ " <td>%s</td>"
+ " <td>%d</td>"
+ " <td>%s</td></tr>\n",
board.getNo(), board.getCategory(), board.getNo(),
(board.getTitle().length() > 0 ? board.getTitle() : "제목없음"), board.getWriter().getName(),
board.getViewCount(), dateFormatter.format(board.getCreatedDate()));
}
%>
</tbody>
</table>
<a href='/'>메인</a>
<jsp:include page="../footer.jsp"/>
</body>
</html>
header.jsp
ㄴ /board/list -> /board/list.jsp 로 변경하기
=>
ㄴ BoardListServlet.java 파일은 삭제해주기
=>
ㄴ 깔끔하게 App Restart
=>
ㄴ [게시글] 선택
=>
=>
ㄴ header.jsp 파일 복사
=>
ㄴ 복사한 파일을 같은 위치에 index 라는 이름으로 붙여넣기
=>
index.jsp
<%@ page
language="java"
pageEncoding="UTF-8"
contentType="text/html;charset=UTF-8"%>
<%@ page import ="bitcamp.report.vo.Member"%>
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>비트캠프</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<h1>MyApp11</h1>
<p>개인 프로젝트 입니다</p>
<jsp:include page="footer.jsp"/>
</body>
</html>
ㄴ HomeServlet.java 내용 복사하여 수정하기
=>
ㄴ HomeServlet.java 파일 삭제
webapp > WEB-INF > web.xml
ㄴ <welcome-file> 태그에 index.jsp 를 맨 위로 올려주도록 함
index.jsp
<%@ page
language="java"
pageEncoding="UTF-8"
contentType="text/html;charset=UTF-8"%>
<%@ page import ="bitcamp.report.vo.Member"%>
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>비트캠프</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<h1>MyApp(JSP)</h1>
<p>개인 프로젝트 입니다</p>
<jsp:include page="footer.jsp"/>
</body>
</html>
=>
App Restart
=>
ㄴ [게시글] 선택
=>
ㄴ printf 는 사용할 수 없음을 확인
=>
list.jsp
ㄴ println 과 String.format() 을 이용
=>
list.jsp
ㄴ 자바코드를 최대한 없애기