본문 바로가기
05_Server (04. JSP 프로그래밍 구현)

[Servlet] 서블릿(2)

by moca7 2024. 8. 27.

 

 

ㅁ 앞으로의 활용 과정

- request 객체에서 데이터를 뽑고 난 이후에, 이 뽑은 데이터를 DB에 기록한다고 하면.

- data를 service를 호출하면서 넘기고, service에서는 dao로 넘기고, dao에서 insert문 때려서 그 결과를 돌려받는다.

(jdbc과정)

- 성공적으로 다 되었다면 응답페이지를 제작해서 돌려줘야 한다.

요청에 대한 결과 페이지를 사용자에게 보여줘야한다.

 

 

 

 

ㅁ 응답페이지

 

 

- 아래처럼 index.html을 작성한다.

 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <!-- web.xml 파일에서 welcome-file로 등록된 파일(메인페이지)  -->
    <h1>Servlet</h1>

    <!-- http://localhost:8888/servlet/a_lifecycle/main.html -->
    <a href="./a_lifecycle/main.html">1. Servlet 클래스의 LifeCycle</a> <br>

    <a href="./b_request/main.html">2. HttpServletRequest</a> <br>

    <!--  내가 요청할 페이지 : http://localhost:8888/servlet/c_response/main.html -->
    <!-- 상대경로 ./c_response/main.html -->
    <a href="/servlet/c_response/main.html">3. HttpServletResponse</a> <br>
    <a href="">4. Forward</a> <br>
    <a href="">5. Redirect</a> <br>

</body>
</html>
 

 

 

- 아래처럼 c_response라는 폴더 안에 main.html 만든다. 

 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h2>HttpServletResponse 확인</h2>

</body>
</html>
 

 

 

- 화면단만 바꿨기 때문에 서버를 재시작할 필요는 없다.

 

 

브라우저만 다시 새로고침하고 3을 누르면.

 

 

- 이렇게 뜬다.

 

 

 

 

ㅁ main.html에 아래와 같이 더 작성.

 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h2>HttpServletResponse 확인</h2>

    <!-- localhost:8888/servlet/response -->
    <form action="/servlet/response" method="post">
        아이디 : <input type="text" name="userId" maxlength="15" required> <br>
        비밀번호 : <input type="password" name="userPwd" maxlength="20" required> <br>

        <input type="submit" value="요청 및 응답확인">
        <!-- 이번에는 요청만 보내는게 아니라 응답페이지도 돌려준다. -->
    </form>

</body>
</html>
 

 

 

 

ㅁ 이클립스에서 c_resonse.Response <- 서블릿클래스 만들기

 

 

- src/main/java 안에 c_response라는 패키지를 만들고 Response라는 서블릿 클래스를 만듦.

- 그리고 @WebServlet("/Response")를 @WebServlet("/response")로 바꿈.

 

 

 

 

- URL mapping을 하는 2번째 방법. 둘 중 하나만 쓰면 됨. web.xml에 쓰면 한 눈에 볼 수 있는 장점이 있지만 작성하는것 자체가 좀 복잡하다. 

annotaion 방식은 단순하게 작성할 수 있지만, 어떤 url 요청시 어떤 servlet이 실행되는지는 일일이 servlet을 까봐야 안다.

- 만약 어노테이션 방식을 쓰지 않고 web.xml 파일을 수정하는 식으로 하겠다면, 

 

- 이런 상태인데 아래와 같이 수정.

 

 

 

 

ㅁ Response.java 에서 어노테이션을 R에서 r로 바꾸고 주석처리만 했다.

 

package c_response;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


// @WebServlet("/response") 이번엔 xml 방식으로 해본다.
public class Response extends HttpServlet {


    private static final long serialVersionUID = 1L;
       
    public Response() {
        super();
    }

     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          response.getWriter().append("Served at: ").append(request.getContextPath());   
     }

     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       doGet(request, response);
     }

}

 

- doPost 메소드의 이미 쓰여져 있는 코드를 보면, 다시 doGet 메소드를 호출하고 있다.

get방식 요청이든 post방식 요청이든 그냥 편하게 doGet 메소드에서 작업하라고 해놨다.

- 참고로 doGet 메소드의 원래 있던 코드는 지워주는게 좋다. 

 

 

 

 

ㅁ < HttpServletResponse >

- 요청을 보낸 클라이언트에게 응답할 수 있는 객체

- doGet() 또는 doPost() 메소드 실행시 해당 객체가 생성되어 매개변수(response)에 전달

 

 

- 응답 관련 주요 메소드

 

1) getWriter()                               :    클라이언트에게 응답문자를 전송할 수 있는 출력 스트림(PrintWriter 타입) 반환

2) setContentType(String)              응답문자의 데이터 형식(MIME-TYPE)을 설정

3) sendRedirect(String)               :    리다이렉트할 URL 전송 

 

- getWriter()는 통로다. 응답 데이터를 전달하려면 클라이언트와 연결된 통로가 필요하다. 

출력 스트림을 통해 클라이언트에게 응답 데이터를 전달한다. 

- ContentType이라고도 하고 MIME-TYPE이라고도 한다. 문자의 유형. 데이터 형식.

- 3)은 나중에 Redirect할 때 따로 배운다.

 

 

 

ㅁ 응답 데이터 형식 3단계

- 응답 Content-type 설정 + UTF-8 인코딩

 

1) HTML : text/html

2) XML : application/xml

3) JSON : application/json

 

- 지금은 응답 페이지를 돌려주기 때문에 HTML을 돌려준다. 그러면 text/html을 쓴다.

- xml을 돌려주는 일은 거의 없을 거고, html 아니면 json을 돌려줄건데 json은 나중에 ajax 배울때 함.

 

 

 

ㅁ Response.java 

 

package c_response;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


// @WebServlet("/response") // 이번엔 xml 방식으로 해본다.
public class Response extends HttpServlet {


    private static final long serialVersionUID = 1L;
      
    public Response() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    request.setCharacterEncoding("utf-8"); // 소문자로 작성해도 상관 없다.

    String userId = request.getParameter("userId"); // 오른쪽 userId는 key값 그대로 써야 함. 왼쪽 변수명은 맘대로 가능.
    String userPwd = request.getParameter("userPwd");

    // 요청시 전달하는 값을 service로 넘기고 service는 dao로 넘기고 dao에서 쿼리 실행 후 결과 받음 (컨트롤러 입장)
    // select 쿼리 실행했다 침. 성공적으로 조회되었다는 가정 하에 응답페이지를 제작 후 출력한다.(로그인)

 

 

    // 1. 응답 문자의 데이터 형식 지정. (Content-type 설정 + UTF-8 인코딩)
    response.setContentType("text/html; charset=utf-8");   // 하나의 문자열이라 세미콜론으로 구분.

    // 2. 응답할 출력 스트림 알아내기
    PrintWriter out = response.getWriter();           //  빨간줄 안 뜸. 예외처리 throws로 하고 있음.

    // 3. 응답문자 전송 (출력 스트림을 통해 html을 출력)
    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head>");
    out.println("<meta charset='UTF-8'>");
    out.println("<title>로그인성공페이지</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<script>alert('반갑습니다');</script>");
    out.println("<h1>반갑습니다 회원님!" + userId + "계정으로 로그인 성공입니다.</h1>");
    out.println("</body>");
    out.println("</html>");
    out.flush();
    out.close();

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doGet(request, response);
    }

}

 

- post 방식 요청도 doGet 메소드에 작성을 해놓아도 된다.

 

 

- [오류발생. 11시 40분]

 

이런게 뜸. 어노테이션과 web.xml에서 둘다 /response를 해서 그럼.

근데 주석을 없애고 클린하고 재시작해도 계속 저렇게 뜸.

web.xml을 날려버리고 어노테이션 방식으로 하니까 됨.

 

 

 

- alert가 먼저 뜨고 위와 같은 응답 화면이 뜬다.

- 브라우저 측에서 얘를 html구문으로 인식해서 사용자에게 시각적으로 보여준다.

 

 

 

- 요청시 전달된 데이터로 요청을 처리하고, 그에 대한 응답 페이지를 이렇게 자바단에서 제작을해서 출력을 시켜주는게 서블릿의 역할입니다. 

- 지금 화면 구현을 자바코드 작성하는 곳에서 진행하고 있다.

지금은 그나마 간단한 페이지라서 코드가 얼마 안되지만 코드가 긴 페이지면 굉장히 복잡해진다.

- 그래서 이런 불편함을 개선해서 나온게 JSP다. 나중에 JSP 배우면 응답페이지 제작 과정을 JSP에 위임함.

 

 

 

================================================================================

 

 

 

ㅁ 이제 4. Forward 

 

 

- src/main/webapp 폴더에 d_forward 폴더를 만들고, d_forward 폴더 안에 main.html 만든다.

 

 

- 아래는 d_forward 폴더의 main.html 이다.

 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h2>forward 확인</h2>

</body>
</html>
 

 

 

- 아래는 webapp 폴더의 index.html이다.

 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <!-- web.xml 파일에서 welcome-file로 등록된 파일(메인페이지)  -->
    <h1>Servlet</h1>

    <!-- http://localhost:8888/servlet/a_lifecycle/main.html -->
    <a href="./a_lifecycle/main.html">1. Servlet 클래스의 LifeCycle</a> <br>

    <a href="./b_request/main.html">2. HttpServletRequest</a> <br>

    <!--  내가 요청할 페이지 : http://localhost:8888/servlet/c_response/main.html -->
    <!-- 상대경로 ./c_response/main.html -->
    <a href="/servlet/c_response/main.html">3. HttpServletResponse</a> <br>

    <a href="/servlet/d_forward/main.html">4. Forward</a> <br>
 
    <a href="">5. Redirect</a> <br>


</body>
</html>
 

 

 

여기서 4를 누르면

이렇게 잘 되는지 확인해봤다.

 

 

 

 

ㅁ 서블릿 클래스 생성

- d_forward.FirstStep, d_forward.SecondStep

- src/main/java에 d_forward 패키지를 만든다.

d_forward 패키지에 FirstStep 서블릿과 SecondStep 서블릿을 만든다. 

 

 

 

- 아래는 d_forward 폴더의 main.html 이다.

 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h2>forward 확인</h2>

    <a href="/servlet/first">첫번째 목적지 요청</a>

</body>
</html>
 

 

- /first라는 url 매핑값의 서블릿을 호출하고 있다. 

 

 

 

- 아래는 FirstStep.java 서블릿 클래스다.

 

package d_forward;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

 

@WebServlet("/first") // http://localhost:8888/servlet/first라는 URL 요청시 실행될 서블릿

public class FirstStep extends HttpServlet {

 

    private static final long serialVersionUID = 1L;

 

    public FirstStep() {

        super();

    }

 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

 

    System.out.println("첫번째 목적지(경유지) 도착");

 

    // forward라는 기술을 사용해서 서블릿에서 서블릿으로 이동할 수 있다.

    request.getRequestDispatcher("/second").forward(request, response); // contextPath 빼고 url mapping만 작성하면 된다.

    }

 

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

    doGet(request, response);

    }

 

}

 

 

- 포워드 하는 구문 (url mapping만 이동할경로에 작성하면 된다)

request.getRequestDispatcher("이동할경로").forward(request, response);

 

 

 

- 아래는 SecondStep.java

 

package d_forward;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

@WebServlet("/second") // http://localhost:8888/servlet/second라는 URL 요청시 실행될 서블릿

public class SecondStep extends HttpServlet {

 

    private static final long serialVersionUID = 1L;

 

    public SecondStep() { super(); }

 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    System.out.println("두번째 목적지(도착지) 도착");

    }

 

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

    doGet(request, response);

    }

 

}

 

 

 

링크 누르면

이렇게 이동.

- 주소창 링크는 첫번째 목적지 주소만 노출되었다.

 

이클립스에는 이렇게 출력됨.

 

 

 

 

ㅁ 첫번째 목적지에서 포워딩시 request, response를 두번째 도착지로 전달했다.

- 그래서 각각 doGet 메소드에 대입되어있다.

- 첫번째 목적지에서 넘어온 파라미터를 두번째 서블릿의 doGet 메소드에서 고스란히 꺼낼 수 있다.

 

 

- 아래는 d_forward 폴더의 main.html 이다.

 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h2>forward 확인</h2>

    <a href="/servlet/first?name=김가온&age=20">첫번째 목적지 요청</a>

</body>
</html>
 

 

- 이건 get방식이라 doGet 메소드만 호출됨.

- forward는 요청의 방식을 유지하므로, GET이든 POST든 원래 요청된 방식을 그대로 사용합니다.

- 데이터를 전달하면서 요청을 보내본다. 

 

 

 

- 아래는 SecondStep.java

 

package d_forward;

 

import java.io.IOException;

import java.io.PrintWriter;

 

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

@WebServlet("/second")   //   http://localhost:8888/servlet/second라는 URL 요청시 실행될 서블릿

public class SecondStep extends HttpServlet {

 

    private static final long serialVersionUID = 1L;

 

    public SecondStep() { super(); }

 

 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

 

    System.out.println("두번째 목적지(도착지) 도착");

 

    //첫번째 목적지에서 포워딩시 request, response를 두번째 도착지로 전달했다.

    //그래서 각각 doGet 메소드에 대입되어있다.

    //첫번째 목적지에서 넘어온 파라미터를 두번째 서블릿의 doGet 메소드에서 고스란히 꺼낼 수 있다.

 

    System.out.println("param(name) : " + request.getParameter("name"));

    System.out.println("param(age) : " + request.getParameter("age"));

 

    // 응답

    response.setContentType("text/html; charset=utf-8");

    PrintWriter out = response.getWriter();

    out.println("<script>");

    out.println("alert('최종 목적지 도착!');");

    out.println("</script>");

    out.flush();

    out.close();

 

}

 

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

    doGet(request, response);

    }

 

}

 

- alert만 띄우는 것은 스크립트만 돌려줘도 잘 작동된다.

자바스크립트는 HTML과 독립적으로 실행될 수 있습니다.

HTML 문서가 자바스크립트를 로드하고 실행하는 구조를 제공할 뿐,

자바스크립트 자체는 특정 HTML 태그에 종속되지 않습니다.

 

 

- 새로고침하고 누르면

-  alert가 뜨고 빈 화면이다.

 

 

 

- 최종적으로는 second라는 url 요청이 되어서 SecondStep이 실행되고 있는데 

url에는 a태그에 작성한 링크만 노출된다. first만 보이고 second는 보이지 않는다.

forward로 이동한 경로(second)는 보이지 않는다.

- 지금은 서블릿에서 서블릿으로 이동했지만, 나중엔 서블릿에서 JSP로 이동할 것이다.

 

 

 

 

 

ㅁ 서블릿이 실행되는 경우 3가지

(1) 클라이언트 요청에 의해서 (a, form submit 등)

(2) 서블릿에서 또다른 서블릿으로 "이동"할 경우 (forward)

(3) 서블릿에서 또다른 서블릿을 "재호출"할 경우 (redirect)    <-    url을 다시 강제로 호출

 

 

 

ㅁ < forward >

- 특정 처리를 대신할 Servlet JSP로 이동시 사용하는 기술

주로 응답페이지 제작하는 과정을 JSP에게 위임할 때 사용할 예정

- 포워딩시 현재 서블릿에서 생성된 request, response 객체를 전달해야 한다.

이동된 곳에서 해당 request, response를 그대로 사용할 수 있다.

- 이동 경로 작성시 contextPath를 제외하고 작성.

서블릿으로 이동하고 싶으면 url 매핑값만,

특정 페이지로 이동하고 싶으면 contextPath를 제외한 나머지 페이지의 경로만 작성.

 

 

- ★★★ URL에는 forward된(이동된) 경로가 노출되지 않는다. ★★★

이게 포워드의 중요한 특징이다.

클라이언트는 이동된 경로를 확인할 수 없다. 클라이언트에게 이동한 경로를 숨길 수 있다.

클라이언트에게는 최초 요청했던 URL만 보여진다.

 

 

- 주로 forward 하는 경우

1) 단순히 페이지 이동할 때

- 경로작성해서 그냥 이동해도 되지만 url이 노출되니까 보안상 위험해서 이동한 경로를 숨기고자 할 때.

- 그냥 링크로 이동해도 되는데, 서블릿을 호출해서, 거기서 포워드 메소드로 페이지로 이동.

2) select 쿼리 이후 

- 조회 이후 forward를 굉장히 자주한다. db로부터 데이터를 조회 후 페이지로 이동할 때 forward 한다.

 

 

 

 

=========================================================================

 

 

 

ㅁ 5. Redirect

 

- 아래는 index.html이다.

 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <!-- web.xml 파일에서 welcome-file로 등록된 파일(메인페이지)  -->
    <h1>Servlet</h1>

    <!-- http://localhost:8888/servlet/a_lifecycle/main.html -->
    <a href="./a_lifecycle/main.html">1. Servlet 클래스의 LifeCycle</a> <br>

    <a href="./b_request/main.html">2. HttpServletRequest</a> <br>

    <!--  내가 요청할 페이지 : http://localhost:8888/servlet/c_response/main.html -->
    <!-- 상대경로 ./c_response/main.html -->
    <a href="/servlet/c_response/main.html">3. HttpServletResponse</a> <br>

    <a href="/servlet/d_forward/main.html">4. Forward</a> <br>

    <a href="/servlet/e_redirect/main.html">5. Redirect</a> <br>


</body>
</html>
 

 

- <a href="/servlet/e_redirect/main.html">  이런게 단순 페이지 이동이다.

 

 

 

- e_redirect 폴더에 main.html 파일을 아래와 같이 만든다.

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <h2>redirect 확인</h2>
   
</body>
</html>
 

 

 

- 이페이지가 잘 뜨는지 확인부터 한다.

 

 

- 5.Redirect를 누르면

 

 

- 잘 뜬다.

 

 

 

 

 

ㅁ forward 해야 하는 경우 1

- db로부터 데이터 조회 후 페이지로 이동해야될 때

 

 

- 아래는 main.html이다

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <h2>redirect 확인</h2>
 

    <a href="/servlet/list.bo">게시글 전체 조회</a>
   
</body>
</html>
 

 

- 'list.bo'가 게시글 전체 조회 요청하는 URL Mapping 값이다.

- '게시글 전체 조회'를 클릭하는 순간 게시글 전체 조회 데이터가 보여지는 정적 페이지로 곧바로 이동할 수 없다.

db에서 데이터를 조회해야하는데 db 데이터 조회를 서블릿에서 하니까 무조건 서블릿을 호출해야 한다.

- db로부터 데이터 조회해서 그 조회된 데이터로 페이지를 구성해야 할 때 서블릿을 호출해야만 한다.

 

 

 

- src/main/java에 e_redirect 패키지를 만들고 BoardList.java라는 서블릿을 아래처럼 만든다. 

 

package e_redirect;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

 

@WebServlet("/list.bo")

public class BoardList extends HttpServlet {

 

    private static final long serialVersionUID = 1L;

 

    public BoardList() { super(); }

 

 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

 

    System.out.println("게시글 전체 조회용 Servlet 실행 (JDBC과정을 통해 필요 데이터 select 진행)");

    // => service => dao => 게시글 전체 데이터 select 쿼리 실행 (JDBC)

    // 위와 같이 조회된 데이터를 가지고 응답페이지 제작

 

    // 위의 과정이 성공적으로 수행됐다는 가정 하에 응답페이지 : /servlet/e_redirect/boardList.html (html로 작성후 포워딩해서 띄운다.)

    request.getRequestDispatcher("/e_redirect/BoardList.html").forward(request, response);

 

    }

 

 

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

        doGet(request, response);

    }

 

}

 

- 아까처럼 response 객체를 이용해서 한땀한땀 사용자에게 응답페이지를 보여줄 수도 있지만,

너무 번거롭기 때문에 나중에 JSP 배우면 응답 페이지 제작 과정은 JSP에게 위임한다.

- 사용자에게는 boardList.html 페이지가 보여지지만 url에 보여지진 않는다. 처음 요청 url만 보여진다. 

forward는 이동경로가 노출되지 않는다.

 

 

 

 

- e_redirect 폴더 안에 boardList.html을 아래처럼 만든다.

 

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <h2>게시글 전체 리스트</h2>
   
    <p>
        조회된 전체 게시글 데이터가 보여지는 자리
    </p>

    <button>게시글 작성</button>

</body>
</html>
 

 

 

- 누르면

 

- 이렇게 잘 뜬다.

- url에는 boardList.html이라고는 뜨지 않는다. 최초 요청한 list.bo만 노출된다.

forward 방식으로 이동한 경로는 url에 노출되지 않는다.

 

- 이클립스 콘솔에도 이렇게 뜬다.

 

 

- 404 오류가 뜨는 이유는 서블릿을 못 찾을 거나 이동할 경로(html 페이지)를 못찾는 경우다.

대부분 오타다. 특히 대소문자.

서블릿 annotation과 포워드 메소드 getRequestDispatcher(이동할경로)에 오타가 없는지 체크한다. 

 

 

 

 

ㅁ boardList.html의 '게시글 작성' 버튼 클릭시 보여줄 페이지를 만든다.

- 게시글 작성 페이지는 db로부터 뭔가 조회할 필요가 없는 페이지다.

그냥 텍스트 상자 두 개가 끝이다. 그냥 정적인 페이지다.

 

 

- 아래는 webapp/e_redirect 폴더의 boardWrite.html이다.

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <h2>게시글 작성 페이지</h2>

    <form action="">
        제목 : <input type="text" name="title"> <br>              <!-- 값이 넘어가려면 key값을 부여해야 한다. -->
        내용 : <textarea name="content"></textarea> <br>   <!-- 얘도 key값 부여해야 값이 넘어간다. -->

        <input type="submit" value="등록하기">
    </form>
   
</body>
</html>
 

 

 

 

- 아래는 e_redirect 폴더의 boardList.html이다.

 

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <h2>게시글 전체 리스트</h2>
   
    <p>
        조회된 전체 게시글 데이터가 보여지는 자리
    </p>

    <button id="write-btn">게시글 작성</button>


    <script>
        // 버튼에 클릭 이벤트 핸들러를 연결한다.
        document.getElementById("write-btn").addEventListener("click", function() {
            location.href = '/servlet/e_redirect/boardWrite.html';
        })
    </script>


</body>
</html>
 

 

- button은 a 태그가 아니라서 href 속성을 사용할 수 없다. 이벤트 핸들러를 연결한다.

- location.href는 현재 페이지의 URL을 설정하는 속성입니다.

이 속성에 새로운 URL을 할당하면, 웹 브라우저는 해당 URL로 페이지를 이동합니다.

- 게시글 작성 페이지는 db로부터 조회해올 데이터가 없으므로 그냥 정적 페이지 이동을 할 수 있다. 

서블릿을 거치지 않아도 된다.

 

 

 

 

- 새로고침하고 눌러보면 아래와 같이 뜬다.

 

그런데 url에 다 노출이 된다. 보안에 문제가 있다.

프로젝트의 디렉토리 구조가 외부에 노출되면 무작위 공격이 들어올 수 있다.

- 그래서 db로부터 조회할 데이터 없는 단순 페이지 이동이라하더라도 서블릿을 호출해서 포워딩 방식으로 이동하는게 제일 좋다.

 

 

 

 

ㅁ forward 해야 하는 경우 2

- 정적페이지(DB로부터 데이터를 조회할 필요가 없는) 이동시 이동할 경로를 URL로부터 숨기고자 할 때

이럴 때도 서블릿을 호출해서 그 서블릿에서 포워딩을 통해서 페이지를 이동하는 것이 좋다.

 

 

- 아래는 boardList.html이다.

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <h2>게시글 전체 리스트</h2>
   
    <p>
        조회된 전체 게시글 데이터가 보여지는 자리
    </p>

    <button id="write-btn">게시글 작성</button>


    <script>
        // 버튼에 클릭 이벤트 핸들러를 연결한다.
        document.getElementById("write-btn").addEventListener("click", function() {

            // location.href = '/servlet/e_redirect/boardWrite.html';
            // 좋지 않은 방식. 위와 같이 바로 페이지의 경로를 작성해서 이동시
            // URL에 해당 애플리케이션 내부 디렉토리가 노출된다.

            location.href = '/servlet/writeForm.bo'; // 서블릿 호출

        })
    </script>


</body>
</html>
 
 

 

 

 

- src/main/java에 e_redirect 패키지에 BoardWriteForm이라는 서블릿 클래스를 만들고 주석으로 /writeForm.bo를 준다.

 

package e_redirect;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

 

@WebServlet("/writeForm.bo")

public class BoardWriteForm extends HttpServlet {

 

    private static final long serialVersionUID = 1L;

 

    public BoardWriteForm() { super(); }

 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

 

    System.out.println("게시글 작성 페이지 이동용 Servlet 실행 (JDBC 과정 없이 바로 페이지 이동)");

 

    // 응답페이지 : /servlet/e_redirect/boardWrite.html        

    request.getRequestDispatcher("/e_redirect/boardWrite.html").forward(request, response);

 

    }

 

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

        doGet(request, response);

    }

 

}

 

- '게시글 작성' 버튼에 이벤트 핸들러를 연결해서, 그냥 바로 정적인 페이지로 이동하면 경로가 노출되니까 그렇지 않게끔, servlet을 호출해서 forward로 정적인 페이지로 이동했다.

- 이 서블릿에서 forward로 이동!!

- jdbc 과정 없이 그냥 페이지 이동만 있는 서블릿 클래스다.

 

 

 

- '게시글 작성' 버튼을 클릭하면 아래와 같이 뜬다.

 

 

- 똑같이 뜨는데 아까와 경로가 다르다.

boardWrite.html이 노출되지 않고 처음 요청 보냈던 서블릿 url mapping 값(wirteForm.bo)만 보여진다.

 

 

 

- '게시글 전체 리스트'가 뜨면 첫째줄 나옴.

- '게시글 작성 페이지'가 뜨면 두번째줄 나옴.

 

 

 

=======================================================================

 

 

Redirect

- 이제 등록하기 버튼 누르면 redirect 할 예정이다.

 

- /servlet/list.bo 요청시 => 페이지1 (전체게시글 조회 페이지 = boardList.html)

- /servlet/writeForm.bo 요청시 => 페이지2 (게시글 작성 페이지 = boardWrite.html)

- 각각 페이지로 이동할 때 바로 이동하지 않고 서블릿을 거쳐서 이동했다.

 

 

 

 

- 이제 게시글 작성 페이지에서 '등록하기' 버튼을 누르면 사용자가 입력한 데이터가 db에 insert 되게 할 것이다

 

 

 

 

- 아래는 boardWrite.html이다.

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <h2>게시글 작성 페이지</h2>

    <form action="/servlet/insert.bo" method="post">
        제목 : <input type="text" name="title"> <br><!-- 값이 넘어가려면 key값을 부여해야 한다. -->
        내용 : <textarea name="content"></textarea> <br> <!-- 얘도 key값 부여해야 값이 넘어간다. -->

        <input type="submit" value="등록하기">
    </form>
   
</body>
</html>
 

 

- 매핑값이 insert.bo라는 서블릿 클래스가 필요하다.

 

 

 

- 이클립스에서 e_redirect 폴더 안에 BoardInsert 서블릿 클래스를 만든다. 

 

package e_redirect;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

@WebServlet("/insert.bo")

public class BoardInsert extends HttpServlet {

 

    private static final long serialVersionUID = 1L;

 

    public BoardInsert() { super(); }

 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

 

    System.out.println("게시글 등록 요청용 Servlet 실행 (JDBC 과정을 통해 데이터 insert 진행)");

 

 

    // 요청시 전달된 값을 뽑아서 각 변수에 기록

    request.setCharacterEncoding("UTF-8");

 

    String title = request.getParameter("title");

    String content = request.getParameter("content");

 

    // 원래대로라면 이제 service 호출하고 dao 호출해서 게시글 데이터 insert 쿼리를 진행한다.

    // 그리고 그 결과를 돌려받고 성공적으로 추가되었다는 가정 하에

    // 응답페이지 : 다시 게시글 전체 조회 페이지로 이동(단, 새로 추가된 데이터도 조회돼서 보여야 한다.)

 

    request.getRequestDispatcher("/e_redirect/boardList.html").forward(request, response);

 

    }

 

 

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

    doGet(request, response);

    }

 

}

 

- post방식 이라 post 메소드가 호출되었다가 doGet 메소드가 호출된다.

 

 

 

 

- 여기에 제목1, 내용1을 입력하고 등록하기 한다. (빨간 밑줄은 재사용한 거임. 신경 ㄴㄴ)

 

 

- 다시 이 페이지로 돌아온다. 

- 그런데 이건 잘못된 방식이다. 전체 리스트 화면에는 항상 list.bo가 찍혀있어야 하는데 insert.bo가 찍혀있다. 

db에 insert는 했지만, 조회하는 과정은 진행하지 않았다.

 

 

문제점1. 포워딩을 통해 해당 페이지가 보여지긴 하나 여전히 /servlet/insert.bo가 노출되어 있음.

문제점2. 새로 추가된 데이터를 다시 조회해서 뿌려야만 하는데 새로이 조회가 진행되지 않음.

 

 

  • forward 방식은 서버 내부에서만 요청을 전달하기 때문에, 클라이언트의 새 요청이 발생하지 않습니다.
  • 이 경우, 전달된 요청에 대한 서버의 응답도 기존 요청의 응답 흐름을 따르기 때문에 데이터가 업데이트되었더라도 새로운 요청을 통해 갱신된 데이터를 조회하지 않습니다.

 

- 전체게시글 조회페이지는 /servlet/list.bo가 찍혀있어야 한다. 다른 url은 안 된다.

게시글 작성페이지는 /servlet/writeForm.bo가 찍혀있어야 한다. 

 

 

 

 

 

ㅁ Redirect (재호출 = 재요청)

 

- 아래는 BoardInsert.java다.

 

package e_redirect;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

 

@WebServlet("/insert.bo")

public class BoardInsert extends HttpServlet {

 

    private static final long serialVersionUID = 1L;

 

    public BoardInsert() { super(); }

 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

 

    System.out.println("게시글 등록 요청용 Servlet 실행 (JDBC 과정을 통해 데이터 insert 진행)");

 

 

    // 요청시 전달된 값을 뽑아서 각 변수에 기록

    request.setCharacterEncoding("UTF-8");

 

    String title = request.getParameter("title");

    String content = request.getParameter("content");

 

    // 원래대로라면 이제 service 호출하고 dao 호출해서 게시글 데이터 insert 쿼리를 진행한다.

    // 그리고 그 결과를 돌려받고 성공적으로 추가되었다는 가정 하에

    // 응답페이지 : 다시 게시글 전체 조회 페이지로 이동(단, 새로 추가된 데이터도 조회돼서 보여야 한다.)

 

 

    /*

    해결방법.

    insert 후에 다시 select하고 게시글전체조회페이지로 이동해야됨

    근데 jdbc 과정으로 DB로부터 데이터를 조회해서 해당 페이지로 이동하는 Servlet이 이미 존재함

    (이미 구현했던 BoardList라는 서블릿이 존재함)

    => 해당 Servlet을 재 호출하면 됨.

    */

 

    response.sendRedirect("/servlet/list.bo"); // => list.bo의 Servlet을 재실행 (재요청)

 

    }

 

 

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

 

    doGet(request, response);

 

    }

 

}

 

- 곧바로 페이지 이동이 아니라, jdbc 과정으로 db로부터 데이터를 조회한 후에 페이지 이동했다. (서블릿으로)

forward가 아닌 redirect를 했다. 

- /list.bo라는 url mapping 값을 가지는 boardList라는 서블릿이 구동된다.

jdbc 과정을 통해서 조회가 진행되고, 포워딩 방식으로 boardList.html 페이지로 이동도 진행된다.

 

 

- 수시로 이클립스, vscode 저장하고 서버 재시작, 프로젝트-클린, 브라우저 새로고침 한다. 

 

- 등록하기를 누르면 

 

- list.bo로 노출된다. 

 

 

- 어떨때 포워딩하고 redirect하는지가 문제다.

요청 처리 후에 어떤 과정을 진행시키고 싶은지를 생각해보라. 

근데 이미 그 과정을 해주는 서블릿이 있다면 그 서블릿을 redirect로 재호출하면 된다.

 

 

 

 

ㅁ < redirect >

- 기존에 정의해둔 Servlet을 재실행 시키는 개념 (URL 요청과 동일)

- 등록 및 수정/삭제 후에 다시 조회 페이지를 요청할 때 주로 사용함

- redirect 시 현재 서블릿에 존재하는 request, response를 전달하지 않음.

서블릿이 새로 실행되는 것이기 때문에 저 객체들도 다시 생성된다.

- 요청할 URL 작성시 contextPath부터 작성

(action에 작성했던 것처럼 절대경로 방식으로)

 

    response.sendRedirect("/servlet/list.bo");           <- 이 경로가 보여지게 된다.

 

 

- 클라이언트 측에서 redirect된 경로(최종 목적지)를 확인할 수 있음

URL에 현재 redirect한 URL이 보여진다.

 

- 주로 redirect 하는 경우

1) insert 쿼리 이후 => 게시글의 전체 조회 페이지 redirect

2) update 쿼리 이후 => 게시글의 상세 조회 페이지 redirect

3) delete 쿼리 이후 => 게시글의 전체 조회 페이지 redirect