웹 어플리케이션 구성 요소


서블릿(Servlet)

 - 클라이언트 요청을 처리하는 역할을 합니다.


필터(Filter)

 - 요청 처리 전/후에 작업을 수행하는 역할을 합니다.


리스너(Listener) 

 - 특정 상태에 놓일 때 작업을 수행하는 역할을 합니다.




  먼저 Listener 리스너 예제를 보겠습니다.  



ServletContextListener?


  웹 애플리케이션이 시작되거나 종료되는 상태에 대한 작업을 수행합니다.



import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class Listener01 implements ServletContextListener {

  @Override
  public void contextInitialized(ServletContextEvent sce) {
    // ServletContext 객체가 생성되었을 때 호출된다.
    // 즉 웹 애플리케이션이 시작되었을 때 호출된다.
    System.out.println("====> Listener01.contextInitialized()");
  }

  @Override
  public void contextDestroyed(ServletContextEvent sce) {
    // ServletContext 객체가 소멸되기 직전에 호출된다.
    // 즉 웹 애플리케이션이 종료되기 직전에 호출된다.
    System.out.println("====> Listener01.contextDestroyed()");
  }

}



ServletRequestListener?


클라이언트로부터 요청이 들어오거나 서버에서 응답을 수행할 때 발생하는 

이벤트에 대한 작업을 수행합니다.



import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class Listener02 implements ServletRequestListener {
  
  @Override
  public void requestInitialized(ServletRequestEvent sce) {
    // ServletRequest 객체가 생성되었을 때 호출된다.
    // 즉 클라이언트로부터 요청이 들어 올 때 호출된다.
    System.out.println("====> Listener02.requestInitialized()");
  }

  @Override
  public void requestDestroyed(ServletRequestEvent sce) {
    // ServletRequest 객체가 소멸되기 직전에 호출된다.
    // 즉 클라이언트에게 응답했을 때 호출된다.
    System.out.println("====> Listener02.requestDestroyed()");
  }

}



ServletRequestAttributeListener?


ServletRequest 객체에 값을 저장, 제거 혹은 변경할 때 호출된다.


import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class Listener03 implements ServletRequestAttributeListener {
  
  @Override
  public void attributeAdded(ServletRequestAttributeEvent sce) {
    // ServletRequest 객체에 값을 저장할 때 호출된다.
    // 즉 req.setAttribute(...) 호출할 때
    System.out.printf("====> Listener03.attributeAdded(): %s=%s\n", 
        sce.getName(), sce.getValue());
  }

  @Override
  public void attributeRemoved(ServletRequestAttributeEvent sce) {
    // ServletRequest 객체에서 값을 제거할 때 호출된다.
    System.out.println("====> Listener03.attributeRemoved()");
  }
  
  @Override
  public void attributeReplaced(ServletRequestAttributeEvent sce) {
    // ServletRequest 객체에 저장된 값을 변경할 때 호출된다.
    System.out.println("====> Listener03.attributeReplaced()");
  }

}



서버에 add한 프로젝트 폴더의 경로를 찾아가 웹브라우저에서 url 주소를 치면

클라이언트의 요청을 인식하고 각 Listener에서 객체를 생성합니다.

이를 통해 어느 시점에서 Listener가 생성 - 소멸되는지 확인할 수 있습니다.





블로그 이미지

필로그래머

,

■ gradle 설정파일 준비하기

# gradle 빌드 도구를 실행할 때 필요한 설정 파일을 준비합니다.


cmd 명령창에서 프로젝트 폴더 경로를 찾아가 다음의 명령어를 줍니다.

> gradle init

build.gradle 등의 파일이 생성됩니다. 이제 build.gradle 설정 파일을 편집하면 됩니다.



■ gradle 사용법

## gradle에서 사용할 수 있는 도구를 확인합니다.


cmd 명령창에서 프로젝트 폴더 경로를 찾아가 다음의 명령어를 줍니다.

> gradle tasks



## gradle에 자바 빌드 도구 플러그인을 추가합니다.

[bulid.gradle 파일 txt로 열기]


apply plugin: 'java'


cmd 명령창에서 프로젝트 폴더 경로를 찾아가 다음의 명령어

> gradle tasks --all 를 주면 자바 빌드 도구 플러그인이 추가된 것을 확인할 수 있습니다.




## gradle 자바 빌드 도구 사용하기
[자바 소스 컴파일하기]

src > main > java > example > Hello.java 파일을 만들어 봅니다.


> gradle [명령(task) 이름]
> gradle compileJava

* (src/main/java/ 폴더의 모든 자바 소스 파일을 컴파일 한 후 그 결과를 build/classes/main/ 폴더에 저장한다.)

그러면 build > classes > main > example 폴더에서 컴파일된 Hello.class 파일이 
생성된 것을 확인할 수 있습니다.
실행 결과 : build/classes/main/example/Hello.class 파일 생성


[빌드 관련 모든 폴더와 파일 제거하기]
> gradle clean

실행 결과 : build 폴더가 제거됩니다.



[resources 폴더 파일을 빌드 폴더에 복사하기]

> gradle processResources


* (src/main/resources/ 폴더의 모든 파일이 build/resources/main/ 폴더에 복사된다.)


실행 결과 : build/resources/main/beans.properties  파일 생성


[compileJava 기능 + proccessResources 기능 = classes]

> gradle classes


실행 결과 : CompileJava와 processResources 명령을 모두 실행한 결과와 같다.


[JAR 배포 파일 만들기]
> gradle jar

실행 결과 : classes 명령을 실행한 후 .jar 파일을 생성한다.



[빌드 전 과정을 실행하기]

> gradle build


실행 결과 : 컴파일 ---> 단위 테스트 ---> JAR 파일 생성 등 빌드 전과정을 실행한다.



Java plugin - tasks





## gradle에 이클립스 빌드 도구 플러그인을 추가합니다.

[bulid.gradle 파일 txt로 열기]


apply plugin: 'eclipse'



[이클립스 프로젝트 설정 파일 삭제하기]
> gradle cleanEclipseProject

실행 결과 : .project 파일만 제거한다.



[이클립스 관련 모든 설정 파일 만들기]
> gradle eclipse

실행 결과 : 
플러그인이 있다면 그 플러그인과 관련된 설정 파일도 만든다.
만약 따로 플러그인이 등록되지 않았다면 .project 파일만 생성될 것이다,.



[이클립스 관련 모든 설정 파일 삭제하기]
> gradle cleanEclipse

실행 결과 : 
플러그인이 있다면 그 플러그인과 관련된 설정 파일도 제거한다.
만약 따로 플러그인이 등록되지 않았다면 .project 파일만 삭제될 것이다,.



## 웹 프로젝트 만들기

[웹 관련 플러그인 등록]

[build.gradle]


apply plugin: 'eclipse-wtp'


 > 이클립스 설정 파일을 다루는 도구

 > 웹 관련 설정 파일을 다루는 도구가 들어 있는 플러그인


apply plugin: 'war'


 > WebArchive 배포 파일을 만드는 도구가 들어 있는 플러그인




블로그 이미지

필로그래머

,

켓의 디렉토리 구조


─────



 > bin
- 톰캣 서버 실행과 관련된 쉘 스크립트 파일 (.sh, bat 등)을 모아둔 곳입니다.

 > conf
- 톰캣 서버를 실행할 때 참조할 설정 파일을 모아둔 곳입니다.

 > lib
- 톰캣 서버를 구성하는 자바 클래스(classs) 라이브러리들을 모아둔 곳입니다.
 
  > logs
- 톰캣 서버를 실행하는 동안 실행 정보와 오류 정보를 기록한 파일을 모아둔 곳입니다.
  client로부터 get 요청이 들어왔는지 post 요청이 들어왔는지도 알 수 있고 
  마지막으로 요청한 정보가 무엇인지, ip address는 어떻게 되는지 등도 
  알 수 있습니다.

  > temp

- 톰캣 서버가 실행하는 동안 임시 데이터를 보관하는 디렉토리입니다.


  > work

- 톰캣 서버가 JSP를 실행할 때 그 중간 파일을 보관하는 곳입니다.


  > webapps

- 웹 애플리케이션을 모아둔 곳입니다.



개발용 웹 애플리케이션 실행 환경

 $workspace/Servers/localhost.../*.xml


- 톰캣 서버의 설정 파일을 복사해 옵니다.
- $tomcat_home/conf 에서 설정파일을 복사해서 생성된 파일입니다.
- 여기서 편집한 설정 파일은 실행환경 임시폴더인 아래 폴더에 저장이 됩니다.

$workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0


또한 

 $workspace/.metadata/.plugins/org.eclipse.wst.server.core/ 폴더에는  
 이 실행환경에서만 사용할 폴더들이 생성이 됩니다.

>logs
>temp
>work
>webapps
>wtpwebapps > 생성한 웹프로젝트 폴더(ex : web01)


그리고 생성한 웹프로젝트 폴더(ex: web01)에는 $workspace/web01/src/WebContent/에 있는 모든 폴더와 파일이 복사가 되구요,  
> web01 > WEB-INF 폴더 안에는 $workspace/web01/src/WebContent/WEB-INF에 있는 모든 폴더와 파일이 복사가 됩니다.

$workspace/web01/bin/ 에는 $workspace/web01/src/에서 컴파일된 *.class 파일이
저장되고 *.properies *.xml 등의 파일이 복사되어 저장됩니다.




─────

톰캣의 디렉토리 구조와 이클립스 웹 프로젝트 폴더 구조





# .class 파일은 java 파일에서 블록 단위의 class {} 마다 생성이 됩니다.  

## 이클립스에는 버그가 있다. 이클립스는 톰캣 서버를 실행시키면 웹 애플리케이션
    프로젝트에서 실행환경 임시폴더(tmp0)로 파일들을 복사 -> 배치 (deploy)시키는데
    자꾸 이전 파일이 로딩되는 경우가 있는 것이다. 이럴 땐 Servers 창에서 서버를
    중단시키고 삭제하고 다시 추가하면 된다. 그러면 다시 실행환경 임시폴더가 생성될 것이다. 
    다시 프로젝트 폴더를 만드는 바보 같은 행동은 하지 말자!
    

 이클립스 웹 어플리케이션 프로젝트 폴더 구조


 이클립스에서 웹 프로젝트를 다룰 때의 폴더 구조입니다.
 IntelliJ 혹은 NetBeans 등 다른 IDE를 사용할 때는 폴더 구조가 다를 수 있습니다.

$workspace/web01
 > src
- 자바 소스 파일을 두는 곳입니다.
 > WebContent : HTML, CSS, JavaScript, GIT 등 정적 웹 자원(static resource)을 두는 곳입니다. JSP 파일도 이 폴더 안에 두는 것이 일반적입니다.
> WEB-INF : 웹 애플리케이션 정보 파일을 두는 곳입니다.
> classes : 자바 클래스 파일(.class)을 두는 곳입니다.
> lib : 자바 라이브러리 파일(.jar)을 두는 곳입니다.
> web.xml : 웹 애플리케이션 설정 파일입니다.



■ 웹 어플리케이션 배치 폴더구조

$deployment_dir : HTML, CSS, JavaScript, GIT 등 정적 웹 자원(static resource)을 두는 곳입니다. JSP 파일도 이 폴더 안에 두는 것이 일반적입니다.

 > WEB-INF : 웹 애플리케이션 정보 파일을 두는 곳입니다.

> classes : 자바 클래스 파일(.class)을 두는 곳입니다.
> lib : 자바 라이브러리 파일(.jar)을 두는 곳입니다.
> web.xml : 웹 애플리케이션 설정 파일입니다.





블로그 이미지

필로그래머

,

[Java] 자바 이클립스 - 톰캣(tomcat)  or 웹 어플리케이션 서버의 실행 환경을 구축해보자! 여기선 톰캣을 예로 들겠다.



이클립스에 톰캣 실행환경 구축하는 방법

 

1) tomcat.apache.org 사이트에 들어가 아래 버전(현재는 8.5.15 - 안정성이 높다)의 zip 파일을 로컬 디스크 (C:)에 다운 받고 압축을 푼다.




2) 톰캣 서버(or 웹 애플리케이션 서버)가 설치된 폴더를 등록한다


Eclipse 실행 - 메뉴창 

 > Window > Preferences > Server 노드 > Runtime Environments > add > Apache  > 다운받은 톰캣 버전을 선택!


 > Browse > apache-tomcat-8.5.15 > ok




3) 톰캣(or 웹 애플리케이션 서버) 실행 환경을 구축해본다

Window > show view> Servers > 창에서 오른쪽 클릭 > new > 다운받은 톰캣 버전 선택! > Finish

 * 웹 애플리케이션 서버 실행 환경은, 이클립스에 설정된 서버에 대해서만 실행

   환경을 구축할 수 잇다.



4) 웹 애플리케이션 서버 실행 환경 시작하기

Servers > 서버실행환경 선택 > start 클릭

 - 웹브라우저의 url 주소에 localhost:8080 을 치면 톰캣 서버가 실행되고 있는 것을

   알 수 있다. (404 error가 뜬다는 것은 서버가 '연결'되었다는 의미이다.)



5) 실행환경이 구축된 폴더 확인하기

workspace 폴더 (ex: c:/workspace)/.metadata/.plugins

/org.eclipse.wst.server.core/tmp0    -----> tmp0은 임시폴더이다!! 이클립스를 제외한

편집기에서 바꾸면 절대 안된다. 이클립스에서 tmp0/server.xml 파일의 포트 번호(port)를 변경할 수 있다.











블로그 이미지

필로그래머

,

[Java] 자바 이클립스 서블릿 (Servlet) 만들기


서블릿 만들기


 1) 서블릿을 만드는 데 필요한 라이브러리를 가져온다.

- mvnrepository.com 에서 'servlet-api'를 검색한다.

- 3.1.x 버전의 gradle 라이브러리 정보를 복사한다.



- 프로젝트 폴더에 있는 build.gradle 파일의 dependencies {} 블록에 추가한다.





- 터미널(명령창)에서 'gradle eclipse'를 실행하여 

  이클립스 설정 파일 (.classpath, .project)을 갱신한다.




- 이클립스 설정 파일을 갱신하였다면, 프로젝트를 'Refresh(F5)' 해야 한다.



2) javax.servlet.Servlet 규칙에 따라 클래스를 작성해본다.


임의의 HelloServlet 클래스 예제


import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class HelloServlet implements Servlet {
 
  public HelloServlet() {
    System.out.println("HelloServlet()");
  }
 
  @Override
  public void init(ServletConfig config) throws ServletException {
    System.out.println("init()");
  }
  @Override
  public ServletConfig getServletConfig() {
    System.out.println("getServletConfig()");
    return null;
  }
  @Override
  public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    System.out.println("service()");

res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println("Hello, world");
   
  }
  @Override
  public String getServletInfo() {
    System.out.println("getServletInfo()");
    return null;
  }
  @Override
  public void destroy() {
    System.out.println("destroy()");
   
  }
}




서블릿 배포하기


 1) 톰캣 서버에 웹 애플리케이션 폴더를 만든다.

=> $톰캣홈/webapps/만들고싶은폴더명/

 2) 클래스 파일을 배치한다.

=> $톰캣홈/webapps/만든폴더명/WEB-INF/classes/폴더명/HelloServlet.class

 3) 서블릿 정보를 DD 파일에 등록한다.

=> $톰캣홈/webapps/ROOT/WEB-INF/web.xml 파일을 복사한다.

=> $톰캣홈/webapps/만들었던폴더명/WEB-INF/ 아래에 위에서 복사한 파일을 옮긴 후 이클립스로 다음과 같이 파일을 편집한다.


----------------------------------------------------------------------------------------

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

  version="3.1"

  metadata-complete="true">


  <display-name>Welcome to Tomcat</display-name>

  <description>

     Welcome to Tomcat

  </description>

  

  <!-- 서블릿의 클래스 정보를 등록한다. -->

  <servlet>

    <servlet-name>hello</servlet-name> <!-- 서블릿 클래스 별명 -->

    <servlet-class>step24.HelloServlet</servlet-class> <!-- 서블렛 경로 설정 -->

  </servlet>

  

  <!-- HelloServlet에 URL을 부여한다. -->

  <servlet-mapping>

    <servlet-name>hello</servlet-name>

    <url-pattern>/hi</url-pattern> <!-- 반드시 /로 시작해야 한다. -->

  </servlet-mapping>

</web-app>       

----------------------------------------------------------------------------------------


  4) C:\apache-tomcat-8.5.11\bin 경로에서 startup.bat 파일을 실행시켜 톰캣을 켠다.

  5) 웹브라우저를 켜고 url 주소에 "http://www.localhost:8080/test/hi" 를 입력한다.
  6) 화면 창에 out.println("Hello, world"); 결과가 뜬 것을 확인한다.

 맥에서 설정 정보 바꿔주기
     bin./logs 
     > cat catalina.out            : 서블릿 생성자가 호출될 때 메서드를 실행시켜준다.
     > tail -f catalina.out    : 웹브라우저로부터의 요청 정보가 바뀔 때마다 
  위 메서드를 지속적으로 호출시켜줄 수 있도록 설정한다.







블로그 이미지

필로그래머

,

[Java] 자바 이클립스 JDBC 5. Statement와 PreparedStatement 비교



Statement와 PreparedStatement의 차이

1) 보안

    Statement: 

    - 사용자가 입력한 값을 가지고 SQL문을 만들기 때문에 보안이 취약하다.

    - 즉 악의적으로 SQL의 일부를 포함하는 값을 입력할 수 있다.

    PreparedStatement

    - ?(in-parameter) 자리에 임의의 SQL문을 삽입할 수 없다.

    - 삽입해봐야 단순 문자열로 취급한다.

    - 그래서 보안 문제를 발생시키지 않는다.

2) 바이너리 데이터

    Statement

    - 문자열로 SQL문을 만들기 때문에 바이너리 데이터를 삽입할 수 없다.

    PreparedStatement

    - ?(in-parameter) 자리에 바이너리 데이터를 삽입할 수 있다.

3) 코드의 가독성

    Statement

    - + 연산자를 사용하여 컬럼의 값을 연결하는 식으로 문자열을 만들기 때문에,

      컬럼의 개수가 많으면 문자열을 붙이는 코드도 산만해지고 복잡해진다.

    PreparedStatement

    - setXxx()를 사용하여 값을 넣기 때문에 코드가 간결하다.

4) 실행속도

    서버에 SQL문을 전달하기 전에 서버에서 원하는 형식으로 변환하여 보낸다.

    단순히 SQL을 한 번만 실행한다면 Statement나 PreparedStatement 둘 다 속도가 같다.

    그러나 같은 SQL문을 값을 바꿔가면서 여러 번 실행할 경우,

    PreparedStatement는 SQL문을 컴파일한 후 값만 넣어서 보내기 때문에 속도가 빠르다.

   Statement는 executeXxx()를 실행할 때마다 SQL문을 매번 컴파일하기 때문에 속도가 느리다.

      



JDBC 프로그래밍: PreparedStatement 객체 사용 후

 - 더하기 연산자를 사용하여 SQL문을 만드는 대신,

   in-parameter '?' 를 사용하여 SQL 문을 만든다.



JDBC SQL에서 PreparedStatement의 사용 예제


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class Test05_2 {
  public static void main(String[] args) throws Exception {
    // 원래는 값을 JVM 아규먼트나 프로그램 아규먼트 등 외부로부터 받아야 하지만,
    // 테스트를 원활하게 하기 위해서 변수에 값을 담아 놓자!
   
    String jdbcDriver = "com.mysql.jdbc.Driver";
    String jdbcUrl = "jdbc:mysql://localhost:3306/데이터베이스명";
    String jdbcUsername = "유저명";
    String jdbcPassword = "1111";
   
    String name = "홍길동102";
    String email = "hong102@test.com";
    String tel = "1111-1111";
    String password = "1111";
    try {
      Class.forName(jdbcDriver);
    } catch (Exception e) {
      e.printStackTrace();
    }
   
    try (
      Connection con = DriverManager.getConnection(jdbcUrl, jdbcUsername,jdbcPassword);
       
      // SQL 문을 미리 작성해둔다. 값(문자, 숫자 상관없이)이 들어갈 자리는 ?을 사용하여 비워둔다.
      // 이때 ?를 in-parameter라 부른다.
      // preparedStatement()의 리턴 값은 java.sql.PreparedStatement의 구현체이다.
    PreparedStatement stmt = con.prepareStatement("insert into memb(name, tel, email, pwd) values(?,?,?,password(?))");
    ) {
     
    // SQL문을 서버에 보내기 전에 in-parameter 자리에 값을 채운다.
    // 컬럼의 타입에 따라 호출하는 메서드가 다르다.
    // => setXxx(컬럼인덱스, 값)
    // => 컬럼 인덱스는 1부터 시작한다.
    // 빈자리(in-parameter)에 값을 넣을 때 컬럼의 순서를 지킬 필요는 없다.
    // 그렇다고 굳이 임의 순서대로 넣는 것은 코드의 이해도를 떨어뜨린다.
    stmt.setString(1, name);
    stmt.setString(2,  tel);
    stmt.setString(3, email);
    stmt. setString(4, password);
    // 이렇게 setXxx()를 호출하여 SQL 문을 완성하면,
    // + 연산자를 사용하여 SQL문을 완성하는 방법보다 편리하고 가독성이 좋다.
   
    // 위에서 SQL문을 준비하고 값을 채웠기 때문에 여기에서는 그냥 실행만 하면 된다.
   
    int count = stmt.executeUpdate("insert into memb(name, tel, email, pwd) values('" +
      name + "', '"+
      tel + "', '"+
      email + "', password('" +
      password + "'))");
     
    System.out.println(count);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}



블로그 이미지

필로그래머

,