Atomic Habits

자바웹기술(기록) 본문

IT/Spring Framework

자바웹기술(기록)

체계성 2022. 5. 20. 06:54

업데이트 일자 : 22/05/20 

 

■ 정적 웹 프로그래밍(38)
Apache 같은 웹 서버를 이용해서 웹 페이지들을 브라우저에 표시하는 방식을 정적(static) 웹 프로그래밍이라고 합니다.
정적 웹 프로그래밍 : 보여줄 HTML, CSS, img, js 파일을 웹 저장해 두고 브라우저에서 요청할 경우 '고정된 정보'를 '그대로' 전달만하는 방식이며,
사용자는 페이지가 변경되지 않는 한 고정된 웹 페이지를 보게 된다.
(주기적으로 웹 서버 관리자가 '손으로 직접' HTML의 환율 '정보가 있는 코드를 수정'해야 하는 정적 웹)
환율, 주가 등 실시간으로 변하는 정보를 표시해야 할 일이 많기 때문에 이 방식은 거의 사용되지 않는다.
정적 웹 프로그래밍의 구성 요소는 여전히 JSP에서 화면 디자인과 기능 처리를 담당하므로 알아두어야 한다.

■ 용어 정리
웹 서버 : 각 클라이언트에게 서비스를 제공하는 컴퓨터
클라이언트 : 네트워크로 서버에 접속 후 서버로부터 서비스를 제공받는 컴퓨터
HTTP : 웹 서버 - 클라이언 간 정보를 주고 받을 때 사용하는 통신 규약
JS : HTML 웹 페이지의 여러 동적 기능을 제공하는 스크립트 언어
CSS : 서체, 색상, 정렬 등 디자인 관련 기능 제공


■ 동적 웹 프로그래밍(40)
정적 웹 에서 관리자가 해야 했던 '페이지/코드 상의 정보 업데이트'를 웹 애플리케이션 서버(WAS)가 수행한다.
client 요청이 있을 때마다 웹 서버(WAS)가 환율 정보가 저장되어 있는 DB에 접근하여 실시간 정보를 제공한다.

WAS : Web Application Server

동적 웹 프로그래밍의 종류 - JSP, ASP, PHP

■ 스레드 방식의 JSP
                                        DB ( 환율정보조회  query )
                                         |
client - 환율 정보 요청/반환 - 웹 서버 - 웹 애플리케이션 서버
                                   (톰캣/제우스/웹로직)
                                         |   
    외부 환율 조회 기능
                                                                    - 환율 조회 기능을 메모리에 로드 후 외부 웹에서 환율정보를 가져옴.
- 타 client가 재요청 시 위 기능이 메모리에 존재하므로 그대로 사용.
    (메모리 부하 방지)

■ JSP, ASP, PHP 동작 방식의 특징
- 프로세스 방식이 아닌 스레드 방식(메모리로 로드 하는 과정에서 프로그램의 수행 속도 저하 유발)
- client의 요구를 처리하는 기능은 최초 한 번만 메모리에 로드
- client가 동일 기능 요구 시 기존에 사용한 기능을 재사용


■ 이클립스 재설치
JavaUninstallTool 를 필히 다운받아서 실행 후 재시작하면 자바, 이클립스 재설치 시 충돌방지 

C:/user/사용자이름의 경로 아래 4개 폴더를 삭제해야 새롭게 설치 된다.
eclipse
eclipse_workspace( 이클립스가 존재하는 지정한 경로에 있을 수 있음 / 사용자가 지정한 경로일 수도 있음 )
.eclipse
.p2
(.이 붙은 경로는 숨긴 항목 표시 필요)

* 이클립스 존재 경로 찾기
이클립스 아이콘 속성에서 확인 가능


■  문제 발생
could not open c:\java\lib\jvm.cfg file error
종종 이클립스에서 Java JDK를 제대로 인식 못해서 이런 경우가 발생한다.
이럴 때에는,   eclipse.ini 파일을 열어서

-vm
C:\현재 자바가 설치된 경로 주소\java\jdk1.6.0_17\bin
를 입력해서 vm을 수동으로 설정해 주면 정상적으로 이클립스가 실행된다.
eclipse.ini의 위치는 eclipse.exe 파일이 있는 위치에 함께 있다.

■ 톰캣 컨테이너 설치하기(54) - 웹 애플리케이션 서버의 일종
DB, API 등 다른 기능과 연동할 수 있게 동작하는 것이 웹 컨테이너(톰캣)
서블릿이나 JSP를 실행하여 웹 페이지에 가격 정보를 
동적으로 표시해주는 톰캣 컨테이너

■ 오라클 설치 및 계정 생성(85)
sql cmd 창
>sqlplus > user:system, password: 설치시 입력한 비번(1234)
> create user scott identified by tiger; // 계정 scott, 비번 tiger로 지정
> grant resource, connect to scott;  // resource, connect는 계정 사용자가 일반적인 작업을 사용하도록 권한 지정
> exit // System 계정 연결 해제

> conn
> user-name:scott
> pw:tiger

■ 한동안 오라클 사용하지 않을 경우 DBMS가 중지되도록 수동 설정하기(시스템 성능 저하 방지)(86)
"The network adapter could not establish the connection." - 수동일 경우 에러가 발생하면서 DB 접속 불가능 / 
내 PC 우클릭 > 관리 > 서비스 및 응용프로그램 > 서비스 > OracleServiceXE, OracleXTNSListner 의 상태를 자동에서 수동으로 변경
* 주의 : 다시 오라클을 사용하기 위해선 위 경로에서 '자동'으로 변경해얀 한다. '시작'으로 '실행 중' 변경!


■ 웹 애플리케이션의 정의(98)
1) 기존 정적 웹 프로그래밍 방식의 기능에
서블릿, JSP, 자바 클래스들을 추가하여 client에게 동적인 서비스를 
제공하는 프로그램

2) 웹 컨테이너에서 실행되는  JSP,  서블릿, 자바 클래스들을 사용해
정적 웹 프로그래밍 방식의 단점을 보완하여 서비스를 제공하는 서버 프로그램을
웹 애플리케이션이라고 한다.


■ 웹 컨테이너(톰캣)에서 실행하는 
웹 애플리케이션의 기본 디렉터리 구조(99)

아래오 다같은 구조를 갖춰야 웹 애플리케이션 실행 시 오류가 나지 않는다.
다른 기능이 추가되면 디렉터리를 추가해서 사용한다.

웹 애플리케이션 폴더명 - 중복 불가  
  ㄴ 기타 폴더명      - JSP HTML 파일 저장
  ㄴ WEB-INF        - 웹 앱 관련 정보가 저장되며 외부에서 접근 불가
       L  classes   - 웹 앱이 수행하는 서블릿, 일반 클래스들 
   L  lib       - 라이브러리(jar)(DB 연동 드라이버, 프레임워크 기능 관련)
   L  web.xml   - 배치 지시자(deployment descriptor)로서 일종의
                  환경 설정 파일
  
■  웹 애플리케이션에 추가된 구성 요소의 기능(105)
bin - 애플리케이션에서 사용되는 각종 실행 파일 저장
conf - 프레임워크에서 사용하는 각종 설정 파일 저장
src - 자바 소스 파일 저장

■ 컨테이너에서 웹 애플리케이션 실행하기
웹 애플리케이션을 만들어 톰캣 컨테이너에서 실행 후 웹 브라우저에서 요청하면
정상적으로 실행되는지 실습해보자.

자바 애플리케이션은 일반 PC에서 단독으로 실행할 수 있지만,
웹 애플리케이션은 톰캣 같은 웹 컨테이너에서 실행되는 구조이다.
따라서 톰캣에 먼저 등록부터 하자.

■ 톰캣에 등록하는 방법 2가지
1) %CATLALINA_HOME%webApp 디렉터리에 애플리케이션을 저장(개발 완료 후 실제 서비스 제공 시에 사용하는 방법)
설치한 톰캣 루트 디렉터리(CATLALINA_HOME)의 하위 디렉터리인 webapps 에
작성한 웹 애플리케이션을 위치시킨 다음 
톰캣(tomcat9/bin/Tomcat9.ext)을 재시작하면 웹 애플리케이션을 인식/등록 후 
웹 브라우저에서 웹 애플리케이션을 요청하면 실행할 수 있다.

http://ip:port/context/요청파일명
http://localhost:8090/webShop/main.html  - 개인 PC에서 할 경우 localhost로 가능
http://192.168...:8090/webShop/main.html


2) server.xml에 직접 웹 애플리케이션을 등록
임의 위치에 있는 웹 애플리케이션을 톰캣의 설정 파일인 server.xml에 등록해서 실행할 수 있다.
개발 과정에서는 1)번 처럼 프로젝트 파일 통째로 복사해야 한다면 불편할 수 있다.
따라서 C 드라이브 내 임의 위치에 웹 애플리케이션(webShop) 폴더를 두고 그 위치를 server.xml에
등록(<Host> 태그 아래)해 두고 톰캣을 실행하는 식으로 개발해야 한다.
(톰캣이 xml에 입력된 곳으로 이동하여 파일을 확인 후 실행)
마찬가지로 bin/Tomcat9.exe를 실행한 후 http://192.168.0.3:8090/webMal/main.html 를 요청한다.

C:\Tomcat 9.0\conf 아래 server.xml의 <Host>태그 아래 <Context> 태그를 추가한다.

<Context path="/컨텍스트 이름"                                   
              docBase="실제 웹 애플리케이션의 WEB_INF 디렉터리 위치" - 
  reloadable="true 또는 false" /> - 
path : 웹 브라우저에서 실제 웹 애플리케이션을 요청하는 이름
docBase : 해당 컨텍스트에 대한 실제 웹 애플리케이션이 위치한 경로이며 컨테이너 상위 폴더까지 명시
reloadable : 실행 중 소스 코드가 수정될 경우 바로 갱신할지 설정( false : 톰캣 재실행 해야 변경된 코드 반영 )

<Context path="/webMal"
              docBase="C:\WebApp\shoppingMall"
  reloadable="true" />

* Context(110)
server.xml에 등록하는 웹 애플리케이션을 컨텍스트라고 하는데, 
톰캣 입장에서 인식하는 한 개의 웹 애플리케이션이라고 볼 수 있다.

컨터이너 실행 시 웹 애플리케이션당 하나의 컨텍스트가 생성된다.
웹 애플리케이션과 컨텍스트 이름을 같게 만드는 것이 일반적이나 보안상 이유 또는
웹 애플리케이션 이름이 긴 경우 등은 다르게 만들 수도 있다.


[이클립스 웹 애플리케이션 실습]

- 이클립스에서는 한 개의 프로젝트가 한 개의 웹 애플리케이션이며, 프로젝트명이 웹 애플리케이션 이름이다.
1) 프로젝트 생성 > New Dynamic web Project
  Project name : webShop / 3.0 / Default Configuration 
  Source project on build path : src
  Context root : webShop
  Content directory : WebContent 
  (WebContent 대신 기본값인 src/main/java로 둘 경우 java/ 아래에 META_INF, WEB-INF 가 생성됨)
  
2) HTML 생성
 WebContent 바로 아래 main.html 생성
 
3) 이클립스 - 톰캣 연동
 이클립스는 통합 개발 환경 도구이므로 일일이 직접 경로를 만들고 톰캣에 등록할 필요가 없다.
 이클립스에서 설정만 해주면 톰캣이 컨테이너에 알아서 등록을 해주기 때문에 간편하다.
 Server > 우클릭 New > Server > v9.0 > 생성한 프로젝트를 Add할 경우 server.xml에 <Context> 태그로
 등록이 된다. 즉, 톰캣 서버에 webShop 프로젝트가 등록된 것이다.
 
■ 웹 애플리케이션 배포하기(130)
실제 서비스에서는 이클립스에 등록된 톰캣에서 실행하는 것이 아니다. 
웹 애플리케이션 전체 소스를 리눅스 서버에 설치된 톰캣으로 이동시켜서 실행하는데, 이 과정을 배치(deploy)라고 한다.
실제로 서비스한다라는 의미이다.

- 이클립스에서 배치할 프로젝트를 war 압축 파일로 만들고 톰캣 컨테이너의 webapps 폴더에 저장해보자.

개발을 마친 후 프로젝트를 war 압축 파일로 만든 후 FTP를 이용해 톰캣이 미리 설치된 리눅스 운영 서버에 업로드한다. 
그리고 텔넷(telnet)을이용해 bin 폴더의 Tomcat.exe를 다시 실행하면 톰캣 실행 시 war 파일의 압축이 해제되면서
동시에 톰캣에 자동으로 등록되어 웹 애플리케이션이 실행된다.

■ 이클립스 HTML, JSP 한글 인코딩 UTF-8 설정(136)


■ 서블릿(140)
클라이언트 요청에 따라 서버 쪽에서 동작하여 동적으로 서비스를 제공하는 자바 클래스
서블릿은 자바로 작성되어 자바의 일반적인 특징을 모두 가진다. 
하지만 서블릿은 톰캣과 같은 JSP/Servlet 컨테이너에서만 실행된다.

- 서블릿 동작 과정
  클라이언트의 요청을 받은 웹 서버는 그 요청을 웹 애플리케이션 서버(WAS) 즉, 톰캣으로 위임한다.
  그러면 WAS는 각 요청에 대한 서블릿을 실행한다. 
  그리고 서블릿은 요청에 대한 기능(가격 조회 후 할인가 적용 등)을 수행한 후 결과를 반환하여 클라이언트에 전송한다.
  
  서블릿의 특징
  서버 내에서 기능 수행하고, 컨테이너에서 실행됨.  스레드 방식, 컨테이너 종류에 상관없이 실행됨.
  웹 브라우저에서 요청 시 기능을 수행함. 보안 기능 적용 용이함.


■ 톰캣 servlet_api.jar 클래스 패스 설정(145)
톰캣의 서블릿 API 들은 servlet_api.jar 라이브러리로 제공되므로, 
이클립스의 프로젝트에서 클래스 path를 설정해줘야 한다.

1) 프로젝트 생성
2) 프로젝트 우클릭 > Build Path > Java Build Path > Libraries > Classpath > Add External JARs...
> tomcat 설치 폴더 > lib > servlet_api.jar 선택


■ 첫 서블릿 생성(152)
1) Package 생성
 src 우클릭 > New > Package > 
 Source folder : pro05/src 
 Name : sec01.ex01
2) Class 생성
 위 패키지 내에 FirstServlet.java로 생성

3) 서블릿 메서드 작성
public class FirstServlet extends HttpServlet{

@Override
public void init() throws ServletException {
System.out.println("init 메서드 호출");
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet 메서드 호출");
}

@Override
public void destroy() {
System.out.println("destroy 메서드 호출");
}

}

4) 서블릿 매핑하기
 - 브라우저에서 서블릿 이름으로 요청하는 방법
   프로젝트 이름 뒤에 패키지 이름이 포함된 클래스 이름을 입력한다.
   http://IP:port/프로젝트명/전체패키지명.클래스명
   http://192...:8090/pro05/sec01.ex01.FirstSeverlet
   -> 너무 길고, 클래스명의 기능을 짐작할 수 있으므로 보안에도 취약
   
   위 주소를 대체할 수 있는 서블릿 매핑 명칭을 만들자.
   
 - 방법
  1) 각 프로젝트 내 web.xml에서 설정
  2) <servelt> 태그와 <servlet_mapping> 태그를 이용
  3) 여러 서블릿 매핑 시 <servlet> 태그 먼저 정의 후
     <servlet_mapping> 태그를 정의한다.
 
  * servlet_name이 <servlet> 태그와 <servlet_mapping> 태그의 연결 역할
  
  <servlet>
   <servlet-name>aaa</servlet-name>
   <servlet-class>sec01.ex01.FirstServlet</servlet-class>
  </servlet>
  <servlet-mapping>
   <servlet-name>aaa</servlet-name>
   <url-pattern>/first</url-pattern>
  </servlet-mapping>
  
  * 웹에서 /first로 요청 시 aaa 로 연결되는 
    실제 서블릿 sec01.ex01.FirstServlet 을 실행한다.

http://ip:8090/프로젝트명(context name)/서블릿매핑이름
http://localhost:8090/pro05/first
(톰캣이 로컬 pc에 설치된 경우 localhost 도 가능)


■ 다수의 서블릿 매핑하기
- 주문, 회원 관리, 게시판 등 여러 기능을 한 서블릿에 만들면 소스가 복잡해지고 관리가 어렵다.
실제 웹 서비스에서는 한 프로젝트에서 여러 개의 서블릿을 만들어 사용한다.

- 여러 서블릿 매핑 시 <servlet>, <servlet-mapping>를 각각 분리해서 작성한다.
  <servlet>
   <servlet-name>aaa</servlet-name>
   <servlet-class>sec01.ex01.FirstServlet</servlet-class>
  </servlet>
  <servlet>
   <servlet-name>bbb</servlet-name>
   <servlet-class>sec01.ex01.SecondServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
   <servlet-name>aaa</servlet-name>
   <url-pattern>/first</url-pattern>
  </servlet-mapping>
   <servlet-mapping>
  <servlet-name>bbb</servlet-name>
   <url-pattern>/second</url-pattern>
  </servlet-mapping>
  
  * web.xml 수정 시 톰캣 재실행 필요.
  
- 클라이언트 1이 특정 요청을 하면 톰캣은 해당 서블릿이 메모리에 로드되어 있는지 판단해서, 메모리에 로드 시키고, 최초의 요청이므로 init() 메서드를 호출하여
해당 서블릿의 인스턴스를 로드한다. 그 다음 doGet(), doPost() 메서드를 호출하여 
클라이언트에게 제공한다.

- 클라이언트 2가 위와 같은 특정 요청을 하면, 톰캣이 해당 서블릿이 메모리에 로드되어 있는지 확인하는데, 클라이언트 1이 먼저 요청해서 이미 존재한다면 최초 요청이 아니므로 init()은 호출하지 않고
바로 doGet(), doPost() 메서드를 호출하여 서비스한다.

- 이처럼 서블릿은 스레드 방식으로 작동해서 메모리에 존재하는 특정 서블릿을 재사용하여 훨씬 빠르고 효율적으로 동작한다.a

■ 애너테이션을 이용한 서블릿 매핑
- 여러 서블릿을 web.xml에 설정할 경우 서비스의 규모가 커질수록 복잡해진다는 단점이 있다.
  따라서 각 서블릿 클래스에 (@)기호를 사용해 서블릿 표시를 해주면 훨씬 가독성이 좋아진다.
  이처럼 소스 코드에 직접 기능을 설정하는 방식을 애너테이션(annotation)이라 한다.
  
방법 1) 자바 클래스 생성 후 (@) 명시로 서블릿 형태로 만들기
annotation 을 사용하려면 HttpServlet 클래스를 상속받아야 하며, @WebServlet을 명시한다.

@WebServlet("/third")
public class ThirdServlet extends HttpServlet{

}

  
방법 2) 서블릿 생성으로 (@) 바로 적용하기(175)
pakage 우클릭 > New > Servlet > Class Name 적고 URL mappings 수정(브라우저에서 요청할 주소)

* 주의 : annotation이 중복될 경우 에러 발생

■ 서블릿의 세 가지 기본 기능(176)
서블릿 ? -> 웹 브라우저의 요청을 스레드 방식으로 처리하는 기술

1) 클라이언트로부터 요청을 받는다.(ID, PW 입력)
2) WAS 서버 내에서 DB 연동과 같은 비즈니스 로직을 처리한다. 
   (여러 메서드를 이용해 요청 시 전송한 데이터를 받아서, 
    DB에 조회 후 메인 페이지로 이동/오류 메세지 생성 등을 클라이언트에게 전송한다.)
3) 처리된 결과를 클라이언트에게 돌려준다.

■ 서블릿 응답/요청 API(177)
- HttpServletRequest, HttpServletResponse
  doGet(), doPost() 메서드의 매개변수로 사용한다.

- 수행 과정
  WAS(톰캣)에서 클라이언트의 요청을 받고, 요청/응답에 대한 HttpServletRequest, HttpServletResponse 객체 생성 후 
  요청에 대한 정보를 HttpServletRequest의 속성으로 담아 전달하는데,
  서블릿의 doGet(), doPost() 메서드를 호출하면서 매개변수로 위 2개 객체들을 전달한다.
  
- HttpServletRequest, HttpServletResponse 객체의 여러 메서드를 이용해서 요청/응답 관련 작업을 한다.

■ HTML <form> 태그 이용해 서블릿에 요청하기(178)
- 브라우저에서 여러 입력 서식을 이용해 '전송'을 클릭하면 입력한 데이터가 서블릿으로 전송된다.
  서블릿은 여러 메서드를 이용해 전송된 데이터를 받아온다.
  
 <form name = "frmLogin" method="get" action="login" encType="UTF-8">
 아이디 :  <input type="text" name ="user_id"><br>
 비밀번호 : <input type="password" name="user_pw"><br>
<input type="submit" value="로그인"> <input type="reset" value="다시입력">
 </form>
 
 action : 데이터를 전송받을 서블릿, JSP의 매핑 이름
 encType : 인코딩 타입으로 파일 업로드 시  multipart/form-data 로 지정
 
■ 서블릿에서 클라이언트 요청 얻는 방법(182)
- HttpServletRequest 클래스에서 <form> 태그로 전송된 데이터를 받는 메서드
  String getParameter(String name) : 특정 name 으로 전송된 데이터 수신
  String[] getParmeterValues(String name) : 특정 name 으로 여러 데이터 수신
  Enumeration getParameterNames() : name 값을 알지 못할 때 사용
 
[login.html]
 <!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
 <form name = "frmLogin" method="get" action="login" encType="UTF-8">
 아이디 :  <input type="text" name ="user_id"><br>
 비밀번호 : <input type="password" name="user_pw"><br>
<input type="submit" value="로그인"> <input type="reset" value="다시입력">
 </form>
</body>
</html>

[LoginServlet]
package sec01.ex01;

import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class LoginServlet
 */
@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    public LoginServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
System.out.println("init 메서드 호출");
}

public void destroy() {
System.out.println("destroy 메서드 호출");
}

// 톰캣 컨테이너는 HttpServletRequest 객체를 생성한 후 웹에서 전송된 정보를 doGet 메서드로 넘겨준다.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

response.getWriter().append("Served at: ").append(request.getContextPath());

request.setCharacterEncoding("utf-8"); // 전송된 데이터를 UTF-8로 인코딩한다.
String user_id = request.getParameter("user_id"); // form 태그의 name 속성에 해당하는 value를 가져온다.
String user_pw = request.getParameter("user_pw");

System.out.println("ID : "+ user_id);
System.out.println("PW : "+ user_pw);
}

}

■ 여러 개의 값을 한번에 전송받을 때(186)
[input.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
 <form name = "frmLogin" method="get" action="input" encType="UTF-8">
 아이디 :  <input type="text" name ="user_id"><br>
 비밀번호 : <input type="password" name="user_pw"><br>
 <input type="checkbox" name="subject" value="java" checked>자바
 <input type="checkbox" name="subject" value="C">C
 <input type="checkbox" name="subject" value="JSP">JSP
   <input type="checkbox" name="subject" value="HTML">HTML
   <input type="checkbox" name="subject" value="CSS">CSS
 <br><br>
 
<input type="submit" value="전송">
<input type="reset" value="초기화">
 </form>
</body>
</html>

[InputServlet.java]
package sec01.ex01;

import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class InputServlet
 */
@WebServlet("/input")
public class InputServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public InputServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

/**
 * @see Servlet#init(ServletConfig)
 */
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
}

/**
 * @see Servlet#destroy()
 */
public void destroy() {
// TODO Auto-generated method stub
}

/**
 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 */
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());

request.setCharacterEncoding("utf-8");
String user_id = request.getParameter("user_id"); // form 태그의 name 속성에 해당하는 value를 가져온다.
String user_pw = request.getParameter("user_pw");

System.out.println("ID : "+ user_id);
System.out.println("PW : "+ user_pw);

String[] subject = request.getParameterValues("subject"); // 하나의 name("subject")으로 여러 값을 전송받는 경우 사용한다.

for (String str : subject)
{
System.out.println("선택한 과목: "+str);
}
}
}

■ getParameter()를 이용하기에는 매개변수가 너무 많을 경우(186)
- getParameterNames() 메서드 사용


[InputServlet2.java]
package sec01.ex01;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class InputServlet
 */
@WebServlet("/input2")
public class InputServlet2 extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());

request.setCharacterEncoding("utf-8");
// String user_id = request.getParameter("user_id"); // form 태그의 name 속성에 해당하는 value를 가져온다.
// String user_pw = request.getParameter("user_pw");
//
// System.out.println("ID : "+ user_id);
// System.out.println("PW : "+ user_pw);

Enumeration enu = request.getParameterNames();

while (enu.hasMoreElements())
{
String name = (String) enu.nextElement();
System.out.println("        name : "+ name);
String[] values = request.getParameterValues(name);
for (String value : values)
{
System.out.println("name = "+name + ", value = "+value);
}

}
}

}


■ 서블릿의 응답 처리 방법(191)
1) doGet()/doPost() 메서드 안에서 처리
2) HttpServletResponse 객체 활용
3) setContentType()로 클라이언트에게 전송할 데이터 종류(MIME-TYPE)를 지정
4) 클라이언트(브라우저)와 서블릿의 통신은 자바 I/O의 스트림을 이용

브라우저(클라이언트 측)와 서블릿(서버 측)은 네트워크를 통해 데이터를 주고 받는다.
서버(서블릿)에서 웹 브라우저로 데이터를 전송할 때는 어떤 종류의 데이터를 전송하는지 알려줘야 한다.
브라우저가 그 정보를 알고 있을 때 더 빨리 처리할 수 있기 때문이다.
서버(서블릿)에서 자바 I/O 스트림 클래스 웹 브라우저로 데이터를 전송할 때 
톰캣 컨테이너에서 미리 설정해 놓은 데이터 종류(=MIME-TYPE) 중 하나를 지정해서 브라우저로 전송한다.
HTML로 전송 시 : text/html
일반 텍스트로 전송 시 : text/plain
XML 데이터로 전송 시 : apllication/xml

웹 브라우저는 기본적으로 HTML만 인식하므로 서블릿에서 전송하는 대부분의 데이터는  text/html로 지정한다.
새로운 종류의 데이터를 지정하고 싶다면 CATALINA_HOME\conf\web.xml에 추가한다.

■ HttpServletResponse를 이용한 서블릿 응답 실습
1) setContentType()로 MIME-TYPE 지정
2) 데이터를 출력할 PrintWriter 객체 생성
3) 출력 데이터를 HTML 형식으로 만듦
4) PrintWriter 의 print()/println() 를 이용해 데이터 출력

시작 페이지 : http://localhost:8090/pro06/login.html
요청 페이지 : http://localhost:8090/pro06/login2?user_id=abc&user_pw=5555

[login.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
 <form name = "frmLogin" method="get" action="input2" encType="UTF-8">
 아이디 :  <input type="text" name ="user_id"><br>
 비밀번호 : <input type="password" name="user_pw"><br>
 <input type="checkbox" name="subject" value="java" checked>자바
 <input type="checkbox" name="subject" value="C">C
 <input type="checkbox" name="subject" value="JSP">JSP
   <input type="checkbox" name="subject" value="HTML">HTML
   <input type="checkbox" name="subject" value="CSS">CSS
 <br><br>
 
<input type="submit" value="전송">
<input type="reset" value="초기화">
 </form>
</body>
</html>


package sec01.ex01;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

[LoginServlet2.java]
@WebServlet("/login2")
public class LoginServlet2 extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().append("Served at: ").append(request.getContextPath());
System.out.println("----------------------수신1");
request.setCharacterEncoding("utf-8");              // 요청 데이터 인코딩 설정
response.setContentType("text/html;charset=utf-8"); // 응답 데이터 종류 지정

PrintWriter out = response.getWriter(); 
// HttpServletResponse 객체의 getWriter() 로 출력 스트림 PrintWriter 객체를 받는다.

System.out.println("----------------------수신2");

String id = request.getParameter("user_id");
String pw = request.getParameter("user_pw");

String data = "<html>"; // 브라우저로 출력할 데이터를 HTML 태그로 만듦
data += "<body>";
data += "<body>";
data += "ID : "+id;
data += "<br>";
data += "PW : "+pw;
data += "</body>";
data += "</html>";
System.out.println("----------------------수신3");
out.print(data); // PrintWriter 의 print()로 HTML 양식을 웹 브라우저에 출력
}
}



■ 환율 계산기 서블릿

package sec02.ex01;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/calc")
public class CalcServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

request.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");
PrintWriter pw = response.getWriter();
String command = request.getParameter("command");  // 수행할 요청
String won = request.getParameter("won");  // 변환할 원화 금액
String operator = request.getParameter("operator"); // 변환할 외화 종류
System.out.println("command : "+command);
System.out.println("won :"+won);
System.out.println("operator :"+operator);

// 최초 요청 시 command 값이 null이면 계산기 화면 HTML 을 출력하고,
// null이 아니면, 계산 결과를 출력한다.
if (command != null && command.equals("calculate"))
{
String result = calculate(Float.parseFloat(won), operator);
pw.print("<html><font size=10>변환 결과</font><br>");
pw.print("<html><font size=10>"+result+"</font><br>");
pw.print("<a href='/pro06/calc'>환율 계산기</a>");
return; // 아래 라인은 수행하지 않고 전체 메서드 종료
}
pw.print("<html><title>환율계산기</title>");
pw.print("<font size=5>환율 계산기</font><br>");
pw.print("<form  name='frmCalc' method='get'  action='/pro06/calc'  />  ");
pw.print("원화: <input type='text' name='won' size=10  />  ");
pw.print("<select name='operator' >");
pw.print("<option value='dollar'>달러</option>");
pw.print("<option value='en'>엔화</option>");
pw.print("</select>");
pw.print("<input type='hidden' name='command' value='calculate'  />  "); // <hidden> 태그로 계산기에서 서블릿으로 수행할 요청을 전달
pw.println("<input type='submit' value='변환'  />");
pw.println("</form>");
pw.print("</html>");
pw.close();
}

private static String calculate(float won, String operator) { // 환산 기능
String result = null;
float USD_RATE = 1265.3f;
float JPY_RATE = 1265.3f;

if (operator.equals("dollar")) {
result = String.format("%.6f", won / USD_RATE);
} else if (operator.equals("en")) {
result = String.format("%.6f", won / JPY_RATE);
}
return result;
}
}


■ 브라우저에서 서블릿으로 데이터 전송(198) (GET/POST)
GET 방식 : 보안과 관련 없는 간단한 데이터 전송을 쉽게 함
          url에  ?won=1000&operator=dollar  이런 식으로 데이터를 붙여서 전송
POST 방식 : 보안을 위해 데이터를 숨겨서 전송
           TCP/IP 프로토콜 데이터의 HEAD 영역에 숨겨진 채 전송
   전송 용량 무제한(get : 최대 255자)
   서블릿에서 보낸 데이터를 다시 가져오는 작업을 해야 하므로,
   처리 속도가 get 보다 느림

* GET/POST 방식을 사용하면서 두 메서드가 서블릿에 존재하지 않거나, 사용자가 임의로 만든 메서드를 사용하면 오류가 발생한다.

■ GET/POST 요청 동시에 처리하기(203)
실제 웹 서비스에서는 GET과 POST 방식을 혼합해서 많이 사용한다. 이때 각 방식마다 일일이 구분해서 구현해야 한다면 불편할 수 있다. 
GET, POST 메서드로 처리한 후 다시 doHandle()을 호출해서 모든 기능을 구현하는 실습을 해보자.

[login.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
 <form name = "frmLogin" method="get" action="login4" encType="UTF-8">
 아이디 :  <input type="text" name ="user_id"><br>
 비밀번호 : <input type="password" name="user_pw"><br>
<input type="submit" value="로그인"> <input type="reset" value="다시입력">
 </form>
</body>
</html>

[LoginServlet4.java]
package sec02.ex01;

@WebServlet("/login4")
public class LoginServlet4 extends HttpServlet {

public void init(ServletConfig config) throws ServletException {
System.out.println("init 메서드 호출");
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
System.out.println("doGet 메서드 호출 시 doHandle 메서드로 요청을 이관한다.");
doHandle(request, response);
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


System.out.println("doPost 메서드 호출 doHandle 메서드로 요청을 이관한다.");
doHandle(request, response);
}

private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doHandle 메서드 진입 - doGet, doPost 요청 시 doHandle로 작업을 넘기므로 모든 호출 방식에 대해 처리할 수 있다.");
request.setCharacterEncoding("utf-8");

String user_id = request.getParameter("user_id");

String user_pw = request.getParameter("user_pw");

System.out.println("ID : "+user_id);
System.out.println("PW : "+user_pw);
}

public void destory() {
System.out.println("destory 메서드 호출");
}
}



■ 에러 해결
 The servlet name already exists.
 -> WebContent > web.xml 에서 해당 servlet name을 포함하는 아래 파트를 삭제한다.
   <servlet>, <servlet-mapping> 
   
■ 자바스크립트로 서블릿에 요청하기(206)
이전에 <form> 태그에서 바로 서블릿으로 데이터를 전송했다. 하지만 실무에서는 비밀번호 입력 유무 체크처럼 전송할 데이터에 대해 유효성 검사를 하는 경우가 많은데, 
이런 기능은 자바스크립트로 구현하므로 이번에는 자바스크립트로 서블릿에 요청하는 방법을 알아보자.

[login2.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>

<meta charset="UTF-8">
<script type="text/javascript">
console.log("HI");

function 23() { // <form> 태그의 name 속성으로 해당 <form> 태그 객체 자체를 받아옴
console.log("fn_validate");

var frmLogin = document.frmLogin;
var user_id = frmLogin.user_id.value;  // input 태그의 name 속성으로 데이터 받아옴 
var user_pw = frmLogin.user_pw.value;

console.log(user_pw);
console.log(user_id);

if ((user_id.length == 0 || user_id=="") || (user_pw.length== 0 || user_pw == "")) {
alert("ID, PW 입력 필수");
} else {
frmLogin.method = "post";   // <form> 태그 전송 방식 post로 설정
frmLogin.action = "login5"; // action 속성을 서블릿 매핑 이름과 동일하게 login5로 설정
frmLogin.submit();          // 4크립트에서 서블릿으로 전송한다.
}
}
</script>

<body>
  <form name = "frmLogin" method="get" action="login4" encType="UTF-8">
 <!-- <form name = "frmLogin"                                   encType="UTF-8"> -->
 아이디 :  <input type="text" name ="user_id"><br>
 비밀번호 : <input type="password" name="user_pw"><br>
 
<input type="button" onClick="fn_validate()" value="로그인"> 
<input type="reset" value="다시입력">
<input type="hidden" name="user_address" value="서울시 성북구" /> 
<!-- <hidden> 태그를 이용해 화면에는 보이지 않게 하면서 값을 서블릿으로 전송한다.-->
 </form>
</body>
</html>

[LoginServlet4.java]
package sec03.ex03;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/login5")
public class LoginServlet5 extends HttpServlet {
public void init() {
System.out.println("init 메서드 호출");
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
String id = request.getParameter("user_id");
String pw = request.getParameter("user_pw");
String address = request.getParameter("user_address");
System.out.println("아이디   : " + id);
System.out.println("비밀번호 : " + pw);

String data = "<html>";
data += "<body>";
data += "아이디 : " + id;
data += "<br>";
data += "비밀번호 : " + pw;
data += "<br>";
data += "주소 : " + address;
data += "</html>";
data += "</body>";
out.print(data);
}

public void destroy() {
System.out.println("destroy 메서드 호출");
}
}


■ 서블릿에 로그인 요청 시 유효성 검사하기
ID 정상 입력 -> 로그인 성공 메시지 표시
ID 미입력 -> 다시 로그인 화면으로 이동 안내(url 연결)

[WebContent/test01/login.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
 <form name = "frmLogin" method="post" action="/pro06/loginTest" encType="UTF-8">
 WebContent > test01 > login.html<br>
 아이디 :  <input type="text" name ="user_id"><br>
 비밀번호 : <input type="password" name="user_pw"><br>
<input type="submit" value="로그인"> <input type="reset" value="다시입력">
 </form>
</body>
</html>

[src/sec04/ex01/LoginTest.java]
package sec04.ex01;

@WebServlet("/loginTest")
public class LoginTest extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

System.out.println("doPost 메서드 호출 doHandle 메서드로 요청을 이관한다.");

request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();

String id = request.getParameter("user_id");
String pw = request.getParameter("user_pw");

System.out.println("ID : "+ id);
System.out.println("PW : "+ pw);

if (id != null && id.length() != 0 ) {
out.print("<HTML><BODY>");
out.print(id+ " 님이 로그인하셨습니다.");
out.print("</BODY></HTML>");
} else {
out.print("<HTML>");
out.print("<BODY>");
out.print("ID 입력 필요합니다. <BR>");
out.print("<a href='http://localhost:8090/pro06/test01/login.html'> 로그인 창으로 이동 </a>");
out.print("</body>");
out.print("</html>");
}
}
}

■ 서블릿으로 요청될 때 구구단 출력으로 응답하기
[/pro06/WebContent/test01/gugu.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
<h1>출력할 구가단의 수를 지정해 주세요.</h1>
<form method="get" action="/pro06/guguTest">
출력할 구구단 :<input type=text name="dan"/><br>
<input type="submit" value="로그인"> <input type="reset" value="다시입력">
</form>
</body>
</html>

[/pro06/src/sec04/ex01/GuguTest.java]
package sec04.ex01;

public class GuguTest extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");

PrintWriter out = response.getWriter();
int dan = Integer.parseInt(request.getParameter("dan"));

out.print("<table border=1 width=800 align=center>");
out.print("<tr align=center bgcolor='#ffff66'>");
out.print("<td colspan=2>" +dan+" 단 출력 </td>" );
out.print("</tr>");

for  (int i=1; i<10; i++)
{
if( i%2 == 0)
{
out.print("<tr align=center bgcolor=#ACFA58>");

}else {
out.print("<tr align=center bgcolor=#81BEF7>");

}
out.print("<td width=200>");
out.print("<input type='radio' />"+i);
out.print("</td>");
out.print("<td width=200>");
out.print("<input type='checkbox' />"+i);
out.print("</td>");


out.print("<td width=400>");
out.print(dan+" * "+i);
out.print("<td width=400>");
out.print(i*dan);
out.print("</td>");
out.print("</tr>");
}
out.print("</table>");
}
}

■ 오라클 테이블 생성(226)
create table t_member(
    id varchar2(10) primary key,
    pwd varchar2(10),
    name varchar2(50),
    email varchar2(50),
    joinDate date default sysdate
);

INSERT INTO T_MEMBER VALUES('hong', '1212', '홍길동', 'hong@gmail.com', sysdate);
INSERT INTO T_MEMBER VALUES('lee', '1212', '이순신', 'lee@test.com', sysdate);
INSERT INTO T_MEMBER VALUES('kim', '1212', '김유신', 'kim@jweb.com', sysdate);
commit;

select * from t_member;


■ 서블릿의 비즈니스 로직 처리(224)
서블릿이 클라이언트로부터 요청을 받으면 그 요청에 대해 작업을 수행하는 것을 의미한다.
대부분은 DB 연동 관련 작업이지만, 그 외에 다른 서버와 연동해서 데이터를 얻는 작업도 수행합니다.(핵심)
ex) 회원 등록 요청 처리 / 로그인 요청 처리 / 상품 주문 처리 작업 등

1) 클라이언트로부터 요청 받기
2) 비즈니스 로직 처리하기(DB 연동, 외부 서버 접속)
3) 처리 결과 반환하기

클라이언트로부터 요청을 받으면 서블릿은 SQL문을 사용해 DB에 접근하여 작업을 하는데 이 과정에서
DAO와 VO 클래스가 사용된다.

순서 예시
1) 웹 브라우저가 서블릿에게 회원 정보를 요청한다.
2) MemberServlet은 요청을 받은 후 MemberDAO 객체를 생성하여 listMembers() 메서드를 호출한다.
3) listMembers()에서 다시 connDB() 메서드를 호출하여 DB와 연결 후 SQL문을 실행해 회원 정보를 조회한다.
4) 조회된 회원 정보를  MemberVO 속성에 세팅 후 다시 ArrayList에 저장한다.
5) ArrayList를 다시 메서드를 호출한 MemberServlet으로 반환한 후 ArrayList의 MemberVO를 차례대로 가져와
회원 정보를 HTML 태그의 문자열로 만든다.
6) 만들어진 HTML 태그를 웹 브라우저로 전송해서 회원 정보를 출력한다.

* 테이블의 구성
속성명 / 컬럼명 / 자료형 / 크기 / 유일키 여부 / Null 여부 / 키(기본,외래) / 

■ 오라클 DB 연동(229)
이클립스에서 만든 프로젝트에서 DB의 회원 정보를 조회하자.
프로젝트(NEW > Dynamic Web Project)와 DB 연동에 필요한 드라이버 ojdbc6.jar를 프로젝트의 /WebContent/WEB-INF/lib 폴더에 복사하여 붙여 넣는다.
* ojdbc6.jar : 웹에서 다운로드 ?
ojdbc6.jar 파일 자바 이클립스에 위치시키기
ojdbc 다운로드(오라클 설치시 jdbc파일이 저장되어 있음) > 경로 C:\oraclexe\app\oracle\product\11.2.0\server\jdbc\lib
> ojdbc6.jar 파일을 연동하고 싶은 프로젝트에 lib폴더를 만들어 그 안에 복사하기


■ PrepareStatement
Statement는 DBMS가 컴파일을 수행해야 하고, PrepareStatement는 컴파일 후 재사용해서 DBMS에 SQL query를 전송하므로 서비스의 성능을 높일 수 있다.


■ DataSource 이용한 DB 연동 (DataFactory)
prepareStatment, Statment를 활용해서 DB를 조회할 경우 필요할 때마다 DB에 연결하므로 시간이 더 소요된다는 단점이 있다.
쇼핑몰의 경우 동시에 수십 명, 수천 명가지 접속해서 상품 조회, 주문하기 등의 기능을 사용하므로 매번 DB와 연동해야 한다면 너무 비효율적이다.

DataSource를 활용할 경우 웹 애플리케이션이 실행됨과 동시에 커넥션풀 객체를 생성한 후 연동할 DB와 연결을 미리 해 둔다. 
그리고 필요할 때마다 연결해 놓은 상태(위 객체 활용)를 이용해 빠르게 DB와 연동하여 작업을 한다. 
이렇게 미리 DB와 연결된 상태를 유지하는 기술을 커넥션풀(ConnectionPool)이라고 한다.

톰캣 컨테이너는 자체적으로 ConnectionPool 기능을 제공한다. 
톰캣은 (톰캣 서버의) 설정 파일에 기록된 데이터베이스 정보를 이용해 미리 데이터베이스와 연결하여 ConnectionPool 객체를 톰캣 실행 시 생성한 후 애플리케이션이 DB와 연동할 일이 생기면 ConnectionPool 객체의 메서드를 호출해 빠르게 연동하여 작업한다.

■ JNDI(Java Naming and Directory Interface)
필요한 자원을 키/값 쌍으로 저장한 후 필요할 대 키를 이용해 값을 얻는 방법.
즉, 미리 접근할 자원에 키를 지정한 후 애플리케이션이 실행 중일 대 이 키를 이용해 자원에 접근해서 작업을 한다.


■ 톰캣의 DataSource 설정 및 사용 방법1


1) jdbc 드라이버(ojdbc6.jar)를 프로젝트의 /WebContent/WEB-INF/lib 폴더에 설치(파일 이동)
2) ConnectionPool 기능 관련 jar 파일을 /WebContent/WEB-INF/lib 폴더에 설치(파일 이동)
3) CATALINA_HOME/context.xml에 Connection 객체 생성 시 연결할 데이터베이스 정보를 JNDI로 설정
   (Project Explorer 영역 > Servers > context.xml)
4) DAO 클래스에서 DB와 연동 시 미리 설정한 JNDI라는 이름으로 DB와 연결해서 작업

실제 톰캣에서 ConnectionPool 기능을 사용하려면 이 기능을 제공하는 DBCP 라이브러리를 따로 내려 받아야 한다.
이 라이브러리 파일은 jar 압축 파일 형태로 제공되며, 다음 링크에서 tomcat-dbcp-7.0.30.zip 파일을 받은 후 압축을 푼다.
http://www.java2s.com/Code/Jar/t/Downloadtomcatdbcp7030jar.htm

3)에 명시할 DB 정보 - name="jdbc/oracle" 으로 DataSource에 접근한다. 
  (Servers 프로젝트 > context.xml)
    <Resource name="jdbc/oracle" 
     auth="Container" 
     type="javax.sql.DataSource" 
     driverClassName="oracle.jdbc.OracleDriver" 
     url="jdbc:oracle:thin:@localhost:1521:XE" 
     username="scott" 
     password="tiger" 
     initialSize="10" 
     maxActive="10" 
     maxIdle="10" 
     minIdle="10" 
     maxWait="3000" />
<!--
    auth : 컨테이너를 자원 관리자로 기술
    name : JDBC이름, 변경 가능
    driverClassName : JDBC 드라이버
    type : 웹에서 이 리소스를 사용할 때 DataSource로 리턴됨
    username : 접속계정
    password : 접속할 계정 비밀번호
    
    loginTimeout : 연결 끊어지는 시간
    maxActive : 최대 연결 가능한 Connection수 (기본 20개)
    maxIdle : Connection pool 유지를 위해 최대 대기 connection 숫자
    maxWait : 사용 가능한 커넥션이 없을 때 커넥션 회수를 기다리는 시간 (1000 = 1초)
    testOnBorrow : db에 test를 해볼 것인지
-->


출처: https://all-record.tistory.com/104 [세상의 모든 기록]

출처: https://matamong.tistory.com/entry/JAVA-DBCP-커넥션-풀-Connection-Pool [마타몽의 개발새발제발]


■ 톰캣의 DataSource 설정 및 사용 방법2
MemberDAO 클래스 수정
 - prepareStatment에서 사용한 connDB() 메서드는 주석 처리한다.
 - 생성자에서 -> 톰캣 실행 시 톰캣에서 미리 생성한 DataSource를 (context.xml의  Resource에서 설정) name 값인 jdbc/oracle을 이용해 접근/연결 후 받아 온다.
 - 서블릿에서 listMembers() 메서드를 호출하면 getConnection() 메섣를 호출하여 DataSource에 접근 후 DB와 연동한다.


[pro07>sec02.ex02>MemberServlet.java]
package sec02.ex02;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/member33")
public class MemberServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
       
    public MemberServlet() {
        super();
        // TODO Auto-generated constructor stub
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     doHandle(request, response);
    }
    

private void doHandle(HttpServletRequest request, HttpServletResponse response) 
throws IOException, ServletException
{
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
MemberDAO dao = new MemberDAO();
PrintWriter out = response.getWriter();
String command = request.getParameter("command");

if (command != null && command.equals("addMember"))
{
String _id = request.getParameter("id");
String _pwd = request.getParameter("pwd");
String _name = request.getParameter("name");
String _email = request.getParameter("email");
MemberVO vo = new MemberVO();
vo.setId(_id);
vo.setPwd(_pwd);
vo.setName(_name);
vo.setEmail(_email);
dao.addMember(vo);
} else if (command != null && command.equals("delMember"))
{
String id = request.getParameter("id");
dao.delMember(id);
}

List list = dao.listMembers();
out.print("<html><body>");
out.print("<table border=1><tr align='center' bgcolor='lightgreen'>");
out.print("<td>아이디</td><td>PW</td><td>이름</td><td>이메일</td><td>가입일</td><td>삭제</td></tr>");

for (int i = 0; i < list.size(); i++) {
MemberVO memberVO = (MemberVO) list.get(i);
String id = memberVO.getId();
String pwd = memberVO.getPwd();
String name = memberVO.getName();
String email = memberVO.getEmail();
Date joinDate = memberVO.getJoinDate();

out.print("<tr><td>"+id+"</td><td>"+pwd+"</td><td>"+name+"</td><td>"+email+"</td><td>"+
joinDate+"</td><td>"+"<a href='/pro07/member33?command=delMember&id="+id+"'>삭제</a></td></tr>");
}
out.print("</table></body></html>");
out.print("<a href='/pro07/memberForm.html'>새 회원 가입하기</a>");
}
}

[pro07>sec02.ex02>MemberDAO.java]
package sec02.ex02;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;



public class MemberDAO {

/*
// DataSource 사용 시 주석 처리 (246)
private static final String driver = "oracle.jdbc.driver.OracleDriver"; 
private static final String url = "jdbc:oracle:thin:@localhost:1521:XE";
private static final String user = "scott";
private static final String pwd = "tiger";
*/
private Connection con;
private PreparedStatement pstmt;
private DataSource dataFactory;



public MemberDAO() { // 생성자 

try {
Context ctx = new InitialContext(); // JNDI 에 접급하기 위해 기본 경로 지정 /comp/env
Context envContext = (Context) ctx.lookup("java:/comp/env");
dataFactory = (DataSource) envContext.lookup("jdbc/oracle"); 
// context.xml 의  name 값인 jdbc/oracle을 이용해 톰캣이 미리 연결한 DataSource를 받아 온다.
} catch (Exception e) {

}
}


public void delMember(String id) {
try {
Connection con = dataFactory.getConnection();
Statement stmt = con.createStatement();

String query = "delete from t_member" + " where id = ? ";
System.out.println(query);

pstmt = con.prepareStatement(query);
pstmt.setString(1,  id);;
pstmt.executeUpdate(); // insert, delete, update 문은 executeUpdate() 메서드를 호출합니다.

} catch (Exception e) {
e.printStackTrace();
}
}

public void addMember(MemberVO memberVO) {
{
try {
Connection con = dataFactory.getConnection();

String id = memberVO.getId();
String pwd = memberVO.getPwd();
String name = memberVO.getName();
String email = memberVO.getEmail();

String query = "insert into t_member";
query += " (id, pwd, name, email) ";
query += " values(?, ?, ?, ?) "; 

System.out.println("prepareStatement: " + query );

pstmt = con.prepareStatement(query);
pstmt.setString(1,  id);
pstmt.setString(2,  pwd);
pstmt.setString(3,  name);
pstmt.setString(4,  email);
pstmt.executeUpdate();
pstmt.close();

} catch (Exception e) {
e.printStackTrace();
}
}
}


public List listMembers() {
List list = new ArrayList();

try
{
// connDB();   // DataSource 사용 시 주석 처리 (246)
con = dataFactory.getConnection(); 
// context.xml의 DB 연동에 필요한 정보들 즉, DataSource를 이용해 DB에 연결한다.

String query = "select * from t_member";
System.out.println("prepareStatement: "+ query);
pstmt = con.prepareStatement(query);
ResultSet rs = pstmt.executeQuery();

while ( rs.next()) {
String id = rs.getString("id");
String pwd = rs.getString("pwd");
String name = rs.getString("name");
String email = rs.getString("email");
Date joinDate = rs.getDate("joinDate");
MemberVO vo = new MemberVO();
vo.setId(id);
vo.setPwd(pwd);
vo.setName(name);
vo.setEmail(email);
vo.setJoinDate(joinDate);
list.add(vo);


}
rs.close();
pstmt.close();
con.close();

} catch (Exception e) {
e.printStackTrace();
}
return list;
}

// private void connDB() { // DAO에서 직접 DB에 연결하는 기능은 주석 처리
// try {
// Class.forName(driver);
// System.out.println("Oracle 드라이버 로딩 성공");
// con = DriverManager.getConnection(url, user, pwd);
// System.out.println("Connection 생성 성공");
// } catch (Exception e) {
// e.printStackTrace();
// }
// }

}



[pro07>WebContent>memberForm.html]
<!DOCTYPE html>
<html><!-- http://localhost:8090/pro07/memberForm.html -->
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
<script src="mainScript.js" charset="utf-8"  defer></script>
<script type="text/javascript"  ">

/* 
function fn_sendMember() {

var frmMember = document.frmMember;
var id = frmMember.id.value;
var pwd = frmMember.pwd.value;
var name = frmMember.name.value;
var email = frmMember.email.value;

if (id.length == 0 || id == "") {
alert("id 미입력");
} else if (pwd.length == 0 || pwd == "") {
alert("pwd 미입력");
} else if (email.length == 0 || email == "") {
alert("email 미입력");
} else if (name.length == 0 || name == "") {
alert("name 미입력");
} else {
frmMember.method  = "psot"; // 전송 방법을 post로 지정
frmMember.action  = "member33"; // 서블릿 매핑 이름을 member3로 지정
frmMember.submit(); // 서블릿으로 전송
}
}
 */
</script>
</head>
<body>
<form name = "frmMember">
<table>
<tr>
<td>아이디</td>
<td><input type="text" name ="id"></td> <!-- 입력한 id를 서블릿으로 전송 -->
</tr>
<tr>
<td>패스워드</td>
<td><input type="text" name ="pwd"></td>
</tr>
<tr>
<td>이름</td>
<td><input type="text" name ="name"></td> 
</tr>
<tr>
<td>이메일</td>
<td><input type="text" name ="email"></td> 
</tr>
</table>
<input type="button" value="가입하기"  onclick="fn_sendMember()">
<input type="reset" value="다시 입력">
<input type="hidden" name="command" value="addMember" />
    <!-- hidden 태그를 이용해 서블릿에게 '회원 등록' 요청인 것을 알린다.-->
</form>
</body>
</html>

Comments