You are using an outdated browser. Please upgrade your browser to improve your experience.

11/27

로그인 처리하기 bitcamp-java-project-server

bitcamp-web-project ex04 Servlet01

서블릿컨텍스트는 웹어플리케이션 당 1개임!!! 즉 자바프로젝트의 서블릿 컨텍스트는 1개라는 뜻

httpsession은 클라이언트 당 1개

webapps안에 있는 폴더 당 서블릿컨텍스트가 1개

post요청은 무조건 UTF-8인코딩 해줘야한다. get방식은 상관없다.

session에 각 클라이언트 고유 정보를 담는다.

클라이언트가 들어올때 세션아이디 발급, 그걸로 client를 식별,

테스트하려고 가짜로 만든 것들을 꼽는 것 -> mokup

package com.eomcs.pms.web;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.eomcs.pms.domain.Member;
import com.eomcs.pms.service.MemberService;

@WebServlet("/auth/login")
public class LoginServlet extends HttpServlet {
  private static final long serialVersionUID = 1;

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


    // 클라이언트에게 전용 보관소(세션)를 준비한다.
    HttpSession session = request.getSession();


    // 클라이언트로 데이터를 출력할 때 사용할 스트림 준비
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head><title>로그인</title></head>");
    out.println("<body>");

    try {
      out.println("<h1><b>로그인</h1>");

      if (session.getAttribute("loginUser") != null) {
        out.println("<p>로그인 된 상태입니다!</p>");

      } else {
        // 클라이언트가 보낸 데이터를 꺼낸다.
        String email = request.getParameter("email");
        String password = request.getParameter("password");

        // 서블릿이 로그인 작업에 사용할 도구를 준비한다.
        ServletContext ctx = request.getServletContext();
        MemberService memberService = (MemberService) ctx.getAttribute("memberService");

        Member member = memberService.get(email, password);
        if (member == null) {
          out.println("<p>사용자 정보가 맞지 않습니다.</p>");
        } else {
          // 로그인이 성공했으면 회원 정보를
          // 각 클라이언트의 전용 보관소인 session에 저장한다.
          session.setAttribute("loginUser", member);
          out.printf("<p>%s 님 반갑습니다.</p>\n", member.getName());
        }
      }
    } catch (Exception e) {
      out.println("<h2>작업 처리 중 오류 발생! - %s</h2>");
      out.printf("<pre>%s</pre>\n", e.getMessage());

      StringWriter errout = new StringWriter();
      e.printStackTrace(new PrintWriter(errout));

      out.println("<h3>상세 오류 내용</h3>");
      out.printf("<pre>%s</pre>\n", errout.toString());
    }
    out.println("</body>");
    out.println("</html>");
  }
}

GET 요청 HTTP 프로토콜 예)

URL : Uniform Resource Locator(예 : %EA%B0%80)

URI : Uniform Resource Identifier

웹 개발자라면, 특히 프론트엔드라면 자바스크립트에서 데이터를 보낼때 웹브라우저에서 데이터를 보내는게 아니라서 개발자가 직접 URL인코딩을 해서 서버로 보내야 한다는 사실을 반드시 기억하자! 특히 Ajax사용할 때.

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

URN(Uniform Resource Name)

::= "urn:" ":" 예) urn:lex:eu:council:directive:2010-03-09;2010-19-UE => 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 => 데이터 형식 이름=값&이름=값&이름=값 => URL 인코딩 - 데이터를 보낼 때 7bit 네트워크 장비를 거치면 8비트 데이터가 깨진다. - 이를 방지하고자 보내는 데이터를 7비트로 변환한다. - 어떻게? 원래 코드 값을 아스키(ASCII) 문자 코드로 변환한다. - ASCII 코드는 7비트이기 때문에 데이터를 주고 받을 때 깨지지 않을 것이다. - URL 인코딩이란? 문자 코드의 값을 ASCII 코드화시키는 것이다. - 예) "ABC가각"을 전송한다고 가정하자 "ABC가각"의 문자 코드(UTF-8) 값 : 414243EAB080EAB081 7비트 장비를 통과: 41 => 0100 0001 => [7비트 장비] => 100 0001 => [8비트로 복원] => 0100 0001 42 => 0100 0010 => [7비트 장비] => 100 0001 => [8비트로 복원] => 0100 0010 43 => 0100 0011 => [7비트 장비] => 100 0011 => [8비트로 복원] => 0100 0011 EA => 1110 1010 => [7비트 장비] => 110 1010 => [8비트로 복원] => 0110 1010 ASCII 문자코드로 변환 : => 코드 값이 이미 ASCII 라면 그대로 41 ==> 41 42 ==> 42 => 코드 값이 ASCII 가 아니라면 각 4비트 값을 아스키 문자라 간주하고 코드로 변환한다. E ==> 'E' ==> 45 A ==> 'A' ==> 41 이렇게 변경한 후, URL 인코딩 값임을 표시하기 위해 앞에 '%' 코드를 붙인다. EA ==> 25 45 41 ==> 사람이 보는 문자로 표현하면 ==> %EA ==> %EA 문자를 받은 쪽에서는 원래의 값을 변환(URL 디코딩)한다. %EA(3바이트) ==> 1110 1010(1바이트) ## 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 Mac OS 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=ABC%EA%B0%80%EA%B0%81&age=20 ## Get vs Post GET 요청 vs POST 요청 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 방식이 적합하다. getbootstrap.com 브라우저가 보내는건 딱 두가지, get아니면 post밖에 없다. 그래서 method폼엔 delete을 작성할 수 없다. 즉 delete은 get이나 post나 뭘 써도 상관없다. 하지만 실무에서는 대부분 get방식을 쓴다. 무한스크롤 원리 : 스크롤을 내린다 -> 내릴때 자바스크립트로 서버에 그 다음 화면을 요청한다 : 그 다음 화면을 받아온다. 자동으로 되는게 아니라 다시 요청하는거임!! 요청하지 않으면 서버는 응답하지 않는다. 지가 알아서 자동으로 하는게 아니란말임! mapper파일 절대 자동으로 바뀌게 하지 말것!! 보안상 그래서는 안돼ㅑㅁ!!!!\ ## 이미지 업로드하기 이미지는 multipart형식으로 보내야한다. 현재의 서블릿은 multipart형식이 아니기 때문에 이걸 처리할 수 있도록 선언해줘야한다. `@MultipartConfig(maxFileSize = 1024 * 1024 * 10)` 애노테이션을 붙여준다. 업로드 배포폴더에 이미지가 올라간다. 프로젝트 폴더에 올라가는게 아니다! 그럼 왜 만드냐? 만들어둬야 배포폴더에 파일이 업로드 될 것 아니냐고!!! 지금 방법의 단점 : 똑같은 이름의 사진 파일이 올라올 경우 그걸 덮어써버린다. - 방법??? DB에 저장할 때 임의의 랜덤 아이디를 발급해서 그 Id를 파일명으로 쓴다. ```java // <input type="file"...>입력 값 꺼내기 Part photoPart = request.getPart("photo"); // 회원 사진을 저장할 위치를 알아낸다 // => 컨텍스트루트/upload/파일 // => 파일을 저장할 때 사용할 파일명을 준비한다. String filename = UUID.randomUUID().toString(); String saveFilePath = ctx.getRealPath("/upload/" + filename); // 해당 위치에 업로드된 사진 파일을 저장한다. photoPart.write(saveFilePath); // DB에 사진 파일 이름을 저장하기 위해 객체에 보관한다. member.setPhoto(filename); ```