<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Hazel.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 14 Feb 2022 14:20:12 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Hazel.log</title>
            <url>https://images.velog.io/images/hazel_jo/profile/6c5e83ec-f6f6-4c64-bda4-3ebafe85e156/다람이.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Hazel.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hazel_jo" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[스프링 31강 - 스프링 시큐리티, Controller 응답 정리]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-31%EA%B0%95-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-31%EA%B0%95-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0</guid>
            <pubDate>Mon, 14 Feb 2022 14:20:12 GMT</pubDate>
            <description><![CDATA[<h3 id="1-스프링-시큐리티란">1. 스프링 시큐리티란?</h3>
<ul>
<li>애플리케이션에서 보안 기능을 구현하는데 사용되는 프레임워크(디자인패턴 + 라이브러리)</li>
<li>필터 기반으로 동작하므로 스프링 MVC와 분리되어 동작함</li>
</ul>
<h3 id="2-주요-기능">2. 주요 기능</h3>
<ul>
<li>인증(Authentication) : 사용자의 정당성 확인(로그인)</li>
<li>인가(Authorization) : 리소스나 처리에 대할 접근 제어(권한)</li>
</ul>
<h3 id="3-제공-기능">3. 제공 기능</h3>
<ul>
<li>세션 관리</li>
<li>로그인 처리</li>
<li>암호화 처리</li>
<li>자동 로그인</li>
<li>SRF(Cross-site request forgery) 토큰 처리
  : 웹 사이트 취약점 공격을 막아줌
  : 사용자가 자신도 모르게 공격자가 의도한 행위(수정, 삭제 등록)을 웹 사이트에 요청하게 하는 공격</li>
</ul>
<ol start="4">
<li>웹 화면 접근 정책
1) 회원 게시판(board)</li>
</ol>
<ul>
<li>목록(list) : 모두 접근 가능</li>
<li>등록(register) : 로그인한 회원만 접근 가능</li>
</ul>
<p>2) 공지사항 게시판(notice)</p>
<ul>
<li>목록(list) : 모두 접근 가능</li>
<li>등록(register) : 로그인한 관리자만 접근 </li>
</ul>
<h3 id="4-설정">4. 설정</h3>
<p>1) 라이브러리 설치
<a href="https://mvnrepository.com/">https://mvnrepository.com/</a> 에서</p>
<p><strong>Spring-security-web</strong> » 5.0.7.RELEASE - 중간 코드 복사 후
pom.xml에 붙여넣기
<strong>Spring Security Config</strong> » 5.0.7.RELEASE - 중간 코드 복사 후 pom.xml에 붙여넣기
<strong>Spring Security Core</strong> » 5.0.7.RELEASE - 중간 코드 복사 후
pom.xml에 붙여넣기
<strong>Spring Security Taglibs</strong> » 5.0.7.RELEASE - 중간 코드 복사 후 pom.xml에 붙여넣기</p>
<p>프로젝트 우클릭 후 Run As - 4번 클릭 - build success 나오면 성공!</p>
<h4 id="pomxml">pom.xml</h4>
<pre><code>        &lt;!-- 스프링 시큐리티 라이브러리 의존관계 정의 시작 --&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-web&lt;/artifactId&gt;
            &lt;version&gt;5.0.7.RELEASE&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-config&lt;/artifactId&gt;
            &lt;version&gt;5.0.7.RELEASE&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-core&lt;/artifactId&gt;
            &lt;version&gt;5.0.7.RELEASE&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-taglibs&lt;/artifactId&gt;
            &lt;version&gt;5.0.7.RELEASE&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- 스프링 시큐리티 라이브러리 의존관계 정의 끝 --&gt;</code></pre><p>2) web.xml에 아래 코드 추가하기</p>
<ul>
<li>contextConfigLocation에 스프링 시큐리티 설정 파일을 지정</li>
<li>스프링 시큐리티는 필터 기반으로 동작하기 때문에 필터 지정<pre><code>  &lt;!-- contextConfigLocation에 스프링 시큐리티 설정 파일을 지정함 --&gt;
  &lt;!-- The definition of the Root Spring Container shared by all Servlets and Filters --&gt;
  &lt;context-param&gt;
      &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
      &lt;param-value&gt;/WEB-INF/spring/root-context.xml
          /WEB-INF/spring/security-context.xml
      &lt;/param-value&gt;
  &lt;/context-param&gt;</code></pre></li>
</ul>
<p>3) security-context.xml 생성
상단에 아래 코드 추가하기</p>
<pre><code>&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
xmlns:security=&quot;http://www.springframework.org/schema/security&quot;
xsi:schemaLocation=&quot;http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd&quot;&gt;</code></pre><h4 id="security-contextxml">security-context.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xmlns:security=&quot;http://www.springframework.org/schema/security&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;

    &lt;security:http&gt;
    &lt;!-- 
    URI 패턴으로 접근 제한을 설정함 
    pattern : url 요청 경로
    access : 접근 제한 정책
    --&gt;
        &lt;security:intercept-url pattern=&quot;/board/list&quot; access=&quot;permitAll&quot;/&gt; &lt;!-- 모든 사용자에게 접근권한 허용 시(정해진 약속 access) --&gt;
        &lt;security:intercept-url pattern=&quot;/board/register&quot; access=&quot;hasRole(&#39;ROLE_MEMBER&#39;)&quot; /&gt;
        &lt;security:intercept-url pattern=&quot;/notice/list&quot; access=&quot;permitAll&quot; /&gt;
        &lt;security:intercept-url pattern=&quot;/notice/register&quot; access=&quot;hasRole(&#39;ROLE_ADMIN&#39;)&quot; /&gt;
        &lt;!-- 폼 기능 인증 기능 사용.
        접근 제한에 걸리면 스프링 시큐리티가 기본적으로 제공하는 로그인 페이지로 이동함 --&gt;
        &lt;security:form-login/&gt;
    &lt;/security:http&gt;

    &lt;security:authentication-manager&gt;
    &lt;!-- 
    지정된 아이디와 패스워드로 로그인이 가능하도록 설정함
    authorities : 위의 &lt;security:intercept-url의 &quot;hasRole(&#39;ROLE_MEMBER&#39;)&quot;을 말한다.
    member 사용자 : /board/list, /board/register, /notice/list 가능
    admin 사용자 : /board/list, /board/register, /notice/list, /notice/register 가능

    스프링 시큐리티 5버전부터는 패스워드 암호화 처리기를 반드시 이용하도록 변경이 되었음
    암호화 처리기를 사용하지 않도록 &quot;{noop}&quot; 문자열을 비밀번호 앞에 사용.

     --&gt;
        &lt;security:authentication-provider&gt;
            &lt;security:user-service&gt;
                &lt;security:user name=&quot;member&quot; password=&quot;{noop}1234&quot; authorities=&quot;ROLE_MEMBER&quot; /&gt;
                &lt;security:user name=&quot;admin&quot; password=&quot;{noop}abcd&quot; authorities=&quot;ROLE_MEMBER,ROLE_ADMIN&quot; /&gt;
            &lt;/security:user-service&gt;
        &lt;/security:authentication-provider&gt;
    &lt;/security:authentication-manager&gt;    

&lt;/beans&gt;        </code></pre><h3 id="controller-응답-정리">Controller 응답 정리</h3>
<p>1.void</p>
<ul>
<li>호출하는 URL과 동일한 뷰 이름을 나타냄</li>
<li>요청이 /notice/list이면, 뷰는 /notice/list.jsp를 가리킴</li>
</ul>
<p>2.string</p>
<p>3.자바빈즈 클래스(VO)</p>
<p>4.컬렉션 List</p>
<p>5.컬렉션 Map</p>
<p>6.ModelAndView</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 30강 - 스프링 폼 태그 라이브러리, Dao 대신 Mapper interface 활용하기]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-30%EA%B0%95-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%8F%BC-%ED%83%9C%EA%B7%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-Dao-%EB%8C%80%EC%8B%A0-Mapper%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%ED%99%9C%EC%9A%A9-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-30%EA%B0%95-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%8F%BC-%ED%83%9C%EA%B7%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-Dao-%EB%8C%80%EC%8B%A0-Mapper%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%ED%99%9C%EC%9A%A9-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0</guid>
            <pubDate>Mon, 14 Feb 2022 09:23:27 GMT</pubDate>
            <description><![CDATA[<h2 id="스프링-폼-태그-라이브러리">스프링 폼 태그 라이브러리</h2>
<ol>
<li>스프링 폼 태그란?</li>
</ol>
<ul>
<li>HTML 폼을 표시하기 위한 태그 라이브러리</li>
<li>스프링 폼을 사용하면 HTML 폼과 자바 객체를 쉽게 바인딩(= 서로 공유) 할 수 있음</li>
</ul>
<ol start="2">
<li>선언은?</li>
</ol>
<ul>
<li>&lt;%@ taglib prefix=&quot;form&quot; uri=&quot;<a href="http://www.springframework.org/tags/form&quot;">http://www.springframework.org/tags/form&quot;</a> %&gt; </li>
</ul>
<ol start="3">
<li>태그 목록은?</li>
</ol>
<ul>
<li><a href="form:password">form:password</a> : 패스워드 필드</li>
<li><a href="form:form">form:form</a> : 폼</li>
<li>&lt;form:input path=&quot;test&quot;&gt; 테스트 필드. id=&quot;test&quot; name=&quot;test&quot;</li>
<li><a href="form:textarea">form:textarea</a> : 텍스트 영역</li>
<li><a href="form:checkboxes">form:checkboxes</a> : 여러 개 체크박스</li>
<li><a href="form:checkbox">form:checkbox</a> : 체크박스</li>
<li><a href="form:radiobuttons">form:radiobuttons</a> : 여러 개의 라디오 버튼</li>
<li><a href="form:radiobutton">form:radiobutton</a> : 라디오 버튼</li>
<li><a href="form:select">form:select</a> : 셀렉트 박스</li>
<li><a href="form:hidden">form:hidden</a> : 숨겨진 필드</li>
<li><a href="form:label">form:label</a> : 라벨</li>
<li><a href="form:button">form:button</a> : 버튼</li>
<li><a href="form:errors">form:errors</a> : 입력값 검증 오류 표시(validate)</li>
</ul>
<h2 id="dao-클래스-대신-mapper-인터페이스-활용해서-처리">DAO 클래스 대신 Mapper 인터페이스 활용해서 처리</h2>
<h4 id="mapper-인터페이스란">Mapper 인터페이스란?</h4>
<ul>
<li>매핑 파일에 기재된 SQL을 호출하기 위한 인터페이스이다.</li>
<li>Mybatis 3.0부터 생겼다.</li>
<li>매핑 파일에 있는 SQL을 인터페이스로 호출한다.</li>
</ul>
<h4 id="mapper-사용하지-않았을-시">Mapper 사용하지 않았을 시?</h4>
<ul>
<li>sqlSessionTemplate.selectOne(&quot;namespace.id&quot;, 파라미터); 형식이었다.</li>
<li>namespace.SQL id로 지정해야 한다.</li>
<li>문자열로 작성하기 때문에 버그가 생길 수 있다.</li>
<li>IDE에서 제공하는 code assist를 사용할 수 없다.</li>
</ul>
<h4 id="mapper-사용했을-때">Mapper 사용했을 때</h4>
<ul>
<li>Mapper 인터페이스를 개발자가 직접 작성한다.</li>
<li>패키지 이름+&quot;.&quot;+인터페이스 이름+&quot;.&quot;+메서드 이름이  namespace.SQL의 id를 설정해야 한다.</li>
<li>namespace 속성에는 패키지를 포함한 Mapper 인터페이스 이름 형식이다.</li>
<li>SQL id에는 매핑하는 메서드 이름을 지정한다.</li>
</ul>
<h4 id="mapper-인터페이스-작성">Mapper 인터페이스 작성</h4>
<ul>
<li>반드시 인터페이스로 선언해주어야 한다.</li>
<li>namespace명은 패키지 포함 인터페이스 이름으로 작성한다.</li>
<li>ex)&lt;mapper namespace=&quot;myspring.user.dao.UserMapper&quot;&gt;</li>
<li>메서드명은 SQL id와 동일하게 작성한다.</li>
</ul>
<h3 id="설정">설정</h3>
<ol>
<li>pom.xml에 mybatis, springframework, ojdbc6 등 라이브러리가 있는지 확인</li>
<li>root-context.xml 추가<pre><code> &lt;bean class=&quot;org.mybatis.spring.mapper.MapperScannerConfigurer&quot;&gt;
     &lt;property name=&quot;basePackage&quot; value=&quot;kr.or.ddit.*.mapper&quot; /&gt;
 &lt;/bean&gt;
&lt;/beans&gt;</code></pre></li>
</ol>
<ul>
<li>개발자가 직접 DAO를 설정하지 않아도 자동으로 Mapper 인터페이스를 활용하는 객체를 생성함</li>
<li>소스마다 설정하지 않고 자동으로 경로를 설정해서 인식시킬 수 있음</li>
<li>MapperScannerConfigurer : &quot;kr.or.ddit.*.mapper&quot;에 있는 매퍼클래스를 스캐닝할거다!</li>
</ul>
<ol start="3">
<li>Mapper가 될 Interface 만들기</li>
<li>SQL.xml의 namespace 바꾸기</li>
<li>ServiceImpl에서 dao 걷어내고 Mapper로 연결</li>
<li>mybatisAlias.xml 수정</li>
</ol>
<h3 id="예시">예시</h3>
<h4 id="lprod-시리즈-정리하기">Lprod 시리즈 정리하기</h4>
<ol>
<li>kr.or.ddit 폴더에 lprod 폴더 만들기</li>
<li>controller, dao, mapper, service, vo 폴더 각각 만들고, Mapper interface 만들어서 추가
<img src="https://images.velog.io/images/hazel_jo/post/b6dcc372-1bcb-4b2e-a802-0a80a3209093/%EC%A0%9C%EB%AA%A9%20%EC%97%86%EC%9D%8C.jpg" alt=""><h4 id="lprodmapperjava">LprodMapper.java</h4>
<pre><code>package kr.or.ddit.lprod.mapper;
</code></pre></li>
</ol>
<p>import java.util.List;
import java.util.Map;</p>
<p>import kr.or.ddit.BuyerVO;
import kr.or.ddit.lprod.vo.LprodVO;
import kr.or.ddit.member.vo.MemberVO;</p>
<p>public interface LprodMapper {
    //상품분류 별 거래처 목록
    public List<LprodVO> list(Map&lt;String, Object&gt; map);</p>
<pre><code>//상품분류 별 거래처 목록 행의 수
public int listCount(Map&lt;String, Object&gt; map);

//거래처 상세 정보
public BuyerVO detail(String buyerId);

//거래처 수정
public int modify(BuyerVO buyerVO);

//로그인
public MemberVO login(MemberVO memberVO);</code></pre><p>}</p>
<pre><code>
3. SQL.xml의 namespace 바꾸기
namespace=&quot;매퍼 클래스가 있는 패키지명.매퍼클래스명&quot;</code></pre><mapper namespace="kr.or.ddit.lprod.mapper.LprodMapper">
```
4. ServiceImpl에서 dao 걷어내고 Mapper로 연결
#### LprodServiceImpl.java
```
package kr.or.ddit.lprod.service.impl;

<p>import java.util.List;
import java.util.Map;</p>
<p>import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;</p>
<p>import kr.or.ddit.BuyerVO;
import kr.or.ddit.lprod.mapper.LprodMapper;
import kr.or.ddit.lprod.service.LprodService;
import kr.or.ddit.lprod.vo.LprodVO;
import kr.or.ddit.member.vo.MemberVO;</p>
<p>@Service
public class LprodServiceImpl implements LprodService {
    @Autowired
    //LprodDao lprodDao; 이제 dao안쓴다.
    private LprodMapper lprodMapper;</p>
<pre><code>@Override
public List&lt;LprodVO&gt; list(Map&lt;String, Object&gt; map){
    return this.lprodMapper.list(map);
}

//상품분류 별 거래처 목록 행의 수
@Override
public int listCount(Map&lt;String, Object&gt; map) {
    return this.lprodMapper.listCount(map);
}

//거래처 상세 정보
@Override
public BuyerVO detail(String buyerId) {
    return this.lprodMapper.detail(buyerId);
}

//거래처 수정
@Override
public int modify(BuyerVO buyerVO) {
    return this.lprodMapper.modify(buyerVO);
}

//로그인
//메소드 재정의
@Override
public MemberVO login(MemberVO memberVO) {
    //this 생략 가능
    return lprodMapper.login(memberVO);
}</code></pre><p>}</p>
<pre><code>
5. mybatisAlias.xml 수정</code></pre><typeAlias type="kr.or.ddit.lprod.vo.LprodVO" alias="lprodVO"/>
```
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 29강 - 예외처리]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-29%EA%B0%95-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-29%EA%B0%95-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 13 Feb 2022 14:44:14 GMT</pubDate>
            <description><![CDATA[<ol>
<li>예외처리란?</li>
</ol>
<ul>
<li>프로그램을 처리되는 동안 문제가 발생 시 처리를 중단하고 다른 처리를 하는 것</li>
<li>웹 컨테이너(tomcat)는 기본 오류 페이지를 표시해줌</li>
<li>화면에 서버의 내부 정보가 일반인에게 노출되어 보안이 취약해지고 공격을 받을 수 있으므로, 최대한 개발자가 직접 예외를 처리하여 정의한 오류 페이지를 표시하게 해줘야 한다.</li>
</ul>
<ol start="2">
<li>예외 종류?</li>
</ol>
<ul>
<li>시스템 예외</li>
<li>의존 라이브러리에서 발생한 예외</li>
<li>사용자 정의 예외</li>
<li>스프링 프레임워크 예외</li>
</ul>
<ol start="3">
<li>예외 발생 예시?</li>
</ol>
<ul>
<li>등록 시 필수여야 하는데 빈 값을 입력하는 유효값 검증 예외</li>
<li>수정화면 생성 시 뷰 파일에서의 예외</li>
<li>삭제 시 매핑 파일에서의 예외</li>
<li>없는 글에 접근 시 예외</li>
<li>없는 URL 요청 시 예외</li>
</ul>
<h2 id="예시">예시</h2>
<h3 id="1-예외처리-handler-controller">1. 예외처리 Handler Controller</h3>
<h4 id="commonexceptionhandlerjava">CommonExceptionHandler.java</h4>
<pre><code>package kr.or.ddit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

//스프링 컨트롤러에서 발생된 예외를 처리하는 핸들러 클래스 임을 명시함
@ControllerAdvice
public class CommonExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(CommonExceptionHandler.class);

    //괄호 안에 설정한 예외타입을 해당 메서드가 처리한다는 것을 의미함
    // &lt;exception-type&gt;java.lang.Exception&lt;/exception-type&gt;
    //없는 URL 요청 예외(X)
    //수정화면 생성 시 뷰 파일(jsp)에서 예외(X)
    @ExceptionHandler(Exception.class)
    public String handle(Exception e, Model model) {
        logger.info(e.toString());

        model.addAttribute(&quot;exception&quot;, e);

        //forwarding
        return &quot;error/Exception&quot;;

    }

}</code></pre><h3 id="2-에러페이지로-보내주는-controller">2. 에러페이지로 보내주는 Controller</h3>
<h4 id="errorcontrollerjava">ErrorController.java</h4>
<pre><code>package kr.or.ddit;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ErrorController {
    @RequestMapping(value = &quot;/error/400&quot;)
    public String error400() {
        return &quot;error/400&quot;;
    }
    @RequestMapping(value = &quot;/error/404&quot;)
    public String error404() {
        return &quot;error/404&quot;;
    }
    @RequestMapping(value = &quot;/error/500&quot;)
    public String error500() {
        return &quot;error/500&quot;;
    }
}
</code></pre><h3 id="3-jsp">3. JSP</h3>
<h4 id="exceptionjsp">Exception.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;    

&lt;!-- 404 Error Text --&gt;
&lt;div class=&quot;text-center&quot;&gt;
    &lt;div class=&quot;error mx-auto&quot; data-text=&quot;Exception&quot;&gt;Exception&lt;/div&gt;
    &lt;p class=&quot;lead text-gray-800 mb-5&quot;&gt;
    ${exception.getMessage()}
    &lt;/p&gt;
    &lt;ul&gt;
        &lt;c:forEach var=&quot;stack&quot; items=&quot;${exception.getStackTrace()}&quot;&gt;
            &lt;li&gt;${stack.toString()}&lt;/li&gt;
        &lt;/c:forEach&gt;
    &lt;/ul&gt;
    &lt;a href=&quot;/lprod/list&quot;&gt;&amp;larr;처음으로 이동&lt;/a&gt;
&lt;/div&gt;
</code></pre><h4 id="400jsp">400.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;

&lt;!-- 404 Error Text --&gt;
&lt;div class=&quot;text-center&quot;&gt;
    &lt;div class=&quot;error mx-auto&quot; data-text=&quot;400&quot;&gt;400&lt;/div&gt;
    &lt;p class=&quot;lead text-gray-800 mb-5&quot;&gt;Bad Request&lt;/p&gt;
    &lt;a href=&quot;/lprod/list&quot;&gt;&amp;larr;처음으로 이동&lt;/a&gt;
&lt;/div&gt;
</code></pre><h4 id="404jsp">404.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;

&lt;!-- 404 Error Text --&gt;
&lt;div class=&quot;text-center&quot;&gt;
    &lt;div class=&quot;error mx-auto&quot; data-text=&quot;404&quot;&gt;404&lt;/div&gt;
    &lt;p class=&quot;lead text-gray-800 mb-5&quot;&gt;Page Not Found&lt;/p&gt;
    &lt;a href=&quot;/lprod/list&quot;&gt;&amp;larr;처음으로 이동&lt;/a&gt;
&lt;/div&gt;
</code></pre><h4 id="500jsp">500.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;

&lt;!-- 404 Error Text --&gt;
&lt;div class=&quot;text-center&quot;&gt;
    &lt;div class=&quot;error mx-auto&quot; data-text=&quot;500&quot;&gt;500&lt;/div&gt;
    &lt;p class=&quot;lead text-gray-800 mb-5&quot;&gt;Internal Server Error&lt;/p&gt;
    &lt;a href=&quot;/lprod/list&quot;&gt;&amp;larr;처음으로 이동&lt;/a&gt;
&lt;/div&gt;
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 28강 - Tiles]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-28%EA%B0%95-Tiles</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-28%EA%B0%95-Tiles</guid>
            <pubDate>Sun, 13 Feb 2022 14:27:04 GMT</pubDate>
            <description><![CDATA[<p>반복되는 부분들을 한곳에서 관리할 수 있게 도와주는 템플릿 프레임워크</p>
<h2 id="tiles-설정-방법">Tiles 설정 방법</h2>
<ol>
<li>라이브러리 다운로드
<a href="https://mvnrepository.com/">https://mvnrepository.com/</a> 에서
1) *<em>tiles-extras *</em>검색 - 3.0.8 클릭 - 중간 코드 복사 - pom.xml에 추가
2) *<em>tiles-servlet *</em>검색 - 3.0.8 클릭 - 중간 코드 복사 - pom.xml에 추가
3) *<em>tiles-jsp *</em>검색 - 3.0.8 클릭 - 중간 코드 복사 - pom.xml에 추가</li>
</ol>
<p><strong>pom.xml</strong></p>
<pre><code>        &lt;!-- Tiles --&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-extras --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.tiles&lt;/groupId&gt;
            &lt;artifactId&gt;tiles-extras&lt;/artifactId&gt;
            &lt;version&gt;3.0.8&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-servlet --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.tiles&lt;/groupId&gt;
            &lt;artifactId&gt;tiles-servlet&lt;/artifactId&gt;
            &lt;version&gt;3.0.8&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.apache.tiles/tiles-jsp --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.tiles&lt;/groupId&gt;
            &lt;artifactId&gt;tiles-jsp&lt;/artifactId&gt;
            &lt;version&gt;3.0.8&lt;/version&gt;
        &lt;/dependency&gt;</code></pre><ol start="2">
<li>servlet-context 설정
tiles의 순서를 1로 설정한다.</li>
</ol>
<p>-&gt; &lt;beans:property name=&quot;order&quot; value=&quot;1&quot; /&gt;</p>
<pre><code>    &lt;!-- Tiles --&gt;
    &lt;beans:bean id=&quot;tilesConfigurer&quot; 
    class=&quot;org.springframework.web.servlet.view.tiles3.TilesConfigurer&quot;&gt;
        &lt;beans:property name=&quot;definitions&quot;&gt;
            &lt;beans:list&gt;
                &lt;beans:value&gt;/WEB-INF/spring/tiles-config.xml&lt;/beans:value&gt;
            &lt;/beans:list&gt;
        &lt;/beans:property&gt;
    &lt;/beans:bean&gt;
    &lt;beans:bean id=&quot;tilesViewResolver&quot; 
        class=&quot;org.springframework.web.servlet.view.UrlBasedViewResolver&quot;&gt;
        &lt;beans:property name=&quot;viewClass&quot;
            value=&quot;org.springframework.web.servlet.view.tiles3.TilesView&quot; /&gt;
        &lt;beans:property name=&quot;order&quot; value=&quot;1&quot; /&gt;
    &lt;/beans:bean&gt;

    &lt;context:component-scan base-package=&quot;kr.or.ddit&quot; /&gt;</code></pre><p>3.tiles_config.xml 파일 생성 및 내용 추가</p>
<h4 id="tiles_confilgxml">tiles_confilg.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE tiles-definitions PUBLIC  
&quot;-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN&quot;  
&quot;http://tiles.apache.org/dtds/tiles-config_3_0.dtd&quot;&gt;

&lt;!-- tiles 설정 --&gt;
&lt;tiles-definitions&gt;
    &lt;!-- main layout 설정 --&gt;
    &lt;definition name=&quot;tiles-layout&quot; template=&quot;/WEB-INF/views/tiles/index.jsp&quot;&gt;
        &lt;put-attribute name=&quot;header&quot; value=&quot;/WEB-INF/views/tiles/header.jsp&quot; /&gt;
        &lt;put-attribute name=&quot;aside&quot; value=&quot;/WEB-INF/views/tiles/aside.jsp&quot; /&gt;
        &lt;put-attribute name=&quot;body&quot; value=&quot;&quot; /&gt;
        &lt;put-attribute name=&quot;footer&quot; value=&quot;/WEB-INF/views/tiles/footer.jsp&quot; /&gt;
    &lt;/definition&gt;

    &lt;definition name=&quot;*/*&quot; extends=&quot;tiles-layout&quot;&gt;
        &lt;put-attribute name=&quot;body&quot; value=&quot;/WEB-INF/views/{1}/{2}.jsp&quot; /&gt;
    &lt;/definition&gt;
&lt;/tiles-definitions&gt;
</code></pre><ol start="4">
<li>views 폴더에 tiles 폴더를 만들고 aside.jsp, footer.jsp, header.jsp, index.jsp를 추가한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 27강 - file upload]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-27%EA%B0%95-file-upload</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-27%EA%B0%95-file-upload</guid>
            <pubDate>Sun, 13 Feb 2022 14:11:20 GMT</pubDate>
            <description><![CDATA[<h3 id="1-pomxml에-라이브러리-추가">1. pom.xml에 라이브러리 추가</h3>
<p><a href="https://mvnrepository.com/">https://mvnrepository.com/</a> 에서</p>
<p>1) <strong>commons-fileupload</strong> 검색 - 1.4 클릭 - 중간 코드 복사 - pom.xml에 붙여넣기
2) <strong>imgscalr-lib</strong> 검색 - Imgscalr A Java Image Scaling Library » 4.2 클릭 - 중간 코드 복사 - pom.xml에 붙여넣기
3) <strong>jackson-databind</strong> 검색 - 2.13.1 클릭 - 중간코드 복사 - pom.xml에 붙여넣기
4) <strong>thumbnailator</strong> 검색  0.4.16 -&gt; 중간코드 복사 -&gt; pom.xml에 붙여넣기</p>
<h4 id="pomxml">pom.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;kr.or&lt;/groupId&gt;
    &lt;artifactId&gt;ddit&lt;/artifactId&gt;
    &lt;name&gt;springProj&lt;/name&gt;
    &lt;packaging&gt;war&lt;/packaging&gt;
    &lt;version&gt;1.0.0-BUILD-SNAPSHOT&lt;/version&gt;
    &lt;properties&gt;
        &lt;java-version&gt;1.8&lt;/java-version&gt;
        &lt;org.springframework-version&gt;5.2.5.RELEASE&lt;/org.springframework-version&gt;
        &lt;org.aspectj-version&gt;1.6.10&lt;/org.aspectj-version&gt;
        &lt;org.slf4j-version&gt;1.6.6&lt;/org.slf4j-version&gt;
    &lt;/properties&gt;
    &lt;dependencies&gt;
        &lt;!-- Spring --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-context&lt;/artifactId&gt;
            &lt;version&gt;${org.springframework-version}&lt;/version&gt;
            &lt;exclusions&gt;
                &lt;!-- Exclude Commons Logging in favor of SLF4j --&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;commons-logging&lt;/groupId&gt;
                    &lt;artifactId&gt;commons-logging&lt;/artifactId&gt;
                 &lt;/exclusion&gt;
            &lt;/exclusions&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-webmvc&lt;/artifactId&gt;
            &lt;version&gt;${org.springframework-version}&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- AspectJ --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.aspectj&lt;/groupId&gt;
            &lt;artifactId&gt;aspectjrt&lt;/artifactId&gt;
            &lt;version&gt;${org.aspectj-version}&lt;/version&gt;
        &lt;/dependency&gt;    

        &lt;!-- Logging --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
            &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
            &lt;version&gt;${org.slf4j-version}&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
            &lt;artifactId&gt;jcl-over-slf4j&lt;/artifactId&gt;
            &lt;version&gt;${org.slf4j-version}&lt;/version&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
            &lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;
            &lt;version&gt;${org.slf4j-version}&lt;/version&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;log4j&lt;/groupId&gt;
            &lt;artifactId&gt;log4j&lt;/artifactId&gt;
            &lt;version&gt;1.2.15&lt;/version&gt;
            &lt;exclusions&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;javax.mail&lt;/groupId&gt;
                    &lt;artifactId&gt;mail&lt;/artifactId&gt;
                &lt;/exclusion&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;javax.jms&lt;/groupId&gt;
                    &lt;artifactId&gt;jms&lt;/artifactId&gt;
                &lt;/exclusion&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;com.sun.jdmk&lt;/groupId&gt;
                    &lt;artifactId&gt;jmxtools&lt;/artifactId&gt;
                &lt;/exclusion&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;com.sun.jmx&lt;/groupId&gt;
                    &lt;artifactId&gt;jmxri&lt;/artifactId&gt;
                &lt;/exclusion&gt;
            &lt;/exclusions&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;!-- @Inject --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.inject&lt;/groupId&gt;
            &lt;artifactId&gt;javax.inject&lt;/artifactId&gt;
            &lt;version&gt;1&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- Servlet --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
            &lt;artifactId&gt;servlet-api&lt;/artifactId&gt;
            &lt;version&gt;2.5&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet.jsp&lt;/groupId&gt;
            &lt;artifactId&gt;jsp-api&lt;/artifactId&gt;
            &lt;version&gt;2.1&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
            &lt;artifactId&gt;jstl&lt;/artifactId&gt;
            &lt;version&gt;1.2&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- Test --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;junit&lt;/groupId&gt;
            &lt;artifactId&gt;junit&lt;/artifactId&gt;
            &lt;version&gt;4.7&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;!-- Database 라이브러리 시작 --&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --&gt;
        &lt;!-- XML로 쿼리를 작성하게 해주는 라이브러리 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis&lt;/artifactId&gt;
            &lt;version&gt;3.5.9&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --&gt;
        &lt;!-- 스프링과 mybatis를 연동하게 해주는 라이브러리 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-spring&lt;/artifactId&gt;
            &lt;version&gt;2.0.6&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --&gt;
        &lt;!-- 스프링에서 JDBC 드라이버를 통해 DB 연결 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;
            &lt;version&gt;${org.springframework-version}&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 --&gt;
        &lt;!-- 
            데이터베이스 커넥션 풀. 커넥션을 미리 여려개 만들어 놓고, 빌려썼다가 사용이 끝나면 반납.
            최근에 hikaricp를 사용하는 경우도 있음
        --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
            &lt;artifactId&gt;commons-dbcp2&lt;/artifactId&gt;
            &lt;version&gt;2.9.0&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4 --&gt;
        &lt;!-- Logging을 위한 라이브러리. Query를 Console이나 파일 로그로 볼 수 있음 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.bgee.log4jdbc-log4j2&lt;/groupId&gt;
            &lt;artifactId&gt;log4jdbc-log4j2-jdbc4&lt;/artifactId&gt;
            &lt;version&gt;1.16&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc6 --&gt;
        &lt;!-- 오라클과 자바를 연결해주는 라이브러리 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.oracle.database.jdbc&lt;/groupId&gt;
            &lt;artifactId&gt;ojdbc6&lt;/artifactId&gt;
            &lt;version&gt;11.2.0.4&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --&gt;
        &lt;!-- 
            * 자바빈 클래스인 VO에서 Getter/Setter메서드, toString()메서드를 자동 생성
         --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
            &lt;version&gt;1.18.22&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;!-- Database 라이브러리 끝 --&gt;

        &lt;!-- 입력값을 검증하기 위한 라이브러리 의존 관계 정의 --&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
            &lt;artifactId&gt;hibernate-validator&lt;/artifactId&gt;
            &lt;version&gt;5.4.2.Final&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- 파일 업로드 라이브러리 시작 --&gt;
        &lt;!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --&gt;
        &lt;!-- 파일 업로드/다운로드 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;commons-fileupload&lt;/groupId&gt;
            &lt;artifactId&gt;commons-fileupload&lt;/artifactId&gt;
            &lt;version&gt;1.4&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.imgscalr/imgscalr-lib --&gt;
        &lt;!-- 썸네일 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.imgscalr&lt;/groupId&gt;
            &lt;artifactId&gt;imgscalr-lib&lt;/artifactId&gt;
            &lt;version&gt;4.2&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;net.coobird&lt;/groupId&gt;
            &lt;artifactId&gt;thumbnailator&lt;/artifactId&gt;
            &lt;version&gt;0.4.16&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- 파일 업로드 라이브러리 끝 --&gt;

        &lt;!-- json 데이터 바인딩을 위한 의존 라이브러리 --&gt;
        &lt;!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;
            &lt;artifactId&gt;jackson-databind&lt;/artifactId&gt;
            &lt;version&gt;2.13.1&lt;/version&gt;
        &lt;/dependency&gt;

    &lt;/dependencies&gt;
    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;artifactId&gt;maven-eclipse-plugin&lt;/artifactId&gt;
                &lt;version&gt;2.9&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;additionalProjectnatures&gt;
                        &lt;projectnature&gt;org.springframework.ide.eclipse.core.springnature&lt;/projectnature&gt;
                    &lt;/additionalProjectnatures&gt;
                    &lt;additionalBuildcommands&gt;
                        &lt;buildcommand&gt;org.springframework.ide.eclipse.core.springbuilder&lt;/buildcommand&gt;
                    &lt;/additionalBuildcommands&gt;
                    &lt;downloadSources&gt;true&lt;/downloadSources&gt;
                    &lt;downloadJavadocs&gt;true&lt;/downloadJavadocs&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;version&gt;2.5.1&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;source&gt;1.6&lt;/source&gt;
                    &lt;target&gt;1.6&lt;/target&gt;
                    &lt;compilerArgument&gt;-Xlint:all&lt;/compilerArgument&gt;
                    &lt;showWarnings&gt;true&lt;/showWarnings&gt;
                    &lt;showDeprecation&gt;true&lt;/showDeprecation&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt;
                &lt;artifactId&gt;exec-maven-plugin&lt;/artifactId&gt;
                &lt;version&gt;1.2.1&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;mainClass&gt;org.test.int1.Main&lt;/mainClass&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;
</code></pre><h3 id="2-webxml에-servlet-버전-업그레이드">2. web.xml에 servlet 버전 업그레이드</h3>
<p>1) 구글에서 web.xml web-app 3.1검색
<a href="https://antop.tistory.com/entry/webxml-%EC%84%9C%EB%B8%94%EB%A6%BF-%EB%B2%84%EC%A0%84%EB%B3%84-DTD">https://antop.tistory.com/entry/webxml-%EC%84%9C%EB%B8%94%EB%A6%BF-%EB%B2%84%EC%A0%84%EB%B3%84-DTD</a> 에서 servlet 3.1 코드 복사</p>
<p>(1)web.xml에서 아래와 같이 바꾸기</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;web-app xmlns=&quot;http://xmlns.jcp.org/xml/ns/javaee&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd&quot;
    version=&quot;3.1&quot;&gt;</code></pre><p>2)&lt;servlet&gt; 안에 아래 코드 추가</p>
<pre><code>        &lt;!-- 파일업로드 설정 시작 --&gt;
        &lt;multipart-config&gt;
            &lt;location&gt;C:\\upload&lt;/location&gt; &lt;!-- 업로드 되는 파일을 저장할 공간 --&gt;
            &lt;max-file-size&gt;20971520&lt;/max-file-size&gt;&lt;!-- 업로드 최대크기 1MB * 20 (= 20MB)으로 지정 --&gt;
            &lt;max-request-size&gt;41943040&lt;/max-request-size&gt;&lt;!-- 한번에 올릴 수 있는 최대 크기 1MB * 40 (= 40MB)으로 지정--&gt;
            &lt;file-size-threshold&gt;20971520&lt;/file-size-threshold&gt;&lt;!-- 메모리 사용 1MB * 20 (= 20MB)으로 지정  --&gt;
        &lt;/multipart-config&gt;
        &lt;!-- 파일업로드 설정 끝 --&gt;</code></pre><h4 id="webxml">web.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;web-app xmlns=&quot;http://xmlns.jcp.org/xml/ns/javaee&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd&quot;
    version=&quot;3.1&quot;&gt;

    &lt;!-- The definition of the Root Spring Container shared by all Servlets and Filters --&gt;
    &lt;context-param&gt;
        &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
        &lt;param-value&gt;/WEB-INF/spring/root-context.xml&lt;/param-value&gt;
    &lt;/context-param&gt;

    &lt;!-- Creates the Spring Container shared by all Servlets and Filters --&gt;
    &lt;listener&gt;
        &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
    &lt;/listener&gt;

    &lt;!-- Processes application requests --&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;appServlet&lt;/servlet-name&gt;
        &lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&lt;/servlet-class&gt;
        &lt;init-param&gt;
            &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
            &lt;param-value&gt;/WEB-INF/spring/appServlet/servlet-context.xml&lt;/param-value&gt;
        &lt;/init-param&gt;
        &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
        &lt;!-- web.xml 설정 : Tomcat 자체 설정 --&gt;
        &lt;!-- 파일업로드 설정 시작 --&gt;
        &lt;multipart-config&gt;
            &lt;location&gt;C:\\upload&lt;/location&gt; &lt;!-- 업로드 되는 파일을 저장할 공간 --&gt;
            &lt;max-file-size&gt;20971520&lt;/max-file-size&gt;&lt;!-- 업로드 최대크기 1MB * 20 (= 20MB)으로 지정 --&gt;
            &lt;max-request-size&gt;41943040&lt;/max-request-size&gt;&lt;!-- 한번에 올릴 수 있는 최대 크기 1MB * 40 (= 40MB)으로 지정--&gt;
            &lt;file-size-threshold&gt;20971520&lt;/file-size-threshold&gt;&lt;!-- 메모리 사용 1MB * 20 (= 20MB)으로 지정  --&gt;
        &lt;/multipart-config&gt;
        &lt;!-- 파일업로드 설정 끝 --&gt;

    &lt;/servlet&gt;

    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;appServlet&lt;/servlet-name&gt;
        &lt;url-pattern&gt;/&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;

    &lt;!-- 한글설정 --&gt;
    &lt;filter&gt;
        &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;
        &lt;filter-class&gt;
            org.springframework.web.filter.CharacterEncodingFilter
        &lt;/filter-class&gt;
        &lt;init-param&gt;
            &lt;param-name&gt;encoding&lt;/param-name&gt;
            &lt;param-value&gt;UTF-8&lt;/param-value&gt;
        &lt;/init-param&gt;
        &lt;init-param&gt;
            &lt;param-name&gt;forceEncoding&lt;/param-name&gt;
            &lt;param-value&gt;true&lt;/param-value&gt;
        &lt;/init-param&gt;
    &lt;/filter&gt;
    &lt;filter-mapping&gt;
        &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;
        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;
    &lt;!-- 한글설정 END --&gt;

&lt;/web-app&gt;
</code></pre><h3 id="3-sevlet-contextxml-수정">3. sevlet-context.xml 수정</h3>
<p>1) 아래 코드 추가하기</p>
<p>view단에서 multipart/form-data 방식으로 서버에 전송되는 데이터를 스프링 MVC의 multipartResolver로 처리할 수 있다.</p>
<pre><code>    &lt;!-- 첨부파일을 처리하는 빈 설정 --&gt;
    &lt;beans:bean id=&quot;multipartResolver&quot; class=&quot;org.springframework.web.multipart.support.StandardServletMultipartResolver&quot;&gt;

    &lt;/beans:bean&gt;</code></pre><h4 id="servlet-contextxml">servlet-context.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/mvc&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd&quot;&gt;

    &lt;!-- DispatcherServlet Context: defines this servlet&#39;s request-processing infrastructure --&gt;

    &lt;!-- Enables the Spring MVC @Controller programming model --&gt;
    &lt;annotation-driven /&gt;

    &lt;!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --&gt;
    &lt;!-- 정적 폴더  -&gt; images, js, css와 같은 폴더들이 resources 밑에 들어감--&gt;
    &lt;resources mapping=&quot;/resources/**&quot; location=&quot;/resources/&quot; /&gt;
    &lt;!-- 컨트롤러에서 return되는 string값(파일명) 앞 뒤로 조립되는 값 --&gt;
    &lt;!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --&gt;
    &lt;!--  prefix는 앞, suffix는 뒤에 붙는다 (ViewResolver가 조립해준다)--&gt;
    &lt;beans:bean class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&gt;
        &lt;beans:property name=&quot;prefix&quot; value=&quot;/WEB-INF/views/&quot; /&gt;
        &lt;beans:property name=&quot;suffix&quot; value=&quot;.jsp&quot; /&gt;
    &lt;/beans:bean&gt;

    &lt;context:component-scan base-package=&quot;kr.or.ddit&quot; /&gt;

    &lt;!-- 첨부파일을 처리하는 빈 설정 --&gt;
    &lt;beans:bean id=&quot;multipartResolver&quot; class=&quot;org.springframework.web.multipart.support.StandardServletMultipartResolver&quot;&gt;

    &lt;/beans:bean&gt;

&lt;/beans:beans&gt;
</code></pre><h3 id="4-contextxml-수정">4. context.xml 수정</h3>
<p>1) 아래 코드 추가하기</p>
<pre><code>&lt;Context allowCasualMultipartParsing=&quot;true&quot; path=&quot;/&quot;&gt;
    &lt;Resources cashingAllowed=&quot;true&quot; cacheMaxSize=&quot;100000&quot; /&gt;</code></pre><p><strong>context.xml</strong></p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the &quot;License&quot;); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
--&gt;&lt;!-- The contents of this file will be loaded for each web application --&gt;
&lt;Context allowCasualMultipartParsing=&quot;true&quot; path=&quot;/&quot;&gt;
    &lt;Resources cashingAllowed=&quot;true&quot; cacheMaxSize=&quot;100000&quot; /&gt;

    &lt;!-- Default set of monitored resources. If one of these changes, the    --&gt;
    &lt;!-- web application will be reloaded.                                   --&gt;
    &lt;WatchedResource&gt;WEB-INF/web.xml&lt;/WatchedResource&gt;
    &lt;WatchedResource&gt;${catalina.base}/conf/web.xml&lt;/WatchedResource&gt;

    &lt;!-- Uncomment this to disable session persistence across Tomcat restarts --&gt;
    &lt;!--
    &lt;Manager pathname=&quot;&quot; /&gt;
    --&gt;
&lt;/Context&gt;</code></pre><p><strong>서버에 파일 저장 시 고려할 사항</strong></p>
<ol>
<li>파일 업로드 방식 (Post? Ajax?)</li>
<li>파일 이름 중복으로 인한 덮어쓰기 문제 -&gt; UUID로 해결</li>
<li>파일 저장 경로</li>
</ol>
<p>-&gt; 폴더 내 파일 개수가 너무 많아지면 속도가 저하되므로,
업로드 되는 시점별로 폴더를 생성하여 파일을 관리한다.
4. 이미지 파일인 경우 썸네일 생성하기
-&gt; 이미지 파일은 저장된 파일을 다시 화면에 보여줄 때, 썸네일 파일을 보여주게 된다.
따라서 이미지 파일을 서버에 저장 시 추가적으로 그 이미지 파일의 썸네일 파일을 생성해주어야 한다.
-&gt; 처음에 추가했던 imgsclar-lib 라이브러리가 이미지 썸네일 생성을 해준다. </p>
<h2 id="예제">예제</h2>
<h3 id="1-post-방식">1. Post 방식</h3>
<h4 id="1-uploadformjsp">1) uploadForm.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;파일 업로드 테스트&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- 여러 파일 업로드 시 enctype=&quot;multipart-form-data&quot; 필수! --&gt;
&lt;form action=&quot;/uploadTest/uploadFormAction&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
    &lt;input type=&quot;file&quot; name=&quot;uploadFile&quot; multiple /&gt;
    &lt;button&gt;Submit&lt;/button&gt;
&lt;/form&gt;

&lt;/body&gt;
&lt;html&gt;

</code></pre><h4 id="2-uploadcontrollerjava">2) UploadController.java</h4>
<pre><code>package kr.or.ddit;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import net.coobird.thumbnailator.Thumbnailator;

@RequestMapping(&quot;/uploadTest&quot;)
@Controller
public class UploadController {
    private static final Logger logger = LoggerFactory.getLogger(UploadController.class);

    //jsp의 &lt;input name=&quot;uploadFile&quot;과 MultipartFile[]의 uploadFile 이름이 같아야 함
    //MultipartFile[] --&gt; 여러개의 파일을 올리기 위한 배열, jsp에 enctype=&quot;multipart/form-data&quot; 필수
    @PostMapping(&quot;/uploadFormAction&quot;)
    public String uploadFormPost(MultipartFile[] uploadFile, Model model) {

        //파일 저장경로 설정
        String uploadFolder = &quot;D:\\A_TeachingMaterial\\6.JspSpring\\workspace\\springProj\\src\\main\\webapp\\resources\\upload&quot;;

        //아직 db연동 안해서 list로 한다
        List&lt;String&gt; list = new ArrayList&lt;String&gt;();

        for(MultipartFile multipartFile : uploadFile) {
            logger.info(&quot;---------------&quot;);
            logger.info(&quot;파일명 : &quot; + multipartFile.getOriginalFilename());
            logger.info(&quot;파일 크기 : &quot; + multipartFile.getSize());

            //&quot;c:\\upload\\비숑.jpg&quot; 으로 조립
            //이렇게 업로드 하겠다 라고 설계
            File saveFile = new File(uploardFolder, multipartFile.getOriginalFilename());

            try{
                //transfer하기 전에 파일 실제 명을 list에 담음
                list.add(multipartFile.getOriginalFilename());
                //transferTo() : 물리적으로 파일 업로드가 되는 시점이 여기!
                multipartFile.transferTo(saveFile);
            }catch(Exception e){
                logger.info(e.getMessage());
            }//end catch
        }//end for

        //forwarding 하기 전 model에 list를 담는다.
        // 이 list에는 파일명들이 들어 있다.

        //forward
        return &quot;uploadTest/uploadSuccess&quot;;
    }
}</code></pre><h4 id="3-uploadsuccessjsp">3) uploadSuccess.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;업로드 성공&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
업로드 성공! &lt;br&gt;
&lt;c:forEach var=&quot;img&quot; items=&quot;${list}&quot;&gt;
    &lt;img src=&quot;/resources/upload/${img}&quot; /&gt;
&lt;/c:forEach&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="2-ajax-방식">2. Ajax 방식</h3>
<h4 id="1uploadajaxjsp">1)uploadAjax.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/resources/js/jquery-3.6.0.js&quot;&gt;&lt;/script&gt;
&lt;title&gt;Upload with Ajax&lt;/title&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
//파일을 담아두는 변수
var sel_file = [];

$(function(){
    //------------ 이미지 미리보기 시작 ---------------
    $(&quot;input_img&quot;).on(&quot;change&quot;, handleImgFileSelect);

    function handleImgFileSelect(e){
        //e.target : 파일 객체
        //e.target.files : 파일 객체 안의 파일들
        var files = e.target.files;
        //파일을 잘라서 array로 만든다!
        var filesArr = Array.prototypes.slice.call(files);

        //f: 파일 객체
        filesArr.forEach(function(f){
            //미리보기는 이미지만 가능
            if(!f.type.match(&quot;image.*&quot;)){
                alert(&quot;이미지만 가능합니다.&quot;);
                return;
            }

            //파일 객체 복사
            sel_file.push(f);

            //파일을 읽어주는 객체를 생성한다.
            var reader = new FileReader();
            reader.onload = function(e) {
                //하나라면
                //$(&quot;#img&quot;).attr(&quot;src&quot;, e.target.result);

                //복수라면
                //forEach 반복하면서 img 객체 생성하기
                var img_html = &quot;&lt;img src=\&quot;&quot; + e.target.result + &quot;\&quot; /&gt;&quot;;
                $(&quot;.img_wrap&quot;).append(img_html);

            }
            //reader객체에 파일URL읽어서 넣는다. -&gt; 읽어와지면 onload 실행
            reader.readAsDataURL(f);

        });
        //------------ 이미지 미리보기 끝 ---------------

        //모든 파일명.확장자(exe|sh|zip|alz)는 업로드를 못하도록 막는다.
        //첨부파일의 확장자가 exe, sh, zip, alz인 경우 업로드를 제한
        var regex = new RegExp(&quot;(.*?)\.(exe|sh|zip|alz)&quot;);

        //최대 5MB까지만 업로드 가능
        var maxSize = 5242880; //5MB
        //확장자, 크기 체크
        function checkExtension(fileName, fileSize){
            if(fileSize &gt;= maxSize){
                alert(&quot;파일 사이즈 초과&quot;);
                return false;
            }

            if(regex.test(fileName)){
                alert(&quot;해당 종류의 파일은 업로드할 수 없습니다.&quot;);
                return false;
            }
            //체크 통과
            return true;
        }

        //Upload 클릭시 이벤트 발생시키기!
        $(&quot;upload&quot;).on(&quot;click&quot;, function(e){
            //잘 넘어오는지 확인
            //alert(&quot;왔네!&quot;);

            //FormData : 가상의 &lt;form&gt;태그
            //Ajax를 이용하는 파일 업로드는 FormData를 이용해서
            var formData = new FormData();

            //&lt;input type=&quot;file&quot; name=&quot;uploadFile&quot; multiple /&gt; 찾아가기
            var inputFile = $(&quot;input[name=&#39;uploadFile&#39;]&quot;);
            //&lt;input type=&quot;file&quot; 요소 내의 이미지들
            var files = inputFile[0].files;

            //console에 files안의 파일 이름 찍어보기
            for(var i=0; i&lt;files.length; i++){
                console.log(files[i]);
                //확장자, 크기 체크
                //function checkExtension(fileName, fileSize){
                if(!checkExtension(files[i].name, files[i].size) {
                    return false; //!true -&gt; 실패
                }
                formData.append(&quot;uploadFile&quot;, files[i]);
            }
            //없어? 카드가 또?
            //processData, contentType은 반드시 false여야 전송됨
            $.ajax({
                url : &#39;uploadTest/uploadAjaxAction&#39;,
                processData : false,
                contentType : false,
                data : formData,
                type : &#39;POST&#39;,
                success : function(result){
                    //JSON을 string으로 변환해서 console에 찍어본다
                    console.log(&quot;result : &quot; + JSON.stringlyfy(result));
                }
            });
        });
});

&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Upload with Ajax&lt;/h1&gt;

&lt;div class=&quot;uploadDiv&quot;&gt;
    &lt;input type=&quot;file&quot; id=&quot;input_img&quot; name=&quot;uploadFile&quot; multiple /&gt;
&lt;/div&gt;

&lt;button id=&quot;uploadBtn&quot;&gt;Upload&lt;/button&gt;
&lt;div class=&quot;img_wrap&quot;&gt;

&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="2-uploadcontrollerjava-1">2) UploadController.java</h4>
<p>@ResponseBody
: JSON 객체 타입의 데이터를 만들어서 반환하는 용도로 사용됨
: JSON? {&quot;id&quot;:&quot;a001&quot;, &quot;name&quot;:&quot;개똥이&quot;}</p>
<pre><code>//&lt;form&gt;태그를 이용하던 방식과 동일한 방식으로 처리됨
//Ajax 방식으로 결과 데이터를 전달하므로 Model을 사용하지 않는다.

@ResponseBody
@PostMapping(&quot;uploadAjaxAction&quot;)
public List&lt;AttachFileVO&gt; uploadAjaxAction(MultipartFile[] uploadFile) {

    String uploadFolder = &quot;D:\\A_TeachingMaterial\\6.JspSpring\\workspace\\springProj\\src\\main\\webapp\\resources\\upload&quot;;

    //연/월/일 폴더 생성 시작 ---------
    File uploadPath = new File(uploadFolder, getFolder());
    logger.info(&quot;uploadPath : &quot; + uploadPath);

    if(uploadPath.exists()==false) { //해당 경로가 없으면 생성해줘야 함
        uploadPath.mkdirs();
    }
    //연/월/일 폴더 생성 끝 ---------   

    //첨부된 파일의 이름을 담을 List
    List&lt;AttachFileVO&gt; list = new ArrayList&lt;AttachFileVO&gt;();

        for(MultipartFile multipartFile : uploadFile) {
            logger.info(&quot;-----------&quot;);
            logger.info(&quot;파일명 : &quot; + multipartFile.getOriginalFilename());
            logger.info(&quot;파일크기 : &quot; + multipartFile.getSize());

            AttachFileVO attachFileVO = new AttachFileVO();
            //1) fileName 세팅
            attachFileVO.setFileName(multipartFile.getOriginalFilename());

            //-------------------UUID 파일명 처리 시작 -----------------
            //UUID : 동일한 이름으로 업로드되면 기존 파일을 지우게 되므로 이를 방지하기 위함
            UUID uuid = UUID.randomUUID();

            String uploadFileName = uuid.toString() + &quot;-&quot; + multipartFile.getOriginalFilename();
            // c:\\upload\\gongu03.jpg으로 조립
            // 이렇게 업로드 하겠다라고 설계 uploadFolder -&gt; uploadPath
            File saveFile = new File(uploadPath,uploadFileName);
            //-------------------UUID 파일명 처리 끝 -----------------
             try {
                //transferTo() : 물리적으로 파일 업로드가 됨
                multipartFile.transferTo(savaFile);

                //2) uploadPath
                attachFileVO.setUploadPath(uploadPath.getPath());
                //3) UUID
                attachFileVO.setUuid(uuid.toString());
                //--------------------썸네일 처리 시작 -------------------
                //이미지 파일인지 체킹
                if(checkImageType(saveFile)) {
                    logger.info(&quot;이미지 파일? true&quot;);
                //4) image여부
                attachFileVO.setImage(true);
                //uploadPath : 연/월/일이 포함된 경로
                //uploadFileName : UUID가 포함된 파일명
                FileOutPutStream thumbnail = new FileOutputStream(new File(uploadPath, &quot;s_&quot;+uploadFileName));
                Thumbnailator.createThumbnail(multipartFile.getInputStream(), thumbnail, 100, 100); //사이즈
                thumbnail.close();
                }else {
                    logger.info(&quot;이미지 파일? false&quot;);
                }
            //--------------------썸네일 처리 끝 -------------------

            //파일의 실제 명을 list에 담는다.
            list.add(attachFileVO);
             }catch(Exception e) {
                logger.info(e.getMessage());
            }//end catch
        }//end for

        return list;
}//end uploadAjaxAction

//첨부파일을 보관하는 폴더를 연/월/일 계층 형태로 생성
private String getFolder() {
    SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd&quot;);
    Date date = new Date();
    String str = sdf.format(date);
    return str.replace(&quot;-&quot;, File.separator);
}

//특정한 파일이 이미지 타입지 검사해주는 메소드
private boolean checkImageType(File file) {
    try {
         //file.toPath() : 파일의 전체 경로
        logger.info(&quot;file.toPath() : &quot; + file.toPath());
        String contentType = Files.probeContentType(file.toPath());
        logger.info(&quot;contentType : &quot; + contentType);
        //contentType이 image로 시작하면 이미지 타입이므로 true를 리턴함
        return contentType.startsWith(&quot;image&quot;);
    }catch(IOException e) {
        e.printStackTrace();
    }
    return false;

    }
}
</code></pre><ul>
<li>VO -&gt; JSON
{id&quot;:&quot;a001&quot;, &quot;name&quot;:&quot;개똥이&quot;}</li>
<li>List&lt;VO&gt; -&gt; JSON
[{id&quot;:&quot;a001&quot;, &quot;name&quot;:&quot;개똥이&quot;},
 {id&quot;:&quot;b001&quot;, &quot;name&quot;:&quot;뿡뿡이&quot;}]</li>
<li>Map&lt;String, Object&gt; -&gt; JSON
{&quot;key1&quot;:{id&quot;:&quot;a001&quot;, &quot;name&quot;:&quot;개똥이&quot;},
&quot;key2&quot;:{id&quot;:&quot;b001&quot;, &quot;name&quot;:&quot;뿡뿡이&quot;}}</li>
<li>String -&gt; JSON
a001, b001</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 26강 - MyBatis 동적 SQL 정리,
입력값 검증(Validate)]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-26-%EC%9E%85%EB%A0%A5%EA%B0%92-%EA%B2%80%EC%A6%9DValidate</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-26-%EC%9E%85%EB%A0%A5%EA%B0%92-%EA%B2%80%EC%A6%9DValidate</guid>
            <pubDate>Sun, 13 Feb 2022 09:25:27 GMT</pubDate>
            <description><![CDATA[<p><strong>계층형 쿼리</strong></p>
<ul>
<li>서브쿼리 구분 : 위치에 따라
1) select : scalar 서브쿼리
2) where : nested(중첩된) 서브쿼리
3) from : inline 서브쿼리</li>
</ul>
<p><strong>MyBatis 동적 SQL 정리</strong>
MyBatis는 동적 SQL을 조립하는 구조를 지원해줌
SQL 조립 규칙을 매퍼 XML 파일에 정의할 수 있음</p>
<p>-&lt;where&gt; : where절 앞뒤에 내용을 더 추가하거나 삭제할 때 사용
-&lt;if&gt; : 조건 만족 시 SQL을 조립
-&lt;choose&gt; : 여러 선택 항목에서 조건 만족 시 SQL을 조립
-&lt;foreach&gt; : 컬렉션이나 배열에 대해 반복 처리
-&lt;set&gt; : UPDATE절의 SET절 앞 뒤에 내용을 더 추가하거나 삭제 시 사용</p>
<h2 id="hibernate-validator-설치">Hibernate-validator 설치</h2>
<p>입력값 검증을 위한 라이브러리</p>
<p>1) <a href="https://mvnrepository.com/">https://mvnrepository.com/</a> 에서 hibernate-validator 검색 -&gt; 
Home » org.hibernate » hibernate-validator 클릭</p>
<p>2) 5.4.2.Final 버전 클릭 -&gt; 중간 코드 복사
-&gt; pom.xml에 붙여넣기</p>
<p>3) 프로젝트 우클릭 - Run As - 4번 클릭
-&gt; build Success 나오면 설치 성공</p>
<h3 id="입력값-검증validate">입력값 검증(validate)</h3>
<p>스프링 MVC 프레임워크에서 Bean(자바빈 클래스, VO) 기능을 이용하여 요청 파라미터 값이 바인딩(=메모리에 올라감) 된 도메인 클래스(=메모리에 올라간 VO객체)의 입력값 검증을 함</p>
<h3 id="입력값-검증-절차">입력값 검증 절차</h3>
<ul>
<li>메서드 매개변수에 도메인 클래스를 정의하고 @Validated를 지정함</li>
<li>입력값 검증 대상의 도메인 클래스 직후에 BindingResult를 정의</li>
<li>BindingResult에는 요청 데이터의 바인딩 오류와 입력값 검증 오류 정보가 저장됨</li>
<li>forwarding을 통해 뷰(jsp)와 오류 데이터를 공유 -&gt; redirect 방식으로는 jsp에 오류정보를 보낼 수 없다!</li>
</ul>
<h3 id="입력값-검증-결과bindingresult">입력값 검증 결과(BindingResult)</h3>
<ul>
<li>hasErrors() : 오류가 발생한 경우 true를 반환</li>
<li>getGlobalErrors() : 객체 레벨의 오류</li>
<li>getFieldErrors() : 필드(멤버변수) 레벨의 오류</li>
</ul>
<h3 id="입력값-검증-규칙">입력값 검증 규칙</h3>
<ul>
<li>@NotBlank : 문자열이 null이 아니고 trim한 길이가 0보다 크다는 것을 검사</li>
<li>@NotNull : 빈 값이 아닌지 검사</li>
<li>@Email : 이메일 주소 형식인지 검사</li>
<li>@Size : 글자수 검사</li>
<li>@Past : 과거 날짜인지 검사(오늘 기준으로 과거 날짜만 가능)</li>
<li>@Future : 미래 날짜인지 검사(오늘 기준으로 미래 날짜만 가능)</li>
</ul>
<p><strong>예시) detail.jsp에서 수정 시 입력값 검증하기</strong></p>
<h2 id="1vo">1.VO</h2>
<h4 id="buyervojava">buyerVO.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

//자바빈 클래스
public class BuyerVO {
    //멤버변수
    private String buyerId;
    private String buyerName;
    private String buyerLgu;
    private String buyerBank;
    private String buyerBankno;
    private String buyerBankname;
    private String buyerZip;
    private String buyerAdd1;
    private String buyerAdd2;
    @NotEmpty
    private String buyerComtel;
    private String buyerFax;
    @NotEmpty
    @Email
    private String buyerMail;
    @NotEmpty
    private String buyerCharger;
    private String buyerTelext;
    //상품분류명
    private String lprodNm;


    //Buyer : ProdVO = 1: N 관계
    private List&lt;ProdVO&gt; ProdVO;

    //getter/setter 메소드
    public String getBuyerId() {
        return buyerId;
    }
    public void setBuyerId(String buyerId) {
        this.buyerId = buyerId;
    }
    public String getBuyerName() {
        return buyerName;
    }
    public void setBuyerName(String buyerName) {
        this.buyerName = buyerName;
    }
    public String getBuyerLgu() {
        return buyerLgu;
    }
    public void setBuyerLgu(String buyerLgu) {
        this.buyerLgu = buyerLgu;
    }
    public String getBuyerBank() {
        return buyerBank;
    }
    public void setBuyerBank(String buyerBank) {
        this.buyerBank = buyerBank;
    }
    public String getBuyerBankno() {
        return buyerBankno;
    }
    public void setBuyerBankno(String buyerBankno) {
        this.buyerBankno = buyerBankno;
    }
    public String getBuyerBankname() {
        return buyerBankname;
    }
    public void setBuyerBankname(String buyerBankname) {
        this.buyerBankname = buyerBankname;
    }
    public String getBuyerZip() {
        return buyerZip;
    }
    public void setBuyerZip(String buyerZip) {
        this.buyerZip = buyerZip;
    }
    public String getBuyerAdd1() {
        return buyerAdd1;
    }
    public void setBuyerAdd1(String buyerAdd1) {
        this.buyerAdd1 = buyerAdd1;
    }
    public String getBuyerAdd2() {
        return buyerAdd2;
    }
    public void setBuyerAdd2(String buyerAdd2) {
        this.buyerAdd2 = buyerAdd2;
    }
    public String getBuyerComtel() {
        return buyerComtel;
    }
    public void setBuyerComtel(String buyerComtel) {
        this.buyerComtel = buyerComtel;
    }
    public String getBuyerFax() {
        return buyerFax;
    }
    public void setBuyerFax(String buyerFax) {
        this.buyerFax = buyerFax;
    }
    public String getBuyerMail() {
        return buyerMail;
    }
    public void setBuyerMail(String buyerMail) {
        this.buyerMail = buyerMail;
    }
    public String getBuyerCharger() {
        return buyerCharger;
    }
    public void setBuyerCharger(String buyerCharger) {
        this.buyerCharger = buyerCharger;
    }
    public String getBuyerTelext() {
        return buyerTelext;
    }
    public void setBuyerTelext(String buyerTelext) {
        this.buyerTelext = buyerTelext;
    }

    public List&lt;ProdVO&gt; getProdVO() {
        return ProdVO;
    }
    public void setProdVO(List&lt;ProdVO&gt; prodVO) {
        ProdVO = prodVO;
    }
    public String getLprodNm() {
        return lprodNm;
    }
    public void setLprodNm(String lprodNm) {
        this.lprodNm = lprodNm;
    }
    @Override
    public String toString() {
        return &quot;BuyerVO [buyerId=&quot; + buyerId + &quot;, buyerName=&quot; + buyerName + &quot;, buyerLgu=&quot; + buyerLgu + &quot;, buyerBank=&quot;
                + buyerBank + &quot;, buyerBankno=&quot; + buyerBankno + &quot;, buyerBankname=&quot; + buyerBankname + &quot;, buyerZip=&quot;
                + buyerZip + &quot;, buyerAdd1=&quot; + buyerAdd1 + &quot;, buyerAdd2=&quot; + buyerAdd2 + &quot;, buyerComtel=&quot; + buyerComtel
                + &quot;, buyerFax=&quot; + buyerFax + &quot;, buyerMail=&quot; + buyerMail + &quot;, buyerCharger=&quot; + buyerCharger
                + &quot;, buyerTelext=&quot; + buyerTelext + &quot;, lprodNm=&quot; + lprodNm + &quot;, ProdVO=&quot; + ProdVO + &quot;]&quot;;
    }


}
</code></pre><h2 id="2xml">2.xml</h2>
<h4 id="lprod_sqlxml">lprod_SQL.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE mapper
  PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
  &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;
&lt;mapper namespace=&quot;lprod&quot;&gt;
    &lt;resultMap type=&quot;lprodVO&quot; id=&quot;lprodMap&quot;&gt;
        &lt;id property=&quot;lprodGu&quot; column=&quot;LPROD_GU&quot; /&gt;
        &lt;result property=&quot;rnum&quot; column=&quot;RNUM&quot; /&gt;
        &lt;result property=&quot;lprodNm&quot; column=&quot;LPROD_NM&quot; /&gt;
        &lt;collection property=&quot;buyerVO&quot; resultMap=&quot;buyerMap&quot;&gt;
        &lt;/collection&gt;
    &lt;/resultMap&gt;

    &lt;resultMap type=&quot;buyerVO&quot; id=&quot;buyerMap&quot;&gt;
        &lt;result property=&quot;buyerId&quot; column=&quot;BUYER_ID&quot;/&gt;
        &lt;result property=&quot;buyerName&quot; column=&quot;BUYER_NAME&quot;/&gt;
        &lt;result property=&quot;buyerLgu&quot; column=&quot;BUYER_LGU&quot;/&gt;
    &lt;/resultMap&gt;

&lt;!-- 상품분류 별 거래처 목록 --&gt;
&lt;!-- 
parameter : 
    {keyWord=캐주,currentPage=1,size=10}
--&gt;
    &lt;select id=&quot;list&quot; parameterType=&quot;hashMap&quot; resultMap=&quot;lprodMap&quot;&gt;
        SELECT ROWNUM RNUM, T.LPROD_GU, T.LPROD_NM
                         , T.BUYER_ID, T.BUYER_NAME, T.BUYER_LGU
          FROM
          (
               SELECT ROW_NUMBER() OVER(ORDER BY LPROD_NM, BUYER_ID) RNUM
                   , L.LPROD_GU
                   , L.LPROD_NM
                   , B.BUYER_ID
                   , B.BUYER_NAME
                   , B.BUYER_LGU
              FROM   LPROD L INNER JOIN BUYER B
              ON(L.LPROD_GU = B.BUYER_LGU)
             WHERE 1 = 1
             &lt;if test=&quot;keyWord!=null and keyWord!=&#39;&#39;&quot;&gt;
                AND (L.LPROD_GU LIKE &#39;%&#39;||#{keyWord}||&#39;%&#39; 
                OR     L.LPROD_NM LIKE &#39;%&#39;||#{keyWord}||&#39;%&#39; 
                OR     B.BUYER_ID LIKE &#39;%&#39;||#{keyWord}||&#39;%&#39; 
                OR     B.BUYER_NAME LIKE &#39;%&#39;||#{keyWord}||&#39;%&#39;)
              &lt;/if&gt; 
          ) T
          WHERE T.RNUM BETWEEN #{currentPage}*7-6 AND #{currentPage}*7
    &lt;/select&gt;

    &lt;!-- 상품분류 별 거래처 목록의 totalCount --&gt;
    &lt;select id=&quot;listCount&quot; parameterType=&quot;hashMap&quot; resultType=&quot;int&quot;&gt;
        SELECT COUNT(*)
        FROM LPROD L INNER JOIN BUYER B
        ON(L.LPROD_GU = B.BUYER_LGU)
        WHERE 1 = 1
        &lt;if test=&quot;keyWord!=null and keyWord!=&#39;&#39;&quot;&gt;
           AND (L.LPROD_GU LIKE &#39;%&#39;||#{keyWord}||&#39;%&#39; 
           OR     L.LPROD_NM LIKE &#39;%&#39;||#{keyWord}||&#39;%&#39; 
           OR     B.BUYER_ID LIKE &#39;%&#39;||#{keyWord}||&#39;%&#39; 
           OR     B.BUYER_NAME LIKE &#39;%&#39;||#{keyWord}||&#39;%&#39;)
        &lt;/if&gt;         
    &lt;/select&gt;

    &lt;!-- 거래처 상세 정보 --&gt;
    &lt;select id=&quot;detail&quot; parameterType=&quot;String&quot; resultType=&quot;buyerVO&quot;&gt;
    SELECT (SELECT L.LPROD_NM FROM LPROD L WHERE L.LPROD_GU = B.BUYER_LGU) LPROD_NM
           , B.BUYER_ID
           , B.BUYER_NAME
           , B.BUYER_LGU
           , BUYER_BANK     
           , BUYER_BANKNO
           , BUYER_BANKNAME
           , BUYER_ZIP
           , BUYER_ADD1
           , BUYER_ADD2
           , BUYER_COMTEL
           , BUYER_FAX
           , BUYER_MAIL
           , BUYER_CHARGER
           , BUYER_TELEXT
      FROM   BUYER B
      WHERE B.BUYER_ID = #{buyerId}
    &lt;/select&gt;


&lt;/mapper&gt;</code></pre><h2 id="3-dao">3. Dao</h2>
<h4 id="lproddaojava">LprodDao.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;
import java.util.Map;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class LprodDao {

    @Autowired
    SqlSessionTemplate sqlSessionTemplate;

    public List&lt;LprodVO&gt; list(Map&lt;String, Object&gt; map){
        return this.sqlSessionTemplate.selectList(&quot;lprod.list&quot;, map);
    }
    //상품분류 별 거래처 목록 행의 수
    public int listCount(Map&lt;String, Object&gt; map) {
        return this.sqlSessionTemplate.selectOne(&quot;lprod.listCount&quot;, map);
    }
    //거래처 상세 정보
    public BuyerVO detail(String buyerId) {
        return this.sqlSessionTemplate.selectOne(&quot;lprod.detail&quot;, buyerId);
    }



}
</code></pre><h2 id="4-service">4. Service</h2>
<h4 id="lprodservicejava">LprodService.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;
import java.util.Map;

public interface LprodService {

    //메소드 시그니처 처리
    //상품분류 별 거래처 목록
    public List&lt;LprodVO&gt; list(Map&lt;String, Object&gt; map);
    //상품분류 별 거래처 목록 행의 수
    public int listCount(Map&lt;String, Object&gt; map);
    //거래처 상세 정보
    BuyerVO detail(String buyerId);


}
</code></pre><h4 id="lprodserviceimpljava">LprodServiceImpl.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LprodServiceImpl implements LprodService {
    @Autowired
    LprodDao lprodDao;

    @Override
    public List&lt;LprodVO&gt; list(Map&lt;String, Object&gt; map){
        return this.lprodDao.list(map);
    }

    //상품분류 별 거래처 목록 행의 수
    @Override
    public int listCount(Map&lt;String, Object&gt; map) {
        return this.lprodDao.listCount(map);
    }

    //거래처 상세 정보
    @Override
    public BuyerVO detail(String buyerId) {
        return this.lprodDao.detail(buyerId);
    }
}
</code></pre><p>@RequestMapping 정리</p>
<ul>
<li>요청 경로는 필수!</li>
<li>속성이 하나일 때는 속성명 생략 가능</li>
<li>ex) @RequestMapping(value=&quot;detail&quot;, method = RequestMethod.GET) -&gt; @RequestMapping(&quot;/detail&quot;)</li>
<li>클래스 레벨과 메서드 레벨로 지정 가능</li>
<li>클래스 레벨로 요청 경로를 지정하면 메서드 레벨에서 지정한 경로의 기본 경로로 취급</li>
</ul>
<h2 id="5-controller">5. Controller</h2>
<h4 id="lprodcontrollerjava">LprodController.java</h4>
<pre><code>    ///lprod/detail?buyerId=P10101 요렇게 넘어온다
    //리턴 타입 : String =&gt; 뷰 파일의 경로와 파일 이름을 나타내기 위해 사용
    @RequestMapping(value = &quot;detail&quot;, method = RequestMethod.GET)
    public String detail(Model model, @RequestParam String buyerId) {
    logger.info(&quot;buyerId : &quot; + buyerId);

    BuyerVO buyerVO = this.lprodService.detail(buyerId);

    //buyerVO이 null이면 오류를 보자
    if(buyerVO! = null) {
        logger.info(&quot;buyerVO : &quot; + buyerVO.toString());
    }

    model.addAttribute(&quot;buyerVO&quot;, buyerVO);

    //forward
    //반환값이 lprod/detail이므로 lprod폴더의 detail.jsp를 리턴
    return &quot;/lprod/detail&quot;;

   }

    //detail.jsp에서 $(&quot;#buyerVO&quot;).submit();로 보내면 받는다!
    //수정 요청 매핑
    @RequestMapping(value = &quot;/detail&quot;, method = RequestMethod.POST)
    public String detailPost(@Validated BuyerVO buyerVO, BindingResult result) {
    logger.info(&quot;buyerVO : &quot; + buyerVO.toString());

    //validation 중에 문제가 발생된다면 true
    logger.info(&quot;result.hasErrors() : &quot; + result.hasErrors());
    //validation 문제가 발생된다면
    //allErrors = globalErrors + fieldErrors
        if(result.hasErrors()) {
            List&lt;ObjectError&gt; allErrors = result.getAllErrors();
            List&lt;ObjectError&gt; globalErrors = result.getGlobalErrors();
            List&lt;FieldError&gt; fieldErrors = result.getFieldErrors();

            //validation 중에 어떤 오류가 나왔는지 확인..
            for(int i = 0; i&lt;allErrors.size(); i++) {
                ObjectError objectError = allErrors.get(i);
                logger.info(&quot;objectError : &quot; + objectError);
            }

            for(ObjectError objectError : globalErrors) {
                logger.info(&quot;objectError : &quot; + objectError);
            }

            for(FieldError fieldError : fieldErrors) {
                logger.info(&quot;fieldError : &quot; + fieldError.getDefaultMessage());
            }
            //문제가 발생되면 수정프로세스는 중단되고 되돌아감
            return &quot;lprod/detail&quot;;
        }//end if

        //수정 비즈니스 로직 처리 후

        //상세 페이지로 되돌아감
        return &quot;redirect:/lprod/detail?buyerId=&quot;+buyerVO.getBuyerId();

    }

}
</code></pre><h2 id="6-jsp">6. JSP</h2>
<h4 id="detailjsp">detail.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ taglib prefix=&quot;form&quot; uri=&quot;http://www.springframework.org/tags/form&quot; %&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;

&lt;head&gt;

    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;meta name=&quot;description&quot; content=&quot;&quot;&gt;
    &lt;meta name=&quot;author&quot; content=&quot;&quot;&gt;

    &lt;title&gt;상품분류 별 거래처 목록&lt;/title&gt;

    &lt;!-- Custom fonts for this template--&gt;
    &lt;link href=&quot;/resources/sbadmin2/vendor/fontawesome-free/css/all.min.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;&gt;
    &lt;link
        href=&quot;https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&quot;
        rel=&quot;stylesheet&quot;&gt;

    &lt;!-- Custom styles for this template--&gt;
    &lt;link href=&quot;/resources/sbadmin2/css/sb-admin-2.min.css&quot; rel=&quot;stylesheet&quot;&gt;
    &lt;script type=&quot;text/javascript&quot;&gt;
        function fn_submit() {
            var frm = document.searchForm;
            console.log(&quot;frm.size : &quot; + frm.size.value);
            console.log(&quot;frm.keyWord : &quot; + frm.keyWord.value);
            frm.submit();
        }
    &lt;/script&gt;

&lt;/head&gt;

&lt;body id=&quot;page-top&quot;&gt;

    &lt;!-- Page Wrapper --&gt;
    &lt;div id=&quot;wrapper&quot;&gt;

        &lt;!-- Sidebar --&gt;
        &lt;jsp:include page=&quot;../includes/aside.jsp&quot;&gt;&lt;/jsp:include&gt;
        &lt;!-- End of Sidebar --&gt;

        &lt;!-- Content Wrapper --&gt;
        &lt;div id=&quot;content-wrapper&quot; class=&quot;d-flex flex-column&quot;&gt;

            &lt;!-- Main Content --&gt;
            &lt;div id=&quot;content&quot;&gt;

                &lt;!-- Topbar --&gt;
                &lt;jsp:include page=&quot;../includes/header.jsp&quot;&gt;&lt;/jsp:include&gt;
                &lt;!-- End of Topbar --&gt;

&lt;!-- Begin Page Content 본문 시작 --&gt;
&lt;div class=&quot;container-fluid&quot;&gt;
&lt;div class=&quot;card o-hidden border-0 shadow-lg my-5&quot;&gt;
       &lt;div class=&quot;card-body p-0&quot;&gt;
           &lt;!-- Nested Row within Card Body --&gt;
      &lt;div class=&quot;row&quot;&gt;
          &lt;div class=&quot;col-lg-5 d-none d-lg-block bg-register-image&quot;&gt;&lt;/div&gt;
          &lt;div class=&quot;col-lg-7&quot;&gt;
              &lt;div class=&quot;p-5&quot;&gt;
                  &lt;div class=&quot;text-center&quot;&gt;
                      &lt;h1 class=&quot;h4 text-gray-900 mb-4&quot;&gt;${buyerVO.buyerName}&lt;/h1&gt;
                    &lt;/div&gt;
                    &lt;form:form modelAttribute=&quot;buyerVO&quot; class=&quot;user&quot; method=&quot;post&quot; action=&quot;/lprod/detail&quot;&gt;
                        &lt;div class=&quot;form-group row&quot;&gt;
                            &lt;div class=&quot;col-sm-6 mb-3 mb-sm-0&quot;&gt;
                                &lt;form:input path=&quot;buyerId&quot; class=&quot;form-control form-control-user&quot; id=&quot;buyerId&quot; placeholder=&quot;&quot; /&gt;
                            &lt;/div&gt;
                            &lt;div class=&quot;col-sm-6&quot;&gt;
                                &lt;form:input path=&quot;lprodNm&quot; class=&quot;form-control form-control-user&quot; id=&quot;lprodNm&quot; placeholder=&quot;&quot; /&gt;
                            &lt;/div&gt;
                        &lt;/div&gt;
                        &lt;div class=&quot;form-group&quot;&gt;
                            &lt;form:input path=&quot;buyerMail&quot; class=&quot;form-control form-control-user&quot; id=&quot;buyerMail&quot; placeholder=&quot;&quot; /&gt;
                            &lt;font color=&quot;red&quot;&gt;&lt;form:errors path=&quot;buyerMail&quot; /&gt;&lt;/font&gt;
                        &lt;/div&gt;
                        &lt;div class=&quot;form-group row&quot;&gt;
                            &lt;div class=&quot;col-sm-6 mb-3 mb-sm-0&quot;&gt;
                                &lt;form:input path=&quot;buyerComtel&quot; class=&quot;form-control form-control-user&quot; id=&quot;buyerComtel&quot; /&gt;
                                &lt;font color=&quot;red&quot;&gt;&lt;form:errors path=&quot;buyerComtel&quot; /&gt;&lt;/font&gt;
                            &lt;/div&gt;
                            &lt;div class=&quot;col-sm-6&quot;&gt;
                                &lt;form:input path=&quot;buyerCharger&quot; class=&quot;form-control form-control-user&quot; id=&quot;buyerCharger&quot; /&gt;
                                &lt;font color=&quot;red&quot;&gt;&lt;form:errors path=&quot;buyerCharger&quot; /&gt;&lt;/font&gt;
                            &lt;/div&gt;
                        &lt;/div&gt;
                        &lt;a href=&quot;#&quot; id=&quot;btnEdit&quot; class=&quot;btn btn-primary btn-user btn-block&quot;&gt;
                            수정
                        &lt;/a&gt;
                        &lt;a href=&quot;#&quot; id=&quot;btnConfirm&quot; style=&quot;display:none;&quot; class=&quot;btn btn-success btn-icon-split btn-block&quot;&gt;
                            &lt;span class=&quot;icon text-white-50&quot;&gt;
                                &lt;i class=&quot;fas fa-check&quot;&gt;&lt;/i&gt;
                            &lt;/span&gt;
                            &lt;span class=&quot;text&quot;&gt;확인&lt;/span&gt;
                        &lt;/a&gt;
                        &lt;a href=&quot;#&quot; id=&quot;btnCancel&quot; style=&quot;display:none;&quot; class=&quot;btn btn-danger btn-icon-split btn-block&quot;&gt;
                             &lt;span class=&quot;icon text-white-50&quot;&gt;
                                 &lt;i class=&quot;fas fa-trash&quot;&gt;&lt;/i&gt;
                             &lt;/span&gt;
                             &lt;span class=&quot;text&quot;&gt;취소&lt;/span&gt;
                        &lt;/a&gt;            
                        &lt;hr&gt;
                        &lt;a href=&quot;/lprod/list&quot; class=&quot;btn btn-google btn-user btn-block&quot;&gt;
                            &lt;i class=&quot;fab fa-google fa-fw&quot;&gt;&lt;/i&gt; 목록보기
                        &lt;/a&gt;
                    &lt;/form:form&gt;
                    &lt;hr&gt;
                    &lt;div class=&quot;text-center&quot;&gt;
                        &lt;a class=&quot;small&quot; href=&quot;forgot-password.html&quot;&gt;Forgot Password?&lt;/a&gt;
                    &lt;/div&gt;
                    &lt;div class=&quot;text-center&quot;&gt;
                        &lt;a class=&quot;small&quot; href=&quot;login.html&quot;&gt;Already have an account? Login!&lt;/a&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
        &lt;!-- /.container-fluid 본문 끝--&gt;

&lt;/div&gt;
            &lt;!-- End of Main Content --&gt;

            &lt;!-- Footer --&gt;
            &lt;jsp:include page=&quot;../includes/footer.jsp&quot;&gt;&lt;/jsp:include&gt;
            &lt;!-- End of Footer --&gt;

        &lt;/div&gt;
        &lt;!-- End of Content Wrapper --&gt;

    &lt;/div&gt;
    &lt;!-- End of Page Wrapper --&gt;

    &lt;!-- Scroll to Top Button--&gt;
    &lt;a class=&quot;scroll-to-top rounded&quot; href=&quot;#page-top&quot;&gt;
        &lt;i class=&quot;fas fa-angle-up&quot;&gt;&lt;/i&gt;
    &lt;/a&gt;

    &lt;!-- Logout Modal--&gt;
    &lt;div class=&quot;modal fade&quot; id=&quot;logoutModal&quot; tabindex=&quot;-1&quot; role=&quot;dialog&quot; aria-labelledby=&quot;exampleModalLabel&quot;
        aria-hidden=&quot;true&quot;&gt;
        &lt;div class=&quot;modal-dialog&quot; role=&quot;document&quot;&gt;
            &lt;div class=&quot;modal-content&quot;&gt;
                &lt;div class=&quot;modal-header&quot;&gt;
                    &lt;h5 class=&quot;modal-title&quot; id=&quot;exampleModalLabel&quot;&gt;수정 요청&lt;/h5&gt;
                    &lt;button class=&quot;close&quot; type=&quot;button&quot; data-dismiss=&quot;modal&quot; aria-label=&quot;Close&quot;&gt;
                        &lt;span aria-hidden=&quot;true&quot;&gt;×&lt;/span&gt;
                    &lt;/button&gt;
                &lt;/div&gt;
                &lt;div class=&quot;modal-body&quot;&gt;Select &quot;Logout&quot; below if you are ready to end your current session.&lt;/div&gt;
                &lt;div class=&quot;modal-footer&quot;&gt;
                    &lt;button class=&quot;btn btn-secondary&quot; type=&quot;button&quot; data-dismiss=&quot;modal&quot;&gt;취소&lt;/button&gt;
                    &lt;a class=&quot;btn btn-primary&quot; id=&quot;modalConfirm&quot; href=&quot;#&quot;&gt;확인&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- Bootstrap core JavaScript--&gt;
    &lt;script src=&quot;/resources/sbadmin2/vendor/jquery/jquery.min.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/resources/sbadmin2/vendor/bootstrap/js/bootstrap.bundle.min.js&quot;&gt;&lt;/script&gt;

    &lt;!-- Core plugin JavaScript--&gt;
    &lt;script src=&quot;/resources/sbadmin2/vendor/jquery-easing/jquery.easing.min.js&quot;&gt;&lt;/script&gt;

    &lt;!-- Custom scripts for all pages--&gt;
    &lt;script src=&quot;/resources/sbadmin2/js/sb-admin-2.min.js&quot;&gt;&lt;/script&gt;

    &lt;!-- Page level plugins --&gt;
    &lt;script src=&quot;/resources/sbadmin2/vendor/chart.js/Chart.min.js&quot;&gt;&lt;/script&gt;

    &lt;!-- Page level custom scripts --&gt;
    &lt;script src=&quot;/resources/sbadmin2/js/demo/chart-area-demo.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/resources/sbadmin2/js/demo/chart-pie-demo.js&quot;&gt;&lt;/script&gt;

    &lt;script type=&quot;text/javascript&quot;&gt;
           $(function(){
               $(&quot;.form-control-user&quot;).prop(&quot;readonly&quot;,true);

               //수정버튼 클릭 시 수정버튼 사라지고, 확인/취소버튼 보임
               $(&quot;#btnEdit&quot;).on(&quot;click&quot;,function(){
                   $(&quot;.form-control-user&quot;).prop(&quot;readonly&quot;,false);
                   //수정 대상이 아닌 요소 처리
                   //단, disabled -&gt; true(X, 요소가 비활성됨) =&gt; form으로 안넘어감..
                   //readonly를 쓰자!
                   $(&quot;#buyerId&quot;).prop(&quot;readonly&quot;,true);
                   $(&quot;#lprodNm&quot;).prop(&quot;readonly&quot;,true);

                   //수정버튼 사라지고
                   $(&quot;#btnEdit&quot;).css(&quot;display&quot;, &quot;none&quot;);

                   //확인/취소버튼 보임
                   $(&quot;#btnConfirm&quot;).css(&quot;display&quot;, &quot;block&quot;);
                   $(&quot;#btnCancel&quot;).css(&quot;display&quot;, &quot;block&quot;);
               });
               //취소버튼 클릭  시 새로고침 처리
               $(&#39;#btnCancel&#39;).on(&quot;click&quot;,function(){
                   location.reload();
               });
               //확인버튼 클릭 시 수정 요청
               $(&quot;#btnConfirm&quot;).on(&quot;click&quot;,function(){
                   $(&quot;.modal-body&quot;).html(&quot;해당 글을 수정 하시겠습니까?&quot;);
                   $(&quot;#logoutModal&quot;).modal(&quot;show&quot;);
               });
               //모달 확인 버튼 클릭 시 수정 프로세스 진행
               $(&quot;#modalConfirm&quot;).on(&quot;click&quot;,function(){
                   $(&quot;#buyerVO&quot;).submit();
               });
           });
    &lt;/script&gt;


&lt;/body&gt;

&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 25강 - Paging처리]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-25-Paging%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-25-Paging%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 13 Feb 2022 08:29:58 GMT</pubDate>
            <description><![CDATA[<p>** list.jsp에 bootstrap 추가하기**</p>
<p>1) 구글에 sb admin2 검색
<a href="https://startbootstrap.com/theme/sb-admin-2">https://startbootstrap.com/theme/sb-admin-2</a> 들어가서 다운로드 클릭
압축풀고 webapp 폴더의 resources에 넣기</p>
<p>2) views 폴더에 aside.jsp, header.jsp, footer.jsp파일을 각각 만들어 처리하고, list.jsp의 &lt;body&gt;에 &lt;jsp:include&gt;의 page 속성을 사용하여 추가함</p>
<p>3) list.jsp의 경로 바꾸기
 (1) src=&quot;vendor 검색
src=&quot;/resources/sbadmin2/vendor로 바꾸기
 (2) src=&quot;js 검색
src=&quot;/resources/sbadmin2/js 로 바꾸기
 (3) href=&quot;css 검색
href=&quot;/resources/sbadmin2/css로 바꾸기
 (4) href=&quot;vendor 검색
href=&quot;/resources/sbadmin2/vendor로 바꾸기
 (5) src=&quot;img 검색
href=&quot;/resources/sbadmin2/vendor/img로 바꾸기</p>
<h2 id="paging-처리용-클래스-생성">Paging 처리용 클래스 생성</h2>
<h4 id="articlepagejava">ArticlePage.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

//페이징 처리를 위한 클래스
//게시글 데이터와 페이징 관련 정보를 담고 있음
public class ArticlePage {
    //페이징 관련 멤버변수
    //전체 글의 행의 수
    private int total;
    //현재 페이지 번호
    private int currentPage;
    //전체 페이지 개수
    private int totalPages;
    //시작 페이지 번호
    private int startPage;
    //종료 페이지 번호
    private int endPage;
    //페이징의 개수
    private int pagingCount;
    //게시글 데이터
    private List&lt;LprodVO&gt; content;

    //생성자
    //size : 한 화면에 보여질 행의 수
    public ArticlePage(int total, int currentPage, int size, int pagingCount, List&lt;LprodVO&gt; content) {
        super();
        this.total = total;
        this.currentPage = currentPage;
        this.content = content;
        this.pagingCount = pagingCount;
        //select 결과가 없다면..
        if(total == 0) {
            totalPages = 0;
            startPage = 0;
            endPage = 0;
        }else { //select 결과가 있을 때
            //전체 페이지 개수 구하기(전체 글의 수/한 화면에 보여질 행의 수)
            //정수와 정수의 나눗셈 결과는 정수! 13 / 7 =&gt; 1
            totalPages = total / size;
            //보정해줘야 할 경우는? 15 / 7 = 2 경우 처럼 나머지가 0보다 클 때
            if(total % size &gt; 0){
                totalPages++;
            }

            //startPage : 이전 [1] [2] [3] [4] [5] 다음    일 때 1을 의미
            //startPage 공식 : 현재 페이지 / 페이징의 개수 * 페이징의 개수 + 1 
            startPage = currentPage / pagingCount * pagingCount + 1;
            //보정해줘야 할 경우는? 5 / 5 * 5 + 1 =&gt; 6 경우처럼
            //                현재페이지  % 5 == 0 일 때  (5, 10, 15처럼)
            if(currentPage%pagingCount==0) {
                //startPage = startPage - 5(페이징의 개수);
                startPage -= pagingCount;
            //endPage : 이전 [1] [2] [3] [4] [5] 다음    일 때 5를 의미
            endPage = startPage + (pagingCount-1);
            //보정해줘야 할 경우는? endPage &gt; totalPages 일 때 (ex 5 &gt; 3)
            //                    endPage를 totalPages로 바꿔줘야 함 (ex 5를 3으로 바꾸자!)
            if(endPage &gt; totalPages) {
                endPage = totalPages;
            }
        }//end outer if

    }
    //전체 행의 수를 리턴
    //자바빈 클래스가 아니기 때문에 모든 멤버변수의 getter/setter메소드를 만들 필요는 없다.
    public int getTotal() {
        return this.total;
    }

    //select 결과가 없는가? true면 결과가 없다는 의미
    public boolean hasnoArticles() {
        return this.total == 0;
    }

    //select 결과가 있는가? true면 결과가 있다는 의미
    public boolean hasArticles() {
        return this.total &gt; 0;
    }

    //현재 페이지 번호 리펀
    public int getCurrentPage() {
        return this.currentPage;
    }

    //전체 페이지의 개수 리턴
    public int getTotalPages() {
        return totalPages;
    }

    //데이터 VO List 리턴
    public List&lt;LprodVO&gt; getContent() {
        return this.content;
    }

    //목록 하단의 시작 번호를 리턴
    public int getStartPage() {
        return this.startPage;
    }

    //목록 하단의 종료 번호를 리턴
    public int getEndPage() {
        return this.endPage;
    }

}</code></pre><h2 id="컨트롤러에서-articlepage-객체-생성">컨트롤러에서 ArticlePage 객체 생성</h2>
<h4 id="lprodcontrollerjava">LprodController.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

@RequestMapping(value = &quot;lprod&quot;)
@Controller
public class LprodController {
    private static final Logger logger = LoggerFactory.getLogger(LprodController.class);

    @Inject
    LprodService lprodService;

    //http://localhost:8090/lprod/list?currentPage=1 처럼 
    //@RequestParam으로 가져오는 파라미터는 String이 아닌 int, Map, VO로도 가능하다..
    //http://localhost:8090/lprod/list처럼 해당 요청 파라미터를 지정하지 않는 경우, defaultValue 속성에 지정한 문자열을 값으로 이용하게 됨(http://localhost:8090/lprod/list 처럼 currentPage의 값을 모를 때, null일 때)
    @RequestMapping(value = &quot;/list&quot;)
    public String list(Model model, @RequestParam(defaultValue = &quot;1&quot;) int currentPage) {
    //currentPage 출력
    logger.info(&quot;currentPage : &quot; + currentPage);

    List&lt;LprodVO&gt; list = this.lprodService.list();

    //상품분류 별 거래처 목록 행의 수
    int total = this.lprodService.listCount();
    logger.info(&quot;total : &quot; + total);

    //페이징 처리한 객체를 넣는다.
    model.addAttribute(&quot;list&quot;, new ArticlePage(total, currentPage, 7, 5, list));
    model.addAttribute(&quot;total&quot;, total);

    //forward
    return &quot;lprod/list&quot;;
    }



}</code></pre><h2 id="jsp">JSP</h2>
<h4 id="listjsp">list.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;

&lt;head&gt;

    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;meta name=&quot;description&quot; content=&quot;&quot;&gt;
    &lt;meta name=&quot;author&quot; content=&quot;&quot;&gt;

    &lt;title&gt;상품분류 별 거래처 목록&lt;/title&gt;

    &lt;!-- Custom fonts for this template--&gt;
    &lt;link href=&quot;/resources/sbadmin2/vendor/fontawesome-free/css/all.min.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;&gt;
    &lt;link
        href=&quot;https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&quot;
        rel=&quot;stylesheet&quot;&gt;

    &lt;!-- Custom styles for this template--&gt;
    &lt;link href=&quot;/resources/sbadmin2/css/sb-admin-2.min.css&quot; rel=&quot;stylesheet&quot;&gt;

&lt;/head&gt;

&lt;body id=&quot;page-top&quot;&gt;

    &lt;!-- Page Wrapper --&gt;
    &lt;div id=&quot;wrapper&quot;&gt;

        &lt;!-- Sidebar --&gt;
        &lt;jsp:include page=&quot;../includes/aside.jsp&quot;&gt;&lt;/jsp:include&gt;
        &lt;!-- End of Sidebar --&gt;

        &lt;!-- Content Wrapper --&gt;
        &lt;div id=&quot;content-wrapper&quot; class=&quot;d-flex flex-column&quot;&gt;

            &lt;!-- Main Content --&gt;
            &lt;div id=&quot;content&quot;&gt;

                &lt;!-- Topbar --&gt;
                &lt;jsp:include page=&quot;../includes/header.jsp&quot;&gt;&lt;/jsp:include&gt;
                &lt;!-- End of Topbar --&gt;

                &lt;!-- Begin Page Content 본문 시작 --&gt;
                &lt;div class=&quot;container-fluid&quot;&gt;

                    &lt;!-- Page Heading --&gt;
                    &lt;h1 class=&quot;h3 mb-2 text-gray-800&quot;&gt;상품분류 별 거래처 목록&lt;/h1&gt;
                    &lt;p class=&quot;mb-4&quot;&gt;상품분류 별로 거래처의 목록을 화면에 보여줍니다.&lt;/p&gt;

                    &lt;!-- DataTales Example --&gt;
                    &lt;div class=&quot;card shadow mb-4&quot;&gt;
                        &lt;div class=&quot;card-header py-3&quot;&gt;
                            &lt;h6 class=&quot;m-0 font-weight-bold text-primary&quot;&gt;DataTables Example&lt;/h6&gt;
                        &lt;/div&gt;
                        &lt;div class=&quot;card-body&quot;&gt;
                            &lt;div class=&quot;table-responsive&quot;&gt;
                                &lt;div id=&quot;dataTable_wrapper&quot; class=&quot;dataTables_wrapper dt-bootstrap4&quot;&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-sm-12 col-md-6&quot;&gt;&lt;div class=&quot;dataTables_length&quot; id=&quot;dataTable_length&quot;&gt;&lt;label&gt;Show &lt;select name=&quot;dataTable_length&quot; aria-controls=&quot;dataTable&quot; class=&quot;custom-select custom-select-sm form-control form-control-sm&quot;&gt;&lt;option value=&quot;10&quot;&gt;10&lt;/option&gt;&lt;option value=&quot;25&quot;&gt;25&lt;/option&gt;&lt;option value=&quot;50&quot;&gt;50&lt;/option&gt;&lt;option value=&quot;100&quot;&gt;100&lt;/option&gt;&lt;/select&gt; entries&lt;/label&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;col-sm-12 col-md-6&quot;&gt;&lt;div id=&quot;dataTable_filter&quot; class=&quot;dataTables_filter&quot;&gt;&lt;label&gt;Search:&lt;input type=&quot;search&quot; class=&quot;form-control form-control-sm&quot; placeholder=&quot;&quot; aria-controls=&quot;dataTable&quot;&gt;&lt;/label&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-sm-12&quot;&gt;&lt;table class=&quot;table table-bordered dataTable&quot; id=&quot;dataTable&quot; width=&quot;100%&quot; cellspacing=&quot;0&quot; role=&quot;grid&quot; aria-describedby=&quot;dataTable_info&quot; style=&quot;width: 100%;&quot;&gt;
                                    &lt;thead&gt;
                                    &lt;tr role=&quot;row&quot;&gt;
                                        &lt;th class=&quot;sorting sorting_asc&quot; tabindex=&quot;0&quot; aria-controls=&quot;dataTable&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; aria-sort=&quot;ascending&quot; aria-label=&quot;Name: activate to sort column descending&quot; style=&quot;width: 50px;&quot;&gt;번호&lt;/th&gt;
                                        &lt;th class=&quot;sorting&quot; tabindex=&quot;0&quot; aria-controls=&quot;dataTable&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; aria-label=&quot;Position: activate to sort column ascending&quot; style=&quot;width: 200px;&quot;&gt;상품분류코드&lt;/th&gt;
                                        &lt;th class=&quot;sorting&quot; tabindex=&quot;0&quot; aria-controls=&quot;dataTable&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; aria-label=&quot;Office: activate to sort column ascending&quot;&gt;상품분류명&lt;/th&gt;
                                        &lt;th class=&quot;sorting&quot; tabindex=&quot;0&quot; aria-controls=&quot;dataTable&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; aria-label=&quot;Age: activate to sort column ascending&quot; style=&quot;width: 200px;&quot;&gt;거래처코드&lt;/th&gt;
                                        &lt;th class=&quot;sorting&quot; tabindex=&quot;0&quot; aria-controls=&quot;dataTable&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; aria-label=&quot;Start date: activate to sort column ascending&quot;&gt;거래처명&lt;/th&gt;
                                    &lt;/tr&gt;
                                    &lt;/thead&gt;
                                    &lt;tfoot&gt;
                                     &lt;tr&gt;
                                         &lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;번호&lt;/th&gt;
                                        &lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;상품분류코드&lt;/th&gt;
                                        &lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;상품분류명&lt;/th&gt;
                                        &lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;거래처코드&lt;/th&gt;
                                        &lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;거래처명&lt;/th&gt;
                                     &lt;/tr&gt;
                                    &lt;/tfoot&gt;
                                    &lt;tbody&gt;
                                    &lt;c:set var=&quot;i&quot; value=&quot;0&quot;/&gt;
                                    &lt;c:forEach var=&quot;lprodVO&quot; items=&quot;${list.content}&quot;&gt;
                                    &lt;c:forEach var=&quot;buyerVO&quot; items=&quot;${lprodVO.buyerVO}&quot;&gt;
                                    &lt;c:set var=&quot;cnt&quot; value=&quot;${i=i+1}&quot; /&gt;
                                    &lt;c:if test=&quot;${cnt%2==1}&quot;&gt;
                                    &lt;tr class=&quot;odd&quot;&gt;
                                    &lt;/c:if&gt;
                                    &lt;c:if test=&quot;${cnt%2==0}&quot;&gt;
                                    &lt;tr class=&quot;even&quot;&gt;
                                    &lt;/c:if&gt;
                                            &lt;td class=&quot;sorting_1&quot;&gt;${cnt}&lt;/td&gt;
                                            &lt;td&gt;${lprodVO.lprodGu}&lt;/td&gt;
                                            &lt;td&gt;${lprodVO.lprodNm}&lt;/td&gt;
                                            &lt;td&gt;${buyerVO.buyerId}&lt;/td&gt;
                                            &lt;td&gt;${buyerVO.buyerName}&lt;/td&gt;
                                     &lt;/tr&gt;
                                     &lt;/c:forEach&gt;
                                     &lt;/c:forEach&gt;
                                     &lt;/tbody&gt;
                                &lt;/table&gt;
                                &lt;/div&gt;
                                &lt;/div&gt;
                                &lt;div class=&quot;row&quot;&gt;
                                &lt;div class=&quot;col-sm-12 col-md-5&quot;&gt;
                                &lt;div class=&quot;dataTables_info&quot; id=&quot;dataTable_info&quot; role=&quot;status&quot; aria-live=&quot;polite&quot;&gt;
                                &lt;!-- A*7-6 A*7 --&gt;
                                Showing ${param.currentPage*7-6} to ${param.currentPage*7} of ${total} entries
                                &lt;/div&gt;
                                &lt;/div&gt;
                                &lt;div class=&quot;col-sm-12 col-md-7&quot;&gt;
                                &lt;div class=&quot;dataTables_paginate paging_simple_numbers&quot; id=&quot;dataTable_paginate&quot;&gt;
                                &lt;!-- 페이징 처리 시작 --&gt;
                                &lt;ul class=&quot;pagination&quot;&gt;
                                &lt;!-- Previous 시작 --&gt;
                                    &lt;li class=&quot;paginate_button page-item previous &lt;c:if test=&#39;${list.startPage&lt;6}&#39;&gt;disabled&lt;/c:if&gt;&quot; id=&quot;dataTable_previous&quot;&gt;&lt;a href=&quot;/lprod/list?currentPages=${list.startPage-5}&quot; aria-controls=&quot;dataTable&quot; data-dt-idx=&quot;0&quot; tabindex=&quot;0&quot; class=&quot;page-link&quot;&gt;Previous&lt;/a&gt;&lt;/li&gt;
                                &lt;!-- Previous 끝 --&gt;
                                &lt;!-- Page번호 시작, 컨트롤러에서 list에 페이징 객체를 넣었으므로 list.변수명으로 불러옴 --&gt;
                                &lt;c:forEach var=&quot;pNo&quot; begin=&quot;${list.startPage}&quot; end=&quot;${list.endPage}&quot; step=&quot;1&quot;&gt;
                                    &lt;li class=&quot;paginate_button page-item &lt;c:if test=&#39;${param.currentPage eq pNo}&#39;&gt;active&lt;/c:if&gt;&quot;&gt;&lt;a href=&quot;/lprod/list?currentPage=${pNo}&quot; aria-controls=&quot;dataTable&quot; data-dt-idx=&quot;1&quot; tabindex=&quot;0&quot; class=&quot;page-link&quot;&gt;${pNo}&lt;/a&gt;&lt;/li&gt;
                                &lt;/c:forEach&gt;
                                &lt;!-- Page번호 끝 --&gt;
                                &lt;!-- Next 시작 --&gt;
                                    &lt;li class=&quot;paginate_button page-item next&lt;c:if test=&#39;${list.endPage&gt;=list.totalPages}&#39;&gt;disabled&lt;/c:if&gt;&quot; id=&quot;dataTable_next&quot;&gt;&lt;a href=&quot;/lprod/list?currentPage=${list.startPage+5}&quot; aria-controls=&quot;dataTable&quot; data-dt-idx=&quot;7&quot; tabindex=&quot;0&quot; class=&quot;page-link&quot;&gt;Next&lt;/a&gt;&lt;/li&gt;
                                &lt;!-- Next 끝 --&gt;
                                &lt;!-- 페이징 처리 끝 --&gt;
                                &lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
                            &lt;/div&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;

                &lt;/div&gt;
                &lt;!-- /.container-fluid 본문 끝--&gt;

            &lt;/div&gt;
            &lt;!-- End of Main Content --&gt;

            &lt;!-- Footer --&gt;
            &lt;jsp:include page=&quot;../includes/footer.jsp&quot;&gt;&lt;/jsp:include&gt;
            &lt;!-- End of Footer --&gt;

        &lt;/div&gt;
        &lt;!-- End of Content Wrapper --&gt;

    &lt;/div&gt;
    &lt;!-- End of Page Wrapper --&gt;

    &lt;!-- Scroll to Top Button--&gt;
    &lt;a class=&quot;scroll-to-top rounded&quot; href=&quot;#page-top&quot;&gt;
        &lt;i class=&quot;fas fa-angle-up&quot;&gt;&lt;/i&gt;
    &lt;/a&gt;

    &lt;!-- Logout Modal--&gt;
    &lt;div class=&quot;modal fade&quot; id=&quot;logoutModal&quot; tabindex=&quot;-1&quot; role=&quot;dialog&quot; aria-labelledby=&quot;exampleModalLabel&quot;
        aria-hidden=&quot;true&quot;&gt;
        &lt;div class=&quot;modal-dialog&quot; role=&quot;document&quot;&gt;
            &lt;div class=&quot;modal-content&quot;&gt;
                &lt;div class=&quot;modal-header&quot;&gt;
                    &lt;h5 class=&quot;modal-title&quot; id=&quot;exampleModalLabel&quot;&gt;Ready to Leave?&lt;/h5&gt;
                    &lt;button class=&quot;close&quot; type=&quot;button&quot; data-dismiss=&quot;modal&quot; aria-label=&quot;Close&quot;&gt;
                        &lt;span aria-hidden=&quot;true&quot;&gt;×&lt;/span&gt;
                    &lt;/button&gt;
                &lt;/div&gt;
                &lt;div class=&quot;modal-body&quot;&gt;Select &quot;Logout&quot; below if you are ready to end your current session.&lt;/div&gt;
                &lt;div class=&quot;modal-footer&quot;&gt;
                    &lt;button class=&quot;btn btn-secondary&quot; type=&quot;button&quot; data-dismiss=&quot;modal&quot;&gt;Cancel&lt;/button&gt;
                    &lt;a class=&quot;btn btn-primary&quot; href=&quot;login.html&quot;&gt;Logout&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;!-- Bootstrap core JavaScript--&gt;
    &lt;script src=&quot;/resources/sbadmin2/vendor/jquery/jquery.min.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/resources/sbadmin2/vendor/bootstrap/js/bootstrap.bundle.min.js&quot;&gt;&lt;/script&gt;

    &lt;!-- Core plugin JavaScript--&gt;
    &lt;script src=&quot;/resources/sbadmin2/vendor/jquery-easing/jquery.easing.min.js&quot;&gt;&lt;/script&gt;

    &lt;!-- Custom scripts for all pages--&gt;
    &lt;script src=&quot;/resources/sbadmin2/js/sb-admin-2.min.js&quot;&gt;&lt;/script&gt;

    &lt;!-- Page level plugins --&gt;
    &lt;script src=&quot;/resources/sbadmin2/vendor/chart.js/Chart.min.js&quot;&gt;&lt;/script&gt;

    &lt;!-- Page level custom scripts --&gt;
    &lt;script src=&quot;/resources/sbadmin2/js/demo/chart-area-demo.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/resources/sbadmin2/js/demo/chart-pie-demo.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;

&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 24강 - ResultMap으로 1:N 관계 처리(Lprod, Buyer)]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-24%EA%B0%95-1N-%EA%B4%80%EA%B3%84-%EC%B2%98%EB%A6%ACLprod-Buyer</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-24%EA%B0%95-1N-%EA%B4%80%EA%B3%84-%EC%B2%98%EB%A6%ACLprod-Buyer</guid>
            <pubDate>Wed, 09 Feb 2022 09:43:51 GMT</pubDate>
            <description><![CDATA[<h3 id="xmlmybatis에서-resultmap형태로-처리하는-방법">XML(Mybatis)에서 resultMap형태로 처리하는 방법</h3>
<ul>
<li>기본키는 id 아닌 컬럼은 result에</li>
<li>property :vo의 멤버변수명</li>
<li>column : select문의 컬럼명    </li>
<li>1:1 관계는 &lt;association&gt;</li>
<li>1:N 관계는 &lt;collection&gt;</li>
</ul>
<p><strong>예시) LPROD(1)와 BUYER(N) 관계에서 buyerVO를 list로 반환하기</strong></p>
<p><strong>1. &lt;select&gt; 작성</strong></p>
<pre><code>&lt;select id=&quot;아이디&quot; resultMap=&quot;반환할 VO객체의 Map명&quot;&gt;
    조회할 쿼리문 작성
&lt;/select&gt;</code></pre><p><strong>2. resultMap 작성</strong></p>
<pre><code>    &lt;resultMap type=&quot;&lt;select&gt;에서 작성한 VO객체명&quot; id=&quot;&lt;select&gt;에서 작성한 resultMap명&quot;&gt;
        &lt;result property=&quot;VO객체의 멤버변수명&quot; column=&quot;컬럼명&quot; /&gt;
        &lt;collection property=&quot;1:N 관계 중 N에 해당하는 VO객체명&quot; resultMap=&quot;반환할 VO객체의 map명&quot;&gt;
        &lt;/collection&gt;
    &lt;/resultMap&gt;
</code></pre><p><strong>3. 1:N 관계 속성 중 N에 해당하는 resultMap 작성</strong></p>
<pre><code>    &lt;resultMap type=&quot;1:N 관계 중 N에 해당하는 VO객체명&quot; id=&quot;&lt;collection&gt;에서 작성한 resultMap명&quot;&gt;
        &lt;result property=&quot;VO객체의 멤버변수명&quot; column=&quot;컬럼명&quot;/&gt;
    &lt;/resultMap&gt;</code></pre><h2 id="1-vo">1. VO</h2>
<h4 id="lprodvojava">LprodVO.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

public class LprodVO {
    private int lprodId;
    private String lprodGu;
    private String lprodNm;

    //Lprod : Buyer = 1 : N 관계
    private List&lt;BuyerVO&gt; BuyerVO;

    public int getLprodId() {
        return lprodId;
    }

    public void setLprodId(int lprodId) {
        this.lprodId = lprodId;
    }

    public String getLprodGu() {
        return lprodGu;
    }

    public void setLprodGu(String lprodGu) {
        this.lprodGu = lprodGu;
    }

    public String getLprodNm() {
        return lprodNm;
    }

    public void setLprodNm(String lprodNm) {
        this.lprodNm = lprodNm;
    }

    public List&lt;BuyerVO&gt; getBuyerVO() {
        return BuyerVO;
    }

    public void setBuyerVO(List&lt;BuyerVO&gt; buyerVO) {
        BuyerVO = buyerVO;
    }

    @Override
    public String toString() {
        return &quot;LprodVO [lprodId=&quot; + lprodId + &quot;, lprodGu=&quot; + lprodGu + &quot;, lprodNm=&quot; + lprodNm + &quot;, BuyerVO=&quot; + BuyerVO
                + &quot;]&quot;;
    }


}
</code></pre><h4 id="buyervojava">BuyerVO.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

//자바빈 클래스
public class BuyerVO {
    //멤버변수
    private String buyerId;
    private String buyerName;
    private String buyerLgu;
    private String buyerBank;
    private String buyerBankno;
    private String buyerBankname;
    private String buyerZip;
    private String buyerAdd1;
    private String buyerAdd2;
    private String buyerComtel;
    private String buyerFax;
    private String buyerMail;
    private String buyerCharger;
    private String buyerTelext;


    //Buyer : ProdVO = 1: N 관계
    private List&lt;ProdVO&gt; ProdVO;

    //getter/setter 메소드
    public String getBuyerId() {
        return buyerId;
    }
    public void setBuyerId(String buyerId) {
        this.buyerId = buyerId;
    }
    public String getBuyerName() {
        return buyerName;
    }
    public void setBuyerName(String buyerName) {
        this.buyerName = buyerName;
    }
    public String getBuyerLgu() {
        return buyerLgu;
    }
    public void setBuyerLgu(String buyerLgu) {
        this.buyerLgu = buyerLgu;
    }
    public String getBuyerBank() {
        return buyerBank;
    }
    public void setBuyerBank(String buyerBank) {
        this.buyerBank = buyerBank;
    }
    public String getBuyerBankno() {
        return buyerBankno;
    }
    public void setBuyerBankno(String buyerBankno) {
        this.buyerBankno = buyerBankno;
    }
    public String getBuyerBankname() {
        return buyerBankname;
    }
    public void setBuyerBankname(String buyerBankname) {
        this.buyerBankname = buyerBankname;
    }
    public String getBuyerZip() {
        return buyerZip;
    }
    public void setBuyerZip(String buyerZip) {
        this.buyerZip = buyerZip;
    }
    public String getBuyerAdd1() {
        return buyerAdd1;
    }
    public void setBuyerAdd1(String buyerAdd1) {
        this.buyerAdd1 = buyerAdd1;
    }
    public String getBuyerAdd2() {
        return buyerAdd2;
    }
    public void setBuyerAdd2(String buyerAdd2) {
        this.buyerAdd2 = buyerAdd2;
    }
    public String getBuyerComtel() {
        return buyerComtel;
    }
    public void setBuyerComtel(String buyerComtel) {
        this.buyerComtel = buyerComtel;
    }
    public String getBuyerFax() {
        return buyerFax;
    }
    public void setBuyerFax(String buyerFax) {
        this.buyerFax = buyerFax;
    }
    public String getBuyerMail() {
        return buyerMail;
    }
    public void setBuyerMail(String buyerMail) {
        this.buyerMail = buyerMail;
    }
    public String getBuyerCharger() {
        return buyerCharger;
    }
    public void setBuyerCharger(String buyerCharger) {
        this.buyerCharger = buyerCharger;
    }
    public String getBuyerTelext() {
        return buyerTelext;
    }
    public void setBuyerTelext(String buyerTelext) {
        this.buyerTelext = buyerTelext;
    }

    public List&lt;ProdVO&gt; getProdVO() {
        return ProdVO;
    }
    public void setProdVO(List&lt;ProdVO&gt; prodVO) {
        ProdVO = prodVO;
    }

    @Override
    public String toString() {
        return &quot;BuyerVO [buyerId=&quot; + buyerId + &quot;, buyerName=&quot; + buyerName + &quot;, buyerLgu=&quot; + buyerLgu + &quot;, buyerBank=&quot;
                + buyerBank + &quot;, buyerBankno=&quot; + buyerBankno + &quot;, buyerBankname=&quot; + buyerBankname + &quot;, buyerZip=&quot;
                + buyerZip + &quot;, buyerAdd1=&quot; + buyerAdd1 + &quot;, buyerAdd2=&quot; + buyerAdd2 + &quot;, buyerComtel=&quot; + buyerComtel
                + &quot;, buyerFax=&quot; + buyerFax + &quot;, buyerMail=&quot; + buyerMail + &quot;, buyerCharger=&quot; + buyerCharger
                + &quot;, buyerTelext=&quot; + buyerTelext + &quot;, ProdVO=&quot; + ProdVO + &quot;]&quot;;
    }




}
</code></pre><h2 id="2-xml">2. XML</h2>
<h4 id="lprod_sqlxml">lprod_SQL.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE mapper
  PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
  &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;
&lt;mapper namespace=&quot;lprod&quot;&gt;
    &lt;resultMap type=&quot;lprodVO&quot; id=&quot;lprodMap&quot;&gt;
        &lt;result property=&quot;lprodGu&quot; column=&quot;LPROD_GU&quot; /&gt;
        &lt;result property=&quot;lprodNm&quot; column=&quot;LPROD_NM&quot; /&gt;
        &lt;collection property=&quot;buyerVO&quot; resultMap=&quot;buyerMap&quot;&gt;
        &lt;/collection&gt;
    &lt;/resultMap&gt;

    &lt;resultMap type=&quot;buyerVO&quot; id=&quot;buyerMap&quot;&gt;
        &lt;result property=&quot;buyerId&quot; column=&quot;BUYER_ID&quot;/&gt;
        &lt;result property=&quot;buyerName&quot; column=&quot;BUYER_NAME&quot;/&gt;
    &lt;/resultMap&gt;

    &lt;select id=&quot;list&quot; resultMap=&quot;lprodMap&quot;&gt;
        SELECT L.LPROD_GU
            , L.LPROD_NM
            , B.BUYER_ID
            , B.BUYER_NAME
        FROM LPROD L, BUYER B
        WHERE L.LPROD_GU = B.BUYER_LGU
        ORDER BY LPROD_GU ASC
    &lt;/select&gt;

&lt;/mapper&gt;</code></pre><h2 id="3-dao">3. Dao</h2>
<h4 id="lproddaojava">LprodDao.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class LprodDao {

    @Autowired
    SqlSessionTemplate sqlSessionTemplate;

    public List&lt;LprodVO&gt; list(){
        return this.sqlSessionTemplate.selectList(&quot;lprod.list&quot;);
    }


}
</code></pre><h2 id="4-service">4. Service</h2>
<h4 id="lprodservicejava">LprodService.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class LprodDao {

    @Autowired
    SqlSessionTemplate sqlSessionTemplate;

    public List&lt;LprodVO&gt; list(){
        return this.sqlSessionTemplate.selectList(&quot;lprod.list&quot;);
    }


}
</code></pre><h4 id="lprodserviceimpljava">LprodServiceImpl.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LprodServiceImpl implements LprodService {
    @Autowired
    LprodDao lprodDao;

    @Override
    public List&lt;LprodVO&gt; list(){
        return this.lprodDao.list();
    }
}
</code></pre><h2 id="5-controller">5. Controller</h2>
<h4 id="lprodcontrollerjava">LprodController.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

import javax.inject.Inject;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping(value = &quot;/lprod&quot;)
@Controller
public class LprodController {
    @Inject
    LprodService lprodService;

    @RequestMapping(value = &quot;/list&quot;)
    public String list(Model model) {
        List&lt;LprodVO&gt; list = this.lprodService.list();
        model.addAttribute(&quot;list&quot;, list);
        return &quot;lprod/list&quot;;
    }
}
</code></pre><h2 id="6-jsp">6. JSP</h2>
<h4 id="listjsp">list.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;분류번호 별 거래처 목록&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;table border=&quot;1&quot;&gt;
    &lt;tr&gt;
        &lt;th&gt;번호&lt;/th&gt;
        &lt;th&gt;분류번호&lt;/th&gt;
        &lt;th&gt;분류명&lt;/th&gt;
        &lt;th&gt;거래처번호&lt;/th&gt;
        &lt;th&gt;거래처명&lt;/th&gt;
    &lt;/tr&gt;
    &lt;c:forEach var=&quot;lprodVO&quot; items=&quot;${list}&quot;&gt;
        &lt;c:forEach var=&quot;buyerVO&quot; items=&quot;${lprodVO.buyerVO}&quot;&gt;
        &lt;c:set var=&quot;cnt&quot; value=&quot;${i=i+1}&quot; /&gt;
    &lt;tr&gt;
        &lt;td&gt;${cnt}&lt;/td&gt;
        &lt;td&gt;${lprodVO.lprodGu}&lt;/td&gt;
        &lt;td&gt;${lprodVO.lprodNm}&lt;/td&gt;
        &lt;td&gt;${buyerVO.buyerId}&lt;/td&gt;
        &lt;td&gt;${buyerVO.buyerName}&lt;/td&gt;
    &lt;/tr&gt;
        &lt;/c:forEach&gt;
    &lt;/c:forEach&gt;
&lt;/table&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 23강 - Member 게시판 CRUD 2, 1:N 관계 처리]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-23%EA%B0%95-Member-%EC%98%88%EC%A0%9C-1N-%EA%B4%80%EA%B3%84-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-23%EA%B0%95-Member-%EC%98%88%EC%A0%9C-1N-%EA%B4%80%EA%B3%84-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 07 Feb 2022 11:36:58 GMT</pubDate>
            <description><![CDATA[<p>1명의 회원이 여러 개의 카드를 가질 수 있다
-&gt; Member 와 Card 테이블은 1:N 관계</p>
<h2 id="vo">VO</h2>
<h4 id="membervojava">MemberVO.java</h4>
<pre><code>package kr.or.ddit;

import java.util.Date;
import java.util.List;

import org.springframework.format.annotation.DateTimeFormat;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

//자바빈 클래스 
//lombok사용해서 아래처럼 편하게 만들 수 있지만 POJO(초기의 순수한 자바)에 위배됨
//@Setter
//@Getter
//@ToString

public class MemberVO {
    private String memberid;
    private String password;
    private String name;
    private String email;
    private String introduction; //자기소개
    //pattern : date 넣을 때 요 형식으로 써주세요~
    //@DateTimeFormat(pattern=&quot;yyyy-MM-dd&quot;)꼭 써줘야 date형식으로 들어간다
    @DateTimeFormat(pattern=&quot;yyyy-MM-dd&quot;)
    private Date regdate;

    private String[] hobbyArray; //취미 방법 2
    private List&lt;String&gt; hobbyList; //취미 방법 1

    private String gender; //성별

    //MemberVO : CardVO = 1 : n 의 관계
    //한명의 회원이 여러 카드를 가질 수 있다.
    private List&lt;CardVO&gt; cardList;


    //기본 생성자
    public MemberVO() {
        super();
        // TODO Auto-generated constructor stub
    }

    //파라미터 던져주면 멤버변수에 자동으로 세팅된다
    public MemberVO(String memberid, String password, String name, String email, Date regdate) {
        super();
        this.memberid = memberid;
        this.password = password;
        this.name = name;
        this.email = email;
        this.regdate = regdate;
    }

    public String getMemberid() {
        return memberid;
    }
    public void setMemberid(String memberid) {
        this.memberid = memberid;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Date getRegdate() {
        return regdate;
    }
    public void setRegdate(Date regdate) {
        this.regdate = regdate;
    }
    public String getIntroduction() {
        return introduction;
    }

    public void setIntroduction(String introduction) {
        this.introduction = introduction;
    }

    public List&lt;String&gt; getHobbyList() {
        return hobbyList;
    }

    public void setHobbyList(List&lt;String&gt; hobbyList) {
        this.hobbyList = hobbyList;
    }


    public String[] getHobbyArray() {
        return hobbyArray;
    }

    public void setHobbyArray(String[] hobbyArray) {
        this.hobbyArray = hobbyArray;
    }

    //비밀번호 변경 기능 구현 시 사용
    public boolean matchPassword(String pwd) {
        return password.contentEquals(pwd);
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public List&lt;CardVO&gt; getCardList() {
        return cardList;
    }

    public void setCardList(List&lt;CardVO&gt; cardList) {
        this.cardList = cardList;
    }

    @Override
    public String toString() {
        return &quot;MemberVO [memberid=&quot; + memberid + &quot;, password=&quot; + password + &quot;, name=&quot; + name + &quot;, email=&quot; + email
                + &quot;, introduction=&quot; + introduction + &quot;, regdate=&quot; + regdate + &quot;, hobbyArray=&quot; + hobbyArray
                + &quot;, hobbyList=&quot; + hobbyList + &quot;, gender=&quot; + gender + &quot;, cardList=&quot; + cardList + &quot;]&quot;;
    }


}
</code></pre><h4 id="cardvojava">CardVO.java</h4>
<pre><code>package kr.or.ddit;

import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

//자바빈 클래스
public class CardVO {
    private String memberid;
    private String no;
    @DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)
    private Date vaildMonth;

    public String getMemberid() {
        return memberid;
    }
    public void setMemberid(String memberid) {
        this.memberid = memberid;
    }

    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    public Date getVaildMonth() {
        return vaildMonth;
    }
    public void setVaildMonth(Date vaildMonth) {
        this.vaildMonth = vaildMonth;
    }
    @Override
    public String toString() {
        return &quot;CardVO [memberid=&quot; + memberid + &quot;, no=&quot; + no + &quot;, vaildMonth=&quot; + vaildMonth + &quot;]&quot;;
    }

}
</code></pre><h2 id="controller">Controller</h2>
<h4 id="membercontrollerjava">MemberController.java</h4>
<pre><code>package kr.or.ddit;

import java.util.HashMap;

//spring 프레임워크가 자바 빈(=객체)으로 등록해서 관리할 수 있도록 어노테이션 달기
//클래스 단으로 빼면 메소드 위 @RequestMapping(&quot;/member/insert&quot;) -&gt; @RequestMapping(&quot;/insert&quot;)로 사용 가능
@RequestMapping(value=&quot;/member&quot;)
@Controller
public class MemberController {
    //로거 만들기
    private static final Logger logger = LoggerFactory.getLogger(MemberController.class);

    //DI(Dependency Injection : 의존성 주입)하면 컨트롤러에서 MemberService를 사용할 수 있다
    @Autowired
    MemberService memberService;

    //컨트롤러의 insert메서드의 매개변수로 자바빈즈 객체가 전달이 되면 기본적으로 다시 화면(view(jsp))으로 전달함
    //컨트롤러와 뷰 사이에 자바빈즈 객체를 서로 공유한다.
    //방법
    //1) 폼 객체의 속성명을 직접 지정
    //2) 폼 객체의 속성명은 직접 저장하지 않으면 **폼 객체의 클래스명의 맨 처음 문자를 소문자로 변환하여 처리
    //    ex) MemberVO -&gt; memberVO, 아래 매개변수명을 memberVO라고 적지 않아도 앞 클래스명을 변환하여 전달
    //3) ModelAttribute 애너테이션으로 폼 객체의 속성명을 dotori라고 지정했다면...
    //    스프링 폼의 modelAttribute 속성의 값도 똑같이 dotori라고 작성해줘야 함
    @RequestMapping(&quot;/insert&quot;)
    public String insert(Model model, @ModelAttribute(&quot;memberVO&quot;) MemberVO memberVO) {
      //폼 객체의 속성명과 폼 태그의 modelAttribute 속성값이 같아야 함
      //model.addAttribute(&quot;memberVO&quot;, new MemberVO()); 이렇게 적어주거나 파라미터에 넣기

      //폼 객체의 프로퍼티 값을 지정 -&gt; 모델을 통해서 뷰(jsp)로 전달이 됨
      // --&gt; &lt;form:input의 path=&quot;memberid&quot; /&gt; 때문 (단, path값은 VO객체의 멤버변수명과 일치해야 함)
      memberVO.setMemberid(&quot;a001&quot;);
      memberVO.setName(&quot;도토리&quot;);
      //&lt;form:password -&gt; 값을 설정해서 뷰(jsp)에 전달하더라도 패스워드 필드에 반영되지 않음
      memberVO.setPassword(&quot;java&quot;);
      memberVO.setEmail(&quot;test@test.com&quot;);

      String introduction = &quot;안녕하세요.\n반갑습니다.&quot;;
      memberVO.setIntroduction(introduction);

      //취미 세팅
      Map&lt;String, String&gt; hobbyMap = new HashMap&lt;String,String&gt;();
      hobbyMap.put(&quot;01&quot;, &quot;배구&quot;);
      hobbyMap.put(&quot;02&quot;, &quot;EDM Music&quot;);
      hobbyMap.put(&quot;03&quot;, &quot;해리포터 시리즈&quot;);

      model.addAttribute(&quot;hobbyMap&quot;,hobbyMap);

      //성별 세팅
      Map&lt;String, String&gt; genderMap = new HashMap&lt;String, String&gt;();
      genderMap.put(&quot;Male&quot;,&quot;남성&quot;);
      genderMap.put(&quot;Female&quot;,&quot;여성&quot;);
      genderMap.put(&quot;other&quot;,&quot;기타&quot;);

      //session.setAttribute(&quot;gender&quot;,&quot;genderMap&quot;); 처럼
      model.addAttribute(&quot;genderMap&quot;,genderMap);
      //model에는 memberVO객체와 hobbyMap, genderMap이 들어가 있음

      return &quot;member/joinForm&quot;;
      //model은 jsp를, ModelAndView는 ModelAndView 객체를 리턴
    }

    //요청과 메소드를 매핑(연결)하는 어노테이션
    @RequestMapping(value=&quot;/insert&quot;, method=RequestMethod.POST)
    //기본이 GET이기 때문에, GET방식이라면 method=RequestMethod.GET 생략 가능
    //기존에는 ModelAndView로 model의 영역과 view의 영역을 같이 사용했음
    //public ModelAndView insertPost(@RequestParam Map&lt;String, Object&gt; map) {
//        ModelAndView mav = new ModelAndView(map);
//        mav.setViewName(&quot;member/result&quot;);
//        return mav;

    //이제는 model과 view를 분리한다!
    public String insertPost(@ModelAttribute MemberVO memberVO) { //@ModelAttribute를 사용해서 jsp안에 있는 VO로 받을 수 있다.

    //sysout 안쓰고 logger로 찍기
    logger.info(memberVO.getMemberid());
    logger.info(memberVO.getName());
    logger.info(memberVO.getPassword());
    logger.info(memberVO.getEmail());
    logger.info(memberVO.getIntroduction());
    List&lt;String&gt; hobbyList = memberVO.getHobbyList();
    for(String hobby : hobbyList){
        logger.info(hobby);    
    }
    String[] hobbyArray = memberVO.getHobbyArray();
    for(String str : hobbyArray) {
        logger.info(str);
    }
    //성별
    logger.info(memberVO.getGender());

    //뷰의 경로(mav.setViewName(&quot;member/result&quot;))
    //forwarding 방식
    return &quot;member/result&quot;;

   }

  // public ModelAndView list(ModelAndView mav) { 로 하거나 아래처럼
  // String은 뷰의 경로, list(Model model)는 모델 데이터
      // 속성이 하나이면 @RequestMapping(value=&quot;/member/list&quot;, method = RequestMethod.GET)으로 안쓰고 아래처럼 생략가능
    @RequestMapping(&quot;/list&quot;)
    public String list(Model model){
        List&lt;MemberVO&gt; memberVO = this.memberService.select();

        // 1) 데이터를 담아서
        model.addAttribute(&quot;list&quot;, memberVO);

        // 2) 뷰로 보낸다
        // 뷰의 경로, forwarding 방식(데이터를 끌어감)
        return &quot;member/list&quot;;
    }

}
</code></pre><h2 id="dao">Dao</h2>
<h4 id="memberdaojava">MemberDao.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

// spring 프레임워크가 자바 빈(=객체)으로 등록해서 관리할 수 있도록 어노테이션 달기
@Repository
public class MemberDao {

    //sqlSessionTemplate 타입 객체를 MemberDao 객체에 주입(사용가능하게) 함
    @Autowired
    SqlSessionTemplate sqlSessionTemplate; // root-context 객체에 있는 id와 같아야 함

    //회원가입
    public int insert(MemberVO memberVO) {
        //namespace.id
        //result가 0이면 입력실패, 1이면 입력 성공
        int result = this.sqlSessionTemplate.insert(&quot;member.insert&quot;, memberVO);

        return result;
    }
    //회원 정보 목록
    public List&lt;MemberVO&gt; select(){
        //namespace.id
        return this.sqlSessionTemplate.selectList(&quot;member.select&quot;);
    }

}</code></pre><h2 id="service">Service</h2>
<h4 id="memberservicejava">MemberService.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;
/*
* 스프링은 직접 클래스를 생성하는 것을 지양(안함)하고,
* 인터페이스를 통해 접근하는 것을 권장하는 프레임워크.
*/

public interface MemberService {
    //메소드 시그니처 처리
    //회원가입
    public int insert(MemberVO memberVO);

    //회원정보 목록
    List&lt;MemberVO&gt; select();
}

</code></pre><h4 id="memberserviceimpljava">MemberServiceImpl.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

//spring 프레임 워크가 자바 빈(=객체)으로 등록해서 관리할 수 있도록 어노테이션 달기
@Service
public class MemberServiceImpl implements MemberService {
    //DI(의존성 주입)
    //데이터베이스에 접근을 위해 MemberDao 인스턴스를 주입받음
    @Autowired
    MemberDao memberDao;

    //회원가입
    @Override
    public int insert(MemberVO memberVO) {
        return memberDao.insert(memberVO);
    }

    //회원정보 목록
    @Override
    public List&lt;MemberVO&gt; select(){
        return memberDao.select();
    }
}

</code></pre><h2 id="view">View</h2>
<h4 id="joinformjsp">joinForm.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%@ taglib prefix=&quot;form&quot; uri=&quot;http://www.springframework.org/tags/form&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;회원가입&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!--
스프링 폼 태그 라이브러리 &lt;form:&gt;
    - 스프링 폼은 HTML 폼을 표시하기 위한 태그 라이브러리
    - 스프링 폼을 이용하면 HTML 폼과 자바 객체를 쉽게 바인딩할 수 있음

컨트롤러에서 사용하는 modelAttribute가 정의되어 있어야 아래 modelAttribute가 정의되어 있어야 아래 modelAtrribute=&quot;memberVO&quot; 처럼 사용할 수 있음

여기서 path : 폼 팡목에 바인딩되는 폼 객체(Controller에 있음)의 프로퍼티를 지정
--&gt;
&lt;form:form modelAttribute=&quot;memberVO&quot; method=&quot;post&quot; action=&quot;/member/insert&quot;&gt;
    &lt;p&gt;
        아이디 : &lt;form:input path=&quot;memberid&quot; /&gt;
        &lt;font color=&quot;red&quot;&gt;
            &lt;form:errors path=&quot;memberid&quot; /&gt;
        &lt;/font&gt;
    &lt;/p&gt;
    &lt;p&gt;
        이름 : &lt;form:input path=&quot;name&quot; /&gt;
        &lt;font color=&quot;red&quot;&gt;
            &lt;form:errors path=&quot;name&quot; /&gt;
        &lt;/font&gt;
    &lt;/p&gt;
    &lt;p&gt;
        비밀번호 : &lt;form:password path=&quot;password&quot; /&gt;
        &lt;font color=&quot;red&quot;&gt;
            &lt;form:errors path=&quot;password&quot; /&gt;
        &lt;/font&gt;
    &lt;/p&gt;
    &lt;p&gt;
        이메일 : &lt;form:input path=&quot;email&quot; /&gt;
        &lt;font color=&quot;red&quot;&gt;
            &lt;form:errors path=&quot;email&quot; /&gt;
        &lt;/font&gt;
    &lt;/p&gt;
    &lt;p&gt;
        자기소개 :
        &lt;form:textarea path=&quot;introduction&quot; rows=&quot;6&quot; cols=&quot;30&quot; /&gt;
    &lt;/p&gt;
    &lt;p&gt;
        취미 :
        &lt;form:checkboxes path=&quot;hobbyList&quot; items=&quot;${hobbyMap}&quot; /&gt;
        &lt;!--
            items는 눈에 보여질 대상.
            컨트롤러에서 model.addAttribute(&quot;hobbyMap&quot;, hobbyMap);로 보낸 것을 el태그로 받음
        --&gt;
    &lt;/p&gt;
    &lt;p&gt;
        취미(hobbyArray) :
        &lt;form:checkbox path=&quot;hobbyArray&quot; value=&quot;안철수&quot; label=&quot;안철수&quot; /&gt;&lt;br&gt;
        &lt;form:checkbox path=&quot;hobbyArray&quot; value=&quot;이재명&quot; label=&quot;이재명&quot; /&gt;&lt;br&gt;
        &lt;form:checkbox path=&quot;hobbyArray&quot; value=&quot;윤석열&quot; label=&quot;윤석열&quot; /&gt;&lt;br&gt;
        &lt;form:checkbox path=&quot;hobbyArray&quot; value=&quot;심상정&quot; label=&quot;심상정&quot; /&gt;&lt;br&gt;
    &lt;/p&gt;
    &lt;p&gt;
        성별 : 
&lt;%--
        방법 두가지
        1)
        &lt;form:radiobuttons path=&quot;gender&quot; items=&quot;${genderMap}&quot; /&gt; 처럼 사용하거나
        2)
--%&gt;
        &lt;form:radiobutton path=&quot;gender&quot; value=&quot;Male&quot; label=&quot;Male&quot; /&gt;
        &lt;form:radiobutton path=&quot;gender&quot; value=&quot;Female&quot; label=&quot;Female&quot; /&gt;
        &lt;form:radiobutton path=&quot;gender&quot; value=&quot;Other&quot; label=&quot;Other&quot; /&gt;
    &lt;/p&gt;

    &lt;form:button name=&quot;register&quot;&gt;등록&lt;/form:button&gt;
&lt;/form:form&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><h4 id="listjsp">list.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;회원목록&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!--
varStatus
*1) count : 1부터 순서적으로 1씩 증가
*2) index : 0부터 순서적으로 1씩 증가
3) current : 현재의 아이템
4) first : 처음 반복되는 것인지 반환
5) last : 마지막 반복인지 반환
6) begin : 시작값
7) end : 끝값
8) step :증가값  
--&gt;
&lt;table border=&quot;1&quot;&gt;
    &lt;tr&gt;
        &lt;th&gt;번호&lt;/th&gt;
        &lt;th&gt;이름&lt;/th&gt;
        &lt;th&gt;이메일&lt;/th&gt;
        &lt;th&gt;가입일&lt;/th&gt;
        &lt;th&gt;보유 카드번호&lt;/th&gt;
    &lt;/tr&gt;
    &lt;c:forEach var=&quot;memberVO&quot; items=&quot;${list}&quot; varStatus=&quot;stat&quot;&gt; &lt;!-- items는 컨트롤러 model.addAttribute(&quot;list&quot;, memberVO)의 키값 --&gt;
    &lt;tr&gt;
        &lt;td&gt;${stat.count}&lt;/td&gt;
        &lt;td&gt;${memberVO.name}&lt;/td&gt;
        &lt;td&gt;${memberVO.email}&lt;/td&gt;
        &lt;td&gt;${memberVO.regdate}&lt;/td&gt;
        &lt;td&gt;
            &lt;c:forEach var=&quot;cardVO&quot; items=&quot;${memberVO.cardList}&quot;&gt;
                ${cardVO.no}&amp;nbsp;
            &lt;/c:forEach&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre><h2 id="xml">xml</h2>
<h4 id="member_sqlxml">member_SQL.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE mapper
  PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
  &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;
&lt;mapper namespace=&quot;member&quot;&gt;
    &lt;!--
    기본키는 id, 아닌 컬럼은 result에
    property : vo의 멤버변수명
    column : select문의 컬럼명

    &lt;assciation&gt;은 1:1, &lt;collection&gt;은 1:N
    --&gt;

    &lt;!-- 아래에서 만든 resultMap의 상세 속성 적기--&gt;
    &lt;resultMap type=&quot;memberVO&quot; id=&quot;memberMap&quot;&gt;
        &lt;id property=&quot;memberid&quot; column=&quot;MEMBERID&quot; /&gt;
        &lt;result property=&quot;password&quot; column=&quot;PASSWORD&quot;/&gt;
        &lt;result property=&quot;name&quot; column=&quot;NAME&quot;/&gt;
        &lt;result property=&quot;email&quot; column=&quot;EMAIL&quot;/&gt;
        &lt;result property=&quot;regdate&quot; column=&quot;REGDATE&quot;/&gt;
        &lt;result property=&quot;introduction&quot; column=&quot;INTRODUCTION&quot;/&gt;
        &lt;collection property=&quot;cardList&quot; resultMap=&quot;cardMap&quot;&gt;
        &lt;/collection&gt;
    &lt;/resultMap&gt;    

    &lt;!-- 위 &lt;collection&gt;에서 선언한 resultMap의 상세 속성을 다시 적는다--&gt;
    &lt;!-- type=&quot;cardVO&quot;은 mybatisAlias.xml에 추가한 이름과 같게 사용 --&gt;
    &lt;resultMap type=&quot;cardVO&quot; id=&quot;cardMap&quot;&gt;
        &lt;result property=&quot;memberid&quot; column=&quot;MEMBERID&quot; /&gt;
        &lt;result property=&quot;no&quot; column=&quot;NO&quot; /&gt;
        &lt;result property=&quot;vaildMonth&quot; column=&quot;VALID_MONTH&quot; /&gt;
    &lt;/resultMap&gt;


    &lt;!-- 회원가입 --&gt;
    &lt;insert id=&quot;insert&quot; parameterType=&quot;memberVO&quot;&gt; &lt;!-- typeAlias에서 정한 거! --&gt;
        INSERT INTO MEMBER(MEMBERID,PASSWORD,NAME,EMAIL,REGDATE)
        VALUES(#{memberid},#{password},#{name},#{email},SYSDATE)    
    &lt;/insert&gt;

    &lt;!-- 회원정보 목록 --&gt;
    &lt;select id=&quot;select&quot; resultMap=&quot;memberMap&quot;&gt;
        SELECT M.MEMBERID, M.PASSWORD, M.NAME, M.EMAIL, M.REGDATE, M.INTRODUCTION
                , C.NO, C.VALID_MONTH, C.MEMBERID
        FROM MEMBER M, CARD C
        WHERE M.MEMBERID = C.MEMBERID
        ORDER BY C.NO DESC
    &lt;/select&gt;

&lt;/mapper&gt;</code></pre><h4 id="mybatisaliasxml">mybatisAlias.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE configuration
  PUBLIC &quot;-//mybatis.org//DTD Config 3.0//EN&quot;
  &quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;&gt;
&lt;configuration&gt;
    &lt;!-- 
        [마이바티스]
           스프링에서 &quot;_&quot;를 사용한 컬럼명을 사용 시 테이블 컬럼명에 &quot;_&quot;가 있을 경우 카멜케이스로 읽어줌
        member 테이블 컬럼명이 mem_id일 경우 memId로 사용가능하도록 도와줌
        cart 테이블에 cart_prod -&gt; cartProd
    --&gt;
    &lt;settings&gt;
        &lt;setting name=&quot;mapUnderscoreToCamelCase&quot; value=&quot;true&quot; /&gt;
    &lt;/settings&gt;

    &lt;!-- 자주 사용하는 타입의 별칭 --&gt;
    &lt;typeAliases&gt;
        &lt;typeAlias type=&quot;kr.or.ddit.BookVO&quot; alias=&quot;bookVO&quot;/&gt;
        &lt;typeAlias type=&quot;kr.or.ddit.MemberVO&quot; alias=&quot;memberVO&quot;/&gt;
        &lt;typeAlias type=&quot;kr.or.ddit.CardVO&quot; alias=&quot;cardVO&quot;/&gt;
    &lt;/typeAliases&gt;
&lt;/configuration&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 22강 - Member 게시판 CRUD 구현하기]]></title>
            <link>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-22%EA%B0%95-Member-%EA%B2%8C%EC%8B%9C%ED%8C%90-CRUD-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hazel_jo/%EC%8A%A4%ED%94%84%EB%A7%81-22%EA%B0%95-Member-%EA%B2%8C%EC%8B%9C%ED%8C%90-CRUD-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 07 Feb 2022 09:13:27 GMT</pubDate>
            <description><![CDATA[<h3 id="vo">VO</h3>
<h4 id="membervojava">MemberVO.java</h4>
<pre><code>package kr.or.ddit;

import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

//자바빈 클래스 
//lombok사용해서 아래처럼 편하게 만들 수 있지만 POJO(초기의 순수한 자바)에 위배됨
//@Setter
//@Getter
//@ToString

public class MemberVO {
    private String memberid;
    private String password;
    private String name;
    private String email;
    //pattern : date 넣을 때 요 형식으로 써주세요~
    //@DateTimeFormat(pattern=&quot;yyyy/MM/dd&quot;)꼭 써줘야 date형식으로 들어간다
    @DateTimeFormat(pattern=&quot;yyyy/MM/dd&quot;)
    private Date regdate;

    //기본 생성자
    public MemberVO() {
        super();
        // TODO Auto-generated constructor stub
    }

    //파라미터 던져주면 멤버변수에 자동으로 세팅된다
    public MemberVO(String memberid, String password, String name, String email, Date regdate) {
        super();
        this.memberid = memberid;
        this.password = password;
        this.name = name;
        this.email = email;
        this.regdate = regdate;
    }

    public String getMemberid() {
        return memberid;
    }
    public void setMemberid(String memberid) {
        this.memberid = memberid;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Date getRegdate() {
        return regdate;
    }
    public void setRegdate(Date regdate) {
        this.regdate = regdate;
    }

    //비밀번호 변경 기능 구현 시 사용
    public boolean matchPassword(String pwd) {
        return password.contentEquals(pwd);
    }


    @Override
    public String toString() {
        return &quot;MemberVO [memberid=&quot; + memberid + &quot;, password=&quot; + password + &quot;, name=&quot; + name + &quot;, email=&quot; + email
                + &quot;, regdate=&quot; + regdate + &quot;]&quot;;
    }

}
</code></pre><h3 id="controller">Controller</h3>
<h4 id="membercontrollerjava">MemberController.java</h4>
<pre><code>package kr.or.ddit;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

//spring 프레임워크가 자바 빈(=객체)으로 등록해서 관리할 수 있도록 어노테이션 달기
@Controller
public class MemberController {

    @RequestMapping(&quot;/member/insert&quot;)
    public String insert(Model model) {

        model.addAttribute(&quot;memberVO&quot;, new MemberVO());

        return &quot;member/joinForm&quot;;
        //model은 jsp를 return, ModelAndView는 ModelAndView 객체를 리턴 
    }
}
</code></pre><h3 id="dao">Dao</h3>
<h4 id="memberdaojava">MemberDao.java</h4>
<pre><code>package kr.or.ddit;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

//spring 프레임워크가 자바 빈(=객체)으로 등록해서 관리할 수 있도록 어노테이션 달기
@Repository
public class MemberDao {

    //sqlSessionTemplate 타입 객체를 BookDao 객체에 주입(사용가능하게) 함
    @Autowired
    SqlSessionTemplate sqlSessionTemplate; //root-context 객체에 있는 id와 같아야 함

    //회원가입
    public int insert(MemberVO memberVO) {
        //namaspace.id
        //result가 0이면 입력 실패, 1이면 입력 성공
        int result = this.sqlSessionTemplate.insert(&quot;member.insert&quot;, memberVO);

        return result;
    }
}
</code></pre><h3 id="service">Service</h3>
<h4 id="memberservicejava">MemberService.java</h4>
<pre><code>package kr.or.ddit;

/*
* 스프링은 직접 클래스를 생성하는 것을 지양(안함)하고,
* 인터페이스를 통해 접근하는 것을 권장하는 프레임워크.
*/

public interface MemberService {
    //회원가입
    public int insert(MemberVO memberVO);

}
</code></pre><h4 id="memberserviceimpljava">MemberServiceImpl.java</h4>
<pre><code>package kr.or.ddit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


//spring 프레임워크가 자바 빈(=객체)으로 등록해서 관리할 수 있도록 어노테이션 달기
@Service
public class MemberServiceImpl implements MemberService {
    //DI(의존성 주입)
    //데이터베이스에 접근을 위해  MemberDao 인스턴스를 주입받음
    @Autowired
    MemberDao memberDao;

    //회원가입
    @Override
    public int insert(MemberVO memberVO) {
        return memberDao.insert(memberVO);
    }

}
</code></pre><h3 id="jsp">JSP</h3>
<p><strong>스프링 폼 태그 라이브러리</strong></p>
<ul>
<li>스프링 폼은 HTML 폼을 표시하기 위한 태그 라이브러리</li>
<li>스프링 폼을 이용하면 HTML 폼과 자바 객체를 쉽게 바인딩할 수 있음 <blockquote>
<p>form 태그 라이브러리 추가하기
&lt;%@ taglib prefix=&quot;form&quot; uri=&quot;<a href="http://www.springframework.org/tags/form&quot;">http://www.springframework.org/tags/form&quot;</a> %&gt;</p>
</blockquote>
</li>
</ul>
<p><strong>joinForm.jsp</strong></p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%@ taglib prefix=&quot;form&quot; uri=&quot;http://www.springframework.org/tags/form&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;회원가입&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- 
스프링 폼 태그 라이브러리
 - 스프링 폼은 HTML 폼을 표시하기 위한 태그 라이브러리
 - 스프링 폼을 이용하면 HTML 폼과 자바 객체를 쉽게 바인딩할 수 있음
--&gt;
&lt;form:form modelAttribute=&quot;memberVO&quot; method=&quot;post&quot; action=&quot;/member/insert&quot;&gt;
    &lt;p&gt;
        아이디 : &lt;form:input path=&quot;memberid&quot; /&gt;
        &lt;font color=&quot;red&quot;&gt;
            &lt;form:errors path=&quot;memberid&quot; /&gt;
        &lt;/font&gt;
    &lt;/p&gt;
    &lt;p&gt;
        이름 : &lt;form:input path=&quot;name&quot; /&gt;
        &lt;font color=&quot;red&quot;&gt;
            &lt;form:errors path=&quot;name&quot; /&gt;
        &lt;/font&gt;
    &lt;/p&gt;
    &lt;p&gt;
        비밀번호 : &lt;form:input path=&quot;password&quot; /&gt;
        &lt;font color=&quot;red&quot;&gt;
            &lt;form:errors path=&quot;password&quot; /&gt;
        &lt;/font&gt;
    &lt;/p&gt;
    &lt;p&gt;
        이메일 : &lt;form:input path=&quot;email&quot; /&gt;
        &lt;font color=&quot;red&quot;&gt;
            &lt;form:errors path=&quot;email&quot; /&gt;
        &lt;/font&gt;
    &lt;/p&gt;
    &lt;form:button name=&quot;register&quot;&gt;등록&lt;/form:button&gt;
&lt;/form:form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="xml">xml</h3>
<h4 id="member_sqlxml">member_SQL.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE mapper
  PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
  &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;
&lt;mapper namespace=&quot;member&quot;&gt;
    &lt;!-- 회원가입 --&gt;
    &lt;insert id=&quot;insert&quot; parameterType=&quot;memberVO&quot;&gt; &lt;!-- typeAlias에서 정한거 --&gt;
        INSERT INTO MEMBER(MEMBERID,PASSWORD,NAME,EMAIL,REGDATE)
        VALUES(#{memberid},#{password},#{name},#{email},SYSDATE)
    &lt;/insert&gt;
&lt;/mapper&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 21강 - Book 게시판 CRUD 구현하기]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-21%EA%B0%95-Book-%EA%B2%8C%EC%8B%9C%ED%8C%90-CRUD-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-21%EA%B0%95-Book-%EA%B2%8C%EC%8B%9C%ED%8C%90-CRUD-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 03 Feb 2022 14:09:39 GMT</pubDate>
            <description><![CDATA[<p>JSP-&gt;Controller-&gt;Service-&gt;Dao-&gt;xml-&gt;DB</p>
<p>서비스는 인터페이스. 스프링은 껍데기인 인터페이스를 Implement한 serviceImpl을 사용해서 현실화함</p>
<p>왜 인터페이스를 거쳐서 jsp-&gt;service-&gt;serviceImpl로 가야하는지?
-&gt; 컨트롤러에서 ServiceImpl을 쓸 때, Service interface를 @autowired함
-&gt; 스프링은 interface를 거쳐서 요청을 세분화하기 때문(확장의 여지를 남겨놓는다)</p>
<h3 id="vo">VO</h3>
<h4 id="bookvojava">BookVO.java</h4>
<pre><code>package kr.or.ddit;

public class BookVO {
    private int bookId;
    private String title;
    private String category;
    private int price;
    private String insertDate;

    public int getBookId() {
        return bookId;
    }
    public void setBookId(int bookId) {
        this.bookId = bookId;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getCategory() {
        return category;
    }
    public void setCategory(String category) {
        this.category = category;
    }
    public int getPrice() {
        return price;
    }
    public void setPrice(int price) {
        this.price = price;
    }
    public String getInsertDate() {
        return insertDate;
    }
    public void setInsertDate(String insertDate) {
        this.insertDate = insertDate;
    }

    @Override
    public String toString() {
        return &quot;BookVO [bookId=&quot; + bookId + &quot;, title=&quot; + title + &quot;, category=&quot; + category + &quot;, price=&quot; + price
                + &quot;, insertDate=&quot; + insertDate + &quot;]&quot;;
    }


}
</code></pre><h3 id="controller">Controller</h3>
<h4 id="bookcontrollerjava">BookController.java</h4>
<pre><code>package kr.or.ddit;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

//컨트롤러 어노테이션(Annotation)
//어노테이션이 있는 클래스
//스프링프레임워크(디자인패턴 + 라이브러리 집합)가 웹 브라우저(크롬)의 요청(request)을 받아들이는 컨트롤러라고 인지해서
//자바 빈(java Bean)으로 등록해서 관리

@Controller
public class BookController {
    private static final Logger logger = LoggerFactory.getLogger(BookController.class);
    @Autowired
    BookService bookService;
    //Model : 컨트롤러가 반환할 데이터(VO객체, List, List&lt;VO&gt;, Map, List&lt;Map&gt;, JSON)를 담당
    //            반환? 1)forward(데이터 담기 O)    2) redirect(데이터 담기 X)

    //View : 화면 담당
    //localhost:5090/create

    //요청을 매핑한다
    @RequestMapping(value=&quot;/create&quot;, method=RequestMethod.GET)
    public ModelAndView create() {
        ModelAndView mav = new ModelAndView();
        //원래는 localhost:8090/WEB-INF/view/book/create.jsp

        //servlet-context.xml에서...
        //prefix : /WEB-INF/views/
        mav.setViewName(&quot;book/create&quot;);
        //suffix : .jsp
        return mav;
    }

    //HTTP파라미터 : 웹 브라우저(크롬)에서 서버(톰캣)로 전달하는 데이터
    //                제목, 분류, 가격.. 등이 파라미터로 넘어옴
    //?title=톰소여의모험&amp;category=소설&amp;price=10000
    //스프링은 HTTP파라미터를 자바 메소드의 파라미터로 변환해서 컨트롤러 메소드를 호출해줌
    //map으로 받을 때에는 RequestParam 어노테이션
    //{&quot;title&quot;:&quot;톰소여의 모험&quot;,&quot;category&quot;:&quot;소설&quot;...}
    //vo로 받을때에는 ModelAtrribute 어노테이션
    @RequestMapping(value=&quot;/create&quot;, method=RequestMethod.POST)
    public ModelAndView createPost(ModelAndView mav,
            @RequestParam Map&lt;String, Object&gt;map) { //@RequestParam 요청 파라미터를 받아주는 어노테이션
        logger.info(&quot;map : &quot; + map.toString());

        BookVO bookVO = new BookVO();
        //result : insert된 book_id의 값(p.k)
        int result = bookService.insert(mapToVO(map));
        logger.info(&quot;result : &quot; + result);

        if(result==0) { //insert가 안됨 -&gt; 책 입력 화면으로 돌아가라
            //데이터를 담지 않고 단순히 되돌아감.. --&gt; forward아닌 redirect!
            mav.setViewName(&quot;redirect:/create&quot;);
        }else { //insert 성공 -&gt; 상세 페이지를 요청
            //?bookId=1 &lt;-- 쿼리스트링 or 파라미터 목록
            mav.setViewName(&quot;redirect:/detail?bookId=&quot;+result);
        }

        return mav;
    }

    //20220127숙제
    //map -&gt; BookVO로 변환(public BookVO mapToVo(map)..메서드로 처리)
    public BookVO mapToVO(Map&lt;String, Object&gt;map) {
        BookVO bookVO = new BookVO();
        //bookVO.setTitle(map.get(&quot;title&quot;)); map에서 title뽑아내면 Object형이기 때문에 에러(나머지도 마찬가지!)
        bookVO.setTitle((String)map.get(&quot;title&quot;));//형변환
        bookVO.setCategory((String)map.get(&quot;category&quot;));
        bookVO.setPrice(Integer.parseInt((String)map.get(&quot;price&quot;)));
        return bookVO;
    }

    //book 상세보기
    //파라미터 목록(쿼리스트링) : bookId=1
    //{bookId,1}
    @RequestMapping(value=&quot;detail&quot;, method=RequestMethod.GET)
    public ModelAndView detail(@RequestParam Map&lt;String,Object&gt; map) {
        logger.info(&quot;map : &quot; + map);

        BookVO bookVO = new BookVO();
        bookVO.setBookId(Integer.parseInt((String)map.get(&quot;bookId&quot;)));
        //{bookID:1,나머지는 null}
        logger.info(&quot;bookVO(before) : &quot; + bookVO.toString());

        //상세보기 데이터 가져오기
        bookVO = this.bookService.detail(bookVO);

        //{bookId:1,나머지도 있음}
        logger.info(&quot;bookVO(after) : &quot; + bookVO.toString());

        ModelAndView mav = new ModelAndView();
        //book/detail : 뷰의 경로
        //forwarding
        mav.addObject(&quot;data&quot;, bookVO); //데이터를 담음
        mav.setViewName(&quot;book/detail&quot;);

        return mav;
    }
    // /update?bookId=3
    // 쿼리스트링 : bookId=3
    // map : {bookId:3}
    // 책 수정화면 = 책 입력화면(jsp) + 책 상세 서비스 로직
    @RequestMapping(value = &quot;/update&quot;, method=RequestMethod.GET)
    public ModelAndView update(@RequestParam Map&lt;String, String&gt; map) {
        logger.info(&quot;map : &quot; + map.get(&quot;bookId&quot;));

        //책 상세 서비스 로직 사용
        BookVO bookVO = new BookVO();
        bookVO.setBookId(Integer.parseInt(map.get(&quot;bookId&quot;)));
        bookVO = this.bookService.detail(bookVO);

        ModelAndView mav = new ModelAndView();
        //request.setAttribute(&quot;data&quot;, bookVO);
        mav.addObject(&quot;data&quot;, bookVO);
        //forwarding : setViewName()로 페이지 이동값 세팅
        mav.setViewName(&quot;book/update&quot;);

        return mav;
    }

    //책 수정 post
    @RequestMapping(value=&quot;/update&quot;, method=RequestMethod.POST)
    public ModelAndView updatePost(@ModelAttribute BookVO bookVO, ModelAndView mav) {
        logger.info(&quot;bookVO : &quot; + bookVO.toString());
        //true : update성공, false : update실패
        boolean isUpdateSuccess = this.bookService.update(bookVO);

        if(isUpdateSuccess) { //성공
            //상세페이지로 이동
            mav.setViewName(&quot;redirect:/detail?bookId=&quot;+bookVO.getBookId());
        }else {//실패
            //책 수정화면으로 돌아가기
            //방법1)
            //mav.setViewName(&quot;redirect:/update?bookId=&quot;+bookVO.getBookId());
            //방법2)
            Map&lt;String,String&gt; map = new HashMap&lt;String, String&gt;();
            map.put(&quot;bookID&quot;, &quot;&quot; + bookVO.getBookId());
            mav = this.update(map);
        }
        return mav;
    }
    //책 목록 리스트 + 검색 기능 추가
    //http://localhost:8090/list?keyword=야호
    //map : {&quot;keyword&quot;:&quot;야호&quot;} 이렇게 들어간다~
    @RequestMapping(value=&quot;/list&quot;, method=RequestMethod.GET)
    //public ModelAndView allList(ModelAndView mav, @RequestParam String keyword) { //방법 1. 야호를  String 매개변수로 받음
    public ModelAndView allList(ModelAndView mav, @RequestParam Map&lt;String, Object&gt; map) {//방법2. value를 Object로 해서 모든 형태의 데이터를 받을 수 있게 함
    //ModelAndView mav = new ModelAndView로 객체 생성안해도 파라미터에 ModelAndView mav 넣으면 만들어짐
        List&lt;BookVO&gt; allList = this.bookService.allList(map);
        //데이터를 VIEW(jsp)에 전달할 수 있도록 mav 객체에 add함
        mav.addObject(&quot;data&quot;,allList);
        //forwarding
        mav.setViewName(&quot;book/list&quot;); //조립하기, servlet-context의 prefix에 /WEB-INF/views/ 마지막에 /붙으므로 book앞에 /안넣어도 됨
        return mav;
    }
    // &lt;form method=&quot;post&quot; action=&quot;/delete&quot;&gt;
    //&lt;input type=&quot;hidden&quot; name=&quot;bookId&quot; value=&quot;${data.bookId}&quot; /&gt;
    @RequestMapping(value = &quot;/delete&quot;, method = RequestMethod.POST)
    public ModelAndView deletePost(@RequestParam Map&lt;String,Object&gt; map, ModelAndView mav) {
        //map : {bookId=3} 처럼 키와 값으로 구성되어 있음
        boolean isDeleteSuccess = this.bookService.delete(map);

        if(isDeleteSuccess) { //성공
            //목록 화면으로  redirect(요청흐름을 이동시킴. 단, 데이터는 이동 못함)
            mav.setViewName(&quot;redirect:/list&quot;);
        }else { //실패
            //상세 화면으로 redirect
            mav.setViewName(&quot;redirect:/detail?bookId=&quot;+map.get(&quot;bookId&quot;).toString());
        }
        return mav;
    }

    //주문하면 아래 메서드로 매핑
    @RequestMapping(value=&quot;/make&quot;, method=RequestMethod.GET)
    public ModelAndView make() {
        //라이더에 태워서 짬뽕 배송
        ModelAndView mav = new ModelAndView();
        //짬뽕 위치 알려주기
        mav.setViewName(&quot;book/make&quot;);
        //배송하기
        return mav;
    }
    //RequestMapping 어노테이션 : 웹 브라우저의 요청에 실행되는 자바 메소드를 지정해줌
    //method : 속성. http 요청 메소드를 의미함
    //            1) GET : 데이터를 변경하지 않는 경우 사용
    //            2) POST : 데이터가 변경될 경우 사용
    //웹 브라우저에 화면을 보여줄 뿐 데이터의 변경이 일어나지 않으므로 GET 메소드를 사용한 것임
    //jjajang() 메소드는 웹 브라우저에서 /jjajang 주소가 GET 방식으로 입력되었을 때 book/jjajang 경로의 뷰를 보여줌

    @RequestMapping(value=&quot;/jjajang&quot;, method=RequestMethod.GET)
    public ModelAndView jjajang(ModelAndView mav) { //라이더 생성
        // book/jjajang : 뷰의 경로
        mav.setViewName(&quot;book/jjajang&quot;);
        return mav;
    }
}
</code></pre><h3 id="dao">Dao</h3>
<h4 id="bookdaojava">BookDao.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;
import java.util.Map;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

//매퍼 xml을 실행해주는 클래스.
//@Repository 어노테이션을 붙여서 이 클래스는 데이터에 접근하는 클래스라는 것을
//Spring에게 알려줌
//Spring이 데이터를 관리하는 클래스라고 인지해서 자바 빈(java bean)으로 등록해서 관리
@Repository
public class BookDao {
    //sqlSessionTemplate 사용
    /*
     * new 키워드를 통해 직접 생성 안함.
     * 의존성 주입(Dependency Injection - DI)을 통해 주입받음.
     * 스프링은 미리 만들어놓은 sqlSessionTemplate 타입 객체를 BookDao 객체에 주입
     * 이 과정은 자동으로 스프링에서 실행되며, 개발자가 직접 객체를 생성하지 않음(Inversion of Control - IoC) 
     */

    @Autowired
    SqlSessionTemplate sqlSessionTemplate;

    public int insert(BookVO bookVO) {
        //book_SQL.xml파일에서
        //namespace=&quot;book&quot;
        //id=&quot;insert&quot;
        //book.inert : 매퍼 쿼리 명
        //bookVO : 두번째 인수.. 쿼리에 전달할 데이터(String, int, VO, Map 등이 올 수 있음)
        return this.sqlSessionTemplate.insert(&quot;book.insert&quot;,bookVO);
    }

    //책 상세보기
    public BookVO detail(BookVO bookVO) {
        //.selectOne 메소드 : 1행을 가져올 때 사용
        //결과 행 수가 0이면 null반환
        //결과 행 수가 2 이상일 때 TooManyResultxException 예외 발생
        //(namespace.id, 파라미터0)
        return sqlSessionTemplate.selectOne(&quot;book.detail&quot;,bookVO);
    }

    //책 수정하기
    public boolean update(BookVO bookVO) {
        //(namespace.id, 파라미터)
        //update 후에 영향받은 행의 수를 받음
        int result = this.sqlSessionTemplate.update(&quot;book.update&quot;,bookVO);
        //0보다 크다는 것은 update가 성공했다는 의미
        return result &gt; 0;
    }
    //책 목록
    //map : {&quot;keyword&quot;:&quot;야호&quot;}
    public List&lt;BookVO&gt; allList(Map&lt;String, Object&gt; map){
        //.selectList() 메소드는 결과 집합의 목록을 반환
        // List 타입으로 읽어들일 수 있음
        return this.sqlSessionTemplate.selectList(&quot;book.allList&quot;, map); 
    }

    //책 삭제하기
    public boolean delete(Map&lt;String,Object&gt; map) {
        //RDBMS(Relational DataBase Manegement System)에서 (테이블을 relation이라고도 함!)
        //delete 구문은 update 구문처럼 where 조건에 일치하는
        //모든 행을 삭제하므로 영향을 받은 행의 수는 0 혹은 1 이상이 됨
        int result = this.sqlSessionTemplate.delete(&quot;book.delete&quot;, map);
        //result &gt; 0는 삭제가 잘 되었다는 의미..
        return result &gt; 0;
    }
}
</code></pre><h3 id="service">Service</h3>
<h4 id="bookservicejava">BookService.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;
import java.util.Map;

/*
 * 서비스 클래스는 비즈니스 클래스(기능)가 위치하는 곳임.
 * 스프링 MVC 구조에서 Controller - Service - DAO
 * 컨트롤러와 DAO를 연결하는 역할도 함
 * 
 * 스프링은 직접 클래스를 생성하는 것을 지양(안함)하고,
 * 인터페이스를 통해 접근하는 것을 권장하는 프레임워크.
 */
public interface BookService {
    //메소드 시그니쳐 처리
    //book 테이블로 insert
    public int insert(BookVO bookVO);
    //book 상세보기
    BookVO detail(BookVO bookVO);
    //book 수정하기
    boolean update(BookVO bookVO);
    //book 목록보기
    public List&lt;BookVO&gt; allList(Map&lt;String, Object&gt; map);
    //책 삭제하기
    boolean delete(Map&lt;String, Object&gt; map);
}
</code></pre><h4 id="bookserviceimpljava">BookServiceImpl.java</h4>
<pre><code>package kr.or.ddit;

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

//@Service : 어노테이션.. 스프링에게 이 클래스는 서비스 클래스임을 알려줌
//스프링이 자바 빈(java bean)으로 등록하여 관리
@Service
public class BookServiceImpl implements BookService {
    private static final Logger logger = LoggerFactory.getLogger(BookServiceImpl.class);
    //이미 자바빈으로 관리되고 있는 BookDao 객체를 @Autowired로 가져다 쓴다
    //IoC(제어의 역전) : 객체의 생성주기를 개발자가 하는 것이 아니라 스프링 프레임워크가 알아서 함
    //DI(의존성 주입)
    @Autowired
    BookDao bookDao;

    //부모 객체의 메소드를 재정의
    @Override
    public int insert(BookVO bookVO) {
        logger.info(&quot;bookVO : &quot; + bookVO.toString());
        int affectRowcount = this.bookDao.insert(bookVO); //인서트된 행의 수를 int로 받음

        if(affectRowcount == 1) { //입력이 성공
            //xml에서 selectkey에서 세팅된 그 값(max(book_id)+1)
            return bookVO.getBookId();
        }
        //입력 실패
        return 0;
    }
    //책 상세보기
    @Override
    public BookVO detail(BookVO bookVO) {
        return this.bookDao.detail(bookVO);
    }

    //책 수정하기
    @Override
    public boolean update(BookVO bookVO) {
        return this.bookDao.update(bookVO);
    }

    //책 목록
    @Override
    public List&lt;BookVO&gt; allList(Map&lt;String, Object&gt; map) {
        return this.bookDao.allList(map);
    }

    //책 삭제하기
    @Override
    public boolean delete(Map&lt;String,Object&gt; map) {
        return this.bookDao.delete(map);
    }

}
</code></pre><h3 id="jsp">JSP</h3>
<h4 id="createjsp">create.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;책 생성하기&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;책 생성하기&lt;/h1&gt;
    &lt;form method=&quot;post&quot; action=&quot;/create&quot;&gt;
        &lt;p&gt;제목 : &lt;input type=&quot;text&quot; name=&quot;title&quot;&gt;&lt;/p&gt;
        &lt;p&gt;카테고리 : &lt;input type=&quot;text&quot; name=&quot;category&quot;&gt;&lt;/p&gt;
        &lt;p&gt;가격 : &lt;input type=&quot;text&quot; name=&quot;price&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;저장&quot;&gt;&lt;/p&gt;

    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="detailjsp">detail.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;!-- 
BookController에서 mav 객체에 data라는 이름으로 select검색 결과를 넣었으므로
    mav.addObject(&quot;data&quot;,bookVO)
달러{data.title} 형식으로 사용하면 됨
 --&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;책 상세&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;책 상세&lt;/h1&gt;
&lt;p&gt;제목 : ${data.title}&lt;/p&gt;
&lt;p&gt;카테고리 : ${data.category}&lt;/p&gt;
&lt;p&gt;가격 : ${data.price}&lt;/p&gt;
&lt;p&gt;입력일 : ${data.insertDate}&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/update?bookId=${data.bookId}&quot;&gt;수정&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/list&quot;&gt;목록으로&lt;/a&gt;&lt;/p&gt;
&lt;form method=&quot;post&quot; action=&quot;/delete&quot;&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;bookId&quot; value=&quot;${data.bookId}&quot; /&gt;
    &lt;input type=&quot;submit&quot; value=&quot;삭제&quot;/&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="updatejsp">update.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;!-- 
BookController에서 mav 객체에 data라는 이름으로 select검색 결과를 넣었으므로
    mav.addObject(&quot;data&quot;,bookVO)
달러{data.title} 형식으로 사용하면 됨
 --&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;책 수정하기&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;책 수정&lt;/h1&gt;
&lt;!-- action이 없으면../update를 요청 단, method는 post --&gt;
&lt;form method=&quot;post&quot;&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;bookId&quot; value=&quot;${data.bookId}&quot; /&gt;
    &lt;p&gt;제목 : &lt;input type=&quot;text&quot; name=&quot;title&quot; value=&quot;${data.title}&quot; required /&gt;&lt;/p&gt;
    &lt;p&gt;카테고리 : &lt;input type=&quot;text&quot; name=&quot;category&quot; value=&quot;${data.category}&quot; required /&gt;&lt;/p&gt;
    &lt;p&gt;가격 : &lt;input type=&quot;text&quot; name=&quot;price&quot; value=&quot;${data.price}&quot; /&gt;&lt;/p&gt;
    &lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;저장&quot; /&gt;&amp;nbsp;
    &lt;input type=&quot;button&quot; value=&quot;취소&quot; onclick=&quot;javascript:location.href=&#39;/detail?bookId=${data.bookId}&#39;&quot;/&gt;&lt;/p&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="listjsp">list.jsp</h4>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;책 목록&lt;/title&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
function fn_create(){
    location.href=&quot;/create&quot;;
}

&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;책 목록&lt;/h1&gt;
&lt;p&gt;
    &lt;!--
        form 태그의 기본 HTTP 메소드는 GET
        action 속성을 생략하면 현재 url(/list)을 요청 
     --&gt;
     &lt;!-- &lt;form method=&quot;get&quot; action=&quot;/list&quot;&gt; --&gt;
    &lt;form&gt;
        &lt;input type=&quot;text&quot; name=&quot;keyword&quot; value=&quot;${param.keyword}&quot; /&gt;
        &lt;input type=&quot;submit&quot; value=&quot;검색&quot; /&gt;
    &lt;/form&gt;
&lt;/p&gt;
&lt;table border=&quot;1&quot;&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th&gt;제목&lt;/th&gt;
            &lt;th&gt;카테고리&lt;/th&gt;
            &lt;th&gt;가격&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
    &lt;!-- data 객체의 타입 : List&lt;BookVO&gt; --&gt;
    &lt;c:forEach var=&quot;list&quot; items=&quot;${data}&quot;&gt;
        &lt;tr&gt;
            &lt;td&gt;&lt;a href=&quot;/detail?bookId=${list.bookId}&quot;&gt;${list.title}&lt;/a&gt;&lt;/td&gt;
            &lt;td&gt;${list.category}&lt;/td&gt;
            &lt;td&gt;${list.price}&lt;/td&gt;
        &lt;/tr&gt;    
&lt;/c:forEach&gt;    
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;br&gt;
&lt;input type=&quot;button&quot; value=&quot;책 입력&quot; onclick=&quot;fn_create();&quot; /&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="xml">xml</h3>
<h4 id="book_sqlxml">book_SQL.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE mapper
  PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
  &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;
&lt;mapper namespace=&quot;book&quot;&gt;
    &lt;!-- * selectKey? 
    일련번호 처리시 많이 사용
    마이바티스는 쿼리 실행 시 파라미터를 치환해줌
    selectKey 전 : {&quot;title&quot;:&quot;검은태양&quot;,&quot;category&quot;:&quot;드라마&quot;,&quot;price&quot;:10000}
    selectKey 후 : {&quot;bookId&quot;:1, &quot;title&quot;:&quot;검은태양&quot;,&quot;category&quot;:&quot;드라마&quot;,&quot;price&quot;:10000}

    --&gt;
    &lt;insert id=&quot;insert&quot; parameterType=&quot;kr.or.ddit.BookVO&quot;&gt;
        &lt;!-- 중요!! --&gt;
        &lt;selectKey order=&quot;BEFORE&quot; keyProperty=&quot;bookId&quot; resultType=&quot;integer&quot;&gt;
            SELECT NVL(MAX(BOOK_ID),0)+1 FROM BOOK
        &lt;/selectKey&gt;
        INSERT INTO BOOK(BOOK_ID,TITLE,CATEGORY,PRICE,INSERT_DATE)
        VALUES(#{bookId},#{title},#{category},#{price},SYSDATE)
    &lt;/insert&gt;

    &lt;!-- 책 상세보기 --&gt;
    &lt;!-- 1:다 관계가 있다면  resultType을 사용하자--&gt;
    &lt;select id=&quot;detail&quot; parameterType=&quot;bookVO&quot; resultType=&quot;bookVO&quot;&gt;
        SELECT BOOK_ID,TITLE,CATEGORY,PRICE,INSERT_DATE
        FROM BOOK
        WHERE BOOK_ID = #{bookId}
    &lt;/select&gt;

    &lt;!-- 책 수정하기, update태그는 UPDATE 쿼리를 실행하기 위한 마이바티스 태그 --&gt;
    &lt;update id=&quot;update&quot; parameterType=&quot;bookVO&quot;&gt;
        UPDATE  BOOK
        SET     TITLE = #{title}, CATEGORY = #{category}, PRICE = #{price}
        WHERE   BOOK_ID = #{bookId}
    &lt;/update&gt;

    &lt;!-- 책 목록보기 --&gt;
    &lt;!-- 
        map : {keyword=야호} 로 들어오면
        아래 쿼리의 keyword가 야호로 교체됨

        WHERE 1 = 1은 관습적인 구문. 1 = 1은 항상 TRUE이고 
        - 조건이 2개 이상일 경우 처음 시작하는 조건은 WHERE로 시작하고 두번째로 시작하는 조건은 AND 여야 함
        - 매번 첫번째 조건인지 검사하는 것은 번거롭기 때문에 무조건 WHERE 1 = 1을 써둔 후 나머지 조건을 AND로 이어붙임
        - 동적쿼리(&lt;if test~~~) : 쿼리의 내용이 파라미터가 아니라 마이바티스의 규칙에 의해 변경되는 것
     --&gt;
    &lt;select id=&quot;allList&quot; parameterType=&quot;hashMap&quot; resultType=&quot;bookVO&quot;&gt;
        SELECT BOOK_ID,TITLE,CATEGORY,PRICE,INSERT_DATE
        FROM BOOK
        WHERE 1 = 1
        &lt;if test=&quot;keyword!=null and keyword!=&#39;&#39;&quot;&gt;
            AND (TITLE      LIKE &#39;%&#39; || #{keyword} || &#39;%&#39; OR
                 CATEGORY LIKE &#39;%&#39; || #{keyword} || &#39;%&#39;)
        &lt;/if&gt;
        ORDER BY BOOK_ID DESC
    &lt;/select&gt;

    &lt;!-- 책 삭제, detail.jsp에서 작성한 name=&quot;bookId&quot; --&gt;
    &lt;delete id=&quot;delete&quot; parameterType=&quot;hashMap&quot;&gt;
            DELETE FROM BOOK
            WHERE BOOK_ID = #{bookId}
    &lt;/delete&gt;
&lt;/mapper&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 20강 - @Controller, @RequestMapping, MyBatis]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-20%EA%B0%95-Controller-RequestMapping-MyBatis</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-20%EA%B0%95-Controller-RequestMapping-MyBatis</guid>
            <pubDate>Wed, 02 Feb 2022 15:10:33 GMT</pubDate>
            <description><![CDATA[<p>스프링 프레임워크(기본) + boot/SPA/Vue/앵귤러...</p>
<p>DI -&gt; 이미 써봣음! (서비스에서 Dao가져다쓸때, 컨트롤러에서 service가져다 쓸 때)
서비스에서 Dao dao = Dao.getInstance(); 만들어서 Dao 객체 썼었음(상속 안받고도!)</p>
<p>스프링에서는 Dao 위에 @Autowired쓰면 끝</p>
<h3 id="controller-어노테이션annotation">Controller 어노테이션(Annotation)</h3>
<ul>
<li>어노테이션이 있는 클래스</li>
<li>스프링 프레임워크(디자인패턴 + 라이브러리 집합)가 웹 브라우저(크롬)의 요청(request)을 받아들이는 컨트롤러라고 인지해서 자바 빈(java Bean)으로 등록해서 관리</li>
</ul>
<p><strong>Model</strong> : 컨트롤러가 반환할 데이터(VO객체, List, List&lt;VO&gt;, Map, List&lt;Map&gt;, JSON)를 담당</p>
<p><strong>반환?</strong></p>
<p>1) forward(데이터 담기 O)
2) redirect(데이터 담기 X)</p>
<p><strong>View</strong> : 화면 담당 ex) localhost:8090/create</p>
<h3 id="requestmapping-어노테이션">RequestMapping 어노테이션</h3>
<ul>
<li>웹 브라우저의 요청에 의해 실행되는 자바 메소드를 지정해 줌</li>
<li><em>method*</em> : 속성. http 요청 메소드를 의미함
1) GET : 데이터를 변경하지 않는 경우 사용
2) POST : 데이터가 변경될 경우 사용</li>
<li>웹 브라우저에 화면을 보여줄 뿐 데이터의 변경이 일어나지 않으므로 GET 메소드를 사용한 것</li>
</ul>
<h4 id="bookcontrollerjava">BookController.java</h4>
<pre><code>package kr.or.ddit;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

//컨트롤러 어노테이션(Annotation)
//어노테이션이 있는 클래스
//스프링프레임워크(디자인패턴 + 라이브러리 집합)가 웹 브라우저(크롬)의 요청(request)을 받아들이는 컨트롤러라고 인지해서
//자바 빈(java Bean)으로 등록해서 관리

@Controller
public class BookController {
    //Model : 컨트롤러가 반환할 데이터(VO객체, List, List&lt;VO&gt;, Map, List&lt;Map&gt;, JSON)를 담당
    //            반환? 1)forward(데이터 담기 O)    2) redirect(데이터 담기 X)

    //View : 화면 담당
    //localhost:8090/create

    //요청을 매핑한다
    @RequestMapping(value=&quot;/create&quot;, method=RequestMethod.GET)
    public ModelAndView create() {
        ModelAndView mav = new ModelAndView();
        //원래는 localhost:8090/WEB-INF/view/book/create.jsp

        //servlet-context.xml에서...
        //prefix : /WEB-INF/views/
        mav.setViewName(&quot;/book/create&quot;);
        //suffix : .jsp
        return mav;
    }
    //주문하면 아래 메서드로 매핑
    @RequestMapping(value=&quot;/make&quot;, method=RequestMethod.GET)
    public ModelAndView make() {
        //라이더에 태워서 짬뽕 배송
        ModelAndView mav = new ModelAndView();
        //짬뽕 위치 알려주기
        mav.setViewName(&quot;book/make&quot;);
        //배송하기
        return mav;
    }
    //RequestMapping 어노테이션 : 웹 브라우저의 요청에 실행되는 자바 메소드를 지정해줌
    //method : 속성. http 요청 메소드를 의미함
    //            1) GET : 데이터를 변경하지 않는 경우 사용
    //            2) POST : 데이터가 변경될 경우 사용
    //웹 브라우저에 화면을 보여줄 뿐 데이터의 변경이 일어나지 않으므로 GET 메소드를 사용한 것임
    //jjajang() 메소드는 웹 브라우저에서 /jjajang 주소가 GET 방식으로 입력되었을 때 book/jjajang 경로의 뷰를 보여줌

    @RequestMapping(value=&quot;/jjajang&quot;, method=RequestMethod.GET)
    public ModelAndView jjajang(ModelAndView mav) { //라이더 생성
        // book/jjajang : 뷰의 경로
        mav.setViewName(&quot;book/jjajang&quot;);
        return mav;
    }
}
</code></pre><h3 id="mybatis란">MyBatis란?</h3>
<p>xml로 쿼리를 작성하게 해주는 라이브러리</p>
<p><strong>설정 순서</strong></p>
<ol>
<li><p><a href="https://mvnrepository.com/">https://mvnrepository.com/</a> -&gt; mybatis 검색 -&gt; 클릭 -&gt;  org.mybatis » mybatis » 3.5.9 -&gt; 중간에 있는 코드 복사 -&gt; pom.xml에 붙여넣으면
C:\Users\PC-12.m2\repository\org\mybatis\mybatis\3.5.9 로 조립된다
<img src="https://images.velog.io/images/hazel_jo/post/41380fdf-0ca8-442f-8aa7-7f4164ce0056/mybatis1.jpg" alt=""></p>
</li>
<li><p>mybatis-spring(둘을 연결해주는 라이브러리) 검색 -&gt; 2.0.6 클릭 -&gt; 중간에 있는 코드 복사</p>
</li>
</ol>
<p>-&gt; pom.xml에 붙여넣으면 C:\Users\PC-12.m2\repository\org\mybatis\mybatis-spring\2.0.6로 조립된다
<img src="https://images.velog.io/images/hazel_jo/post/e4d7f425-73e6-490a-841a-498254a5d96d/mybatis2.jpg" alt=""></p>
<p><strong>스프링에서 jdbc를 사용하려면?</strong>
spring-jdbc 검색 - spring과 같은 버전인 5.2.5 클릭 - 중간 코드 복사 - pom.xml 붙여넣기 후 &lt;version&gt;${org.springframework-version}&lt;/version&gt;
으로 수정하기
<img src="https://images.velog.io/images/hazel_jo/post/57f67fe6-3405-44e6-9794-dcc8b64282f6/mybatis3.jpg" alt=""></p>
<p><strong>DBCP 다운</strong>
commons-dbcp2 검색 - 2.9.0 클릭 - 중간 코드 복사 - pom.xml 붙여넣기
<img src="https://images.velog.io/images/hazel_jo/post/e5a7c95d-1ef5-4030-a534-954b57659d6d/mybatis4.jpg" alt=""></p>
<p><strong>log4j 다운</strong>
log4jdbc-log4j2-jdbc4 검색 - 1.16 클릭 - 중간 코드 복사 - pom.xml 붙여넣기
<img src="https://images.velog.io/images/hazel_jo/post/e12ad9b9-9442-4ec7-a2a5-722cc6b4c0fa/mybatis5.jpg" alt=""></p>
<p><strong>ojdbc6 다운</strong>
ojdbc6 검색 - 11.2.04 클릭 - 중간 코드 복사 - pom.xml 붙여넣기
<img src="https://images.velog.io/images/hazel_jo/post/1588c815-2f7e-447b-8869-5cafb40fa051/mybatis6.jpg" alt=""></p>
<p><strong>lombok 다운</strong>
lombok 검색 - 1.18.22 클릭 - 중간 코드 복사 - pom.xml 붙여넣기
<img src="https://images.velog.io/images/hazel_jo/post/1c2501b8-5485-47e9-b10e-68c2c4cab797/mybatis7.jpg" alt=""></p>
<p>서버 중지
console 클리어 후
프로젝트 우클릭 run as -&gt; 4번 maven 클릭
-&gt; 창 안뜨고 BUILD SUCCESS 나오면 성공!
<img src="https://images.velog.io/images/hazel_jo/post/36f3e504-a0a7-472a-bb76-b3fe1eec3dab/mybatis.jpg" alt=""></p>
<ol start="3">
<li>root-context.xml 수정</li>
</ol>
<ul>
<li>root-context.xml : 스프링 설정 파일</li>
<li>view와 관련되지 않은 객체를 정의(찐 스프링 설정)</li>
<li>Service(기능, 비즈니스 로직), DAO(Repository), DB등 비즈니스 로직과 관련된 설정</li>
</ul>
<h4 id="root-contextxml">root-context.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;

    &lt;!-- Root Context: defines shared resources visible to all other web components --&gt;

    &lt;!-- 
        root-context.xml : 스프링 설정 파일

        view와 관련되지 않은 객체를 정의(찐 스프링 설정)
        Service(기능, 비즈니스 로직), DAO(Repository), DB등 비즈니스 로직과 관련된 설정
    --&gt;
    &lt;!-- dataSource? 데이터베이스와 관련된 정보를 세팅 --&gt;
    &lt;bean id=&quot;dataSource&quot;
        class=&quot;org.apache.commons.dbcp2.BasicDataSource&quot;
        destroy-method=&quot;close&quot;&gt;
        &lt;property name=&quot;driverClassName&quot; 
        value=&quot;oracle.jdbc.driver.OracleDriver&quot; /&gt;
        &lt;property name=&quot;url&quot; 
        value=&quot;jdbc:oracle:thin:@localhost:1521:xe&quot; /&gt;
        &lt;property name=&quot;username&quot; value=&quot;jspexam&quot; /&gt;
        &lt;property name=&quot;password&quot; value=&quot;java&quot; /&gt;
    &lt;/bean&gt;
    &lt;!-- 
    sqlSessionFactory는 데이터베이스와 연결을 맺고 끊어질 때까지의 
    라이프 사이클을 관리하는 sqlSession 객체를 만듦
     --&gt;
    &lt;bean id=&quot;sqlSessionFactory&quot;
    class=&quot;org.mybatis.spring.SqlSessionFactoryBean&quot;&gt;
        &lt;!-- 데이터소스를 설정 --&gt;
        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot; /&gt;
        &lt;!-- 매퍼 xml 파일의 위치.  --&gt;
        &lt;property name=&quot;mapperLocations&quot;
        value=&quot;classpath:/sqlmap/**/*_SQL.xml&quot; /&gt;
        &lt;property name=&quot;configLocation&quot;
        value=&quot;/WEB-INF/mybatisAlias/mybatisAlias.xml&quot; /&gt;
    &lt;/bean&gt;
    &lt;!--
    데이터베이스에 개별적으로 쿼리를 실행시키는 객체. 우리는 소스코드에서 이 객체를 사용하여 Query를 실행시킴*** 
    --&gt;
    &lt;bean id=&quot;sqlSessionTemplate&quot;
    class=&quot;org.mybatis.spring.SqlSessionTemplate&quot;&gt;
        &lt;constructor-arg index=&quot;0&quot; ref=&quot;sqlSessionFactory&quot; /&gt;
    &lt;/bean&gt;
&lt;/beans&gt;
</code></pre><h4 id="pomxml">pom.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;kr.or&lt;/groupId&gt;
    &lt;artifactId&gt;ddit&lt;/artifactId&gt;
    &lt;name&gt;springProj&lt;/name&gt;
    &lt;packaging&gt;war&lt;/packaging&gt;
    &lt;version&gt;1.0.0-BUILD-SNAPSHOT&lt;/version&gt;
    &lt;properties&gt;
        &lt;java-version&gt;1.8&lt;/java-version&gt;
        &lt;org.springframework-version&gt;5.2.5.RELEASE&lt;/org.springframework-version&gt;
        &lt;org.aspectj-version&gt;1.6.10&lt;/org.aspectj-version&gt;
        &lt;org.slf4j-version&gt;1.6.6&lt;/org.slf4j-version&gt;
    &lt;/properties&gt;
    &lt;dependencies&gt;
        &lt;!-- Spring --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-context&lt;/artifactId&gt;
            &lt;version&gt;${org.springframework-version}&lt;/version&gt;
            &lt;exclusions&gt;
                &lt;!-- Exclude Commons Logging in favor of SLF4j --&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;commons-logging&lt;/groupId&gt;
                    &lt;artifactId&gt;commons-logging&lt;/artifactId&gt;
                 &lt;/exclusion&gt;
            &lt;/exclusions&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-webmvc&lt;/artifactId&gt;
            &lt;version&gt;${org.springframework-version}&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- AspectJ --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.aspectj&lt;/groupId&gt;
            &lt;artifactId&gt;aspectjrt&lt;/artifactId&gt;
            &lt;version&gt;${org.aspectj-version}&lt;/version&gt;
        &lt;/dependency&gt;    

        &lt;!-- Logging --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
            &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
            &lt;version&gt;${org.slf4j-version}&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
            &lt;artifactId&gt;jcl-over-slf4j&lt;/artifactId&gt;
            &lt;version&gt;${org.slf4j-version}&lt;/version&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
            &lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;
            &lt;version&gt;${org.slf4j-version}&lt;/version&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;log4j&lt;/groupId&gt;
            &lt;artifactId&gt;log4j&lt;/artifactId&gt;
            &lt;version&gt;1.2.15&lt;/version&gt;
            &lt;exclusions&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;javax.mail&lt;/groupId&gt;
                    &lt;artifactId&gt;mail&lt;/artifactId&gt;
                &lt;/exclusion&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;javax.jms&lt;/groupId&gt;
                    &lt;artifactId&gt;jms&lt;/artifactId&gt;
                &lt;/exclusion&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;com.sun.jdmk&lt;/groupId&gt;
                    &lt;artifactId&gt;jmxtools&lt;/artifactId&gt;
                &lt;/exclusion&gt;
                &lt;exclusion&gt;
                    &lt;groupId&gt;com.sun.jmx&lt;/groupId&gt;
                    &lt;artifactId&gt;jmxri&lt;/artifactId&gt;
                &lt;/exclusion&gt;
            &lt;/exclusions&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;!-- @Inject --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.inject&lt;/groupId&gt;
            &lt;artifactId&gt;javax.inject&lt;/artifactId&gt;
            &lt;version&gt;1&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- Servlet --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
            &lt;artifactId&gt;servlet-api&lt;/artifactId&gt;
            &lt;version&gt;2.5&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet.jsp&lt;/groupId&gt;
            &lt;artifactId&gt;jsp-api&lt;/artifactId&gt;
            &lt;version&gt;2.1&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
            &lt;artifactId&gt;jstl&lt;/artifactId&gt;
            &lt;version&gt;1.2&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;!-- Test --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;junit&lt;/groupId&gt;
            &lt;artifactId&gt;junit&lt;/artifactId&gt;
            &lt;version&gt;4.7&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;!-- Database 라이브러리 시작 --&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --&gt;
        &lt;!-- XML로 쿼리를 작성하게 해주는 라이브러리 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis&lt;/artifactId&gt;
            &lt;version&gt;3.5.9&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --&gt;
        &lt;!-- 스프링과 mybatis를 연동하게 해주는 라이브러리 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-spring&lt;/artifactId&gt;
            &lt;version&gt;2.0.6&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;
            &lt;version&gt;${org.springframework-version}&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 --&gt;
        &lt;!-- 데이터베이스 커넥션 풀. 커넥션을 미리 여러개 만들어 놓고, 빌려썼다가 사용이 끝나면 반납. --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
            &lt;artifactId&gt;commons-dbcp2&lt;/artifactId&gt;
            &lt;version&gt;2.9.0&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4 --&gt;
        &lt;!-- Logging을 위한 라이브러리. Query를 console이나 파일 로그로 볼 수 있음 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.bgee.log4jdbc-log4j2&lt;/groupId&gt;
            &lt;artifactId&gt;log4jdbc-log4j2-jdbc4&lt;/artifactId&gt;
            &lt;version&gt;1.16&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc6 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.oracle.database.jdbc&lt;/groupId&gt;
            &lt;artifactId&gt;ojdbc6&lt;/artifactId&gt;
            &lt;version&gt;11.2.0.4&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --&gt;
        &lt;!-- 
            * 자바빈 클래스인 VO에서 Getter/Setter메서드, toString()메서드를 자동 생성
            LoggerFactory 사용 가능
         --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
            &lt;version&gt;1.18.22&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;!-- Database 라이브러리 끝 --&gt;
    &lt;/dependencies&gt;
    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;artifactId&gt;maven-eclipse-plugin&lt;/artifactId&gt;
                &lt;version&gt;2.9&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;additionalProjectnatures&gt;
                        &lt;projectnature&gt;org.springframework.ide.eclipse.core.springnature&lt;/projectnature&gt;
                    &lt;/additionalProjectnatures&gt;
                    &lt;additionalBuildcommands&gt;
                        &lt;buildcommand&gt;org.springframework.ide.eclipse.core.springbuilder&lt;/buildcommand&gt;
                    &lt;/additionalBuildcommands&gt;
                    &lt;downloadSources&gt;true&lt;/downloadSources&gt;
                    &lt;downloadJavadocs&gt;true&lt;/downloadJavadocs&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;version&gt;2.5.1&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;source&gt;1.6&lt;/source&gt;
                    &lt;target&gt;1.6&lt;/target&gt;
                    &lt;compilerArgument&gt;-Xlint:all&lt;/compilerArgument&gt;
                    &lt;showWarnings&gt;true&lt;/showWarnings&gt;
                    &lt;showDeprecation&gt;true&lt;/showDeprecation&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt;
                &lt;artifactId&gt;exec-maven-plugin&lt;/artifactId&gt;
                &lt;version&gt;1.2.1&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;mainClass&gt;org.test.int1.Main&lt;/mainClass&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;
</code></pre><h4 id="sevlet-contextxml">sevlet-context.xml</h4>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/mvc&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd&quot;&gt;

    &lt;!-- DispatcherServlet Context: defines this servlet&#39;s request-processing infrastructure --&gt;

    &lt;!-- Enables the Spring MVC @Controller programming model --&gt;
    &lt;annotation-driven /&gt;

    &lt;!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --&gt;
    &lt;!-- 정적 폴더  -&gt; images, js, css와 같은 폴더들이 resources 밑에 들어감--&gt;
    &lt;resources mapping=&quot;/resources/**&quot; location=&quot;/resources/&quot; /&gt;
    &lt;!-- 컨트롤러에서 return되는 string값(파일명) 앞 뒤로 조립되는 값 --&gt;
    &lt;!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --&gt;
    &lt;!--  prefix는 앞, suffix는 뒤에 붙는다 (ViewResolver가 조립해준다)--&gt;
    &lt;beans:bean class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&gt;
        &lt;beans:property name=&quot;prefix&quot; value=&quot;/WEB-INF/views/&quot; /&gt;
        &lt;beans:property name=&quot;suffix&quot; value=&quot;.jsp&quot; /&gt;
    &lt;/beans:bean&gt;

    &lt;context:component-scan base-package=&quot;kr.or.ddit&quot; /&gt;



&lt;/beans:beans&gt;
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 19강 - STS 플러그인 설치]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-19%EA%B0%95-SPRING-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-19%EA%B0%95-SPRING-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Wed, 02 Feb 2022 14:03:05 GMT</pubDate>
            <description><![CDATA[<p>Spring은 STS 플러그인 설치해야 함</p>
<p><strong>1. 이클립스 STS 플러그인</strong></p>
<ul>
<li>현재 자바 버전은 15까지 나온 상태이지만, 주로 8버전(jdk 1.8)이 일반적임</li>
<li>이클립스 2020-09 버전부터는 자바 버전 11 이상을 필요로 하므로 자바 버전 8버전용으로
마지막 버전인 2020-06 버전을 많이 사용함</li>
<li>STS : Spring Tools Suite 플러그인을 이클립스에 설치</li>
<li>스프링 개발시에 필요한 기능들을 support하는 역할</li>
</ul>
<p><strong>2. Spring -&gt; Spring Legacy Project -&gt; spring MVC Project</strong></p>
<ul>
<li>일반적인 MVC 프로젝트</li>
</ul>
<p><strong>3. Maven 메이븐</strong></p>
<ul>
<li>프로젝트를 생성하고 library를 관리하고 build하는 것까지 프로젝트의
라이프 사이클(생성-활용-제거)을 관리하는 소프트웨어</li>
<li>자동으로 라이브러리를 쓸 수 있게 해 줌</li>
</ul>
<p><strong>4. Spring 초스피드 엑기스 간단 정리</strong></p>
<ul>
<li>pom.xml : 메이븐 프로젝트 빌드 파일</li>
<li>자바 소스 경로는 : src/main/java</li>
<li>정적 리소스 파일 경로는 : src/main/resources</li>
<li>web.wml : 웹 컨테이너(톰캣) 설정 파일</li>
<li>servlet-context.xml : 스프링 웹 설정 파일</li>
<li>root-context.xml : 스프링 설정 파일</li>
<li>views 폴더 : 뷰 파일(jsp) 경로</li>
</ul>
<p><strong>5. 로그 레벨 설정</strong></p>
<ul>
<li>FATAL : 아주 심각한 오류 발생</li>
<li>ERROR : 요청 처리 중 문제 발생</li>
<li>WARN : 실행에는 문제 없음. but, 향후 오류의 원인이 될 수 있을 때 경고</li>
<li>INFO : 어떠한 상태변경과 같은 정보성 메시지</li>
<li>DEBUG : 개발 시 디버그 용도</li>
<li>TRACE : 상세 이벤트 발생 시 알려줌</li>
</ul>
<p>ex) &lt;level value=&quot;info&quot; /&gt;면 Fatal부터 info까지 나옴</p>
<p>** &lt;STS 플러그인 설치 방법&gt;**</p>
<ol>
<li><p>Help - 이클립스 마켓플레이스 - spring tools 3 검색
Standalone Edition 설치 - Confirm - finish</p>
</li>
<li><p>Spring Legacy Project - Spring MVC Project 생성 후 properties - java build path - java system library - edit -<br>메인 environment 클릭 - JavaSE-1.8 클릭 - apply</p>
</li>
<li><p>alternative도 JavaSE-1.8 맞는지 확인 후 apply</p>
</li>
<li><p>project facets - Dynamic web module 3.1로, java 1.8로 변경</p>
</li>
<li><p>developmet assembly add 아무것도 없는거 확인하고 apply</p>
</li>
<li><p>상단 window - preference - encoding 검색해서 UTF-8확인</p>
</li>
<li><p>pom.xml (Maven 프로젝트 설정하는 파일)
&lt;org.springframework-version&gt;5.2.5.RELEASE&lt;/org.springframework-version&gt; 로 바꾸기
&lt;java-version&gt;1.8&lt;/java-version&gt;로 바꾸기
<img src="https://images.velog.io/images/hazel_jo/post/a9776394-8509-4992-9790-ab4908cd134a/sts%EC%84%A4%EC%B9%981.jpg" alt=""></p>
</li>
<li><p>프로젝트 우클릭 RunAs - 4번 클릭
Goals 에 compile 입력 - apply - run
[INFO] BUILD SUCCESS 뜨면 성공!
<img src="https://images.velog.io/images/hazel_jo/post/01048125-6ec2-4c3a-afe8-f55294297c02/sts%EC%84%A4%EC%B9%982.jpg" alt="">
<img src="https://images.velog.io/images/hazel_jo/post/2d3915b1-c30e-4692-b549-3f79eef9c177/sts%EC%84%A4%EC%B9%983.jpg" alt="">
<img src="https://images.velog.io/images/hazel_jo/post/8c13f3f6-ac70-4358-8dfd-54171f4dc57b/sts%EC%84%A4%EC%B9%984.jpg" alt=""></p>
<ol start="9">
<li>하단 server 에서 JSPBOOK remove</li>
<li>Tomcat 우클릭 Add and Remove - 방금 만든 프로젝트 add하고 finish</li>
<li>서버 더블클릭 modules - root path 바꾸기
edit 눌러서 /만 남기고 저장
(스프링에서 /는 WEB-INF의 views 폴더를 말한다)</li>
</ol>
</li>
<li><p>sevlet-context.xml 확인
여기서 path를 설정할 수 있음
@controller설정 시 controller가 됨
prefix가 앞, suffix가 뒤로 합쳐진다.</p>
</li>
</ol>
<p>-&gt;ViewResolver (controller에서 리턴되는 파일명 앞 뒤로 합쳐지는 값)
-&gt; resources(img,js파일 등을 저장하는 장소)는 직접 접근이 가능하지만 views는 보안때문에 접근불가!
<img src="https://images.velog.io/images/hazel_jo/post/4459d53e-b65d-481d-9e7f-bff14c9ab468/sts%EC%84%A4%EC%B9%985.jpg" alt=""></p>
<ol start="13">
<li><p>자동으로 UTF-8 설정해서 인코딩하는 번거로움 없애기
web.xml 수정(<a href="https://homesi.tistory.com/entry/Spring%EC%97%90%EC%84%9C-%ED%95%9C%EA%B8%80%EA%B9%A8%EC%A7%90-%EB%B0%A9%EC%A7%80%EB%A5%BC-%EC%9C%84%ED%95%B4-webxml%EC%97%90%EC%84%9C-%ED%95%9C%EA%B8%80-%EC%84%A4%EC%A0%95">https://homesi.tistory.com/entry/Spring%EC%97%90%EC%84%9C-%ED%95%9C%EA%B8%80%EA%B9%A8%EC%A7%90-%EB%B0%A9%EC%A7%80%EB%A5%BC-%EC%9C%84%ED%95%B4-webxml%EC%97%90%EC%84%9C-%ED%95%9C%EA%B8%80-%EC%84%A4%EC%A0%95</a>)
아래와 같이 한글 설정 추가 후 톰캣 재기동하기
<img src="https://images.velog.io/images/hazel_jo/post/8677d243-219a-49f5-890e-e08cc4dd6682/sts%EC%84%A4%EC%B9%986.jpg" alt=""></p>
</li>
<li><p>log4j.xml 수정
<img src="https://images.velog.io/images/hazel_jo/post/2daae774-03f4-4959-aafd-028a4f256067/sts%EC%84%A4%EC%B9%987.jpg" alt=""></p>
</li>
</ol>
<h4 id="숙제-스프링-삼각형에-대한-이해-후-개념-발표">숙제! 스프링 삼각형에 대한 이해 후 개념 발표</h4>
<p>POJO (Plain Old Java Object)  순수한 자바로 작성된 객체
과거에는 자바로 웹 어플리케이션을 개발하기 위해 Servlet 클래스를 상속받아서 사용했음
스프링은 Servlet 클래스를 작성하지 않고 POJO만으로 웹 어플리케이션을 개발할 수 있다.</p>
<p>스프링 삼각형 = 스프링의 3대 프로그래밍 모델
<strong>1. IoC/DI</strong>
제어의 역전/ 의존성 주입</p>
<p>프로그래밍에서 의존성이란?
운전자는 자동차를 생산한다. = new Car()
자동차는 내부적으로 타이어를 생산한다. = Car 객체 생성자에서 new Tire();</p>
<p>Car 객체 생성자에서 new를 실행함으로 Car가 Tire에 의존한다.
-&gt;여기서 new 라는 키워드는 의존성이라고 할 수 있다.</p>
<p>의존 관계를 어떻게 맺냐에 따라 강한 결합인지, 느슨한 결합인지 구분됨</p>
<p>1) 강한 결합 : 객체 내부에서 다른 객체를 생성하는 것
ex) A라는 클래스 내부에서 B라는 객체를 직접 생성하고 있을 때
B객체를 C객체로 바꾸고 싶은 경우 A 클래스도 수정해야 한다.
위에서 설명한 new 키워드처럼!</p>
<p>2) 느슨한 결합 : 외부에서 생성된 객체를 인터페이스를 통해서 넘겨받는 것
의존성 주입으로 결합도를 낮추어 유연성과 확장성을 향상시킨다</p>
<p>의존성 주입(Dependancy Injection)
스프링에서 어떻게 의존성을 주입하는지?</p>
<p>여기서 빈(Bean) : Spring IoC 컨테이너가 관리하는 자바 객체</p>
<p>스프링에서는 XML 설정 파일들이 존재할 뿐만 아니라 Bean을 관리하게 되는데,
개발자가 코드에서 생성할 Bean을 XML 파일에 등록하여 의존성을 주입할 수 있다.</p>
<p>의존성 주입 3가지 방법</p>
<p>1) Field 주입
스프링 초창기부터 현재까지 사용되는 방법으로, 멤버 객체에 @Autowired를 붙여주면
스프링 프레임워크에서 알맞은 클래스를 기반으로 만든 Bean을 주입받는다</p>
<p>2) Setter 주입
setter 메소드 위에 @Autowired를 선언하게 되면, setter 메소드의 파라미터에 해당하는 객체에
Bean을 주입받는다</p>
<p>3) Constructor 주입
스프링 프레임워크에서 추천하는 방법
클래스 생성자에 Bean들을 파라미터로 주입받는 방법
Field 주입과 Setter 주입에서 사용하지 못하는 final 키워드를 붙일 수 있다.</p>
<p>3가지 방법 모두 개발자가 new를 사용하여 객체를 주입하는 것이 아니라 툴이 제어권을 가져
 객체를 주입받아 사용한다
 -&gt; 제어권이 외부로 역전되었다.(IoC, Inversion of Control)</p>
<p><strong>2. AOP</strong>
Aspect-Oriented Programming
관점 지향 프로그래밍</p>
<p>여러 개의 핵심 비즈니스 로직 외에 공통으로 처리되어야 하는 로그 출력, 보안처리, 예외처리와
같은 코드를 모아서 처리를 편리하게 해준다.
ex @Transactional 로 트랜잭션 처리를 공통으로 넣어줄 수 있다.</p>
<p>여기서 공통된 코드 = AOP에서 말하는 aspect</p>
<p>Spring AOP는 프록시 패턴이라는 디자인 패턴을 사용하여 AOP를 지원한다.</p>
<p>프록시를 통해 핵심 로직을 구현한 객체에 접근하게 되면 핵심 로직을 실행하기 전, 후에
공통된 기능을 적용하는 방식으로 AOP를 구현한다.</p>
<p><strong>3. PSA</strong>
Portable Service Abstraction 일관성 있는 서비스 추상화
어댑터 패턴을 적용해 같은 일을 하는 다수의 기술이
어떠한 데이터 베이스를 사용하더라도 일관성 있는 방식으로 제어할 수 있도록 공통의 인터페이스를 제공하는 것</p>
<p>ex) JDBC는 오라클, MySQL, MS-SQL 중 무엇을 사용하든
Connection, Statement, ResultSet을 이용해 공통된 방식으로 코드 작성이 가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JSP 18강 - DBCP 이용한 방명록 게시판(CRUD)]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-18%EA%B0%95-DBCP-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B0%A9%EB%AA%85%EB%A1%9D-%EA%B2%8C%EC%8B%9C%ED%8C%90CRUD</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-18%EA%B0%95-DBCP-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B0%A9%EB%AA%85%EB%A1%9D-%EA%B2%8C%EC%8B%9C%ED%8C%90CRUD</guid>
            <pubDate>Wed, 02 Feb 2022 12:50:35 GMT</pubDate>
            <description><![CDATA[<p><strong>실행 순서</strong>
JSP -&gt; service -&gt; dao -&gt; db
JSP &lt;- service &lt;- dao &lt;- db</p>
<p><strong>jquery 다운로드</strong>
<a href="https://jquery.com/">https://jquery.com/</a> 접속
download 클릭
upcompressed 클릭 -&gt; 내용 복사
js폴더에 파일 만들고, 복사한 내용 붙여넣기
---&gt; 이제 script src에 해당 위치 적어서 사용 가능!</p>
<p><strong>&lt;방명록 게시판 만드는 순서&gt;</strong>
1.GuestbookMessage 객체 생성
2.Dao 객체 생성
3.Service 객체 생성 및 예외처리
4.jsp파일로 View 작성</p>
<h3 id="1guestbookmessage-객체-생성">1.GuestbookMessage 객체 생성</h3>
<h4 id="guestbookmessagejava">GuestbookMessage.java</h4>
<pre><code>package dto;

//자바빈 클래스
public class GuestbookMessage {
    private int messageId;
    private String guestName;
    private String password;
    private String message;

    //기본생성자(Constructor)
    public GuestbookMessage() {
        super();
        // TODO Auto-generated constructor stub
    }

    //생성자
    public GuestbookMessage(int messageId, String guestName, String password, String message) {
        super();
        this.messageId = messageId;
        this.guestName = guestName;
        this.password = password;
        this.message = message;
    }


    public int getMessageId() {
        return messageId;
    }

    public void setMessageId(int messageId) {
        this.messageId = messageId;
    }


    public String getGuestName() {
        return guestName;
    }


    public void setGuestName(String guestName) {
        this.guestName = guestName;
    }


    public String getPassword() {
        return password;
    }


    public void setPassword(String password) {
        this.password = password;
    }


    public String getMessage() {
        return message;
    }


    public void setMessage(String message) {
        this.message = message;
    }


    @Override
    public String toString() {
        return &quot;GuestbookMessage [messageId=&quot; + messageId + &quot;, guestName=&quot; + guestName + &quot;, password=&quot; + password
                + &quot;, message=&quot; + message + &quot;]&quot;;
    }


}
</code></pre><h3 id="2dao-객체-생성">2.Dao 객체 생성</h3>
<h4 id="messagedaojava">MessageDao.java</h4>
<pre><code>package guestbook.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import dto.GuestbookMessage;
import jdbc.JdbcUtil;

//Data Access Object
public class MessageDao {
    //싱글톤 패턴
    private static MessageDao instance = new MessageDao();
    public static MessageDao getInstance() {
        return instance;
    }
    private MessageDao() {}

    /**guestbook_message 테이블로 insert
     * insert, update, delete 기본 return type은 int
     * params : 커넥션 객체, 무엇을 입력할 것인지!(대상)
     */
    public int insert(Connection conn, GuestbookMessage message) {
        //선언
        PreparedStatement pstmt = null;
        int result = 0;
        try {
            pstmt = conn.prepareStatement(
                &quot;INSERT INTO GUESTBOOK_MESSAGE(MESSAGE_ID,GUEST_NAME,PASSWORD,MESSAGE)&quot; + 
                &quot; VALUES(&quot; + 
                &quot;    (SELECT NVL(MAX(MESSAGE_ID),0)+1 FROM GUESTBOOK_MESSAGE)&quot; + 
                &quot;    ,?,?,?&quot; + 
                &quot;)&quot;
            );
            pstmt.setString(1, message.getGuestName());
            pstmt.setString(2, message.getPassword());
            pstmt.setString(3, message.getMessage());
            return pstmt.executeUpdate();
        }catch(SQLException ex){
            ex.printStackTrace();
            return 0;
        }finally {
            //주의! 커넥션 객체는 비즈니스로직에서 닫음(Service)
            JdbcUtil.close(pstmt);
        }
    }//end insert

    //list.jsp의 메시지 목록
    public List&lt;GuestbookMessage&gt; selectList(Connection conn) throws SQLException {
        Statement stmt = null;
        ResultSet rs = null;

        try {
            //SELECT구문..
            String query = &quot;SELECT MESSAGE_ID&quot; + 
            &quot;     , GUEST_NAME&quot; + 
            &quot;     , PASSWORD&quot; + 
            &quot;     , MESSAGE &quot; + 
            &quot; FROM   GUESTBOOK_MESSAGE&quot; + 
            &quot; ORDER BY MESSAGE_ID DESC&quot;;

            stmt = conn.createStatement();
            rs = stmt.executeQuery(query);
            //커서가 다음행으로 바라봤을 때 데이터가 있다면 실행
            if(rs.next()) {
                List&lt;GuestbookMessage&gt; messageList = new ArrayList&lt;GuestbookMessage&gt;();

                do {
                    GuestbookMessage vo = new GuestbookMessage();
                    vo.setMessageId(rs.getInt(&quot;MESSAGE_ID&quot;));
                    vo.setGuestName(rs.getString(&quot;GUEST_NAME&quot;));
                    vo.setPassword(rs.getString(&quot;PASSWORD&quot;));
                    vo.setMessage(rs.getString(&quot;MESSAGE&quot;));
                    //message_id는 자료형이 int이므로 getInt()로 받는다

                    messageList.add(vo);
                }while(rs.next());
                return messageList;
            }else { //select 결과 없을 때
                return Collections.emptyList();
            }//end if
        }finally {
            JdbcUtil.close(rs);
            JdbcUtil.close(stmt);
        }
    }

    //메시지 수정(커넥션, 무엇을)
    public int update(Connection conn, GuestbookMessage message) {
        PreparedStatement pstmt = null;

        try {
            pstmt = conn.prepareStatement(&quot;UPDATE GUESTBOOK_MESSAGE&quot; +
                    &quot; SET GUEST_NAME = ?, MESSAGE = ?&quot; +
                    &quot; WHERE MESSAGE_ID = ? AND PASSWORD = ?&quot;);
            pstmt.setString(1, message.getGuestName());
            pstmt.setString(2, message.getMessage());
            pstmt.setInt(3, message.getMessageId());
            pstmt.setString(4, message.getPassword());

            return pstmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
            return 0;
        }finally {
            JdbcUtil.close(pstmt);
            //커넥션 객체는 서비스단에서 close해준다.
        }//end try
    }

    //삭제 : conn은 db연결, message는 삭제할 대상 지정
    public int delete(Connection conn, GuestbookMessage message) {
        PreparedStatement pstmt = null;

        try {
            pstmt = conn.prepareStatement(&quot;DELETE FROM GUESTBOOK_MESSAGE&quot; +
                    &quot; WHERE MESSAGE_ID = ?&quot;);
            pstmt.setInt(1, message.getMessageId());

            return pstmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
            return 0;
        }finally {
            JdbcUtil.close(pstmt);
        }
    }
}
</code></pre><h3 id="3service-객체-생성-및-예외처리">3.Service 객체 생성 및 예외처리</h3>
<h4 id="writemessageservicejava">WriteMessageService.java</h4>
<pre><code>package guestbook.service;

import java.sql.Connection;
import java.sql.SQLException;

import dto.GuestbookMessage;
import guestbook.dao.MessageDao;
import jdbc.ConnectionProvider;
import jdbc.JdbcUtil;
//***Service.java =&gt; 비즈니스(기능) 로직
public class WriteMessageService {
    //싱글톤 패턴 적용
    private static WriteMessageService instance = new WriteMessageService();
    public static WriteMessageService getInstance() {
        return instance;
    }
    private WriteMessageService() {}

    //방명록 insert
    public void write(GuestbookMessage message) {
        //커넥션 객체를 선언(DBCP에서 가져온 커넥션)
        Connection conn = null;
        try {
            //DBCP 커넥션 객체를 생성
            conn = ConnectionProvider.getConnection();
            //guestbook_message테이블로 insert =&gt; Data Access Object 영역
            MessageDao messageDao = MessageDao.getInstance();
            int result = messageDao.insert(conn, message);
        }catch(SQLException ex) {
            throw new ServiceException(&quot;메시지 등록 실패 : &quot; + ex.getMessage(), ex);
        }finally {
            JdbcUtil.close(conn);
        }
    }

}
</code></pre><h4 id="getmessagelistservicejava">GetMessageListService.java</h4>
<pre><code>package guestbook.service;

public class ServiceException extends RuntimeException {

    //생성자
    public ServiceException(String message, Exception cause) {
        super(message, cause);
    }
    //생성자
    public ServiceException(String message) {
        super(message);
    }
}
</code></pre><h4 id="updatemessageservicejava">UpdateMessageService.java</h4>
<pre><code>package guestbook.service;

import java.sql.Connection;
import java.sql.SQLException;

import dto.GuestbookMessage;
import guestbook.dao.MessageDao;
import jdbc.ConnectionProvider;
import jdbc.JdbcUtil;

public class UpdateMessageService {
    //싱글톤 패턴
    private static UpdateMessageService instance = new UpdateMessageService(); 
    public static UpdateMessageService getInstance() {
        return instance;
    }
    private UpdateMessageService() {}

    //메시지를 update하는 비즈니스 로직
    public int update(GuestbookMessage message) {
        Connection conn = null;
        int result = 0;
        try {
            conn = ConnectionProvider.getConnection();
            //DAO 객체 생성
            MessageDao messageDao = MessageDao.getInstance();
            result = messageDao.update(conn, message);

        }catch(SQLException ex) {
            throw new ServiceException(&quot;메시지 수정 실패 : &quot; + ex.getMessage(), ex);
        }finally {
            JdbcUtil.close(conn);
        }
        return result;
    }
}
</code></pre><h4 id="deletemessageservicejava">DeleteMessageService.java</h4>
<pre><code>package guestbook.service;

import java.sql.Connection;
import java.sql.SQLException;

import dto.GuestbookMessage;
import guestbook.dao.MessageDao;
import jdbc.ConnectionProvider;
import jdbc.JdbcUtil;

public class DeleteMessageService {
    //new를 사용하면 요청할 때마다 객체가 생성되어 과부하가 되므로 싱글톤을 사용한다.

    private static DeleteMessageService instance = new DeleteMessageService();
    public static DeleteMessageService getInstance() {
        return instance;
    }
    private DeleteMessageService() {}
    //데이터 삭제
    public int delete(GuestbookMessage message) {
        Connection conn = null;

        try {
            conn = ConnectionProvider.getConnection();

            MessageDao messageDao = MessageDao.getInstance();

            int result = messageDao.delete(conn, message);
            return result;

        } catch (SQLException ex) {
            //삭제 시 문제가 발생되면 롤백 처리
            //DC(control)L : commit, rollback(마지막 커밋 시점으로 돌아감)
            // -&gt; 트랜잭션이 종료가 되는 동시에 새로운 트랜잭션이 시작
            // -&gt; 트랜잭션? DB를 변경하기 위해 수행되어야 할 논리적 단위(여러개의 sql로 구성)
            JdbcUtil.rollback(conn);
            throw new ServiceException(&quot;삭제 실패&quot;, ex);
        }finally {
            JdbcUtil.close(conn);
        }
    }
}
</code></pre><h4 id="serviceexceptionjava">ServiceException.java</h4>
<pre><code>package guestbook.service;

public class ServiceException extends RuntimeException {

    //생성자
    public ServiceException(String message, Exception cause) {
        super(message, cause);
    }
    //생성자
    public ServiceException(String message) {
        super(message);
    }
}
</code></pre><h3 id="4jsp파일로-view-작성">4.jsp파일로 View 작성</h3>
<h4 id="listjsp">list.jsp</h4>
<pre><code>&lt;%@page import=&quot;guestbook.service.GetMessageListService&quot;%&gt;
&lt;%@ page import=&quot;dto.GuestbookMessage&quot;%&gt;
&lt;%@ page import=&quot;java.util.List&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
//list.jsp에서  사용할
//메시지 목록(List&lt;GuestbookMessage&gt;)을 가져와주는 서비스 로직
GetMessageListService messageListService = GetMessageListService.getInstance();
List&lt;GuestbookMessage&gt; list = messageListService.getMessageList();
%&gt;
&lt;c:set var=&quot;list&quot; value=&quot;&lt;%=list%&gt;&quot; /&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/js/jquery-3.6.0.js&quot;&gt;&lt;/script&gt;
&lt;title&gt;방명록 메시지 목록&lt;/title&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
$(function(){
//    alert(&quot;왔다!&quot;);
});

//fn_updt(기본키, 작성자 , 메시지)
function fn_updt(vara, varb, varc){
    //alert(vara);
    //기본키 데이터를 vara 매개변수로 받아서 frmUpdate 폼의 기본키 데이터로 사용
    $(&quot;#messageId&quot;).val(vara);
    //작성자 매개변수를 frmUpdate 폼의 이름 데이터로 사용
    $(&quot;#guestName&quot;).val(varb);
    //메시지 매개변수를 frmUpdate 폼의 내용 데이터로 사용
    $(&quot;#message&quot;).val(varc);
    $(&quot;#frmWrite&quot;).css(&quot;display&quot;,&quot;none&quot;);
    $(&quot;#frmUpdate&quot;).css(&quot;display&quot;,&quot;block&quot;);
}
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form id=&quot;frmWrite&quot; method=&quot;post&quot; action=&quot;writeMessage.jsp&quot; style=&quot;display:block;&quot;&gt;
    이름 : &lt;input type=&quot;text&quot; name=&quot;guestName&quot;/&gt;&lt;br&gt;
    비밀번호 : &lt;input type=&quot;password&quot; name=&quot;password&quot;/&gt;&lt;br&gt;
    내용 : &lt;textarea rows=&quot;3&quot; cols=&quot;30&quot; name=&quot;message&quot;&gt;&lt;/textarea&gt;&lt;br&gt;
    &lt;input type=&quot;submit&quot; value=&quot;메시지 남기기&quot; /&gt;
&lt;/form&gt;
&lt;form id=&quot;frmUpdate&quot; method=&quot;post&quot; action=&quot;updateMessage.jsp&quot; style=&quot;display:none;&quot;&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;messageId&quot; id=&quot;messageId&quot; /&gt;
    이름 : &lt;input type=&quot;text&quot; name=&quot;guestName&quot; id=&quot;guestName&quot;/&gt;&lt;br&gt;
    비밀번호 : &lt;input type=&quot;password&quot; name=&quot;password&quot;/&gt;&lt;br&gt;
    내용 : &lt;textarea rows=&quot;3&quot; cols=&quot;30&quot; name=&quot;message&quot; id=&quot;message&quot;&gt;&lt;/textarea&gt;&lt;br&gt;
    &lt;input type=&quot;submit&quot; value=&quot;확인&quot; /&gt;&amp;nbsp;
    &lt;input type=&quot;button&quot; value=&quot;취소&quot; onclick=&quot;javascript:location.href=&#39;list.jsp&#39;;&quot;/&gt;
&lt;/form&gt;
&lt;hr&gt;

&lt;!-- 
[수정]
1. 클릭 시 상단의 폼에 정보가 입력되고(비밀번호 제외)
    &quot;메시지 남기기&quot; 버튼이 hidden, &quot;확인&quot; 및 &quot;취소&quot; 버튼이 block
2. &quot;확인&quot; 클릭 시 해당 정보가 업데이트가 되는데, 이때 비밀번호가 일치해야 함
3. &quot;취소&quot; 클릭 시 현재 페이지의 목록으로 되돌아감
    &quot;메시지 남기기&quot; 버튼이 block, &quot;확인&quot; 및 &quot;취소&quot; 버튼이 hidden    

 --&gt;
&lt;c:if test=&quot;${param.result eq 1}&quot;&gt;
    &lt;div style=&quot;color:red;&quot;&gt;변경 성공했습니다.&lt;/div&gt;
&lt;/c:if&gt;
&lt;c:if test=&quot;${param.result &lt; 1}&quot;&gt;
    &lt;div style=&quot;color:red;&quot;&gt;비밀번호를 확인해주세요.&lt;/div&gt;
&lt;/c:if&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;c:forEach var=&quot;message&quot; items=&quot;${list}&quot;&gt;
    &lt;tr&gt;
        &lt;td&gt;
            메시지   번호 : &lt;span&gt;${message.messageId}&lt;/span&gt;&lt;br&gt;
            손  님  이  름 : &lt;span&gt;${message.guestName}&lt;/span&gt;&lt;br&gt;
            메     시    지 : &lt;span&gt;${message.message}&lt;/span&gt;&lt;br&gt;
            &lt;a href=&quot;#&quot; id=&quot;updt&quot; onclick=&quot;fn_updt(&#39;${message.messageId}&#39;,&#39;${message.guestName}&#39;,&#39;${message.message}&#39;)&quot;&gt;[수정]&lt;/a&gt;&amp;nbsp;
            &lt;a href=&quot;deleteMessage.jsp?messageId=${message.messageId}&quot;&gt;[삭제]&lt;/a&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
&lt;/c:forEach&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="writemessagejsp">writeMessage.jsp</h4>
<pre><code>&lt;%@page import=&quot;guestbook.service.WriteMessageService&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%
    request.setCharacterEncoding(&quot;UTF-8&quot;);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;방명록 메시지 남김&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;jsp:useBean id=&quot;guestbookMessage&quot; class=&quot;dto.GuestbookMessage&quot;&gt;
    &lt;jsp:setProperty name=&quot;guestbookMessage&quot; property=&quot;*&quot; /&gt;
    &lt;!-- request로 넘어온 모든 데이터를  guestbookMessage 이름으로 매핑한다--&gt;
&lt;/jsp:useBean&gt;
${guestbookMessage.messageId}&lt;br&gt;
${guestbookMessage.guestName}&lt;br&gt;
${guestbookMessage.password}&lt;br&gt;
${guestbookMessage.message}&lt;br&gt;
&lt;%
    //싱글톤 패턴
    WriteMessageService writeMessageService = WriteMessageService.getInstance();
    writeMessageService.write(guestbookMessage);
%&gt;
방명록에 메시지를 남겼습니다.
&lt;a href=&quot;list.jsp&quot;&gt;[목록보기]&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="updatemessagejsp">updateMessage.jsp</h4>
<pre><code>&lt;%@page import=&quot;dto.GuestbookMessage&quot;%&gt;
&lt;%@page import=&quot;guestbook.service.UpdateMessageService&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
request.setCharacterEncoding(&quot;UTF-8&quot;);

String messageId = request.getParameter(&quot;messageId&quot;);
String guestName = request.getParameter(&quot;guestName&quot;);
String password = request.getParameter(&quot;password&quot;);
String message = request.getParameter(&quot;message&quot;);


out.print(&quot;messageId : &quot; + messageId + &quot;&lt;br&gt;&quot;);
out.print(&quot;guestName : &quot; + guestName + &quot;&lt;br&gt;&quot;);
out.print(&quot;password : &quot; + password + &quot;&lt;br&gt;&quot;);
out.print(&quot;message : &quot; + message + &quot;&lt;br&gt;&quot;);

//getter 안쓰는 대신 생성자 파라미터에 이렇게 넣는다!
GuestbookMessage guestbookmessage = new GuestbookMessage(Integer.parseInt(messageId), guestName, password, message);

//여기서 사용할 UpdateMessageService의 객체를 생성
UpdateMessageService service = UpdateMessageService.getInstance();
int result = service.update(guestbookmessage);
if(result&gt;0){ //변경 성공
    out.print(&quot;&lt;script type=&#39;text/javascript&#39;&gt;location.href=&#39;list.jsp?result=1&#39;&lt;/script&gt;&quot;);
}else{ //변경 실패
    out.print(&quot;&lt;script type=&#39;text/javascript&#39;&gt;location.href=&#39;list.jsp?result=0&#39;&lt;/script&gt;&quot;);
}

%&gt;</code></pre><h4 id="deletemessagejsp">deleteMessage.jsp</h4>
<pre><code>&lt;%@page import=&quot;guestbook.service.ServiceException&quot;%&gt;
&lt;%@page import=&quot;dto.GuestbookMessage&quot;%&gt;
&lt;%@page import=&quot;guestbook.service.DeleteMessageService&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%
    request.setCharacterEncoding(&quot;UTF-8&quot;);

    //list.jsp에서 보낸 파라미터 받기 -&gt; request.getParameter로 받으면 무조건 String형
    String messageId = request.getParameter(&quot;messageId&quot;);

    GuestbookMessage message = new GuestbookMessage();
    //int messageId 멤버변수(String messageId)로 형변환
    message.setMessageId(Integer.parseInt(messageId));
    //삭제 성공 여부(성공으로 세팅)
    boolean result = true;

    //삭제된 건수
    int cnt = 0;

    try{
        DeleteMessageService service = DeleteMessageService.getInstance();
        cnt = service.delete(message);
    }catch(ServiceException ex){ //서비스에서 throw new ServiceException으로 던진거 받기
        //실패 처리
        result = false;
    }

%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;방명록 메시지 삭제&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
if(result){ //result : true일 때 
    if(cnt&gt;0){ //삭제가 잘 된 경우
        out.print(&quot;메시지를 삭제하였습니다.&quot;);
    }else{ //기본키에 해당되는 데이터가 없을 경우
        out.print(&quot;해당 데이터가 없습니다.&quot;);
    }
}else{ //result : false
    out.print(&quot;삭제가 되지 않았습니다.&quot;);
}
%&gt;
&lt;br&gt;
&lt;a href=&quot;list.jsp&quot;&gt;[목록 보기]&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><blockquote>
<p>JSP   - Service - Dao - Oracle
 l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l
view&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Model
사용자&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;비즈니스 로직
영역</p>
</blockquote>
<p>Controller는 View의 요청과 Model의 응답을 처리한다</p>
<p>Spring -&gt; Spring Boot(스프링을 더 쉽게 이용하기 위한 도구)</p>
<p>프레임워크 : 디자인패턴(view, model) + 라이브러리(jar)</p>
<p><strong>카멜 표기 변환</strong></p>
<pre><code>
create or replace FUNCTION FN_GETCAMEL(COLUMN_NAME IN VARCHAR2)
RETURN VARCHAR2
IS
RSLT VARCHAR2(30);
BEGIN
--카멜표기로 변환(SITE_NUM -&gt; siteNum)
SELECT LOWER(SUBSTR(REPLACE(INITCAP(COLUMN_NAME),&#39;_&#39;),1,1))
|| SUBSTR(REPLACE(INITCAP(COLUMN_NAME),&#39;_&#39;),2) INTO RSLT
FROM DUAL;
--리턴
RETURN RSLT;
END;
/

--구글 카멜변환(https://heavenly-appear.tistory.com/270)
SELECT COLUMN_NAME
, DATA_TYPE
, CASE WHEN DATA_TYPE=&#39;NUMBER&#39; THEN &#39;private int &#39; || FN_GETCAMEL(COLUMN_NAME) || &#39;;&#39;
WHEN DATA_TYPE IN(&#39;VARCHAR2&#39;,&#39;CHAR&#39;) THEN &#39;private String &#39; || FN_GETCAMEL(COLUMN_NAME) || &#39;;&#39;
WHEN DATA_TYPE=&#39;DATE&#39; THEN &#39;private Date &#39; || FN_GETCAMEL(COLUMN_NAME) || &#39;;&#39;
ELSE &#39;private String &#39; || FN_GETCAMEL(COLUMN_NAME) || &#39;;&#39;
END AS CAMEL_CASE
, &#39;&#39; RESULTMAP
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME = &#39;CUS&#39;;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[JSP 17강 - 데이터베이스 프로그래밍(JDBC, DBCP)]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-17%EA%B0%95-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DJDBC-DBCP-%EB%B0%A9%EB%AA%85%EB%A1%9D-%EB%A7%8C%EB%93%A4%EA%B8%B01</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-17%EA%B0%95-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DJDBC-DBCP-%EB%B0%A9%EB%AA%85%EB%A1%9D-%EB%A7%8C%EB%93%A4%EA%B8%B01</guid>
            <pubDate>Wed, 26 Jan 2022 12:23:23 GMT</pubDate>
            <description><![CDATA[<h4 id="데이터베이스-프로그래밍-순서">데이터베이스 프로그래밍 순서</h4>
<p><img src="https://images.velog.io/images/hazel_jo/post/8622f2a5-8bf5-47ad-a132-82cb66d64394/image.png" alt=""></p>
<ol>
<li>연결 : 데이터베이스를 사용하기 위해 먼저 데이터베이스에 연결해야 함</li>
<li>데이터베이터에 데이터를 삽입/변경/삭제하는 등의 작업을 수행</li>
<li>2~3번 과정을 작업을 완료할 때까지 반복해서 수행</li>
<li>원하는 작업 수행 후 연결 종료</li>
</ol>
<p>DB는 개념이다!
&lt;조건&gt;</p>
<ol>
<li>공유</li>
<li>통행</li>
<li>저장</li>
<li>운영</li>
</ol>
<h2 id="jdbcjava-database-connectivity">JDBC(Java Database Connectivity)</h2>
<ul>
<li><p>자바에서 DB 프로그래밍을 하기 위해 사용되는 API</p>
</li>
<li><p>JDBC API 사용 어플리케이션의 기본 구성
<img src="https://images.velog.io/images/hazel_jo/post/c11b6fc9-6789-460f-baf6-71f885703a57/image.png" alt=""></p>
</li>
<li><p>자바는 DBMS 종류에 상관없이 하나의 JDBC API를 사용해서 데이터베이스 작업을 처리할 수 있다.</p>
</li>
</ul>
<h4 id="jdbc-드라이버">JDBC 드라이버</h4>
<ul>
<li><p>DBMS와 통신을 담당하는 자바 클래스</p>
</li>
<li><p>DBMS별로 알맞은 JDBC 드라이버 필요</p>
</li>
<li><blockquote>
<p>보통 jar파일로 제공</p>
</blockquote>
</li>
<li><p>JDBC 드라이버 로딩</p>
</li>
<li><ul>
<li>로딩? 하드디스크로부터 메모리에 올리는 것</li>
</ul>
</li>
</ul>
<p><strong>로딩코드</strong></p>
<blockquote>
<p>Class.forName(&quot;JDBC드라이버 클래스의 완전한 이름&quot;);</p>
</blockquote>
<p>주요 DBMS의 JDBC드라이버</p>
<blockquote>
<ul>
<li>오라클 : oracle.jdbc.driver.OracleDriver</li>
</ul>
</blockquote>
<ul>
<li>MySQL : com.mysql.jdbc.Driver</li>
<li>MS SQL 서버 : com.microsoft.sqlserver.jdbc.SQLServerDriver</li>
</ul>
<p><strong>JDBC URL</strong></p>
<ul>
<li>DBMS와의 연결을 위한 식별 값</li>
<li>JDBC 드라이버에 따라 형식 다름</li>
<li>일반적인 구성</li>
<li><blockquote>
<p>jdbc:[DBMS]:[데이터베이스 식별자]</p>
</blockquote>
</li>
<li>주요 DBMS의 JDBC URL 구성<blockquote>
<p>Oracle: jdbc:oracle:thin:@HOST:PORT:SID
MySQL : jdbc:mysql://HOST[:PORT]/DBNAME[?param=value&amp; param1...]
MS SQL : jdbc:sqlserver://HOST[:PORT];databaseName=DB</p>
</blockquote>
</li>
</ul>
<p>-&gt; 오라클 드라이버는 thin 드라이버와 OCI 드라이버가 있는데, thin 드라이버는 자바 언어로만 구현된 JDBC 드라이버로서 JDK만 설치되어 있으면 어디서는 사용할 수 있다.</p>
<h4 id="jdbc-프로그래밍-코딩-순서">JDBC 프로그래밍 코딩 순서</h4>
<ol>
<li>JDBC 드라이버 로딩
Class.forName(&quot;oracle.jdbc.driver.OracleDriver&quot;);</li>
</ol>
<p>-&gt; 외우는 방법 : 오라클 JDBC 드라이버는 오라클꺼다~</p>
<ol start="2">
<li>데이터베이스 커넥션(연결 객체) 생성</li>
</ol>
<p>-&gt; 순서 : 1)jdbcDriver 2)계정아이디 3)비밀번호</p>
<blockquote>
<p>String jdbcDriver = &quot;jdbc:oracle:thin:@localhost:1521:xe&quot;;
String dbUser = &quot;사용자계정명&quot;;
String dbPass = &quot;비밀번호&quot;;</p>
</blockquote>
<p>-&gt; 커넥션 객체에 이 세개를 담는다.</p>
<blockquote>
<p>conn = DriverManager.getConnection(jdbcDriver,dbUser,dbPass);</p>
</blockquote>
<ol start="3">
<li><p>Statement 생성</p>
<blockquote>
<p>stmt = conn.createStatement();</p>
</blockquote>
</li>
<li><p>쿼리 실행</p>
<blockquote>
<p>rs = stmt.excuteQuery(query);</p>
</blockquote>
</li>
<li><p>쿼리 실행 결과 출력</p>
</li>
</ol>
<p>-&gt; while(rs.next()) 사용</p>
<ol start="6">
<li>사용한 Statement객체 종료</li>
</ol>
<p>-&gt; 중요! 닫지 않으면 메모리가 꽉차서 Connection 객체와 연결이 끊어진다.</p>
<blockquote>
<pre><code>if(rs!=null)try{rs.close();}catch(SQLException ex){}
if(stmt!=null)try{stmt.close();}catch(SQLException ex){}</code></pre></blockquote>
<ol start="7">
<li>커넥션 객체 종료<blockquote>
<p>if(conn!=null)try{conn.close();}catch(SQLException ex){}</p>
</blockquote>
</li>
</ol>
<h3 id="viewmemberlistjsp">viewMemberList.jsp</h3>
<pre><code>&lt;%@page import=&quot;java.sql.SQLException&quot;%&gt;
&lt;%@page import=&quot;java.sql.ResultSet&quot;%&gt;
&lt;%@page import=&quot;java.sql.Statement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@page import=&quot;java.sql.DriverManager&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;회원 목록&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
MEMBER 테이블의 내용&lt;br&gt;
&lt;table width=&quot;100%&quot; border=&quot;1&quot;&gt;
    &lt;tr&gt;
        &lt;th&gt;이름&lt;/th&gt;&lt;th&gt;아이디&lt;/th&gt;&lt;th&gt;이메일&lt;/th&gt;
    &lt;/tr&gt;
&lt;%
//1.JDBC 드라이버 로딩
Class.forName(&quot;oracle.jdbc.driver.OracleDriver&quot;);
//--&gt;오라클 JDBC 드라이버는 오라클꺼다~~
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;

try{
//2.데이터베이스 커넥션(연결 객체) 생성
//jdbcDriver? 2)계정아이디? 3)비밀번호?
String jdbcDriver = &quot;jdbc:oracle:thin:@localhost:1521:xe&quot;;
String dbUser = &quot;jspexam&quot;;
String dbPass = &quot;java&quot;;

    String query = &quot;SELECT MEMBERID,PASSWORD,NAME,EMAIL FROM MEMBER&quot;;
    conn = DriverManager.getConnection(jdbcDriver,dbUser,dbPass);
//Connection 객체에 위에서 만든 세개 담기

//3.Statement 생성
    stmt = conn.createStatement();

//4.쿼리 실행
    rs = stmt.executeQuery(query);

//5.쿼리 실행 결과 출력
    while(rs.next()){
        out.print(&quot;&lt;tr&gt;&quot;);
        out.print(&quot;&lt;td&gt;&lt;a href=&#39;viewMember_pool.jsp?memberId=&quot;+rs.getString(&quot;MEMBERID&quot;)+&quot;&#39;&gt;&quot;+rs.getString(&quot;NAME&quot;)+&quot;&lt;/td&gt;&quot;);
        out.print(&quot;&lt;td&gt;&quot;+rs.getString(&quot;MEMBERID&quot;)+&quot;&lt;/td&gt;&quot;);
        out.print(&quot;&lt;td&gt;&quot;+rs.getString(&quot;EMAIL&quot;)+&quot;&lt;/td&gt;&quot;);
        out.print(&quot;&lt;/tr&gt;&quot;);
    }

}catch(SQLException ex){
    out.print(ex.getMessage());
    ex.printStackTrace();
}finally{
//6.사용한 Statement객체 종료 (중요!!! 닫지않으면 메모리 꽉차서 Connection객체와 연결 끊어짐)
    if(rs!=null)try{rs.close();}catch(SQLException ex){}
    if(stmt!=null)try{stmt.close();}catch(SQLException ex){}
//7.커넥션 객체 종료
    if(conn!=null)try{conn.close();}catch(SQLException ex){}
}
%&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><h3 id="updateformjsp">updateForm.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;이름 변경 폼&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form method=&quot;post&quot; action=&quot;update.jsp&quot;&gt;
    &lt;table border=&quot;1&quot;&gt;
        &lt;tr&gt;
            &lt;td&gt;아이디&lt;/td&gt;&lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;memberId&quot; size=&quot;10&quot; /&gt;&lt;/td&gt;
            &lt;td&gt;이름&lt;/td&gt;
            &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot; size=&quot;10&quot; /&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td colspan=&quot;4&quot;&gt;&lt;input type=&quot;submit&quot; value=&quot;변경&quot; /&gt;&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>*<em>Statement 객체를 이용한 쿼리 실행
*</em></p>
<ul>
<li>Connection객체를 생성한 후에는 Connection 객체로부터 Connection.createStatement() 메서드를 사용하여 Statement를 생성하고 Query를 실행할 수 있다.</li>
<li>Statement 객체가 제공하는 메서드로 쿼리를 실행한다.</li>
<li><blockquote>
<p>insert,update,delete문 실행 시 int excuteUpdate(String query) 메서드 사용
===&gt;실행 결과 변경된 레코드의 개수를 리턴! </p>
</blockquote>
</li>
</ul>
<p>-&gt; select문 실행 시 ResultSet executeQuery(String query) 메서드 사용
===&gt; ResultSet을 결과값으로 리턴!</p>
<p><strong>ResultSet에서 값 조회</strong></p>
<ul>
<li><p>next()메서드로 데이터 조회 여부 확인(커서가 이동됨)
<img src="https://images.velog.io/images/hazel_jo/post/ff9aa5b3-92ba-4fe5-a519-84809c58356e/image.png" alt=""></p>
</li>
<li><p>데이터 조회를 위한 주요 메서드</p>
</li>
<li><blockquote>
<p>ResultSet은 현재 커서 위치에 있는 행으로부터 데이터를 읽어오기 위해 get~~()형태의 메서드를 제공한다.
ex) getString(), getInt(),getLong()...</p>
</blockquote>
</li>
</ul>
<p><strong>ResultSet에서 데이터 조회하는 코드</strong></p>
<ul>
<li>1개 행 처리(if문 사용)</li>
<li>복수 행 처리 시 (whie문 사용)</li>
</ul>
<p><strong>PreparedStatement를 이용한 처리</strong></p>
<ul>
<li>SQL의 틀을 미리 정해놓고, 나중에 값을 지정한다.<blockquote>
<p>stmt = conn.prepareStatement(
 &quot;insert into MEMBER (MEMBERID, NAME, EMAIL) values (?, ?, ?)&quot;);
pstmt.setString(1, “a001”); // 첫번째 물음표의 값 지정
Pstmt.setString(2, “김은대&quot;);   // 두번째 물음표의 값 지정
pstmt.executeUpdate()</p>
</blockquote>
</li>
</ul>
<p><strong>PreparedStatement의 사용 이유</strong></p>
<ul>
<li>반복해서 실행되는 동일 쿼리의 속도를 향상시킴</li>
</ul>
<h3 id="updatejsp">update.jsp</h3>
<pre><code>&lt;%@page import=&quot;java.sql.DriverManager&quot;%&gt;
&lt;%@page import=&quot;java.sql.SQLException&quot;%&gt;
&lt;%@page import=&quot;java.sql.Statement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    request.setCharacterEncoding(&quot;UTF-8&quot;);

    //파라미터 목록 중 memberId 및 name을 get함
    String memberId = request.getParameter(&quot;memberId&quot;);
    String name = request.getParameter(&quot;name&quot;);

    out.print(&quot;memberId : &quot;+ memberId + &quot;, name : &quot; + name + &quot;&lt;br&gt;&quot;);

    //jdbc 드라이버 로딩(메모리에 올림)
    Class.forName(&quot;oracle.jdbc.driver.OracleDriver&quot;);

    //커넥션 객체 선언
    Connection conn = null;
    //Statement 객체 선언
    Statement stmt = null;

    String query = &quot;UPDATE MEMBER SET NAME = &#39;&quot;+name+&quot;&#39; WHERE MEMBERID = &#39;&quot;+memberId+&quot;&#39;&quot;;
    int updateCount = 0;

    try{
        String jdbcDriver = &quot;jdbc:oracle:thin:@localhost:1521:xe&quot;;
        String dbUser = &quot;jspexam&quot;;
        String dbPass = &quot;java&quot;;

        //커넥션 객체 생성
        conn = DriverManager.getConnection(jdbcDriver,dbUser,dbPass);

        //Statement 객체 생성
        stmt = conn.createStatement();

        //insert,update,delete --&gt; executeUpdate()
        //select --&gt; executeQuery()
        updateCount = stmt.executeUpdate(query);
    }catch(SQLException ex){
        out.print(ex.getMessage());        
    }finally{
        if(stmt!=null)try{stmt.close();}catch(SQLException ex){}
        if(conn!=null)try{conn.close();}catch(SQLException ex){}        
    }
    if(updateCount&gt;0){
        out.print(memberId + &quot;의 이름을 &quot; + name + &quot;(으)로 변경&quot;);
    }else{
        out.print(memberId + &quot; 아이디가 없음&quot;);
    }


%&gt;</code></pre><h3 id="insertformjsp">insertForm.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;MEMBER 테이블에 레코드 입력&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form method=&quot;post&quot; action=&quot;insert.jsp&quot;&gt;
    &lt;table border=&quot;1&quot;&gt;
        &lt;tr&gt;
            &lt;th&gt;아이디&lt;/th&gt;
            &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;memberid&quot; size=&quot;10&quot;&gt;&lt;/td&gt;
            &lt;th&gt;비밀번호&lt;/th&gt;
            &lt;td&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot; size=&quot;10&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;th&gt;이름&lt;/th&gt;
            &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot; size=&quot;10&quot;&gt;&lt;/td&gt;
            &lt;th&gt;이메일&lt;/th&gt;
            &lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;email&quot; size=&quot;10&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td colspan=&quot;4&quot;&gt;&lt;input type=&quot;submit&quot; value=&quot;입력&quot; /&gt;&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="insertjsp">insert.jsp</h3>
<pre><code>&lt;%@page import=&quot;java.sql.PreparedStatement&quot;%&gt;
&lt;%@page import=&quot;java.sql.SQLException&quot;%&gt;
&lt;%@page import=&quot;java.sql.DriverManager&quot;%&gt;
&lt;%@page import=&quot;java.sql.Statement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    request.setCharacterEncoding(&quot;UTF-8&quot;);

    String memberid = request.getParameter(&quot;memberid&quot;);
    String password = request.getParameter(&quot;password&quot;);
    String name = request.getParameter(&quot;name&quot;);
    String email = request.getParameter(&quot;email&quot;);

    out.print(memberid + &quot;, &quot; + password + &quot;, &quot; + name + &quot;, &quot; + email);

    Class.forName(&quot;oracle.jdbc.driver.OracleDriver&quot;);

    //커넥션 객체 선언
    Connection conn = null;
    //Statement 객체 선언
    PreparedStatement pstmt = null;

    String query = &quot;INSERT INTO MEMBER(MEMBERID,PASSWORD,NAME,EMAIL) &quot;
            + &quot; VALUES(?,?,?,?)&quot;;

    try{
        String jdbcDriver = &quot;jdbc:oracle:thin:@localhost:1521:xe&quot;;
        String dbUser = &quot;jspexam&quot;;
        String dbPass = &quot;java&quot;;

        //커넥션 객체 생성
        conn = DriverManager.getConnection(jdbcDriver,dbUser,dbPass);

        //Statement 객체 생성
        pstmt = conn.prepareStatement(query);
        pstmt.setString(1, memberid);
        pstmt.setString(2, password);
        pstmt.setString(3, name);
        pstmt.setString(4, email);

        //insert,update,delete --&gt; executeUpdate()
        //select --&gt; executeQuery()
        pstmt.executeUpdate();
    }catch(SQLException ex){
        out.print(ex.getMessage());        
    }finally{
        if(pstmt!=null)try{pstmt.close();}catch(SQLException ex){}
        if(conn!=null)try{conn.close();}catch(SQLException ex){}        
    }

%&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;입력&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    MEMBER 테이블에 새로운 레코드를 입력했습니다.
&lt;/body&gt;
&lt;/html&gt;</code></pre><ul>
<li><p>자바빈 클래스 생성</p>
<h3 id="memjava">Mem.java</h3>
<pre><code>package dto;
//자바빈 클래스
public class Mem {
  private String memberid;
  private String password;
  private String name;
  private String email;

  //기본생성자
  public Mem() {
      super();
  }

  public String getMemberid() {
      return memberid;
  }

  public void setMemberid(String memberid) {
      this.memberid = memberid;
  }

  public String getPassword() {
      return password;
  }

  public void setPassword(String password) {
      this.password = password;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public String getEmail() {
      return email;
  }

  public void setEmail(String email) {
      this.email = email;
  }

</code></pre></li>
</ul>
<p>}</p>
<pre><code>
## 커넥션 풀
- 데이터베이스와 연결된 커넥션을 미리 만들어서 풀(pool)속에 저장해두고 있다가 필요할 때에 커넥션을 풀에서 가져다 쓰고, 사용이 끝나면 다시 풀에 반환하는 기법

- 특징
1. 커넥션을 미리 생성해두기 때문에 사용자가 DB를 사용할 때마다 매번 생성하는 것보다 더 빠른 속도를 보장한다.

2. 커넥션의 최대 생성 개수도 제어해주기 때문에 많은 사용자가 몰려도 과부하를 방지할 수 있다.

- 커넥션 풀의 오픈소스에는 대표적으로 DBCP와 C3P0이 있는데 여기선 DBCP를 사용했다. DBCP는 Apache 사이트에서 받을 수 있다.
- 그 외에 DBCP에서 사용한 Pool인 Commons Pool이 필요하다.
- 부가적으로 로그 기록에 사용되는 Commons Logging도 필요하다.

### DBCP API를 이용한 커넥션 풀 사용
**1. 필요 라이브러리**
&gt;
![](https://images.velog.io/images/hazel_jo/post/36075d1c-1eb5-41fd-8633-52210f3dddbb/image.png)

**    설치 방법**

https://mvnrepository.com/ 에서
commons-dbcp검색
Apache Commons DBCP클릭 - 2.9.0 클릭
jar 클릭해서 다운로드


commons-pool검색
Apache Commons Pool클릭 - 2.9.0
jar 클릭해서 다운로드


commons-logging검색
 Apache Commons Logging 클릭 -  1.2
jar 클릭해서 다운로드

다운로드 받은 파일들 복사해서 WEB-INF\lib에 붙여넣기

**2.데이터 베이스 세팅**

1)계정 생성 및 권한 부여
2)테이블 생성

- DBCP 설정 클래스
### DBCPInit.java</code></pre><p>package jdbc;</p>
<p>import java.sql.DriverManager;</p>
<p>import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;</p>
<p>import org.apache.commons.dbcp2.ConnectionFactory;
import org.apache.commons.dbcp2.DriverManagerConnectionFactory;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDriver;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;</p>
<p>//서블릿 클래스
public class DBCPInit extends HttpServlet {
    @Override
    public void init() throws ServletException{
        //JDBC Driver를 로딩
        loadJDBCDriver();
        //커넥션풀을 초기화
        initConnectionPool();
    }</p>
<pre><code>//JDBC Driver를 로딩
private void loadJDBCDriver() {
    try {
        //커넥션풀이 내부에서 사용할 JDBC Driver를 로딩함
        Class.forName(&quot;oracle.jdbc.driver.OracleDriver&quot;);
    }catch(ClassNotFoundException ex){
        throw new RuntimeException(&quot;fail to load JDBC Driver&quot;, ex);
    }
}
//커넥션풀을 초기화
private void initConnectionPool() {
    try {
        //의국에서 새로운 외과 의사를 생성할 때 필요한 외과가 있어야 함
        //커넥션풀에서 새로운 커넥션을 생성할 때 사용할 커넥션 팩토리를 생성 
        ConnectionFactory connFactory = 
                new DriverManagerConnectionFactory(
                        &quot;jdbc:oracle:thin:@localhost:1521:xe&quot;
                        ,&quot;jspexam&quot;
                        ,&quot;java&quot;
                        );
        //DBCP는 커넥션풀에 커넥션을 보관할 때 PoolableConnection을 사용
        //이 Class는 내부적으로 실제 Connection을 담고 있음
        //커넥션풀을 관리
        PoolableConnectionFactory poolableConnFactory = 
                new PoolableConnectionFactory(connFactory, null);

        //커넥션이 유효한지 여부 검사 시 사용할 쿼리
        poolableConnFactory.setValidationQuery(&quot;select 1&quot;);
        //커넥션 풀의 설정 정보 생성
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        //유휴 커넥션 검사 주기(1/1000초)
        //놀고 있는 커넥션을 풀에서 제거하는 시간 기준 (5분)
        poolConfig.setTimeBetweenEvictionRunsMillis(1000L * 60L * 5L);
        //풀에 보관중인 커넥션이 유효한지 검사할지 여부
        poolConfig.setTestWhileIdle(true);
        //커넥션 최소 개수
        poolConfig.setMinIdle(4);
        //커넥션 최대 개수
        poolConfig.setMaxTotal(50);

        //커넥션풀의 설정 정보를 생성. 팩토리와 커넥션풀 설정을 파라미터로 받음
        GenericObjectPool&lt;PoolableConnection&gt; connectionPool = 
                new GenericObjectPool&lt;&gt;(poolableConnFactory, poolConfig);
        //풀러블커넥션팩토리에도 생성이 된 커넥션 풀을 연결
        poolableConnFactory.setPool(connectionPool);
        //커넥션풀을 제공하는 JDBC 드라이버를 등록함
        Class.forName(&quot;org.apache.commons.dbcp2.PoolingDriver&quot;);
        PoolingDriver driver = (PoolingDriver)DriverManager.getDriver(&quot;jdbc:apache:commons:dbcp:&quot;);
        //커넥션 풀 드라이버에 생성된 커넥션풀을 등록
        //jdbc:apache:commons:dbcp:ddit
        driver.registerPool(&quot;ddit&quot;, connectionPool);
    }catch(Exception e) {
        throw new RuntimeException(e);

    }//end try
}//end initConnectionPool();</code></pre><p>}</p>
<pre><code>
- 커넥션을 제공하는 클래스
### ConnectionProvider.java</code></pre><p>package jdbc;</p>
<p>import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;</p>
<p>//커넥션을 제공해주는 클래스
public class ConnectionProvider {
    public static Connection getConnection() throws SQLException{
        return DriverManager.getConnection(&quot;jdbc:apache:commons:dbcp:ddit&quot;);
    }
}</p>
<pre><code>- JDBCUtil 클래스 생성해서 사용한 객체 종료 쉽게하기
### JDBCUtil.java</code></pre><p>package jdbc;</p>
<p>import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
//하나의 클래스에 같은 이름의 메소드를 여러번 사용 가능?
//오버로딩</p>
<p>public class JdbcUtil {
    public static void close(ResultSet rs) {
        if(rs!=null) try {rs.close();}catch(SQLException ex) {}
    }
    public static void close(Statement stmt) {
        if(stmt!=null) try {stmt.close();}catch(SQLException ex) {}
    }
    public static void close(Connection conn) {
        if(conn!=null) try {conn.close();}catch(SQLException ex) {}
    }
    public static void close(PreparedStatement pstmt) {
        if(pstmt!=null) try {pstmt.close();}catch(SQLException ex) {}
    }
    public static void rollback(Connection conn) {
        if(conn!=null) try {conn.close();}catch(SQLException ex) {}
    }
}</p>
<pre><code>
#### JSP 서블릿(Servlet)
- 동적 웹 어플리케이션 컴포넌트
- .java 확장자
- 클라이언트의 요청에 동적으로 작동하고, 응답은 html을 이용
- 자바 thread를 이용하여 동작 -&gt; 서버에 부하가 적게 걸리는 장점이 있음
- MVC 패턴에서 controller로 이용됨(JSP는 뷰로 이용됨)



**DBCP 이용한 방법으로 list 출력하기**
### viewMemberUsingPool.jsp</code></pre><p>&lt;%@page import=&quot;jdbc.ConnectionProvider&quot;%&gt;
&lt;%@page import=&quot;jdbc.JdbcUtil&quot;%&gt;
&lt;%@page import=&quot;java.sql.SQLException&quot;%&gt;
&lt;%@page import=&quot;java.sql.DriverManager&quot;%&gt;
&lt;%@page import=&quot;java.sql.ResultSet&quot;%&gt;
&lt;%@page import=&quot;java.sql.Statement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;<a href="http://java.sun.com/jsp/jstl/core&quot;">http://java.sun.com/jsp/jstl/core&quot;</a> %&gt; 
<!DOCTYPE html></p>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
MEMBER 테이블의 내용<br>
<table width="100%" border="1">
    <tr>
        <th>이름</th><th>아이디</th><th>이메일</th>
    </tr>
<%
//1.JDBC 드라이버 로딩
    Class.forName("oracle.jdbc.driver.OracleDriver");
    //-->오라클 JDBC 드라이버는 오라클꺼다~~
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;

<p>try{
//2.데이터베이스 커넥션(연결 객체) 생성
//1)jdbcDriver? 2)계정아이디? 3)비밀번호?
    String jdbcDriver = &quot;jdbc:apache:commons:dbcp:ddit&quot;;
    String query = &quot;SELECT MEMBERID,PASSWORD,NAME,EMAIL FROM MEMBER&quot;;
//     conn = DriverManager.getConnection(jdbcDriver); 이제 이거 안씀
    conn = ConnectionProvider.getConnection();</p>
<p>//3.Statement 생성
    stmt = conn.createStatement();</p>
<p>//4.쿼리 실행
    rs = stmt.executeQuery(query);</p>
<p>//5.쿼리 실행 결과 출력
    while(rs.next()){
        out.print(&quot;<tr>&quot;);
        out.print(&quot;<td><a href='viewMember.jsp?memberId="+rs.getString("MEMBERID")+"'>&quot;+rs.getString(&quot;NAME&quot;)+&quot;</td>&quot;);
        out.print(&quot;<td>&quot;+rs.getString(&quot;MEMBERID&quot;)+&quot;</td>&quot;);
        out.print(&quot;<td>&quot;+rs.getString(&quot;EMAIL&quot;)+&quot;</td>&quot;);
        out.print(&quot;</tr>&quot;);
    }</p>
<p>}catch(SQLException ex){
    out.print(ex.getMessage());
    ex.printStackTrace();
}finally{
//6.사용한 Statement객체 종료 
    JdbcUtil.close(rs);
    JdbcUtil.close(stmt);
//7.커넥션 객체 종료 -&gt; dbcp이니까 커넥션 객체를 커넥션 풀로 반환
    JdbcUtil.close(conn);
}
%&gt;</p>
</table>
</body>
</html>
```

<p><strong>위 리스트에서 a태그(memberid)클릭 시 상세화면 이동</strong></p>
<h3 id="viewmemberjsp">viewMember.jsp</h3>
<pre><code>&lt;%@page import=&quot;dto.Mem&quot;%&gt;
&lt;%@page import=&quot;jdbc.ConnectionProvider&quot;%&gt;
&lt;%@page import=&quot;jdbc.JdbcUtil&quot;%&gt;
&lt;%@page import=&quot;java.sql.DriverManager&quot;%&gt;
&lt;%@page import=&quot;java.sql.SQLException&quot;%&gt;
&lt;%@page import=&quot;java.sql.ResultSet&quot;%&gt;
&lt;%@page import=&quot;java.sql.Statement&quot;%&gt;
&lt;%@page import=&quot;java.sql.Connection&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    String memberId = request.getParameter(&quot;memberId&quot;);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;회원 정보&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
    //jdbc 드라이버 로딩(메모리에 올림)
    Class.forName(&quot;oracle.jdbc.driver.OracleDriver&quot;);

    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    Mem memVO = new Mem();

    String query = &quot;SELECT MEMBERID,PASSWORD,NAME,EMAIL FROM MEMBER &quot;
                + &quot; WHERE MEMBERID = &#39;&quot;+memberId+&quot;&#39;&quot;;

    try{
//         conn = DriverManager.getConnection(
//                 &quot;jdbc:oracle:thin:@localhost:1521:xe&quot;,
//                 &quot;jspexam&quot;,
//                 &quot;java&quot;);
        conn = ConnectionProvider.getConnection();
        stmt = conn.createStatement();
        rs = stmt.executeQuery(query);
        if(rs.next()){
            memVO.setMemberid(rs.getString(&quot;MEMBERID&quot;));
            memVO.setName(rs.getString(&quot;NAME&quot;));
            memVO.setPassword(rs.getString(&quot;PASSWORD&quot;));
            memVO.setEmail(rs.getString(&quot;EMAIL&quot;));
        }
        out.print(&quot;memVO : &quot; + memVO.toString());
    }catch(SQLException ex){
        out.print(ex.getMessage());
    }finally{
        //6.사용한 Statement객체 종료 
        JdbcUtil.close(rs);
        JdbcUtil.close(stmt);
        //7.커넥션 객체 종료 -&gt; dbcp이니까 커넥션 객체를 커넥션 풀로 반환
        JdbcUtil.close(conn);
    }
%&gt;
&lt;c:set var=&quot;memVO&quot; value=&quot;&lt;%=memVO%&gt;&quot; scope=&quot;request&quot; /&gt;
&lt;!-- scope=&quot;request&quot; 면 하나의 객체를 공유해서 쓸수있다 --&gt;
&lt;table border=&quot;1&quot;&gt;
    &lt;tr&gt;
        &lt;th&gt;아이디 &lt;/th&gt;&lt;td&gt;${memVO.memberid}&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;th&gt;비밀번호 &lt;/th&gt;&lt;td&gt;${memVO.password}&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;th&gt;이름&lt;/th&gt;&lt;td&gt;${memVO.name}&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;th&gt;이메일&lt;/th&gt;&lt;td&gt;${memVO.email}&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;



&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[JSP 16강 - 장바구니 만들기, 상품 주문하기]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-16%EA%B0%95-%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-16%EA%B0%95-%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sun, 23 Jan 2022 12:04:47 GMT</pubDate>
            <description><![CDATA[<p>장바구니 만들기
기본이 세션, 쿠키가 양념
쿠키는 크롬에 저장을 하고, 세션은 톰켓에 저장한다</p>
<p>웹브라우저를 실행하면 JSessionID가 자동으로 생성되고, 쿠키에 들어간다.</p>
<p>장바구니는 세션, 장바구니 Id = 세션 Id</p>
<h3 id="addcartjsp">addCart.jsp</h3>
<pre><code>&lt;%@page import=&quot;java.util.ArrayList&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%@ page import=&quot;dto.Product&quot;%&gt;
&lt;%@ page import=&quot;ch04.com.dao.ProductRepository&quot;%&gt;
&lt;%@ page import=&quot;java.util.List&quot;%&gt;
&lt;%
    String id = request.getParameter(&quot;id&quot;);
    //id가 없거나 값이 없을 때
    if(id==null||id.trim().equals(&quot;&quot;)){
        response.sendRedirect(&quot;products.jsp&quot;);
        return;
    }

    //상품저장소 객체 생성
    ProductRepository dao = ProductRepository.getInstance();

    //상품 아이디에 해당하는 정보를 얻어와보자
    Product product = dao.getProductById(id);
    //id의 값이 P999 이런 경우 상품이 없다
    if(product == null){
        response.sendRedirect(&quot;/exceptionNoProductId.jsp&quot;);
    }
    //모든 상품을 가져와보자
    List&lt;Product&gt; goodsList = dao.getAllProducts();
    Product goods = new Product();
    for(int i=0; i&lt;goodsList.size();i++){
        //요청 파라미터 아이디의 상품이 존재하는지 검사
        goods = goodsList.get(i);
        //A.equals(id)
        if(goods.getProductId().equals(id)){
            //for문에서 벗어나라
            break;
        }
    }

    //*요청 파라미터 아이디의 상품을 담은 장바구니를 초기화
    //세션 : cartlist를 얻어와  Array 객체에 저장
    ArrayList&lt;Product&gt; list = (ArrayList&lt;Product&gt;)session.getAttribute(&quot;cartlist&quot;);
    out.print(&quot;list의 크기 : &quot; + list);
    //만약 cartlist라는 세션 정보가 없다면 ArrayList 객체를 생성하고 cartlist세션 생성
    if(list == null){
        list = new ArrayList&lt;Product&gt;();
        session.setAttribute(&quot;cartlist&quot;, list);
    }
    //list : 장바구니에 담긴 상품 목록
    int cnt = 0;
    Product goodsQnt = new Product();
    for(int i=0; i&lt;list.size();i++){
        //요청 파라미터 아이디 addCart.jsp?id=P1234의 상품이 장바구니에 담긴 목록에 있다면
        //해당 상품의 수량을 1 증가
        goodsQnt = list.get(i);
        if(goodsQnt.getProductId().equals(id)){
            cnt++;
            int orderQuantity = goodsQnt.getQuantity() + 1;
            goodsQnt.setQuantity(orderQuantity);
        }
    }
    //요청 파라미터 아이디 addCart.jsp?id=P1234의 상품이 장바구니에 담긴 목록에 없다면
    //해당 상품의 수량을 1로 처리
    if(cnt == 0){
        goods.setQuantity(1);
        list.add(goods);
    }

    response.sendRedirect(&quot;product.jsp?id=&quot; + id);
%&gt;</code></pre><h3 id="productjsp">product.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%@ page import=&quot;dto.Product&quot;%&gt;
&lt;%@ page import=&quot;ch04.com.dao.ProductRepository&quot;%&gt;
&lt;%@ page errorPage=&quot;/exceptionNoProductId.jsp&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot; /&gt;
&lt;title&gt;상품 상세 정보&lt;/title&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
    function addToCart(){
        //확인 : true, 취소 : false
        if(confirm(&quot;상품을 장바구니에 추가하겠습니까?&quot;)){ //확인
            document.addForm.submit();
        }else{
            document.addForm.reset();
        }
    }
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%-- 객체를 새로 생성한다.
&lt;jsp:useBean id=&quot;productDAO&quot; class=&quot;ch04.com.dao.ProductRepository&quot; /&gt; --%&gt;
&lt;%
    ProductRepository productDAO = ProductRepository.getInstance();
%&gt;
    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h1 class=&quot;display-3&quot;&gt;상품 정보&lt;/h1&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;%
//     product.jsp?id=P1234
    String id = request.getParameter(&quot;id&quot;);
//     out.print(&quot;id : &quot; + id + &quot;&lt;br /&gt;&quot;);

    //예전엔 이렇게 썼지만 이제는 useBean사용
//  ProductRepository productDAO = new ProductRepository();
    Product product = productDAO.getProductById(id); 

    //null.toString() -&gt; 오류 임의 발생(errorPage 발생 유도)
    product.toString();
    %&gt;
    &lt;c:set var=&quot;product&quot; value=&quot;&lt;%=product%&gt;&quot; /&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;div class=&quot;row&quot;&gt;
            &lt;div class=&quot;col-md-5&quot;&gt;
                &lt;img src=&quot;/upload/${product.filename}&quot; style=&quot;width:100%;&quot;/&gt;
            &lt;/div&gt;
            &lt;div class=&quot;col-md-6&quot;&gt;
                &lt;h3&gt;${product.pname}&lt;/h3&gt;
                &lt;p&gt;${product.description}&lt;/p&gt;
                &lt;p&gt;
                    &lt;b&gt;상품 코드 : &lt;/b&gt;
                    &lt;span class=&quot;badge badge-danger&quot;&gt;${product.productId}&lt;/span&gt;
                &lt;/p&gt;
                &lt;p&gt;&lt;b&gt;제조사&lt;/b&gt; : ${product.manufacturer}&lt;/p&gt;
                &lt;p&gt;&lt;b&gt;분류&lt;/b&gt; : ${product.category}&lt;/p&gt;
                &lt;p&gt;&lt;b&gt;재고 수&lt;/b&gt; : ${product.unitsInStock}&lt;/p&gt;
                &lt;h4&gt;${product.uniPrice}원&lt;/h4&gt;
                &lt;p&gt;
                    &lt;form name=&quot;addForm&quot; method=&quot;post&quot; action=&quot;addCart.jsp?id=${product.productId}&quot;&gt;
                        &lt;a href=&quot;#&quot; class=&quot;btn btn-info&quot; onclick=&quot;addToCart()&quot;&gt;상품 주문&amp;raquo;&lt;/a&gt;
                        &lt;a href=&quot;cart.jsp&quot; class=&quot;btn btn-warning&quot;&gt;장바구니&amp;raquo;&lt;/a&gt;
                        &lt;a href=&quot;products.jsp&quot; class=&quot;btn btn-secondary&quot;&gt;상품목록&amp;raquo;&lt;/a&gt;
                    &lt;/form&gt;
                &lt;/p&gt;

            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="deletecartjsp">deleteCart.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
String id = request.getParameter(&quot;cartId&quot;);
if(id==null||id.trim().equals(&quot;&quot;)){
    response.sendRedirect(&quot;cart.jsp&quot;);
    return;
}
//장바구니에 등록된 모든 상품을 삭제
session.invalidate();

//cart.jsp로 되돌아가기
response.sendRedirect(&quot;cart.jsp&quot;);
%&gt;</code></pre><h3 id="productsjsp">products.jsp</h3>
<pre><code>&lt;%@page import=&quot;ch04.com.dao.ProductRepository&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%@ page import=&quot;dto.Product&quot;%&gt;
&lt;%@ page import=&quot;java.util.List&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot; /&gt;
&lt;title&gt;상품 목록&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%-- &lt;jsp:useBean id=&quot;productDAO&quot; class=&quot;ch04.com.dao.ProductRepository&quot; /&gt; --%&gt;
&lt;%
ProductRepository productDAO = ProductRepository.getInstance();
%&gt;

&lt;!-- top.jsp 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;
&lt;!-- top.jsp 인클루드 끝 --&gt;
&lt;!-- jumbotron 스타일 시작 --&gt;
    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
        &lt;!-- container -&gt; 이 안에 내용이 있을거야 --&gt;
            &lt;h1 class=&quot;display-3&quot;&gt;상품 목록&lt;/h1&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;!-- jumbotron 스타일 끝 --&gt;
    &lt;%
        List&lt;Product&gt; listOfProducts = productDAO.getAllProducts();
    %&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;div class=&quot;row&quot; align=&quot;center&quot;&gt;
            &lt;!-- row -&gt; 행 단위로 찍을거야 --&gt;
            &lt;!-- 상품 반복 시작 --&gt;
            &lt;%
                for(int i=0; i&lt;listOfProducts.size(); i++){
                    Product product = listOfProducts.get(i);
            %&gt;

                &lt;div class=&quot;col-md-4&quot;&gt;
                    &lt;img src=&quot;/upload/&lt;%=product.getFilename()%&gt;&quot; style=&quot;width: 350px; height: 300px&quot;/&gt;
                    &lt;h3&gt;&lt;%=product.getPname() %&gt;&lt;/h3&gt;
                    &lt;p&gt;&lt;%=product.getDescription() %&gt;&lt;/p&gt;
                    &lt;p&gt;&lt;%=product.getUniPrice() %&gt;원&lt;/p&gt;
                    &lt;p&gt;&lt;a href=&quot;product.jsp?id=&lt;%=product.getProductId() %&gt;&quot; class=&quot;btn btn-secondary&quot; role=&quot;button&quot;&gt;상세 정보&amp;raquo;&lt;/a&gt;&lt;/p&gt;
                &lt;/div&gt;
            &lt;%
                }//end for
            %&gt;    

            &lt;!-- 상품 반복 끝 --&gt;        
            &lt;/div&gt;
        &lt;/div&gt;
&lt;!-- bottom.jsp 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;        
&lt;!-- bottom.jsp 인클루드 끝 --&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="shippinginfojsp">shippingInfo.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot; /&gt;
&lt;title&gt;배송 정보&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- top 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;
&lt;!-- top 인클루드 끝 --&gt;
    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h1 class=&quot;display-3&quot;&gt;배송 정보&lt;/h1&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;form method=&quot;post&quot; class=&quot;form-horizontal&quot; action=&quot;shippinginfo_process.jsp&quot;&gt;
            &lt;input type=&quot;hidden&quot; name=&quot;cartId&quot; value=&quot;${param.cartId}&quot; /&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;성명&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;name&quot; class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;배송일&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;shippingDate&quot; class=&quot;form-control&quot; /&gt;
                    (yyyy/mm/dd)
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;국가&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;country&quot; class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;우편번호&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;zipCode&quot; class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;주소&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;addressName&quot; class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;div class=&quot;col-sm-offset-2 col-sm-10&quot;&gt;
                &lt;!-- ?cartId=3F33D163E0590415FB7AB78354FC6BD3 --&gt;
                    &lt;a href=&quot;shippingInfo.jsp?cardId=${param.cartId}&quot; class=&quot;btn btn-secondary&quot; role=&quot;button&quot;&gt;이전&lt;/a&gt;
                    &lt;input type=&quot;submit&quot; class=&quot;btn btn-primary&quot; value=&quot;등록&quot; /&gt;                
                    &lt;a href=&quot;checkOutcancelled.jsp&quot; class=&quot;btn btn-secondary&quot; role=&quot;button&quot;&gt;취소&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;

        &lt;/form&gt;

    &lt;/div&gt;
&lt;!-- bottom 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;
&lt;!-- bottom 인클루드 끝 --&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="shippinginfo_processjsp">shippinginfo_process.jsp</h3>
<pre><code>&lt;%@page import=&quot;java.net.URLEncoder&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    request.setCharacterEncoding(&quot;UTF-8&quot;);

    //쿠키 생성 문법?
    //Cookie 객체명 = new Cookie(name,value);
    Cookie cartId = new Cookie(&quot;Shipping_cartId&quot;,
            URLEncoder.encode(request.getParameter(&quot;cartId&quot;),&quot;UTF-8&quot;));
    Cookie name = new Cookie(&quot;Shipping_name&quot;,
            URLEncoder.encode(request.getParameter(&quot;name&quot;),&quot;UTF-8&quot;));
    Cookie shippingDate = new Cookie(&quot;Shipping_shippingDate&quot;,
            URLEncoder.encode(request.getParameter(&quot;shippingDate&quot;),&quot;UTF-8&quot;));
    Cookie country = new Cookie(&quot;Shipping_country&quot;,
            URLEncoder.encode(request.getParameter(&quot;country&quot;),&quot;UTF-8&quot;));
    Cookie zipCode = new Cookie(&quot;Shipping_zipCode&quot;,
            URLEncoder.encode(request.getParameter(&quot;zipCode&quot;),&quot;UTF-8&quot;));
    Cookie addressName = new Cookie(&quot;Shipping_addressName&quot;,
            URLEncoder.encode(request.getParameter(&quot;addressName&quot;),&quot;UTF-8&quot;));
    //생성한 쿠키의 유효시간을 24시간으로 설정
    cartId.setMaxAge(24*60*60);
    name.setMaxAge(24*60*60);
    shippingDate.setMaxAge(24*60*60);
    country.setMaxAge(24*60*60);
    zipCode.setMaxAge(24*60*60);
    addressName.setMaxAge(24*60*60);
    //쿠키를 등록하기 위해 response객체를 통해 클라이언트(크롬)에게 전달해 줌
    response.addCookie(cartId);
    response.addCookie(name);
    response.addCookie(shippingDate);
    response.addCookie(country);
    response.addCookie(zipCode);
    response.addCookie(addressName);

    response.sendRedirect(&quot;orderConfirmation.jsp&quot;);

%&gt;</code></pre><h3 id="orderconfirmationjsp">orderConfirmation.jsp</h3>
<pre><code>&lt;%@page import=&quot;java.net.URLDecoder&quot;%&gt;
&lt;%@page import=&quot;dto.Product&quot;%&gt;
&lt;%@page import=&quot;java.util.List&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; %&gt;
&lt;%
    request.setCharacterEncoding(&quot;UTF-8&quot;);

    String cartId = session.getId();

    Cookie[] cookies = request.getCookies();
    String shipping_cartId = &quot;&quot;;
    String shipping_name = &quot;&quot;;
    String shipping_shippingDate = &quot;&quot;;
    String shipping_country = &quot;&quot;;
    String shipping_zipCode = &quot;&quot;;
    String shipping_addressName = &quot;&quot;;

    // 쿠키가 있어야 함 
    if(cookies != null){
       for(int i=0; i&lt;cookies.length;i++){
          Cookie thisCookie = cookies[i];
          // 쿠키명을 가져오기
          String n = thisCookie.getName();
          // 쿠키명에 매핑되어 있는 값을 가져오기
          if(n.equals(&quot;Shipping_cartId&quot;)){
             shipping_cartId = URLDecoder.decode(thisCookie.getValue(),&quot;UTF-8&quot;);
          }
          if(n.equals(&quot;Shipping_name&quot;)){
             shipping_name = URLDecoder.decode(thisCookie.getValue(),&quot;UTF-8&quot;);
          }
          if(n.equals(&quot;Shipping_shippingDate&quot;)){
             shipping_shippingDate = URLDecoder.decode(thisCookie.getValue(),&quot;UTF-8&quot;);
          }
          if(n.equals(&quot;Shipping_country&quot;)){
             shipping_country = URLDecoder.decode(thisCookie.getValue(),&quot;UTF-8&quot;);
          }
          if(n.equals(&quot;Shipping_zipCode&quot;)){
             shipping_zipCode = URLDecoder.decode(thisCookie.getValue(),&quot;UTF-8&quot;);
          }
          if(n.equals(&quot;Shipping_addressName&quot;)){
             shipping_addressName = URLDecoder.decode(thisCookie.getValue(),&quot;UTF-8&quot;);
          }


       }
    }
%&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot;&gt;
&lt;title&gt;주문 정보&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- top 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;
&lt;!-- top 인클루드 끝 --&gt;
    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h1 class=&quot;display-3&quot;&gt;주문 정보&lt;/h1&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;!-- 배송에 관한 정보 시작 --&gt;
    &lt;div class=&quot;container col-8 alert alert-info&quot;&gt;
        &lt;div class=&quot;text-center&quot;&gt;
            &lt;h1&gt;영수증&lt;/h1&gt;
        &lt;/div&gt;
        &lt;div class=&quot;row justify-content-between&quot;&gt;
            &lt;div class=&quot;col-4&quot; align=&quot;left&quot;&gt;
                &lt;strong&gt;배송 주소&lt;/strong&gt;&lt;br&gt;
                성명 : &lt;%=shipping_name%&gt; &lt;br&gt;
                우편번호 : &lt;%=shipping_zipCode%&gt; &lt;br&gt;
                주소 : &lt;%=shipping_addressName%&gt;(&lt;%=shipping_country%&gt;)&lt;br&gt;
            &lt;/div&gt;
            &lt;div class=&quot;col-4&quot; align=&quot;right&quot;&gt;
                &lt;p&gt;&lt;em&gt;배송일 : &lt;%=shipping_shippingDate %&gt;&lt;/em&gt;&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;!-- 배송에 관한 정보 끝 --&gt;
        &lt;div&gt;
            &lt;table class=&quot;table table-hover&quot;&gt;
                &lt;tr&gt;
                    &lt;th class=&quot;text-center&quot;&gt;상품&lt;/th&gt;
                    &lt;th class=&quot;text-center&quot;&gt;#&lt;/th&gt;
                    &lt;th class=&quot;text-center&quot;&gt;가격&lt;/th&gt;
                    &lt;th class=&quot;text-center&quot;&gt;소계&lt;/th&gt;
                &lt;/tr&gt;
                &lt;%
                    //장바구니는 세션을 사용한다(세션명 : cartlist)
                    List&lt;Product&gt; cartList = (List&lt;Product&gt;)session.getAttribute(&quot;cartlist&quot;);
                %&gt;
                &lt;c:set var=&quot;cartList&quot; value=&quot;&lt;%=cartList%&gt;&quot;/&gt;
                &lt;!--상품에 관한 정보 시작--&gt;
                &lt;c:forEach var=&quot;product&quot; items=&quot;${cartList}&quot;&gt;
                &lt;tr&gt;
                    &lt;td class=&quot;text-center&quot;&gt;&lt;em&gt;${product.pname}&lt;/em&gt;&lt;/td&gt;
                    &lt;td class=&quot;text-center&quot;&gt;${product.quantity}&lt;/td&gt;
                    &lt;td class=&quot;text-center&quot;&gt;&lt;fmt:formatNumber value=&quot;${product.uniPrice}&quot; pattern=&quot;#,###&quot;/&gt;원&lt;/td&gt;
                    &lt;td class=&quot;text-center&quot;&gt;&lt;fmt:formatNumber value=&quot;${product.quantity * product.uniPrice}&quot; pattern=&quot;#,###&quot;/&gt;원&lt;/td&gt;
                &lt;/tr&gt;
                    &lt;!-- sum으로 누적 --&gt;
                    &lt;c:set var=&quot;sum&quot; value=&quot;${sum + product.quantity * product.uniPrice}&quot; /&gt;
                &lt;/c:forEach&gt;
                &lt;tr&gt;
                    &lt;td&gt;&lt;/td&gt;
                    &lt;td&gt;&lt;/td&gt;
                    &lt;td&gt;&lt;strong&gt;총액 : &lt;/strong&gt;&lt;/td&gt;
                    &lt;td class=&quot;text-center text-danger&quot;&gt;&lt;strong&gt;&lt;fmt:formatNumber value=&quot;${sum}&quot; pattern=&quot;#,###&quot;/&gt;원&lt;/strong&gt;&lt;/td&gt;
                &lt;/tr&gt;
            &lt;/table&gt;
        &lt;a href=&quot;shippingInfo.jsp?cartId=${param.cartId }&quot; class=&quot;btn btn-secondary&quot; role=&quot;button&quot;&gt;이전&lt;/a&gt;
        &lt;a href=&quot;thankCustomer.jsp&quot; class=&quot;btn btn-success&quot;&gt;주문완료&lt;/a&gt;
        &lt;a href=&quot;checkOutCancelled.jsp&quot; class=&quot;btn btn-secondary&quot; role=&quot;button&quot;&gt;취소&lt;/a&gt;

        &lt;/div&gt;


    &lt;/div&gt;        
&lt;!-- bottom 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;
&lt;!-- bottom 인클루드 끝 --&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="checkoutcancelledjsp">checkOutcancelled.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot; /&gt;
&lt;title&gt;주문 취소&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- top 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;
&lt;!-- top 인클루드 끝 --&gt;

    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h1 class=&quot;display-3&quot;&gt;주문 취소&lt;/h1&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;h2 class=&quot;alert alert-danger&quot;&gt;주문이 취소되었습니다.&lt;/h2&gt;
    &lt;/div&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;p&gt;
            &lt;a href=&quot;products.jsp&quot; class=&quot;btn btn-secondary&quot;&gt;&amp;raquo;상품 목록&lt;/a&gt;
        &lt;/p&gt;

    &lt;/div&gt;

&lt;!-- bottom 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;
&lt;!-- bottom 인클루드 끝 --&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="thankcustomerjsp">thankCustomer.jsp</h3>
<pre><code>&lt;%@page import=&quot;java.net.URLDecoder&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    Cookie[] cookies = request.getCookies();

    String shipping_cartId = &quot;&quot;;
    String shipping_shippingDate = &quot;&quot;;

    if(cookies != null){
        for(int i=0;i&lt;cookies.length;i++){
            Cookie thisCookie = cookies[i];
            String n = thisCookie.getName();
            if(n.equals(&quot;Shipping_cartId&quot;)){
                shipping_cartId = URLDecoder.decode(thisCookie.getValue(),&quot;UTF-8&quot;);
            }
            if(n.equals(&quot;Shipping_shippingDate&quot;)){
                shipping_shippingDate = URLDecoder.decode(thisCookie.getValue(),&quot;UTF-8&quot;);
            }
        }
    }
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot; /&gt;
&lt;title&gt;주문 완료&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- top 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;
&lt;!-- top 인클루드 끝 --&gt;

    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h1 class=&quot;display-3&quot;&gt;주문 완료&lt;/h1&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;h2 class=&quot;alert alert-danger&quot;&gt;주문해주셔서 감사합니다.&lt;/h2&gt;
        &lt;p&gt;주문은 &lt;%=shipping_shippingDate %&gt; 에 배송될 예정입니다.&lt;/p&gt;
        &lt;p&gt;주문번호 : &lt;%=shipping_cartId %&gt; &lt;/p&gt;
    &lt;/div&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;p&gt;
            &lt;a href=&quot;products.jsp&quot; class=&quot;btn btn-secondary&quot;&gt;&amp;raquo;상품 목록&lt;/a&gt;
        &lt;/p&gt;

    &lt;/div&gt;

&lt;!-- bottom 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;
&lt;!-- bottom 인클루드 끝 --&gt;
&lt;/body&gt;
&lt;/html&gt;

&lt;%
    //세션으로 저장된 장바구니 정보를 모두 삭제함
    session.invalidate();
    //쿠키에 저장된 배송 정보를 모두 삭제함
    for(int i=0;i&lt;cookies.length;i++){
        Cookie thisCookie = cookies[i];
          // 쿠키명을 가져오기
          String n = thisCookie.getName();
          // 쿠키명에 매핑되어 있는 값을 가져오기
          if(n.equals(&quot;Shipping_cartId&quot;)){
              thisCookie.setMaxAge(0);
          }
          if(n.equals(&quot;Shipping_name&quot;)){
              thisCookie.setMaxAge(0);
          }
          if(n.equals(&quot;Shipping_shippingDate&quot;)){
              thisCookie.setMaxAge(0);
          }
          if(n.equals(&quot;Shipping_country&quot;)){
              thisCookie.setMaxAge(0);
          }
          if(n.equals(&quot;Shipping_zipCode&quot;)){
              thisCookie.setMaxAge(0);
          }
          if(n.equals(&quot;Shipping_addressName&quot;)){
              thisCookie.setMaxAge(0);
          }

          response.addCookie(thisCookie);

    }
%&gt;


</code></pre><h3 id="cartjsp">cart.jsp</h3>
<pre><code>&lt;%@page import=&quot;dto.Product&quot;%&gt;
&lt;%@page import=&quot;java.util.ArrayList&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%
    //세션의 고유 아이디를 가져옴
    String cartId = session.getId();
%&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot; /&gt;
&lt;title&gt;장바구니&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- top.jsp 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;
&lt;!-- top.jsp 인클루드 끝 --&gt;
    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h1 class=&quot;display-3&quot;&gt;장바구니&lt;/h1&gt;
        &lt;/div&gt;
    &lt;/div&gt;    
        &lt;div class=&quot;container&quot;&gt;
            &lt;div class=&quot;row&quot;&gt;
            &lt;table width=&quot;100%&quot;&gt;
                &lt;tr&gt;
                    &lt;td align=&quot;left&quot;&gt;
                        &lt;a href=&quot;deleteCart.jsp?cartId=&lt;%=cartId%&gt;&quot; class=&quot;btn btn-danger&quot;&gt;삭제하기&lt;/a&gt;
                    &lt;/td&gt;
                    &lt;td align=&quot;right&quot;&gt;
                        &lt;a href=&quot;shippingInfo.jsp?cartId=&lt;%=cartId %&gt;&quot; class=&quot;btn btn-success&quot;&gt;주문하기&lt;/a&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            &lt;/table&gt;
            &lt;/div&gt;


        &lt;!-- 장바구니 목록 구현 시작 --&gt;
        &lt;div style=&quot;padding-top:50px;&quot;&gt;
            &lt;table class=&quot;table table-hover&quot;&gt;
                &lt;tr&gt;
                    &lt;th&gt;상품&lt;/th&gt;
                    &lt;th&gt;가격&lt;/th&gt;
                    &lt;th&gt;수량&lt;/th&gt;
                    &lt;th&gt;소계&lt;/th&gt;
                    &lt;th&gt;비고&lt;/th&gt;
                &lt;/tr&gt;
&lt;%
ArrayList&lt;Product&gt; cartList = (ArrayList&lt;Product&gt;)session.getAttribute(&quot;cartlist&quot;);
//세션으로 가져오면 object이므로 형변환 필수
//out.print(&quot;cartList 크기 : &quot; + cartList.size());
//cartList : 장바구니
if(cartList == null){
    cartList = new ArrayList&lt;Product&gt;();
}

int sum = 0; //total을 누적
for(int i=0; i&lt;cartList.size();i++){
    Product product = cartList.get(i);
    //금액 = 가격 * 수량
    int total = product.getUniPrice() * product.getQuantity();
    //total을 누적
    sum = sum + total;
%&gt;
                &lt;tr&gt;
                    &lt;td&gt;&lt;%=product.getProductId() %&gt;-&lt;%=product.getPname() %&gt;&lt;/td&gt;
                    &lt;td&gt;&lt;%=product.getUniPrice() %&gt;&lt;/td&gt;
                    &lt;td&gt;&lt;%=product.getQuantity() %&gt;&lt;/td&gt;
                    &lt;td&gt;&lt;%=total %&gt;&lt;/td&gt;
                    &lt;td&gt;삭제&lt;/td&gt;
                &lt;/tr&gt;    
&lt;%
}
%&gt;
                &lt;tr&gt;
                    &lt;td&gt;&lt;/td&gt;
                    &lt;td&gt;&lt;/td&gt;
                    &lt;td&gt;총액&lt;/td&gt;
                    &lt;td&gt;&lt;%=sum %&gt;&lt;/td&gt;
                    &lt;td&gt;&lt;/td&gt;
                &lt;/tr&gt;

            &lt;/table&gt;
            &lt;a href=&quot;products.jsp&quot; class=&quot;btn btn-secondary&quot;&gt;&amp;raquo; 쇼핑 계속하기&lt;/a&gt;
        &lt;/div&gt;
        &lt;/div&gt;

&lt;!-- bottom.jsp 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;        
&lt;!-- bottom.jsp 인클루드 끝 --&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[JSP 15강 - 쿠키]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-15%EA%B0%95-%EC%BF%A0%ED%82%A4-%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-15%EA%B0%95-%EC%BF%A0%ED%82%A4-%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sun, 23 Jan 2022 11:53:29 GMT</pubDate>
            <description><![CDATA[<h4 id="쿠키cookie">쿠키(Cookie)</h4>
<ul>
<li><p>쿠키(cookie)는 웹 브라우저가 보관하고 있는 데이터로서 웹 서버에 요청을 보낼 때 함께 전송</p>
</li>
<li><p>동작 방식
<img src="https://images.velog.io/images/hazel_jo/post/6d87c639-6179-46eb-b004-1b37fa1f8725/image.png" alt=""></p>
</li>
<li><p>웹 브라우저에 쿠키가 저장되면, 웹 브라우저는 쿠키가 삭제되기 전까지 웹 서버에 쿠키를 전송함</p>
</li>
<li><p>웹 어플리케이션을 사용하는 동안 지속적으로 유지해야 하는 정보는 쿠키를 사용해서 저장하면 됨</p>
</li>
</ul>
<h4 id="쿠키의-구성">쿠키의 구성</h4>
<p><strong>구성요소</strong></p>
<ul>
<li>이름 : 각각의 쿠키를 구별하는 데 사용되는 이름</li>
<li>값 : 쿠키의 이름과 관련된 값</li>
<li>유효시간 : 쿠키의 유지 시간</li>
<li>도메인 : 쿠키를 전송할 도메인</li>
<li>경로 : 쿠키를 전송할 요청 경로</li>
</ul>
<p><strong>쿠키 이름의 제약</strong></p>
<ul>
<li><p>쿠키의 이름은 아스키 코드의 알파벳과 숫자만을 포함할 수 있다.</p>
</li>
<li><p>콤마(,), 세미콜론(;), 공백(&#39; &#39;) 등의 문자는 포함할 수 없다.</p>
</li>
<li><p>$로 시작할 수 없다.</p>
</li>
<li><p>보통 쿠키 이름을 작성할 때에는 알파벳과 숫자만 이용함</p>
</li>
<li><p>쿠키 값을 생성할 때에는 알맞은 방식으로 인코딩 함</p>
</li>
<li><p>별도 유효시간을 지정하지 않으면 웹 브라우저를 종료할 때 쿠키를 함께 삭제함. 지정한 도메인이나 경로로만 쿠키를 전송하도록 제한할 수도 있음</p>
</li>
</ul>
<h4 id="jsp에서-쿠키-생성읽기">JSP에서 쿠키 생성/읽기</h4>
<ul>
<li><p>Cookie 클래스를 이용해서 쿠키 생성</p>
<pre><code>&lt;%
  Cookie cookie = new Cookie(&quot;cookieName&quot;, &quot;cookieValue&quot;);
  response.addCookie(cookie);
%&gt;</code></pre></li>
<li><p>클라이언트가 보낸 쿠키 읽기
Cookie[] cookies = request.getCookies();</p>
</li>
<li><p>읽기 관련 주요 메서드</p>
<blockquote>
<p>메서드 &nbsp;&nbsp;&nbsp; 리턴타입 &nbsp;&nbsp;&nbsp;설명 getName()&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;&nbsp;&nbsp;쿠키의 이름을 구한다.
getValue()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;&nbsp;&nbsp;쿠키의 값을 구한다.</p>
</blockquote>
</li>
</ul>
<h4 id="쿠키-값의-인코딩디코딩-처리">쿠키 값의 인코딩/디코딩 처리</h4>
<ul>
<li><p>쿠키는 값으로 한글과 같은 문자를 가질 수 없음</p>
</li>
<li><blockquote>
<p>쿠키의 값을 인코딩해서 지정할 필요 있음</p>
</blockquote>
</li>
<li><p>쿠키 값의 처리</p>
<blockquote>
<p>-&gt; 값 설정시 : URLEncoder.encode(&quot;값&quot;, &quot;euc-kr&quot;)
예, new Cookie(&quot;name&quot;, URLEncoder.encode(&quot;값&quot;, &quot;euc-kr&quot;));
&nbsp;</p>
</blockquote>
</li>
<li><blockquote>
<p>값 조회시 : URLDecoder.decode(&quot;값&quot;, &quot;euc-kr&quot;)
Cookie cookie = …;
String&nbsp;&nbsp;value&nbsp;&nbsp;=&nbsp;&nbsp;URLDecoder.decode(cookie.getValue(), &quot;euc-kr&quot;);</p>
</blockquote>
</li>
</ul>
<h4 id="쿠키-값-변경">쿠키 값 변경</h4>
<ul>
<li>기존에 존재하는지 확인 후, 쿠키값 새로 설정<pre><code>Cookie[] cookies = request.getCookies();
if(cookies!=null &amp;&amp; cookies.length &gt; 0){
  for(int i = 0; i&lt;cookies.length; i++){
      if(cookies[i].getName().equals(&quot;name&quot;)){
          Cookie cookie = new Cookie(name, value);
                  response.addCookie(cookie);
              }
      }
}</code></pre></li>
</ul>
<h4 id="쿠키-삭제">쿠키 삭제</h4>
<ul>
<li>Cookie 클레스의 setMaxAge() 메서드를 호출할 때 인자값 0을 주면 됨</li>
<li><blockquote>
<p>Cookie.setMaxAge(0);</p>
</blockquote>
</li>
<li>Cookie 클래스는 쿠키를 삭제하는 기능을 별도로 제공하지 않음</li>
<li>유효시간을 0으로 지정해준 후 응답 헤더에 추가하면, 웹 브라우저가 관련 쿠키를 삭제하게 됨</li>
</ul>
<p><strong>[쿠키] 정리</strong></p>
<ol>
<li>쿠키란?</li>
</ol>
<ul>
<li>세션과 마찬가지로 클라이언트(크롬)와 웹 서버(톰켓)간의 상태를 지속적으로 유지하는 방법</li>
<li>상태 정보(name, value)를 클라이언트(크롬의 쿠키저장소)에 저장</li>
<li>쿠키 생성은 서버에서 함. 그 이후에 웹 서버로 전송되는 요청에는 쿠키 정보가 포함됨
예) 아이디 저장하기</li>
<li>처음 방문한 사용자가 로그인 인증 -&gt; 아이디와 비밀번호를 기록한 쿠키가 생성 -&gt; 그 이후에 사용자가 그 웹사이트에 접속하면 별도의 절차를 거치지 않고 쉽게 접속 가능</li>
</ul>
<ol start="2">
<li><p>쿠키 생성은?</p>
<blockquote>
<p>Cookie cookie = new Cookie(&quot;memberid&quot;, &quot;admin&quot;);
response.addCookie(cookie);</p>
</blockquote>
</li>
<li><p>쿠키 삭제는?</p>
<blockquote>
<p>cookie.setMaxAge(0);
response.addCookie(cookie);</p>
</blockquote>
</li>
<li><p>쿠키 생성?</p>
<blockquote>
<p>Cookie cookie = new Cookie(String name, String value);</p>
</blockquote>
</li>
<li><p>쿠키 정보 받기</p>
</li>
</ol>
<ul>
<li>request.getCookie() 메소드를 사용</li>
<li>쿠키 객체를 얻어온 후 getName()메소드를 통해 쿠키 이름을 가져옴</li>
<li>getValue() 메소드를 통해 해당 이름의 쿠키의 값을 얻음</li>
</ul>
<ol start="6">
<li>쿠키 삭제</li>
</ol>
<ul>
<li>쿠키를 더 유지할 필요가 없다면 유효기간을 만료하기</li>
<li>setMaxAge(0)</li>
</ul>
<h3 id="cookie01jsp">cookie01.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Cookie&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form method=&quot;post&quot; action=&quot;cookie01_process.jsp&quot;&gt;
        &lt;p&gt;아 이 디 : &lt;input type=&quot;text&quot; name=&quot;id&quot; /&gt;&lt;/p&gt;
        &lt;p&gt;비밀번호 : &lt;input type=&quot;text&quot; name=&quot;passwd&quot; /&gt;&lt;/p&gt;
        &lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;전송&quot; /&gt;&lt;/p&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><h3 id="cookie01_processjsp">cookie01_process.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Cookie&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
    //?id=admin&amp;passwd=1234
    String user_id = request.getParameter(&quot;id&quot;);
    String user_pw = request.getParameter(&quot;passwd&quot;);

    if(user_id.equals(&quot;admin&quot;)&amp;&amp;user_pw.equals(&quot;1234&quot;)){
        //Cookie 객체 생성
        Cookie cookie_id = new Cookie(&quot;userID&quot;, user_id);
        Cookie cookie_pw = new Cookie(&quot;userPW&quot;, user_pw);
        response.addCookie(cookie_id);
        response.addCookie(cookie_pw);

        out.print(&quot;쿠키 생성 성공!&quot;);
        out.print(user_id + &quot;님 환영합니다&quot;);
    }else{
        out.print(&quot;쿠키 생성 실패&quot;);
    }
%&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="cookie02jsp">cookie02.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    //쿠키 정보를 얻어오기
    Cookie[] cookies = request.getCookies();

    for(int i=0; i&lt;cookies.length; i++){
        out.print(&quot;설정된 쿠키의 속성 명 [&quot; + i + &quot;] : &quot; + cookies[i].getName() + &quot;&lt;br&gt;&quot;);
        out.print(&quot;설정된 쿠키의 속성 값 [&quot; + i + &quot;] : &quot; + cookies[i].getValue() + &quot;&lt;br&gt;&quot;);
        out.print(&quot;===============================&lt;br&gt;&quot;);
    }

%&gt;</code></pre><h3 id="detelejsp">detele.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Cookie&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
    Cookie[] cookies = request.getCookies();

    for(int i=0; i&lt;cookies.length; i++){
        //쿠키 삭제 : 유효기간을 0으로 설정
        cookies[i].setMaxAge(0);
        response.addCookie(cookies[i]);
    }
    response.sendRedirect(&quot;cookie02.jsp&quot;);

%&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[JSP 14강 - 세션, 에러페이지 처리]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-14%EA%B0%95-%EC%84%B8%EC%85%98-%EC%97%90%EB%9F%AC%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-14%EA%B0%95-%EC%84%B8%EC%85%98-%EC%97%90%EB%9F%AC%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 23 Jan 2022 08:03:28 GMT</pubDate>
            <description><![CDATA[<p>리다이렉트와 포워드의 차이
리다이렉트 : 단순히 페이지를 이동시킴
포워드 : list, map, vo 등을 담아서 이동</p>
<p>모달 사용 시 
<a href="https://adminlte.io/themes/v3/pages/UI/modals.html">https://adminlte.io/themes/v3/pages/UI/modals.html</a> 참고</p>
<h4 id="세션session이란">세션(session)이란</h4>
<ul>
<li><p>서버 세션을 사용하면 클라이언트(크롬)의 상태를 저장할 수 있음</p>
</li>
<li><p>쿠키와의 차이점은 세션은 웹 브라우저가 아닌 <strong>서버에 값을 저장</strong>한다는 점</p>
</li>
<li><p>웹 컨테이너(톰켓)에서 클라이언트(크롬)의 정보를 보관 및 상태를 유지할 때 사용</p>
</li>
<li><p>로그인한 사용자 정보를 유지하기 위한 목적</p>
</li>
<li><p>클라이언트마다 세션이 생성(크롬과 edge의 세션이 다름)
(웹 브라우저마다 세션이 따로 존재하기 때문에, 세션은 웹 브라우저와 관련된 정보를 저장하기에 알맞은 장소임)</p>
</li>
<li><p>웹 브라우저에 정보 보관 : 쿠키
(클라이언트 측의 데이터 보관소)</p>
</li>
<li><p>웹 컨테이너에 정보 보관 : 세션
(서버측의 데이터 보관소)</p>
</li>
<li><p>쿠키와 마찬가지로 세션도 생성을 해야만 정보를 저장할 수 있음</p>
</li>
<li><p>일단 세션을 생성하면 session 기본 객체를 통해서 세션을 사용할 수 있음</p>
</li>
</ul>
<h4 id="세션과-session-기본-객체">세션과 session 기본 객체</h4>
<ul>
<li><p>page 디렉티브의 session 속성 값을 true로 지정</p>
</li>
<li><blockquote>
<p>세션이 존재하지 않을 경우 세션이 생성되고, 세션이 존재할 경우 이미 생성된 세션을 접근</p>
</blockquote>
</li>
<li><p>session 기본 객체를 이용해서 세션에 접근</p>
</li>
<li><blockquote>
<p>session 기본값은 true(생략 가능)이므로 false로 하지 않는 이상 항상 세션 사용</p>
</blockquote>
</li>
<li><p>속성 이용해서 클라이언트 관련 정보 저장</p>
</li>
<li><p>세션이 생성되면 session 기본 객체를 통해서 세션을 사용할 수 있음</p>
</li>
</ul>
<p><img src="https://images.velog.io/images/hazel_jo/post/e642638d-778b-44f2-a3f8-2ea3b0345922/image.png" alt=""></p>
<h4 id="세션이-생성되는-원리">세션이 생성되는 원리</h4>
<p><img src="https://images.velog.io/images/hazel_jo/post/36a43f25-49b5-43f0-888a-4c4678a53a9a/image.png" alt=""></p>
<h4 id="session-기본-객체가-제공하는-세션-정보-관련-메서드">session 기본 객체가 제공하는 세션 정보 관련 메서드</h4>
<blockquote>
<p>메서드&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;리턴타입&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;설명
getId()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;세션의 고유 ID를 구함(세션ID)
getCreationTime()&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;세션이 생성된 시간을 구함. 시간은 1970년 1월 1일 이후 흘러간 시간을 의미. 단위는 1/1000초
 getLastAccessedTime()&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;&nbsp;&nbsp;&nbsp;웹 브라우저가 가장 마지막에 세션에 접근한 시간을 구함. 시간은 1970년 1월 1일 이후 흘러간 시간을 의미. 단위는 1/1000초</p>
</blockquote>
<ul>
<li>웹 브라우저마다 별도의 세션을 갖음. 이때 각 세션을 구분하기 위해 세션마다 고유 ID를 할당하는데 그 아이디를 세션ID라고 함</li>
<li>웹 서버는 웹 브라우저에 세션ID를 전송함</li>
<li>웹 브라우저는 웹 서버에 연결할 때마다 매번 세션ID를 보내서 웹 서버가 어떤 세션을 사용할 지 판단할 수 있게 함</li>
<li>웹 서버는 세션ID를 이용해서 웹 브라우저를 위한 세션을 찾으므로 웹 서버와 웹 브라우저는 세션ID를 공유할 수 있는 방법이 필요함. 세션ID를 공유하기 위해 사용하는 것이 쿠키임
<strong>JSESSIONID 쿠키가 세션ID를 공유할 때 사용하는 쿠키</strong></li>
</ul>
<h4 id="세션-종료">세션 종료</h4>
<ul>
<li>세션을 유지할 필요가 없으면 session.invalidate()을 이용해서 세션 종료</li>
<li><blockquote>
<p>세션이 종료되면 기존에 생성된 세션이 삭제</p>
</blockquote>
</li>
<li><blockquote>
<p>현재 사용중인 session 기본 객체를 삭제하고, session 기본 객체에 저장했던 속성 목록도 함께 삭제</p>
</blockquote>
</li>
<li><blockquote>
<p>이후 접근 시 새로운 세션 생성됨</p>
</blockquote>
</li>
</ul>
<h3 id="sessioninfojsp">sessioninfo.jsp</h3>
<pre><code>&lt;%@page import=&quot;java.util.Date&quot;%&gt;
&lt;%@page import=&quot;java.text.SimpleDateFormat&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;세션 정보&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
    //long 타입의 시간 값을 저장하기 위해 사용
    Date time = new Date();    
    SimpleDateFormat formatter = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
%&gt;

세션 ID : &lt;%=session.getId() %&gt; &lt;br&gt;
&lt;%
    //세션의 생성 시간을 Date 객체인 time에 저장
    time.setTime(session.getCreationTime());
%&gt;
세션 생성시간 : &lt;%=formatter.format(time)%&gt; &lt;br&gt;
&lt;%
    //세션의 마지막 접근 시간(long) -&gt; Date 타입으로 세팅
    time.setTime(session.getLastAccessedTime());
%&gt;
최근 접근시간 : &lt;%=formatter.format(time) %&gt; &lt;br&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="setmemberinfojsp">setMemberinfo.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    session.setAttribute(&quot;MEMBERID&quot;, &quot;ddit&quot;);
    session.setAttribute(&quot;NAME&quot;, &quot;개똥이&quot;);
%&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;세션에 정보 저장&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
세션에 정보를 저장했습니다.&lt;br&gt;
&lt;a href=&quot;getMemberInfo.jsp&quot;&gt;저장된 세션의 정보 보기&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="getmemberinfojsp">getMemberinfo.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    String memberId = (String)session.getAttribute(&quot;MEMBERID&quot;); //ddit 가져올거야 , 결과는 object 형태이므로 형변환한다
    String name = (String)session.getAttribute(&quot;NAME&quot;); //개똥이 가져올거야
%&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;저장된 세션의 정보 확인&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
회원 id : &lt;%=memberId %&gt;&lt;br&gt;
회원 이름 : &lt;%=name %&gt;&lt;br&gt;
&lt;a href=&quot;setMemberInfo.jsp&quot;&gt;되돌아가기&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="sessionjsp">session.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Session&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form method=&quot;post&quot; action=&quot;session_process.jsp&quot;&gt;
        &lt;p&gt;아 이 디 : &lt;input type=&quot;text&quot; name=&quot;id&quot;&gt;&lt;/p&gt;
        &lt;p&gt;비밀번호 : &lt;input type=&quot;password&quot; name=&quot;passwd&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;전송&quot;&gt;&lt;/p&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="session_processjsp">session_process.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    String memberId = (String)session.getAttribute(&quot;MEMBERID&quot;); //ddit 가져올거야 , 결과는 object 형태이므로 형변환한다
    String name = (String)session.getAttribute(&quot;NAME&quot;); //개똥이 가져올거야
%&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;저장된 세션의 정보 확인&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
회원 id : &lt;%=memberId %&gt;&lt;br&gt;
회원 이름 : &lt;%=name %&gt;&lt;br&gt;
&lt;a href=&quot;setMemberInfo.jsp&quot;&gt;되돌아가기&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="welcomejsp">welcome.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    String userID = (String)session.getAttribute(&quot;userID&quot;);
    String userPW = (String)session.getAttribute(&quot;userPW&quot;);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;&lt;%=userID %&gt;님 반갑습니다.&lt;/h1&gt;
    &lt;a href=&quot;session_out.jsp&quot;&gt;로그아웃&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="session_outjsp">session_out.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    session.invalidate();

    response.sendRedirect(&quot;session.jsp&quot;);
%&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="loginjsp">login.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Login&lt;/title&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h1 class=&quot;display-3&quot;&gt;로그인&lt;/h1&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;container&quot; align=&quot;center&quot;&gt;
        &lt;div class=&quot;col-md-4 col-md-offset-4&quot;&gt;
            &lt;h3 class=&quot;form-singin-heading&quot;&gt;Please Sign in&lt;/h3&gt;
            &lt;c:if test=&quot;${param.error eq &#39;1&#39;}&quot;&gt;
                &lt;!--parma 목록 중에 error라는 목록이 있냐? 그 값이 1이 맞냐? --&gt;
                &lt;div class=&quot;alert alert-danger&quot;&gt;
                    아이디와 비밀번호를 확인해주세요.
                &lt;/div&gt;
            &lt;/c:if&gt;
        &lt;/div&gt;
        &lt;!-- j_security_check, j_username, j_password : 폼 기반 인증 처리 --&gt;
        &lt;form class=&quot;form-signin&quot; method=&quot;post&quot; 
            action=&quot;j_security_check&quot;&gt;
            &lt;div class=&quot;form-group&quot;&gt;
                &lt;label for=&quot;inputUserName&quot; class=&quot;sr-only&quot;&gt;User Name&lt;/label&gt;
                &lt;input type=&quot;text&quot; class=&quot;form-control&quot; placeholder=&quot;ID&quot;
                    name=&quot;j_username&quot; required autofocus /&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group&quot;&gt;
                &lt;label for=&quot;inputPassword&quot; class=&quot;sr-only&quot;&gt;Password&lt;/label&gt;
                &lt;input type=&quot;password&quot; class=&quot;form-control&quot; placeholder=&quot;Password&quot;
                    name=&quot;j_password&quot; required autofocus /&gt;
            &lt;/div&gt;
            &lt;button class=&quot;btn btn btn-lg btn-success btn-block&quot;
                type=&quot;submit&quot;&gt;로그인&lt;/button&gt;
        &lt;/form&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="login_failedjsp">login_failed.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%
//로그인 인증(login.jsp)에 실패 시 강제 이동
response.sendRedirect(&quot;login.jsp?error=1&quot;);
%&gt;</code></pre><h3 id="logoutjsp">logout.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%
    //로그아웃
    //security를 통해 로그인 인증을 할 때 웹 브라우저에 저장된 모든 사용자를 삭제함
    session.invalidate();
    response.sendRedirect(&quot;addProduct.jsp&quot;);
%&gt;</code></pre><h4 id="예외처리">예외처리</h4>
<ol>
<li><p>프로그램이 처리되는 동안 특정한 문제가 발생 시 처리를 중단하고 다른 처리를 하는 것(오류 처리)</p>
</li>
<li><p>Page 디렉티브를 이용</p>
<ul>
<li>errorPage 속성으로 오류 페이지 호출
&lt;%@ page errorPage=&quot;오류 페이지 URL&quot; %&gt;</li>
<li>isErrorPage 속성으로 오류 페이지 만듦(나는 오류 페이지야) default는 false(생략 시)
&lt;%@ page isErrorPage=&quot;true&quot; %&gt;</li>
</ul>
</li>
<li><p>web.xml
web.xml 파일을 통해 오류 상태(404,500,널오류)와 오류 페이지를 보여줌</p>
<pre><code>&lt;error-page&gt;
 &lt;error-code&gt;오류 코드&lt;/error-code&gt;
 &lt;location&gt;오류 페이지 URI&lt;/location&gt;
&lt;/error-page&gt;
</code></pre></li>
</ol>
<error-page>
    <exception-type>예외 유형</exception>
    <location>오류 페이지의 URI</location>
</error-page>

<pre><code>
4. try-catch-finally
try{
    실행문
}catch(처리할 예외 유형 설정){
    예외 처리문
}finally{
    예외와 상관없이 무조건 실행(생략 가능)
}


### exceptionNoPage.jsp</code></pre><p>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;<a href="http://java.sun.com/jsp/jstl/core&quot;">http://java.sun.com/jsp/jstl/core&quot;</a> %&gt; 
<!DOCTYPE html></p>
<html>
<head>
<link rel="stylesheet" href="/css/bootstrap.min.css" />
<title>페이지 오류</title>
</head>
<body>
<!-- top 인클루드 시작 -->
    <jsp:include page="/ch03/top.jsp" />
<!-- top 인클루드 끝 -->

<pre><code>&lt;div class=&quot;jumbotron&quot;&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;h2 class=&quot;alert alert-danger&quot;&gt;요청하신 페이지를 찾을 수 없습니다.&lt;/h2&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;!-- 
중요!!!!!!!!!!!!!!!!
request.getRequestURL() : 오류 발생 시 해당 오류 페이지 경로를 출력 
                http://localhost:8090/ch04/addProduct.jsp
request.getQueryString() : 요청 파라미터(productId=P1234)
            결과 : http://localhost:8090/ch04/addProduct.jsp?productId=P1234
--&gt;
&lt;div class=&quot;container&quot;&gt;
    &lt;p&gt;&lt;%=request.getRequestURL()%&gt;?&lt;%=request.getQueryString()%&gt;&lt;/p&gt; 
    &lt;p&gt;
    &lt;a href=&quot;/ch04/products.jsp&quot; class=&quot;btn btn-secondary&quot;&gt;
    상품 목록 &amp;raquo;
    &lt;/a&gt;
    &lt;/p&gt; 


&lt;/div&gt;</code></pre><!-- bottom 인클루드 시작 -->
<pre><code>&lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;</code></pre><!-- bottom 인클루드 끝 -->    
</body>
</html>
```

<h3 id="exceptionnoproductidjsp">exceptionNoProductId.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot; /&gt;
&lt;title&gt;상품 아이디 오류&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- top 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;
&lt;!-- top 인클루드 끝 --&gt;

    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h2 class=&quot;alert alert-danger&quot;&gt;해당 상품이 존재하지 않습니다.&lt;/h2&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;!-- 
    중요!!!!!!!!!!!!!!!!
    request.getRequestURL() : 오류 발생 시 해당 오류 페이지 경로를 출력 
                    http://localhost:8090/ch04/addProduct.jsp
    request.getQueryString() : 요청 파라미터(productId=P1234)
                결과 : http://localhost:8090/ch04/addProduct.jsp?productId=P1234
    --&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;p&gt;&lt;%=request.getRequestURL()%&gt;?&lt;%=request.getQueryString()%&gt;&lt;/p&gt; 
        &lt;p&gt;
        &lt;a href=&quot;/ch04/products.jsp&quot; class=&quot;btn btn-secondary&quot;&gt;
        상품 목록 &amp;raquo;
        &lt;/a&gt;
        &lt;/p&gt; 


    &lt;/div&gt;

&lt;!-- bottom 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;
&lt;!-- bottom 인클루드 끝 --&gt;    
&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[JSP 13강 - 다국어, bundle, 시큐리티]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-13%EA%B0%95-%EB%8B%A4%EA%B5%AD%EC%96%B4-bundle-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-13%EA%B0%95-%EB%8B%A4%EA%B5%AD%EC%96%B4-bundle-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0</guid>
            <pubDate>Sun, 23 Jan 2022 06:56:06 GMT</pubDate>
            <description><![CDATA[<h4 id="다국어-지원">다국어 지원</h4>
<p><strong>1. 페이지에 태그 라이브러리 추가</strong></p>
<pre><code>&lt;%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; %&gt; </code></pre><p><strong>2. body 태그에 아래와 같이 추가</strong></p>
<p>1) fmt:setLocale 설정</p>
<pre><code>&lt;!-- ko는 한글, en은 영어 --&gt;
    &lt;fmt:setLocale value=&quot;${param.language}&quot;/&gt;
    &lt;fmt:bundle basename=&quot;resourceBundle.message&quot;&gt;
&lt;!-- 패키지명.파일명 으로 접근한다(ko는 resourceBundle.properties)--&gt;
</code></pre><p>2)${param.language} 속성을 사용해서 Korean 클릭 시 languae의 값으로 ko를, English 클릭시 en을 파라미터로 보낸다. </p>
<pre><code>&lt;div class=&quot;text-right&quot;&gt;
    &lt;a href=&quot;?language=ko&quot;&gt;Korean&lt;/a&gt;|&lt;a href=&quot;?language=en&quot;&gt;English&lt;/a&gt;
    &lt;a href=&quot;logout.jsp&quot; class=&quot;btn btn-sm btn-success pull-right&quot;&gt;logout&lt;/a&gt;
&lt;/div&gt;</code></pre><p><strong>3. resourceBundle 패키지 및 properties 파일 생성</strong>
bundle : 꾸러미</p>
<h3 id="messageproperties">message.properties</h3>
<pre><code>&lt;!--순서는 key = value --&gt;
title=\uC0C1\uD488 \uB4F1\uB85D
productId=\uC0C1\uD488 \uCF54\uB4DC
pname=\uC0C1\uD488\uBA85
unitPrice=\uAC00\uACA9
description=\uC0C1\uC138 \uC124\uBA85
manufacturer=\uC81C\uC870\uC0AC
category=\uBD84\uB958
unitsInStock=\uC7AC\uACE0\uC218
condition=\uC0C1\uD0DC
productImage=\uC774\uBBF8\uC9C0
condition_New=\uC2E0\uADDC \uC81C\uD488
condition_Old=\uC911\uACE0 \uC81C\uD488
condition_Refurbished=\uC7AC\uC0DD \uC81C\uD488
button=\uB4F1\uB85D</code></pre><h3 id="message_enproperties">message_en.properties</h3>
<pre><code>&lt;!--순서는 key = value --&gt;
title=Product Addition
productId=Product ID
pname=Name
unitPrice=Unit Price
description=Description
manufacturer=Manufacturer
category=Category
unitsInStock=Units in Stock
condition=Condition
productImage=Imange
condition_New=New
condition_Old=Old
condition_Refurbished=Refurbished
button=Insert</code></pre><ol start="4">
<li>해당 값에 fmt:message 설정<pre><code>&lt;div class=&quot;form-group row&quot;&gt;
 &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;category&quot; /&gt;&lt;/label&gt;
 &lt;div class=&quot;col-sm-3&quot;&gt;
     &lt;input type=&quot;text&quot; name=&quot;category&quot; class=&quot;form-control&quot; /&gt;
 &lt;/div&gt;
&lt;/div&gt;</code></pre></li>
</ol>
<h3 id="addproductjsp">addProduct.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot;&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/ckeditor/ckeditor.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/js/validation.js&quot;&gt;&lt;/script&gt;
&lt;title&gt;상품 등록&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- ko는 한글, en은 영어 --&gt;
    &lt;fmt:setLocale value=&quot;${param.language}&quot;/&gt;
    &lt;fmt:bundle basename=&quot;resourceBundle.message&quot;&gt;
&lt;!-- 패키지명.파일명 으로 접근한다(ko는 resourceBundle.properties)--&gt;

&lt;!-- top 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;
&lt;!-- top 인클루드 끝 --&gt;

&lt;!-- content 시작 --&gt;
    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h1 class=&quot;display-3&quot;&gt;&lt;fmt:message key=&quot;title&quot; /&gt;&lt;/h1&gt;
            &lt;!-- key에는  message.properties에서 적어놓은 속성들을 적는다--&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;div class=&quot;text-right&quot;&gt;
            &lt;a href=&quot;?language=ko&quot;&gt;Korean&lt;/a&gt;|&lt;a href=&quot;?language=en&quot;&gt;English&lt;/a&gt;
            &lt;a href=&quot;logout.jsp&quot; class=&quot;btn btn-sm btn-success pull-right&quot;&gt;logout&lt;/a&gt;
        &lt;/div&gt;
    &lt;!-- addProduct_process.jsp : common-fileupload --&gt;
    &lt;!-- addProduct_process2.jsp : cos.jar --&gt;
        &lt;form name=&quot;newProduct&quot; action=&quot;addProduct_process2.jsp&quot; 
            method=&quot;post&quot; class=&quot;form-horizontal&quot; enctype=&quot;multipart/form-data&quot;&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;productId&quot; /&gt;&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;productId&quot; id=&quot;productId&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;pname&quot; /&gt;&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;pname&quot; id=&quot;pname&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;unitPrice&quot; /&gt;&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;uniPrice&quot; id=&quot;uniPrice&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;description&quot; /&gt;&lt;/label&gt;
                &lt;div class=&quot;col-sm-5&quot;&gt;
                    &lt;textarea name=&quot;description&quot; rows=&quot;2&quot; cols=&quot;50&quot; class=&quot;form-control&quot;&gt;&lt;/textarea&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;manufacturer&quot; /&gt;&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;manufacturer&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;category&quot; /&gt;&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;category&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;unitsInStock&quot; /&gt;&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;unitsInStock&quot; id=&quot;unitsInStock&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;condition&quot; /&gt;&lt;/label&gt;
                &lt;div class=&quot;col-sm-5&quot;&gt;
                    &lt;input type=&quot;radio&quot; name=&quot;condition&quot; id=&quot;condition1&quot; value=&quot;new&quot; /&gt;
                        &lt;label for=&quot;condition1&quot;&gt;&lt;fmt:message key=&quot;condition_New&quot; /&gt;&lt;/label&gt;
                    &lt;input type=&quot;radio&quot; name=&quot;condition&quot; id=&quot;condition2&quot; value=&quot;old&quot; /&gt;
                        &lt;label for=&quot;condition2&quot;&gt;&lt;fmt:message key=&quot;condition_Old&quot; /&gt;&lt;/label&gt;
                    &lt;input type=&quot;radio&quot; name=&quot;condition&quot; id=&quot;condition3&quot; value=&quot;Refurbished&quot; /&gt;
                        &lt;label for=&quot;condition3&quot;&gt;&lt;fmt:message key=&quot;condition_Refurbished&quot; /&gt;&lt;/label&gt;
                &lt;/div&gt;
            &lt;/div&gt;
&lt;!--             &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;이미지&lt;/label&gt;
                &lt;div class=&quot;col-sm-5&quot;&gt;
                    &lt;input type=&quot;file&quot; name=&quot;productImage&quot; class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt; --&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;&lt;fmt:message key=&quot;productImage&quot; /&gt;&lt;/label&gt;
                &lt;div class=&quot;col-sm-5&quot;&gt;
                    &lt;input type=&quot;file&quot; name=&quot;productImage2&quot; class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;div class=&quot;col-sm-offset-2 col-sm-10&quot;&gt;
                    &lt;input type=&quot;button&quot; class=&quot;btn btn-primary&quot; value=&quot;&lt;fmt:message key=&quot;button&quot; /&gt;&quot; onclick=&quot;CheckAddProduct()&quot;/&gt;
                &lt;/div&gt;

            &lt;/div&gt;
        &lt;/form&gt;    
    &lt;/div&gt;
&lt;!-- content 끝 --&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
CKEDITOR.replace(&quot;description&quot;);
&lt;/script&gt;

&lt;!-- bottom 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;
&lt;!-- bottom 인클루드 끝 --&gt;
&lt;/fmt:bundle&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="시큐리티">시큐리티</h4>
<ol>
<li>시큐리티란?</li>
</ol>
<ul>
<li>허가된 사용자만이 접근할 수 있도록 제한하는 보안 기능</li>
</ul>
<ol start="2">
<li>프로세스?</li>
</ol>
<ul>
<li>사용자가 웹 페이지에 접근</li>
<li>JSP 컨테이너(Tomcat)는 요청된 페이지에 보안 제약(Constraints)이 있는지 확인</li>
<li>사용자에게 인증(authentication)을 요청</li>
<li>권한 부여(authorization) : JSP 컨테이너는 사용자가 해당 페이지에 접근할 수 있는지 확인. 승인</li>
</ul>
<ol start="3">
<li>역할?</li>
</ol>
<ul>
<li>사용자가 권한이 없는 데이터에 접근하는 것을 막음</li>
<li>스니핑(sniffing) : 웹 공격자가 전송 데이터를 중간에 가로채는 것 방지</li>
</ul>
<ol start="4">
<li>그래서 시큐리티란?</li>
</ol>
<ul>
<li>허가된 사용자만이 특정 웹 페이지에 접근할 수 있도록 제한하는 보안 기능</li>
<li>사용자가 권한이 없는 데이터에 접근하는 것을 막아줌</li>
<li>웹 공격자가 전송 데이터를 중간에 가로채는 것을 방지(스니핑 방지)</li>
</ul>
<ol start="5">
<li>선언적 시큐리티</li>
</ol>
<ul>
<li>web.xml 파일(웹 어플리케이션(JSPBook)) 배포 설명자)에 보안 구성을 작성하여 수행</li>
<li>보안 역할(role), 보안 제약 사항(constraint), 인증 처리(login.jsp, login-failed.jsp) 설정</li>
</ul>
<p><strong>1) tomcat-users.xml 설정</strong>
34번째 줄&lt;role&gt;부터 주석 제거
&lt;role rolename&gt; 은 권한
&lt;user username&gt; 은 사용자</p>
<pre><code>  &lt;role rolename=&quot;tomcat&quot;/&gt;
  &lt;role rolename=&quot;role1&quot;/&gt;
  &lt;user username=&quot;tomcat&quot; password=&quot;tomcat1234&quot; roles=&quot;tomcat&quot;/&gt;
  &lt;user username=&quot;both&quot; password=&quot;both1234&quot; roles=&quot;tomcat,role1&quot;/&gt;
  &lt;user username=&quot;role1&quot; password=&quot;role1234&quot; roles=&quot;role1&quot;/&gt;</code></pre><p><strong>2) JSPBook web.xml 설정</strong>
마지막줄에 추가</p>
<pre><code>     &lt;!-- 시큐리티 admin 관리 시작 --&gt;
&lt;!--     &lt;security-role&gt;
        &lt;description&gt;&lt;/description&gt;
        &lt;role-name&gt;admin&lt;/role-name&gt;
    &lt;/security-role&gt; --&gt;
    &lt;!-- 시큐리티 제약 사항 설정 --&gt;
    &lt;security-constraint&gt;
        &lt;web-resource-collection&gt;
            &lt;web-resource-name&gt;JSPBook&lt;/web-resource-name&gt;
            &lt;!-- 접근을 제한할 요청 경로 --&gt;
            &lt;url-pattern&gt;/ch04/addProduct.jsp&lt;/url-pattern&gt;
            &lt;http-method&gt;GET&lt;/http-method&gt;
        &lt;/web-resource-collection&gt;
        &lt;auth-constraint&gt;
            &lt;description&gt;&lt;/description&gt;
            &lt;!-- 권한이 부여된 role 이름 --&gt;
            &lt;role-name&gt;admin&lt;/role-name&gt;
            &lt;!-- 위의 admin과 url-pattern을 admin권한을 가진 사용자만 이용할 수 있도록 매핑 --&gt;
        &lt;/auth-constraint&gt;
    &lt;/security-constraint&gt;
    &lt;security-constraint&gt;
        &lt;web-resource-collection&gt;
            &lt;web-resource-name&gt;JSPBook&lt;/web-resource-name&gt;
            &lt;!-- 접근을 제한할 요청 경로  --&gt;
            &lt;url-pattern&gt;/ch03/*&lt;/url-pattern&gt;
            &lt;http-method&gt;GET&lt;/http-method&gt;
        &lt;/web-resource-collection&gt;
        &lt;auth-constraint&gt;
            &lt;description&gt;&lt;/description&gt;
            &lt;!-- 권한이 부여된 role 이름 --&gt;
            &lt;role-name&gt;tomcat&lt;/role-name&gt;
        &lt;/auth-constraint&gt;
    &lt;/security-constraint&gt;
    &lt;!-- 시큐리티 인증 설정 --&gt;
    &lt;login-config&gt;
        &lt;!-- BASIC 인증 처리 기법으로 설정 --&gt;
        &lt;!-- &lt;auth-method&gt;BASIC&lt;/auth-method&gt; --&gt;
        &lt;!-- FORM 기반 인증 처리 기법으로 설정 --&gt;
        &lt;auth-method&gt;FORM&lt;/auth-method&gt;
        &lt;form-login-config&gt;
            &lt;!-- 인증 처리를 위한 로그인 페이지 설정 --&gt;
            &lt;form-login-page&gt;/ch04/login.jsp&lt;/form-login-page&gt;
            &lt;!-- 인증 실패 시 오류 페이지 설정 --&gt;
            &lt;form-error-page&gt;/ch04/login_failed.jsp&lt;/form-error-page&gt;
        &lt;/form-login-config&gt;
    &lt;/login-config&gt;
    &lt;!-- 시큐리티 역할 role1  관리 끝 --&gt;
    &lt;!-- 
        웹 어플리케이션이 시작할 때(톰켓을 시작할 때) DBCPInit 서블릿 클래스가
        자동으로 시작되고, init() 메서드가 호출됨
    --&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;oracleDriverLoader&lt;/servlet-name&gt;
        &lt;servlet-class&gt;jdbc.DBCPInit&lt;/servlet-class&gt;
        &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
        &lt;!-- 1 : 톰켓이 재 기동할 때 이 클래스를 가장 먼저 실행해라~ --&gt;
    &lt;/servlet&gt;</code></pre><h3 id="security01jsp">security01.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Security&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;인증 성공했습니다.&lt;/h2&gt;
    &lt;h4&gt;사용자명 : &lt;%=request.getRemoteUser() %&gt;&lt;/h4&gt;
    &lt;h4&gt;인증방법 : &lt;%=request.getAuthType() %&gt;&lt;/h4&gt;
    &lt;h4&gt;
        역할명(role) tomcat에 속한 사용자가 로그인한건가요?
        &lt;%=request.isUserInRole(&quot;tomcat&quot;) %&gt;
    &lt;/h4&gt;
    &lt;h4&gt;
        역할명(role) role1에 속한 사용자가 로그인한건가요?
        &lt;%=request.isUserInRole(&quot;role1&quot;) %&gt;
    &lt;/h4&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="loginjsp">login.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Security&lt;/title&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;!-- 폼 기반 인증 처리를 위해 j_security_check을 사용 --&gt;
    &lt;form name=&quot;loginForm&quot; action=&quot;j_security_check&quot; method=&quot;post&quot;&gt;
        &lt;p&gt;사용자명 : &lt;input type=&quot;text&quot; name=&quot;j_username&quot; /&gt;&lt;/p&gt;
        &lt;p&gt;비밀번호 : &lt;input type=&quot;password&quot; name=&quot;j_password&quot; /&gt;&lt;/p&gt;
        &lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;전송&quot; /&gt;&lt;/p&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="login_failedjsp">login_failed.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Security&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;인증 실패했습니다.&lt;/h2&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[JSP 12강 - 유효성 검사2]]></title>
            <link>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-12%EA%B0%95-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%82%AC2</link>
            <guid>https://velog.io/@hazel_jo/JSP-%EC%8A%A4%ED%94%84%EB%A7%81-12%EA%B0%95-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%82%AC2</guid>
            <pubDate>Mon, 17 Jan 2022 11:43:04 GMT</pubDate>
            <description><![CDATA[<p><strong>자주 사용되는 정규식 패턴 정리</strong></p>
<pre><code>숫자만 : ^[0-9]*$
영문자만 : ^[a-zA-Z]*$
한글만 : ^[가-힣]*$
이메일 : /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i
휴대전화 : ^01(?:0|1|[6-9])-(?:\d{3}|\d{4})-\d{4}$
전화번호 : ^\d{2,3}-\d{3,4}-\d{4}$ 
주민번호 : ^\d{6]\-[1-4]\d{6}
IP주소 : ([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})
URL : ^(file|gopher|news|nntp|telnet|https?|ftps?|sftp):\/\/([a-z0-9-]+\.)+[a-z0-9]{2,4}.*\$
날짜 : ^\d{2,4}\/\d{1,2}\/\d{1,2}$</code></pre><p><a href="http://regexlib.com%EC%97%90">http://regexlib.com에</a> 정규식 관련 정보 있으니 찾아보기</p>
<h4 id="6-데이터-형식-유효성-검사">6. 데이터 형식 유효성 검사</h4>
<ul>
<li>사용자가 폼 페이지의 입력 항목에 입력한 데이터 값이 특정 형태에 적합한지 검사하기 위해 정규 표현식을 사용하는 방법</li>
<li>특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어</li>
<li>정규 표현식(regExp)에서 사용하는 기호를 메타 문자라고 함</li>
<li>메타 문자는 정규 표현식 내부에서 특정한 의미를 가진 문자</li>
</ul>
<h3 id="validation05jsp">validation05.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Validation&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
    function checkMember() {
        //id 체크 정규식 : 숫자, 영문만 입력 가능 
        var regExpId = /^[0-9a-z]+$/;
        //비밀번호 : 숫자, 특수문자 각 1회 이상, 영문은 2개 이상 사용하여 8자리 이상 입력
        var regExpPasswd = /(?=.*\d{1,50})(?=.*[~`!@#$%\^&amp;*()-+=]{1,50})(?=.*[a-zA-Z]{2,50}).{8,50}$/;
        //이름 : 한글 검사
        var regExpName = /^[가-힣]*$/;
        //전화번호 형식인지 검사
        var regExpPhone = /^\d{2,3}-\d{3,4}-\d{4}$/;
        //이메일 형식인지 검사
        var regExpEmail = /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i;


        var form = document.Member;

        var id = form.id.value;
        var passwd = form.passwd.value;
        var name = form.name.value;
        var phone = form.phone1.value + &quot;-&quot; + form.phone2.value + &quot;-&quot; + form.phone3.value;
        var email = form.email.value;

        //정규표현식.test(변수)
        if(!regExpId.test(id)){
            alert(&quot;아이디는 숫자, 영문만 입력 가능합니다.&quot;);
            form.id.select();
            return;
        }
        if(!regExpPasswd.test(passwd)){
            alert(&quot;숫자, 특수문자 각 1회 이상, 영문은 2개 이상 사용하여 8자리 이상 입력 가능합니다.&quot;);
            form.passwd.select();
            return;
        }
        if(!regExpName.test(name)){
            alert(&quot;이름은 한글만 입력해주세요&quot;);
            form.name.select();
            return;
        }
        if(!regExpPhone.test(phone)){
            alert(&quot;연락처 형식에 맞춰주세요&quot;);
            form.phone2.select();
            return;
        }
        if(!regExpEmail.test(email)){
            alert(&quot;이메일 형식에 맞춰주세요&quot;);
            form.email.select();
            return;
        }
        //모두 테스트 통과 시 submit 처리
        form.submit();
    }
&lt;/script&gt;

&lt;h3&gt;회원 가입&lt;/h3&gt;
&lt;form name=&quot;Member&quot; method=&quot;post&quot; action=&quot;validation05_process.jsp&quot;&gt;
    &lt;p&gt;아이디 : &lt;input type=&quot;text&quot; name=&quot;id&quot;/&gt;&lt;/p&gt;
    &lt;p&gt;비밀번호 : &lt;input type=&quot;text&quot; name=&quot;passwd&quot;/&gt;&lt;/p&gt;
    &lt;p&gt;이름: &lt;input type=&quot;text&quot; name=&quot;name&quot;/&gt;&lt;/p&gt;
    &lt;p&gt;연락처 :
        &lt;select name=&quot;phone1&quot;&gt;
            &lt;option value=&quot;010&quot;&gt;010&lt;/option&gt;
            &lt;option value=&quot;011&quot;&gt;011&lt;/option&gt;
            &lt;option value=&quot;016&quot;&gt;016&lt;/option&gt;
            &lt;option value=&quot;017&quot;&gt;017&lt;/option&gt;
            &lt;option value=&quot;019&quot;&gt;019&lt;/option&gt;
        &lt;/select&gt;
        &lt;input type=&quot;text&quot; maxlength=&quot;4&quot; size=&quot;4&quot; name=&quot;phone2&quot; /&gt;
        &lt;input type=&quot;text&quot; maxlength=&quot;4&quot; size=&quot;4&quot; name=&quot;phone3&quot; /&gt;
    &lt;/p&gt;
    &lt;p&gt;이메일 : &lt;input type=&quot;text&quot; name=&quot;email&quot;/&gt;&lt;/p&gt;
    &lt;p&gt;&lt;input type=&quot;button&quot; value=&quot;가입하기&quot; onclick=&quot;checkMember()&quot; /&gt;
&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="jstl_fmt02jsp">jstl_fmt02.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;숫자 형식 맞춰 화면출력&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;숫자 : &lt;fmt:formatNumber value=&quot;3200100&quot; /&gt;&lt;/p&gt;
&lt;p&gt;설정된 숫자를 숫자 형식으로 표현 : 
    &lt;fmt:formatNumber value=&quot;3200100&quot; type=&quot;number&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;천 단위 구분 기호(,) 없이 표현 :
    &lt;fmt:formatNumber value=&quot;3200100&quot; type=&quot;number&quot; groupingUsed=&quot;false&quot;/&gt;
&lt;/p&gt;
    &lt;!-- groupingUsed가 true여야만 통화 형식으로 표현된다  --&gt;
&lt;p&gt;천 단위 구분 기호(,) 있고, 통화 형식으로 표현 :
    &lt;fmt:formatNumber value=&quot;3200100&quot; type=&quot;currency&quot; groupingUsed=&quot;true&quot;/&gt;
&lt;/p&gt;
&lt;p&gt;천 단위 구분 기호(,) 있고, 통화 형식으로 표현 :
    &lt;fmt:formatNumber value=&quot;3200100&quot; type=&quot;currency&quot; currencySymbol=&quot;&amp;&quot;/&gt;
&lt;/p&gt;
&lt;p&gt;퍼센트 형식 :
    &lt;fmt:formatNumber value=&quot;0.45&quot; type=&quot;percent&quot;/&gt;
&lt;/p&gt;
&lt;p&gt;소수점 자리 표현 (최대 10자리, 소수점 둘째 자리):
    &lt;fmt:formatNumber value=&quot;3200100&quot; minIntegerDigits=&quot;10&quot; minFractionDigits=&quot;2&quot;/&gt;
&lt;/p&gt;
&lt;p&gt;패턴 지정:
    &lt;fmt:formatNumber value=&quot;3200100.45&quot; pattern=&quot;.000&quot;/&gt;
&lt;/p&gt;
&lt;p&gt;패턴 지정:
    &lt;fmt:formatNumber value=&quot;3200100.45&quot; pattern=&quot;#,#00.0#&quot;/&gt;
&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><p>숙제
⦁    다음 조건에 맞게 작업해보자
<img src="https://images.velog.io/images/hazel_jo/post/980e1ff9-8fea-467d-83d5-c3686b947416/image.png" alt=""></p>
<p>1)    WebContent/js/ 폴더에 validationBook.js 파일 생성
2)    생성된 validationBook.js 파일에 유효성 검사를 위한 핸들러 함수 작성</p>
<ul>
<li>도서 아이디가 ISBN으로 시작되고 숫자를 포함하여 문자 길이가 5~12자 인지 검사</li>
<li>도서명의 문자 길이가 4~12인지 검사</li>
<li>가격의 문자 길이가 0인지 숫자인지 검사(길이가 0이면 안됨)</li>
<li>가격이 음수인지 검사(음수이면 안됨)</li>
<li>재고 수가 숫자인지 검사</li>
</ul>
<h3 id="validationbookjs">validationBook.js</h3>
<pre><code>function CheckAddBook(){
    var bookCode = document.getElementById(&quot;bookCode&quot;);    
    var bookName = document.getElementById(&quot;bookName&quot;);    
    var price = document.getElementById(&quot;price&quot;);
    var stock = document.getElementById(&quot;stock&quot;);

    //도서 아이디가 ISBN으로 시작되고 숫자를 포함하여 문자 길이가 5~12자 인지 검사
    if(!check(/^[0-9]{5,12}$/,bookCode,
        &quot;[상품 코드]\nISBN으로 시작되고 숫자를 포함하여 문자 길이를  5~12자까지 입력하세요.&quot;)){
        //submit 안함
        return false;
    }
    //도서명의 문자 길이가 4~12인지 검사
    if(bookName.value.length &lt; 4 || bookName.value.length &gt; 12){
        alert(&quot;[도서명]\n최소 4자에서 최대 12자까지 입력하세요&quot;);
        bookName.select();
        bookName.focus();
        return false;
    }
    //가격의 문자 길이가 0인지 숫자인지 검사(길이가 0이면 안됨)
    if(price.value.length == 0 || isNaN(price.value)){
        alert(&quot;[가격]\n숫자만 입력해주세요&quot;);
        price.select();
        price.focus();
        return false;
    }//가격이 음수인지 검사(음수이면 안됨)
    if(price.value &lt; 0){
        alert(&quot;[가격]\n음수는 입력할 수 없습니다.&quot;);
        price.select();
        price.focus();
        return false;
    }
    //재고 수가 숫자인지 검사
    if(isNaN(stock.value)){
        alert(&quot;[재고 수]\n숫자만 입력해주세요&quot;);
        stock.select();
        stock.focus();
        return false;
    }

    //regExg : 정규표현식, e : 대상객체, msg : alert할 메시지
    //정규표현식에 맞으면 true, 다르면 false를 요청한 곳으로 return
    function check(regExp, e, msg){
        //정규식에 맞으면 바로 true를 return
        if(regExp.test(e.value)){
            return true;
        }
        //정규식에 맞지 않으면..
        alert(msg);
        e.select();
        e.focus();
        //form이 submit 되지 않음
        return false;
    }//end check()
    document.newBook.submit();
}
</code></pre><p>3)    WebContent/ch08/ 폴더에 addBook.jsp 파일 작성</p>
<ul>
<li>폼 페이지에서 입력되는 도서 아이디, 도서명, 가격, 재고 수의 유효성 검사를 위해 핸들러 함수가 저장되어 있는 validationBook.js 파일을 연결하도록 작성</li>
<li>[등록] 버튼을 클릭하면 핸들러 함수가 실행되도록 onclick 속성을 작성</li>
</ul>
<h3 id="addbookjsp">addBook.jsp</h3>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot;&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/ckeditor/ckeditor.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/js/validationBook.js&quot;&gt;&lt;/script&gt;
&lt;title&gt;도서 등록&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;

    &lt;div class=&quot;jumbotron&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;h2 class=&quot;display-3&quot;&gt;도서 등록&lt;/h2&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;container&quot;&gt;    
    &lt;form name=&quot;newBook&quot; action=&quot;processAddBook.jsp&quot; 
                method=&quot;post&quot; class=&quot;form-horizontal&quot; enctype=&quot;multipart/form-data&quot;&gt;
                &lt;div class=&quot;form-group row&quot;&gt;
                    &lt;label class=&quot;col-sm-2&quot;&gt;도서 코드&lt;/label&gt;
                    &lt;div class=&quot;col-sm-3&quot;&gt;
                        &lt;input type=&quot;text&quot; name=&quot;bookCode&quot; id=&quot;bookCode&quot;
                            class=&quot;form-control&quot; /&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;도서명&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;bookName&quot; id=&quot;bookName&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;가격&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;price&quot; id=&quot;price&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;저자&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;writer&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;출판사&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;publisher&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;                        
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;출판일&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;bookDate&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;                        
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;총페이지 수&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;bookPage&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;상세정보&lt;/label&gt;
                &lt;div class=&quot;col-sm-5&quot;&gt;
                    &lt;textarea name=&quot;bookInfo&quot; rows=&quot;2&quot; cols=&quot;50&quot; class=&quot;form-control&quot;&gt;&lt;/textarea&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;분류&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;category&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;재고 수&lt;/label&gt;
                &lt;div class=&quot;col-sm-3&quot;&gt;
                    &lt;input type=&quot;text&quot; name=&quot;stock&quot; id=&quot;stock&quot;
                        class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;상태&lt;/label&gt;
                &lt;div class=&quot;col-sm-5&quot;&gt;
                    &lt;input type=&quot;radio&quot; name=&quot;condition&quot; id=&quot;condition1&quot; value=&quot;new&quot; /&gt;
                        &lt;label for=&quot;condition1&quot;&gt;신규도서&lt;/label&gt;
                    &lt;input type=&quot;radio&quot; name=&quot;condition&quot; id=&quot;condition2&quot; value=&quot;Refurbished&quot; /&gt;
                        &lt;label for=&quot;condition2&quot;&gt;중고도서&lt;/label&gt;
                    &lt;input type=&quot;radio&quot; name=&quot;condition&quot; id=&quot;condition3&quot; value=&quot;ebook&quot; /&gt;
                        &lt;label for=&quot;condition3&quot;&gt;E-Book&lt;/label&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;label class=&quot;col-sm-2&quot;&gt;이미지&lt;/label&gt;
                &lt;div class=&quot;col-sm-5&quot;&gt;
                    &lt;input type=&quot;file&quot; name=&quot;filename&quot; class=&quot;form-control&quot; /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group row&quot;&gt;
                &lt;div class=&quot;col-sm-offset-2 col-sm-10&quot;&gt;
                    &lt;input type=&quot;submit&quot; class=&quot;btn btn-primary&quot; value=&quot;등록&quot; onclick=&quot;CheckAddBook()&quot;/&gt;
                &lt;/div&gt;

            &lt;/div&gt;
        &lt;/form&gt;    
    &lt;/div&gt;
&lt;!-- content 끝 --&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
CKEDITOR.replace(&quot;bookInfo&quot;);
&lt;/script&gt;

&lt;!-- bottom 인클루드 시작 --&gt;
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;
&lt;!-- bottom 인클루드 끝 --&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>4)    Src.dto 패키지에 Book.java 작성</p>
<ul>
<li>위 그림에서 입력 받는 요소들을 담을 자바빈 클래스 작성</li>
<li>멤버변수, getter/setter 메소드, toString 메소드 생성</li>
</ul>
<h3 id="bookjava">Book.java</h3>
<pre><code>package dto;

public class Book {

    private String bookCode;
    private String bookName;
    private Integer price;
    private String writer;
    private String publisher;
    private String bookDate;
    private Integer bookPage;
    private String bookInfo;
    private String category;
    private long stock;
    private String condition;
    private String filename;


    public Book() {
        super();
    }

    //생성자 
    public Book(String bookCode, String bookName, Integer price) {
        super();
        this.bookCode = bookCode;
        this.bookName = bookName;
        this.price = price;
    }

    public String getBookCode() {
        return bookCode;
    }
    public void setBookCode(String bookCode) {
        this.bookCode = bookCode;
    }
    public String getBookName() {
        return bookName;
    }
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
    public Integer getPrice() {
        return price;
    }
    public void setPrice(Integer price) {
        this.price = price;
    }
    public String getWriter() {
        return writer;
    }
    public void setWriter(String writer) {
        this.writer = writer;
    }
    public String getPublisher() {
        return publisher;
    }
    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }
    public String getBookDate() {
        return bookDate;
    }
    public void setBookDate(String bookDate) {
        this.bookDate = bookDate;
    }
    public Integer getBookPage() {
        return bookPage;
    }
    public void setBookPage(Integer bookPage) {
        this.bookPage = bookPage;
    }
    public String getBookInfo() {
        return bookInfo;
    }
    public void setBookInfo(String bookInfo) {
        this.bookInfo = bookInfo;
    }
    public String getCategory() {
        return category;
    }
    public void setCategory(String category) {
        this.category = category;
    }
    public long getStock() {
        return stock;
    }
    public void setStock(long stock) {
        this.stock = stock;
    }
    public String getCondition() {
        return condition;
    }
    public void setCondition(String condition) {
        this.condition = condition;
    }
    public String getFilename() {
        return filename;
    }
    public void setFilename(String filename) {
        this.filename = filename;
    }

    @Override
    public String toString() {
        return &quot;Book [bookCode=&quot; + bookCode + &quot;, bookName=&quot; + bookName + &quot;, price=&quot; + price + &quot;, writer=&quot; + writer
                + &quot;, publisher=&quot; + publisher + &quot;, bookDate=&quot; + bookDate + &quot;, bookPage=&quot; + bookPage + &quot;, bookInfo=&quot;
                + bookInfo + &quot;, category=&quot; + category + &quot;, stock=&quot; + stock + &quot;, condition=&quot; + condition + &quot;, filename=&quot;
                + filename + &quot;]&quot;;
    }



}
</code></pre><p>5)    src.ch08.com.dao 패키지에 BookRepository.java 클래스 생성</p>
<ul>
<li>src.ch04.com.dao.ProductRepository.java 클래스를 참고하여 적합하게 작성</li>
</ul>
<h3 id="bookrepositoryjava">BookRepository.java</h3>
<pre><code>package ch04.com.dao;

import java.util.ArrayList;
import java.util.List;

import dto.Book;

//자바빈즈로 사용할 상품 데이터 접근 클래스
public class BookRepository {
    private List&lt;Book&gt; listOfBooks = new ArrayList&lt;Book&gt;();

    //싱글톤 패턴
    private static BookRepository instance = new BookRepository();
    public static BookRepository getInstance() {
        return instance;
    }

    //생성자
    public BookRepository() {
        Book hbook = new Book(&quot;9788983928207&quot;, &quot;해리 포터와 마법사의 돌 (미나리마 에디션)&quot;, 30000);
        hbook.setWriter(&quot;J.K. 롤링&quot;);
        hbook.setPublisher(&quot;문학수첩&quot;);
        hbook.setBookDate(&quot;2020-10-20&quot;);
        hbook.setBookPage(368);
        hbook.setBookInfo(&quot;해리 포터 미나리마 에디션 시리즈&quot;);
        hbook.setCategory(&quot;외국판타지/환상소설&quot;);
        hbook.setStock(100);
        hbook.setCondition(&quot;New&quot;);
        hbook.setFilename(&quot;B1234.jpg&quot;);

        Book dbook = new Book(&quot;9791165343729&quot;, &quot;달러구트 꿈 백화점 2 &quot;, 13800);
        dbook.setWriter(&quot;이미예&quot;);
        dbook.setPublisher(&quot;팩토리나인&quot;);
        dbook.setBookDate(&quot;2021-07-27&quot;);
        dbook.setBookPage(308);
        dbook.setBookInfo(&quot;달러구트 꿈 백화점 2 - 단골손님을 찾습니다&quot;);
        dbook.setCategory(&quot;한국판타지/환상소설&quot;);
        dbook.setStock(1000);
        dbook.setCondition(&quot;EBook&quot;);
        dbook.setFilename(&quot;B1235.jpg&quot;);

        Book sbook = new Book(&quot;9791157675982&quot;, &quot;2022 수제비 정보처리기사 실기 FINAL 실전 모의고사 &quot;, 25000);
        sbook.setWriter(&quot;NCS 정보처리기술사 연구회&quot;);
        sbook.setPublisher(&quot;건기원&quot;);
        sbook.setBookDate(&quot;2021-06-15&quot;);
        sbook.setBookPage(474);
        sbook.setBookInfo(&quot;수험생 입장에서 제대로 쓴 합격 비법서&quot;);
        sbook.setCategory(&quot;수험서/자격증&quot;);
        sbook.setStock(1000);
        sbook.setCondition(&quot;Refurbished&quot;);
        sbook.setFilename(&quot;B1236.jpg&quot;);

        listOfBooks.add(hbook);
        listOfBooks.add(dbook);
        listOfBooks.add(sbook);
    }
    //저장된 모든 상품 가져오기
    public List&lt;Book&gt; getAllBooks(){
        return listOfBooks;
    }

    //상품  상세정보 가져오기
    public Book getBookByCode(String bookCode) {
        Book BookByCode = null;

        for(int i=0; i&lt;listOfBooks.size(); i++) {
            Book book = listOfBooks.get(i);

            if(book!=null&amp;&amp;book.getBookCode()!=null&amp;&amp;
                    book.getBookCode().equals(bookCode)) {
                BookByCode = book;
                break;
            }
        }
        return BookByCode;
    }

    //상품 추가
    public void addBook(Book book) {
        listOfBooks.add(book);
    }


}
</code></pre><p>6)    WebContent/ch08/ 폴더에 addBook_process.jsp 파일 작성</p>
<ul>
<li>addBook.jsp 요청 시 담긴 폼 데이터를 받아 BookRepository 클래스를 통해 생성한 객체에 넣어줌</li>
</ul>
<h3 id="processaddbookjsp">ProcessAddBook.jsp</h3>
<pre><code>&lt;%@page import=&quot;dto.Book&quot;%&gt;
&lt;%@page import=&quot;ch04.com.dao.BookRepository&quot;%&gt;
&lt;%@page import=&quot;java.util.Enumeration&quot;%&gt;
&lt;%@page import=&quot;com.oreilly.servlet.multipart.DefaultFileRenamePolicy&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;%@ page import=&quot;com.oreilly.servlet.MultipartRequest&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;cos.jar 도서 업로드&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;%
    request.setCharacterEncoding(&quot;UTF-8&quot;);
    //웹 어플리케이션상의 절대 경로
    String realFolder = &quot;C:\\JSPSpring\\workspace\\JSPBook\\WebContent\\upload&quot;;

    String filename=&quot;&quot;;

    //최대 업로드될 파일의 크기 5MB
    int maxSize = 5*1024*1024;

    //인코딩 유형
    String encType = &quot;UTF-8&quot;;

    //cos.jar 라이브러리의 핵심 클래스
    MultipartRequest multi = new MultipartRequest(
            request, realFolder, maxSize, encType, new DefaultFileRenamePolicy()
            );
    //폼 데이터 처리
    String bookCode = multi.getParameter(&quot;bookCode&quot;);
    String bookName = multi.getParameter(&quot;bookName&quot;);
    String price = multi.getParameter(&quot;price&quot;);
    String writer = multi.getParameter(&quot;writer&quot;);
    String publisher = multi.getParameter(&quot;publisher&quot;);
    String bookDate = multi.getParameter(&quot;bookDate&quot;);
    String bookPage = multi.getParameter(&quot;bookPage&quot;);
    String bookInfo = multi.getParameter(&quot;bookInfo&quot;);
    String category = multi.getParameter(&quot;category&quot;);
    String stock = multi.getParameter(&quot;stock&quot;);
    String condition = multi.getParameter(&quot;condition&quot;);

    Integer bprice;
    if(price.isEmpty()){
        bprice = 0;
    }else{
        bprice = Integer.valueOf(price);
    }

    Integer bpage;
    if(bookPage.isEmpty()){
        bpage = 0;
    }else{
        bpage = Integer.valueOf(bookPage);
    }

    long bstock;
    if(stock.isEmpty()){
        bstock = 0;
    }else{
        bstock = Long.valueOf(stock);
    }

    //파일객체 처리
        //multi객체 안에는 request 객체가 들어있고, request객체 안에는 파일 객체가 있음
        Enumeration files = multi.getFileNames();
        //productImage2
        String fname = (String)files.nextElement();
        //폼 페이지에서 전송되어 서버에 업로드된 파일을 가져옴
        String fileName = multi.getFilesystemName(fname);

        out.print(&quot;fname : &quot; + fname + &quot;, fileName : &quot; + fileName);

        //싱글톤패턴
        BookRepository dao =  BookRepository.getInstance();

        Book newBook = new Book();
        newBook.setBookCode(bookCode);
        newBook.setBookName(bookName);
        newBook.setPrice(bprice);
        newBook.setWriter(writer);
        newBook.setPublisher(publisher);
        newBook.setBookDate(bookDate);
        newBook.setBookPage(bpage);
        newBook.setBookInfo(bookInfo);
        newBook.setCategory(category);
        newBook.setStock(bstock);
        newBook.setCondition(condition);


        //파일 객체의 파일명(fileName : P1237.jpg)
        newBook.setFilename(fileName);
        //dao : data access object(여기서는 메모리 DB라고 생각)
        dao.addBook(newBook);
        //상품 목록으로 리다이렉트
        response.sendRedirect(&quot;books.jsp&quot;);
    %&gt;

    &lt;/body&gt;
    &lt;/html&gt;</code></pre><p>7)    WebContent/ch08/products.jsp 생성</p>
<ul>
<li>addBook.jsp와 addBook_process.jsp를 통해 추가된 book정보를 목록으로 출력</li>
<li>WebContent/ch04/products.jsp 참고</li>
</ul>
<h3 id="booksjsp">Books.jsp</h3>
<pre><code>&lt;%@page import=&quot;dto.Book&quot;%&gt;
&lt;%@page import=&quot;ch04.com.dao.BookRepository&quot;%&gt;
&lt;%@page import=&quot;java.util.List&quot;%&gt;
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/bootstrap.min.css&quot; /&gt;
&lt;title&gt;도서 목록&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%
BookRepository bookDAO = BookRepository.getInstance();
%&gt;
    &lt;jsp:include page=&quot;/ch03/top.jsp&quot; /&gt;
    &lt;div class=&quot;jumbotron&quot;&gt;
    &lt;div class=&quot;container&quot;&gt;
        &lt;h1 class=&quot;display-3&quot;&gt;도서 목록&lt;/h1&gt;
    &lt;/div&gt;
    &lt;/div&gt;
&lt;%
    List&lt;Book&gt; listOfBooks = bookDAO.getAllBooks();
%&gt;    
    &lt;div class=&quot;container&quot;&gt;
        &lt;div class=&quot;row&quot; align=&quot;center&quot;&gt;
        &lt;%
            for(int i=0; i&lt;listOfBooks.size(); i++){
                Book book = listOfBooks.get(i);
        %&gt;
            &lt;div class=&quot;col-md-4&quot;&gt;
                &lt;img src=&quot;/upload/&lt;%=book.getFilename()%&gt;&quot; style=&quot;width: 350px; height: 370px&quot;/&gt;
                &lt;h3&gt;&lt;%=book.getBookName() %&gt;&lt;/h3&gt;
                &lt;p&gt;&lt;%=book.getBookInfo() %&gt;&lt;/p&gt;
                &lt;p&gt;&lt;%=book.getWriter()%&gt;|&lt;%=book.getPublisher()%&gt;|&lt;%=book.getPrice() %&gt;원&lt;/p&gt;
                &lt;a href=&quot;book.jsp?id=&lt;%=book.getBookCode() %&gt;&quot; class=&quot;btn btn-secondary&quot; role=&quot;button&quot;&gt;상세 정보&amp;raquo;&lt;/a&gt;

            &lt;/div&gt;
        &lt;%
            }
        %&gt;

        &lt;/div&gt;
    &lt;/div&gt;        
    &lt;jsp:include page=&quot;/ch03/bottom.jsp&quot; /&gt;            
&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
    </channel>
</rss>