45. JSP MVC model1
- jsp를 잘못 만들면 코드 블럭이 복잡해지는 문제 해결을 위해 고안

- 흩어져 있는 코드 블럭을 한 곳에 모으기
입력코드, 출력코드 분리
출력할 데이터를 변수화해 제어
→ 코드블럭을 최대한 한 곳에 넣고, model 변수에 결과만 넣은 방식
MVC model1

- controller : 순수 자바 코드
- view : 주로 html 코드
- model : 출력 데이터
46. JSP MVC model1 VS model2


- Controller는 서블릿
- View는 jsp 형태로 만듬

- Controller 단에서 View를 연결하기 위해 포워딩하는 방법이 사용됨.
- View는 jsp로 되어있긴 하지만 서블릿. Controller 도 서블릿.
- 서블릿에서 서블릿으로 이전되며 흐름을 이어받아 코드를 진행하는 데 사용되는 게 포워딩.

- 수가 추가될 때마다 Controller는 Dispatcher를 이용해 포워딩함.

- 하지만 각각의 Controller 마다 가지고 있는 Dispatcher 기능이 비효율적이기 때문에, Dispatcher는 하나만 두고, Controller를 전담하는 것을 따로 만듬.
- 실질적으로 Servlet은 하나만 만들고, 일반적인 업무 로직은 별도의 POJO 클래스로, 즉 서블릿 클래스가 아닌 일반 클래스로 만듬.

- 사용자 요청이 들어오면 Dispatcher가 적절한 Controller를 찾아 수행하도록 함.

- Controller는 로직에 해당하는 View를 호출해서 Dispatcher에게 관련 내용을 알림.
- Dispatcher는 그 내용을 가지고 응답.
코드
- 이제 jsp는 껍데기고, java 파일이 실행되어야 하기 때문에 java에서 서버 재실행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.newlecture.web;
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;
@WebServlet("/mvc2")
public class Nana extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int num = 0;
String result = ""; // model(index.jsp의 result와 같음)
String num_ = request.getParameter("num");
if (num_ != null && !num_.equals("")) {
num = Integer.parseInt(num_);
}
result = num%2 == 0 ? "짝수" : "홀수";
// <서버상 저장소>
// page context : 페이지 내에서 혼자 사용할 수 있는 저장소.
// request : 둘 사이의 포워드 관계에서 사용할 수 있는 저장소.
// session : 현재 세션에서 공유되는 저장소.
// page : 모든 세션, 모든 페이지에서 공유되는 저장소.
// <클라이언트상 저장소>
// cookie : 클라이언트에 저장하는 저장소.
request.setAttribute("result", result); // request에 담은 값을 index.jsp에서 꺼내쓸 수 있음.
// index.jsp와 이 클래스 둘 사이를 연결해줄 수 있는 도구가 필요.
// result를 index.jsp로 전달하기 위해, 둘 사이의 연결고리가 되는 저장소가 필요.
// 이 클래스에서 index.jsp로 전이하기 위해 포워딩이라는 저장소를 사용.
// 1. 포워딩(foward) : 현재 작업한 내용을 이어갈 수 있도록 무언가를 공유.
// 2. 리다이렉트(redirect) : 현재 작업과 상관없는 전혀 새로운 요청을 할 때 사용.
RequestDispatcher dispatcher = request.getRequestDispatcher("index.jsp"); // 같은 디렉토리에 있기 때문에 이렇게 표기
dispatcher.forward(request, response); // req, res를 index.jsp에 공유할 수 있음.
}
}

MVC1 → MVC2 비교 키포인트
- 코드가 완전히 물리적으로 분리
- Controller는 서블릿(java), View는 jsp(view)
47. EL(Expression Language)

- Controller에 쓴 코드를 view에서 써야 하는데, view에서는 가급적 자바 코드를 쓰는 것을 권장하지 않음.

- 그래서 JSP에 EL이라는 방법이 추가되어, 쉽게 값을 꺼낼 수 있음.
list 값을 el로 쉽게 꺼낸 예
48. EL의 데이터 저장소
- 47강 cnt 키워드는 request 저장소에 담겨 있음

- 서버 상에서 사용할 수 있는 4가지 저장소 객체
4가지 객체를 이용해 데이터를 저장하거나 꺼냄
- jsp 내에서 만들어진 객체 중
pageContext: 페이지 내에서 사용가능한 서블릿 객체들(request, response, session, application)을 모아놓은 객체. - pageContext는 페이지 단위로 데이터 저장소 역할이 필요하면, request 처럼 저장소 역할도 할 수 있음.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<%
pageContext.setAttribute("aa", "hello");
// 페이지 객체 : 페이지 내에서 저장소로 쓸 수 있는 객체
// 페이지 객체에 담는 내용을 el을 통해 쓸 수 있음
%>
<body>
${aa }
</body>
</html>
<!-- http://localhost:8080/pageContext -->
- 즉, EL을 이용해 뽑아낼 수 있는 객체는
- 페이지 객체
- request 객체
- session 객체
- application 객체임.

- 하지만 4개 종류 저장소에 같은 이름으로 저장된 변수를 불러오게 된다면? ⇒ el은 4개 저장소를 전부 뒤져서, 우선순위가 높은 것부터 가져옴.
- 우선순위
- page
- request
- session
- application
- 우선순위
- 특정 저장소에서 꺼내오고 싶으면, scope 키워드를 붙여 사용
~scope는 객체가 아니라 한정사임
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%
pageContext.setAttribute("aa", "hello");
// 페이지 객체 : 페이지 내에서 저장소로 쓸 수 있는 객체
// 페이지 객체에 담는 내용을 el을 통해 쓸 수 있음
%>
<%
pageContext.setAttribute("result", "hi");
%>
<body>
${aa }<br>
${result } // 자바 단의 result와 이 페이지 상단의 코드블럭 result 변수이름이 동일하기 때문에, page객체가 우선<br>
${requestScope.result } // 한정사를 붙임으로서 request 객체를 찾음
</body>
</html>
- EL은 4대 저장소 외에도 사용할 수 있는 저장소가 있음


1
2
3
4
// 4대 저장소 이외 저장소 <br>
${param.num} // 자바단에 전달하는 num 변수 <br>
${header.accept } // 사용자가 request할때 보내는 헤더 정보 <br>
<!-- http://localhost:8080/pageContext?num=5 -->
- 가져올 수 있는 request 정보들

- pageContext 내 객체를 얻을 때는 getter로 씀.
- 코드 블럭으로 표기할때 : getter 함수 사용.
- EL로 표기할 때 : EL은 속성을 호출하기만 하지, 함수를 호출할 수는 없기 때문에 이런식으로 사용. (getRequest() → request, getMethod() → method)
![Untitled]()
49. EL 연산자
-
종류
![Untitled]()

51. JSP를 이용해서 자바 웹 프로그램 만들기 시작

<%%>: 일반<%=%>: 출력을 위한 코드블럭<%!%>: 멤버 변수, 멤버 함수 정의하기 위한 코드블럭<%@%>: 지시를 하기 위한 코드 블럭
50. 수업용 프로젝트 준비하기
- https://newlecture.com/customer/notice/9
- /notice/list.html → list.jsp
- list.jsp - properties : 인코딩 설정
![Untitled]()
- 실행 후 출력할 때도 한글로 나와야 하기 때문에 설정
1 2 3
<%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="UTF-8"%> // list.jsp에 붙여넣기
-
jdbc 설정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<%@page import="java.sql.ResultSet"%> <%@page import="java.sql.Statement"%> <%@page import="java.sql.DriverManager"%> <%@page import="java.sql.Connection"%> // ctrl + space로 import <% String url = "jdbc:oracle:thin:@localhost:1521/xepdb1"; String sql = "SELECT * FROM NOTICE"; Class.forName("oracle.jdbc.driver.OracleDriver"); Connection con = DriverManager.getConnection(url, "newlec", "skdine"); Statement st = con.createStatement(); ResultSet rs = st.executeQuery(sql); %> <% rs.close(); st.close(); con.close(); %>
1 2 3 4 5 6 7 8 9 10 11 12 13 14
CREATE USER NEWLEC IDENTIFIED BY 1234; GRANT CONNECT, RESOURCE, DBA TO NEWLEC; create table notice ( id number, title varchar(100), write_id varchar(50), content varchar(4000), regdate date, hit number, files varchar(4000) ); INSERT INTO NOTICE VALUES (6, 'TITLE', 'WRITE_ID', 'CONTENT', SYSDATE, 6, 'FILES'); SELECT * FROM NOTICE;
-
오라클 드라이버 lib 폴더에 넣기
![Untitled]()
- 이 예시에선 프로젝트 - build path 에서 order and expert에 ojdbc를 넣으면 안 됨.
- jar 파일은 컴파일 할 때, 실행할 때 필요하기 때문.
- web 프로젝트에선
50_project에서 컴파일이 되는 것이 아니라,50_project에서 만들어진 결과물이 tomcat으로 배포되어 실행되기 때문. → 라이브러리의 build path가 달라짐. - 또한, tomcat을 서버로 옮길 수 있기 때문에 서버 환경에서 jar 파일 위치가 달라질 수 있음.
- web 개발 환경에서는 라이브러리가 배포되어 실행되어야 하기 때문에 같이 가져가야 함. 때문에, 단순히 ‘jar파일이 어디 있다’ 라이브러리 경로를 지정하는 build path 가 아니라, 라이브러리 자체를 같이 배포할 수 있도록
50_project프로젝트 내에 포함시켜야 함.
- 상세 페이지 구현

1
2
3
<td class="title indent text-align-left">
<a href="detail.jsp?id=<%=rs.getInt("ID")%>"><%= rs.getString("TITLE") %></a>
</td>
- detail.html → detail.jsp
1
2
3
4
5
6
7
8
9
10
11
<%
int id = Integer.parseInt(request.getParameter("id"));
String url = "jdbc:oracle:thin:@localhost:1521/xe";
String sql = "SELECT * FROM NOTICE WHERE ID = ?";
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection(url, "newlec", "1234");
PreparedStatement st = con.prepareStatement(sql); // 쿼리 ?를 채울 수 있음, 미리 준비한 sql 변수(쿼리)를 추가
st.setInt(1, id); // 1번째 ?에 id를 넣겠다
ResultSet rs = st.executeQuery(); // 모든 준비 후 실행만 하면 됨
%>
- notice/list.jsp 에서 재실행

54. 자세한 페이지 MVC model1으로 변경하기
- 이전
50_project는 스파게티 코드로 되어있음

- 이렇게 작성되어 있는 코드를 이렇게 바꿀 예정(자바 코드 따로, html 코드 따로)

- html 부분에는 출력할 수 있는 최소한의 변수
code/title/writer/...만 둠

- detail.jsp : 코드블럭 전부 위로 ㄱ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<%@page import="java.sql.ResultSet"%>
<%@page import="java.sql.Statement"%>
<%@page import="java.sql.DriverManager"%>
<%@page import="java.sql.Connection"%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<%
int id = Integer.parseInt(request.getParameter("id"));
String url = "jdbc:oracle:thin:@localhost:1521/xe";
String sql = "SELECT * FROM NOTICE WHERE ID = ?";
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection(url, "newlec", "1234");
PreparedStatement st = con.prepareStatement(sql); // 쿼리 ?를 채울 수 있음, 미리 준비한 sql 변수(쿼리)를 추가
st.setInt(1, id); // 1번째 ?에 id를 넣겠다
ResultSet rs = st.executeQuery(); // 모든 준비 후 실행만 하면 됨
rs.next(); // 호출 시 ResultSet이 사용하는 공간에 레코드 하나가 적재됨
String title = rs.getString("TITLE");
String writer = rs.getString("WRITE_ID");
int hit = rs.getInt("HIT");
Date regdate = rs.getDate("REGDATE");
String files = rs.getString("FILES");
String content = rs.getString("CONTENT");
rs.close();
st.close();
con.close();
%>

55. MVC model2 방식으로 변경하기
🔰 이전 54_project-model1 방식

- 사용되는 모델이 지역적으로 view 단에서 사용(m,v,c가 하나의 함수로 작용)
🔰 55_project-model2 MVC2 방식은 M, V, C를 물리적으로 나눔

- 여기서 model은 지역변수의 형태로서 사용할 수가 없게 됨.
- servlet과 jsp을 이어주고, 값을 넘길 수 있는 공유 방법이 필요하게 됨.
- 물리적으로 나누는 것의 장점 :
- 개별적 유지관리 가증
- servlet, jsp 재사용 가능
- jsp는 요청 - servlet 코드로 만들어지는 과정 - 컴파일 - 로드 과정을 거침. 때문에 C가 jsp단에 있을 때 덩치가 너무 커져서 실행 성능에 영향을 줄 수 있음. → C를 나눠서 일부라도 미리 컴파일 해놓고, 나중에 로드 - 실행할 수 있음.
🔰 Controller에서 Model을 만들어 View 단에 전달하기 위한 상태 저장 방법

- model을 둘 사이 서블릿이 공유할 수 있는 방법 : pageContext 제외, 세가지 - 범위가 상대적으로 좁은
request가 가장 적합.
🔰 코드
- java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.newlecture.web.controller;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/notice/detail")
public class NoticeDetailController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
int id = Integer.parseInt(request.getParameter("id"));
String url = "jdbc:oracle:thin:@localhost:1521/xe";
String sql = "SELECT * FROM NOTICE WHERE ID = ?";
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection(url, "newlec", "1234");
PreparedStatement st = con.prepareStatement(sql); // 쿼리 ?를 채울 수 있음, 미리 준비한 sql 변수(쿼리)를 추가
st.setInt(1, id); // 1번째 ?에 id를 넣겠다
ResultSet rs = st.executeQuery(); // 모든 준비 후 실행만 하면 됨
rs.next(); // 호출 시 ResultSet이 사용하는 공간에 레코드 하나가 적재됨
String title = rs.getString("TITLE");
String writer = rs.getString("WRITE_ID");
int hit = rs.getInt("HIT");
Date regdate = rs.getDate("REGDATE");
String files = rs.getString("FILES");
String content = rs.getString("CONTENT");
rs.close();
st.close();
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
- list.jsp 수정
1
<td class="title indent text-align-left"><a href="detail?id=<%=rs.getInt("ID")%>"><%= rs.getString("TITLE") %></a></td>
56. Model 데이터를 구조화하기
- 출력을 위해 사용된 데이터
- 개념적으로 말할 수 있는 데이터의 집합 : 엔티티(개체), 개념화된 데이터, 사용자형 자료형, 구조적 데이터(엔티티를 계층으로 만들었기 때문)

- 데이터를 낱개로 사용하게 되면 반복이 많아지고 구분이 어려움. → 낱개가 아닌 데이터 속성으로 묶어서 표현.

- id, title, writer등을 개별로 표현하지 않고, notice 객체에 한꺼번에 담아서 사용.

🔰 코드 - 56_project-notice
- Notice 객체 생성
- 코드 - 생성자, getter, setter 추가

- controller 에 추가
1
2
Notice notice = new Notice(title, writer, hit, regdate, files, content);
request.setAttribute("notice", notice);
57. 목록 페이지도 MVC model2로 수정하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%
List<Notice> list = (List<Notice>)request.getAttribute("list");
for (Notice n : list) {
pageContext.setAttribute("n", n); // 꺼내온 list를 page 저장소에 담음
%>
<tr>
<td>${n.id }</td>
<td class="title indent text-align-left"><a href="detail?id=${n.id }">${n.title }</a></td>
<!--el 키워드는 저장소에 n이라는 값이 있어야 하고, 거기서 값을 꺼내오는 것 : 여기서 임시저장소로 적합한 게 page저장소 -->
<td>${n.writer }</td>
<td>${n.regdate }</td>
<td>${n.hit }</td>
</tr>
<%
}
%>
58. view 페이지 은닉하기
🔰 코드 : 58_view
- WEB-INF
- 라이브러리, 설정, 코드파일, 뷰 페이지 등 외부에서 서비스되지 않는 파일들을 저장.
- notice, member, admin, student 폴더를 /WEB-INF/view 폴더로 이동
NoticeListController코드를 수정, 실행하면 정상 작동.
1
2
3
@WebServlet("/notice/list")
......
request.getRequestDispatcher("/WEB-INF/view/notice/list.jsp").forward(request, response);
- 그러나,
list.jsp파일을 실행하면 페이지를 찾을 수 없다는 404 에러가 남. (서버에서 찾을 수 없거나, 제공되지 않음) - 즉, WEB-INF 안의 루트는 외부에서 요청할 수 없는 디렉토리.
- jsp 파일(뷰 페이지)를 WEB-INF 폴더 안에 저장하면 은닉 가능.

- 참고 : https://jg-han.tistory.com/15
59. view에서 흐름 제어를 위한 자바 코드 블록 제거하기(list.jsp 에서 반복문 제거하기)
- 뷰 단에 남아있는 코드블럭 없애기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%
List<Notice> list = (List<Notice>)request.getAttribute("list");
for (Notice n : list) {
pageContext.setAttribute("n", n); // 꺼내온 list를 page 저장소에 담음
%>
<tr>
<td>${n.id }</td>
<td class="title indent text-align-left"><a href="detail?id=${n.id }">${n.title }</a></td>
<!--el 키워드는 저장소에 n이라는 값이 있어야 하고, 거기서 값을 꺼내오는 것 : 여기서 임시저장소로 적합한 게 page저장소 -->
<td>${n.writer }</td>
<td>${n.regdate }</td>
<td>${n.hit }</td>
</tr>
<%
}
%>
- 자바 코드를 대신할 수 있는 무언가가 필요하게 됨 :
태그


- 태그를 사용하기 위해 JSTL jar 파일 다운로드 : https://mvnrepository.com/artifact/javax.servlet/jstl/1.2
- /WEB-INF/lib 폴더에 jar 파일 옮기기
-
list.jsp에 해당 설정 추가
1
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 접두사 c를 찍어보면 사용 가능한 태그가 보임.
-
jstl 태그로 반복문 만들기
1 2 3 4 5 6 7 8 9 10 11
<!-- n이라는 키워드로 list안의 값을 사용할 수 있게 됨 --> <!-- 저장소에서 값을 꺼내는 것은 el이, 그 값을 이용할 수 있게 page 저장소에 담아주는 작업을 var="n" --> <c:forEach var="n" items="${list}"> <tr> <td>${n.id }</td> <td class="title indent text-align-left"><a href="detail?id=${n.id }">${n.title }</a></td> <td>${n.writer }</td> <td>${n.regdate }</td> <td>${n.hit }</td> </tr> </c:forEach>
- 위 코드는 아래 코드와 같음
1 2 3 4 5
<% List<Notice> list = (List<Notice>)request.getAttribute("list"); for (Notice n : list) { pageContext.setAttribute("n", n); // 꺼내온 list를 page 저장소에 담음 %>
60. Tag 라이브러리와 JSTL
- JSTL이 제공하는 다섯가지 태그 라이브러리

- CORE : 가장 기본적인 제어의 행위 담당하는 태그 라이브러리
- Formating : 숫자, 날짜, 화폐 등 포맷 기능하는 태그 라이브러리
- Functions : el를 이용해 데이터를 추출할 때, 그 결과를 문자열 조작할 수 있는 함수를 묶어놓은 태그 라이브러리
- SQL, XML : 사용하지 않는 추세(뷰 단에 코드를 쓰는 mvc 방식)
🔰 태그 라이브러리 심화

- 접두사를 사용하지 않았을 때 → jsp에서 이해할 수 없음. 반드시 접두사를 작성해야 함.

- for태그를 만든다고 할 때, 이를 식별하기 위해 특정 uri를 붙여 줌.

- 때문에, uri는 forEach라는 태그명에 대한 식별자고, 식별자를 대신하는 prefix를 c로 정의.

- 각각의 행위에서 발생하는 이벤트에 대한 태그를 정의할 수 있음.


61. 중간 정리
- 서블릿 : 자바 웹 서버 프로그램
- MVC : 코드블록을 한 쪽에 몰아넣음

62. forEach의 속성 사용하기

- varStatus

1
2
3
4
5
6
7
8
9
10
<!-- forEach에 대한 상태값 설정 : varStatus="(상태값변수)" -->
<c:forEach var="n" items="${list}" begin="1" end="3" varStatus="st">
<tr>
<td>${st.index} / ${n.id }</td>
<td class="title indent text-align-left"><a href="detail?id=${n.id }">${n.title }</a></td>
<td>${n.writer }</td>
<td>${n.regdate }</td>
<td>${n.hit }</td>
</tr>
</c:forEach>
- 결과 : begin=”1” end=”3”이기 때문에 1부터 나옴.

- begin, end를 지우면 다음과 같이 나옴
1
2
3
4
5
6
7
8
9
10
<!-- forEach에 대한 상태값 설정 : varStatus="(상태값변수)" -->
<c:forEach var="n" items="${list}" varStatus="st">
<tr>
<td>${st.index} / ${n.id }</td>
<td class="title indent text-align-left"><a href="detail?id=${n.id }">${n.title }</a></td>
<td>${n.writer }</td>
<td>${n.regdate }</td>
<td>${n.hit }</td>
</tr>
</c:forEach>

63. JSTL로 pager 번호 만들기
- 5번씩 반복한다고 할 때, p에 대한 배열은 다음과 같음

- 5번 반복한다고 할 때, 첫번째 숫자를 기준으로 현제 페이지번호(p)를 구하도록 함

- p의 나머지(p%5)를 구해 계산 → 19 % 5 - 1 = 4 - 1 = 3 (첫번째 번호 16과 19 사이의 거리)

🔰 63_pager 코드
1
2
3
4
5
6
7
8
9
<!-- 임시변수 생성 -->
<c:set var="page" value="${(param.p == null) ? 1 : param.p }" /> <!-- el을 통해 param 변수를 사용할 수 있음 -->
<c:set var="startNum" value="${page-(page-1)%5 }" /> <!-- 여기서 page변수는 위에서 만든 것 -->
<ul class="-list- center">
<c:forEach var="i" begin="0" end="4">
<!-- <li><a class="-text- orange bold" href="?p=${i+1 }&t=&q=" >${i+1 }</a></li> -->
<li><a class="-text- orange bold" href="?p=${startNum+i }&t=&q=" >${startNum+i }</a></li>
</c:forEach>
-
http://localhost:8888/notice/list?p=6
![Untitled]()
-
http://localhost:8888/notice/list?p=28
![Untitled]()
64. JSTL: if문으로 pager 이전/다음 번호 만들기
-
다음 번호
1 2 3 4 5 6 7 8 9 10 11
<!-- 임시변수 생성 --> <c:set var="page" value="${(param.p == null) ? 1 : param.p }" /> <!-- el을 통해 param 변수를 사용할 수 있음 --> <c:set var="startNum" value="${page-(page-1)%5 }" /> <!-- 여기서 page변수는 위에서 만든 것 --> <c:set var="lastNum" value="23" /> <!-- 마지막 페이지 번호를 인위적으로 만듬 --> <c:if test="${(startNum+5) < lastNum }"> <a href="?p=${startNum+5 }&t=&q=" class="btn btn-next">다음</a> </c:if> <c:if test="${(startNum+5) >= lastNum }"> <span class="btn btn-next" onclick="alert('다음 페이지가 없습니다.');">다음</span> </c:if>
![Untitled]()
-
이전 번호
1 2 3 4 5 6 7 8 9 10 11 12 13
<!-- 임시변수 생성 --> <c:set var="page" value="${(param.p == null) ? 1 : param.p }" /> <!-- el을 통해 param 변수를 사용할 수 있음 --> <c:set var="startNum" value="${page-(page-1)%5 }" /> <!-- 여기서 page변수는 위에서 만든 것 --> <c:set var="lastNum" value="23" /> <!-- 마지막 페이지 번호를 인위적으로 만듬 --> <div> <c:if test="${startNum-1 > 0 }"> <a href="?p=${startNum-1 }&t=&q=" span class="btn btn-prev">이전</span> </c:if> <c:if test="${startNum-1 <= 0 }"> <span class="btn btn-prev" onclick="alert('이전 페이지가 없습니다.');">이전</span> </c:if> </div>
![Untitled]()
65. forTokens로 첨부파일 목록 출력하기
- 컬럼 값

- forTokens 사용
1
2
3
4
5
6
<th>첨부파일</th>
<td colspan="3">
<c:forTokens var="fileName" items="${notice.files }" delims=",">
<li><a href="${fileName }">${fileName }</a></li>
</c:forTokens>
</td>

-
구분자 조건걸기
1 2 3 4 5 6 7 8 9
<tr> <th>첨부파일</th> <td colspan="3"> <c:forTokens var="fileName" items="${notice.files }" delims="," varStatus="st"> <a href="${fileName }">${fileName }</a> <c:if test="${!st.last }">/</c:if> <!-- 마지막 인자는 / 빼기 --> </c:forTokens> </td> </tr>
![Untitled]()
66. format태그로 날짜 형식 변경하기
- 작성일 날짜는 jsp에서 임의로 출력된 형식임. (db에 적재된 값의 형태와 다름)


-
코드
1 2 3 4
<!-- 날짜 관련 태그 추가 --> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <td><fmt:formatDate pattern="YYYY-MM-DD hh:mm:ss" value="${n.regdate }"></fmt:formatDate></td>
![Untitled]()
![Untitled]()
65. 숫자 출력 형식 지정하기 JSTL:formatNumber
-
코드
1 2 3 4
<!-- 기본값 --> <fmt:formatNumber value="${ notice.hit }" /><br/> <!-- 패턴 --> <fmt:formatNumber type="number" pattern="##,####$" value="${ notice.hit }" />
![캡처.PNG]()
68. EL에서 함수 이용하기 JSTL:functions
-
출력만 대문자로 하고 싶을 때
1 2 3 4 5 6 7 8
<!-- function 태그 추가 : el안쪽에서 함수를 호출하는 패키지 형태로 사용 --> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <c:forTokens var="fileName" items="${notice.files }" delims="," varStatus="st"> <!-- 대문자로 출력 --> <a href="${fileName }">${fn:toUpperCase(fileName) }</a> <c:if test="${!st.last }">/</c:if> </c:forTokens>
![Untitled]()
-
style 설정
1 2 3 4 5 6 7 8 9 10
<c:forTokens var="fileName" items="${notice.files }" delims="," varStatus="st"> <!-- zip인 경우만 style 설정 --> <c:set var="style" value=""/> <c:if test="${fn:endsWith(fileName, '.zip') }"> <c:set var="style" value="font-weight:bold; color:red;"/> </c:if> <!-- style 적용 --> <a href="${fileName }" style="${style}">${fileName }</a> <c:if test="${!st.last }">/</c:if> </c:forTokens>
![Untitled]()
-
https://docs.oracle.com/javaee/5/jstl/1.1/docs/tlddocs/fn/tld-summary.html
69. 기업형으로 레이어를 나누는 이유와 설명
🔰 코드 분리를 위한 사전 설명

- Servlet
- 사용자의 응답을 처리, 출력에 대한 내용을 .jsp에 위임, 출력 주도(controller)
- DB에 직접 계정이 연결되어 쿼리를 만들고 그 결과를 .jsp에 보냄.
- 사용자 입출력 처리, 중간 업무 처리 담당.
🔰 서비스 종류와 규모가 늘어난다면?

- 기업형에서는 서비스를 분리해서 만듬.
- 업무 서비스 : 사용자가 요청하는 단위; 결재 시스템과 같은 중요한 서비스.
- Servlet은 업무 요청이 오면
업무 서비스에 그것을 전달하고 결과를 받아 .jsp 문서를 만들어줌. - 재사용 가능.
- ex)계좌이체 로직 : a 계좌의 금액은 +, b 계좌의 금액은 -를 할 때, 두 과정을 하나로 묶어 처리해야 함. → 한쪽만 실행되는 일이 없어야 하므로 트랜잭션 필요.

- 데이터 서비스(DAO) 계층
- 업무 서비스에서는 자바만 이용하고 싶을 때.
- 업무 서비스에서 데이터 소스가 달라지는 것을 신경쓰고 싶지 않을 때.
- 데이터 서비스를 책임지고, 데이터 소스를 숨기는 기능을 담당.

데이터 서비스에서는 업무 서비스에 entity(구조화된 데이터)라는 데이터 객체를 자바 형태로 제공.업무 서비스에서는 처리한 결과를 Servlet(Controller)에 Model 형태로 제공.Servlet(Controller)는 업무 서비스에서 받은 Model을 .jsp(View)에 심어 보냄.
💡 정리
Servlet(controller): 입출력 담당업무 서비스: 비지니스 로직DAO: 실제 데이터 조작 레이어
🔰 그 외 형태
- 비지니스 로직(서비스 레이어)를 따로 빼지 않았을 때

- 데이터 서비스(DAO)를 따로 빼지 않고 업무 서비스에서 SQL 처리할 때














