부스트코스 강의를 듣고 정리한 내용.

Java Server Pages

JSP란?

간단한 JSP 실습

우선 1부터 10까지 계산하는 JSP 예제 파일을 작성해 보자.

http://localhost:8080/servlet-test/sum10.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>
<%
	int total = 0;
	for(int i = 1; i <= 10; i++) total += i;
%>

1부터 10까지의 ! <%=total %>
</body>
</html>

HTML가 비슷해 보이나 곳곳에 <%@ page 같은 기호들이 보일 것이다. 이것들을 지시자라고 한다.

JSP는 JSP 자체가 동작하는 것이 아니라 서블릿으로 바뀌어 동작한다. 지시자는 JSP가 서블릿으로 바뀔 때 “이렇게 바꿔주세요”라고 알려주는 역할을 한다고 보면 된다.

그럼 다시 예제로 돌아와서 코드 첫 줄을 보자.

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

page 지시자가 있는 page 지시문이고, language 속성은 이 JSP 파일 안에서 무슨 언어로 작성된 코드가 나올건지를 명시한다. 이론적으로 JSP는 내부적으로 자바 말고 다른 언어를 써도 된다고 하긴 하는데 자바를 쓴다. contentType은 이 JSP 파일로 출력되는 결과를 받는 브라우저에게 컨텐츠의 타입을 알려준다. 마지막으로 pageEncoding은 해당 JSP 파일의 인코딩 방식을 지정한다.

이런 지시를 듣는 것은 JSP를 실행하는 WAS라 생각하면 된다.

그런데 HTML 안에 굳이 코드를 넣는 이유는 이 HTML 페이지에서 코드를 실행시키고 싶기 때문이다. 이 부분은 scriptlet이라 부른다.

<%
	int total = 0;
	for(int i = 1; i <= 10; i++) total += i;
%>

scriptlet로 코드를 실행시킨 결과를 HTML로 가져올 때는 표현식이라는 것을 사용한다.

1부터 10까지의 합! <%=total %>

JSP가 서블릿으로 바뀔 때 어떻게 바뀔까 생각해면서 작성하는 것이 중요하다.

JSP 등장 배경

  • MS사에서 ASP(Active Server Page)라는 쉽게 웹을 개발할 수 있는 스크립트 엔진 발표 (1998)
  • 1997년에 발표된 서블릿은 ASP에 비해 개발 방식이 상대적으로 불편함
  • ASP에 대항하기 위해 1999년 썬마이크로시스템즈에서 JSP 발표
  • JSP는 실제로 서블릿 기술 사용

JSP Life Cycle

WAS는 웹브라우저로부터 JSP에 대한 요청을 받게 되면, JSP 코드를 서블릿 소스코드로 변환 후 컴파일해 실행한다. 서블릿으로 컴파일되어 실행될 때, 상황에 따라 어떤 메소드들이 실행되는지 잘 알아야 JSP를 알맞게 작성할 수 있다.

위의 sum10.jsp가 최초로 실행하게 되면 특별한 형태의 서블릿으로 소스가 변환된다. 우리는 톰캣을 사용하니까 톰캣이 JSP를 서블릿으로 바꿔주는데, 어떻게 바꿔주는지 알아보자.

우선 파일 탐색기를 열어 workspace로 이동한 다음 .metadata > .plugins > org.eclipse.wst.server.core > tmp0 > wtpwebapps로 이동하면 프로젝트 이름과 같은 폴더들이 들어있을 것이다. 여기서 servlet-test 디렉토리를 클릭하면 sum10.jsp 파일이 있고, 이 jsp 파일이 서블릿으로 바뀐 코드는 work 디렉토리 안의 servlet-test 폴더에 들어있다.

C:\Users\user\eclipse-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\servlet-test\org\apache\jsp

sum10_jsp.class
sum10_jsp.java

sum10_jsp.java를 열어보자.

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/9.0.16
 ...
 */
package org.apache.jsp;

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

public final class sum10_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {
  
  ...
  
  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 {
   
    ...
    
    try {
      response.setContentType("text/html; charset=UTF-8");
      
      ...

      out.write("\r\n");
      out.write("<!DOCTYPE html>\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("<meta charset=\"UTF-8\">\r\n");
      out.write("<title>Insert title here</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");

      int total = 0;
      for(int i = 1; i <= 10; i++) total += i;

      out.write("\r\n");
      out.write("\r\n");
      out.write("1부터 10까지의 합! ");
      out.print(total );
      out.write("\r\n");
      out.write("</body>\r\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
	  ...
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

중간에 public void _jspService()라는 메소드가 있는데, 서블릿의 init, service, destroy 역할을 하는 메소드라 생각하면 된다.

우리가 만드는 코드는 기본적으로 Service라는 메소드 안에 그래도 만들어진다. JSP가 서블릿으로 만들 때, 자바 코드로 만들 때 ㄹ알아서 만들어놓는 객체들이 있는데 이를 내장 객체라 부른다.

public void _jspService()를 보면

out.write("\r\n");
out.write("<!DOCTYPE html>\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta charset=\"UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");

int total = 0;
for(int i = 1; i <= 10; i++) total += i;

out.write("\r\n");
out.write("\r\n");
out.write("1부터 10까지의 합! ");
out.print(total );
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>");

진짜 내가 작성한 코드를 자바로 옮겨온 것을 확인할 수 있다. 신기해 ㅋㅋㅋㅋ

sum10.jsp가 실행될 때 벌어지는 일

  • eclipse workspace 아래의 .metadata 폴더에 sum10_jsp.java 파일이 생성된다.
  • 해당 파일의 _jspService() 메소드 안을 살펴보면 jsp 파일의 내용이 변환돼 들어가 있는 것을 확인할 수 있다.
  • sum10_jsp.java는 서블릿 소스로 자동으로 컴파일되면서 실행돼 그 결과가 브라우저에 보여진다.

JSP 실행 순서

  1. 브라우저가 웹서버에게 JSP에 대한 요청 정보를 전달한다.
    • 이때 서버는 이 JSP에 해당되는 서블릿이 존재하는지를 체크한다. 만약 존재하지 않으면 최초로 요청한 거임
  2. 브라우저가 요청한 JSP가 최초로 요청한 경우
    • JSP로 작성된 코드가 서블릿 코드로 변환된다. (java 파일 생성)
    • 서블릿 코드를 컴파일해 실행 가능한 bytecode로 변환한다. (class 파일 생성)
    • 서블릿 클래스를 로딩하고 인스턴스를 생성한다.
  3. 서블릿이 실행돼 요청을 처리하고 응답 정보를 생성한다.

그럼 init할 때만 실행되는 코드를 작성하고 싶을 때는 어떻게 하면 좋을까?

%!라는 선언식을 사용하면 이 클래스에 메소드나 필드를 선언할 때 넣어주면 service 메소드 내에 만들어지는게 아니라 메소드 바깥쪽에 할 수 있다. 이를 응용해 init()과 destroy()를 내가 직접 작성해 주면 됨.

<%!
	public void jspInit() {
		System.out.println("jspInit()");
	}
%>

<%!
	public void jspDestroy() {
		System.out.println("jspDestroy()");
	}
%>

JSP 문법

스크립트 요소의 이해

JSP 페이지에는 선언문, 스크립트릿, 표현식이라는 세가지 스크립트 요소 제공

  • 선언문 (Declaration)
    • <%! %>
    • 전역변수 선언 및 메소드 선언에 사용
  • 스크립트릿 (Scriptlet)
    • <% %>
    • 프로그래밍 코드 기술에 사용
  • 표현식
    • <%=%>
    • 화면에 출력할 내용 기술에 사용
    • 응답 결과에 포함할 부분을 넣을 때 사용하는 부분

선언문 (Declaration)

  • <%! 문장 %>
  • JSP 페이지 내에서 필요한 멤버변수나 메소드가 필요할 때 선언해 사용하는 요소
  • 선언문을 사용하면 서비스 메소드가 아니라 클래스 바디 쪽에 해당 코드가 바뀌는 것을 볼 수 있다.

id를 출력해주는 예제를 작성해보자.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
id: <%=getId() %>

<%!
	String id = "u001"; // 멤버변수 선언
	public String getId() { // 메소드 선언
		return id;
	}
%>
</body>
</html>

참고로 JSP 파일 안에서 선언문의 위치는 중요하지 않다. body 안에만 있으면 되는 듯?

변환된 자바 파일을 열어보면

... 

public final class exam1_jsp extends org.apache.jasper.runtime.HttpJspBase  
    implements org.apache.jasper.runtime.JspSourceDependent,  
  org.apache.jasper.runtime.JspSourceImports {  
  
  
   String id = "u001"; // 멤버변수 선언  
  public String getId() { // 메소드 선언  
  return id;  
  }
  ... 

처럼 클래스 바디에 해당 코드가 작성되었음을 확인할 수 있다.

스크립트릿 (Scriptlet)

  • <% 문장 %>
  • 가장 일반적으로 많이 쓰이는 스크립트 요소
  • 주로 프로그래밍의 로직을 기술할 때 사용
  • 스크립트릿에서 선언된 변수는 지역변수. 모두 service 메소드 안에 선언된다.

그런데 스크립트릿 안의 값은 응답 결과에 포함되지 않는데, 만약 스크립트릿 안에서 html을 조작하고 싶다면 어떻게 할까?

스크립트릿을 쪼개 작성하면 된다.

<%
	for(int i = 1; i <= 6; i++) { 
%>
<H<%=i%>>점점 작아지는 폰트 사이즈</H<%=i%>>
<% 
	}
%>

이렇게 응용하면 태그가 동적으로 만들어져 추가된다! +_+

표현식 (Expression)

  • <%=a문장%>
  • JSP 페이지에서 웹 브라우저에게 출력할 부분을 표현
    • 화면에 출력하기 위한 것
  • 스크립트릿 내에서 출력할 부분은 내장 객체인 out 객체의 print() 또는 println() 메소드를 사용해 출력

주석

  • JSP에서 사용할 수 있는 주석: HTML 주석, 자바 주석, JSP 주석
  • HTML 주석
    • <!-- 로 시작해 -->로 끝나는 형태
  • JSP 주석
    • <%-- 로 시작해 --%>로 끝나는 형태
    • JSP 페이지에서만 사용됨
    • 해당 페이지를 웹 브라우저를 통해 출력 결과로서 표시하거나 웹 브라우저 상에서 소스 보기를 해도 표시되지 않음. 또한 JSP 주석 내에 실행코드를 넣어도 그 코드는 실행되지 않음
  • 자바 주석
    • // , /**/을 사용
    • 자바와 주석 처리 방법 같음

HTML, JSP, 자바 주석을 넣어 실행시키고

<body>
<%--JSP 주석
여거 줄로 사용 가능
--%>
<!-- HTML 주석 -->
<%
// 자바 주석
/*
여러 줄도 가능
*/
%>
<%
	for(int i = 1; i <= 6; i++) { 
%>
<H<%=i%>>점점 작아지는 폰트 사이즈</H<%=i%>>
<% 
	}
%>
</body>

소스를 뜯어보면 HTML은 화면에 출력되는 것을 볼 수 있다.

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- HTML 주석 -->
<H1>점점 작아지는 폰트 사이즈</H1>
<H2>점점 작아지는 폰트 사이즈</H2>
<H3>점점 작아지는 폰트 사이즈</H3>
<H4>점점 작아지는 폰트 사이즈</H4>
<H5>점점 작아지는 폰트 사이즈</H5>
<H6>점점 작아지는 폰트 사이즈</H6>
</body>
</html>

자바 주석은 자바 파일에서 확인 가능하다.

out.write("\r\n");
out.write("<!DOCTYPE html>\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\r\n");
out.write("<!-- HTML 주석 -->\r\n");

// 자바 주석
/*
여러 줄도 가능
*/
...
out.write('\r');
out.write('\n');

for(int i = 1; i <= 6; i++) { 
  out.write("\r\n");
  out.write("<H");
  out.print(i);
  out.write(">점점 작아지는 폰트 사이즈</H");
  out.print(i);
  out.write('>');
  out.write('\r');
  out.write('\n');
}
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>");

반면 JSP 주석은 볼 수 없다.

JSP 내장 객체

JSP 내장객체란?

  • JSP를 실행하면 서블릿 소스가 생성되고 실행된다.
  • JSP에 입력한 대부분의 코드는 생성되는 서블릿 소스의 _jspService() 메소드 안에 삽입되는 코드로 생성된다.
  • _jspService()에 삽입된 코드의 윗부분에 미리 선언된 객체들이 있는데, 해당 객체들은 JSP에서도 사용 가능하다.
  • response, request, application, session, out과 같은 변수를 내장 객체라 한다.
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;

내장 객체의 종류

내장객체의종류

내장 객체를 사용해 본 코드. JSP는 이런 내장 객체들이 있으니까 필요에 따라서 갖다 쓰면 된다는 정도 기억하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
	StringBuffer url = request.getRequestURL();

	out.print("url: " + url);
%>
</body>
</html>