📄example.jsp
// example.jsp
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JSP - Hello World</title>
</head>
<body>
<h1>Hello</h1>
</body>
</html>
📄example.jsp 의 서블릿 컴파일 코드
public final class calculator_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
// 멤버함수 , 멤버변수
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
// 지역변수 , 알고리즘
}
}
이 메서드가 JSP한 요청의 실제 처리를 한다. 원래 service()를 오버라이드하는 서블릿과 달리 Jasper가 _jspService로 생성하고 컨테이너가 호출한다.
📄example.jsp
// example.jsp
1️⃣ 환영합니다.
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JSP - Hello World</title>
</head>
<body>
<h1>Hello</h1>
</body>
</html>
가령 상단에 1️⃣번 처럼 환영합니다.를 쓰게 된다면 서블릿 컴파일은 아래와 같이 출력을 하게된다.
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
1️⃣out.write("환영합니다.");
}
하지만 실행되어야할 코드를 넣을 땐 아래와 같이 코드 블록을 사용한다.
📄example.jsp
// example.jsp
<%
1️⃣ y = x + 3
%>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JSP - Hello World</title>
</head>
<body>
<h1>Hello</h1>
</body>
</html>
📄example.jsp 의 서블릿 컴파일 코드
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
1️⃣ y = x + 3
}
<% %> 를 사용하게 되면 _jspService메서드에 선언이 된다.
아래와 같이 JSP파일에서 함수를 선언하고 싶을 때는 이렇게 사용을 하면 에러가 발생한다.
❌
<%
public int sum (int a, int b) {
return a + b;
}
%>
👌
<%!
public int sum (int a, int b) {
return a + b;
}
%>
<% %> 를 사용하게 되면 _jspService메서드에 선언이 되기 때문이다. 메서드 내부에 또다른 메서드가 정의가 되는 것이 되므로 컴파일 에러가 발생한다. 그래서 클래스 내부에 메서드가 정의되어야 함으로 <%! %> 를 사용한다.
_jspService 내부에 out.write("...")_jspService(...) 안에서 그대로 출력됨예시
용도: 고정 레이아웃/마크업, 변하지 않는 안내문
<!-- header.jsp -->
<header class="gnb">
<a href="/">Home</a>
<nav> ... </nav>
</header>
팁: 정적 레이아웃은 JSP 인클루드(정적/동적)로 재사용하면 유지보수 용이
<% ... %>_jspService 메서드 몸통에 그대로 삽입_jspService(...) 실행 시 자바 코드 수행<%! ... %> 사용해야 함예시
용도: 임시 디버깅/레거시 코드에서 빠른 분기
<%-- 가급적 JSTL/EL로 대체 --%>
<%
boolean isAdmin = "admin".equals(session.getAttribute("role"));
if (!isAdmin) { response.sendRedirect("/403.jsp"); return; }
%>
팁: 실무에선 JSTL/EL + 컨트롤러에서 데이터 준비로 대체하는 게 표준
<%= ... %>_jspService 안의out.write(String.valueOf(...))<%= 1 + 2 %> <!-- 출력: 3 -->예시
용도: 간단 값 출력(로그인 사용자명 등)
<p>안녕하세요, <strong><%= request.getAttribute("userName") %></strong> 님!</p>
팁: 표현식도 지양하고 ${userName} EL을 쓰는 게 더 안전/일관
${ ... }PageContextImpl.proprietaryEvaluate(...)ExpressionFactory 호출_jspService(...) 중 EL 평가 후 출력page/request/session/application 스코프에서 속성 탐색예시
용도: 모델 데이터 출력/간단 계산/널 안전 접근
<p>안녕하세요, <strong>${user.name}</strong> 님!</p>
<p>장바구니: ${cart.totalCount}개 / 합계: ${cart.totalPrice}원</p>
<p>기본값: ${empty user.nickname ? user.name : user.nickname}</p>
팁: EL은 속성 스코프(page/request/session/application)에 있는 값만 본다.
컨트롤러/서블릿에서 request.setAttribute("user", userDto) 식으로 전달
<%! ... %> (필드/메서드 선언)jspInit() / jspDestroy() → 각각 _jspInit() / _jspDestroy()_jspInit()_jspDestroy()예시
용도: (지양) 페이지 전역 유틸/상수/초기화 로직
<%!
private static final String VERSION = "v1.2.3";
public void jspInit() { getServletContext().log("calculator.jsp init"); }
public void jspDestroy() { getServletContext().log("calculator.jsp destroy"); }
%>
<footer>build: <%= VERSION %></footer>
팁: 전역/상태는 서블릿/필터/리스너/스프링 빈으로 옮기는 게 베스트
<%@ page ... %>_jspService 시작부에서 response.setContentType(...) 등 설정 적용예시
용도: 인코딩/버퍼/임포트/에러 페이지 등 JSP 동작 설정
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
buffer="8kb" autoFlush="true" isELIgnored="false" %>
<%@ page import="java.time.LocalDateTime" %>
팁: 한글 깨짐 방지: contentType + pageEncoding 둘 다 UTF-8 명시
<%@ include file="..." %> (정적 include)예시
용도: 빌드 타임 결합(공통 헤더/푸터/메뉴) – 한 서블릿으로 컴파일
<%@ include file="/WEB-INF/views/common/header.jsp" %>
<main>...</main>
<%@ include file="/WEB-INF/views/common/footer.jsp" %>
팁: 정적 include는 변수 공유가 쉽고 성능도 좋음. 단 파일 간 의존 주의
<jsp:include page="..."> (동적 include)JspRuntimeLibrary.include(...) 또는 RequestDispatcher.include(...) 호출 생성예시
용도: 런타임에 다른 리소스 출력 포함(동적 데이터 포함)
<jsp:include page="/banner.jsp">
<jsp:param name="slot" value="HOME_TOP"/>
</jsp:include>
팁: 동적 include는 각 JSP가 독립적으로 실행되어 유지보수에 유리
<jsp:forward page="...">JspRuntimeLibrary.forward(...) 호출 후 SkipPageException 처리예시
용도: 조건부로 다른 화면으로 넘기기(남은 출력 스킵)
<c:if test="${empty sessionScope.user}">
<jsp:forward page="/login.jsp"/>
</c:if>
팁: 컨트롤러(서블릿/스프링)에서 forward/redirect를 결정하는 구조가 더 깔끔
pageContext.findAttribute(...) → 없으면 생성 → 세터 호출 코드 생성예시
용도: (레거시) JSP에서 자바빈을 생성/프로퍼티 채우기/출력
<jsp:useBean id="form" class="com.example.Form" scope="request"/>
<jsp:setProperty name="form" property="*" /> <%-- 파라미터 이름과 매핑 --%>
이름: <jsp:getProperty name="form" property="name"/>
팁: 현대 실무는 컨트롤러가 DTO를 채워 request.setAttribute("form", dto) 로 전달
<c:...>, <fmt:...> 등)setPageContext / 속성 setter → doStartTag() / doEndTag() 호출 코드 생성예시
용도: 조건/반복/국제화/포맷팅/URL 빌드 등 뷰 로직
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<c:if test="${not empty products}">
<ul>
<c:forEach var="p" items="${products}">
<li>${p.name} - <fmt:formatNumber value="${p.price}" type="currency"/></li>
</c:forEach>
</ul>
</c:if>
팁: 조건/반복은 JSTL로, 단순 출력/연산은 EL로. 스크립틀릿은 지양
<%-- ... --%>예시
용도: 운영에 노출되면 안 되는 내부 메모/비밀 값
<%-- TODO: 결제 수수료 정책 변경(9/1) --%>
팁: 클라이언트에 절대 노출 안 됨. 반면 HTML 주석은 노출됨!
<!-- ... -->out.write("<!-- ... -->")예시
용도: 프론트 협업용 가이드 주석(노출 허용 가능 내용만)
<!-- 여기는 런딩 상단 배너 영역 -->
팁: 브라우저 “보기-소스”에 그대로 보임 민감한 내용 금지
서블릿 로딩 시 (1회)
jspInit() → _jspInit() 실행매 요청마다
_jspService(request, response) 실행request, response, session, application, out 등)서블릿 언로드 시 (1회)
jspDestroy() → _jspDestroy()