JSP(Java Server Page)

JSP는 웹에서 동적 문서에 해당하는 부분으로 Servlet의 종류 중 하나다.

앞에서 서블릿을 자세히 설명한 이유는 JSP와 동작 원리가 같기 때문이다.

JSP는 클라이언트가 동적페이지를 요청 했을 경우 웹서버에서 WAS에게 제어권을 넘기고 WAS가 서블릿 소스코드로 변환되고 서블리스 클래스로 컴파일 되어 응답하는 구조로 이루어져 있다.

앞서 서블릿을 공부하면서 불편한 부분이 많았을 것이다.

서블릿은 기본적으로 자바를 기본으로 html코드를 out.println()의 형태로 작성하는 구조이지만 JSP는 html코드를 기본으로 html문서 도중 java코드가 삽입 되어있는 형태이다.

따라서 해당 파트가 java문법임을 알려주는 태그(?)가 있다.

  1. <%! — — —  %> : 변수 선언문으로 !와% 사이에 선언하고자 하는 변수를 작성하면 된다.
  2. <% — — —  %> : 스크립트릿으로 %와%사이에 java코드 그대로 작성하면 된다. 주로 연산처리나 반복문 처리에 사용되게 된다.
  3. <% =— — — %> : 표현식으로 html문서 즉 웹에 바로 출력될 내용을 입력한다.

아래 코드 예제를 통해서 자세히 알아보자.

%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<% 
String s = "String";
%>
<html>
<head>
<meta charset="UTF-8">
<title>hello</title>
</head>
<body>
	<h2>Hello JSP</h2>
	현재 날짜와 시간은 : <%=new java.util.Date() %><br>
	<%= s %> 이러한 형태 또는 <%out.println(s); %>이와 같은 형태로 작성해면 된다.<br>
</body>
</html>

위와 같은 결과를 볼 수 있다. 여기서 소스를 직접 보면 우리가 코딩한것과 다른것을 볼 수 있다. 컴파일 시에 변수는 문자열 처리가 되어 작성 되었고 java코드 또한 바뀌어 컴파일 된것을 볼 수 있다.

<!DOCTYPE html>

<html>
<head>
<meta charset="UTF-8">
<title>hello</title>
</head>
<body>
	<h2>Hello JSP</h2>
	현재 날짜와 시간은 : Tue Jun 09 18:15:38 KST 2020<br>
	String 이러한 형태 또는 String
이와 같은 형태로 작성해면 된다.<br>
</body>
</html>

그렇다면 하는김에 좀 더 변수를 가지고 놀아보자

기본적으로 변수를 사용하려면 변수를 선언한 후에 사용하게 된다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>

<html>
<head>
<meta charset="UTF-8">
<title>hello</title>
</head>
<body>
	<h2>Hello JSP</h2>
	현재 날짜와 시간은 : <%=new java.util.Date() %><br>
	<%= s %> 이러한 형태 또는 <%out.println(s); %>이와 같은 형태로 작성해면 된다.<br>

<% 
	String s = "String";
%>
</body>
</html>

따라서 위와 같이 코딩하면 빨간 줄과 함께 코드에 문제가 있음을 확인할 수 있다.

하지만 우리는 변수를 선언하는 다른 선언문을 알고있다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>

<html>
<head>
<meta charset="UTF-8">
<title>hello</title>
</head>
<body>
	<h2>Hello JSP</h2>
	현재 날짜와 시간은 : <%=new java.util.Date() %><br>
	<%= s %> 이러한 형태 또는 <%out.println(s); %>이와 같은 형태로 작성해면 된다.<br>

<%!
	String s = "String";
%>
</body>
</html>

이렇게 선언하면 아래와 같이 정상적으로 실행 됨을 알 수있다. 대체 어떻게 가능한 일인가 보자

이클립스 기준으로D:\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\myWeb\org\apache\jsp

해당 경로에 가면 JSP파일이 컴파일 된것을 볼 수 있다. 소스코드를 확인해보면 우리가 작성한 JSP파일이 자바의 형태로 바뀐것을 볼 수있다.

package org.apache.jsp.sample;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {

	//변수 선언문으로 선언한 변수
	String s = "String";

  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

  private static final java.util.Set<java.lang.String> _jspx_imports_packages;

  private static final java.util.Set<java.lang.String> _jspx_imports_classes;

  static {
    _jspx_imports_packages = new java.util.HashSet<>();
    _jspx_imports_packages.add("javax.servlet");
    _jspx_imports_packages.add("javax.servlet.http");
    _jspx_imports_packages.add("javax.servlet.jsp");
    _jspx_imports_classes = null;
  }

  private volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }

  public java.util.Set<java.lang.String> getPackageImports() {
    return _jspx_imports_packages;
  }

  public java.util.Set<java.lang.String> getClassImports() {
    return _jspx_imports_classes;
  }

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }

  public void _jspInit() {
  }

  public void _jspDestroy() {
  }

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
      throws java.io.IOException, javax.servlet.ServletException {

    if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
      final java.lang.String _jspx_method = request.getMethod();
      if ("OPTIONS".equals(_jspx_method)) {
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        return;
      }
      if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP들은 오직 GET, POST 또는 HEAD 메소드만을 허용합니다. Jasper는 OPTIONS 메소드 또한 허용합니다.");
        return;
      }
    }
    
	//JSP 내장 객체
    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html; charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
      			null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("\r\n");
      out.write("<!DOCTYPE html>\r\n");
      out.write("\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("<meta charset=\"UTF-8\">\r\n");
      out.write("<title>hello</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
      out.write("\t<h2>Hello JSP</h2>\r\n");
      out.write("\t현재 날짜와 시간은 : ");
      out.print(new java.util.Date() );
      out.write("<br>\r\n");
      out.write("\t");
      out.print( s );
      out.write(" 이러한 형태 또는 ");
out.println(s); 
      out.write("이와 같은 형태로 작성해면 된다.<br>\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("</body>\r\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

좀 장황하나 주석을 달아놓은 부분을 자세히 살펴보면 클래스의 시작과 동시에 변수를 선언한것을 볼 수 있다. 변수 선언문을 사용하면 클래스의 가장 위에 선언이 된다. 따라서 어느 위치에서든지 사용할 수 있게 된 것이다.

 

JSP 주석

JSP에는 세 가지 주석이 있다.

  1. <! -- --> : html주석 html에서 사용하는 주석으로 JSP에서도 사용할 수 있다.
  2. <%-- --%> : JSP주석
  3. //, /* */ : JAVA주석

각각의 주석에는 차이가 있다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSP File</title>
</head>
<body>
	<h2>JSP Script 예제</h2>
	<% 
		String scriptlet = "스크립트릿 입니다.";
		String comment = "주석문 입니다.";
		out.println("내장객체를 이용한 출력 : "+declation+"<br></br>");
	%>
	선언문 출력하기(변수) : <%=declation %><br></br>
	<!-- JSP에서 사용하는 HTML 주석부분 -->
	<!--  HTML 주석 : <%=comment%> --><br></br>
	<%-- JSP 주석 : <%=comment%> --%><br></br>
	<%
		//자바 주석
		/*
			자바 여러줄 주석
		*/
	%>
	<%! 
		String declation = "선언문 입니다.";
	%>
	<%!
		public String declationMethod(){
			return declation;
		}
	%>
</body>
</html>

위 코드를 실행시켜보면

주석 처리한 부분이 잘 되어있는것을 확인할 수 있다. 하지만 소스보기를 통해 확인 해 보면 아래와 같이 보임을 알 수 있다.

여기서 알 수 있는 점은

1. html주석은 소스보기를 통해 볼 수 있다.

2. JAVA주석은 소스를 볼 순 없지만 흔적이 남아있는것을 볼 수 있다.

3. JSP주석은 흔적도 소스도 남지 않는것을 볼 수 있다.

'FRONT > JSP' 카테고리의 다른 글

[Front] Servlet(서블릿)의 구조와 동작 원리  (0) 2020.06.04
서블릿의 구조

서블릿은 좀 전에 자바 웹 프로그램이라고 소개한 적이 있다.

이는 웹을 구성하는 기본적인 요소인데 우리는 보통 이 웹 어플리케이션으로 Tomcat을 사용하게 된다. Tomcat의 폴더를 가 보면

이와 같이 구성 되어있는 것을 볼 수 있다. 이는 웹 어플리케이션이라면 가져야 할 폴더 구조이다.

https://www.edwith.org/viewer/image?src=https%3A%2F%2Fcphinf.pstatic.net%2Fmooc%2F20180124_133%2F15167752967943AqfC_PNG%2F1_5_1_____.PNG

서블릿의 동작 원리와 라이프 사이클(life cycle)

https://mangkyu.tistory.com/14

더 정확히 말하자면 여기서 클라이언트가 아니라 WAS(Web Application Server)일 것이다.

여기서 서블릿 컨테이너의 역할은 서블릿과 서버를 연결 해 주고 객체를 생성 해 주는 것이다. 따라서 요청이 있을때 마다 객체를 생성 해 주는 역할을 하게 된다.

  1. 클라이언트로부터 요청이 들어오면 http 프로토콜을 사용하게 되므로 request, response객체를 생성하기 된다.
  2. 요청된 url에 매핑된 서블릿의 생성자를 호출하게 된다.
  3. 생성자는 init()메서드를 실행하게 된다.
  4. init()메서드는 service()메서드를 호출하고 service()메서드는 스레드에 담겨진다.
  5. 요청한 방식(get, post)에 따라서 해당 메서드로 이동한다.
  6. 응답하고 연결을 끊는다.

이것이 servlet의 동작 순서이다. 그러면 각 메서드의 역할을 알아보자.

  1. 생성자 : 생성자는 따로 작성하지 않고 default 생성자를 이용해도 된다. default 생성자는 init()메서드를 호출하게 된다.
  2. init() : init()메서드는 service()메서드를 호출하게 된다.
  3. service() : 요청된 방식(get, post)를 확인하고 맞는 메서드로 분기 시켜준다. (doGet(), doPost())
  4. doGet() : get방식으로 요청된 처리를 한다.
  5. doPost() : post방식으로 요청된 처리를 한다.
  6. destroy() : 연결 해제

자 여기 까지 알았으면 service()메서드가 스레드에 담겨진다는 말에 대한 의문이 생길 것이다.

매번 이러한 과정을 거치게 된다면 시간도 오래걸릴뿐만 아니라 매번 객체가 생성되어 서버가 마비가 되는 등의 문제가 발생하게 된다. 따라서 최초 요청에만 위와 같은 동작을 하게 되고 이후에는 바로 스레드의 서비스 메서드로 이동하게 된다. 이를 코드를 통해 알아보자.

package sample;

import java.io.IOException;

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


public class LifeCycle extends HttpServlet {
	private static final long serialVersionUID = 1L;
      
	public int count =0;
    public LifeCycle() {
    	System.out.println("LifeServlet의 생성자 호출됨...");
    }
    
    @Override
    public void init() throws ServletException {
    	System.out.println("init() 호출됨...");
    }
    
    @Override
    public void destroy() {
    	System.out.println("destroy() 호출됨...");
    }
    
    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
    	System.out.println("service() 호출됨...");
    	System.out.println(count);
    	count++;
    }
}

이해를 돕기 위해 모든 메서드를 오버라이딩 해놨다.

사실 destroy() 메서드는 톰캣을 끄는 속도가 더 빨라서 확인할 수 없지만 알 수 없는 오류로 운좋게 확인했다.

이와 같이 최초 요청시에만 생성자와 init()메서드가 실행되었고 새로고침 하면서 계속 요청을 하면 service() 메서드만 실행되는 것을 볼 수 있다.

 

'FRONT > JSP' 카테고리의 다른 글

[Front] JSP 동작 원리와 기초  (0) 2020.06.10

+ Recent posts