내장 객체를 사용하지 않는 JSP 프로그래밍은 상상할 수 없습니다. 웹 프로그래밍의 근간이 되는 요청과 응답부터 출력, 세션, 페이지와 애플리케이션 등 없어서는 안 될 개념들을 내장 객체로 제공하므로 수시로 광범위하게 활용됩니다.
클라이언트의 요청을 받거나 응답할 때 사용되는 JSP의 기본 내장 객체들의 종류와 사용법을 정리했습니다.
[JSP] 내장 객체(Implicit Object)란?
1. 내장 객체란?
제품에 어떤 기능을 기본으로 내장했다고 함은 내장된 기능이 그 제품에서 핵심적이고 매우 유용하다는 뜻입니다. JSP의 내장 객체Implicit Object도 마찬가지입니다. 기본적인 요청과 응답, 화면 출력 등은 거의 모든 웹 프로그래밍에 있어 필수라 할 수 있습니다.
웹은 앞의 그림처럼 클라이언트가 서버로 요청을 보내면, 서버는 그에 맞는 결과를 응답으로 돌려주는 형태로 동작합니다. 이때 JSP의 내장 객체는 요청과 응답 혹은 HTTP 헤더(Header) 등의 정보를 쉽게 다룰 수 있도록 해줍니다.
내장 객체는 JSP 페이지가 실행될 때 컨테이너가 자동으로 생성해줍니다. 별도로 선언하거나 객체로 생성하지 않아도 즉시 사용할 수 있는데, 그 이유는 JSP의 실행 과정에서 찾을 수 있습니다. JSP는 실행될 때 자바 파일인 서블릿으로 변환되어 컴파일됩니다. 이 변환 과정에서 _jspService( ) 메서드가 생성되는데, 이 메서드 안에 다음과 같은 코드가 삽입됩니다.
public void _jspService() {
... 생략 ...
final jakarta.servlet.jsp.PageContext pageContext;
jakarta.servlet.http.HttpSession session = null;
final jakarta.servlet.ServletContext application;
final jakarta.servlet.ServletConfig config;
jakarta.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
jakarta.servlet.jsp.JspWriter _jspx_out = null;
jakarta.servlet.jsp.PageContext _jspx_page_context = null;
... 생략 ...
삽입된 문장을 살펴보면 객체를 선언하고 초기화하는 선언문인 것을 알 수 있습니다. 바로 내장 객체의 참조 변수를 컨테이너가 생성하는 부분이죠.
내장 객체는 다음과 같은 특징을 지니고 있습니다.
- 컨테이너가 미리 선언해놓은 참조 변수를 이용해 사용합니다.
- 별도의 객체 생성 없이 각 내장 객체의 메서드를 사용할 수 있습니다.
- JSP 문서 안의 <% 스크립틀릿 %>과 <%= 표현식 %>에서만 사용할 수 있습니다.
- <%! 선언부 %>에서는 즉시 사용하는 건 불가능하고, 매개변수로 전달받아 사용할 수는 있습니다.
▼ 내장 객체의 종류
표에서 보듯 내장 객체는 총 아홉 가지가 있습니다. 이번 장에서는 이중 가장 기본적인 request, response, out, application, exception에 대해 자세히 알아보겠습니다.
2. request 객체
request 내장 객체는 JSP에서 가장 많이 사용되는 객체로, 클라이언트(주로 웹 브라우저)가 전송한 요청 정보를 담고 있는 객체입니다. 주요 기능은 다음과 같습니다.
- 클라이언트와 서버에 대한 정보 읽기
- 클라이언트가 전송한 요청 매개변수에 대한 정보 읽기
- 요청 헤더 및 쿠키 정보 읽기
예제와 함께 하나씩 알아보겠습니다.
2.1 클라이언트와 서버의 환경정보 읽기
첫 번째로 request 내장 객체를 통해 클라이언트와 서버의 환경정보를 알아내보겠습니다. 클라이언트는 웹 브라우저를 통해 서버 측으로 요청을 하게 됩니다. 이때 요청은 GET 방식 혹은 POST 방식으로 구분되고, 요청 URL, 포트 번호, 쿼리스트링 등을 명시할 수 있습니다. request 내장 객체를 이용하면 이러한 정보를 얻어올 수 있습니다.
💡Note: 본문의 예제들은 webapp/02ImplicitObject 폴더에 위치합니다. 첫 번째 예제 코드를 작성하기 전에 webapp 밑에 02ImplicitObject 폴더를 만들어주세요.
아래 요청 페이지 예제는 클라이언트의 요청을 전송하기 위한 페이지로, HTML로만 구성되어 있습니다.
▼ 요청 페이지
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - request</title></head>
<body>
<h2>1. 클라이언트와 서버의 환경정보 읽기</h2>
<a href="./RequestWebInfo.jsp?eng=Hello&han=안녕"> ❶ GET 방식으로 요청
GET 방식 전송
</a>
<br />
<form action="RequestWebInfo.jsp" method="post"> ❷ POST 방식으로 요청
영어 : <input type="text" name="eng" value="Bye" /><br />
한글 : <input type="text" name="han" value="잘 가" /><br />
<input type="submit" value="POST 방식 전송" />
</form>
<h2>2. 클라이언트의 요청 매개변수 읽기</h2>
<form method="post" action="RequestParameter.jsp"> ❸ 다양한 <input> 태그 사용
아이디 : <input type="text" name="id" value="" /><br />
성별 :
<input type="radio" name="sex" value="man" />남자
<input type="radio" name="sex" value="woman" checked="checked" />여자
<br />
관심사항 :
<input type="checkbox" name="favo" value="eco" />경제
<input type="checkbox" name="favo" value="pol" checked="checked" />정치
<input type="checkbox" name="favo" value="ent" />연예<br />
자기소개:
<textarea name="intro" cols="30" rows="4"></textarea>
<br />
<input type="submit" value="전송하기" />
</form>
<h2>3. HTTP 요청 헤더 정보 읽기</h2>
<a href="RequestHeader.jsp"> ❹ HTTP 요청 헤더 읽기
요청 헤더 정보 읽기
</a>
</body>
</html>
❶ 에서는 클라이언트의 요청에 따른 서버의 환경정보를 읽기 위해 링크를 생성하였습니다. <a> 태그로 만든 링크이므로 GET 방식으로 전송되고, 링크 뒤에는 2개의 매개변수가 쿼리스트링(Query String)으로 전달됩니다. ❷ 는 ❶ 과 똑같은 링크이나 <form> 태그를 사용하여 POST 방식으로 요청을 전송합니다.
💡Note: 쿼리스트링은 URL 뒤에 “?키=값&키=값” 형태로 덧붙여진 부분을 말합니다.
❸ 역시 <form> 태그를 통해 POST 방식으로 요청합니다. 다만 이번에는 다양한 <input> 태그를 사용했습니다. type 속성에 따라 매개변수를 읽어오는 방식이 조금 다른데, 다음 절에서 설명합니다.
❹ 에서는 HTTP 요청 헤더를 읽기 위한 링크를 생성했습니다. 2.3에서 링크를 통해 이동했을 때와 직접 실행했을 때 출력되는 정보의 차이를 확인해보겠습니다.
다음은 RequestMain.jsp 실행 화면 중 ‘1. 클라이언트와 서버의 환경정보 읽기’ 부분입니다.
그리고 다음 예제 코드는 이 페이지에서 ‘GET 방식 전송’ 링크나 [POST 방식 전송] 버튼을 클릭했을 때 나타나는 페이지의 소스입니다. 보다시피 request 내장 객체로부터 클라이언트와 서버의 환경정보를 읽어와 화면에 표시해줍니다.
▼ 환경정보 읽기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - request</title></head>
<body>
<h2>1. 클라이언트와 서버의 환경정보 읽기</h2>
<ul>
<li>데이터 전송 방식 : <%= request.getMethod() %></li> ❶
<li>URL : <%= request.getRequestURL() %></li> ❷
<li>URI : <%= request.getRequestURI() %></li> ❸
<li>프로토콜 : <%= request.getProtocol() %></li>
<li>서버명 : <%= request.getServerName() %></li>
<li>서버 포트 : <%= request.getServerPort() %></li>
<li>클라이언트 IP 주소 : <%= request.getRemoteAddr() %></li> ❹
<li>쿼리스트링 : <%= request.getQueryString() %></li> ❺
<li>전송된 값 1 : <%= request.getParameter("eng") %></li> ❻
<li>전송된 값 2 : <%= request.getParameter("han") %></li>
</ul>
</body>
</html>
❶ 의 getMethod( ) 메서드는 GET과 POST 같은 전송 방식을 반환합니다. 앞서 ‘GET 방식 전송’ 링크를 클릭했다면 “GET”을, [POST 방식 전송] 버튼을 클릭했다면 “POST”를 반환할 것입니다.
❷ 의 getRequestURL( )과 ❸ 의 getRequestURI( ) 메서드는 요청 주소를 반환합니다. 여기서 URL과 URI의 차이는 호스트host를 포함하느냐 여부입니다. URL은 호스트를 포함한 전체 주소를 뜻하며, URI는 호스트를 제외한 컨텍스트 루트부터의 주소를 뜻합니다.
💡Note: 컨텍스트 루트는 호스트명 다음에 나오는 프로젝트명 정도로 생각하면 됩니다.
❹ 의 getRemoteAddr( ) 메서드는 클라이언트의 IP 주소를 반환합니다. localhost로 접속했을 때는 0:0:0:0:0:0:0:1과 같이 출력됩니다. 윈도우 10에서는 IPv6를 반환하는 게 기본이기 때문인데, WAS 설정을 IPv4로 변경하면 127.0.0.1로 출력됩니다.
❺의 getQueryString( ) 메서드는 요청 주소 뒷부분의 매개변수 전달을 위한 쿼리스트링 전체를 반환합니다. 쿼리스트링 중 특정 키값을 얻어오려면 ❻ 처럼 getParameter( ) 메서드에 키값을 인수로 넣어주면 됩니다.
RequestMain.jsp 실행 화면에서 ‘GET 방식 전송’ 링크를 클릭했을 때의 결과는 다음과 같습니다. 주소표시줄을 보면 쿼리스트링으로 매개변수가 2개 전달되었고, 출력도 정상적으로 되었습니다.
[POST 방식 전송] 버튼을 클릭했을 때의 결과는 다음과 같습니다.
❶ POST 방식이므로 주소표시줄에는 경로 외에는 아무것도 표시되지 않습니다. ❷ 그러므로 쿼리스트링은 null이 출력됩니다.
2.2 클라이언트의 요청 매개변수 읽기
<form> 태그 하위 요소를 통해 입력한 값들도 서버로 전송됩니다. 전송된 값은 서버에서 읽은 후 변수에 저장하고, 적절한 처리를 위해 컨트롤러(Controller)나 모델(Model)로 전달됩니다. 대표적으로 회원가입이나 로그인 등을 예로 들 수 있습니다.
다음은 RequestMain.jsp 실행 화면 중 ‘2. 클라이언트의 요청 매개변수 읽기’ 부분입니다.
값을 적당히 입력하고 [전송하기] 버튼을 클릭하면 POST 방식으로 RequestParameter.jsp에 전송됩니다.
▼ 요청 매개변수 읽기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - request</title></head>
<body>
<%
request.setCharacterEncoding("UTF-8"); ❶
String id = request.getParameter("id"); ❷
String sex = request.getParameter("sex");
String[] favo = request.getParameterValues("favo"); ❸
String favoStr = "";
if (favo != null) { ❹
for (int i = 0; i < favo.length; i++) {
favoStr += favo[i] + " ";
}
}
String intro = request.getParameter("intro").replace("\r\n", "<br/>"); ❺
%>
<ul>
<li>아이디 : <%= id %></li>
<li>성별 : <%= sex %></li>
<li>관심사항 : <%= favoStr %></li>
<li>자기소개 : <%= intro %></li>
</ul>
</body>
</html>
톰켓의 버전에 따라 POST 방식으로 전송된 값이 한글인 경우 깨짐 현상이 발생할 수 있습니다. 이럴 때는 ❶ 처럼 다국어를 지원하는 UTF-8로 인코딩해주면 해결됩니다.
❷ 전송되는 값이 하나라면 getParameter( ) 메서드로 받을 수 있습니다. 주로 type 속성이 text, radio, password인 경우 사용되고, checkbox인 경우라도 선택값이 하나라면 getParameter( )로 값을 받을 수 있습니다.
하지만 type 속성이 checkbox인 경우는 대부분 값을 여러 개 선택하기 위해 사용하므로 ❸ 처럼 getParameterValues( )로 받아야 합니다. 값이 2개 이상이므로 String 배열을 반환합니다. ❹ 그리고 for문을 이용해서 String 배열에 담긴 값들을 하나의 문자열로 합쳤습니다.
마지막으로 textarea 태그는 텍스트를 여러 줄 입력할 수 있습니다. ❺ 그래서 출력 시에는 enter 키를 <br> 태그로 변환해야 줄바꿈이 제대로 반영됩니다. enter 는 특수 기호 \r\n으로 입력됩니다.
다음은 전송 결과의 예시입니다.
2.3 HTTP 요청 헤더 정보 읽기
HTTP 프로토콜은 헤더에 부가적인 정보를 담도록 하고 있습니다. 웹 브라우저의 종류나 선호하는 언어 등 일반적인 HTML 문서 데이터 외의 추가 정보를 서버와 클라이언트가 교환할 수 있도록 문서의 선두에 삽입할 수 있습니다. 먼저 요청 헤더를 읽어오는 방법을 알아보죠.
‘요청 헤더 정보 읽기’를 클릭하면 아래 요청 헤더 읽기의 RequestHeader.jsp 코드가 실행됩니다.
▼ 요청 헤더 읽기
<%@ page import="java.util.Enumeration"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - request</title></head>
<body>
<h2>3. 요청 헤더 정보 출력하기</h2>
<%
Enumeration headers = request.getHeaderNames(); ❶
while (headers.hasMoreElements()) { ❷
String headerName = (String)headers.nextElement(); ❸
String headerValue = request.getHeader(headerName); ❹
out.print("헤더명 : " + headerName + ", 헤더값 : " + headerValue + "<br/>");
}
%>
<p>이 파일을 직접 실행하면 referer 정보는 출력되지 않습니다.</p>
</body>
</html>
❶ 의 getHeaderNames( ) 메서드는 모든 요청 헤더의 이름을 반환합니다. 반환 타입은 Enumeration입니다. ❷ 에서는 while문에서 hasMoreElements( )를 이용해 출력할 요청 헤더명이 더 있는지 확인합니다. ❸ 헤더명이 더 있다면 요청 헤더의 이름을 얻어온 다음, ❹ getHeader( ) 메서드에 헤더명을 건네 헤더값을 얻어옵니다.
실행 결과는 다음과 같습니다.
- user-agent : 웹 브라우저의 종류를 알 수 있습니다. 크롬, 파이어폭스, 익스플로러 등 여러가지 웹 브라우저에서 테스트해보면 조금씩 다른 결과가 출력될 것입니다.
- referer : 리퍼러는 웹을 서핑하면서 링크를 통해 다른 사이트로 방문 시 남는 흔적을 말합니다. 예를 들어 [예제 2-1]에서 클릭해서 이동하면 리퍼러가 출력되지만, RequestedHeader. jsp를 직접 실행하면 리퍼러는 출력되지 않을 것입니다. 리퍼러는 웹 사이트 방문객이 어떤 경로로 접속하였는지 알아볼 때 유용합니다.
- cookie : 요청 헤더를 통해 쿠키도 확인할 수 있습니다.
3. response 객체
request 내장 객체가 클라이언트의 요청 정보를 저장하는 역할을 했다면, response 내장 객체는 그와 반대로 요청에 대한 응답을 웹 브라우저로 보내주는 역할을 합니다. 주요 기능으로는 페이지 이동을 위한 리다이렉트redirect와 HTTP 헤더에 응답 헤더 추가가 있습니다. 이 두 기능 외에도 몇 가지가 더 있으나 JSP에서는 거의 사용하지 않습니다.
3.1 sendRedirect()로 페이지 이동하기
페이지를 이동하기 위해 HTML은 <a> 태그를 사용하고, 자바스크립트에서는 location 객체를 사용합니다. JSP에서는 response 내장 객체의 sendRedirect( )를 이용합니다. 다음 그림과 같이 간단한 로그인 폼을 제작하여 리다이렉트 기능을 확인해보겠습니다.
▼ 로그인 폼과 응답 헤더 설정 페이지
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - response</title></head>
<body>
<h2>1. 로그인 폼</h2>
<%
String loginErr = request.getParameter("loginErr"); ❶
if (loginErr != null) out.print("로그인 실패"); ❷
%>
<form action="./ResponseLogin.jsp" method="post"> ❸
아이디 : <input type="text" name="user_id" /><br />
패스워드 : <input type="text" name="user_pwd" /><br />
<input type="submit" value="로그인" />
</form>
<h2>2. HTTP 응답 헤더 설정하기</h2>
<form action="./ResponseHeader.jsp" method="get"> ❹
날짜 형식 : <input type="text" name="add_date" value="2022-12-20 09:00" /><br /> ❺
숫자 형식 : <input type="text" name="add_int" value="8282" /><br />
문자 형식 : <input type="text" name="add_str" value="홍길동" /><br />
<input type="submit" value="응답 헤더 설정 & 출력" />
</form>
</body>
</html>
❶ 의 loginErr은 로그인 실패 여부를 알려주는 매개변수입니다. 곧이어 살펴볼 로그인 처리하기 예제에서 만약 로그인에 실패하면 이 매개변수에 값을 설정하도록 했습니다. ❷ 즉, 이 매개변수에 값이 들어 있다면 로그인에 실패했음을 뜻하므로 “로그인 실패”를 출력합니다. 그리고 ❸ 에서는 아이디와 패스워드를 입력받는 간단한 로그인 폼을 구성합니다.
❹ 에서는 응답 헤더 추가를 위한 입력 폼을 구성하며, ❺ 에서 헤더에 추가할 데이터의 형식별로 value 속성에 미리 입력해두었습니다. 이 부분은 다음 절에서 사용합니다.
실행 화면은 다음과 같습니다.
💡Note: 날짜 형식의 응답 헤더를 지정하는 경우에는 반드시 날짜와 함께 시간을 추가하는 것이 좋습니다. 대한민국은 세계 표준시보다 9시간이 느립니다. 따라서 09:00을 추가하지 않으면 하루 전 날짜가 출력됩니다.
‘1. 로그인 폼’ 영역에 아이디와 패스워드를 입력한 후 [로그인] 버튼을 클릭하면 요청이 로그인 처리하기 예제의 ResponseLogin.jsp 페이지로 전송됩니다.
▼ 로그인 처리하기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - Response</title></head>
<body>
<%
String id = request.getParameter("user_id"); ❶
String pwd = request.getParameter("user_pwd");
if (id.equalsIgnoreCase("must") && pwd.equalsIgnoreCase("1234")) { ❷
response.sendRedirect("ResponseWelcome.jsp"); ❸
}
else {
request.getRequestDispatcher("ResponseMain.jsp?loginErr=1") ❹
.forward(request, response);
}
%>
</body>
</html>
이 코드는 먼저 ❶ 에서 request 내장 객체로 전송된 매개변수를 얻어온 다음, ❷ 에서 회원 인증을 진행합니다. 아직 데이터베이스 연동법을 배우지 않았기 때문에 아이디와 패스워드를 각각 must”와 “1234”로 하드코딩해뒀습니다.
인증에 성공하면 ❸ 이 실행되며, sendRedirect( ) 메서드에 건넨 응답 페이지로 이동합니다. 자바스크립트의 location.href와 같은 기능이라 보면 됩니다. ResponseWelcome.jsp는 로그인 성공 페이지입니다.
❹ 반면 인증에 실패하면 request 내장 객체를 통해 로그인 페이지, 즉 ResponseMain.jsp로 포워드forward; 전달됩니다. 포워드는 페이지 이동과는 다르게 제어 흐름을 넘겨주고자 할 때 사용합니다. 이때 쿼리스트링으로 loginErr 매개변수를 전달하여 로그인 성공 여부를 알려주고 있습니다.
다음으로 회원 인증에 성공했을 때 이동되는 ResponseWelcome.jsp 파일을 보겠습니다.
▼ 로그인 성공 페이지
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - response</title></head>
<body>
<h2>로그인 성공</h2>
</body>
</html>
특별한 로직 없이 성공 여부만 간단히 알려줍니다.
하지만 로그인에 실패하면 로그인 처리하기 예제의 ❹ 에서 ResponseMain.jsp로 포워드하는데, 이때 매개변수 loginErr가 전달됩니다. 이를 통해 로그인 실패 메시지를 화면에 출력할 수 있습니다.
그런데 화면에는 분명 ResponseMain.jsp의 내용이 출력되었지만, 웹 브라우저의 주소표시줄을 보면 ResponseLogin.jsp로 표시되어 있습니다. 포워드는 이처럼 실행의 흐름만 특정한 페이지로 넘겨주는 역할을 합니다.
3.2 HTTP 헤더에 응답 헤더 추가하기
response 내장 객체는 응답 헤더에 정보를 추가하는 기능을 제공합니다. 정보 추가용 메서드는 add 계열과 set 계열이 있습니다. add 계열은 헤더값을 새로 추가할 때 사용하고, set 계열은 기존의 헤더를 수정할 때 사용합니다. 로그인 폼과 응답 헤더 설정 페이지 예제의 ‘2. HTTP 응답 헤더 추가 설정하기’ 부분에서 적당한 값을 입력한 후 [응답 헤더 설정 & 출력] 버튼을 클릭해보세요.
버튼을 클릭하면 응답 헤더에 값 추가하기 예제의 ResponseHeader.jsp가 실행되며, 이 페이지는 전달받은 값을 응답 헤더에 추가해 되돌려줍니다.
▼ 응답 헤더에 값 추가하기
<%@ page import="java.util.Collection"%>
<%@ page import="java.text.SimpleDateFormat"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
// 응답 헤더에 추가할 값 준비
SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm"); ❶
long add_date = s.parse(request.getParameter("add_date")).getTime();
int add_int = Integer.parseInt(request.getParameter("add_int")); ❷
String add_str = request.getParameter("add_str");
// 응답 헤더에 값 추가
response.addDateHeader("myBirthday", add_date);
response.addIntHeader("myNumber", add_int);
response.addIntHeader("myNumber", 1004); // 추가 ❸
response.addHeader("myName", add_str);
response.setHeader("myName", "안중근"); // 수정 ❹
%>
<html>
<head><title>내장 객체 - response</title></head>
<body>
<h2>응답 헤더 정보 출력하기</h2> ❺
<%
Collection<String> headerNames = response.getHeaderNames();
for (String hName : headerNames) {
String hValue = response.getHeader(hName);
%>
<li><%= hName %> : <%= hValue %></li>
<%
}
%>
<h2>myNumber만 출력하기</h2> ❻
<%
Collection<String> myNumber = response.getHeaders("myNumber");
for (String myNum : myNumber) {
%>
<li>myNumber : <%= myNum %></li>
<%
}
%>
</body>
</html>
우선 응답 헤더에 추가할 값들을 준비하는 코드를 살펴봅시다. ❶ 에서는 0000-00-00 00:00(년-월-일 시:분) 형식으로 전송된 add_date 매개변수의 값을 long 타입으로 변경합니다. 변경된 값은 타임스탬프(Timestamp)라고 표현하는데, 1970년 1월 1일 0시 0분 0초부터 현재까지의 시간을 밀리초 단위로 환산한 값을 말합니다. ❷ 한편, 폼값으로 전송되는 값은 항상 String 타입이므로 add_int도 문자열로 얻어집니다. 따라서 이를 정수 형태로 사용할 때는 반드시 변환해야 합니다.
다음은 응답 헤더에 값을 추가하는 코드 차례입니다. 먼저 add 계열의 메서드로 헤더값을 추가합니다. 특히 ❸ 에서는 바로 위에서 추가한 “myNumber”라는 동일한 헤더명으로 새로운 값을 추가하고 있는데, add 계열이므로 같은 헤더명으로 값이 하나 더 추가됩니다. 반면 ❹ 처럼 set 계열의 메서드를 사용하면 이전 값이 수정됩니다(같은 이름의 헤더가 없었다면 새로 추가됩니다).
❺ 부분의 실행 결과는 다음과 같습니다.
보다시피 “myNumber”가 두 번 출력되며 그 값은 모두 8282로 동일합니다. 이는 getHeader( ) 메서드의 특성으로, 값이 여러 개더라도 첫 번째 값만 가져오기 때문입니다.
반면 myName은 set 계열 메서드를 이용해서 수정되었으므로 “홍길동”이 아닌 “안중근”이 출력되었습니다.
그럼 이번에는 myNumber만 출력하는 ❻ 부분의 실행 결과를 보겠습니다.
헤더명은 같지만 다른 값을 출력했습니다. 이와 같이 add 계열 메서드는 헤더명이 같더라도 (덮어쓰지 않고) 계속 누적시킵니다.
💡Note
- add 계열 메서드: 새로운 헤더명으로 값을 추가합니다. 동일한 헤더명이 있으면 동일한 이름으로 값을 추가합니다.
- set 계열 메서드: 기존의 헤더값을 수정합니다. 단, 동일한 헤더명이 존재하지 않는다면 새롭게 추가합니다.
4. out 객체
out 내장 객체는 웹 브라우저에 변수 등의 값을 출력할 때 주로 사용합니다. 하지만 JSP를 작성하면서 사용하는 일은 그리 많지 않습니다. 대부분 상황에서 이 객체보다는 표현식<%= %>이 더 편리하기 때문입니다. 하지만 스크립틀릿 내에서 변수를 웹 브라우저에 출력해야 한다면 표현식보다는 out 내장 객체를 사용하는 편이 좋습니다.
out 내장 객체는 버퍼를 사용합니다. 즉, 출력되는 모든 정보는 버퍼에 먼저 저장된 후 웹 브라우저에 출력됩니다. 예제를 살펴보시죠.
▼ out 객체로 값 출력하기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - out</title></head>
<body>
<%
// 버퍼 내용 삭제하기
out.print("출력되지 않는 텍스트"); // 버퍼에 저장 ❶
out.clearBuffer(); // 버퍼를 비움(윗 줄의 출력 결과 사라짐) ❷
out.print("<h2>out 내장 객체</h2>");
// 버퍼 크기 정보 확인
out.print("출력 버퍼의 크기 : " + out.getBufferSize() + "<br>"); ❸
out.print("남은 버퍼의 크기 : " + out.getRemaining() + "<br>"); ❹
out.flush(); // 버퍼 내용 출력 ❺
out.print("flush 후 버퍼의 크기 : " + out.getRemaining() + "<br>"); ❻
// 다양한 타입의 값 출력 ❼
out.print(1);
out.print(false);
out.print('가');
%>
</body>
</html>
버퍼를 사용한다는 점만 주의하면 어렵지 않습니다. 우선 ❶ 에서 print( ) 메서드로 쓴 내용은 먼저 버퍼에 들어가게 되는데, ❷ 에서 clearBuffer( ) 메서드로 버퍼 안의 내용을 지워버렸습니다. 그래서 ❶ 에서 쓴 문자열은 출력되지 않습니다.
❸~❻ 에서는 버퍼의 크기 관련 정보를 얻는 예를 보여줍니다. 의 getBufferSize( )는 현재 페이지에 설정된 버퍼의 크기를 가져옵니다. 버퍼 크기를 page 지시어로 따로 선언하지 않았으므로 기본값인 8KB가 될 것입니다. 한편 getRemaining( ) 메서드는 사용하고 남은 버퍼의 크기를 알려줍니다. 그래서 ❹ 와 ❻ 은 각각 버퍼 플러시 전과 후의 남은 크기를 출력해줍니다.
❺ 의 flush( ) 메서드는 버퍼에 담긴 내용을 강제로 플러시합니다. 버퍼는 원래 모두 채워졌을 때 플러시되면서 내용을 출력하지만, 필요한 경우에는 이처럼 즉시 출력할 수 있습니다.
마지막으로 ❼ 은 print( ) 메서드를 이용해 다양한 타입의 데이터를 출력하는 예입니다. 문자열, 문자, 숫자, boolean 등 기본 자료형은 모두 가능합니다.
실행 결과는 다음과 같습니다.
print( )를 사용하면서 버퍼가 채워지다가, flush( )로 모두 출력한 후 크기가 복원된 것을 알 수 있습니다. print( ) 외에 println( )도 있는데, 차이점은 입력 끝에 줄바꿈 문자(\r\n)를 추가한다는 것뿐입니다. 여기서 오해를 하는 경우가 있는데요, 줄바꿈 문자가 추가되니 웹 브라우저에서 보이는 화면에서도 줄바꿈 처리가 되지 않을까 라는 생각을 하게 됩니다. 하지만 HTML 문서에서 줄을 바꾸려면 줄바꿈 문자가 아니라 <br/> 태그를 사용해야 합니다. 따라서 print( )와의 차이는 띄어쓰기 한 칸이 적용되는 정도입니다.
2.5 application 객체
application 내장 객체는 웹 애플리케이션당 하나만 생성되며, 모든 JSP 페이지에서 접근할 수 있습니다. 앞서 내장 객체의 종류 표에서 언급했듯이 application 내장 객체는 ServletContext 타입입니다. 주로 웹 애플리케이션 전반에서 이용하는 정보를 저장하거나, 서버의 정보, 서버의 물리적 경로 등을 얻어오는 데 사용합니다.
이번에는 web.xml에 설정한 컨텍스트 초기화 매개변수를 읽어오고 폴더의 물리적 경로를 알아내는 모습을 보여드리겠습니다.
참고로 web.xml은 웹 애플리케이션에 대한 여러 가지 설정을 저장하는 곳으로, 배포 서술(Deployment Descriptor)라고 부릅니다. 물리적 경로란 특정 파일이나 폴더의 전체 경로, 즉 절대 경로를 말하며, 파일을 업로드하거나 입출력 작업 시에 주로 사용합니다.
먼저 WEB-INF 폴더에 있는 web.xml 파일을 열어서 아래 초기화 매개변수 추가하기 예제처럼 초기화 매개변수를 추가해주세요.
▼ 초기화 매개변수 추가하기
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi=... 생략 ...>
<display-name>MustHaveJSP</display-name>
<welcome-file-list>
... 생략 ...
</welcome-file-list>
<!-- 컨텍스트 초기화 매개변수 -->
<context-param>
<param-name>INIT_PARAM</param-name>
<param-value>web.xml에 저장한 초기화 매개변수</param-value>
</context-param>
보시는 것처럼 <context-param> 태그를 만들고, 그 안에 초기화 매개변수의 이름과 값을 입력하면 됩니다.
다음은 application 내장 객체를 이용하는 코드를 보겠습니다.
▼ 다양한 서버 정보 얻어오기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - application</title></head>
<body>
<h2>web.xml에 설정한 내용 읽어오기</h2>
초기화 매개변수 : <%= application.getInitParameter("INIT_PARAM") %> ❶
<h2>서버의 물리적 경로 얻어오기</h2>
application 내장 객체 : <%= application.getRealPath("/02ImplicitObject") %> ❷
<h2>선언부에서 application 내장 객체 사용하기</h2>
<%!
public String useImplicitObject() { ❸
return this.getServletContext().getRealPath("/02ImplicitObject");
}
public String useImplicitObject(ServletContext app) { ❹
return app.getRealPath("/02ImplicitObject");
}
%>
<ul>
<li>this 사용 : <%= useImplicitObject() %></li> ❺
<li>내장 객체를 인수로 전달 : <%= useImplicitObject(application) %></li> ❻
</ul>
</body>
</html>
❶ 에서는 getInitParameter( ) 메서드를 이용해서 web.xml에 설정한 초기화 매개변수를 읽어왔습니다. web.xml에서 “INIT_PARAM”을 설정하지 않았다면 null을 반환할 것입니다.
❷ 에서는 getRealPath( ) 메서드로 현재 예제를 작성 중인 폴더(“/02ImplicitObject”)의 물리적 경로를 얻어와서 출력합니다. 인수로는 ‘컨텍스트 루트를 제외한’ 경로를 입력하면 됩니다.
❸ 과 ❹ 는 선언부에 정의한 메서드들입니다. 두 메서드 모두 “/02ImplicitObject”의 물리적 경로를 반환하지만, 이때 필요한 서블릿 컨텍스트를 ❸ 은 this로부터 얻는 데 반해 ❹ 는 매개변수로 받습니다.
마지막으로 ❺ 에서는 this를 이용하는 ❸ 을 호출하고, ❻ 에서는 application 내장 객체를 인수로 하여 ❹ 를 호출합니다.
실행 결과는 다음과 같습니다.
서버의 물리적 경로는 세 가지 모두 동일한 결과가 출력될 것입니다. 단지 사용 방법이 조금 다릅니다. 스크립틀릿이나 표현식에서 사용할 때는 application 내장 객체를 그대로 사용하면 됩니다. 하지만 선언부에서는 application 내장 객체를 바로 사용하는 것은 불가능합니다. 따라서 this를 통해 application 내장 객체를 얻어오거나, 인수로 전달해야지만 사용할 수 있습니다.
실행 결과를 보면 조금 의아한 점이 있을 것입니다. 분명히 우리는 02Workspace 하위에 프로젝트를 생성했는데 출력 내용은 뭔가 어마무시하게 길어져 있습니다. 이유는 우리가 이클립스를 사용했기 때문입니다. 작성한 JSP 파일을 실행하면 이클립스는 별도의 디렉터리를 생성하여 서블릿 변환과 컴파일을 진행하게 됩니다. 차후 19장에서 톰캣에 배포한 후 실행하면 JSP 파일이 있는 위치가 그대로 출력될 것입니다.
6. exception 객체
exception 내장 객체는 주로 오류명과 오류 메시지를 출력하는 부분에서 사용하며, JSP에서 그 이상으로 사용되는 경우가 거의 없기 때문에 이번에는 오류 페이지를 처리하는 또 다른 방식을 학습해보겠습니다.
JSP로 프로그래밍을 하다 보면 가장 빈번하게 발생되는 에러가 404, 405, 500 에러입니다.
▼ HTTP 에러 코드 설명 및 조치 방법
HTTP에서 발생되는 에러는 훨씬 다양하나, 위 표의 세 가지 정도만 알고 있어도 충분합니다.
이번에는 에러가 발생했을 때 에러별로 출력할 페이지를 분기하는 방법을 알아보겠습니다. 이 방법을 이용하려면 web.xml에 설정을 추가해야 합니다.
▼ web.xml에 에러별 출력 페이지 설정
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi=... 생략 ...>
... 생략 ...
<!-- 에러 페이지 처리하기 -->
<!-- <error-page>
<error-code>404</error-code> ❶ 에러 코드
<location>/02ImplicitObject/Exception.jsp</location> ❷ 출력할 페이지
</error-page>
<error-page>
<error-code>405</error-code>
<location>/02ImplicitObject/Exception.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/02ImplicitObject/Exception.jsp</location>
</error-page> -->
</web-app>
보다시피 에러 코드를 정수로 명시하고, 해당 에러 발생 시 웹 브라우저에 출력할 페이지와 경로를 지정했습니다. <location> 엘리먼트에 경로를 명시할 때는 컨텍스트 루트를 제외한 나머지를 기술하면 됩니다. 경로는 슬래시(/)로 시작해야 합니다.
편의상 Exception.jsp라는 파일 하나에서 모든 에러를 처리하도록 했습니다. 다음 예제는 출력 페이지의 코드입니다.
▼ 에러 출력 페이지
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>내장 객체 - exception</title></head>
<body>
<%
int status = response.getStatus(); // response 내장 객체로부터 에러 코드 확인
System.out.println(status);
// 에러 코드에 따라 적절한 메시지 출력
if (status == 404) {
out.print("404 에러가 발생하였습니다.");
out.print("<br/>파일 경로를 확인해주세요.");
}
else if (status == 405) {
out.print("405 에러가 발생하였습니다.");
out.print("<br/>요청 방식(method)을 확인해주세요.");
}
else if (status == 500) {
out.print("500 에러가 발생하였습니다.");
out.print("<br/>소스 코드에 오류가 없는지 확인해주세요.");
}
%>
</body>
</html>
테스트를 위해 [JSP] 웹 프로그래밍 시작하기 예러 발생 페이지 예제의 Error500.jsp를 실행해보죠. 원래는 에러 페이지가 보였지만 지금은 다음 화면이 보일 것입니다.
웹 브라우저의 주소표시줄에는 여전히 Error500.jsp라는 파일명이 보이지만, 실제 출력된 것은 Exception.jsp의 내용입니다.
학습 마무리
JSP의 내장 객체는 별도의 선언 없이 사용할 수 있는 객체입니다. 클라이언트의 요청을 받거나 요청에 대한 응답을 할 수 있고, 자바 코드에 대한 예외 처리를 할 수 있습니다.
핵심 요약
- request 객체 : 클라이언트의 요청을 받거나 웹 브라우저에 대한 정보 혹은 요청 헤더에 대한 정보를 읽을 때 사용합니다.
- response 객체 : 요청에 대한 응답을 웹 브라우저로 보낼 때 사용합니다. 페이지 이동이나 응답 헤더를 추가할 때도 사용합니다.
- out 객체 : 변수 등의 값을 웹 브라우저에 출력할 때 주로 사용합니다.
- application 객체 : 웹 애플리케이션을 구성하는 모든 JSP에서 접근 가능한 객체로, 웹 애플리케이션에 대한 설정값을 저장할 때 주로 사용합니다.
- exception 객체 : 예외 처리를 위해 사용합니다.
성낙현
월드컵으로 뜨거웠던 2002년부터 웹 개발을 시작하여 에듀웰 모바일 웹 기획 및 개발, 마이닥터 웹/앱 개발(앱스토어 의료부문 1위), 국민건강보험 건강마라톤대회 앱 개발 등 다양한 앱 개발과 프로젝트를 두루 경험하였다. 현재는 한국소프트웨어인재개발원에서 JAVA 개발자 과정을 강의하고 있다.