자바를 사용해 동적 페이지 만들기 (근데 이렇게는 일 안한다)

애는 사실상 정적 페이진데 왜 톰캣이 관리할까? uri 요청이니까 톰캣이 작동함! html코드를 왜 자바에 이식 했냐? = 동적페이지로 만들기 위함 html 안에 자바 변수나 이런걸… 넣으면 동적 페이지가 됨.
URI는 그냥 특정 리소스를 식별하는 방법이다 (URL은 URI의 한 종류)
(ex) "urn:isbn:9781234567890"은 URN이라는 URI을 가지고 있음)
아파치만 작동한다 = 자바 컴파일이 실행되지 않는다 = 정적이다
[ 자바에서 HTML 코드 ]



.html은 정적이니까 webapp에 만들라 ?
* web-inf 는 보안 폴더라 여기 안에 파일을 집어넣으면 못찾음
* index.jsp는 필요 없으니 지워라
(애가 있으면 url에 자원명이나… 그런걸 아무것도 입력하지 않을 시 index.jsp 파일이 뜸)
[ 자바에서 변수명 넣는 법 ] "+변수명+"

문자열 사이에 자바 코드를 넣어주려면 [ "+변수명+" ] 이렇게 입력해주자 자바 코드에서 html을 만들면 자바 코드를 끼워넣을 수 있어서 동적으로 만들 수 있다 (버퍼에 html코드를 담고, 그사이에 자바 코드를 담을 수 있다) (서블렛으로 하면 디자인 하나를 for문 같은걸 돌릴 수 있음!!)

자바 변수를 담아 동적으로 만드니 현재시간 나오죠?
[ for문 돌려보자 ]
버퍼에 html코드를 담아서 그 사이에 자바 코드를 담을 수 있다

일단 ;로 문자열 끊고, html에 html 누적 함수!


for문 돌았죠?
이렇게 DAO 만들고, DB에 SELECT해서, 그 정보를 HTML 코드 곳곳에 담을 수 있다. 우리가 옛날에 만들었던 AirBnB 사진도 for문으로 돌릴 수 있다는 말 그런데 너무... 복잡하고 어렵지 않니? CSS까지 담는다고 생각하면..ㅠㅠ -> 이렇게 일 안함!
새로운 서블렛 Join. 톰캣아 파싱을…
회원가입을 했더니,.,, join이 없어서 404 오류남 > 새로운 서블렛을 만들어야함



/join-form 에서 바디 데이터(문자열)을 들고 오니까, 우린 파싱 먼저 해야한다 (/join=form에서 username=ssar&password=1234&email=ssar@nate.com 이 들어올 것이다)
서블릿 파일 자체가 라우팅을 하고 있어서 디스패처 필요 없이 우린 서블렛만 만들면 된다. …주소마다 서블렛을 만드는 제일 비효율적인 방식이지만…^^
그래서 안 함! 연습 및 이해하려고 하는 것임
HttpServletRequest req = 톰캣 입장에서 버퍼드 리더
HttpServletResponse resp = 톰캣 입장에서 버퍼드 라이터
[ 톰캣아 파싱을… ]

일반적으로 버퍼로 읽은 HTML 코드는 컨트롤러에서 처리되어야 한다. 컨트롤러에서 적절한 처리를 통해 클라이언트로 전달되어야 웹 애플리케이션의 동적인 동작을 구현하니까... 근데 이거 너무 비효율적인다 ㅠㅠ 아무튼 브라우저에서 회원가입 하니까

이렇게 들어오는데... 이건 그냥 문자열이잖아! 파싱.. 안되어있다!! 톰캣!! 파싱해줘!
name이 잘못되면?

input태그에 name(키 값)이 없다. 그러면 password에 해당하는 키와 밸류값 모두 날아오지 않는다

안 날아오죠? 이름이 null값이 날아온다 뭐 이러면… 일단 html코드의 name을 살펴봐라. getParameter나…
톰캣아 파싱해줘
톰캣은 버퍼에 있는 데이터를 자기가 알아서 파싱해서 키 값을 찾아준다



html 코드의 input의 'name'과 해당 코드에 적힌 매개변수가 동일해야 들고올 수 있음. (getParameter() 메서드는 HttpServletRequest 객체에서 지정된 매개변수의 값을 가져오는 역할) username이 키 값이니까... key값으로 value를 찾으니까? 이제 다시 회원 가입을 해보면….

짠!! 톰캣이 받은 데이터를 알아서 객체로 만들어서 이렇게! 객체로 짠!! 사용자로부터 전송된 폼 데이터를 HttpServletRequest 객체를 통해 받으면, 각각의 데이터를 매개변수로 지정하여 편리하게 값을 가져올 수 있다.
톰캣이 들고 있는 HttpServletRequest 덕분에 파싱을 내가 안 해도 된다. 고맙습니다
그치만 스프링은 이런 것도 쓸 필요 없다.
[ 버퍼를 소비하면 출력되는 데이터가 당연히 없다 ]

왜 버퍼 내용 출력이 안되는가? 위의 코드에서 버퍼에 있는걸 소비해서 다 읽어왔으니까, br.readLine() 해도 못읽는다. 이미 가져와서 다 처리했으니 읽을게 없음
버퍼에 저장된 데이터는 순차적으로 처리된다. 즉, 버퍼에 있는 데이터를 읽고 소비하면 해당 데이터는 사용되어 없어지게 되고 이후에는 새로운 데이터가 버퍼에 채워지게 된다.
버퍼 소비 예시


BufferedReader의 readLine() 메소드는 요청의 본문을 한 줄씩 읽어오는 역할. 여기서 한 줄을 읽어오는 동안에는 읽은 데이터를 버퍼에서 제거하게 된다. 따라서, br.readLine();을 호출하여 버퍼를 소비하면 이후에 req.getParameter()를 통해 파라미터 값을 가져올 때 null이 반환되는 것
이거 2개… 같은 기능인데, 위는 파싱을 내가, 아래는 파싱을 톰캣이


유효성 검사(내가하면 1000줄) ~ DAO
메인 페이지 그리기 (이렇게 X)

board에 MainServlet 생성 우린 지금 회원가입이 다 완료되면 유저에게 어느 페이지를 돌려줄건지(보여줄건지) 정할 것이다. (이런걸 ux라고 한다.) 보통은 회원가입 완료 후 로그인 페이지나 메인 페이지를 뜨게끔 한다.

회원 가입을 하니 메인 페이지 창이 뜨도록 설게했다. 그런데... 코드를 봐보자.
[ MainServlet 클래스 ]

[ joinServlet 클래스 ]

왜... 메인 html 코드가 2개냐? 동일한데.... 중복코드다. 이러면 안된다. 리다이렉트로 잘 만든 코드를 가져오자! (이렇게 중복해서 안 쓸 것. 리다이렉트가 필요하다)
[ 전체 코드 ]
[ board - MainServlet ]
package com.example.userapp.board; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; //@WebServlet("/main") public class MainServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Content-Type", "text/html; charset=utf-8"); String html = "<!DOCTYPE html>\n" + "<html lang=\"en\">\n" + "\n" + "<head>\n" + " <meta charset=\"UTF-8\">\n" + " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" + " <title>Document</title>\n" + "</head>\n" + "\n" + "<body>\n" + " <h1>메인 페이지</h1>\n" + " <hr>\n" + "</body>\n" + "\n" + "</html>"; resp.getWriter().println(html); } }
[ user - JoinFormServlet ]
package com.example.userapp.user; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.time.LocalDateTime; @WebServlet("/join-form") public class JoinFormServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { LocalDateTime now = LocalDateTime.now(); String html = " <!DOCTYPE html>\n" + " <html lang=\"en\">\n" + " <head>\n" + " <meta charset=\"UTF-8\">\n" + " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" + " <title>Document</title>\n" + " </head>\n" + " <body>\n"; for (int i = 0; i < 5; i++) { html = html +" <h1>"+i+"회원가입 페이지"+now+"<h1>\n"; } html = html +" <hr>\n" + " <form action=\"/join\" method=\"post\">\n" + " <input type=\"text\" placeholder=\"username\" name=\"username\">\n" + " <input type=\"text\" placeholder=\"password\" name=\"password\">\n" + " <input type=\"text\" placeholder=\"email\" name=\"email\">\n" + " <button>회원가입</button>\n" + " </form>\n" + " </body>\n" + " </html>"; resp.getWriter().println(html); } }
[ user - JoinServlet ]
package com.example.userapp.user; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; @WebServlet("/join") public class JoinServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Content-Type", "text/html; charset=utf-8"); // username=ssar&password=1234&email=ssar@nate.com 이 들어올 것이다 // BufferedReader br = req.getReader(); // // String requestBody = ""; // // while (true) { // String line = br.readLine(); // // if (line == null) break; // // requestBody = requestBody + line; // // } // System.out.println(requestBody); // 1. 파싱 String username = req.getParameter("username"); String password = req.getParameter("password"); String email = req.getParameter("email"); System.out.println("username : " + username); System.out.println("password : " + password); System.out.println("email : " + email); // 2. 유효성 검사 (username에 글자수 제한을 둘 것이다) if (username.length() < 3 || username.length() > 10) { resp.getWriter().println("<h1>username 글자수가 3 ~ 10 사이여야 합니다."); return; } // 3. DB 연결 // 4. DAO의 insert 메서드를 호출 // 5. 메인 페이지 그리기 // 6. 리다이렉트 // resp.sendRedirect("/main"); // resp.setStatus(302); // resp.setHeader("Location", "/main"); // 헤더에 정보 담기 // response.setHeader("clock", "12pm"); // 프로토콜이 없어서 안뜸 } }
서블릿은 Java 코드 안에 HTML 코드를 섞어 사용할 수 있는데, 이를 통해 동적인 HTML 페이지를 생성하거나 HTML 내에 동적 데이터를 삽입할 수 있다.
resp.setHeader("Content-Type", "text/html; charset=utf-8"); 한글 안깨지게 하는 코드. 모든 서블렛마다 적용되어야 하는 이런걸 '공통 로직'이라고 한다.
-Dfile.encoding=UTF-8
-Dconsole.encoding=UTF-8
콘솔창에서 한글 깨지면
- 상단 메뉴에서 "Run"을 선택한 다음 "Edit Configurations"을 클릭합니다.
- "Run/Debug Configurations" 창이 열리면 왼쪽 패널에서 실행 구성을 선택합니다.
- 오른쪽 패널에서 "VM options"란에
-Dfile.encoding=UTF-8 -Dconsole.encoding=UTF-8
를 입력합니다.
- 변경 내용을 저장하고 창을 닫습니다.
Share article