- 프로젝트 개발 : 우리가 만들 프로젝트는 서버측에서 스프링부트를 이용하여 데이터를 RESTful API로 전송하기만 하고 클라이언트측에서 이러한 데이터를 받아서 클라이언트 측에서 전반적인 웹 개발(대부분의 업무 로직)을 진행한다.**
- 이클립스 자동완성 : option + space
1. Servlet, JSP : 230213
1) 웹 서버 프로그래밍
- ‘동적이다’라는 의미 : 클라이언트가 요청하거나 서버를 실행할 때, 문서가 만들어 진다.
- 현재 코드의 문제점 : 데이터를 출력하는 코드와 입력하는 코드가 섞여 있다.
- I/O 작업과 메모리 작업이 매우 다르다. 메모리 작업이 빨라서 먼저 처리하고 나중에 출력해주는 것이 빠르다.
- View : 출력하는 부분, Controller : 사용자가 입력을 하면 수반되는 코드, Model : 사용자가 입력하면 전달하는 데이터.
- ListController2.java, ListView.java 이용하기.
- 데이터를 전달하는 방법(2가지) 차이점? : Redirection, Forwarding
- Redirection :
- response 도구에 포함되어있다. 이거는 출력 도구인데 출력 도구인데 redirect를 해준다.
- 2번째 서블릿을 호출하는 것이 아니라 직접 호출하는 것이 아니다.
- 응답은 하긴 했지만 다른 곳에서 그 요청을 가져다 준다.
- list2는 클라이언트가 요청했고 응답을 했는데 다시 redirect로 그 응답을 해서 listview를 찾아간다. 이러한 방법이 redirection이다.
- 정리** : 현재 작업하는 것과 상관없이 새로운 요청을 하게 된다, 저장소의 데이터에는 없어진다.
- Forwarding :
- 서버에서 서버를 요청해준다! 즉, 내가 요청한 도구이다.
- 기다리는 동안에는 현재 2개의 서블릿이 살아 있고 입력값이 공유된다.
- Dispatcher : ListController2에서 요청을 하면 이어서 너도 연결받아라. 일단, 일을 기다리고 forward를 통해 ListView에서도 req, resp를 공유한다.
- 정리** : 현재 작업하는 내용을 계속 이어 갈 수 있도록 공유한다, 저장소의 데이터도 공유한다.
- 실습코드 :
- ListController2.java
package com.newlecture.web.controller.menu;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.newlecture.web.entity.GList;
import com.newlecture.web.entity.Menu;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// GList 사용!!**
// 서블릿 바꿔주기!!** 경로!!
@WebServlet("/menu/list2")
public class ListController2 extends HttpServlet{
// 인터페이스명인 MenuService는 아무렇게 짓고 service로 불러오면 된다!
// private MenuService service; // 목록에 대한 데이터서비스를 얘한테 부탁한다!!
@Override // doGet 뿐만 아니라 doDelete, doPut, doPost, doHead 등이 있다.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 서블릿은 메인함수 직접 작성하지 않고 입출력 도구가 달라짐!
// 이렇게 제너릭을 이용하면 하면 형변환할 필요가 없다!!(아래쪽 코드에 있다.)**
// 제너릭 클래스의 재영에 Menu가 들어가서 그 제너릭 클래스의 메서드를 이용한다!!
// GList<Menu> menus = new GList<Menu>(); // GList 객체 생성! 콜렉션이다.
// List는 인터페이스라서 new를 할 수 없다.
// 따라서, 자식 객체급의 ArrayList에서 new로 생성된다.(이 개념 추상화에서 했었다.)
List<Menu> menus = new ArrayList<Menu>(); // GList 객체 생성! 콜렉션이다.
// 이것은 내가하는 인코딩 방식이다. 요즘에는 기본으로 UTF-8로 인코딩이 된다.
// resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html; charset=utf-8");
// resp.setContentType("text/txt; charset=utf-8");
// 내가 인코딩을 하는 것이 아니라 브라우저가 이런 방식으로 읽으라고 설정해준다.
PrintWriter out = resp.getWriter();
out.print("hello");
// 인젝션 sql 때문에 수정함.
String query = "";
String sql = String.format("select * from member where nicname like '%s'", "%"+query+"%") ;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:@oracle.newlecture.com:1521/xepdb1";
Connection con = DriverManager.getConnection(url, "NEWLEC", "rland");
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
// Menu[] menus = new Menu[100]; // 배열 하나 쓰기
// List<Menu> list = new ArrayList<>(); // 콜렉션 하나 쓰기 - 데이터를 수집하고 관리해주는 객체!
// 이렇게 말고 제너릭을 쓰자!
// 필터링, 집계, 정렬
while(rs.next()) // 서버의 커서를 한칸 내리고 그 위치의 레코드를 옮겨 오는 것. -> 레코드 하나가 저장되는 공간은?
{
int id = rs.getInt("id");
String name = rs.getString("name");
String nicName = rs.getString("nicname");
Menu menu = new Menu(id, name, 1000, "");
menus.add(menu); // List 같이 객체에 콜렉션을 넣자!
// 오토박싱 : Wrapper 클래스!!
// menus.add(3);
// Object obj = 3; // 콜렉션에 '3'은 직접 대입되지 않는다.
// 박씽과 언박씽을 해주는 것이 오토 박싱/ 오토 언박싱이다.
// 자바에서는 Integer 클래스가 다 있어서 박싱이 다 되어 있어서 Wrapper 클래스라고 부른다.
// 박싱을 해주는 클래스이다. 자바스크립트에서만 기본 자료형이 Wrapper 클래스 였다.
// Object n = new Integer(3);
// 그래서 콜렉션을 Object형태 뿐만 아니라 사용 방식에 따른 범용 콜렉션이 필요하다!
// 그리하여, 제너릭을 이용한 템플릿으로 새로운 객체를 만든다 .
}
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
// resp.sendRedirect("listview"); // redirect 데이터를 넘겨주는 친구?
req.setAttribute("menus", menus); // forwarding이 될때만 값이 전달된다. redirect일 경우는 안 된다.
req
.getRequestDispatcher("listview")
.forward(req, resp);
// 데이터를 마련하는 부분
// ================================
// 데이터를 출력하는 부분
// out.write("<!DOCTYPE html>");
// out.write("<html>");
// out.write("<head>");
// out.write("<meta charset=\"UTF-8\">");
// out.write("<title>Insert title here</title>");
// out.write("</head>");
// out.write("<body>");
// out.write(" <h1>메뉴 목록</h1>");
// out.write(" <table>");
// out.write(" <tr>");
// out.write(" <td>번호</td>");
// out.write(" <td>이름</td>");
// out.write(" <td>가격</td> ");
// out.write(" </tr>");
//
// for(int i = 0; i<menus.size(); i++) {
//
// // 제너릭을 사용하면, 이제는 형변환을 안 해줘도 된다.!!**
// Menu m = menus.get(i); // 데이터를 출력하는 부분!
//
// out.write("<tr>");
// out.write(" <td>"+m.getId()+"</td>");
// out.write(" <td>"+m.getName()+"</td>");
// out.write(" <td>5000</td>");
// out.write("</tr>");
// }
//
// out.write("</table>");
// out.write("</body>");
// out.write("</html>");
}
}
- ListView.java
package com.newlecture.web.controller.menu;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.newlecture.web.entity.GList;
import com.newlecture.web.entity.Menu;
import com.newlecture.web.service.MenuService;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// GList 사용!!**
// 서블릿 바꿔주기!!** 경로!!
@WebServlet("/menu/listview")
public class ListView extends HttpServlet{
// 인터페이스명인 MenuService는 아무렇게 짓고 service로 불러오면 된다!
private MenuService service; // 목록에 대한 데이터서비스를 얘한테 부탁한다!!
@Override // doGet 뿐만 아니라 doDelete, doPut, doPost, doHead 등이 있다.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 이것은 내가하는 인코딩 방식이다. 요즘에는 기본으로 UTF-8로 인코딩이 된다.
// resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html; charset=utf-8");
// resp.setContentType("text/txt; charset=utf-8");
// 내가 인코딩을 하는 것이 아니라 브라우저가 이런 방식으로 읽으라고 설정해준다.
PrintWriter out = resp.getWriter();
// 데이터를 마련하는 부분
List<Menu> menus = (List<Menu>) req.getAttribute("menus");
// ================================
// 데이터를 출력하는 부분
out.write("<!DOCTYPE html>");
out.write("<html>");
out.write("<head>");
out.write("<meta charset=\"UTF-8\">");
out.write("<title>Insert title here</title>");
out.write("</head>");
out.write("<body>");
out.write(" <h1>메뉴 목록</h1>");
out.write(" <table>");
out.write(" <tr>");
out.write(" <td>번호</td>");
out.write(" <td>이름</td>");
out.write(" <td>가격</td> ");
out.write(" </tr>");
// 조작하는 부분을 다른 것으로 올리기!!
for(int i = 0; i<menus.size(); i++) {
// 제너릭을 사용하면, 이제는 형변환을 안 해줘도 된다.!!**
Menu m = menus.get(i); // 데이터를 출력하는 부분!
out.write("<tr>");
out.write(" <td>"+m.getId()+"</td>");
out.write(" <td>"+m.getName()+"</td>");
out.write(" <td>5000</td>");
out.write("</tr>");
}
out.write("</table>");
out.write("</body>");
out.write("</html>");
// // 서비스에게 맡겨도 된다!
// Menu[] list = service.getList();
// int count = service.count();
// Class.forName("oracle.jdbc.driver.OracleDriver"); // 스태틱 생성자와 같이 바로 불러낼 수 있다.
//
// // https://service/a/b/c와 같이 http라는 프로토콜에 service 객체에 a,b,c 순으로 파일이 이동된다.
// // thin 도 약속되어있는 명칭이다.
// String url = "jdbc:oracle:thin:@oracle.newlecture.com:1521/xepdb1"; // xepdb1는 SEED로부터 복제된 샘플(플러그된 것에서 복제 )이다.
// // 조작을 아무렇게 해도 된다!
//
// Connection con = DriverManager.getConnection(url, "NEWLEC", "rland");
//
// Statement st = con.createStatement();
// ResultSet rs = st.executeQuery("select * from member"); // select * from member where id > 100; 로 조건걸어주기!
//
// out.print("hello");
}
}
2) 4대 저장소(암기) :
- Page 저장소 : PageContext
- Request 저장소 : HttpServletRequest
- Session 저장소 : HttpSession, 한 사용자가 같으면 같은 사용자는 같은 공간을 이용한다.
-
Application 저장소 : ServletContext, 모든 유저가 이용할 수 있는 공간.
- 정리 : 2개 사이의 서블릿에서 데이터를 공유하고 싶을 때, 위의 4가지 저장소를 사용한다. 또한, 이럴 때 forward를 이용한다.
2. JSP : 230214
1) Jasper
-
확장자에서 jsp파일을 만들어서 jsp라는 요청을 주면 Jasper라는 알바가 서블릿 코드를 만들어 준다.
-
톰캣의 프로젝트명은 Catalina이고 제품명이 Tomcat이다.
-
Jasper를 동작시키기 위해서는 jsp파일에서 서버를 켜야 Jasper가 일을 할 수 있다.
-
jsp가 만들어준 java 파일은 고쳐주면 안 된다. 우리는 jsp 파일만 수정할 수 있다.
2) JSP
- 문자열을쓰기위한 out. write, 숫자나 문자를 출력하기 위한 out.print이다.
3) JSP 코드 블럭 종류
- 일반적인 코드 블럭 :
<% %>
- 변수를 넣어 줄 수 있는 코드 블럭(out.print(i)를 대신 해준다.) :
<%= i %>
- 지역변수 말고 멤버로 넣을 수 있는(함수를 넣을 수 있는) 코드 블럭 :
<%! %>
- private int x=3;
- 지시자 코드 블럭 (JSP 인코딩 방식을 위한 방법) :
- response.setContentType이 안 된다. out 코드를 불러 오기 전에 써야하기 때문에 의미가 없다. 그래서 가장 처음에 실행해주기 위해서 처음에 지시해주기 위해서 앞에 써주자.
<%@ %>
-
Jasper의 내장된 객체가 있다. pageContext, request, session, application, page가 있고 이것은 예약어라서 이것과 같은 변수를 만들 수 없다.
-
this를 의미해주는 page가 있다.
-
@import
: 새로 추가된 List나 Menu 객체들을 JSP 파일에 추가해주는 코드블럭.
4) view 파일을 숨기는 방법
-
view 부분은 클라이언트로부터 호출되어서는 안된다. 그래서, 클라이언트에게 WEB-INF 리소스를 배포해서 안되고 WEB-INF 리소스는 클라이언트에게 숨기는 파일들이다.
-
중요 : WEB-INF 리소스는 서버 상에서만 요청할 수 있다.
-
/WEB-INF/view/menu/list.jsp
: 지금은 상대경로보다는 절대경로가 편하다.
- 실습 코드 :
- ListController2.java
package com.newlecture.web.controller.menu;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.newlecture.web.entity.GList;
import com.newlecture.web.entity.Menu;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// jsp파일을 변경해주면, 서블릿 경로도 바꿔줘야 한다.
// 실행도 컨트롤러에서 실행을 시켜주어야 한다.
@WebServlet("/menu/list2")
public class ListController2 extends HttpServlet{
// 인터페이스명인 MenuService는 아무렇게 짓고 service로 불러오면 된다!
// private MenuService service; // 목록에 대한 데이터서비스를 얘한테 부탁한다!!
@Override // doGet 뿐만 아니라 doDelete, doPut, doPost, doHead 등이 있다.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// List는 인터페이스라서 new를 할 수 없다.
// 따라서, 자식 객체급의 ArrayList에서 new로 생성된다.(이 개념 추상화에서 했었다.)
List<Menu> menus = new ArrayList<Menu>(); // GList 객체 생성! 콜렉션이다.
resp.setContentType("text/html; charset=utf-8");
PrintWriter out = resp.getWriter();
out.print("hello");
// 인젝션 sql 때문에 수정함.
String query = "";
String sql = String.format("select * from member where nicname like '%s'", "%"+query+"%") ;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:@oracle.newlecture.com:1521/xepdb1";
Connection con = DriverManager.getConnection(url, "NEWLEC", "rland");
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
while(rs.next()) // 서버의 커서를 한칸 내리고 그 위치의 레코드를 옮겨 오는 것. -> 레코드 하나가 저장되는 공간은?
{
int id = rs.getInt("id");
String name = rs.getString("name");
String nicName = rs.getString("nicname");
Menu menu = new Menu(id, name, 1000, "");
menus.add(menu); // List 같이 객체에 콜렉션을 넣자!
}
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
// resp.sendRedirect("listview"); // redirect 데이터를 넘겨주는 친구?
req.setAttribute("menus", menus); // forwarding이 될때만 값이 전달된다. redirect일 경우는 안 된다.
// url도 jsp파일과 같게 설정해주어야 한다. 실제 디렉토리 경로가 아니라 url 경로이다.
req
.getRequestDispatcher("/WEB-INF/view/menu/list.jsp") // 지금은 상대경로보다는 절대경로가 편하다.
.forward(req, resp);
// 데이터를 마련하는 부분
// ================================
// 데이터를 출력하는 부분
// out.write("<!DOCTYPE html>");
// out.write("<html>");
// out.write("<head>");
// out.write("<meta charset=\"UTF-8\">");
// out.write("<title>Insert title here</title>");
// out.write("</head>");
// out.write("<body>");
// out.write(" <h1>메뉴 목록</h1>");
// out.write(" <table>");
// out.write(" <tr>");
// out.write(" <td>번호</td>");
// out.write(" <td>이름</td>");
// out.write(" <td>가격</td> ");
// out.write(" </tr>");
//
// for(int i = 0; i<menus.size(); i++) {
//
// // 제너릭을 사용하면, 이제는 형변환을 안 해줘도 된다.!!**
// Menu m = menus.get(i); // 데이터를 출력하는 부분!
//
// out.write("<tr>");
// out.write(" <td>"+m.getId()+"</td>");
// out.write(" <td>"+m.getName()+"</td>");
// out.write(" <td>5000</td>");
// out.write("</tr>");
// }
//
// out.write("</table>");
// out.write("</body>");
// out.write("</html>");
}
}
- list.jsp
<%@page import="com.newlecture.web.entity.Menu"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- JSTL 사용하기! uri가 없으면 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!-- 위에는 지시자 코드블럭-->
<%
List<Menu> menus = (List) request.getAttribute("menus");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>메뉴 목록</h1>
<table>
<tr>
<td>번호</td>
<td>이름</td>
<td>가격</td>
</tr>
<!-- 문자열을쓰기위한 out. write, 숫자나 문자를 출력하기 위한 out.print이다. -->
<c:forEach begin="1" end="5" step="1">
<tr>
<td>1</td>
<td>아이스 아메리카노</td>
<td>5000원</td>
<!-- 값이라서 세미콜론이 없다. -->
<%-- <td><%= m.getId() %></td>
<td><%= m.getName() %></td>
<td>5000</td> --%>
</tr>
</c:forEach>
</table>
</body>
</html>
5) JSTL(JSP Standard Tag Library), EL 태그
a. JSTL
- jsp에서 java 코드를 쓰지 않게 하기
- Tag Handler API(많이 안 쓴다.) : Tag Library를 직접 만들 수도 있다.
- WEB-INF 폴더에 ~.tld라는 파일이 존재한다.
- JSTL : 보편적으로 쓰는 태그 라이브러리
- JSTL 종류 : core, formating, functions를 쓴다.
- core : 제어문 관련 태그(if문, for문 등)
- formating : 숫자에 대한 함수와 날짜 등
- function : substring, 소문자를 대문자로 바꿔준다.
- core 태그 :
- 반복문 : forEach, forTokens
- 조건문 : if
- 선택하는 구문 : choose, when이고 추가로 url
<c:forEach begin="1" end="5" step="1">
<tr>
<td>1</td>
<td>아이스 아메리카노</td>
<td>5000원</td>
<!-- 값이라서 세미콜론이 없다. -->
<%-- <td><%= m.getId() %></td>
<td><%= m.getName() %></td>
<td>5000</td> --%>
</tr>
</c:forEach>
3. JSP, EL : 230215
1) EL 태그 : core 이용
- request가 교도보 역할을 해주고 이것은 원래
request.getAttrubute("title")
로 값을 출력해야 한다. 하지만 EL 태그를 쓰면 더 간단히 표현할 수 있다.${menus}
로 쓸 수 있다.
- forEach 구문에 의해서 담아놓은
${menu}
를 꺼내서 쓸 때는pageContext.getAttribute
로 꺼내서 쓴다.pageContext.getAttribute
- pageContext.getAttribute은 객체라서 형변환((Menu))을 해주고 표현식은 꺼내 쓸 수 있게 getName()을 get도 지우고 ()도 지우고 name으로 바꿔서 사용한다.
((Menu) pageContext.getAttribute("m")).getName()
- 따라서, EL 태그를 이용해서 간단히
${m.name}
으로 사용한다. - 원래는 getter이지만, 속성을 이용하는 것과 같다.
- EL은 4대 저장소에 들어가서 저장하기 때문에 저장소에 있는 것을 꺼내 사용한다.
List<Menu> menus = (List)request.getAttribute("menus"); %>
이제 이 지역 변수 부분은 없애도 된다. request라는 저장소에 들어 가게 된다.
2) EL 태그 : fmt 이용
- pattern, value 이용
<fmt:formatDate pattern="yyyy-MM-dd HH:mm:ss" value="${m.regDate}" />
- 원래는, List(menus)에서 객체(var)를 뽑아낸 것이다.
- fmt 태그에서 var에 다시 넣어주면, pageContext에 들어가는 것과 같다. 그것을 다시 더 간단히 꺼내서 사용할 수 있다.
<c:forEach var="m" items="${menus}">
<fmt:formatNumber var="price" pattern="#,###" value="${m.price}"/>
<fmt:formatDate var="regDate" pattern="yyyy-MM-dd HH:mm:ss" value="${m.regDate}" />
<tr>
<td>${m.id}</td>
<td>${m.name}</td>
<td>${price}원</td>
<td>${regDate}</td>
</tr>
</c:forEach>
- forTokens 태그 : 각각의 파일들을 개별적으로 스타일링하는 방법
- 스타일링이라서 원래는 view단에서 해주는 것이 맞다.
- 구분자를 가지고 문자열을 쪼개주고 tokens 수만큼 나누어 하나의 요소로 구분시켜 줄 수 있다.
- 구분자도 바꿔줄 수 있다.
- 실습 :
- entity에 images 추가해주기
<td colspan="4">
<c:forTokens var="img" items="${m.images}" delims=",">
${img}
</c:forTokens>
</td>
3) EL 태그를 이용한 반복문의 조건 처리
- varStatus :
- 반복문의 조건 처리를 위해서 if문을 쓰고 꼭 상태값(varStatus)이 필요하다.
- st.current : 현재 토큰이 출력.
- st.begin, st.end : 특정 범위의 index를 출력해준다.
- 반복문의 조건 처리를 위해서 if문을 쓰고 꼭 상태값(varStatus)이 필요하다.
- EL 표현식의 연산자 : ${} 안에서 연산자를 적어주는 것이 적용된다.
- empty 연산자 :
st.last != null and st.last != ""
2개의 연산을 한번에 처리해준다.(문자열 검사에서 사용)empty st.last
- empty 연산자 :
- not 연산자 :
!st.last
ornot st.last
<tr>
<!-- forTokens 이용! -->
<td colspan="4">
<c:forTokens var="img" items="${m.images}" delims="," varStatus="st">
<!-- a태그로 감싸줘서 페이지 이동도 가능하다. -->
<a href="upload/${img}">${img}</a>
<!-- 구분자의 가장 마지막 구분자는 없애 줘야 한다. -->
<c:if test="${!st.last}"> | </c:if>
</c:forTokens>
<br>
</td>
</tr>
- 하지만, if-else 구문에서 else 구문은 없어서 if 문으로만 사용해야 한다.
- when, when-otherwise 구문도 있다.()
4) EL 태그의 변수 집중화 :
- Context path를 바꾸면 톰캣의 서버도 지워주고 다시 실행해야 한다.
- core의 set 태그
// 경로에서 쓰인다. var는 컨텍스트페이지에 담긴다.
<c:set var="path" value="/webprj" />
- core의 url 태그
<%-- 경로에서 보통 쓰이는 방법 : -->
<c:url var="path" value="/upload"/>
<%-- context가 있으면, url 태그를 이용해서 컨텍스트명을 알아서 붙여준다. -->
<a href="${path}/${img}">${img}</a>
- core의 fn 태그
- 문자열 쪼개기 정도로만 쓰인다.
- 서버의 주석 처리 :
<%-- aa -->
- 클라이언트의 주석 처리 :
<!-- aa -->
- 전체 실습 코드 :
- list.jsp
<%@page import="com.newlecture.web.entity.Menu"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- JSTL 사용하기! uri가 없으면 가져오기-->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!-- EL은 4대 저장소에 들어가서 저장하기 때문에 저장소에 있는 것을 꺼내 사용한다. -->
<%-- <%
List<Menu> menus = (List) request.getAttribute("menus");
%>
--%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>메뉴 목록</h1>
<!-- html 테이블 속성 추가 -->
<table border="1">
<tr>
<td>번호</td>
<td>이름</td>
<td>가격</td>
<td>날짜</td>
</tr>
<!-- 문자열을쓰기위한 out. write, 숫자나 문자를 출력하기 위한 out.print이다. -->
<%-- <c:forEach begin="1" end="5" step="1"> --%>
<c:forEach var="m" items="${menus}">
<fmt:formatNumber var="price" pattern="#,###" value="${m.price}"/>
<fmt:formatDate var="regDate" pattern="yyyy-MM-dd HH:mm:ss" value="${m.regDate}" />
<tr>
<td>${m.id}</td>
<td>${m.name}</td>
<td>${price}원</td>
<td>${regDate}</td>
</tr>
<tr>
<!-- forTokens 이용! -->
<td colspan="4">
<c:forTokens var="img" items="${m.images}" delims="," varStatus="st">
<!-- a태그로 감싸줘서 페이지 이동도 가능하다. -->
<a href="upload/${img}">${img}</a>
<!-- 구분자의 가장 마지막 구분자는 없애 줘야 한다. -->
<c:if test="${!st.last}"> | </c:if>
</c:forTokens>
<br>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>
- ListController2.java
package com.newlecture.web.controller.menu;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.newlecture.web.entity.GList;
import com.newlecture.web.entity.Menu;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// jsp파일을 변경해주면, 서블릿 경로도 바꿔줘야 한다.
// 실행도 컨트롤러에서 실행을 시켜주어야 한다.
@WebServlet("/menu/list2")
public class ListController2 extends HttpServlet{
// 인터페이스명인 MenuService는 아무렇게 짓고 service로 불러오면 된다!
// private MenuService service; // 목록에 대한 데이터서비스를 얘한테 부탁한다!!
@Override // doGet 뿐만 아니라 doDelete, doPut, doPost, doHead 등이 있다.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// List는 인터페이스라서 new를 할 수 없다.
// 따라서, 자식 객체급의 ArrayList에서 new로 생성된다.(이 개념 추상화에서 했었다.)
List<Menu> menus = new ArrayList<Menu>(); // GList 객체 생성! 콜렉션이다.
resp.setContentType("text/html; charset=utf-8");
PrintWriter out = resp.getWriter();
out.print("hello");
// 인젝션 sql 때문에 수정함.
String query = "";
String sql = String.format("select * from member where nicname like '%s'", "%"+query+"%") ;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:@oracle.newlecture.com:1521/xepdb1";
Connection con = DriverManager.getConnection(url, "NEWLEC", "rland");
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
while(rs.next()) // 서버의 커서를 한칸 내리고 그 위치의 레코드를 옮겨 오는 것. -> 레코드 하나가 저장되는 공간은?
{
int id = rs.getInt("id");
String name = rs.getString("name");
String nicName = rs.getString("nicname");
Date regDate = rs.getDate("reg_date");
String images = "pic1.png, pic2.png, pic3.png"; // DB에 칼럼이 없어서 직접 입력하여 테스트하기
Menu menu = new Menu(id, name, 1000, "", regDate);
menu.setImages(images); // 직접 입력해주므로 setter로 List에 값을 넣어준다.
menus.add(menu); // List 같이 객체에 콜렉션을 넣자!
}
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
// resp.sendRedirect("listview"); // redirect 데이터를 넘겨주는 친구?
req.setAttribute("menus", menus); // forwarding이 될때만 값이 전달된다. redirect일 경우는 안 된다.
// url도 jsp파일과 같게 설정해주어야 한다. 실제 디렉토리 경로가 아니라 url 경로이다.
req
.getRequestDispatcher("/WEB-INF/view/menu/list.jsp") // 지금은 상대경로보다는 절대경로가 편하다.
.forward(req, resp);
// 데이터를 마련하는 부분
// ================================
// 데이터를 출력하는 부분
// out.write("<!DOCTYPE html>");
// out.write("<html>");
// out.write("<head>");
// out.write("<meta charset=\"UTF-8\">");
// out.write("<title>Insert title here</title>");
// out.write("</head>");
// out.write("<body>");
// out.write(" <h1>메뉴 목록</h1>");
// out.write(" <table>");
// out.write(" <tr>");
// out.write(" <td>번호</td>");
// out.write(" <td>이름</td>");
// out.write(" <td>가격</td> ");
// out.write(" </tr>");
//
// for(int i = 0; i<menus.size(); i++) {
//
// // 제너릭을 사용하면, 이제는 형변환을 안 해줘도 된다.!!**
// Menu m = menus.get(i); // 데이터를 출력하는 부분!
//
// out.write("<tr>");
// out.write(" <td>"+m.getId()+"</td>");
// out.write(" <td>"+m.getName()+"</td>");
// out.write(" <td>5000</td>");
// out.write("</tr>");
// }
//
// out.write("</table>");
// out.write("</body>");
// out.write("</html>");
}
}
- Menu.java
package com.newlecture.web.entity;
import java.util.Date;
public class Menu {
private int id;
private String name;
private int price;
private String img;
private Date regDate; // Date는 java.util로 임포트 해주기
private String images;
// 생성자(기본, 오버로드)
// Getters/ Setters
// toString();
// 스태틱 생성자!!는 무조건 실행된다.
// static { //이것이 ojdbc를 가져온다.
// System.out.println("아무것도... Menu전역 초기화 ");
// Menu.setName
//
// }
public Menu() {
}
public Menu(int id, String name, int price, String img, Date regDate) {
this.id = id;
this.name = name;
this.price = price;
this.img = img;
this.regDate = regDate;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
public String getImages() {
return images;
}
public void setImages(String images) {
this.images = images;
}
@Override
public String toString() {
return "Menu [id=" + id + ", name=" + name + ", price=" + price + ", img=" + img + ", regDate=" + regDate
+ ", images=" + images + "]";
}
}
4. Spring 시작 : 230216
- 레이어 나누기 :
업무를 잘하는 사람?
은 DB와의 여러 문제까지 같이 충돌되는 현상을 처리하는 것까지 다 해결하는 사람이 업무를 잘하는 사람이다.
1) JAVA로 레이어 나누기
- 실무에서 DB를 다루려면, 업무를 잘 알아야 한다.(ex) 여러 포인트 결제 시스템 - 취소했을 때, 발생하는 경우)
- 인터페이스는 사용하는 쪽에서 정의한다.
- 각 계층의 레이어들을 연결해줄 때, 인터페이스 구현체에 연결하는 것이 아니라 인터페이스에 연결해주어야 한다.
2) SOLID 원칙
- 객체지향 디자인 원칙 : SRP, OCP, LSP, ISP, DIP 법칙
- 예시 :
- SRP : 책임을 하나만 사용해야 한다. 역할 분배를 잘해라
- OCP : 객체가 수정에는 닫혀 있고 확장에는 열려 있어야 한다.
- 모듈을 서로 배분하고 사용하는 것 npm 사이트가 있는데 JS로 이용하는 수많은 라이브러리가 있다.
- npm 레포지토리에서 모듈이 유명한데, 모든 모듈을 사용할 수 없었다. 어떤 사람이 역대급으로 함수 이름을 바꿔 버림..
- 그래서, 그 유명한 모듈을 사용할 수 없다.
- 하지만, 확장을 하면 코드는 무거워지지만 전체 버전을 업그레이드 해야 한다.
- LSP : 하위 객체는 치환해도 문제가 없어야 한다.
- ISP: 너무 많은 것을 한번에 약속(인터페이스)을 정하지 마라. 클라이언트가 필요하지 않은 인터페이스는 없애고 만들어라.
- DIP : 종속성 역전 원칙? 사용하는 객체 이름이 ‘A’라고 하면, 우리는 객체를 쓸 때, ‘A’라는 부품 객체 이름을 쓰지 말고 부모의 이름을 사용해서 써라!
- 조금 더 범용화된 참조형을 써야 한다. 왜냐하면, 메서드 추가할 떄, 그 객체를 이용하여 사용한다.
3) Layered Architecture
1) DAO
- 테이블의 데이터를 가져와서 자바스럽게 사용할 수 있게 해주는 변환 객체이다. SQL문을 몰라도 순수하게 자바코드로만 데이터를 다룰 수 있다.
- ex) MemberDao, NoticeDao, MenuDao로 사용한다.
- DAO는 메서드를 보통 find(), insert(), delete(), update()라는 이름을 이용한다.
2) 인터페이스 심화 개념
- 참조하는 것을 자식형인 구현체화된 이름을 써주는 것이 아니라 부모형을 써줘야 한다.
- 결론 : 인터페이스만 일치하면, 바꿔치기 하는데 문제가 없다,
3) 외부파일에서 객체 생성하는 방법
- 그래서 객체 생성을 외부에서 생성해줄 수 도 있다.
- 생성해야할 객체 생성 방법을 외부 파일에 둔다. XML과 Annotation 방식이 있다.
- 객체를 생성하는 일이 내가 코딩하는 부분에 있다면, 그 코드를 수정할 때, 그 소스코드 내부에서 수정해줘야 한다.
- 그래서, 이러한 객체를 생성해주고 연결해주는 코드를 Spring 프레임워크가 대신 해준다.
4) 스프링이 해주는 일
- 객체를 텍스트에 저장해두면, 텍스트 문자열 부분을 원래는 Class.forName으로 직접 만들어주어야하지만 텍스트로서 저장만 해두면 객체 생성을 스프링이 만들어 줄 것이다.(getBean())
4) 자바로 만든 Layered Architecture의 실습 코드
ListController4.java
package com.newlecture.web.controller.menu;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.newlecture.web.entity.GList;
import com.newlecture.web.entity.Menu;
import com.newlecture.web.service.DefaultMenuService;
import com.newlecture.web.service.MenuService;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// !!! 컨트롤러는 사용자 입력과 출력만 만들어 준다.(껍데기 느낌)!!
@WebServlet("/menu/list4")
public class ListController4 extends HttpServlet{
private MenuService service; // 목록에 대한 데이터서비스를 얘한테 부탁한다!!
public ListController4() {
// 컨트롤러 객체와 구현된 서비스 객체를 강한 결합으로 연결
service = new DefaultMenuService();
}
@Override // doGet 뿐만 아니라 doDelete, doPut, doPost, doHead 등이 있다.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
PrintWriter out = resp.getWriter();
List<Menu> menus = service.getList(); // 이젠 service에서 List를 받아온다.
// resp.sendRedirect("listview"); // redirect 데이터를 넘겨주는 친구?
req.setAttribute("menus", menus); // forwarding이 될때만 값이 전달된다. redirect일 경우는 안 된다.
// url도 jsp파일과 같게 설정해주어야 한다. 실제 디렉토리 경로가 아니라 url 경로이다.
req
.getRequestDispatcher("/WEB-INF/view/menu/list.jsp") // 지금은 상대경로보다는 절대경로가 편하다.
.forward(req, resp);
}
}
MenuService.java
package com.newlecture.web.service;
import java.util.List;
import com.newlecture.web.entity.Menu;
// MenuService는 listController가 사용할 부품(인터페이스)이 된다.
public interface MenuService {
List<Menu> getList();
}
DefaultMenuService.java
package com.newlecture.web.service;
import java.util.List;
import com.newlecture.web.entity.Menu;
import com.newlecture.web.repository.MenuDao;
import com.newlecture.web.repository.jdbc.JdbcMenuDao;
// 다양한 업무를 처리하기 위한 상관 관계를 잘 아는 사람이 데이터를 조작하도록 한다.
// 단, 데이터를 조작하기 위한 방법은 몰라도 되도록 하는 것이 어떨지...
// 다음과 같은것들을 모르고도 자바 지식만으로 업무를 처리할 수 있게 하는 것이 좋지 않을까?
// - 어떤 DB를 사용해야하는지..
// - 쿼리를 어떻게 작성해야 하는지...
// - 데이터 소스가 다양한데 그것이 어떤 것들을 사용해야 하는지...
// - 데이터 베이스가 달라지면? SQL과 연결 문자열과 드라이브 등이 달라지는데... 그럼 모든 업무로직 코드를 수정해야 한다.
// **지금은 코드가 간단하지만, 원래는 서비스 로직에서는 2개 이상의 업무를 처리하는 객체가 있어야 한다.
// 중요!!
// 서비스 객체의 메서드는 사용자 요청이 함수화 된다.
// getList(), likeUp() : 사용자가 요구하는 내용이 메서드의 이름이 된다.
//DAO 객체의 메서드는 SQL 명령어를 함수화한다.
// 데이터를 조작하는 객체에 메서드 이름은 서비스와 달라야 하는 것이 맞다.
public class DefaultMenuService implements MenuService {
private MenuDao menuDao;
public DefaultMenuService() {
// 컨트롤러 객체와 구현된 서비스 객체를 강한 결합으로 연결
menuDao = new JdbcMenuDao();
}
@Override
public List<Menu> getList() {
List<Menu> list = menuDao.findAll();
return list;
}
}
MenuDao.java
package com.newlecture.web.repository;
import java.util.List;
import com.newlecture.web.entity.Menu;
public interface MenuDao {
List<Menu> findAll();
}
JdbcMenuDao.java
package com.newlecture.web.repository.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.newlecture.web.entity.Menu;
import com.newlecture.web.repository.MenuDao;
public class JdbcMenuDao implements MenuDao {
@Override
public List<Menu> findAll() {
List<Menu> menus = new ArrayList<>();
String query = "";
String sql = String.format("select * from member where nicname like '%s'", "%"+query+"%") ;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:@oracle.newlecture.com:1521/xepdb1";
Connection con = DriverManager.getConnection(url, "NEWLEC", "rland");
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
while(rs.next()) // 서버의 커서를 한칸 내리고 그 위치의 레코드를 옮겨 오는 것. -> 레코드 하나가 저장되는 공간은?
{
int id = rs.getInt("id");
String name = rs.getString("name");
String nicName = rs.getString("nicname");
Date regDate = rs.getDate("reg_date");
String images = "pic1.png, pic2.png, pic3.png"; // DB에 칼럼이 없어서 직접 입력하여 테스트하기
Menu menu = new Menu(id, name, 1000, "", regDate);
menu.setImages(images); // 직접 입력해주므로 setter로 List에 값을 넣어준다.
menus.add(menu); // List 같이 객체에 콜렉션을 넣자!
}
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return menus;
}
}
Menu.java
package com.newlecture.web.entity;
import java.util.Date;
public class Menu {
private int id;
private String name;
private int price;
private String img;
private Date regDate; // Date는 java.util로 임포트 해주기
private String images;
// 생성자(기본, 오버로드)
// Getters/ Setters
// toString();
// 스태틱 생성자!!는 무조건 실행된다.
// static { //이것이 ojdbc를 가져온다.
// System.out.println("아무것도... Menu전역 초기화 ");
// Menu.setName
//
// }
public Menu() {
}
public Menu(int id, String name, int price, String img, Date regDate) {
this.id = id;
this.name = name;
this.price = price;
this.img = img;
this.regDate = regDate;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
public String getImages() {
return images;
}
public void setImages(String images) {
this.images = images;
}
@Override
public String toString() {
return "Menu [id=" + id + ", name=" + name + ", price=" + price + ", img=" + img + ", regDate=" + regDate
+ ", images=" + images + "]";
}
}
5. Spring 도입부 : 230217
1) 이전 복습 :
- 서비스 객체 :
- 업무 : 사용자가 요구하는 기능
- 컨트롤러 객체 :
- 사용자 요청을 서비스 객체에 전달하고 View단에 사용자에게 보여줄 출력을 담당하는 친구
- DAO 객체 :
- 업무자가 자바코드 말고 안쓰게 하려고
- 결국 자바 객체만 사용한다.
- DAO 코드를 수정할 때는 새로운 확장되어서 바꿔치기 해야 한다.(소스코드를 직접 바꾸면 코드 수정 시, 사용자가 사용하지 못한다.)
- 객체를 생성해주는 라이브러리는 스프링이 해줄 것이다.
- DAO에서 데이터를 가져오는 부분도 라이브러리를 이용할 것이다. Mybatis, JPA을 이용한다.
2) 웹 에러 정리
- 인증이 안되었거나 권한이 없어서 이 요청을 처리할 수 없는 경우 우리는 어떤 상태 코드를 전달해야 할까? 사용자에게 : 403 에러
- 매개변수 인지가 일치하지 않는 경우 : 400에러
- POST 요청에 응답할 수 있는 메소드 처리함수가 없을 경우의 오류 상태 코드는? 405 에러
3) front-controller 개념
- 서블릿(톰캣)처럼 특화된 것에만 종속되어 있다. 이렇게 특화된 방식으로 입력을 처리한다.
- 입력 받는 코드가 너무 반복적이고 불편하다.
-
출력을 위한 Distpatcher forward가 매번 똑같은 코드로 반복된다.
- 해결 방법 :
- 그래서, Front Controller가 모든 응답을 담당한다.
- 요청도 입력하고 문서도 출력한다.
- 요청과 출력 그리고 URL도 얘가 처리한다.
- 기존 컨트롤러는 자바스럽게 전달한다. 매개변수만 존재한다.
4) 사용자 입력 받는 방법
1) 쿼리 스트링을 이용한 입력 :
- 쿼리(질의) : 사용자가 문서를 달라고 하는 것 + 추가적으로 제공하게 되는 옵션 값을 쿼리 스트링이라고 한다.
- 웹이 기본적으로 요청할 때 옵션 값이다.
- 하지만, 이것은 서버가 기본적으로 이러한 옵션을 제공해주어야 사용이 가능하다.
- 예시 : 페이지, 검색어, 레코드 개수, 기간, ….
- 전달된 내용은 무조건 String 형이다. 바로 문자열을 사용할 수 없어서 정수로 쓰기 위해서는 형변환을 해주어야 한다.
response.setContentType("text/html; charset=utf-8");
String page = request.getParameter("p");
String query = request.getParameter("q");
String size = request.getParameter("s");
- 그럼 요청 방법은?
PrintWriter out = response.getWriter();
out.printf("%s %s %s ", page, query, size);
// s는 size이다. 웹의 주소는 100자 내외의 길이 제한했던 부분이 있다.
2) form을 이용한 입력
- 사실 쿼리 스트링은 사용자가 전달하는 값이라기 보다 페이지를 제공하는 쪽에서 정해진 값을 선택하게 하는 방식입니다
- 예를 들어서 쿼리스트링은 사용자가 URI창을 직접 편집하는 것이 아니라 다음처럼 링크에 있는 경우가 일반적입니다.
<a href="/input?p=2&q=hello&s=15“>2</a>
<a href=”/input?p=3&q=hello&s=15“>1</a>
- 위와 같이 이미 정해진 값들 중에 하나의 링크를 선택함으로써 입력을 요청하게 됩니다.
- 반면에 사용자가 값을 직접 입력하게 하고 싶다면 어떻게 할까? 그 때는 form을 제공함으로써 입력을 하게 합니다.
out.write (String.format ("page:%s, query:%s, size:%s<br>", page, query, size));
out.write("<form action=\"/webprj2/input\" method=\"get\">");
out.write(" <label>page:</label>");
out.write(" <input type=\"text\" name=\"p\"><br>");
out.write(" <label>검색어 : </label>") ;
out.write(" <input type=\"text\" name=\"q\"><br>");
out.write(" <label>size:</label>");
out.write(" <input type=\"text\" name=\"s\"><br>");
out.write(" <input type=\"submit\" value=\"제출\">");
out.write("</form>");
request.setAttribute("page", page);
request.setAttribute("query", query);
request.setAttribute("size", size);
3) 우선순위 설정 및 param 이용
-
4대 저장소에서 데이터가 저장된다.
-
page-request-session-application 순서로 EL 태그를 찾는다.
-
model이 있을 때, tag library로 변수가 생성되어 값이 저장되어 있으면, 우선 순위가 밀린다.
-
그때 model에 우선순위를 주기 위해서 Scope를 사용해서 콕 집어 준다. (requestScope)
- 추가로 쿠키나 세션을 객체 생성하지 않고 그냥 사용할 때, 그냥 param에서 꺼내서 사용할 수 있다.**
- 우선순위 : requestScope 이용
- input.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="site" value=""/>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="/webprj2/input?p=2&q=hello&s=15">2</a>
<br>
<a href="/webprj2/input?p=3&q=hello&s=15">3</a>
<br>
<a href="/webprj2/input?p=4&q=hello&s=15">4</a>
<br>
<div>
page : ${page} , query : ${query} , size : ${requestScope.size}
</div>
<form action="/webprj2/input" method="get">
<label>page:</label>
<input type="text" name="p"><br>
<label>검색어 : </label>
<input type="text" name="q"><br>
<label>size:</label>
<input type="text" name="s"><br>
<input type="submit" value="제출">
</form>
</body>
</html>
- inputController.java
package com.newlecture.web;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/input")
public class InputController extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 쿼리 스트링을 이용한 입력
// 쿼리(질의) : 사용자가 문서를 달라고 하는 것 + 추가적으로 제공하게 되는 옵션 값을 쿼리 스트링이라고 한다.
// 웹이 기본적으로 요청할 때 옵션 값이다.
// 하지만, 이것은 서버가 기본적으로 이러한 옵션을 제공해주어야 사용이 가능하다.
// 예시 : 페이지, 검색어, 레코드 개수, 기간, ....
// 전달된 내용은 무조건 String 형이다. 바로 문자열을 사용할 수 없어서 정수로 쓰기 위해서는 형변환을 해주어야 한다.
response.setContentType("text/html; charset=utf-8");
String page = request.getParameter("p");
String query = request.getParameter("q");
String size = request.getParameter("s");
// 그럼 요청 방법은?
PrintWriter out = response.getWriter();
// out.printf("%s %s %s ", page, query, size);
// s는 size이다. 웹의 주소는 100자 내외의 길이 제한했던 부분이 있다.
// 2. form을 이용한 입력
// 사실 쿼리 스트링은 사용자가 전달하는 값이라기 보다 페이지를 제공하는 쪽에서 정해진 값을 선택하게 하는 방식입니다
// 예를 들어서 쿼리스트링은 사용자가 URI창을 직접 편집하는 것이 아니라 다음처럼 링크에 있는 경우가 일반적입니다.
// <a href="/input?p=2&q=hello&s=15“>2</a>
// <a href=”/input?p=3&q=hello&s=15“>1</a>
// 위와 같이 이미 정해진 값들 중에 하나의 링크를 선택함으로써 입력을 요청하게 됩니다 .
// 반면에 사용자가 값을 직접 입력하게 하고 싶다면 어떻게 할까? 그 때는 form을 제공함으로써 입력을 하게 합니다.
out.write (String.format ("page:%s, query:%s, size:%s<br>", page, query, size));
out.write("<form action=\"/webprj2/input\" method=\"get\">");
out.write(" <label>page:</label>");
out.write(" <input type=\"text\" name=\"p\"><br>");
out.write(" <label>검색어 : </label>") ;
out.write(" <input type=\"text\" name=\"q\"><br>");
out.write(" <label>size:</label>");
out.write(" <input type=\"text\" name=\"s\"><br>");
out.write(" <input type=\"submit\" value=\"제출\">");
out.write("</form>");
request.setAttribute("page", page);
request.setAttribute("query", query);
request.setAttribute("size", size);
// [EL 저장소]
// 4대 저장소에서 데이터가 저장된다.
// page-request-session-application 순서로 EL 태그를 찾는다.
// model이 있을 때, tag library로 변수가 생성되어 값이 저장되어 있으면, 우선 순위가 밀린다.
// 그때 model에 우선순위를 주기 위해서 Scope를 사용해서 콕 집어 준다. (requestScope)
// 3. 쿠키 입력
// 4. Header 입력
// 3. hidden 필드 입력
request.getRequestDispatcher("WEB-INF/view/input.jsp").forward(request, response);
}
}
0. css 정리
-
reset.css :
-
utils.css :
-
style.css :
-
component.css :
-
button.css :