out.write(""); 출력 부분을 jsp페이지로 넘길 예정
jsp는 add.jsp 파일명 그대로 URL매핑되어 실제 만들어진 서블릿 코드는 "\work\Catalina\~~~~\add_jsp.java"가 됨
doGet에서 직접 out.write할 필요 없이 calculator.jsp파일을 만듦
${3+4} → 7로 출력됨
서버 재가동할 필요없이 새로고침만 해도 변동 반영
서버 OPEN 혹은 더블클릭 시 sever 설정창 열림
WebContent : 개발 디렉토리
Server path : 임시 배포 디렉토리
[컴파일된 배포 파일]
workspace/.metadata/.plugins/org.eclipse.wst.server.core/temp0/work/Catalina/localhost/Root/org/apache/jsp/calculator_jsp.class
멤버함수, 멤버 변수 정의
<%! %>
일반적인 코드블럭
<% %>
출력 코드블럭
<%= %>
Page 지시자[지시블럭]
<%@ page ~~ %>
- html태그에 앞서 일반적인 코드블럭에 response.setCharacterEncoding("utf-8"); 같은 설정을 한다 하더라도 예외발생
코드지시자는 어떤 출력보다 앞서 설정이 이루어지기 때문에 지시자를 통해 설정함
내장객체
- 내가 직접 한 코드에는 없지만 jsp가 만들어낸 서블릿에 내가 모르는 변수가 존재할 수 있음 ex. "page"
- Jasper가 만들어낸 서블릿 안에 미리 선언된 변수
- pageContext, session, application, config, out, page, request, response > 내장객체라고 함
- ServletContext 전역적 사용 | PageContext 내부에서만 사용
Request 입력도구
- getParameterNames(), getParameter(name), getParameterValues(name), getCookies(), getMethod(), getSession()
getRemoteAddr(), getProtocol(), setCharacterEncoding(), getHeaderNames(), getHeaders(name), getQueryString()
Response 출력도구
- setContentType(type), setHeader(name, value),setDateHeader(name, date), sendError(status, msg), sendRedirect(url), addCookie(cookie), endcodeURL(url), setStatus(sc)
Out
- getBufferSize(), getRemaining(), clearBuffer(), flush(), close(), println(content). print(content)
- out객체는 직접 잘 쓰지 않음. jsp에서는 response.getParameter("name");할 필요 없이 바로 작성
Session
- getId(), getCreationTime(), getLastAccessedTime(),getMaxInactiveInterval(), setMaxInactiveInterval(t)
-getAttribute(attr), setAttribute(name, attr), invalidate(), removerAttribute(name)
Application
- setAttribute(name, value), getAttribute(name), getRealPath(path), getResource(path), getServerInfo(), getSession(), getRemoteAddr(), getProtocol(), setCharacterEncoding()
MVC model1
컨트롤러와 뷰가 물리적으로 분리되지 않은 방식
Model : 출력 데이터
Controller : 입력과 제어를 담당 [ 자바코드 ]
View : 출력 담당 [ HTML코드 ]
- 출력코드와 입력코드로 양분화해서 코드를 단순화
- 입력코드는 상위에 <% %>
- 출력코드는 최대한 model변수만을 출력하는 간단한 코드 ex. <%=result%> 입니다
MVC model2
컨트롤러와 뷰가 물리적으로 분리된 방식
- Controller & Dispatcher
컨트롤러에서 뷰를 연결하기 위한 방식 포워딩 > Dispatcher
- JSP : view단
cf. jsp또한 엄밀히 따지면 서블릿
- Dispatcher를 집중화하기 전, 페이지마다 Controller와 Dispatcher를 만들었지만
- 집중화 후 다수, 별도의 Controller(일반 클래스 형태)를 만들고 실질적 서블릿은 Distpatcher 하나만 만듦
- 사용자 요청이 들어오면 Dispatcher에서 적절한 Controller찾아서 처리
- Controller는 진행한 로직에 해당하는 뷰를 호출할 수 있도록 다시 Dispatcher에게 관련된 내용 전달(url-mapping)
- Dispatcher는 View 호출
- 서블릿 코드로 만들기 : src 폴더에 java파일 만들고 HttpServlet 상속 & @WebServlet("/요청이름")
- redirect : 현재 작업내용과 무관한 새로운 요청
- forward : 현재 작업내용 그대로 이어감
상태 저장소
1. pageContext : 페이지 내에서 혼자서 사용
2. request : forward 관계에서 사용
3. session : 현재 session에서 공유
4. page : 모든 페이지에서 공유
5. cookie : 클라이언트에 저장
실행은 무조건 컨트롤러에서 할 수 있음. 뷰단은 껍데기일뿐
package com.newlecture.web;
@WebServlet("/spag")
public class Spag extends HttpServlet{
@Override
protectd void doGet(HttpServletRequest request, HttpServletResponse response){
int num = 0;
String num_ = request.getParameter("n");
if(num_!= null %% !num_.equals(""))
num = Integer.parseInt(num_);
String result;
if(num%2!=0)
result = "홀수";
else
result = "짝수";
request.setAttribute("result", result);
//redirect : 별개의 새로운 작업일 때
//forward : 작업을 이어갈 때
RequestDispatcher dispatcher
= request.getRequestDispatcher("spag.jsp"); //URL상 같은 디렉터리라 경로지정안함
//jsp로 되어있지만 서블릿임
dispatcher.forward(request, response); //처리한 내용을 공유할 수 있음
}
}
-------------------------------------------------
spag.jsp
<%=request.getAttribute("result")%>
EL(Expression Language)
저장 객체에서 값을 추출해서 출력하는 표현식
Controller | View |
request.setAttribute("cnt",30); List list = new ArrayList(){"1", "test" ....}; request.setAttribute("list", list); String[] names = {"newlec", "dragon"}; request.setAttribute("names", names); Map n = new HashMap("title", "제목"); request.setAttribute("n", n); Map<String, Object> notice = new HashMap(); notice.put("id", 1); notice.put("title", "EL 좋아요"); request.setAttribute("notice", notice); |
기존의방식 request.getAttribute("cnt"); ((List) request.getAttribute("list")).get(0) ((Map) request.getAttribute("n")).get("title") |
EL ${cnt} ${list[0]} //Array, ArrayList 모두 배열처럼 꺼냄 ${names[1]} ${n.title} //HashMap : name.key으로 바로 꺼냄 ${notice.title} |
EL의 데이터 저장소
내장객체 | 설명 |
pageScope | page 영역의 생명주기에서 사용되는 저장소 |
requestScope | request 영역의 생명주기에서 사용되는 저장소, 두 개의 서블릿이 공유 |
sessionScope | session 영역의 생명주기에서 사용되는 저장소 |
applicationScope | application 영역의 생명주기에서 사용되는 저장소 |
param | 파라미터 값을 저장하고 있는 저장소 |
paramValues | 파라미터 값을 배열로 저장하고 있는 저장소 |
header | Header 정보를 저장하고 있는 저장소 |
headerValues | Header 정보를 저장하고 있는 저장소 |
cookie | 쿠키 정보를 저장하고 있는 저장소 |
initParam | 컨텍스트의 초기화 파라미터를 저장하고 있는 저장소 |
pageContext | 페이지 범위의 컨텍스트 저장소 [페이지 내에서 사용] |
- EL에선 request뿐만 아니라 pageContext (페이지객체) 또한 이용가능
<%
pageContext.setAttribute("aa", "hello");
%>
<body>
${aa} //hello 출력됨
</body>
- 일반적으로 한정사를 붙이지 않고 EL을 사용한다면 아래 저장 객체에서 값을 추출하는 순서에 따라 탐색
- 한정사를 붙인다면 특정 객체에서만 값을 찾아오겠다는 뜻, 단 sessionScope가 session객체를 뜻하는 것은 아니고 범위를 한정
- EL에서는 함수 호출하는 방식으로는 불가하고 속성처럼 가져올 수 있음
- 예시) pageContext의 경우 getter를 사용해 메소드 사용 가능 (단 표현 방식이 달라짐 - 예시참고)
그러나 getter가 아닌 속성처럼 객체를 얻거나 메소드를 호출 get빼고 소문자, 괄호 없이 이어서 씀
저장 객체에서 값을 추출하는 순서 | 한정사 사용 |
page → request → session → application page에서 찾으면 그 뒤는 찾지 않음 같은 이름의 속성이 있어도 page에서 찾으면 그 값을 출력 묵시적으로 ${cnt}로 검색하면 순서에 따라 검색 |
Scope 키워드를 사용하여 검색한다면 해당 특정 객체에서만 값을 꺼내옴 ${sessionScope.cnt} ${requestScope.cnt} ${param.cnt} ${header.host} ${header["host"]} //변수명 규칙에 맞지 않을 때는 대괄호 이용 /, 사이띄기(스페이스)가 들어갈 때 ${param.n} //?n=3 ${header.accept} //accept : 읽어들일 수 있는 문서의 종류 클라이언트 환경을 확인해 맞는 정보 전송할 때 <%= pageContext.getRequest().getMethod() %> ${pageContext.request.method} //request 객체를 가져와서 메소드 호출 |
EL연산자
[] . | |
() | |
not ! empty ${not empty param.n} 삼항연산자로 활용 가능 ${not empty param.n ? '값이 비어있습니다.' : param.n} |
empty: 논리연산자 ${empty param.n} > null이어도 참, 빈문자열이어도 참 즉 ${param.n == null || param.n == ''}과 같음 |
* / div % mod | |
+ - | |
< > <= >= lt gt le ge cf. less than, greater than, greater or equal, less or equal |
왜 <>가 아닌 lt gt를 쓸까? html 태그에 꺾음쇠가 빈번하게 사용되기 때문에 에러 방지 기본적인 html에는 간편하게 기호 사용. 쓸수없는 환경에서 문자 사용 |
== != eq ne | |
&& and | |
|| or | |
? : |
html파일 jsp파일로 변환하기
1. 복사 붙여넣기 후 File-Properties[ Alt+Enter ] : 인코딩 방식 변경 (utf-8)
2. 여전히 서블릿 코드는 달라진 게 없기 때문에 페이지 설정 상단에 추가
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
서블릿코드는 재배포, 컴파일 다시 해야하기 때문에 서버를 재가동해야했지만
jsp는 파일 내용이 바뀌어도 서버를 재시작할 필요 없음. 서블릿코드를 알아서 재스퍼가 새로 만들어줌. 새로고침만 하면 됨
실습과제 주의사항
1.
ojdbc.jar을 기존에는 bulid Path했지만,
실제로 웹개발할 때는 라이브러리를 같이 배포할 수 있도록 WEB-INF lib에 포함시켜야 한다.
특별한 자료 파일이 아니라 자바 실행환경에 해당하는 라이브러리, 톰캣 라이브러리는 굳이 넣지 않아도 된다.
(실행환경을 이미 갖추고 있기 때문에)
→ ojdbc6.jar lib파일에 복사붙여넣기
2.
CreateStatement | PrepareStatement |
Statement st = con.createStatement(); ResultSet rs = set.executeQuery(sql); |
String sql = "select * from Notice where id =?"; PreparedStatement st = con.prepareStatement(sql); st.setInt(1, id); ResultSet rs = set.executeQuery(); 미리 쿼리문을 준비하기 때문에 sql을 매개변수로 가지지 않음 |
3. MVC1으로 바꾸기
Control : 입력처리, 자바코드 상단으로 모두 빼서 rs.getString("~~")를 변수(Model)에 담음
View : 문서출력
4.MVC2로 바꾸기
Controller는 Servlet으로, View는 JSP는 물리적으로 분리코드의 복잡도는 높아지지만 개별적 유지관리 가능, 협업 가능실행 성능에서도 도움 됨
- Controller는 미리 컴파일되어 로드 후 실행하면 가벼움 | View는 그때그때 컴파일 이루어짐
Controller에서 model을 View단에 전달하기 위해 앞서 다루었던 객체들을 사용
pageContext | request | session | applicationrequest는 입력도구이면서 저장소로 사용
- Servlet 패키지, 클래스 만들기 → 패키지명 controller 클래스명 NoticeDetailController
- HttpServlet 상속, @WebServlet("/notice/detail")
- service를 추가할 것인지 get요청에 특화된 메소드 doGet을 추가할 것인지 결정해야하는데,
가능한 특화된 메소드를 사용하는 것이 좋다. - doGet안에 자바코드 복붙 후 import, 예외처리
- 흐름 제어 redirect(완전히 보냄) OR forward(작업 유지)
→ request.getRequestDispatcher("/notice/detail.jsp").forward(request, response); - forward 전에(try안에) 데이터 저장 request.setAttribute("이름", 변수);
- 서블릿을 매핑했기 때문에 list.jsp 목록의 href 링크 걸어줄 때 <a href="detail?id=~~">로 수정해야 함
기존에는 detail.jsp에서 제어+출력을 모두 담당했지만, 이제는 서블릿에서 제어를 전담하기 때문에 해당 서블릿으로 먼저 연결 - dto 만들기 (아래 참조) - controller에서 해당 객체 만들어 속성으로 담기
Notice notice = new Notice(id, title, writerId, regdate, hit, files, content); //순서 주의
request.setAttribute("n", notice); - list.jsp에서 자바코드 따로 빼서 서블릿으로 만들기
package com.newlecture.web.controller
@WebServlet("/notice/list")
public class NoticeListController {
List<Notice> list = new ArrayList<>(); //목록은 Notice객체 여러개
자바코드 복붙
list.add(notice);
//view단에 전달하기
request.setAttribute("list", list);
request.getRequestDispatcher("/notice/list.jsp").forward(request, response);
}list.jsp
출력 시 EL사용
반복문 사용 : EL은 반복을 할 수 없기 때문에 반복을 위해 태그라이브러리 사용
${list[0].writerId}
${list[0].title}
//57강에서는 jstl 배우지 않아 forEach문으로 넣었다 빼는 방법 설명함
- 사실 스파게티 코드, mvc1, mvc2 모두 사용 가능하지만 mvc2가 유지보수차원에서 권장됨
- view는 사용자가 직접 요청할 수 없게 해야함 →WEB-INF에 view폴더 생성 후 전부(admin, notice, member, student) 넣기
이에 따른 경로 변경 필수 (getRqeustDispatcher("/WEB-INF/view/notice/list.jsp").forward(request, response));
Model 데이터를 위한 구조화
- 엔티티(개체) : 개념적인 데이터 집합- 묶어서 계층을 만들었다는 의미에서 구조화된 데이터라고도 한다
request.setAttribute("title", rs.getString("title")); request.setAttribute("writerId", rs.getString("writerId)); request.setAttribute("regdate", rs.getDate("regdate")); request.setAttribute("content", rs.getString("content")); request.setAttribute("hit", rs.getInt("hit")); |
① request.addAttribute("notice", notice); 묶어서 개체로, 직관적으로 표현 가능 |
② com.newlecture.web.entity public class Notice{ private int id; private String title; private String writer; private Date regdate; private String content; private int hit; getter, setter, toString(테스트 출력위해 추가) 생성자 (오버로드된 생성자 필수, 기본생성자는 선택) } |
③ View단 getter 메소드가 존재하면 표현언어로 바로 꺼낼 수 있다 ${n.id} ${n.title} ${n.writer} ${n.regdate} ${n.content} ${n.hit} |
forEach문을 쓰기 전 자바 코드&EL태그를 이용하여 목록 불러오기
JSTL 태그라이브러리 (JSP Standard Tag Library)
- 태그라이브러리를 실제로 만들 수 있다
1.Tag Library Descriptor : WEB-INF/{파일명}.tld 파일 만듦
- 문서에서 <for>태그는 forTag 클래스가 처리하겠다
2. Tag Handler : TagSupport 클래스를 상속받아 메소드 선언
과거에는 이런식으로 직접 태그라이브러리를 만들어 사용했지만 우리는 이미 만들어진 라이브러리 사용
3. 같은 이름의 태그가 여러개 있을 수 있기 때문에 domain name으로 uri 식별자를 만들고 있다
prefix = "c" 는 곧 uri="고유도메인주소"를 의미한다는 뜻
Jasper에게 서버에서 처리할 taglib임을 알 수 있게 한다
수정전 | 수정후 |
list.jsp파일 반복문 발췌 <% List<Notice> list = (List<Notice>) request.getAttribute("list"); for(Notice n : list) { pageContext.setAttribute("n", n); } %> |
<%@ taglib prefix="c" uri=Ctrl+Space → "http://java.sun.com/jsp/jstl/core"%> //list에서 하나씩 꺼내서 n에 담아 반복 후 setAttribute까지 자동 <c:forEach var="n" items="${list}"> <tr> <td> %{n.id}</td> 중략 </c:forEach> //저장소에서 값을 꺼내오는 건 EL //태그는 꺼낸 것을 var="n"키워드로 담아 setAttribute대신함 |
항상 Controller에서 실행 |
태그라이브러리는 다섯개의 범주를 가진다. 그러나 여기선 세 가지 (Core, Format, Functions)만 알아본다.
SQL XML 사용하지 않는 게 바람직함. 뷰단에서 이것저것 처리
1. Core 제어의 행위 담당
Jasper가 HTML인지 어떤 태그인지 모르기 때문에 이를 설명하기 위해 접두사 c를 사용
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" &>
<c:fotTokens> | <c:set> |
토큰을 만들어주면서 반복 <c:forTokens var="filesName" items="${n.files}" delims=","> //delims: 구분자 |
임시변수 설정 <c:set var="" value="${}"> 연산은 el태그로 표현 삼항연산자 활용하여 null값일때 기본값 주기 |
<c:foreach> | <c:if> |
items ="${list}" 컬렉션의 내용을 꺼낸다 var = "n" pageContext에 "n"으로 담는다 begin="1" end="3" 인덱스1부터 3까지 [인덱스는 0부터시작] varStatus="st" 상태값 알기위한 사용자 지정 변수명 #{st.} 관련 속성 아래 더보기 |
test="${ }" 속성 el태그 안에 연산해서 조건문 설정 else if 태그는 따로 없고 if로 작성 |
<c:choose> | <c:when> |
<c:otherwise> | <c:catch> |
<c:remove> | <c:url> |
비권장 : param, import, redirect, out
<forEach var="n" items="${list}" varStatus="st">
${st.current} 현재 반복되는 아이템 ex.notic객체 | 숫자
${st.index} 현재 반복되는 반복 인덱스(0부터 시작) | ${st.count} 현재 반복 횟수
${st.first} 첫번째 아이템이라면 true | ${st.last} 마지막 아이템이라면 true
${st.begin} begin 속성에 설정한 값 | ${st.end} end 속성에 설정한 값
${st.step} 반복되는 인덱스의 증가치
<c:forEach var="n" items="${list}" varStatus ="st">
<tr>
<td>${st.index} / ${n.id}</td> <!-- detail.jsp에서 그냥 detail로 요청 -->
${st.current} 현재 반복되는 아이템 ex.notic객체 | 숫자
${st.index} 현재 반복되는 반복 인덱스(0부터 시작) | ${st.count} 현재 반복 횟수
${st.first} 첫번째 아이템이라면 true | ${st.last} 마지막 아이템이라면 true
${st.begin} begin 속성에 설정한 값 | ${st.end} end 속성에 설정한 값
${st.step} 반복되는 인덱스의 증가치

<c:forEach var="n" items="${list}" varStatus ="st">
<tr>
<td>${st.index} / ${n.id}</td> <!-- detail.jsp에서 그냥 detail로 요청 -->
${st.index+1} //1부터 나오고 싶다면 더해주기
Pager 번호 생성하기
현재 페이지가 해당하는 페이지 목록의 첫번째 페이지 번호(startNum)를 알아내야 한다.
먼저, 현재 페이지(page)에서 startNum까지의 간격을 알아낸다. (page-1)%5 (cf. 5 = 블록 단위)
startNum = page - (page-1)%5
사용 : set, if, forEach
forTokens로 첨부파일 목록 출력하기
2. Format 날짜, 화폐 포맷팅
Function EL을 이용해 데이터 추출해 사용할 때 문자열 조작
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
숫자 날짜 형식 | 로케일지정 | ||
formatNumber | 숫자를 양식에 맞춰 출력 | setLocale | 국제화 태그들이 사용할 로케일 지정 |
formatDate | 날짜 정보를 담은 객체 포맷팅하여 출력 | requestEncoding | 요청 파라미터의 인코딩 지정 |
parseDate | 문자열을 날짜로 파싱 | 메시지처리 | |
parseNumber | 문자열을 수치로 파싱 | bundle | 태그 몸체에서 사용할 리소스 번들 지정 |
setTimeZone | 시간대별로 시간 처리 | message(param) | 메시지 출력 |
timeZone | 시간대별로 시간 처리 | setBundle | 특정 리소스 번들 사용할 수 있도록 로딩 |
날짜 및 시간 표현
"yyyy-MM-dd hh:mm:ss"
<fmt:formatDate pattern="yyyy-MM-dd hh:mm:ss" value="${n.regdate}"/>
월은 왜 대문자일까? minute과의 구분 위해 | yy-mm-h:m:s 등 패턴 다양하게 표현 가능
숫자표현
자릿수가 커질 때 가독성을 위해 숫자 끊어 볼 수 있도록 포맷팅
<fmt:formatNumber value="${n.hit}"/> value에 원하는 숫자값 넣는 순간 자동 포맷팅 (세자리 끊기)
- type = "percent/number/currency($)" //원화 표현 : <fmt:formatNumber value="${amount}" type="number/> 원
- maxIntegerDigits (정수 몇째자리까지 표현할지)
- maxFractionDigits (소수점은 셋째자리까지가 디폴트)
- pattern 그외 표현 //넷째자리마다 끊기, pattern="#,####" type="number" 앞의 #의 개수는 중요하지 않음
//"#,###원"
3. Functions로 EL에서 함수 이용하기
head에 페이지 지시자 추가
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
contains, containsIngnoreCase, endsWith, indexOf, replace, split, startWith, substring 등
사용방법 : ${fn:함수명(변수명)} //el태그 내부에서 사용한다
원하는 함수가 없을 경우 jstl function으로 커스텀할 수 있지만,
뷰단에서 그렇게 까지 데이터를 변환할 일 없음.
'Spring > Servlet' 카테고리의 다른 글
뉴렉처 서블릿4 - 공지사항 게시판 만들기 예제 (0) | 2021.06.14 |
---|---|
뉴렉처 스프링 MVC (0) | 2021.06.09 |
뉴렉처 서블릿2 (동적 페이지 | doGet, doPost) (0) | 2021.06.06 |
스프링 애너테이션 (0) | 2021.06.03 |
뉴렉처 서블릿 (0) | 2021.06.01 |