Day 52

ChangWoo·2022년 11월 17일
0

중앙 HTA

목록 보기
5/51

MERGE INTO 문
-- 하나의 SQL문으로 INSERT, UPDATE, DELETE 작업을 수행할 수 있다.
-- 형식
-- MERGE INTO 테이블명
-- USING DUAL
-- ON (컬럼명 = 값 AND 컬럼명 = 값) // 이 조건에 맞는 행이 있다면
-- WHEN MATCH THEN
-- UPDATE
-- SET 컬럼명 = 값 // UPDATE를 하고
-- WHEN NOT MACHTED THEN
-- INSERT (컬럼명, 컬럼명, 컬럼명)
-- VALUES (값, 값, 값) // 업다면 INSERT를 진행한다.

MERGE INTO SAMPLE_BOARD_BOOK_CART_ITEMS
    USING DUAL
    ON (BOOK_NO = 100022 AND USER_ID = 'hong')
WHEN MATCHED THEN
    UPDATE
        SET ITEM_AMOUNT = ITEM_AMOUNT + 1
WHEN NOT MATCHED THEN
    INSERT (ITEM_NO, BOOK_NO, USER_ID)
    VALUES (SAMPLE_CARTS_SEQ.NEXTVAL, 100022, 'hong');

기존

실행 후

MERGE INTO SAMPLE_BOARD_BOOK_CART_ITEMS
    USING DUAL
    ON (BOOK_NO = 100010 AND USER_ID = 'hong')
WHEN MATCHED THEN
    UPDATE
        SET ITEM_AMOUNT = ITEM_AMOUNT + 1
WHEN NOT MATCHED THEN
    INSERT (ITEM_NO, BOOK_NO, USER_ID)
    VALUES (SAMPLE_CARTS_SEQ.NEXTVAL, 100010, 'hong');


없던 목록도 추가된다.

carts.xml
	<insert id="insertCartItem" parameterClass="com.sample.vo.CartItem">
		merge into sample_board_book_cart_items
		using dual
			on (book_no = #bookNo# and user_id = #userId#)
		when matched then
			update
				set
					item_amount = item_amount + 1,
					item_updated_date = sysdate
		when not matched then
			insert (item_no, book_no, user_id)
			values (sample_carts_seq.nextval, #bookNo#, #userId#)
	</insert>


장바구니에 담을 수 있는 권수가 늘어난다.

delete.jsp
<%
	User user = (User) session.getAttribute("loginedUser");
	if (user == null) {
		response.sendRedirect("")
	}
%>

이 부분을 워낙 많이 쓰기 때문에 하나의 jsp에 담아 사용하기 편리하게 한다.

common/logincheck.jsp
<%
	User loginUser = (User) session.getAttribute("loginedUser");
	if (loginUser == null) {
		response.sendRedirect("/web-board/user/loginform.jsp?error=deny");
		return;
	}
%>
addItem.jsp / deleteItem.jsp / list.jsp에 위의 delete.jsp와 같은 부분을 지우고 추가
<%@ include file="../common/logincheck.jsp" %>
// 사용자 아이디 조회(loginUser는 logincheck.jsp에서 정의한 변수다.)
String userId = loginUser.getId(); //user를 loginUser로 변경해 주었다.
	// CartItemDao 객체를 생성하고, getCartItemsByUserId(String userId) 메소드를 실행해서 장바구니 아이템 목록을 조회한다.
	CartItemDao cartItemDao = new CartItemDao();
	List<CartItemDto> dtoList = cartItemDao.getCartItemsByUserId(loginUser.getId()); // 이것 역시 loginUser로 변경해 주었다.

초록색 방식 = include한 곳에서 정상동작
빨간색 방식 = 오류 발생

Static include (정적 include)

home.jsp에 header.jsp가 그대로 포함되어 하나의 자바파일(jsp파일)로 된다.
그래서 밑에서 System.out.println(user)가 사용 가능하다.(변수 공유 가능)
변수를 공유하고 싶을 때 사용
그러나 변수명 중복 선언에 주의해야 한다! (header.jsp와 home.jsp에서 같은 이름의 변수를 사용하면 안된다!)

Dynamic include (동적 include)

head.jsp.java / home.jsp.java 파일이 각각 하나씩 생긴다.
home.jsp.java에 잠깐 들렸다가 다시 head.jsp.java로 돌아간다.
(home.jsp.java 코드에서 header.jsp를 include하는 메소드가 실행된다.)
그래서 밑에서 System.out.println(user)가 사용 불가능하다. (변수 공유 불가)
변수를 공유하고 싶지 않을 때 사용
로그인했는지 확인만 가능하고 이후에 해당 사용자정보를 이용해서 다른 로직은 처리할 수 없다.
변수이름 충돌을 신경쓸 필요가 없다.
header.jsp에 정의된 변수를 home.jsp에서 사용할 수 없다.

logincheck.jsp는 코드가 그대로 들어와 있다.

header.jsp는 코드가 포함되어있지 않다.

특정 장바구니 아이템 삭제하기와 장바구니 아이템 전부 삭제하기

deleteItem.jsp
<%
// 요청객체에서 요청파라미터 정보 조회
int itemNo = StringUtils.stringToInt(request.getParameter("itemNo"));

// itemNo에 해당하는 장바구니 아이템정보를 삭제한다.
CartItemDao cartItemDao = new CartItemDao();
cartItemDao.deleteCartItemByNo(itemNo);
// 재요청 URL을 응답으로 보낸다.
response.sendRedirect("list.jsp");

%>

carts.xml
// 특정 장바구니 아이템 삭제하기
delete from
sample_board_book_cart_items
where
item_no = #value#

<delete id="deleteCartItemsByUserId" parameterClass="string"> // 장바구니 아이템 전부 삭제하기
	delete from 
		sample_board_book_cart_items
	where
		user_id = #value#
</delete>

CartItemDao.java
public void deleteCartItemByNo(int itemNo) {
SqlMapper.delete("carts.deleteCartItemByNo", itemNo);
}

public void deleteCartItemsByUserId(String userId) {
	SqlMapper.delete("carts.deleteCartItemsByUserId", userId);
}

특정 장바구니 아이템 삭제 전

특정 장바구니 아이템 삭제 후

전체삭제

cart/list.jsp
<div class="">
      <a href="clear.jsp" class="btn btn-secondary btn-sm" >전체 삭제</a>
      <a href="" class="btn btn-secondary btn-sm" >전체 구매</a>
      
      <a href="../book/list.jsp" class="btn btn-outline-primary btn-sm float-end">쇼핑계속</a>
	</div>

cart/clear.jsp
<%@ include file="../common/logincheck.jsp"%>

<%
	// 로그인한 사용자의 아이디 조회
	String userId = loginUser.getId();
	
	CartItemDao cartItemDao = new CartItemDao();
	// 로그인한 사용자의 모든 장바구니 아이템을 삭제한다.
	cartItemDao.deleteCartItemsByUserId(userId);
	
	// 재요청 URL을 응답으로 보낸다.
	response.sendRedirect("list.jsp");
%>

특정 장바구니 아이템을 삭제하기 위해서는 item_no가 필요하고
장바구니 아이템을 전부 삭제하기 위해서는 user_id가 필요하다.
그래서 session객체에서 그 정보들을 가지고 온다.

전체 삭제 전

전체 삭제 후

선택 삭제

요청 파라미터에서 값을 전달하는 방법

1. <a href="sample.jsp?no=100&page=3">링크</a> --> &과 no로 보내는 방법 (값이 미리 정해져 있어야 한다.)

GET sample.jsp?no=100&page=3 HTTP/1.1
Accept:text/html,application/xml
Accept-Encoding: ...
Accpet-Language ...
----------------------------------------------------------------------
2. <form method="get" action="sample.jsp">	--> get 방식으로 보내는 방법
  			<input type="number" name="minPrice" />
  			<input type="number" name="maxPrice" />
  			<input type="text" name="keyword" />
 			 <button type="submit">제출 </button>
	</form>

GET sample.jsp?minPrice=10000&maxPrice=50000&keyword=미니카HTTP/1.1
Accept:text/html,application/xml
Accept-Encoding: ...
Accpet-Language ...
----------------------------------------------------------------------
3. <form method="post" action="sample.jsp"> --> post 방식으로 보내는 방법
  			<input type="number" name="minPrice" />
  			<input type="number" name="maxPrice" />
  			<input type="text" name="keyword" />
 			 <button type="submit">제출 </button>
	</form>
    
POST sample.jsp HTTP/1.1
Accept:text/html,application/xml
Accept-Encoding: ...
Accpet-Language ...   

userId=hong&userPwd=zxcv1234

선택삭제는 사용자가 무엇을 선택할지 모르므로 번호를 미리 넣어놓을 수 없다.
그래서 1번과 같은 방법은 불가능하다.
그렇기 때문에 form 태그를 사용해야 한다!
추가 / 업데이트 시에는 POST 방식
삭제 시에는 GET 방식을 사용한다.
form 태그로 장바구니 리스트 밑 모든 것이 포함되어야 한다.

--
값이 정해져 있다면 1번 방법을 사용. (무언가를 눌렀을 때 딱 이 값이 삭제되거나 이 값이 보여져야 할 때)
값이 안 정해져 있다면 2번 혹은 3번 방법을 사용. (form을 만들어서 그 것을 다 포함해서 그 중에 선택된 것을 삭제해야 할 때)

cart/list.jsp (table시작부터 div 닫는 곳 까지 하나의 form으로 감싸준다.)
<p>장바구니 목록을 확인하세요.</p>
	<form method="get" action="deleteItems.jsp">
	<table class="table">
    ...
    ...
    ...
<div class="">
      <a href="clear.jsp" class="btn btn-secondary btn-sm" >전체 삭제</a>
      <button type="submit" class="btn-secondary btn-sm">선택 삭제</button>
      <a href="../book/list.jsp" class="btn btn-outline-primary btn-sm float-end">쇼핑계속</a>
	</div>
	</form>


위의 체크박스에는 name과 value를 주지 않았다.
그러나 값이 넘어가기 위해서는 name과 value가 있어야 한다.
for문이 돌면서 체크박스가 생기게 하고, 아이템 번호가 생기게 하였다.
체크박스는 같은 것 끼리는 이름이 같아야 한다. (값만 다르다.)

name과 value가 deleteItems.jsp로 제출된다.

<td><input type="checkbox" name="itemNo" value="<%=dto.getItemNo() %>" /></td>
cart/deleteItems.jsp
<%@ include file="../common/logincheck.jsp" %>

<%
	// 입력폼에서 체크한 체크박스의 값을 요청객체에서 조회하기
	String[] values = request.getParameterValues("itemNo");
	// 체크된 아이템이 없으면 values는 null이다. values가 null이면 장바구니리스트를 재요청하는 URL을 응답으로 보낸다.
	if (values == null) {
		response.sendRedirect("list.jsp");
		return;
	}
	
	// CartItemDao 객체를 생성하고, 전달받은 장바구니 아이템을 삭제하는 수행문을 반복실행한다.
	CartItemDao cartItemDao = new CartItemDao();
	for (String value : values) {
		int itemNo = StringUtils.stringToInt(value);
		cartItemDao.deleteCartItemByNo(itemNo);
	}
	
	// 재요청 URL을 응답으로 보낸다.
	response.sendRedirect("list.jsp");
%>

선택 삭제 전

선택 삭제 후

delteItem과 deleteItems는 같기 때문에 deleteItem을 없애준다.
(하나를 지우더라도 어차피 삭제하는 것은 같기 때문에 delteItems로 해놓으면 하나를 지우던 두개를 지우던 세개를 지우던 다 가능하다.)

cart/list.jsp
<a href="deleteItem.jsp?itemNo=<%=dto.getItemNo() %>" class="btn btn-secondary btn-sm">삭제</a><a href="deleteItems.jsp?itemNo=<%=dto.getItemNo() %>" class="btn btn-secondary btn-sm">삭제</a>로 변경

apach/commons/codec/download/1.15.bin 다운 & WEB-INF에 CODEC-1.15.jar 복사 붙여넣기
여러 프로젝트에서 사용되는 공통기능(암/복호화, 인코딩 등)들을 제공해준다.
컴퍼넌트 : 특별한 목적에 맞는 기능들을 제공해준다.

비밀번호 암호화

user/register.jsp
// 비밀번호 암호화하기
	String secretPassword = DigestUtils.sha256Hex(password);
	
	// User 객체를 생성해서 조회된 값을 저장한다.
	User user = new User();
	user.setId(id);
	user.setPassword(secretPassword);
	user.setName(name);
	user.setEmail(email);

SQL DEVELOPER에서
SAMPLE_BOARD_USERS의 USER_PASSWORD를 CHAR 타입으로 / 64자로 만들어준다.
그리고 회원가입을 하면,

위와 같이 비밀번호가 암호화가 된다.

그런데, 여기서 문제는 기존 회원가입 했던 아이디들을 로그인하려면 ZXCV1234 포함 64자 띄어쓰기가 있으므로 그것을 해결하고자

sql devleloper

UPDATE SAMPLE_BOARD_USERS
SET
    USER_PASSWORD = (SELECT USER_PASSWORD
                      FROM SAMPLE_BOARD_USERS
                        WHERE USER_ID = 'jung');
COMMIT;

를 해주면


위와 같이 모든 아이디의 비밀번호가 변경된다.

Salt (암호를 찾기 힘들게 만드는 방법)

그런데 여기서 또 비밀번호를 알아낼 수 있으므로 "salt" 소금을 쳐주는 방식(id와 email이 password에 합쳐지게 한다.)

user/register.jsp
// 비밀번호 암호화하기
	String salt = id + email;
	String secretPassword = DigestUtils.sha256Hex(salt+password);


ryu와 ahn도 같은 비밀번호인 zxcv1234이지만, salt로 인해 비밀번호가 달라진다.
salt를 어떤 방식으로 지정하는지 알 수 없으므로 이러면 비밀번호를 알아내기가 힘들어진다.

그러나 아직 로그인이 되지 않으므로 login.jsp에서 salt비밀번호와 비교하기 위해
현재 로그인하는 비밀번호에 salt를 똑같이 해주면 그 둘을 비교하게 되고 그 둘이 같다면 로그인이 되게 한다.

user/login.jsp
String salt = savedUser.getId() + savedUser.getEmail();
	String secretPassword = DigestUtils.sha256Hex(salt+password);
	
	// 조회된 사용자정보의 비밀번호와 입력한 비밀번호가 일치하지 않으면 로그인화면을 재요청하는 URL을 응답으로 보낸다.
	if (!savedUser.getPassword().equals(secretPassword)) {
		response.sendRedirect("loginform.jsp?error=fail");
		return;

비밀번호가 같아도 각 아이디마다 다른 salt비밀번호를 가지게 되고 로그인이 된다.

codec

  • 텍스트 데이터나 바이너리 데이터를 인코딩/디코딩한다.
  • 인코딩
    원본 데이터 -> 변환된 데이터 (원본 데이터를 다른 형태로 바꾸는 것)
  • 디코딩
    변환된 데이터 -> 원본 데이터 (다른 형태의 데이터를 다시 원본 데이터로 바꾸는 것)

비대칭키 암호화 알고리즘 : 공인인증서
비대칭키 암호화 알고리즘 = 공개키(은행이 가지고 있는 것) + 개인키(내가 가지고 있는 것)
공개키는 맞는 개인키에만 풀리고 / 개인키는 맞는 공개키로만 풀린다.
DigestUtil
- 단방향 암호화알고리즘을 사용해서 평문을 암호문으로 변환한다.
- 인코딩만 가능 / 디코딩은 불가능

Base64
- 64개의 문자로 데이터를 표현한 것
- 특수문자는 포함되지 않는다.
- url 뒤에 붙여서 표현하기 안전하지 않은 문자들이 있다.

서블릿 (jakarta의 인터페이스 중 하나)

  • 서버에서 실행되는 자바프로그램
  • 모든 서블릿 클래스가 반드시 구현해야하는 인터페이스다.
  • 서블릿의 라이프사이클 메소드(객체의 삶과 관련된 메소드)가 정의되어 있다.
  • 자바의 객체에도 삶이 있다.
    서블릿의 주요 메소드 : void init(ServletConfig config)
    -- 서블릿 객체가 초기화될 때 실행되는 메소드다.
    void destroy()
    -- 서블릿 객체가 폐기될 때 실행되는 메소드다.
    void service(ServletRequest request, ServletResponse response)
    -- 서블릿 객체가 클라이언트의 요청을 처리할 때 실행되는 메소드다.
jakarta.servlet.GenericServlet <Abstract Class> // 추상 클래스
	- Servlet 인터페이스를 구현하는 추상 클래스다. 
    - Servlet 인터페이스의 주요 메소드들을 대부분 구현하고 있다.
    - Servlet 인터페이스에 정의되어 있지 않은 추가 메소드를 제공한다.
    - 주요 메소드
    	void int() {...} // 구현 메소드
        String getinitParameter(String name) {...}
        - 서블릿 초기화 파라미터값을 반환한다.
        ServletConfig getServletConfig() {...}
        - ServletConfig 객체를 반환한다.
        ServletContext getServiceContext() {...}
        - ServletContext 객체를 반환한다.
대부분 HTTP라는 프로토콜을 통해서 클라이언트와 서버에서 통신(요청/응답)을 한다. 
그래서 서블릿은 HTTP와 연동이 되도록 만든다.
request는 요청하는 것 / response는 응답해줄 것이다.
servlet을 만드는 것은 service라는 메소드를 재정의하는 것이다.
jakarta.Servlet.http.HttpServlet<Abstract Class>는HTTP 서블릿은 Generic클래스를 기반으로 만들어졌고 HTTP와 연동이 가능하다.

jakarta.Servlet.http.HttpServlet<Abstract Class>
- HTTP 프로토콜에 특화된 서블릿을 개발할 때 상속받는 클래스다.
- 주요 메소드 (dnlsms Servlet 메소드였지만 아래는 모두 HttpServlet 메소드다.)
	void doGet(HttpServletRequest request.HttpServletResponse response) {...}
    - GET 방식의 요청은 이 메소드를 재정의해서 처리한다.
    void doPut(HttpServletRequest request.HttpServletResponse response) {...}
     - PUT 방식의 요청은 이 메소드를 재정의해서 처리한다.
     void doPost(HttpServletRequest request.HttpServletResponse response) {...}
      - POST 방식의 요청은 이 메소드를 재정의해서 처리한다.
     void doDelete(HttpServletRequest request.HttpServletResponse response) {...}
     - DELETE 방식의 요청은 이 메소드를 재정의해서 처리한다.
     void service(HttpServletRequest request.HttpServletResponse response) {...}
     - 클라이언트의 요청을 분석해서 요청방식에 맞는 doXXX 메소드를 실행한다.
     * 요청방식에 상관없이 항상 실행되는 메소드다.
     * service() 메소드의 본래 기능을 버리고, 클라이언트의 요청을 처리하는 코드로 재정의한다.
 
 public class HelloServlet extends HttpServlet { 
 	//HttpServlet클래스의 service(request, response) {
     	// 클라이언트의 요청을 처리하고, 응답을 제공하는 코드
    }
 }
/com.sample.servlet/HelloServlet.java // 클래스 생성 후 extends 해주고 s + ctrl + shif 해서 service를 실행해 Http를 재정의 해준 뒤 응답을 보내준다.
package com.sample.servlet;

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("/hello")
public class HelloServlet extends HttpServlet {
	
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		response.setContentType("text/html; charset=utf-8");
		PrintWriter out = response.getWriter();
		out.println("<!doctype html>");
		out.println("<html lang='ko'>");
		out.println("<meta charset='utf-8'>");
		out.println("<title>헬로 서블릿</title>");
		out.println("</head>");
		out.println("<body>");
		out.println("<h1>헬로 서블릿</h1>");
		out.println("<p>나는 서블릿입니다.</p>");
		out.println("</body>");
		out.println("</html>");

	}
}


- JSP 발명 전에는 이렇게 적었어야 했다.
- JSP의 
 <%@ page language="java" contentType="text/html; charset=UTF-8"이 
  서블릿의 response.setContentType("text/html; charset=utf-8");이 된다.
- PrintWriter는 브라우저와 연결되어 있는 출력장치(객체)다.

서블릿은 동적인 컨텐츠를 위해서는 사용하지 않는다. (서블릿으로 HTTP응답해주는 것을 안만드는 이유는 너무 힘들기 때문이다.)

JSP는 URL에 JSP경로를 적어 실행하였다.

그러나 서블릿은 웹 자원이 아닌 자바의 자원이라 위와 같은 방법으로는 실행하지 못한다. 그래서 서블릿은 요청 URL과 매핑시켜줘야 한다. (그게 @WebServlet("/hello")다.)

JSP는 내가 이 곳에 들어갈 코드만 적으면 됐지만, 서블릿은 내가 다 해줘야 한다.

아래와 같이 하나의 데이터만 가지는 경우도 있고 여러 데이터를 가지는 경우도 있다.
이 중 여러 데이터를 가질 때 처리하는 방법은

<form>
	이름
	<input type="type" name="name" />
	전화번호
	<input type="type" name="tel" />
	이메일
	<input type="type" name="email" />
	보유기술
	<input type="checkbox" name="skill" value="java">자바
	<input type="checkbox" name="skill" value="sql">SQL
	<input type="checkbox" name="skill" value="c">c
	<input type="checkbox" name="skill" value="c#">c#
	<input type="checkbox" name="skill" value="python">파이썬
	<input type="checkbox" name="skill" value="bigdata">빅데이터
	경력증명서
	<input type="file" name="attachedFile" />
	<input type="file" name="attachedFile" />
	<input type="file" name="attachedFile" />
	<input type="file" name="attachedFile" />
	<input type="file" name="attachedFile" />
	수행프로젝트
	<input type"text" name="project" />
	<input type"text" name="project" />
	<input type"text" name="project" />
	<input type"text" name="project" />
	<button type="submit">제출</button>
</form>
profile
한 걸음 한 걸음 나아가는 개발자

0개의 댓글