Servlet & JSP를 접하기 전에
JAVA WEB 개발 환경 구축에 대해 먼저 살펴보겠다.
WEB이란 가장 많이 사용하는 Client/Sever Program의 일종이다.
⇒ Client/Server는 누가 요구하고 응답을 주냐 에 따라 용도 상으로 결정된다.
💡 Client Sever의 개수에 따른 명칭
2개 ⇒ Two tier Architecture
3개 ⇒ Three tier Architecture
4개 이상 ⇒ n tier Architecture
Client / Server Program의 3가지 요소
Client : Request를 하는 Application
Browser (다양한 제품)
- WEB이라는 C/S에서 URL을 통해 Request해 주는 Client APP
- HTML을 Parsing해서 내용을 Display해 주는 역할
Server : Response를 하는 Application
- Web Server (다양한 Web Sever중 무료로 제공해 주는 Apache 사용)
- WEB 이라는 C/S에서 resource를 관리해주고 response해 주는 Server App
[Apache의 htdocs Directory에서 Resource 관리]
💡 Apache License의 기본
- Open Source이다.
- 기능을 추가하여 상용화 해도 좋지만, 참조 문헌 작성 필히 해라.
APP / BEAN /
APLET → SERVLET[Aplet을 보완한 기술]← JSP [Servlet을 Extends한 기술]
Web Server ⇒ Apache 이용
웹 브라우저와 같은 클라이언트로부터 HTTP 요청을 받아들이고, HTML 문서와 같은 웹 페이지를 반환하는 컴퓨터 프로그램
URL을 통해 화면을 제공해 주며, 80번 port를 사용한다.
Java class의 일종으로, java.io.* API를 이용하여 Browser에게 html을 주는 기술
Servlet을 Extends한 것
WAS는 WebServer, Web Container[Servlet Container, JSP Container 등]을 기본적으로
제공해주는 Program의 일종이다.
우리가 Web Container의 Spec을 알고 있다면 WAS의 종류와 상관없이 해당 Spec을 지원하는 버전 어떤 것이라도 사용 가능하다는 Polymorphism이 적용된다.
추가적으로 JRE를 자체적으로 가지고 있는 WAS가 있는 반면, 우리가 사용하는 WAS인 TOMCAT은 JRE를 제공하지 않아 JRE Platform을 따로 설치해 줘야 한다. 긍정적인 시점에서 본다면 JRE가 없기 때문에 프로그램이 가볍다고 볼 수 있다.
WAS마다 resource의 위치가 다르면 종속적이기 때문에 각종 resource의 위치를 폴더로서 표준화
위와 같은 표준화 된 폴더 구조는 .war[Web Archaive]라는 확장자로 압축하여 동일한 spec을 지원하는 WAS라면 copy&paste로 WAS에서 ~.war의 내용을 읽어올 수 있다.
또한 표준화 된 폴더 구조의 ClassPath Root는 Classes이다. 이러한 Classes 내에서 package로서의 구조로 구성할 수 있다.
💡 Enterprise Edition의 설치 없이 어떻게 우리가 Servlet과 JSP API를 이용 가능한 것일까?
- Java 진영에서 Standard Edition에 추가적인 기능이 더해진 Enterprise Edition의 버전에 따라 Servlet과 JSP의 spec이 결정되며, was에서 필요로 하는 spec만 알고 있다면 was에 의존적이지 않고 어떤 제조사의 WAS던지 사용할 수 있다는 polymorphism을 지원한다.
- 우리가 이용하는 TOMCAT의 경우 spec에 맞는 servlet. jar를 제공해 준다.
XML [EXtensible Markup Language]
<servlet> //==> 서블릿 객체 설정
<servlet-name> 객체의 이름 </servlet-name>
<servlet-class> 객체를 생성할 클래스 </servlet-class>
</servlet>
<servlet-mapping>
<servlet-name> 이름 </servlet-name> //==> 일할 서블릿 객체의 이름
<url-pattern>패턴</url-pattern> //==> 클라이언트가 요청할 url 패턴
</servlet-mapping>
TOMCAT에서는 exe파일, window의 비트 별 zip file을 제공한다.
exe파일을 이용한 wizard 세팅도 가능하지만,
zip파일로서 제공하는 tomcat_home의 bin folder 하위에 있는 startup.dat와 shutdown.dat로 웹 서버 구동과 멈춤이 가능하다.
또한 startup.dat가 실행되면서 많은 개발자들이 설정하는 java_home을 찾아 JRE path를 자동 인식한다. 그러나 지원하는 JRE 버전과 java_home의 path의 버전이 다르다면 Error가 발생한다.
💡 apache-tomcat.exe로 설치 시
- ORACLE과 TOMCAT을 함께 사용할 때 PORT의 충돌에 유의하자!
Oracle을 설치하면 8080 port를 사용하는 web server가 설치된다.- TOMCAT 또한 web server를 포함하고 있고 8080 port를 이용하기 때문에 그대로 설치를 한다면 충돌의 오류가 발생한다.
- Oracle DBMS나 Tomcat의 web server port를 변경해 주어야 한다.
Tomcat의 port 변경은 간단하지만, Oracle의 경우 아래와 같은 명령어로 변경해 준다.
EXEC DBMS_XDB.SETHTTPPORT(포트번호)- 추가적으로 JRE의 PATH를 설정해 주어야 한다.
Container의 역할
servlet을 instance 생성하여 method()를 실행해 주는 역할을 한다.
모든 method를 인식할 수 있는 것은 아니다. 우리가 직접 생성한 instance의 내용을 알고 우리가 호출 하는 것이 아닌 Container라는 App에서 처리하는 것이기 때문에Container가 인식할 수 있는 약속에 의한 method()를 정의해 주어야 한다.
약속에 의한 method()의 구조는 아래와 같다.
여기서 Java의 장점 몇 가지를 살펴볼 수 있다.
client로 부터 request가 들어오면 process로 매번 대응하는 것이 아닌, thread가 호출된다.
thread로 대응하기 때문에 start()로 인하여 run()이 호출되며 init() runnable에 올라간다.
모든 작업이 끝나면 소멸하기 때문에 위와 같이 단 한번 호출되어 효율적이다.
Thread의 동작 방식은 절차 은닉이다.
할당된 Thread는 doGet() 혹은 doPost() method를 실행한다.
해당 method의 값이 Return되면 destroy() method를 실행한다.
Client의 매 요청 마다 doGet() 혹은 doPost() method를 실행한다는 것은 시스템의 overhead가 발생 가능성이 있음을 의미한다. overhead 발생을 방지하기 위해 Thread Pool을 이용한다.
우선 HTTP와 HTTPS의 차이점을 알아보자.
Client가 서버로 request하는 방식에는 크게 두 가지가 있다.
GET 방식
client가 get방식의 QueryString으로 request 할 경우 Server에서 header에서 encoding된 정보를 URI 후위에 붙어가기 때문에 주소 창에서 있는 그대로 확인 할 수 있다.
Server로 reqeust하는 default방식은 get방식이다.(주소창 입력으로 접속)
POST 방식
client가 post방식으로 request할 경우 Server에서 header에는 URI에 대한 정보 만을 인식하고, body에서 encoding된 정보를 처리해 주기 때문에 주소 창에서는 URI에 대한 정보만을 확인할 수 있다. 즉, 가시적인 보안을 지원한다.
Header에는 data의 limit이 있지만, body에는 limit이 없어 data량이 많은 경우 post방식을 사용하는 것이 유리하다.
또한 대량 트래픽으로 인한 서버 다운의 우려가 있어 한계를 지정해 주어야 한다.
servlet class는 servlet container에서 실행해 주기 때문에 약속된 method name인 init(), service(), destroy()와 동일한 naming을 해주어야 하지만, 확장된 class와 interface를 hierarchy를 타고 올라가면서 해당 method를 호출할 수 있게 된다.
Overriding을 하여 우리가 원하는 방식으로 재정의 가능하나 service의 경우 재정의를 하지 않으면 아래의 방식으로 동작을 한다.
HttpServlet
Http에 특화된 abstract class으로,
OutputStream os = res.getOutputStream();
=> HttpServletResponse의 method인 getOutputStream()의 return type이
ServletOutputStream인데, 상위 클래스인 OutputStream으로 묵시적 형변환이 일어나 가능
response쪽으로 값을 처리해 주지만, 한글이 깨진다는 단점이 존재한다.
============================개선===========================
Writer wr = new OutputStreamWriter(res.getOutputStream());
한글은 사용 가능하지만, 한글자 씩 처리하기 때문에 비효율 적이다.
============================개선===========================
PrintWriter pw
= new PrintWriter(new OutputStreamWriter(res.getOutputStream()));
=> PrintWriter pw = res.getWriter();
getWriter()이 client에 문자 text를 전송해 주는 PrintWriter object를 return해 주고,
PrintWriter가 setCharacterEncoding()을 통해 encoding된 문자를
getCharacterEncoding()을 return해 준다.
💡 HTML에 대한 Java Servlet
1. URL로 사용을 하게 되면 IP에 종속적이게 되어 URI를 사용한다.
2. URI를 사용하면 FIX된 구조로 Context-Root에 종속적이게 된다.
3. Relative URI를 사용 하므로서 dynamic하게 사용한다.
2 Layered Architecture
Login → Presentation / Business Logic
Login Bean → View / Business Logic
Login Bean (VO/DAO) → View / Model (Value Object/ Data Access Object)
Login Bean Init Param → View/Model (VO/DAO/Web.xml)
Login Bean Pool → View/Model (VO/DAO → connection pool)
⇒ Connection에 loading 시간이 오래 걸린다.
⇒ Connection instance 생성하는데 오래 걸린다.
⇒ Connection Pool에 Connection이 필요한 만큼 미리 할당하고 Close하지 않는다.
⇒ Java진영에서 Spec화 하여 제공하는 Interface인 Data Source를 WAS에 등록하여 사용
[DBCP : Data Base Connection Pool]
Request 하고 Response하는 순간 연결이 끊어져 부하가 거의 없다는 장점이 있다.
시대가 변하면서 상태 유지하는 기술 즉, 일정 시간 동안 동작에 대한 유지 기술을 필요로 한다.
쿠키와 세션을 사용하는 이유
HTTP 프로토콜의 특징이자 약점을 보완하기 위해서 사용된다.
Connectionless 프로토콜 (비연결지향)
클라이언트가 서버에 요청(Request)을 했을 때,그 요청에 맞는 응답(Response)을 보낸 후 연결을 끊는 처리방식이다.
Stateless 프로토콜 (상태정보 유지 안함)클라이언트와 첫번째 통신에서 데이터를 주고 받았다 해도,두번째 통신에서 이전 데이터를 유지하지 않는다.
클라이언트의 상태 정보를 가지지 않는 서버 처리 방식이다.
HTTP의 일종으로 사용자가 어떠한 웹 사이트를 방문할 경우,그 사이트가 사용하고 있는 서버에서 사용자의 컴퓨터에 저장하는 작은 기록 정보 파일이다.
HTTP에서 클라이언트의 상태 정보를 클라이언트의 PC에 저장하였다가필요시 정보를 참조하거나 재사용할 수 있다.
import javax.servlet.httml.Cookie;
Cookie cookie = new Cookie("name", URLEncoder.encode("홍길동",encoding);
cookie.setMaxAge(60); //cookie 유효기간(초)
cookie.setMaxAge(-1); //cookie memory 저장
cookie.setMaxAge(0); //cookie 삭제
resquest.addCookie(cookie); //Client response instance를 사용 cookie전송
Cookie[] cookies = request.getCookies(); //cookie의 name=value 처리 변수
일정 시간동안 같은 사용자(브라우저)로부터 들어오는일련의 요구를 하나의 상태로 보고, 그 상태를 일정하게 유지시키는 기술이다.
여기서 일정 시간은 방문자가 웹 브라우저를 통해웹 서버에 접속한 시점으로부터 웹 브라우저를 종료하여 연결을 끝내는 시점을 말한다.즉, 방문자가 웹 서버에 접속해 있는 상태를 하나의 단위로 보고 그것을 세션이라고 한다.
화면이 이동해도 로그인이 풀리지 않고 로그아웃하기 전까지 유지
import javax.servlet.http.HttpSession;
HttpSession session = request.getSession(); // session 공간 생성
session.setAttribute("name",value); // name=value 형식으로 저장
session.getAttribute("name"); //저장된 name에 해당하는 value 가져옴
💡 Q. 세션을 쓰면되는데 굳이 쿠키를 사용하는 이유?
- A. 세션이 쿠키에 비해 보안도 높은 편이나 쿠키를 사용하는 이유는세션은 서버에 저장되고, 서버자원을 사용하기 때문에 사용자가 많을 경우 소모되는 자원이 상당하다.이러한 자원관리 차원에서 쿠키와 세션을 적절한 요소 및 기능에 병행 사용하여,서버 자원의 낭비를 방지하며 웹사이트의 속도를 높일 수 있다.
init() method가 한번 호출 되기 때문에 filed variable이 존재할 경우 Service에 해당 variable이 있을 경우 thread를 통해 계속해서 호출 될 때 동기화 문제가 발생한다.
SingleThreadModel [ Marker Interface / deprecated]
동기화 문제가 발생할 것 같으면 instance를 하나 더 만들어 주어 해결 가능하지만, Java의 장점을 포기하는 방법이다.
Service에 synchronize를 거는 방법이 있으나, Synchronized pool의 overhead 발생 우려
filed를 작성하지 않는 방법이 있으나, service에 의존적인 개발
결론적으로 해결 방법은 없으나, 방문자 수 카운트 등에 이용할 수 있다.
그러나 서버 재 구동 시 초기화 되기 때문에 file system에 저장하여 사용해야 한다.
bean에서 발생하는 exception은 was에서 구동하기 때문에 was container로 다 던지지만, was container에서는 처리를 하지 않는다. 따라서, web.xml에 선언 적으로 exception을 정의하여 해당 exception 발생 시, 화면을 출력하도록 구성 가능하다.