no image
JSP와 서블릿을 이용한 MVC 프레임워크 만들기
이 글은 인프런 김영한님의 Spring 강의를 바탕으로 개인적인 정리를 위해 작성한 글입니다. Model, View, Controller 컨트롤러: HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행한다. 그리고 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다. 모델: 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아서 전달해주는 덕분에 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있다. 뷰: 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다. 여기서는 HTML을 생성하는 부분을 말한다. 참고 컨트롤러에 비즈니스 로직을 둘 수도 있지만, 이렇게 되면 컨트롤러가 너무 많은 역할을 담당한다. 그래서 일반적으로 비즈니..
2024.02.17
no image
[JSP] JSTL(JSP Standard Tag Library)
실행환경 windows 11 pro IntelliJ : 23.3.2 Apache Tomcat : 9.0 JDK : 17.0.9 라이브러리 추가 JSTL이란 JSP Standard Tag Library를 뜻한다. 먼저 JSTL JAR 파일을 받아야 한다. 아래의 링크에서 다운로드를 진행해야 한다. https://mvnrepository.com/artifact/javax.servlet/jstl/1.2 빨간색으로 표시된 부분을 클릭해서 jar 파일을 다운로드 받자. 인텔리제이로 돌아와서 Project Structure를 클릭한다. modules - 해당 프로젝트 선택 후 오른쪽에 보이는 +(Add) 버튼을 클릭한다. 이후 JARs or Dircetories.. 버튼을 눌러서 아까 다운로드 받은 JAR 파일을 ..
2024.01.10
no image
[JSP] IntelliJ에서 Servlet 생성
개발환경 windows 11 pro jdk : 17.0.9 Apache Tomcat : 9.0 intelliJ : 2023.3.2 프로젝트에서 new 를 할 때 서블릿 추가 버튼이 없는 사람을 위한 글이다. Servlet 생성 새로운 프로젝트를 생성한다. Add Frameworks Support에서 Java EE 에서 아래 부분을 체크하고 apply - ok 를 누른다. Project Structure - Libraries - +버튼 - From Maven을 누른다. 톰캣 9.0 기준 javax.servlet:javax.servlet-api:4.0.1 를 검색한다(오른쪽 검색버튼을 누르고 기다리면 됌) 프로젝트를 선택하고 ok를 누른다. servlet api가 추가된 것을 확인하고 apply - ok 버..
2024.01.09
no image
[JSP] IntelliJ 에서 JSP 개발환경 만들기
개발 환경 windows 11 pro jdk : 17.0.9 Apache Tomcat : 10.1.17 intelliJ : 2023.3.2 IntelliJ에서 JSP 개발환경 초기 설정 인텔리제이를 실행하고 프로젝트를 만든다. 우측 상단에 돋보기 버튼을 누른다. add framework support 를 검색하고 클릭한다. Java EE - web applition을 체크하고 Create web.xml 도 체크하고 ok를 누른다. 이후 우측 상단의 current file 의 드랍다운 박스를 눌러서 Edit configurations...를 누른다. 아래와 같은 화면이 나오면 왼쪽 상단의 + 버튼을 누른다. 이후 Tomcat Server - Local을 누른다. Application server에서 Con..
2024.01.08
no image
[JSP] 파일 업로드
위 파일을 프로젝트 bin 폴더에 추가한다. FileUp.jsp 파일 업로드 이름 파일 선택 파일 업로드는 form태그에 을 통해서 하게 된다. 하지만 별다른 처리를 하지 않으면 실제로 넘어오는건 업로드한 파일이름만 넘어오고, 파일자체는 넘어오지 않는다. 먼저 파일업로드를 받기 위해서는 enctype="multipart/form-data" 속성을 추가해줘야 한다. FileUploadProc.jsp 동일한 이름이 있을경우 파일 이름을 자동 변경 MultipartRequest multi = new MultipartRequest(request, realfolder, maxSize, encType, new DefaultFileRenamePolicy()); %> 당신의 이름은 :
2023.12.25
no image
[JSP] JSP와 데이터베이스 연동
JDBC(Java DataBase Connectivity) Java에서 DBMS의 종류와 관계없이 데이터베이스를 조작하기 위한 API(Application Programming Interface)를 의미 JDBC를 간단하게 요약하면 메소드 호출용 SQL 인터페이스라고 표현할 수 있음 JDBC 드라이버 다양한 DBMS 제조사들은 본사에서 개발한 DBMS를 Sun사의 Java 프로그램과연동할 수 있도록 기술을 지원하는 것을 의미 JDBC는 MySQL 설치과정에서 이미 설치하였으므로 따로 설치할 필요는 없지만 JDBC 드라이버가 어느 폴더에 저장되어 있는지에 대해서는 알고 있어야 함 JSP프로젝트 내에 lib폴더에 JDBC 드라이버를 추가해준다. 자세한 JDBC에 대한 내용은 아래의 포스트를 참고 2023.0..
2023.11.27
no image
[JSP] 액션 태그(Action tag)
액션 태그 JSP에서 기본으로 제공하는 태그들의 집합으로 서버 또는 클라이언트에게 수행할 명령을 지시 액션 태그를 사용하게 되면 Java 코드를 사용하지 않아도 JSP 웹페이지를 개발할 수 있음 액션 태그는 XML 형식인 를 사용하며 끝나는 태그는 반드시 />로 마무리해야 함 액션 태그는 JSP 웹페이지를 코딩할 때 Java 코드의 작성을 피하거나 최소화하기 위해 사용 JSP 웹페이지에서 Java 코드를 최소화하게 되면 소스 코드에 대한 유지/보수를 효율적으로 수행 가능 param 현재 위치한 JSP 웹페이지에서 다른 웹페이지로 정보를 전달할 때 사용하는 태그 param 액션 태그는 단독으로 사용할 수 없으므로 태그나 태그의 내부에 선언하여 사용 param 액션 태그는 여러 개의 파라미터를 선언하여 다른..
2023.11.27
no image
[JSP] 세션(Session)
세션 네트워크 환경에서 클라이언트와 웹 서버 간의 상태를 지속적으로 유지하기 위한 방법을 의미 세션은 서버 공간에 생성되므로 보안 유지에 유리하지만 데이터를 저장하기 위한 한계성에 대한 문제는 존재함 세션은 클라이언트의 요청에 따라 접속된 웹 서버와 가상으로 연결된 상태를 유지하도록 해 줌 세션에 의한 클라이언트 구분 세션은 웹 서버 공간에 생성되는 객체로 웹 브라우저마다 하나씩 존재 웹 서버와의 접속을 통해 생성된 세션은 네트워크 환경에서 여러 사용자 중 특정인에 대한 구분자의 역할을 수행 세션을 통해 접속된 웹 브라우저를 닫기 전까지는 웹페이지를 이동하더라도 사용자에 대한 정보가 웹 서버에 객체 상태로 저장되어 있으므로 사용자 정보를 지속적으로 활용할 수 있게 됨 세션과 쿠키 웹 브라우저에서 서버로 ..
2023.11.25

이 글은 인프런 김영한님의 Spring 강의를 바탕으로 개인적인 정리를 위해 작성한 글입니다.


Model, View, Controller

  • 컨트롤러: HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행한다. 그리고 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.
  • 모델: 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아서 전달해주는 덕분에 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있다. 
  • 뷰: 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다. 여기서는 HTML을 생성하는 부분을 말한다. 

 

참고
컨트롤러에 비즈니스 로직을 둘 수도 있지만, 이렇게 되면 컨트롤러가 너무 많은 역할을 담당한다.
그래서 일반적으로 비즈니스 로직은 서비스(Service)라는 계층을 별도로 만들어서 처리한다.
그리고 컨트롤러는 비즈니스 로직 이 있는 서비스를 호출하는 역할을 담당한다. 참고로 비즈니스 로직을 변경하면 비즈니스 로직을 호출하는 컨트롤러의 코드도 변경될 수 있다.
앞에서는 이해를 돕기 위해 비즈니스 로직을 호출한다는 표현 보다는, "비즈니스 로직을 실행한다" 고 설명했다.

 

redirect vs forward
리다이렉트는 실제 클라이언트(웹 브라우저)에 리다이렉트 경로를 알려주고, 클라이언트가 redirect 경로로 다시 요청한다. 따라서 클라이언트가 인지할 수 있고, URL 경로도 실제로 변경된다.
반면에 포워드는 서버 내부에서 일어나는 호출이기 때문에 클라이언트가 전혀 인지하지 못한다.
즉, 포워드는 사용자의 요청에 따라 동적으로 컨텐츠를 생성해서 경로 변경없이 보여준다.
/WEB-INF
이 경로안에 JSP가 있으면 외부에서 직접 JSP를 호출할 수 없다.
즉, 이 경로에 있으면 항상 컨트롤러를 통해서 JSP를 호출할 수 있다다.

 

프론트 컨트롤러 패턴

FrontController 패턴 특징

  • 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
  • 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출
  • 공통 처리 가능
  • 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨

 

스프링 웹 MVC와 프론트 컨트롤러

  • 스프링 웹 MVC의 핵심도 바로 FrontController
  • 스프링 웹 MVC의 DispatcherServlet이 FrontController 패턴으로 구현되어 있음

 

MVC  프레임워크 만들기

서블릿을 컨트롤러로 사용하고, JSP를 뷰로 사용해서 MVC 패턴을 적용한다.
Model
HttpServletRequest 객체를 사용한다.

request
request는 내부에 데이터 저장소를 가지고 있다.
- request.setAttribute() : 데이터 저장
- request.getAttribute() : 데이터 조회

 

View를 담당하는 JSP

members.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="/index.html">메인</a>
<table>
    <thead>
    <th>id</th>
    <th>username</th>
    <th>age</th>
    </thead>
    <tbody>
    <c:forEach var="item" items="${members}">
        <tr>
            <td>${item.id}</td>
            <td>${item.username}</td>
            <td>${item.age}</td>
        </tr>
    </c:forEach>
    </tbody>
</table>
</body>
</html>

 

new-form.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!-- 상대경로 사용, [현재 URL이 속한 계층 경로 + /save] -->
<form action="save" method="post">
    username: <input type="text" name="username" />
    age: <input type="text" name="age" />
    <button type="submit">전송</button>
</form>
</body>
</html>

 

save-result.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
</head>
<body>
성공
<ul>
    <li>id=${member.id}</li>
    <li>username=${member.username}</li>
    <li>age=${member.age}</li>
</ul>
<a href="/index.html">메인</a>
</body>
</html>

 

프론트 컨트롤러 도입 - v1

 

프론트 컨트롤러 V1 인터페이스

package hello.servlet.web.frontcontroller.v1;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

public interface ControllerV1 {
    void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}

서블릿과 비슷한 모양의 컨트롤러 인터페이스를 도입한다. 각 컨트롤러들은 이 인터페이스를 구현하면 된다. 프론트 컨 트롤러는 이 인터페이스를 호출해서 구현과 관계없이 로직의 일관성을 가져갈 수 있다.

 

MemberFormControllerV1 - 회원 등록 컨트롤러

package hello.servlet.web.frontcontroller.v1.controller;

import hello.servlet.web.frontcontroller.v1.ControllerV1;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class MemberFormControllerV1 implements ControllerV1 {
    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String viewPath = "/WEB-INF/views/new-form.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}
RequestDispatcher
요청을 다른 리소스(서블릿, JSP 파일 또는 HTML 파일)로 전송하는 데 사용되는 객체이다.
이를 통해 서블릿 간에 제어를 전달하거나, 데이터를 포함한 요청을 서블릿이나 JSP로 포워드하거나, 요청 처리 결과를 포함하는 컨텐츠를 클라이언트에게 직접 포함하여 응답할 때 사용된다. 주로 서버 내에서의 요청 전달(포워드)과 포함(인클루드)에 사용된다.
dispatcher.forward()
다른 서블릿이나 JSP로 이동할 수 있는 기능.
서버 내부에서 다시 호출이 발생.

 

MemberSaveControllerV1 - 회원 저장 컨트롤러

package hello.servlet.web.frontcontroller.v1.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.v1.ControllerV1;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class MemberSaveControllerV1 implements ControllerV1 {
    private MemberRepository memberRepository = MemberRepository.getInstance();
    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);

        request.setAttribute("member", member);

        String viewPath = "/WEB-INF/views/save-result.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}

 

MemberListControllerV1 - 회원 목록 컨트롤러

package hello.servlet.web.frontcontroller.v1.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.v1.ControllerV1;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;

public class MemberListControllerV1 implements ControllerV1 {
    private MemberRepository memberRepository = MemberRepository.getInstance();
    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();
        request.setAttribute("members", members);

        String viewPath = "/WEB-INF/views/members.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}

 

FrontControllerServletV1 - 프론트 컨트롤러

package hello.servlet.web.frontcontroller.v1;

import hello.servlet.web.frontcontroller.v1.ControllerV1;
import hello.servlet.web.frontcontroller.v1.controller.MemberFormControllerV1;
import hello.servlet.web.frontcontroller.v1.controller.MemberListControllerV1;
import hello.servlet.web.frontcontroller.v1.controller.MemberSaveControllerV1;
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.util.HashMap;
import java.util.Map;

@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*") ///front-controller/v1` 를 포함한 하위 모든 요청은 이 서블릿에서 받아들인다.
public class FrontControllerServletV1 extends HttpServlet {
    private Map<String, ControllerV1> controllerMap = new HashMap<>();

    public FrontControllerServletV1() {
        //맵에 url 경로와 컨트롤러를 저장
        controllerMap.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
        controllerMap.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
        controllerMap.put("/front-controller/v1/members", new MemberListControllerV1());
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("FrontControllerServletV1.service");
        String requestURI = request.getRequestURI(); //들어온 경로를 저장
        
        ControllerV1 controller = controllerMap.get(requestURI); //들어온 경로에 맞게 컨트롤러를 맵핑
        if(controller == null) { //이상한 경로로 들어온다면
            response.setStatus(HttpServletResponse.SC_NOT_FOUND); //404 에러
            return;
        }
        controller.process(request, response); //해당 컨트롤러에 있는 process 메소드 호출
    }
}

URL에 따라 컨트롤러가 정해지고, 해당 컨트롤러의 process 메소드가 호출된다.

아래의 코드와 같이 모든 컨트롤러에서 뷰로 이동하는 부분에 중복이 있고, 깔끔하지 않다.

String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);

 

View 분리 - v2

 

MyView

package hello.servlet.web.frontcontroller;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Map;

public class MyView {
    private String viewPath;

    public MyView(String viewPath) {
        this.viewPath = viewPath;
    }

    public void render(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
         RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
         dispatcher.forward(request, response);
     }

}

 

프론트 컨트롤러 V2 인터페이스

 

package hello.servlet.web.frontcontroller.v2;

import hello.servlet.web.frontcontroller.MyView;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public interface ControllerV2 {
    MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}

 

MemberFormControllerV2 - 회원 등록 폼

package hello.servlet.web.frontcontroller.v2.controller;

import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v2.ControllerV2;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class MemberFormControllerV2 implements ControllerV2 {
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        return new MyView("/WEB-INF/views/new-form.jsp");
    }
}

각 컨트롤러는 복잡한 dispatcher.forward() 를 직접 생성해서 호출하지 않아도 된다.

단순히 MyView 객체를 생성하고 거기에 뷰 이름만 넣고 반환하면 된다.

 

MemberSaveControllerV2 - 회원 저장

package hello.servlet.web.frontcontroller.v2.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v2.ControllerV2;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class MemberSaveControllerV2 implements ControllerV2 {
    private MemberRepository memberRepository = MemberRepository.getInstance();
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);

        request.setAttribute("member", member);

        return new MyView("/WEB-INF/views/save-result.jsp");
    }
}

 

MemberListControllerV2 - 회원 목록

package hello.servlet.web.frontcontroller.v2.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v2.ControllerV2;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;

public class MemberListControllerV2 implements ControllerV2 {
    private MemberRepository memberRepository = MemberRepository.getInstance();
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();
        request.setAttribute("members", members);

        return new MyView("/WEB-INF/views/members.jsp");
    }
}

 

프론트 컨트롤러 V2

package hello.servlet.web.frontcontroller.v2;

import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v2.controller.MemberFormControllerV2;
import hello.servlet.web.frontcontroller.v2.controller.MemberListControllerV2;
import hello.servlet.web.frontcontroller.v2.controller.MemberSaveControllerV2;
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.util.HashMap;
import java.util.Map;

@WebServlet(name = "frontControllerServletV2", urlPatterns = "/front-controller/v2/*")
public class FrontControllerServletV2 extends HttpServlet {
    private Map<String, ControllerV2> controllerMap = new HashMap<>();

    public FrontControllerServletV2() {
        controllerMap.put("/front-controller/v2/members/new-form", new MemberFormControllerV2());
        controllerMap.put("/front-controller/v2/members/save", new MemberSaveControllerV2());
        controllerMap.put("/front-controller/v2/members", new MemberListControllerV2());
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String requestURI = request.getRequestURI();

        ControllerV2 controller = controllerMap.get(requestURI);

        if(controller == null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        MyView view = controller.process(request,response);
        view.render(request, response);
    }
}

ControllerV2의 반환 타입이 MyView 이므로 프론트 컨트롤러는 컨트롤러의 호출 결과로 MyView 를 반환 받는다. 그리고view.render() 를 호출하면 forward 로직을 수행해서 JSP가 실행된다.

프론트 컨트롤러의 도입으로 MyView 객체의 render() 를 호출하는 부분을 모두 일관되게 처리할 수 있다. 각각의 컨트롤러는 MyView 객체를 생성만 해서 반환하면 된다.

 

하지만 MyView는 HttpServletRequest, HttpServletResponse 가 필요하지만, 컨트롤러는 HttpServletRequest, HttpServletResponse가 필요없다. -> 서블릿 종속성

V2는 컨트롤러까지 HttpServletRequest, HttpServletResponse 를 전달한다는 단점이 존재한다.

또한 컨트롤러에서 지정하는 뷰 이름에 중복이 있다.

 

Model 추가 - v3

V3는 서블릿 종속성과 뷰 이름 중복을 제거하는 것이 핵심이다.

 

ModelView

서블릿의 종속성을 제거하기 위해 Model을 직접 만들고, 추가로 View 이름까지 전달하는 객체를 만든다.

즉 컨트롤러에서 HttpServletRequest를 사용하지 않는다.

package hello.servlet.web.frontcontroller;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class ModelView {
    private String viewName;
    private Map<String, Object> model = new HashMap<>();

    public ModelView(String viewName) {
        this.viewName = viewName;
    }

    public String getViewName() {
        return viewName;
    }

    public void setViewName(String viewName) {
        this.viewName = viewName;
    }

    public Map<String, Object> getModel() {
        return model;
    }

    public void setModel(Map<String, Object> model) {
        this.model = model;
    }
}

뷰의 이름과 뷰를 렌더링할 때 필요한 model 객체를 가지고 있다. model은 단순히 map으로 되어 있으므로 컨트롤러에서 뷰에 필요한 데이터를 key, value로 넣어주면 된다.

여기서 사용하는 Map은 <String, Object>이다.

Object에 다양한 객체가 저장된다.

 

프론트 컨트롤러 V3 인터페이스

package hello.servlet.web.frontcontroller.v3;

import hello.servlet.web.frontcontroller.ModelView;

import java.util.Map;

public interface ControllerV3 {
    ModelView process(Map<String, String> paramMap);
}

이 컨트롤러는 서블릿 기술을 전혀 사용하지 않는다. 따라서 구현이 매우 단순해지고, 테스트 코드 작성시 테스트 하기 쉽다.
HttpServletRequest
가 제공하는 파라미터는 프론트 컨트롤러가 paramMap에 담아서 호출해주면 된다.
응답 결과로 뷰 이름과 뷰에 전달할 Model 데이터를 포함하는 ModelView 객체를 반환하면 된다.

 

MemberFormControllerV3 - 회원 등록 폼

package hello.servlet.web.frontcontroller.v3.controller;

import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;

import java.util.Map;

public class MemberFormControllerV3 implements ControllerV3 {
    @Override
    public ModelView process(Map<String, String> paramMap) {
        return new ModelView("new-form");
    }
}

ModelView 를 생성할 때 new-form 이라는 view의 논리적인 이름을 지정한다. 실제 물리적인 이름은 프론트 컨트롤러에서 처리한다.

 

MemberSaveControllerV3 - 회원 저장

package hello.servlet.web.frontcontroller.v3.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;

import java.util.Map;

public class MemberSaveControllerV3 implements ControllerV3 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public ModelView process(Map<String, String> paramMap) {
        String username = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));

        Member member= new Member(username, age);
        memberRepository.save(member);

        ModelView mv = new ModelView("save-result");
        mv.getModel().put("member", member);
        return mv;
    }
}

mv.getModel().put("member", member);`
-> 모델<String, Object> 에 뷰에서 필요한 member 객체를 담고 반환한다.

 

MemberListControllerV3 - 회원 목록

package hello.servlet.web.frontcontroller.v3.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;

import java.util.List;
import java.util.Map;

public class MemberListControllerV3 implements ControllerV3 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public ModelView process(Map<String, String> paramMap) {
        List<Member> members = memberRepository.findAll();

        ModelView mv = new ModelView("members");
        mv.getModel().put("members", members);

        return mv;
    }
}

mv.getModel().put("members", members);`
-> 모델<String, Object> 에 뷰에서 필요한 members 객체를 담고 반환한다.

 

FrontControllerServletV3

package hello.servlet.web.frontcontroller.v3;

import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v3.controller.MemberFormControllerV3;
import hello.servlet.web.frontcontroller.v3.controller.MemberListControllerV3;
import hello.servlet.web.frontcontroller.v3.controller.MemberSaveControllerV3;
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.util.HashMap;
import java.util.Map;

@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")
public class FrontControllerServletV3 extends HttpServlet {
    private Map<String, ControllerV3> controllerMap = new HashMap<>();

    public FrontControllerServletV3() {
        controllerMap.put("/front-controller/v3/members/new-form", new MemberFormControllerV3());
        controllerMap.put("/front-controller/v3/members/save", new MemberSaveControllerV3());
        controllerMap.put("/front-controller/v3/members", new MemberListControllerV3());
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String requestURI = request.getRequestURI(); //현재 경로를 가져옴

        ControllerV3 controller = controllerMap.get(requestURI);
        if(controller == null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        Map<String, String> paramMap = createParamMap(request); //request의 파라미터를 모두 저장
        ModelView mv = controller.process(paramMap); //선택된 컨트롤러의 process 메서드에 paramMap을 전달 -> 이후 리턴된 ModelView 객체에 저장

        String viewName = mv.getViewName(); //ModelView의 논리적 경로를 가져옴
        MyView view = viewResolver(viewName); //뷰 리졸버를 통해 논리적 뷰를 실제 뷰의 경로를 가져옴
        view.render(mv.getModel(), request, response); // MyView의 render 메서드를 통해서 뷰를 렌더링
    }

    private Map createParamMap(HttpServletRequest request) {
        Map<String, String> paramMap = new HashMap<>();
        request.getParameterNames().asIterator()
                .forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName))); //request의 모든 내용을 맵에 저장
        return paramMap;
    }

    private MyView viewResolver(String viewName) {
        return new MyView("/WEB-INF/views/" + viewName + ".jsp"); //논리적 뷰 경로를 이용하여 실제 뷰경로를 리턴
    }

}
request.getParameterNames().asIterator()
                .forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));

request.getParameterNames() 메소드를 호출하여 요청에 포함된 모든 파라미터의 이름을 Enumeration<String> 타입으로 반환한다.

asIterator() 메소드는 이 Enumeration을 Iterator로 변환하여, 스트림 연산이나 반복 연산을 사용할 수 있게 한다.

forEachRemaining 메소드는 Iterator의 모든 요소에 대해 주어진 액션(여기서는 람다 표현식)을 실행한다. 이 메소드는 Iterator에 남아 있는 각 요소에 대해 한 번씩 액션을 수행한다.

람다 표현식 paramName -> paramMap.put(paramName, request.getParameter(paramName))는 각 파라미터 이름(paramName)에 대해 다음을 수행한다
- request.getParameter(paramName)을 호출하여 해당 파라미터의 값을 가져온다.
- 가져온 파라미터 이름과 값을 paramMap에 저장한다. 여기서 paramMap은 파라미터 이름을 키로, 파라미터 값을 값으로 하는 Map 객체이다.

 

 

MyView

package hello.servlet.web.frontcontroller;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Map;

public class MyView {
    private String viewPath;

    public MyView(String viewPath) {
        this.viewPath = viewPath;
    }

    public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }

    public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        modelToRequestAttribute(model, request); //모델의 내용을 request의 파라미터로 변환
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }

    private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
        model.forEach((key, value) -> request.setAttribute(key, value)); //모델의 모든 키-밸류를 request의 파라미터로 변환
        //setAttribute(String name, Object value)
    }
}

모델의 키와 밸류 값을 request로 변환하는 부분이 핵심이다.

 

v3 컨트롤러는 서블릿 종속성을 제거하고 뷰 경로의 중복을 제거하는 등, 잘 설계된 컨트롤러이다. 그런데 실제 컨트톨러 인터페이스를 구현하는 개발자 입장에서 보면, 항상 ModelView 객체를 생성하고 반환해야 하는 부분이 조금은 번거롭다.

 

좋은 프레임워크는 아키텍처도 중요하지만, 그와 더불어 실제 개발하는 개발자가 단순하고 편리하게 사용할 수 있어야 한다. 소위 실용성이 있어야 한다.

 

 

단순하고 실용적인 컨트롤러 - v4

v3를 조금 변경해서 실제 구현하는 개발자들이 매우 편리하게 개발할 수 있는 v4 버전을 개발해보자.

기본적인 구조는 V3와 같다. 대신에 컨트롤러가 ModelView(객체) 를 반환하지 않고, ViewName(String) 만 반환한다.

 

 

프론트 컨트롤러 V4 인터페이스

package hello.servlet.web.frontcontroller.v4;
import java.util.Map;
public interface ControllerV4 {
    String process(Map<String, String> paramMap, Map<String, Object> model);
}

 

 

MemberFormControllerV4

package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import java.util.Map;
public class MemberFormControllerV4 implements ControllerV4 {
    @Override
    public String process(Map<String, String> paramMap, Map<String, Object> model) {
        return "new-form";
    }
}

리턴 타입이 String이다.

즉, 컨트롤러는 단순히 뷰의 논리적 경로만 반환한다.

 

MemberSaveControllerV4

package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import java.util.List;
import java.util.Map;
public class MemberListControllerV4 implements ControllerV4 {
    private MemberRepository memberRepository = MemberRepository.getInstance();
    @Override
    public String process(Map<String, String> paramMap, Map<String, Object> model) {
        List<Member> members = memberRepository.findAll();
        model.put("members", members);
        return "members";
    }
}

모델이 파라미터로 전달되기 때문에, 모델을 직접 생성하지 않아도 된다.

 

MemberListControllerV4

package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import java.util.Map;
public class MemberSaveControllerV4 implements ControllerV4 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public String process(Map<String, String> paramMap, Map<String, Object> model) {
        String username = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);
        model.put("member", member);

        return "save-result";
    }
}

 

 

FrontControllerServletV4

package hello.servlet.web.frontcontroller.v4;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v4.controller.MemberFormControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberListControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberSaveControllerV4;
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.util.HashMap;
import java.util.Map;
@WebServlet(name = "frontControllerServletV4", urlPatterns = "/front-controller/v4/*")
public class FrontControllerServletV4 extends HttpServlet {
     private Map<String, ControllerV4> controllerMap = new HashMap<>();
     public FrontControllerServletV4() {
         controllerMap.put("/front-controller/v4/members/new-form", new MemberFormControllerV4());
         controllerMap.put("/front-controller/v4/members/save", new MemberSaveControllerV4());
         controllerMap.put("/front-controller/v4/members", new MemberListControllerV4());
     }

     @Override
     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         String requestURI = request.getRequestURI(); //경로를 가져옴
         ControllerV4 controller = controllerMap.get(requestURI); //경로에 맞는 컨트롤러 맵핑
         if (controller == null) {
             response.setStatus(HttpServletResponse.SC_NOT_FOUND);
             return;
         }

         Map<String, String> paramMap = createParamMap(request); //request의 파라미터를 모두 저장
         Map<String, Object> model = new HashMap<>(); //모델 생성

         String viewName = controller.process(paramMap, model);// 해당 컨트롤러 호출을 통해 논리적 뷰 경로이름을 받아옴
         MyView view = viewResolver(viewName); //뷰 리졸버를 통해 실제 뷰 경로를 받아옴
         view.render(model, request, response); //MyView의 render 메서드를 통해서 뷰를 렌더링
     }
     private Map<String, String> createParamMap(HttpServletRequest request) {
         Map<String, String> paramMap = new HashMap<>();
         request.getParameterNames().asIterator().forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));

         return paramMap;
     }
     private MyView viewResolver(String viewName) {
        return new MyView("/WEB-INF/views/" + viewName + ".jsp");
     }
}

 

Map<String, Object> model = new HashMap<>();

모델 객체를 프론트 컨트롤러에서 생성해서 넘겨준다. 컨트롤러에서 모델 객체에 값을 담으면 여기에 그대로 담겨있게 된다.

 

 String viewName = controller.process(paramMap, model);
 MyView view = viewResolver(viewName);

컨트롤러가 직접 뷰의 논리 이름을 반환하므로 이 값을 사용해서 실제 물리 뷰를 찾을 수 있다.

 

유연한 컨트롤러 - v5

만약 어떤 개발자는 `ControllerV3` 방식으로 개발하고 싶고, 어떤 개발자는 `ControllerV4` 방식으로 개발하고 싶다면 어댑터 패턴을 사용해서 프론트 컨트롤러가 다양한 방식의 컨트롤러를 처리할 수 있도록 변경해야 한다.

  • 핸들러 어댑터: 중간에 어댑터 역할을 하는 어댑터가 추가되었는데 이름이 핸들러 어댑터이다. 여기서 어댑터 역 할을 해주는 덕분에 다양한 종류의 컨트롤러를 호출할 수 있다.
  • 핸들러: 컨트롤러의 이름을 더 넓은 범위인 핸들러로 변경했다. 그 이유는 이제 어댑터가 있기 때문에 꼭 컨트롤러 의 개념 뿐만 아니라 어떠한 것이든 해당하는 종류의 어댑터만 있으면 다 처리할 수 있기 때문이다.

 

 

어댑터용 인터페이스

package hello.servlet.web.frontcontroller.v5;

import hello.servlet.web.frontcontroller.ModelView;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.logging.Handler;

public interface MyHandlerAdapter {
    boolean supports(Object handler);

    ModelView handle(HttpServletRequest request, HttpServletResponse response, Object Handler) throws ServletException, IOException;
}

 

boolean supports(Object handler)

  • handler는 컨트롤러를 말한다.
  • 어댑터가 해당 컨트롤러를 처리할 수 있는지 판단하는 메서드다.

 

ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler)

  • 어댑터는 실제 컨트롤러를 호출하고, 그 결과로 ModelView를 반환해야 한다.
  • 실제 컨트롤러가 ModelView를 반환하지 못하면, 어댑터가 ModelView를 직접 생성해서라도 반환해야 한다.
  • 이전에는 프론트 컨트롤러가 실제 컨트롤러를 호출했지만 이제는 이 어댑터를 통해서 실제 컨트롤러가 호출된다.

 

ControllerV3HandlerAdapter

ControllerV3를 지원하는 어댑터

package hello.servlet.web.frontcontroller.v5.adapter;

import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;
import hello.servlet.web.frontcontroller.v5.MyHandlerAdapter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class ControllerV3HandlerAdapter implements MyHandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof ControllerV3);
    }

    @Override
    public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object Handler) throws ServletException, IOException {
        ControllerV3 controller = (ControllerV3) Handler;

        Map<String, String> paramMap = createParamMap(request);

        ModelView mv = controller.process(paramMap);
        return mv;
    }

    private Map<String, String> createParamMap(HttpServletRequest request) {
        Map<String, String> paramMap = new HashMap<>();
        request.getParameterNames().asIterator()
                .forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
        return paramMap;
    }
}

 

 

ControllerV4HandlerAdapter

package hello.servlet.web.frontcontroller.v5.adapter;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import hello.servlet.web.frontcontroller.v5.MyHandlerAdapter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.util.HashMap;
import java.util.Map;
public class ControllerV4HandlerAdapter implements MyHandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof ControllerV4);
    }
    @Override
    public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        ControllerV4 controller = (ControllerV4) handler;
        Map<String, String> paramMap = createParamMap(request);
        Map<String, Object> model = new HashMap<>();
        String viewName = controller.process(paramMap, model);

    	//중요
    	//`ControllerV4` 는 뷰의 이름을 반환했지만, 어댑터는 이것을 ModelView로 만들어서 형식을 맞추어 반환
        ModelView mv = new ModelView(viewName);
        mv.setModel(model);

        return mv;
    }
    private Map<String, String> createParamMap(HttpServletRequest request) {
        Map<String, String> paramMap = new HashMap<>();
        request.getParameterNames().asIterator()
                .forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
        return paramMap;
    }
}

어댑터가 호출하는 ControllerV4 는 뷰의 이름을 반환한다.

그런데 어댑터는 뷰의 이름이 아니라 ModelView 를 만들어서 반환해야 한다. 여기서 어댑터가 꼭 필요한 이유가 나온다.

ControllerV4 는 뷰의 이름을 반환했지만, 어댑터는 이것을 ModelView로 만들어서 형식을 맞추어 반환한다.

 

 

 

FrontControllerServletV5

package hello.servlet.web.frontcontroller.v5;

import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v3.controller.MemberFormControllerV3;
import hello.servlet.web.frontcontroller.v3.controller.MemberListControllerV3;
import hello.servlet.web.frontcontroller.v3.controller.MemberSaveControllerV3;
import hello.servlet.web.frontcontroller.v4.controller.MemberFormControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberListControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberSaveControllerV4;
import hello.servlet.web.frontcontroller.v5.adapter.ControllerV3HandlerAdapter;
import hello.servlet.web.frontcontroller.v5.adapter.ControllerV4HandlerAdapter;
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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@WebServlet(name = "frontControllerServletV5", urlPatterns = "/front-controller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {
    private final Map<String, Object> handlerMappingMap = new HashMap<>(); //핸들러(컨트롤러)의 맵핑 정보를 저장하는 Map
    private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>(); //어댑터의 리스트를 담고있는 리스트
    public FrontControllerServletV5() {
        initHandlerMappingMap(); //핸들러(컨트롤러)의 맵핑 정보를 저장하는 Map에 v3, v4의 핸들러를 저장
        initHandlerAdapters(); //어댑터의 리스트를 담고있는 리스트에 v3, v4의 어댑터를 저장
    }
    private void initHandlerMappingMap() {
        //v3
        handlerMappingMap.put("/front-controller/v5/v3/members/new-form", new MemberFormControllerV3());
        handlerMappingMap.put("/front-controller/v5/v3/members/save", new MemberSaveControllerV3());
        handlerMappingMap.put("/front-controller/v5/v3/members", new MemberListControllerV3());

        //v4
        handlerMappingMap.put("/front-controller/v5/v4/members/new-form", new MemberFormControllerV4());
        handlerMappingMap.put("/front-controller/v5/v4/members/save", new MemberSaveControllerV4());
        handlerMappingMap.put("/front-controller/v5/v4/members", new MemberListControllerV4());
    }
    private void initHandlerAdapters() {
        handlerAdapters.add(new ControllerV3HandlerAdapter()); //v3
        handlerAdapters.add(new ControllerV4HandlerAdapter()); //V4
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object handler = getHandler(request); //uri에 맵핑된 핸들러를 가져옴
        if (handler == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        MyHandlerAdapter adapter = getHandlerAdapter(handler); //핸들러 어댑터들 중에서 이용 가능한 핸들러 어댑터를 저장
        ModelView mv = adapter.handle(request, response, handler); //선택된 어댑터의 handle 메서드를 통해 ModelView를 반환받고 저장
        MyView view = viewResolver(mv.getViewName()); //뷰 리졸버를 통해 실제 뷰의 경로를 저장
        view.render(mv.getModel(), request, response); //뷰를 렌더링
    }
    private Object getHandler(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        return handlerMappingMap.get(requestURI);
    }
    private MyHandlerAdapter getHandlerAdapter(Object handler) { //핸들러 어댑터들 중에서 매개변수로 들어온 핸들러에 대해 지원 가능 핸들러 찾기
        for (MyHandlerAdapter adapter : handlerAdapters) { //어댑터 리스트를 통해 알맞는 어댑터를 가져옴
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
        throw new IllegalArgumentException("handler adapter를 찾을 수 없습니다. handler=" + handler);
    }
    private MyView viewResolver(String viewName) {
        return new MyView("/WEB-INF/views/" + viewName + ".jsp");
    }
}

 

어댑터의 handle(request, response, handler) 메서드를 통해 실제 어댑터가 호출된다.

실행환경

  • windows 11 pro
  • IntelliJ : 23.3.2
  • Apache Tomcat : 9.0
  • JDK : 17.0.9

 

라이브러리 추가

JSTL이란 JSP Standard Tag Library를 뜻한다.

먼저 JSTL JAR 파일을 받아야 한다.

아래의 링크에서 다운로드를 진행해야 한다.

https://mvnrepository.com/artifact/javax.servlet/jstl/1.2

 

빨간색으로 표시된 부분을 클릭해서 jar 파일을 다운로드 받자.

 

인텔리제이로 돌아와서 Project Structure를 클릭한다.

 

modules - 해당 프로젝트 선택 후 오른쪽에 보이는 +(Add) 버튼을 클릭한다.

이후 JARs or Dircetories.. 버튼을 눌러서 아까 다운로드 받은 JAR 파일을 추가해준다.

 

apply - ok를 누른다.

 

JSTL 라이브러리

라이브러리 기능 접두어
코어 일반 프로그램 언어에서 제공하는 변수선언, 조건/제어/반복문등의 기능을 제공한다. c
포맷팅 숫자,날짜,시간을 포맷팅 하는 기능과 국제화, 다국어 지원 기능을 제공한다. fmt
함수 문자열을 처리하는 함수를 제공한다.  fn
데이터베이스 데이터베이스의 데이터를 입력/수정/삭제/조회하는 기능을 제공한다. sql
XML처리 XML 문서를 처리할 때 필요한 기능을 제공한다. x
<!--아래의 코드는 JSP에서 코어 태그라이브러리를 사용할 때 작성해야 한다.-->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

 

JSTL 문법

연산자 예제 결과내용
c:url url 호출 
<c:url value="test.jsp"/>
<c:url value="/test.jsp" context="/other"/>
c:out 객체를 화면에 출력 
<c:out value="${data}"/>
<c:out value="${data}" default="값없음"/>
c:set  저장영역에 객체를 저장 
<!-- scope : page, request, session, application -->
<c:set scope="request" var="data" value="my_testValue" />
c:forEach  반복문 제어
<c:forEach var="NOTICE" items="${NOTICE_LIS}" varStatus="status">
<c:out value="${NOTICE.COLUMN}"/>
</c:forEach>
 
<!-- 0123456 -->
<c:forEach var="S" begin="0" end="6">
<c:out value="${S}"/>
</c:forEach>
 
<!-- 036 -->
<c:forEach var="S" begin="0" end="6" step="3">
<c:out value="${S}"/>
</c:forEach>
c:remove  저장영역에서 객체를 삭제  <c:remove scope="request" var="data" />
c:if  조건문 제어
<c:if test="${empty data}">
data 값이 비어있으면 실행
</c:if>
c:choose
c:when
c:otherwise 
복합조건문 제어
<c:choose>
<c:when test="${data == 'A'}">data 값이 A이면 실행</c:when>
<c:when test="${data == 'B'}">data 값이 B이면 실행</c:when>
<c:when test="${data == 'C'}">data 값이 C이면 실행</c:when>
<c:otherwise>data 값이 A, B, C값이 아닐 경우 실행</c:otherwise>
</c:choose>
c:import!  다른 JSP 화면을 현재 화면에 출력 <c:import! url="test.jsp"/>
c:redirect  경로 이동 <c:redirect url="주소"/>

 

status 상태 속성 제어

< c:foreach items=”${RESULT}” var=”RESULT” varStatus=”status”>
 
    ${status.current}<br/> <!– 현재 아이템 –>
    ${status.index}<br/>   <!– 0부터의 순서 –>
    ${status.count}<br/>   <!– 1부터의 순서 –>
    ${status.first}<br/>   <!– 현재 루프가 처음인지 반환 –>
    ${status.last}<br/>    <!– 현재 루프가 마지막인지 반환 –>
    ${status.begin}<br/>   <!– 시작값 –>
    ${status.end}<br/>     <!– 끝값 –>
    ${status.step}<br/>    <!– 증가값 –>
 
< /c:forEach>

 

JSTL for문

인덱스로 접근

<c:forEach var="i" begin="1" end="5" step="1" varStatus="status">
    번호 : ${status.count}
    이름 : ${item[i].name}
    직업 : ${item[i].job}
    급여 : ${item[i].salary}
</c:forEach>

 

begin과 end로 접근

<c:forEach var="item" items="${list}" begin=0 end=5 step=1 varStatus="status">
    번호 : ${status.count}
    이름 : ${item.name}
    직업 : ${item.job}
    급여 : ${item.salary}
</c:forEach>

 

일반적인 접근

<c:forEach var="item" items="${list}" varStatus="status">
    번호 : ${status.count}
    이름 : ${item.name}
    직업 : ${item.job}
    급여 : ${item.salary}
</c:forEach>

 

JSTL break문 구현

<c:set var="doneLoop" value="false" /> 
<c:forEach var="entity" items="${_STORE}" varStatus="status">
    <c:if test="${status.count % 6 eq 1}">
        <c:set var="doneLoop" value="false" />
    </c:if>
    <c:if test="${not doneLoop}"> 
        <span><input type="checkbox" id="store_id" name="store_id" value="<c:out value="${entity.STORE_ID}"/>" <c:out value="${entity.USE_YN}"/>><c:out value="${entity.STORE_NM}"/></span>
        <c:if test="${status.count % 6 eq 0}"> 
            <c:set var="doneLoop" value="true"/> 
        </c:if>
    </c:if>
</c:forEach>

 

reference : https://yunamom.tistory.com/179

개발환경

  • windows 11 pro 
  • jdk : 17.0.9
  • Apache Tomcat : 9.0
  • intelliJ : 2023.3.2

 

프로젝트에서 new 를 할 때 서블릿 추가 버튼이 없는 사람을 위한 글이다.

Servlet 생성

새로운 프로젝트를 생성한다.

 

Add Frameworks Support에서 Java EE 에서 아래 부분을 체크하고 apply - ok 를 누른다.

 

Project Structure - Libraries - +버튼 - From Maven을 누른다.

 

톰캣 9.0 기준

javax.servlet:javax.servlet-api:4.0.1 를 검색한다(오른쪽 검색버튼을 누르고 기다리면 됌)

 

프로젝트를 선택하고 ok를 누른다.

 

servlet api가 추가된 것을 확인하고 apply - ok 버튼을 누른다.

 

file - settings.. 버튼을 누른다.

 

Editor - File and Code Templates -  Other 탭에서

Web - Java code templates - Servlet Class.java를 누르면

아래에 보이는 것처럼 자바 소스코드가 보인다.

이 부분을 전체 복사한다.

 

이제 Other 탭 말고 Files 탭으로 가서

+버튼을 눌러서 이름은 Servlet으로 지정하고

아까 복사했던 자바 코드를 아래 사진과 같이 붙여넣은 뒤 apply - ok 버튼을 누른다.

 

그러면 이제 Servlet 버튼이 생겼다.

 

서블릿을 생성하면 아래와 같은 창이 뜨는데, 왜 뜨는지 아직 모르겠다.

대충 javaee type에 jdk-17을 넣고

클래스 이름에는 서블릿 이름을 넣어주어서 생성한다.

 

이러면 서블릿이 생성된다.

 

+이렇게 서블릿을 생성한 뒤, IOExceptiom, ServletException 오류가 뜨는 경우가 있다. (내가 그랬다.)

Incompatible types. Found: 'javax.servlet.ServletException', required: 'java.lang.Throwable'

이런 경우엔 조금 기다렸다가 인텔리제이를 재부팅하니까 해결되었다.

정확한 원인은 모르겠으나 인텔리제이의 단순 오류로 추정된다.

 

개발 환경

  • windows 11 pro
  • jdk : 17.0.9
  • Apache Tomcat : 10.1.17
  • intelliJ : 2023.3.2

 

IntelliJ에서 JSP 개발환경 초기 설정

인텔리제이를 실행하고 프로젝트를 만든다.

 

 

우측 상단에 돋보기 버튼을 누른다.

 

add framework support 를 검색하고 클릭한다.

 

Java EE - web applition을 체크하고

Create web.xml 도 체크하고 ok를 누른다.

 

이후 우측 상단의 current file 의 드랍다운 박스를 눌러서 Edit configurations...를 누른다.

 

아래와 같은 화면이 나오면 왼쪽 상단의 + 버튼을 누른다.

 

이후 Tomcat Server - Local을 누른다.

 

  1. Application server에서 Configure.. 를 눌러서 톰캣의 설치 경로를 잡아준다.
  2. Name 은 톰캣과 똑같게 지어준다(권장 사항)
  3. VM options 에 -Dfile.encoding=UTF-8 를 입력한다.(한글 깨짐 방지)

 

이후 Deloyment 탭에서 + 버튼을 눌러서 Artifact...를 누른다.

 

그러면 아래와 같이 완성이된다.

이제 apply 버튼을 누르고 ok 버튼을 누른다.

 

아래와 같이 RUN을 해서 실행결과가 나오면 정상적으로 된 것이다.

 

셧다운 포트 설정

톰캣을 종료하려고 하면 아래와 같은 오류가 발생하는 것을 확인할 수 있다.

 

톰캣이 설치된 폴더로 이동한다.

이후 conf 폴더로 이동한다.

필자의 경로는 C:\Program Files\Apache Software Foundation\Tomcat 10.1\conf 이다.

해당 폴더에서 server.xml을 메모장으로 켜준다.

 

server port가 -1로 설정되어 있다.

이를 8005로 변경한다.

이제 정상적으로 톰캣이 종료된다.

 

 

톰캣 언어 설정 변경(영어로 설정)

Edit Configurations... 를 누른다.

 

VM options를 -Duser.language=en -Duser.region=us 로 바꾼다.

(한글로 설정하려면 -Dfile.encoding=UTF-8 )

이후 apply와 ok를 누른다.

 

우측 상단의 돋보기 버튼을 눌러서

Edit custom VM Options... 를 누른다.

 

-Dfile.encoding=UTF-8 를 추가하고 저장한다.

 

그러면 이제 톰캣 콘솔창이 영어로 출력된다.

 

'Java Category > JSP' 카테고리의 다른 글

[JSP] JSTL(JSP Standard Tag Library)  (0) 2024.01.10
[JSP] IntelliJ에서 Servlet 생성  (0) 2024.01.09
[JSP] 파일 업로드  (0) 2023.12.25
[JSP] JSP와 데이터베이스 연동  (2) 2023.11.27
[JSP] 액션 태그(Action tag)  (1) 2023.11.27

cos.jar
0.05MB

위 파일을 프로젝트 bin 폴더에 추가한다.

 

FileUp.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
	<center>
	<h2> 파일 업로드 </h2>
	<form action= "FileUploadProc.jsp" method="post" enctype = "multipart/form-data">
		<table width = "350" border="1" bordercolor="gray">
			<tr height="40">
				<td width="150">이름</td>
				<td width="200"><input type="text" name="name"></td>
			</tr>
	
			<tr height="40"> 
				<td width="150">파일 선택</td>
				<td width="200"><input type="file" name="filedate"></td>
			</tr>
	
			<tr height="40">
				<td align="center" colspan="2"><input type="submit" value="파일 업로드"></td>
			</tr>	
	
		</table>
	</form>
	</center>

</body>
</html>

파일 업로드는 form태그에 <input type="file">을 통해서 하게 된다.

하지만 별다른 처리를 하지 않으면 실제로 넘어오는건 업로드한 파일이름만 넘어오고, 파일자체는 넘어오지 않는다.

먼저 파일업로드를 받기 위해서는 enctype="multipart/form-data" 속성을 추가해줘야 한다.

 

 

FileUploadProc.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import = "com.oreilly.servlet.multipart.DefaultFileRenamePolicy" %>
<%@ page import = "com.oreilly.servlet.MultipartRequest" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<% 
		request.setCharacterEncoding("UTF-8");
		//프로젝트내에 만들어질 폴더를 저장할 이름 변수 선언 
		String realfolder=""; 
		//실제로 만들어질 폴더명을 설정 
		String savefolder = "/upload"; 
		//한글 설정
		String encType = "UTF-8"; 
		//저장할때 사이즈를 설정
		int maxSize = 5*1024*1024;//5mb
		
		//파일이 저장될 경로 값을 읽어 오는 객체 
		ServletContext context = getServletContext();
		realfolder = context.getRealPath(savefolder);
		System.out.println(realfolder);
		
		try{ 
		//클라이언트로 부터 넘어온 데이터를 저장해주는 객체 
		//DefaultFileRenamePolicy() -> 동일한 이름이 있을경우 파일 이름을 자동 변경
		MultipartRequest multi = new MultipartRequest(request, realfolder, maxSize, encType, new DefaultFileRenamePolicy()); 
		
%>
 		당신의 이름은 : <%=multi.getParameter("name")%>
<% 	
		out.println(realfolder); //파일 저장위치 출력
		}catch (Exception e){
 			e.printStackTrace(); 
		} 
%>
</body>
</html>

 

'Java Category > JSP' 카테고리의 다른 글

[JSP] IntelliJ에서 Servlet 생성  (0) 2024.01.09
[JSP] IntelliJ 에서 JSP 개발환경 만들기  (1) 2024.01.08
[JSP] JSP와 데이터베이스 연동  (2) 2023.11.27
[JSP] 액션 태그(Action tag)  (1) 2023.11.27
[JSP] 세션(Session)  (1) 2023.11.25

JDBC(Java DataBase Connectivity)

  • Java에서 DBMS의 종류와 관계없이 데이터베이스를 조작하기 위한
  • API(Application Programming Interface)를 의미
  • JDBC를 간단하게 요약하면 메소드 호출용 SQL 인터페이스라고 표현할 수 있음

 

JDBC 드라이버

  • 다양한 DBMS 제조사들은 본사에서 개발한 DBMS를 Sun사의 Java 프로그램과연동할 수 있도록 기술을 지원하는 것을 의미
  • JDBC는 MySQL 설치과정에서 이미 설치하였으므로 따로 설치할 필요는 없지만 JDBC 드라이버가 어느 폴더에 저장되어 있는지에 대해서는 알고 있어야 함
  • JSP프로젝트 내에 lib폴더에 JDBC 드라이버를 추가해준다.

 

자세한 JDBC에 대한 내용은 아래의 포스트를 참고

2023.08.17 - [Java Category/Java] - [Java] JDBC 개요 및 DB 연결하기

 

[Java] JDBC 개요 및 DB 연결하기

이 게시글은 이것이 자바다(저자 : 신용권, 임경균)의 책과 동영상 강의를 참고하여 개인적으로 정리하는 글임을 알립니다. JDBC 자바는 데이터베이스와 연결해서 데이터 입출력 작업을 할 수 있

rebugs.tistory.com

 

예제 데이터베이스 기본 (MySQL)

 

 

JDBC 연결 테스트

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.sql.*" %>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> JDBC 커넥션 테스트 </title>
</head>
<body>
	<%
		// 1. 변수 4개 선언
		String driverName="com.mysql.jdbc.Driver"; //jdbc 드라이버 이름
		String url = "jdbc:mysql://localhost:3306/odbo"; //연결 문자열, 마지막은 데이터베이스 이름
		String username = "root"; //DB 계정 id
		String password = "0000"; //DB 계정 pw
		Connection conn = null; //Connection 객체를 null로 레퍼런스
		try{
			// 2. 드라이버 로딩	
			Class.forName(driverName);  //JDBC 드라이버 메모리에 로딩
			// 3. 연동
			conn = DriverManager.getConnection(url, username, password); //연결문자열, DB 계정을 Connection객체에 대입
			// 4. 여기까지 오류가 없다면 연결 성공
			out.println(">> 연결 성공 : " + conn);
		} catch(ClassNotFoundException e){
			out.println(">> 연결 실패 : 드라이버 복사 필요!");			
		} catch(SQLException e){
			out.println(">> 연결 실패 : SQL 명령문 확인 필요!");
		} finally{
			// 5. 닫기
			try{
				if(conn != null)
					conn.close();
			} catch(SQLException e){
				;
			}
		}
	%>
</body>
</html>

 

회원 가입

 

signup.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title> 회원 가입 </title>
</head>
<body>
	Home > Signup the Membership
	<hr>
	<form action="signup_process.jsp" name="user_info" method="post">
		<fieldset style="width:200px">
			<legend> 회원 가입 </legend><p>
	
			아이디 : <br>
			<input type="text" name="userID"><br><br>
		
			비밀번호 : <br> 
			<input type="password" name="userPW"><br><br>
			
			이메일 : <br>
			<input type="email" name="userMAIL"><br><br>		
			<hr>
			
		<div align="center">
			<input type="submit" value=" 가입하기 "> &nbsp;&nbsp;
			<input type="reset" value=" 다시작성 ">
		</div><br>
		</fieldset>
	</form>
</body>
</html>

 

signup_process.jsp

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.sql.*" %>
<% 
	String u_id = request.getParameter("userID");
	String u_pw = request.getParameter("userPW");
	String u_mail = request.getParameter("userMAIL");
	
	//INSERT INTO members(id, passwd, email) VALUES( 'idData', 'passwdData', 'emailData' )
	String sql = "INSERT INTO members(id, passwd, email) VALUES";
	sql += "('" + u_id + "','" + u_pw + "','" + u_mail + "')";
	
	String driverName="com.mysql.jdbc.Driver"; //jdbc 드라이버 이름
	String url = "jdbc:mysql://localhost:3306/odbo"; //연결 문자열
	String username = "root"; //DB 계정 id
	String password = "0000"; //DB 계정 pw
	Connection conn = null; //Connection 객체를 null로 레퍼런스
	
	Class.forName(driverName); //JDBC 드라이버 메모리에 로딩
	//DriverManager의 정적 메소드인 getConnection는 Connection 객체를 리턴함
	conn = DriverManager.getConnection(url, username, password); //연결문자열, DB 계정, Connection객체 대입
	//Staement는 변경되지 않는 정적 SQL 문을 실행할 때 사용, 동적 SQL문은 PreparedStatement를 사용
	Statement sm = conn.createStatement(); //Statement 얻기
	
	int count = sm.executeUpdate(sql); //쿼리 실행
	if(count == 1){ //실행한 쿼리가 1개라면
		out.println("회원가입 성공!");
	}else{
		out.println("회원가입 실패!");
	}
	sm.close(); //Statement 끊기
	conn.close(); //Connection 끊기
%>

성공적으로 DB에 저장된 것을 확인할 수 있다.

 

회원 탈퇴

 

withdraw.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title> 회원 탈퇴 </title>
</head>
<body>
	Home > Withdraw the Membership
	<hr>
	<form action="withdraw_process.jsp" name="user_info" method="post">
		<fieldset style="width:200px">
			<legend> 회원 탈퇴 </legend><p>
	
			아이디 : <br>
			<input type="text" name="userID"><br>
			
		<div align="center">
			<input type="submit" value=" 탈퇴하기 "> &nbsp;&nbsp;
		</div><br>
		</fieldset>
	</form>
</body>
</html>

 

withdraw_process.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.sql.*" %>
<% 
	String u_id = request.getParameter("userID");
	String sql = "DELETE FROM members WHERE id = ?"; //?에 넣어질 값은 PreparedStatement에서 처리
	
	String driverName="com.mysql.jdbc.Driver";
	String url = "jdbc:mysql://localhost:3306/odbo";
	String username = "root";
	String password = "0000";
	Connection conn = null;
	
	Class.forName(driverName);//메모리에 로딩
	conn = DriverManager.getConnection(url, username, password); //db에 연결
	
	PreparedStatement sm = conn.prepareStatement(sql); //sql 처리 준비
	sm.setString(1, u_id); //첫 번째 ?에 u_id를 대입

	int count = sm.executeUpdate(); //sql문을 처리하고 리턴되는 행의 수를 count에 저장
	
	if(count == 1){
		out.print("회원 탈퇴 성공!");
	}else{
		out.print("회원 탈퇴 실패!");
	}
	sm.close();
	conn.close();	
%>

성공적으로 DB에서 DELETE문이 이뤄진 것을 확인할 수 있다.

 

데이터베이스 예제 심화(Oracle SQL)

데이터베이스 구조

 

DTO (Data Transfer Object)

memberBean.java

더보기
public class MemberBean {
	private String id;
	private String pass1;
	private String email;
	private String tel;
	private String hobby;
	private String job;
	private String age;
	private String info;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getTel() {
		return tel;
	}
	public void setTel(String tel) {
		this.tel = tel;
	}
	public String getHobby() {
		return hobby;
	}
	public void setHobby(String hobby) {
		this.hobby = hobby;
	}
	public String getJob() {
		return job;
	}
	public void setJob(String job) {
		this.job = job;
	}
	public String getAge() {
		return age;
	}
	public void setAge(String age) {
		this.age = age;
	}
	public String getInfo() {
		return info;
	}
	public void setInfo(String info) {
		this.info = info;
	}
}

 

DAO (Data Access Object)

memberDAO.java

더보기
package model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Vector;
public class MemberDAO
{
	String id = "java";
	String pass = "oracle";
	String url = "jdbc:oracle:thin:@localhost:1521/orcl";
	Connection conn = null;
	PreparedStatement pstmt = null;
	ResultSet rs; //DB의 테이블 결과를 리턴받아 저장하는 객체
	
	public void getCon()
	{
		 try{	
		    Class.forName("oracle.jdbc.OracleDriver");
	   		conn = DriverManager.getConnection(url, id, pass);
		}
		catch(Exception e) { e.printStackTrace(); }
	}
	
	public void insertMember(MemberBean mbean)
	{
		try
		{
			getCon();
	  		String SQL = "INSERT INTO member VALUES(?,?,?,?,?,?,?,?)";
			pstmt = conn.prepareStatement(SQL);
	  		pstmt.setString(1,mbean.getId());
	  		pstmt.setString(2,mbean.getPass1());
	  		pstmt.setString(3,mbean.getEmail());
	  		pstmt.setString(4,mbean.getTel());
	  		pstmt.setString(5,mbean.getHobby());
	  		pstmt.setString(6,mbean.getJob());
	  		pstmt.setString(7,mbean.getAge());
	  		pstmt.setString(8,mbean.getInfo());
	  		pstmt.executeUpdate();
		}
		catch(Exception e) { e.printStackTrace(); }
		finally { if(conn != null) { try { } catch (Exception e) { e.printStackTrace(); } } }
	}
	
	public Vector<MemberBean> allSelectMember()
	{
		Vector<MemberBean> v = new Vector<>();
		try 
		{
			getCon();
			String SQL = "SELECT * FROM member";
			pstmt = conn.prepareStatement(SQL);
			rs = pstmt.executeQuery();
			
			while(rs.next())
			{
				MemberBean bean = new MemberBean();
				bean.setId(rs.getString(1));
				bean.setPass1(rs.getString(2));
				bean.setEmail(rs.getString(3));
				bean.setTel(rs.getString(4));
				bean.setHobby(rs.getString(5));
				bean.setJob(rs.getString(6));
				bean.setAge(rs.getString(7));
				bean.setInfo(rs.getString(8));
				v.add(bean);
			}
		}
		catch(Exception e) { e.printStackTrace(); }
		finally { if(conn != null) { try { } catch (Exception e) { e.printStackTrace(); } } }
		return v;
	}
	
	public MemberBean oneSelectMember(String id)
	{
		MemberBean bean = new MemberBean();
		try 
		{
			getCon();
			String SQL = "SELECT * FROM member WHERE id=?";
			pstmt = conn.prepareStatement(SQL);
			pstmt.setString(1, id);
			rs = pstmt.executeQuery();
			if(rs.next())
			{
				bean.setId(rs.getString(1));
				bean.setPass1(rs.getString(2));
				bean.setEmail(rs.getString(3));
				bean.setTel(rs.getString(4));
				bean.setHobby(rs.getString(5));
				bean.setJob(rs.getString(6));
				bean.setAge(rs.getString(7));
				bean.setInfo(rs.getString(8));
			}
		}
		catch(Exception e) { e.printStackTrace(); }
		finally { if(conn != null) { try { } catch (Exception e) { e.printStackTrace(); } } }
		return bean;
	}
	
	public String getPass(String id)
	{
		String pass="";
		try 
		{
			getCon();
			String SQL = "SELECT * FROM member WHERE id=?";
			pstmt = conn.prepareStatement(SQL);
			pstmt.setString(1, id);
			rs = pstmt.executeQuery();
			if(rs.next()) pass = rs.getString(2);
		}
		catch(Exception e) { e.printStackTrace(); }
		finally { if(conn != null) { try { } catch (Exception e) { e.printStackTrace(); } } }
		return pass;
	}
	
	public void updateMember(MemberBean bean)
	{
		try 
		{
			getCon();
			String SQL = "UPDATE member SET email=?, tel=? WHERE id=?";
			pstmt = conn.prepareStatement(SQL);
			pstmt.setString(1, bean.getEmail());
			pstmt.setString(2, bean.getTel());
			pstmt.setString(3, bean.getId());
			pstmt.executeQuery();
		}
		catch(Exception e) { e.printStackTrace(); }
		finally { if(conn != null) { try { } catch (Exception e) { e.printStackTrace(); } } }
	}
	
	public void deleteMember(String id)
	{
		try 
		{
			getCon();
			String SQL = "DELETE FROM member WHERE id=?";
			pstmt = conn.prepareStatement(SQL);
			pstmt.setString(1, id);
			pstmt.executeQuery();
		}
		catch(Exception e) { e.printStackTrace(); }
		finally { if(conn != null) { try { } catch (Exception e) { e.printStackTrace(); } } }
	}
}

 

MemberJoin.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<center>
		<h2>회원 가입</h2>
		<form action="MemberJoinProc.jsp">
			<table width="500" border="1">
				<tr height="50">
					<td width="150" align="center">아이디 </td>
					<td width="350" align="center">
						<input type="text" name="id" size="40" placeholder="id를 넣으세요">						
					</td>
				</tr>
		
				<tr height="50">
					<td width="150" align="center">패스워드</td>
					<td width="350" align="center">
						<input type="password" name="pass1" size="40"
						placeholder="비밀번호는 숫자와 영어만 넣어주세요">						
					</td>
				</tr>
				
				<tr height="50">
					<td width="150" align="center">패스워드 확인</td>
					<td width="350" align="center">
						<input type="password" name="pass2" size="40">						
					</td>
				</tr>
				
				<tr height="50">
					<td width="150" align="center">이메일</td>
					<td width="350" align="center">
						<input type="email" name="email" size="40">						
					</td>
				</tr>
				
				<tr height="50">
					<td width="150" align="center">전화 번호 </td>
					<td width="350" align="center">
						<input type="tel" name="tel" size="40">				
					</td>
				</tr>
				
				<tr height="50">
					<td width="150" align="center">당신의 관심 분야</td>
					<td width="350" align="center">
						<input type="checkbox" name="hobby" value="캠핑">캠핑&nbsp;
						<input type="checkbox" name="hobby" value="등산">등산&nbsp;
						<input type="checkbox" name="hobby" value="독서">독서&nbsp;
						<input type="checkbox" name="hobby" value="음악">음악&nbsp;				
					</td>
				</tr>
				
			   <tr height="50">
					<td width="150" align="center">당신의 직업은</td>
					<td width="350" align="center">
						<select name="job">
						<option value="교사">교사</option>
						<option value="의사">의사</option>
						<option value="변호사">변호사</option>
						<option value="기술사">기술사</option>
						</select>				
					</td>
				</tr>
				
				<tr height="50">
					<td width="150" align="center">당신의 연령은</td>
					<td width="350" align="center">
						<input type="radio" name="age" value="10">10대&nbsp;
						<input type="radio" name="age" value="20">20대&nbsp;
						<input type="radio" name="age" value="30">30대&nbsp;
						<input type="radio" name="age" value="40">40대&nbsp;			
					</td>
				</tr>
				
				<tr height="50">
					<td width="150" align="center">하고 싶은말</td>
					<td width="350" align="center">
							<textarea rows="5" cols="40" name="info"></textarea>	
					</td>
				</tr>
					
				<tr height="50" align="center">
					<td width="150" colspan="2">
						<input type="submit" value="회원 가입">	
						<input type="reset" value="취소">			
					</td>
				</tr>	
			</table>
		</form>	
	</center>
</body>
</html>

 

MemberJoinProc.jsp

더보기
<%@page import="model.MemberDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
	<%
		request.setCharacterEncoding("UTF-8");
		String hobby[] = request.getParameterValues("hobby");
		String textHobby = "";
		for(int i = 0; i < hobby.length; ++i) textHobby += hobby[i] + " ";
	%>
	<jsp:useBean id="mbean" class="model.MemberBean">
		<jsp:setProperty name="mbean" property="*"/>
	</jsp:useBean>
	<%
	mbean.setHobby(textHobby);
	MemberDAO mdao = new MemberDAO();
	mdao.insertMember(mbean);
	response.sendRedirect("MemberList.jsp");
	%>
</body>
</html>

 

MemberList.jsp

더보기
<%@page import="model.MemberBean" %>
<%@page import="model.MemberDAO" %>
<%@page import="java.util.Vector" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
	<%
		MemberDAO mdao = new MemberDAO();
		Vector<MemberBean> vec = mdao.allSelectMember();
	%>
	<center>
	<h2>모든 회원 정보</h2>
	<table width="800" border="1">
		<tr height="50">
			<td align="center" width="150">아이디</td>
			<td align="center" width="250">이메일</td>
			<td align="center" width="200">전화번호</td>
			<td align="center" width="200">취미</td>
		</tr>
		<%
			for(int i = 0; i < vec.size(); ++i)
			{
				MemberBean bean = vec.get(i);
		%>
		<tr height="50">
			<td align="center" width="150"><a href="MemberInfo.jsp?id=<%= bean.getId() %>"><%= bean.getId() %></a></td>
			<td align="center" width="250"><%= bean.getEmail() %></td>
			<td align="center" width="200"><%= bean.getTel() %></td>
			<td align="center" width="200"><%= bean.getHobby() %></td>
		</tr>
		<%}%>
	</table>
	</center>
</body>
</html>

 

MemberUpdateForm.jsp

더보기
<%@page import="model.MemberBean"%>
<%@page import="model.MemberDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<%
	String id = request.getParameter("id");
	MemberDAO mdao = new MemberDAO();
	MemberBean mbean = mdao.oneSelectMember(id);
%>
<center>
	<h2>회원 정보</h2>
		
		<table width="400" border="1">
			<form action="MemberUpdateProc.jsp" method="post">
			<tr height="50">
				<td align="center" width="150">아이디</td>
				<td width="250"><%= mbean.getId() %></td>
			</tr>
			<tr height="50">
				<td align="center" width="150">이메일</td>
				<td width="250"><input type="email" name="email" value="<%= mbean.getEmail() %>"></td>
			</tr>
			<tr height="50">
				<td align="center" width="150">전화</td>
				<td width="250"><input type="tel" name="tel" value="<%= mbean.getTel() %>"></td>
			</tr>
			<tr height="50">
				<td align="center" width="150">패스워드</td>
				<td width="250"><input type="password" name="pass1"></td>
			</tr>
			<tr height="50">
				<td align="center" colspan="2">
				<input type="hidden" name="id" value="<%= mbean.getId() %>">
				<input type="submit" value="회원 수정하기"> &nbsp;&nbsp;
			</form>
				<button onclick="location.href='MemberList.jsp'">회원 전체 보기</button>
				
			</td>
		</tr>
	</table>
</center>
</body>
</html>

 

MemberUpdateProc.jsp

더보기
<%@page import="model.MemberDAO"%>
<%@page import="model.MemberBean"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
	<%
		request.setCharacterEncoding("UTF-8");
	%>
	<jsp:useBean id="mbean" class="model.MemberBean">
		<jsp:setProperty name="mbean" property="*"/>
	</jsp:useBean>
	<%
		MemberDAO mdao = new MemberDAO();
		String pass = mdao.getPass(mbean.getId());
		if(mbean.getPass1().equals(pass))
		{
			mdao.updateMember(mbean);
			response.sendRedirect("MemberList.jsp");
		}
		else
		{
	%>
		<script type="text/javascript">
		alert("패스워드가 맞지 않습니다. 다시 확인해 주세요")
		history.go(-1);
		</script>
	<%
		}
	%>
</body>
</html>

 

MemberInfo.jsp

더보기
<%@page import="model.MemberBean" %>
<%@page import="model.MemberDAO" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
	<%
		String id = request.getParameter("id");
		MemberDAO mdao = new MemberDAO();
		MemberBean mbean = mdao.oneSelectMember(id);
	%>
	<center>
		<h2>회원 정보</h2>
			<table width="400" border="1">
				<tr height="50">
					<td align="center" width="150">아이디</td>
					<td width="250"><%= mbean.getId() %></td>
				</tr>
				<tr height="50">
					<td align="center" width="150">이메일</td>
					<td width="250"><%= mbean.getEmail() %></td>
				</tr>
				<tr height="50">
					<td align="center" width="150">전화</td>
					<td width="250"><%= mbean.getTel() %></td>
				</tr>
				<tr height="50">
					<td align="center" width="150">취미</td>
					<td width="250"><%= mbean.getHobby() %></td>
				</tr>
				<tr height="50">
					<td align="center" width="150">직업</td>
					<td width="250"><%= mbean.getJob() %></td>
				</tr>
				<tr height="50">
					<td align="center" width="150">나이</td>
					<td width="250"><%= mbean.getAge() %></td>
				</tr>
				<tr height="50">
					<td align="center" width="150">정보</td>
					<td width="250"><%= mbean.getInfo() %></td>
				</tr>
				<tr height="50">
					<td align="center" colspan="2">
						<button onclick="location.href='MemberUpdateForm.jsp?id=<%=mbean.getId()%>'">회원수정</button>
						<button onclick="location.href='MemberDeleteForm.jsp?id=<%=mbean.getId()%>'">회원삭제</button>
						<button onclick="location.href='MemberList.jsp?id=<%=mbean.getId()%>'">목록보기</button>
						<button onclick="location.href='MemberJoin.jsp?id=<%=mbean.getId()%>'">회원가입</button>
					</td>
				</tr>
			</table>
	</center>
</body>
</html>

 

MemberDeleteForm.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<center>
	<h2>회원 삭제 하기</h2>
		<table width="400" border="1">
			<form action="MemberDeleteProc.jsp" method="post">
			<tr height="50">
				<td align="center" width="150">아이디</td>
				<td width="250"><%= request.getParameter("id") %></td>
			</tr>
			<tr height="50">
				<td align="center" width="150">패스워드</td>
				<td width="250"><input type="password" name="pass1"></td>
			</tr>
			<tr height="50">
				<td align="center" colspan="2">
				<input type="hidden" name="id" value="<%= request.getParameter("id") %>">
				<input type="submit" value="회원 삭제하기"> &nbsp;&nbsp;
			</form>
				<button onclick="location.href='MemberList.jsp'">회원 전체 보기</button>
			</td>
		</tr>
	</table>
</center>
</body>
</html>

 

MemberDeleteProc.jsp

더보기
<%@page import="model.MemberDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
	<%
		request.setCharacterEncoding("UTF-8");
	%>
	<jsp:useBean id="mbean" class="model.MemberBean">
		<jsp:setProperty name="mbean" property="*"/>
	</jsp:useBean>
	<%
		MemberDAO mdao = new MemberDAO();
		String pass = mdao.getPass(mbean.getId());
		if(mbean.getPass1().equals(pass))
		{
			mdao.deleteMember(mbean.getId());
			response.sendRedirect("MemberList.jsp");
		}
		else
		{
	%>
		<script type="text/javascript">
		alert("패스워드가 맞지 않습니다. 다시 확인해 주세요")
		history.go(-1);
		</script>
	<%
		}
	%>
</body>
</html>

'Java Category > JSP' 카테고리의 다른 글

[JSP] IntelliJ 에서 JSP 개발환경 만들기  (1) 2024.01.08
[JSP] 파일 업로드  (0) 2023.12.25
[JSP] 액션 태그(Action tag)  (1) 2023.11.27
[JSP] 세션(Session)  (1) 2023.11.25
[JSP] 쿠키(Cookie)  (2) 2023.11.24

액션 태그

  • JSP에서 기본으로 제공하는 태그들의 집합으로 서버 또는 클라이언트에게 수행할 명령을 지시
  • 액션 태그를 사용하게 되면 Java 코드를 사용하지 않아도 JSP 웹페이지를 개발할 수 있음
  • 액션 태그는 XML 형식인 <jsp: … />를 사용하며 끝나는 태그는 반드시 />로 마무리해야 함
  • 액션 태그는 JSP 웹페이지를 코딩할 때 Java 코드의 작성을 피하거나 최소화하기 위해 사용
  • JSP 웹페이지에서 Java 코드를 최소화하게 되면 소스 코드에 대한 유지/보수를 효율적으로 수행 가능

 

param

  • 현재 위치한 JSP 웹페이지에서 다른 웹페이지로 정보를 전달할 때 사용하는 태그
  • param 액션 태그는 단독으로 사용할 수 없으므로 <jsp:forward> 태그나 <jsp:include> 태그의 내부에 선언하여 사용
  • param 액션 태그는 여러 개의 파라미터를 선언하여 다른 페이지에 여러 개의 정보를 전달할 수도 있음

 

forward

<jsp:forward page="이동 페이지명"/>
  • response.sendRedirect()와 같이 다른 페이지로 이동(흐름 제어)한다는 점은 같다.
  • 하지만 param을 이용하여 파라미터를 전달할 때 sendRedirect()는 한 번 전달하면 파라미터가 사라지지만
    forward 액션태그는 사라지지 않고 계속 살아있다.

 

 

또한 param태그와 함께 같이 쓰일 수 있다.

<jsp:forward page = "ResponseRedirect.jsp">
        <jsp:param value="mmmm" name="id">
        <jsp:param value="1234" name="phone">
</jsp:forward>

 

예제

더보기

forwardLogin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<center>
	<form action="ResponseProc.jsp" method="post">
	<table width="400">
		<tr height="50">
			<td align="center" width="150"> 아이디 </td>
			<td width="250"> <input type="text" name="id"></td>
		</tr>
		<tr height="50">
			<td align="center" width="150"> 패스워드 </td>
			<td width="250"> <input type="password" name="pass"></td>
		</tr>
		<tr height="50">
			<td align="center" colspan="2">
			<input type="submit" value="로그인">
			&nbsp;&nbsp;
			<input type="reset" value="취소">
		</tr>
	</table>
	</form>
</center>
</body>
</html>

 

ResponseProc.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<h2>이 페이지는 로그인 정보가 넘어오는 페이지 입니다.</h2>
	<%
	request.setCharacterEncoding("UTF-8");
	String id = request.getParameter("id");
	//response.sendRedirect("ReponseRedirect.jsp"); //이 페이지로 이동, 당연히 id값을 안넘김
	//response.sendRedirect("ReponseRedirect.jsp?id="+id); //이 페이지로 이동, get방식으로 id넘김, 객체가 살아있는 것이 아님
	%>
	<!-- 객체가 살아 있음, id는 받아온거 넘기고, 번호는 추가적으로 인위적인 것을 넘길 수 있음 -->
	<jsp:forward page="ReponseRedirect.jsp">
		<jsp:param value="01333" name="phone"/>
	</jsp:forward>
<h3>아이디 : <%= id %></h3>
</body>
</html>

 

ReponseRedirect.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<h2>이 페이지는 ReponseRedirect가 넘어오는 페이지 입니다.</h2>
	<%
	request.setCharacterEncoding("UTF-8");
	String id = request.getParameter("id");
	String phone = request.getParameter("phone");
	%>
<h3>아이디 : <%= id %></h3>
<h3>아이디 : <%= phone %></h3>
</body>
</html>

 

 

 

include

  • 디렉티브 태그의 include는 스크립틀릿을 사용하는 방식이며 해당 소스를 포함시킨 후 컴파일을 실시한다.
  • 액션 태그의 include는 디렉티브 include와 다르게 실행 시점에 해당 파일을 수행하여 결과를 포함시킨다.

디렉티브 태그의 include는 정적이고, 액션 태그의 include는 동적이고, 독립적이라고 할 수 있다.

 

예제

더보기

 Main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<center>
<%request.setCharacterEncoding("UTF-8");%>
<table width="800" border="1">
	<tr height="150">
		<td align="center" colspan="2">
		<jsp:include page="Top.jsp" >
			<jsp:param value="asd" name="id"/>
		</jsp:include>
		</td>
	</tr>
	<tr height="400">
		<td align="center" width="200"><jsp:include page="Left.jsp"/></td>
		<td align="center" width="600"><jsp:include page="Center.jsp"/></td>
	</tr>
	<tr height="100">
		<td align="center" colspan="2">
		<jsp:include page="Bottom.jsp"/>
		</td>
	</tr>
</table>
</center>
</body>
</html>

 

 Left.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<table width="200">
	<tr height="60">
		<td width=200 align="center"><a href="#"> 스노우 피크 </a></td>
	</tr>
	<tr height="60">
		<td width=200 align="center"><a href="#"> 콜맨 </a></td>
	</tr>
	<tr height="60">
		<td width=200 align="center"><a href="#"> 지프 </a></td>
	</tr>
	<tr height="60">
		<td width=200 align="center"><a href="#"> 코삐아 </a></td>
	</tr>
</table>
</body>
</html>

 

Center.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<table width="600">
	<tr height="400">
		<td align="center">
		<img alt="" src="img/2.jpg" width="400" height="350">
		</td>
	</tr>
</table>
</body>
</html>

 

Bottom.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<table width="800">
	<tr height="100">
		<td align="center">
		이용약관
		이메일무단수집거부
		개인정보처리방침
		영상정보처리기기 운영관리방침
		아이디어 정책
		Copyright. SAMSUNG ELECTRO-MECHANICS All rights reserved.
		삼성전기(주)대표이사 경계현사업자등록번호 124-81-00979
		경기도 수원시 영통구 매영로 150 (매탄동)
		</td>
	</tr>
</table>
</body>
</html>

 

Top.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<%request.setCharacterEncoding("UTF-8");%>
<center>
<table width="800">
	<tr height="100">
		<td colspan="2" align="center" width = 260>
		<img alt ="" src="img/1.png" width="200" height="70">
		</td>
		<td colspan ="4" align="center">
		<font size="10"> 낭만 캠핑 </font>
		</td>
	</tr>
	<tr height="50">
		<td width="110"  align="center"> 텐트 </td>
		<td width="110"  align="center"> 의자 </td>
		<td width="110"  align="center"> 식기류 </td>
		<td width="110"  align="center"> 침낭 </td>
		<td width="110"  align="center"> 테이블 </td>
		<td width="110"  align="center"> 화롯대 </td>
		<td width="140"  align="center"> <%= request.getParameter("id") %> </td>
	</tr>
</table>
</center>
</body>
</html>

 

useBean

자바 빈즈(java Beans)

  • JSP 웹페이지에는 HTML 코드와 스크립트 태그 등을 기술하기 때문에 자칫 프로그램이 복잡하게 작성되어 유지/보수 어려운 상황 초래
  • 자바빈즈 액션 태그를 사용하게 되면 복잡하게 구성된 JSP 웹페이지에서 Java 소스 코드만 따로 뽑아 별도 작성으로 JSP 웹페이지가 복잡해지는 것을 방지할 수 있음
  • 자바빈즈는 데이터 표현을 목적으로 수행하는 Java 클래스로 데이터를 담는 멤버 변수인 프로퍼티(Property)와 데이터를 가져오거나 저장하는 메소드로 구성됨

 

JSP와 자바 빈즈의 프로세스

  1. 웹 브라우저에서 서블릿으로 서비스를 요청
  2. 서블릿은 자바빈즈와 통신을 수행
  3. 자바빈즈는 데이터베이스와 연결하여 데이터를 관리
  4. 서블릿은 JSP 웹페이지에게 정보를 전달
  5.  JSP 웹페이지는 요청한 웹 브라우저에서 전달된 정보를 출력

 

useBean 액션 태그

  • 자바빈즈를 JSP 웹페이지에서 사용하기 위해 Java 클래스를 선언하고 초기화하는 태그로 사용 형식은 다음과 같음

 

useBean 태그 속성

  • id 속성
    id 속성을 사용하여 지정한 객체명의 사용 용도
    -꺼낸 객체의 참조 변수명으로 사용
    -getAttribute( )로 값을 꺼낼 때 사용하는 이름
    -객체를 생성할 경우 보관소에 저장하는 key 값의 이름으로 사용
  • scope 속성
    scope 속성을 사용하여 보관소를 지정
    -page = JspContext(기본값)
    -request = ServletRequest
    -session = HttpSession
    -application = ServletContext
  • class 속성
    -Java 객체를 생성하기 위해 사용할 클래스 이름을 지정할 때 선언
    -new 연산자를 사용하므로 인터페이스는 올 수 없고 반드시 패키지 이름을 포함해야 함
    -scope 속성에서 지정한 보관소에서 객체를 찾지 못하였을 경우 class의 값을 사용하여 인스턴스를 생성
    -생성된 객체는 scope 보관소에 자동으로 저장되며 class 속성을 선언하지 않을 경우 객체를 생성할 수 없게 됨
  • type 속성
    -type 속성은 참조 변수에서 사용할 타입(클래스 또는 인터페이스)을 지정할 때 선언
    -이 속성을 사용할 때는 반드시 패키지 이름을 포함해야 하며 type 속성을 지정하지 않으면 class 속성의 값이 사용됨

 

예제

더보기

memberBean.java(자바 빈즈)

public class MemberBean
{
	private String id;
	private String pass1;
	private String email;
	private String tel;
	private String address;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getTel() {
		return tel;
	}
	public void setTel(String tel) {
		this.tel = tel;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
}

 

MemberJoin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<center>
	<h2> 회원 가입</h2>
	<form action="MemberJoinProc.jsp" method="post">
		<table width="500" border ="1">
			<tr height = 50>
				<td width="150" align="center"> 아이디 </td>
				<td width="350" align="center"><input type="text" name="id" size="40" placeholder="id를 입력해주세요"></td>
			</tr>
			<tr height = 50>
				<td width="150" align="center"> 패스워드 </td>
				<td width="350" align="center"><input type="password" name="pass1" size="40" 
				placeholder="비밀번호는 영문과 숫자만 넣어주세요"></td>
			</tr>
			<tr height = 50>
				<td width="150" align="center"> 이메일 </td>
				<td width="350" align="center"><input type="email" name="email" size="40"></td>
			</tr>
			<tr height = 50>
				<td width="150" align="center"> 전화번호 </td>
				<td width="350" align="center"><input type="tel" name="tel" size="40"></td>
			</tr>
			<tr height = 50>
				<td width="150" align="center"> 주소 </td>
				<td width="350" align="center"><input type="text" name="address" size="40"></td>
			</tr>
			<tr height = 50>
				<td align="center" colspan="2"><input type="submit" value="회원가입"></td>
			</tr>
		</table>
	</form>
</center>
</body>
</html>

 

MemberJoinProc.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<center>
	<h2>회원 정보 보기</h2>
	<%request.setCharacterEncoding("UTF-8");%>
	<jsp:useBean id="mbean" class="bean.MemberBean"> <!-- 객체생성과 유사 -->
		<jsp:setProperty name="mbean" property="*"/> <!-- mbean의 모든 속성을 가져옴 -->
	</jsp:useBean>
	
	<!-- 속성을 출력 -->
	<h2>당신의 아이디는 <jsp:getProperty property="id" name="mbean"/></h2>
	<h2>당신의 패스워드는 <jsp:getProperty property="pass1" name="mbean"/></h2>
	<h2>당신의 이메일은 <jsp:getProperty property="email" name="mbean"/></h2>
	<h2>당신의 전화번호는 <jsp:getProperty property="tel" name="mbean"/></h2>
	<h2>당신의 주소는 <%= mbean.getAddress() %></h2>
</center>
</body>
</html>

 

 

 

'Java Category > JSP' 카테고리의 다른 글

[JSP] 파일 업로드  (0) 2023.12.25
[JSP] JSP와 데이터베이스 연동  (2) 2023.11.27
[JSP] 세션(Session)  (1) 2023.11.25
[JSP] 쿠키(Cookie)  (2) 2023.11.24
[JSP] 내장 객체  (2) 2023.11.24

세션

  • 네트워크 환경에서 클라이언트와 웹 서버 간의 상태를 지속적으로 유지하기 위한 방법을 의미
  • 세션은 서버 공간에 생성되므로 보안 유지에 유리하지만 데이터를 저장하기 위한 한계성에 대한 문제는 존재함
  • 세션은 클라이언트의 요청에 따라 접속된 웹 서버와 가상으로 연결된 상태를 유지하도록 해 줌

 

세션에 의한 클라이언트 구분

  • 세션은 웹 서버 공간에 생성되는 객체로 웹 브라우저마다 하나씩 존재
  • 웹 서버와의 접속을 통해 생성된 세션은 네트워크 환경에서 여러 사용자 중 특정인에 대한 구분자의 역할을 수행
  • 세션을 통해 접속된 웹 브라우저를 닫기 전까지는 웹페이지를 이동하더라도 사용자에 대한 정보가 웹 서버에 객체 상태로 저장되어 있으므로 사용자 정보를 지속적으로 활용할 수 있게 됨

 

세션과 쿠키

  • 웹 브라우저에서 서버로 접속할 때 쿠키 대신 세션을 사용하는 이유는 쿠키에 비해 세션이 보안에 강하기 때문임
  • 쿠키와 세션의 가장 큰 차이점은 웹 브라우저를 통해 서비스를 요청할 때 사용자의 정보가 저장되는 위치
  • 쿠키는 서버의 자원을 전혀 사용하지 않지만 세션은 서버의 자원을 사용함
  • 웹 서비스에 대한 요청 속도는 세션보다 쿠키가 더 빠르므로 쿠키와 세션의 선택은 주어진 개발 환경에 맞춰 사용하면 됨

 

세션 내장 객체 메소드

  • 웹 브라우저에서 웹 서버에게 서비스를 요청할 경우 요청한 웹 브라우저에 관한 정보를 저장하고 관리하는 역할을 수행
  • session 객체는 웹 브라우저당 1개의 세션이 할당되도록 하려면 page 디렉티브의 session 속성이 true로 설정되어 있어야 함

 

세션 생성

  • 세션을 생성할 때는 session 내장 객체의 setAttribute( ) 메소드를 사용하여 다음과 같이 생성

괄호 안에 매개 변수의 의미

  • name : 사용할 세션의 이름
  • value : 세션의 속성값

 

세션 정보 확인

단일 세션 정보

  • getAttribute( ) 메소드를 사용하여 세션에 저장된 하나의 세션 속성 이름에 대한 속성을 가져오려면 다음과 같이 선언

  • 주의해야 할 점은 getAttribute( ) 메소드의 반환 유형은 Object 형이므로 반드시 형 변환을 수행한 다음 사용해야 함
  • 괄호 안의 name 매개변수는 세션에 저장된 속성의 이름을 의미하며 name을 선언하지 않게 되면 null의 값을 반환해 줌

 

다중 세션 정보

  • getAttributeNames( ) 메소드를 사용하여 세션에 저장된 여러 개의 세션 속성 이름에 대한 속성을 가져오려면 다음과 같이 선언
  • getAttributeNames( ) 메소드의 반환 유형은 Enumeration 객체 타입이므로 page 디렉티브 태그의 import 속성을 사용하여 java.util.Enumeration을 설정해야 함

 

세션 유효 시간

유효시간 설정 방법

setMaxInactiveInterval( ) 메소드

  • 세션의 유효시간을 설정할 때 사용하는 session 내장 객체의 메소드
  • 메소드의 괄호 안에 정수의 값을 선언한 매개변수로 세션의 유효시간을 설정할 수 있음
  • 세션의 유효시간은 ‘초’ 단위이며 기본값은 1,800초(30분)로 설정되어 있음
  • 세션의 유효시간을 0 또는 음수의 값으로 설정하게 될 경우 유효시간이 없는 상태가 되므로 세션을 삭제 후에도 웹 서버에는 계속해서 남아있게 되어 메모리 부족 현상이 발생될 수 있으므로 주의해야 함

 

 

세션 삭제

단일 세션 삭제

removeAttribute( ) 메소드

  • 단일 세션 memberID를 삭제할 때 사용하는 session 내장 객체의 메소드로 다음과 같이 선언

 

다중 세션 삭제

invalidate( ) 메소드

  • 다중 세션을 삭제할 때 사용하는 session 내장 객체의 메소드로 다음과 같이 선언

 

예제

SessionLoginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<center>
	<h2> 세션 로그인 </h2>
	<form action = "SessionLoginProc.jsp" method = "post">
		<table width = "400" border = "1">
			<tr height = "50">
				<td width="150" align="center"> 아이디 </td>
				<td width="250"> <input type = "text" name = "id"></td>
			</tr>
			<tr height = "50">
				<td width="150" align="center"> 패스워드 </td>
				<td width="250"> <input type = "password" name = "pass"></td>
			</tr>
			<tr height = "50">
				<td align="center" colspan = "2"><input type="submit" value = "로그인"></td>
			</tr>
		</table>
	</form>
</center>
</body>
</html>

 

SessionLoginProc.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<%
	//세션이란 클라이언트가 아니라 서버 PC 메모리에 저장되는 것
	//웹 브라우저당 하나의 세션이 만들어진다.
	//그 세션이 웹 컨테이너(톰캣 서버)에 저장됨
	//브라우저가 종료되지 않는 이상 유지
	
	request.setCharacterEncoding("UTF-8");
	String id = request.getParameter("id");
	String pass = request.getParameter("pass");
	
	session.setAttribute("id", id); //세션 값 생성
	session.setAttribute("pass", pass); //세션 값 생성
	session.setMaxInactiveInterval(60*3);
%>
<h3>당신의 아이디는 <%= id %> 이고, 패스워드는 <%= pass %> 입니다.</h3>
<!-- <a href = "SessionLoginProc2.jsp?id=<%=id%>&pass=<%=pass%>">다음 페이지로 이동(get방식으로 넘김)</a> -->
<a href = "SessionLoginProc2.jsp">다음 페이지로 이동</a>
</body>
</html>

 

SessionLoginProc2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
<%
	request.setCharacterEncoding("UTF-8");
	//String id = request.getParameter("id");
	//String pass = request.getParameter("pass");
	String id = (String)session.getAttribute("id"); // 세션에 저장된 id값을 가져옴
	String pass = (String)session.getAttribute("pass"); //세션에 저장된 pass값을 가져옴
%>
<h3>당신의 아이디는 <%= id %> 이고, 패스워드는 <%= pass %> 입니다.</h3>
</body>
</html>

 

 

'Java Category > JSP' 카테고리의 다른 글

[JSP] JSP와 데이터베이스 연동  (2) 2023.11.27
[JSP] 액션 태그(Action tag)  (1) 2023.11.27
[JSP] 쿠키(Cookie)  (2) 2023.11.24
[JSP] 내장 객체  (2) 2023.11.24
[JSP] 스크립트 태그(Script Tag)  (1) 2023.11.24