본문 바로가기
네이버클라우드/JAVA 웹 프로그래밍

JAVA 62일차 (2023-08-21) 자바 프로그래밍_Servlet 정리_ex03~ex04

by prometedor 2023. 8. 21.
- 서블릿 프로그래밍
  - 서블릿, 필터, 리스너 컴포넌트 만들고 배치하는 방법
  - GET/POST/PUT 등 요청 메서드를 구분하는 방법
  - 요청 파라미터 값을 꺼내는 방법
  - multipart/form-data 로 전송된 파라미터 값을 다루는 방법
  - 썸네일 이미지를 생성하는 방법
  - HttpServlet 클래스의 역할 이해

 

Servlet 정리

servlet-app 프로젝트

ㄴ eomcs.servlet.ex03 패키지 생성

=>

ㄴ ㄴ /Users/사용자명/git/eomcs-java/eomcs-servlet/app/src/main/java/com/eomcs/web/ex03 에 존재하는 파일 모두 복사

=>

ㄴ eomcs.servlet.ex03 패키지에 복사한 파일 인텔리제이에서 붙여넣기

=>

클라이언트로 출력하기

     출력 스트림을 꺼내기 전에
     출력할 때 사용할 문자표(charset)를 지정하지 않으면
     리턴 받은 출력 스트림은 기본 문자표 ISO-8859-1 을 사용한다.
     즉 자바의 유니코드 문자를 ISO-8859-1 문자표에 따라 변환하여 출력한다.
     자바(Unicode2;UTF-16) ===> 출력문자(ISO-8859-1)

Servlet01.java (ex03)

// 클라이언트로 출력하기
package eomcs.servlet.ex03;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/ex03/s1") // http://localhost:8080/web/ex03/s1
public class Servlet01 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {
      
    PrintWriter out = res.getWriter();

    // 다음 영어 유니코드 문자는 ISO-8859-1 문자표에 있기 때문에 제대로 변환된다.
    out.println("Hello!");

    // 그러나 다음 유니코드 문자는 ISO-8859-1 문자표에 없기 때문에
    // 없다는 의미에서 '?' 문자표 바뀌어 출력된다.
    out.println("안녕하세요!");
    out.println("こんにちは");
    out.println("您好");
    out.println("مع السلامة؛ إلى اللقاء!");
  }
}

 

=>

ㄴ App 실행

=>

ㄴ 다음 유니코드 문자는 ISO-8859-1 문자표에 없기 때문에 없다는 의미에서 '?' 문자표 바뀌어 출력됨

 

 

클라이언트로 출력하기 - 한글 깨짐 현상 처리

     한글 깨짐 처리하기
     => 출력 스트림을 꺼내기 전에
        출력 스트림이 사용할 문자표(charset)를 지정하라.
     => 반드시 출력 스트림을 얻기 전에 설정해야 한다.
          res.setContentType("MIME Type;charset=문자표이름");


MIME Type : Multi-purpose Internet Mail Extension
     => 콘텐트의 형식을 표현
     => 콘텐트타입/상세타입
     => 예) text/plain, text/css, text/html 등
     => 웹 브라우저는 콘텐트를 출력할 때 서버가 알려준 MIME 타입을 보고
        어떤 방식으로 출력할 지 결정한다.

Servlet02.java (ex03)

// 클라이언트로 출력하기 - 한글 깨짐 현상 처리
package eomcs.servlet.ex03;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/ex03/s2")
public class Servlet02 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    res.setContentType("text/plain;charset=UTF-8"); // UCS2(UTF-16) ==> UTF-8
    PrintWriter out = res.getWriter();

    out.println("Hello!");

    // 한글이나 아랍문자, 중국문자, 일본문자는
    // UTF-8 문자표에 정의되어 있기 때문에
    // UTF-8 문자로 변환할 수 있다.
    out.println("안녕하세요!");
    out.println("こんにちは");
    out.println("您好");
    out.println("مع السلامة؛ إلى اللقاء!");

  }
}

=>

ㄴ 정상적으로 출력됨을 확인

 

클라이언트로 출력하기 - HTML 출력하기

Servlet03.java (ex03)

// 클라이언트로 출력하기 - HTML 출력하기
package eomcs.servlet.ex03;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/ex03/s3")
public class Servlet03 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // HTML 출력할 때 MIME 타입에 HTML을 지정하지 않으면
    // 웹 브라우저는 일반 텍스트로 간주하여 출력한다.
    res.setContentType("text/html;charset=UTF-8"); // UTF-16 ==> UTF-8
    PrintWriter out = res.getWriter();

    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head><title>servlet03</title></head>");
    out.println("<body><h1>안녕하세요2</h1></body>");
    out.println("</html>");
  }
}

 

Servlet03.java (ex03)

ㄴ HTML 출력 시 MIME 타입에 HTML 이 아닌 plain 을 지정

ㄴ build 해주기

=>

ㄴ html 이 아닌 날것의 텍스트가 출력됨

 

Servlet03.java (ex03)

ㄴ text 를 toxt 라고 오타를 내도록 함

=>

ㄴ 파일 변경 시 Gradle 탭의 servlet-app > Tasks > build 대신 servlet-app > Tasks > classes 를 이용하여 컴파일하도록 함

=>

=>

ㄴ s3 라는 파일이 다운로드 되는 현상이 발생

 

 


저장 시 자동 빌드 설정

=>

ㄴ Tools > Actions on Save 에서 Build project 체크 > [OK] 선택

ㄴ Optimize imports, Rearrange code 도 추가로 설정해주기


ㄴ servlet-app/app/src/main/webapp 바로 아래에 photo.jpeg 라는 이름의 이미지 파일이 있는지 확인

=>

클라이언트로 출력하기 - 바이너리 데이터 출력하기

Servlet04.java (ex03)

// 클라이언트로 출력하기 - 바이너리 데이터 출력하기
package eomcs.servlet.ex03;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

@WebServlet("/ex03/s4")
public class Servlet04 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // photo.jpeg 파일의 실제 경로 알아내기
    // 1) 서블릿의 환경 정보를 다루는 객체를 먼저 얻는다.
    ServletContext ctx = req.getServletContext();

    // 2) ServletContext를 통해 웹 자원의 실제 경로를 알아낸다.
    // => getRealPath(현재 웹 애플리케이션의 파일 경로) : 실제 전체 경로를 리턴한다.
    String path = ctx.getRealPath("/photo.jpeg");
    System.out.println(path);

    FileInputStream in = new FileInputStream(path);

    // 바이너리를 출력할 때 MIME 타입을 지정해야 웹 브라우저가 제대로 출력할 수 있다.
    // => 웹 브라우저가 모르는 형식을 지정하면 웹 브라우저는 처리하지 못하기 때문에
    //    그냥 다운로드 대화상자를 띄운다.
    res.setContentType("image/jpeg");

    OutputStream out = res.getOutputStream(); // 바이너리타입

    int b;
    while ((b = in.read()) != -1) {
      out.write(b);
    }

    out.flush(); // 버퍼 데코레이터에 보관된 데이터를 클라이언트로 방출한다.
    out.close();
    in.close();
  }
}

ㄴ 바이너리타입 데이터를 출력할 때 OutputStream 을 이용

=>

ㄴ flush() 중요!

ㄴ 바이너리를 출력할 때 MIME 타입을 image/jpeg 로 지정

=>

ㄴ Content-Type 이 image/jpeg 임을 확인

=>

Servlet03.java (ex03)

ㄴ HTML 은 PrintWriter를 이용하여 HTML 코드를 출력하는 것이 가능

 

 

ㄴ ex04 패키지를 만들고 ex03 패키지와 동일한 방법으로 파일 가져와서 붙여넣기

 


ㄴ [name:] 우클릭 > Disable Hints 선택하여 힌트 없애기


클라이언트가 보낸 데이터 읽기 - GET 요청 데이터 읽기

     GET 요청
     - 웹 브라우저에 URL을 입력한 후 엔터를 치면 GET 요청을 보낸다.
     - 웹 페이지에서 링크를 클릭하면(자바스크립트 처리하지 않은 상태) GET 요청을 보낸다.
     - 웹 페이지의 폼(method='GET' 일 때)에서 전송 버튼을 클릭하면 GET 요청을 보낸다.

     테스트
     - http://localhost:8080/web/ex04/test01.html 실행

 

test01.html (ex04)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test01</title>
</head>
<body>
<h1>GET 요청1</h1>
<p>
웹 브라우저 주소 창에 'http://localhost:8888/ex04/s1?name=홍길동&age=20' 
입력한 후 엔터를 친다. 
</p>

<h1>GET 요청2</h1>
<p>
다음 링크를 클릭한다.<br>
<a href="http://localhost:8888/ex04/s1?name=홍길동&age=20">GET 요청</a>
</p>

<h1>GET 요청3</h1>
<p>
다음 입력폼에 값을 넣고 '전송' 버튼을 클릭한다.
</p>
<form action="http://localhost:8888/ex04/s1" method="get">
  이름: <input type="text" name="name"><br>
  나이: <input type="number" name="age"><br>
  <input type="submit" value="전송">
</form>
</body>
</html>

 

 웹브라우저에서 웹서버의 자원을 요청하는 방법
     1) 서블릿 클래스를 실행하고 싶을 때
         => 서블릿 클래스의 실제 위치:
         톰캣배치폴더/wtpwebapps/eomcs-java-web/WEB-INF/classes/com/eomcs/web/ex04/Servlet01.class
         => 요청:
         해당 서블릿을 서버에 등록할 때 사용한 URL을 지정해야 한다.
         http://localhost:9999/eomcs-java-web/ex04/s1

     2) HTML, CSS, JavaScript, JPEG 등 정적 파일을 받고 싶을 때
         => 정적 파일의 실제 위치:
         톰캣배치폴더/wtpwebapps/eomcs-java-web/ex04/test01.html
         => 요청:
         http://localhost:9999/eomcs-java-web/ex04/test01.html

     3) /WEB-INF/ 폴더에 있는 정적 파일을 받고 싶을 때
         => 정적 파일의 실제 위치:
         톰캣배치폴더/wtpwebapps/eomcs-java-web/WEB-INF/ex04/test01.html
         => 요청:
         /WEB-INF 폴더 아래에 있는 파일은 클라이언트에서 요청할 수 없다!
         웹 애플리케이션의 정보를 두는 폴더이기 때문이다.

 HTTP 요청 형식

 method sp request-URI sp http_version CRLF
 *((general header | request header | entity header) CRLF)
 CRLF // 헤더와 메시지 본문을 구분하기 위한 빈 줄
 message-body
 

 GET 요청 HTTP 프로토콜 예)
     => GET 요청은 데이터를 request-URI에 붙여서 보낸다.
     => request-URI
     예) /java-web/ex04/s1?name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=20
     서블릿 URL : /java-web/ex04/s1
     데이터(Query String) : ?name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=20
     => 데이터 형식
     이름=값&이름=값&이름=값
     => request-URI는 URI 규칙에 따라 인코딩되어야 한다.

 URL 인코딩(=퍼센트 인코딩)
     - URI 작성 규칙에 따라 데이터를 변환하는 것.
     - 문법
     원래 코드를 4비트 단위로 짤라서 문자로 간주하고 문자 코드로 인코딩 한다.


 URI 작성 규칙
     - RFC 3986 규약에 따라 URL을 작성해야 한다.
     - URL 비예약어(unreserved)인 경우 그대로 사용할 수 있다.
     예) 영어 대.소문자, -, _, ., ~
     - URL 문법에서 사용하기 위해 예약된 키워드 및 기타 언어의 문자를 데이터로 작성할 때는 URL 인코딩해야 한다.
     예약어 => !, *, : , ; ,#, /, @ 등
     기타 문자 => 한글, 한자, 일본어 등
     즉 8비트 값이 음수일 경우 URL 인코딩 대상이 된다.


 톰캣 서버에 GET 요청으로 받은 데이터가 UTF-8 임을 설정하는 방법
 톰캣 7 이하 버전에서는 conf/server.xml 파일에서 다음과 같이 설정해야 한다.
 <Connector connectionTimeout="20000" port="8080"
 protocol="HTTP/1.1" redirectPort="8443"
 URIEncoding="UTF-8" <------ 이 속성을 추가하라!
 />
 주의!
 => 톰캣 서버가 아닌 다른 서블릿 컨테이너는 사용 안내서를 확인해보라!


 HTTP GET 요청 예)
 ------------------------------------------------------------------------------
 GET /java-web/ex04/s1?name=AB%EA%B0%80%EA%B0%81&age=20 HTTP/1.1
 Host: localhost:8080
 Connection: keep-alive
 Pragma: no-cache
 Cache-Control: no-cache
 Upgrade-Insecure-Requests: 1
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, likeGecko)
 Chrome/73.0.3683.86 Safari/537.36
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,
 Accept-Encoding: gzip, deflate, br
 Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6
 빈 줄


 HTTP 응답 프로토콜
 => 형식
 status-line(HTTP프로토콜 상태코드 간단한문구) CRLF
 *(general header | response header | entity header) CRLF
 CRLF
 message-body

 => 예:

 HTTP 응답 예)
 ------------------------------------------------------------------------------
 HTTP/1.1 200 OK
 Content-Type: text/plain;charset=UTF-8
 Content-Length: 27
 Date: Thu, 28 Mar 2019 05:46:08 GMT
 <== 빈 줄
 이름=홍길동
 나이=20


 URI (Uniform Resource Identifier)
 => 웹 자원의 위치를 가리키는 식별자
 => 종류
 URL(Uniform Resource Locator)
 scheme:[//[user:password@]host[:port]][/]path[?query string][#fragment]
 예) http://localhost:8080/ex04/s1?name=홍길동&age=20

 URN(Uniform Resource Name)
 <URN> ::= "urn:" <NID> ":" <NSS>
 예) urn:lex:eu:council:directive:2010-03-09;2010-19-UE

URI : Uniform Resource Indentifier

 

Servlet01.java (ex04)

// 클라이언트가 보낸 데이터 읽기 - GET 요청 데이터 읽기
package eomcs.servlet.ex04;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/ex04/s1")
public class Servlet01 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // 웹 브라우저가 보낸 데이터 읽기
    // ServletRequest.getParameter("파라미터이름")
    //
    String name = req.getParameter("name");
    String age = req.getParameter("age");

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.printf("이름=%s\n", name);
    out.printf("나이=%s\n", age);
  }
}

=>

ㄴ http://localhost:8888/ex04/s1 요청 결과

=>

ㄴ http://localhost:8888/ex04/s1?age=20 요청 결과

=>

ㄴ http://localhost:8888/ex04/s1?age=20&name=ok 요청 결과

=>

ㄴ http://localhost:8888/ex04/s1?age=20&name=한글 요청 결과

 

클라이언트가 보낸 데이터 읽기 - POST 요청 데이터 읽기

 HTTP 요청 형식
 method sp request-URI sp http_version CRLF
 *((general header | request header | entity header) CRLF)
 CRLF
 message-body

 POST 요청 HTTP 프로토콜 예)
 => POST 요청은 데이터를 message-body에 붙여서 보낸다.
 => 데이터 형식과 URL 인코딩은 GET 요청과 같다.
 => 예)

 POST /java-web/ex04/s2 HTTP/1.1
 Host: localhost:8080
 Connection: keep-alive
 Content-Length: 33
 Pragma: no-cache
 Cache-Control: no-cache
 Origin: http://localhost:8080
 Upgrade-Insecure-Requests:1
 Content-Type: application/x-www-form-urlencoded
 User-Agent: Mozilla/5.0 (Macintosh; Intel MacOS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko)
 Chrome/73.0.3683.86 Safari/537.36
 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,
 Referer:http://localhost:8080/java-web/ex04/test02.html
 Accept-Encoding: gzip, deflate, br
 Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6
 빈 줄
 name=AB%EA%B0%80%EA%B0%81&age=20


 GET 요청 vs POST 요청
 0) 데이터 전송 방식
 => GET
 - request uri(URL 주소)에 데이터를 포함한다.
 예) /java-web/ex04/s1?name=AB%EA%B0%80%EA%B0%81&age=20
 => POST
 - message body에 데이터를 포함한다.
 예) name=AB%EA%B0%80%EA%B0%81&age=20
 1) 전송 데이터 용량
 => GET
 - 대부분의 웹서버가 request-line과 헤더의 크기를 8KB로 제한하고 있다.
 - 따라서 긴 게시글과 같은 큰 용량의 데이터를 GET 방식으로 전송할 수 없다.
 => POST
 - HTTP 요청 헤더 다음에 message-body 부분에 데이터를 두기 때문에
 용량의 제한 없이 웹 서버에 전송할 수 있다.
 - 즉 웹 서버가 제한하지 않는 한 전송 데이터의 크기에 제한이 없다.
 - 웹 서버가 제한한다?
 특정 사이트에서는 게시글의 크기나 첨부파일의 크기에 제한을 둔다.
 => 용도
 - 게시글 조회나 검색어 입력 같은 간단한 데이터 전송에는 GET 요청으로 보내고
 - 게시글 등록이나 첨부파일 같은 큰 데이터 전송에는 POST 요청으로 보낸다.

 2) 바이너리 데이터 전송
 => GET
 - request-URI가 텍스트로 되어 있다.
 따라서 바이너리 데이터를 request-URI에 붙여서 전송할 수 없다.
 - 그럼에도 꼭 GET 요청으로 바이너리 데이터를 보내고자 한다면?
 바이너리 데이터를 텍스트로 변환하면 된다.
 예를 들어 바이너리 데이터를 Base64로 인코딩하여 텍스트를 만든 후에
 GET 요청 방식대로 이름=값 으로 보내면 된다.
 - 그래도 결국 용량 제한 때문에 바이너리 데이터를 GET 요청으로 전송하는 것은 바람직하지 않다.
 => POST
 - 이 방식에서도 이름=값 형태로는 바이너리 값을 전송할 수 없다.
 - multipart 형식을 사용하면 바이너리 데이터를 보낼 수 있다.
 - 보통 파일 업로드를 구현할 때 이 multipart 전송 방식으로 사용한다.

 3) 보안
 => GET
 - URL에 전송 데이터가 포함되어 있기 때문에
 사용자 아이디나 암호 같은 데이터를 GET 방식으로 전송하는 것은 위험하다.
 - 웹 브라우저는 주소 창에 입력한 값을 내부 캐시에 보관해 두기 때문이다.
 - 그러나 게시물 번호 같은 데이터는 URL에 포함되어야 한다.
 그래야 다른 사람에게 URL과 함께 데이터를 보낼 수 있다.
 => POST
 - mesage-body 부분에 데이터가 있기 때문에
 웹 브라우저는 캐시에 보관하지 않는다.
 - 또한 주소 창에도 보이지 않는다.
 - 사용자 암호 같은 데이터를 전송할 때는 특히 이 방식으로 보내는 것이 바람직 하다.
 즉 보내는 데이터를 웹 브라우저의 캐시 메모리에 남기고 싶지 않을 때는 POST 방식을 사용한다.
 - 꺼꾸로 특정 페이지를 조회하는 URL일 경우 POST 방식을 사용하면
 URL에 조회하려는 정보의 번호나 키를 포함할 수 없기 때문에
 이런 상황에서는 POST 방식이 적절하지 않다.
 오히려 GET 방식이 적합하다.

 

test02.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test02</title>
</head>
<body>
<h1>POST 요청</h1>
<p>
다음 입력폼에 값을 넣고 '전송' 버튼을 클릭한다.
</p>
<!-- action 속성의 URL이 상대 경로라면, 
     현재 페이지의 경로를 기준으로 URL을 계산한다.-->
<form action="s2" method="post"> 
  이름: <input type="text" name="name"><br>
  나이: <input type="number" name="age"><br>
  <input type="submit" value="전송">
</form>
</body>
</html>

 

Servlet02.java (ex04)

// 클라이언트가 보낸 데이터 읽기 - POST 요청 데이터 읽기
package eomcs.servlet.ex04;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/ex04/s2")
public class Servlet02 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // POST 요청
    // - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
    //
    // 테스트
    // - http://localhost:8080/web/ex04/test02.html 실행
    //

    // 웹 브라우저가 보낸 데이터 읽기
    // => 데이터를 읽을 때는 GET 요청과 POST 요청이 같다.
    // ServletRequest.getParameter("파라미터이름")
    //
    // => POST 요청으로 보낸 데이터는 서블릿 컨테이너 측에서 영어(ISO-8859-1)라고 간주한다.
    // 그래서 한글 코드 값도 영어라고 간주하고 UCS2(UTF-16) 문자 코드로 변환하기 때문에
    // getParameter() 가 리턴한 한글은 깨진 한글이다.
    //
    // 클라이언트가 보낸 한글을 읽을 때 깨지는 문제 해결?
    // => 다음 코드의 주석을 풀고 테스트 해보라!
    // 정상적으로 잘 출력될 것이다.
    req.setCharacterEncoding("UTF-8");

    // => 원리
    // getParameter()를 최초로 호출하기 전에 먼저
    // 클라이언트 보낸 데이터의 인코딩 형식이 어떤 문자표로 되어 있는지 알려줘야 한다.
    // => 주의!
    // 반드시 getParamter()를 최초로 호출하기 전이어야 한다.
    // 한 번 getParameter()를 호출한 후에는 소용없다.
    //

    String age = req.getParameter("age");
    String name = req.getParameter("name");

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.printf("이름=%s\n", name);
    out.printf("나이=%s\n", age);
    out.println("-------------------");

    char[] chars = name.toCharArray();
    for (char c : chars) {
      out.printf("%04x\n", (int) c);
    }
  }
}

 

Postman 이용해 보기

ㄴ servlet-app 컬렉션의 더보기 아이콘 > Add request 선택

=>

ㄴ ex04/s2 로 새로운 request 이름 설정

=>

ㄴ localhost:8888/ex04/s2 로 경로 설정

=>

ㄴ POST 로 설정해주기

=>

ㄴ Body 탭에서 x-www-form-urlencoded 선택

=>

ㄴ name : 홍길동, age : 20 으로 설정해주기

ㄴ [Send] 선택

=>

=>

Servlet02.java (ex04)

ㄴ 주석처리 해보기

=>

ㄴ 이름이 정상적으로 출력되지 않음을 확인

=>

Servlet02.java (ex04)

ㄴ 다시 주석 풀어주기

=>

ㄴ 이름이 정상적으로 출력됨을 확인

=>

ㄴ 해당 값 입력

=>

ㄴ 정상적으로 출력됨을 확인

ㄴ 점선 아래는 해당 값들을 16진수로 표현하고 4자리로 정렬한 값

=>

Servlet02.java (ex04)

ㄴ 다시 주석 처리 해주기

=>

ㄴ 이름이 정상적으로 출력되지 않음을 확인

=>

클라이언트가 보낸 데이터 읽기 - 파일 업로드 처리하기

 

Servlet03.java (ex04)

// 클라이언트가 보낸 데이터 읽기 - 파일 업로드 처리하기
package eomcs.servlet.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex04/s3")
public class Servlet03 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // POST 요청으로 파일 전송하기
    // - 파일을 첨부하여 서버에 전송한다.
    // - multipart/form-data 형식으로 데이터를 전송하지 않으면
    // 첨부 파일의 데이터는 받을 수 없다.
    //
    // 테스트
    // - http://localhost:8080/java-web/ex04/test03.html 실행
    //

    req.setCharacterEncoding("UTF-8");

    String age = req.getParameter("age");
    String name = req.getParameter("name");
    String photo = req.getParameter("photo");

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.printf("이름=%s\n", name);
    out.printf("나이=%s\n", age);
    out.printf("사진=%s\n", photo);

    // GET 요청이나 일반 POST 요청을 한 경우에는
    // 파일이 이름만 넘어오고 파일 데이터는 넘어오지 않는다.
    //
    // 파일의 데이터를 전송하려면,
    // <form> 태그에 enctype 속성을 "multipart/form-data"로 설정해야 한다.
    //
    // 단 멀티파트 형식으로 데이터가 넘어온 경우에는
    // getParameter()로 그 값을 꺼낼 수 없다.
  }
}

 

test03.html  (ex04)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test03</title>
</head>
<body>
<h1>파일 업로드</h1>
<p>
'파일 선택(Browse)' 버튼을 클릭하여 사진 파일을 선택한 후 '전송' 버튼을 누른다.
</p>

<h2>GET 요청으로 파일 업로드</h2>
<form action="s3" method="get">
  이름: <input type="text" name="name"><br>
  나이: <input type="number" name="age"><br>
  사진: <input type="file" name="photo"><br>
  <input type="submit" value="GET 전송">
</form>
<p>
GET 으로 파일을 전송할 수 없다. 단지 파일 이름만 전송한다. 
</p>

<h2>POST 요청으로 파일 업로드</h2>
<form action="s3" method="post">
  이름: <input type="text" name="name"><br>
  나이: <input type="number" name="age"><br>
  사진: <input type="file" name="photo"><br>
  <input type="submit" value="POST 전송">
</form>
<p>
현재 form의 데이터 인코딩 형식은 "application/x-www-form-urlencoded"이다.
즉 "이름=값&이름=값&이름=값" 형식으로 서버에 데이터를 보낸다.
이 방식 또한 첨부한 파일의 데이터를 보내지 못한다.
단지 파일 이름만 보낸다.
</p>

<h2>POST 요청 + multipart/form-data 형식으로 파일 업로드</h2>
<form action="s3" enctype="multipart/form-data" method="post">
  이름: <input type="text" name="name"><br>
  나이: <input type="number" name="age"><br>
  사진: <input type="file" name="photo"><br>
  <input type="submit" value="POST 전송">
</form>
<p>
파일을 업로드 하려면, 반드시 multipart/form-data 형식으로 POST 요청해야 한다.
그래야 파일의 이름 뿐만아니라 파일 데이터도 전송한다.
</p>
</body>
</html>

=>

=>

ㄴ Get 방식으로 요청되어 Headers 에서 전달된 값 확인 가능

=>

=>

=>

ㄴ POST 방식으로 요청되어 Payload 에서 전달된 값 확인 가능

=>

ㄴ Content-type 이 application/x-www-form-urlencoded 로 설정되어 있음을 확인

=>

=>

=>

ㄴ POST 방식으로 요청되어 Payload 에서 전달된 값 확인 가능

ㄴ photo 는 바이너리 데이터

=>

ㄴ view source 선택하여 확인

 

 

멀티파트 파일 업로드 처리하기 - Servlet 3.0의 기본 라이브러리 사용

 멀티파트 형식의 데이터를 처리할 서블릿으로 선언하라.
 1) DD 파일(web.xml)에 설정하기
 <servlet>
   <servlet-name>ex04.Servlet05</servlet-name>
   <servlet-class>com.eomcs.web.ex04.Servlet05</servlet-class>
   <multipart-config>
     <max-file-size>10000000</max-file-size>
   </multipart-config>
 </servlet>

 2) 애노테이션으로 설정하기

 

test06.html (ex04)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test05</title>
</head>
<body>
<h1>POST 요청 - 파일 업로드(multipart/form-data)</h1>
<p>
첨부 파일의 데이터를 서버에 보내려면 "multipart/form-data" 형식으로 전송해야 한다.
form 태그의 enctype 속성을 "multipart/form-data" 로 변경하라.
</p>
현재 HTML의 URL을 기준으로 요청 URL을 상대적으로 기술할 수 있다.
<form action="s5" 
      method="post"
      enctype="multipart/form-data">
  이름: <input type="text" name="name"><br>
  나이: <input type="number" name="age"><br>
  사진: <input type="file" name="photo"><br>
  <input type="submit" value="전송">
</form>
</body>
</html>

 

Servlet05.java (ex04)

// 멀티파트 파일 업로드 처리하기 - Servlet 3.0의 기본 라이브러리 사용
package eomcs.servlet.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.UUID;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;

// 2) 애노테이션으로 설정하기
@MultipartConfig(maxFileSize = 1024 * 1024 * 10)
@WebServlet("/ex04/s5")
public class Servlet05 extends GenericServlet {

  private static final long serialVersionUID = 1L;
  private String uploadDir;

  @Override
  public void init() throws ServletException {
    this.uploadDir = this.getServletContext().getRealPath("/upload");
  }

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // 테스트
    // - http://localhost:8080/java-web/ex04/test05.html 실행
    //

    // Servlet 3.0의 멀티파트 처리 기능을 이용할 때는
    // 원래 하던대로 클라이언트가 보낸 데이터의 인코딩을 지정하라.
    req.setCharacterEncoding("UTF-8");

    // 파라미터로 받은 ServletRequest를 원래의 타입으로 변환하라.
    HttpServletRequest httpReq = (HttpServletRequest) req;

    res.setContentType("text/html;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println("<html>");
    out.println("<head><title>servlet04</title></head>");
    out.println("<body><h1>파일 업로드 결과</h1>");

    // 일반 폼 데이터를 꺼낼 때는 원래 하던 방식대로 값을 꺼낸다.
    out.printf("이름=%s<br>\n", httpReq.getParameter("name"));
    out.printf("나이=%s<br>\n", httpReq.getParameter("age"));

    // 파일 데이터는 getPart()를 이용한다.
    Part photoPart = httpReq.getPart("photo");
    if (photoPart.getSize() > 0) {
      // 파일을 선택해서 업로드 했다면,
      String filename = UUID.randomUUID().toString();
      photoPart.write(this.uploadDir + "/" + filename);
      out.printf("사진=%s<br>\n", filename);
      out.printf("<img src='../upload/%s' height='50'><br>\n", filename);
      out.printf("<img src='../upload/%s'><br>\n", filename);
    }
    out.println("</body></html>");
  }
}

=>

=>

=>

 

 

클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우I

test06.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test06</title>
</head>
<body>
<h1>같은 이름으로 여러 개의 데이터 보내기</h1>
<h2>좋아하는 영화 장르</h2>
<form action="s6" method="get">
  <input name="genre1" type="checkbox">로맨틱<br>
  <input name="genre2" type="checkbox">스릴러<br>
  <input name="genre3" type="checkbox">호러<br>
  <input name="genre4" type="checkbox">드라마<br>
  <input name="genre5" type="checkbox">액션<br>
  <input name="genre6" type="checkbox">SF<br>
  <input type="submit" value="전송">
</form>
</body>
</html>

 

Servlet05.java (ex04)

// 클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우
package eomcs.servlet.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex04/s6")
public class Servlet06 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    req.setCharacterEncoding("UTF-8");

    // POST 요청
    // - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
    //
    // 테스트
    // - http://localhost:8080/java-web/ex04/test06.html 실행
    //

    // 1) 서로 다른 이름으로 값을 보낼 경우
    // => 예) genre1=on&genre2=on&genre4=on
    // => 다음과 같이 각각의 이름에 대해 값을 꺼내 확인해야 한다.
    String genre1 = req.getParameter("genre1");
    String genre2 = req.getParameter("genre2");
    String genre3 = req.getParameter("genre3");
    String genre4 = req.getParameter("genre4");
    String genre5 = req.getParameter("genre5");
    String genre6 = req.getParameter("genre6");

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println("선택한 장르:");
    if (genre1 != null) {
      out.println("로맨틱");
    }
    if (genre2 != null) {
      out.println("스릴러");
    }
    if (genre3 != null) {
      out.println("호러");
    }
    if (genre4 != null) {
      out.println("드라마");
    }
    if (genre5 != null) {
      out.println("액션");
    }
    if (genre6 != null) {
      out.println("SF");
    }
  }
}

 

=>

=>

=>

ㄴ 체크박스를 체크한 값이 on 으로 전달됨

=>

test06.html

ㄴ checkbox 타입의 input 태그의 value 값을 지정해주기 (on 대신 출력할 값)

=>

=>

ㄴ on 대신 input 태그의 value 값이 url 에 전달됨

 

 

클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우II

test06_2.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test06</title>
</head>
<body>
<h1>같은 이름으로 여러 개의 데이터 보내기 II</h1>
<h2>좋아하는 영화 장르</h2>
<form action="s6_2" method="get">
  <input type="checkbox" name="genre" value="1">로맨틱<br>
  <input type="checkbox" name="genre" value="2">스릴러<br>
  <input type="checkbox" name="genre" value="3">호러<br>
  <input type="checkbox" name="genre" value="4">드라마<br>
  <input type="checkbox" name="genre" value="5">액션<br>
  <input type="checkbox" name="genre" value="6">SF<br>
  <input type="submit" value="전송">
</form>
</body>
</html>

ㄴ value 값을 숫자 1 ~ 6 으로 주기

 

Servlet06_2.java

// 클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우
package eomcs.servlet.ex04;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/ex04/s6_2")
public class Servlet06_2 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    req.setCharacterEncoding("UTF-8");

    // POST 요청
    // - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
    //
    // 테스트
    // - http://localhost:8080/java-web/ex04/test06.html 실행
    //

    // 2) 같은 이름으로 값을 보낼 경우
    // => 예) genre=1&genre=2&genre=4
    // => 다음과 같이 한 번에 값을 리턴 받는다.
    String[] genres = req.getParameterValues("genre");
    String[] genreData = {"", "로맨틱", "스릴러", "호러", "드라마", "액션", "SF"};

    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println("선택한 장르:");
    for (String genre : genres) {
      out.println(genreData[Integer.parseInt(genre)]);
    }
    // 같은 값을 여러 개 입력 받아야 하는 경우
    // 같은 이름을 사용하라.
    // 그러면 위와 같이 한 번에 배열로 그 값들을 받을 수 있다.
    // 배열로 받으면 반복문을 이용하여 보다 쉽고 간결하게 처리할 수 있다.
  }
}

=>

=>

=>

 

ㄴ 아무것도 체크하지 않고 [전송] 선택 해보기

=>

ㄴ 아예 값이 넘어가지 않음을 확인

=>

Servlet06_2.java

ㄴ genres 를 출력하여 넘어가는 값이 null 임을 확인 => 체크박스를 선택하지 않으면 어떠한 값도 넘어가지 않음을 확인할 수 있음

 

 

클라이언트가 보낸 데이터 읽기 - 빈 값과 null

test07.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test07</title>
</head>
<body>
<h1>빈 값을 보내기</h1>
<form action="s7" method="get">
  a: <input type="text" name="a" value="aaaa"><br>
  b: <input type="text" name="b"><br>
  a 입력 상자에는 값을 넣고, b 입력 상자에는 값을 입력하지 않고 전송해보자!<br>
  
  c: <input type="checkbox" name="c"><br>
  체크 상자를 체크한 경우와 체크하지 않은 경우를 확인해보자!<br>  
  <input type="submit" value="전송">
</form>
</body>
</html>

 

Servlet07.java

// 클라이언트가 보낸 데이터 읽기 - 빈 값과 null
package eomcs.servlet.ex04;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/ex04/s7")
public class Servlet07 extends GenericServlet {

  private static final long serialVersionUID = 1L;

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    req.setCharacterEncoding("UTF-8");

    // POST 요청
    // - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
    //
    // 테스트
    // - http://localhost:8080/java-web/ex04/test07.html 실행
    //

    // 파라미터 이름만 넘어갈 때 getParameter()의 리턴 값은 빈 문자열 객체이다.
    // null 이 아니다.
    // 입력 상자에 값을 입력하지 않아도 빈 문자열이 서버에 전송된다는 것이다.
    // => 예) a=aaa&b=
    //
    res.setContentType("text/plain;charset=UTF-8");
    PrintWriter out = res.getWriter();

    out.printf("a = %s\n", req.getParameter("a"));
    out.printf("b = %s\n", req.getParameter("b"));

    // 파라미터 이름 자체가 없으면 getParameter()는 null을 리턴한다.
    out.printf("c = %s\n", req.getParameter("c"));
  }
}

=>

ㄴ [전송] 선택

=>

ㄴ 파라미터 이름만 넘어갈 때 getParameter()의 리턴 값은 빈 문자열 객체임 (null 이 아님)
    => 입력 상자에 값을 입력하지 않아도 빈 문자열이 서버에 전송됨을 알 수 있음

ㄴ b= 다음에 빈 문자열이 있음

 

 

썸네일 이미지 만들기

test08.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test08</title>
</head>
<body>
<h1>파일 업로드 - 썸네일 이미지 만들기</h1>
<p>
업로드 파일을 가지고 특정 크기의 썸네일 이미지 파일을 따로 생성한다.
</p>
<form action="s8" 
      method="post"
      enctype="multipart/form-data">
  이름: <input type="text" name="name"><br>
  나이: <input type="number" name="age"><br>
  사진: <input type="file" name="photo"><br>
  <input type="submit" value="전송">
</form>
</body>
</html>

 

Servlet08.java

// 썸네일 이미지 만들기
package eomcs.servlet.ex04;

import net.coobird.thumbnailator.ThumbnailParameter;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import net.coobird.thumbnailator.name.Rename;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.UUID;

@MultipartConfig(maxFileSize = 1024 * 1024 * 10)
@WebServlet("/ex04/s8")
public class Servlet08 extends GenericServlet {

  private static final long serialVersionUID = 1L;
  private String uploadDir;

  @Override
  public void init() throws ServletException {
    this.uploadDir = this.getServletContext().getRealPath("/upload");
  }

  @Override
  public void service(ServletRequest req, ServletResponse res)
      throws ServletException, IOException {

    // 테스트
    // - http://localhost:8080/java-web/ex04/test08.html 실행
    //

    req.setCharacterEncoding("UTF-8");

    HttpServletRequest httpReq = (HttpServletRequest) req;

    res.setContentType("text/html;charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println("<html>");
    out.println("<head><title>servlet04</title></head>");
    out.println("<body><h1>파일 업로드 결과</h1>");

    // 일반 폼 데이터를 원래 하던 방식대로 값을 꺼낸다.
    out.printf("이름=%s<br>\n", httpReq.getParameter("name"));
    out.printf("나이=%s<br>\n", httpReq.getParameter("age"));

    // 파일 데이터는 getPart()를 이용한다.
    Part photoPart = httpReq.getPart("photo");
    if (photoPart.getSize() == 0) {
      out.println("</body></html>");
      return;
    }

    // 파일을 선택해서 업로드 했다면,
    String filename = UUID.randomUUID().toString();
    photoPart.write(this.uploadDir + "/" + filename);

    // 원본 사진을 가지고 특정 크기의 썸네일 이미지를 만들기
    // 1) 썸네일 이미지를 생성해주는 자바 라이브러리 추가
    // => mvnrepository.com에서 thumbnailator 라이브러리 검색
    // => build.gradle 에 추가
    // => '$ gradle eclipse' 실행
    // => eclise IDE에서 프로젝트 리프래시

    // 2) 썸네일 이미지 만들기
    // => 원본 이미지 파일이 저장된 경로를 알려주고
    // 어떤 썸네일 이미지를 만들어야 하는지 설정한다.
    Thumbnails.Builder<File> thumbnailBuilder = Thumbnails.of(this.uploadDir + "/" + filename);
    thumbnailBuilder.size(20, 20);
    thumbnailBuilder.outputFormat("jpg");
    thumbnailBuilder.toFiles(Rename.PREFIX_DOT_THUMBNAIL);

    Thumbnails.Builder<File> thumbnailBuilder2 = Thumbnails.of(this.uploadDir + "/" + filename);
    thumbnailBuilder2.size(20, 20);
    thumbnailBuilder2.crop(Positions.CENTER);
    thumbnailBuilder2.outputFormat("jpg");
    thumbnailBuilder2.toFiles(new Rename() {
      @Override
      public String apply(String name, ThumbnailParameter param) {
        return name + "_20x20";
      }
    });

    Thumbnails.of(this.uploadDir + "/" + filename).size(80, 80).outputFormat("jpg")
        .crop(Positions.CENTER)
        // .toFiles(Rename.PREFIX_DOT_THUMBNAIL);
        .toFiles(new Rename() {
          @Override
          public String apply(String name, ThumbnailParameter param) {
            return name + "_80x80";
          }
        });

    Thumbnails.of(this.uploadDir + "/" + filename).size(160, 160).outputFormat("jpg")
        .crop(Positions.CENTER)
        // .toFiles(Rename.PREFIX_DOT_THUMBNAIL);
        .toFiles(new Rename() {
          @Override
          public String apply(String name, ThumbnailParameter param) {
            return name + "_160x160";
          }
        });

    out.printf("사진=%s<br>\n", filename);
    out.printf("<img src='../upload/thumbnail.%s.jpg'><br>\n", filename);
    out.printf("<img src='../upload/%s_20x20.jpg'><br>\n", filename);
    out.printf("<img src='../upload/%s_80x80.jpg'><br>\n", filename);
    out.printf("<img src='../upload/%s' height='80'><br>\n", filename);
    out.printf("<img src='../upload/%s_160x160.jpg'><br>\n", filename);
    out.printf("<img src='../upload/%s'><br>\n", filename);
    out.println("</body></html>");
  }
}

=>

ㄴ upload 폴더에 있는 이미지 파일 모두 삭제해주기

=>

ㄴ [전송] 선택

=>

 

 

네이버클라우드 서비스 알아보기

Cloud Functions 알아보기

https://www.ncloud.com/

 

NAVER CLOUD PLATFORM

cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification

www.ncloud.com

 

ㄴ Compute > Cloud Functions

=>

=>