- JSTL (JSP Standard Tag Library)
- JSP에서 자주 사용되거나 공통적으로 사용되는 Java 문법(코드)을
스크립틀릿(scriptlet) 대신 HTML 태그 형식으로 전환하여
표준으로 제공하는 라이브러리(아파치 재단에서 제공)
- if, for, 변수 선언(scope 지정 가능), 변수 제거(scope 지정 가능),
형변환, 문자열, 숫자, 날짜 관련 포맷 지정 - JSTL 적용 방법
- JSTL 관련 jar 파일을 webapp/WEB-INF/lib 폴더에 추가
- JSTL을 사용하려는 JSP 페이지 상단에 tablib JSP 지시자를 추가 - JSTL로 변수 선언 / 제거 / 출력
1. 변수 선언(== 속성 추가) (c:set 태그)
- 변수 선언 (속성 추가 == setAttribute)을 위한 태그
- 작성 가능한 속성
1) var : 선언할 변수명 (== 속성 key)
2) value : 대입할 값 (== 속성 value)
3) scope : 범위 지정 (page, request, session, application / 기본값은 page)
2. 변수 제거 (== 속성 제거) (c:remove 태그)
- 변수 제거 (== 속성 제거(removeAttribute("key")))
- 작성 가능한 속성
1) var : 제거할 변수명 (== 속성 key)
2) scope : 지정된 scope의 변수만 제거 (기본값은 모든 scope 일괄 제거)
3. 변수 출력 (c:out 태그)
- EL 구문과 유사
- 단, escapeXml 속성에 따라 html 태그 해석 여부가 결정됨
true : 해석 X (기본값) / false : 해석 O - JSTL 조건문
1. 단일 조건문 (c:if 태그)
- 조건식이 true이면 수행 + 화면에 출력
- 별도의 else 구문 없음
- 속성은 test만 존재
★ ★ test 속성값 작성 시 주의 사항★ ★
1) 속성값은 무조건 EL 구문으로만 작성
ex) test="${key==abc}"
2) 속성값은 무조건 true / false 가 결과로 나와야 함
3) 속성값을 작성하는 " " 제일 앞/뒤에는 띄어쓰기가 있으면 안 됨
※ 오류 예시
test=" ${key==abc}" // 앞에 공백
test="${key==abc} " // 뒤에 공백
2. 연속된 조건문 (c:choose / c:when / c:otherwise)
- c:choose 태그
: c:when / c:otherwise를 감싸는 태그 (내부에 if ~ else if ~ else 구문을 쓰는 것)
★ ★ ★ c:choose 태그 작성 시 주의 사항 ★ ★ ★
c:choose 태그 내부에는
c:when, c:otherwise, JSP 주석, 공백 이외에는 아무 것도 작성되면 안 됨
- c:when 태그
: if, else if를 나타내는 태그 / 속성은 test만 있음 (c:if 참고)
- c:otherwise 태그
: else 역할의 태그로 아무 속성이 없음 - JSTL 반복문 ( c:forEach 태그 )
- 일반 for문 + 추가 기능
- 작성할 수 있는 속성
1) var : 현재 반복 횟수에 해당하는 변수 ( ex. int i )
2) begin : 반복 시 var 시작 값
3) end : 반복이 종료될 var 값
4) step : 반복할 때마다 var가 증가하는 값 (기본값: 1)
5) item : 반복 접근한 객체 (배열, 컬렉션 객체)
6) varStatus : 현재 반복 상태와 관련된 정보를 제공하는 변수 선언
- varStatus="변수명"
→ c:forEach 구문 내에서 "변수명"을 통해 원하는 값을 얻을 수 있음
※ varStatus에서 제공되는 값
a) current : 현재 반복 횟수 (현재 var 값) 또는 현재 반복 접근 중인 객체(컬렉션/배열 요소)
b) index : 현재 인덱스값 반환 (0부터 시작)
c) count : 현재 몇 바퀴째인지 반복 횟수 반환 (1부터 시작)
d) first : 첫 번째 반복이면 true, 아니면 false
e) last : 마지막 반복이면 true, 아니면 false - JSTL 예시
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JSTL</title>
</head>
<body>
<h3>JSTL 확인하기</h3>
<ul>
<li>
JSTL 기초 <button id="btn1">이동</button>
</li>
<li>
JSTL 조건문 <button id="btn2">이동</button>
</li>
<li>
JSTL 반복문 <button id="btn3">이동</button>
</li>
</ul>
<!-- resources 폴더 내 파일 접근 시 절대 경로 사용 권장 -->
<!-- 절대 경로 기준 : webapp == /JSPProject3 -->
<script src="/JSPProject3/resources/js/main.js"></script>
</body>
</html>
- JavaScript
// #btn1 요소 클릭 시
document.getElementById("btn1").addEventListener("click", function(){
// location.href; : 현재 주소 반환
// location.href = 주소; : 대입된 주소로 이동(GET 방식)
location.href="jstl/basic";
})
// #btn2 요소 클릭 시
document.querySelector("#btn2").addEventListener("click", ()=>{
location.href="jstl/condition";
})
// #btn3 요소 클릭 시
document.getElementById("btn3").addEventListener("click", function(){
location.href="jstl/loop";
})
- Servlet Container [ 1 ]
→ BasicServlet
package edu.kh.jsp.jstl.controller;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/jstl/basic")
public class BasicServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/jstl/basic.jsp");
dispatcher.forward(req, resp);
}
}
- JSP [ 1 ]
→ basic ( 변수 선언 / 변수 제거 / 변수 출력)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%-- JSTL에 사용하려는 태그 추가하기 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
<%@ %> : JSP 지시자 태그
taglib : 태그 라이브러리 추가
prefix 속성 : 태그 앞에 붙을 접두사를 지정
uri(Uniform Resource Identifier, 통합 자원 식별자)
: 자원을 식별하는 고유 문자열 (ID)
(참고)
URL(Uniform Resource Locator, 통합 자원 경로)
: 자원의 위치를 나타내는 문자열 (주소, 경로)
--%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSTL 기초</title>
</head>
<body>
<h1>1. 변수 선언(== 속성 추가) (c:set 태그)</h1>
<% // 스크립틀릿으로 page scope 추가하기
pageContext.setAttribute("num1", 5);
%>
<%-- JSTL로 page scope 속성 추가하기 --%>
<c:set var="num2" value="10" scope="page"/>
<!-- <태그명 /> : 태그가 시작되자마자 종료 == 내용이 없는 요소 -->
<h3>${num1}</h3>
<h3>${num2}</h3>
<hr>
<h3>request scope에 세팅</h3>
<c:set var="num2" value="20" scope="request"/>
<h3>request Scope num2 : ${requestScope.num2}</h3>
<hr>
<h3>session, application scope에 세팅</h3>
<c:set var="num2" value="2000" scope="session"/>
<c:set var="num2" value="12345" scope="application"/>
<h3>session Scope num2 : ${sessionScope.num2}</h3>
<h3>application Scope num2 : ${applicationScope.num2}</h3>
<h3>scope 미지정 시 좁은 범위부터 탐색(우선순위) : ${num2}</h3>
<hr>
<h1>2. 변수 제거(== 속성 제거) (c:remove 태그)</h1>
<c:set var="test1" value="테스트1"/>
<%-- scope 미지정 : page --%>
<h4>test1 : ${pageScope.test1}</h4>
<!-- test1 제거 -->
<c:remove var="test1" scope="page"/>
<h4>제거 후 test1 : ${pageScope.test1}</h4>
<hr>
<h3>scope 지정해서 제거하기</h3>
<c:set var="test2" value="page" scope="page"/>
<c:set var="test2" value="request" scope="request"/>
<c:set var="test2" value="session" scope="session"/>
<c:set var="test2" value="application" scope="application"/>
<!-- 특정 scope만 제거 -->
<c:remove var="test2" scope="session"/>
<!-- 모든 scope 제거 (scope 속성 작성 X) -->
<c:remove var="test2"/>
<ul>
<li>${pageScope.test2}</li>
<li>${requestScope.test2}</li>
<li>${sessionScope.test2}</li>
<li>${applicationScope.test2}</li>
</ul>
<hr>
<h1>3. 변수 출력 (c:out 태그)</h1>
<c:set var="temp" value="<h1>c:out 테스트 중 입니다.</h1>"/>
HTML 태그 해석 X : <c:out value="${temp}"/>
<br>
HTML 태그 해석 O : <c:out value="${temp}" escapeXml="false"/>
<br>
EL : EL로 출력시 escapeXml="false" 상태 적용 (HTML 해석함)
${temp}
</body>
</html>
- 화면 [ 1 ]
1) 변수 선언
2) 변수 제거
3) 변수 출력
- Servlet Container [ 2 ]
→ ConditionServlet
package edu.kh.jsp.jstl.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/jstl/condition")
public class ConditionServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/WEB-INF/views/jstl/condition.jsp").forward(req, resp);
}
}
- JSP [ 2 ]
→ condition ( JSTL 조건문 - if )
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%-- core 태그 : 자주 사용하는 자바 코드 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%-- format 태그 : 형식, 형변환 관련 코드 --%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSTL 조건문</title>
</head>
<body>
<h1>1. 단일 조건문 (c:if 태그)</h1>
<h3>c:if 테스트용 변수 선언</h3>
<!-- 변수명 : menu, 범위 : page, 값: 카페모카 -->
<c:set var="menu" value="카페모카" scope="page"/>
<c:set var="price" value="45000" scope="page"/>
<ul>
<li>${menu}</li>
<li>${price}</li>
</ul>
<c:if test="${price == 4500}">
<h4>4500원 입니다.</h4>
</c:if>
<!-- 별도의 else 구문 존재 X -->
<c:if test="${price != 4500}">
<h4>4500원이 아닙니다.</h4>
</c:if>
<hr>
<%-- fmt:parseNumber 태그
- 값을 숫자로 변환한 후 새로운 변수를 생성해서 대입
(변수 생성은 선택 가능)
- 속성
1) value : 숫자로 바꾸려는 값 (필수)
2) pattern : value의 패턴을 인식하여 변화할 수 있게 함
ex) value="1,000,000" pattern="0,000,000"
-> 1000000
3) var : 변수명
4) scope : 범위 지정
5) integerOnly :
true : 정수 부분만 변환
false : 정수가 아닌 부분(소수)도 변환 (기본값)
--%>
<fmt:parseNumber value="${price}" var="parsePrice"/>
<c:if test="${parsePrice <= 50000}">
${menu}를 마실 수 있다.
</c:if>
<c:if test="${parsePrice > 50000}">
${menu}를 마실 수 없다.
</c:if>
<hr>
<h2>2. 연속된 조건문 (c:choose / c:when / c:otherwise)</h2>
<!-- if ~ else if ~ else -->
<h4>테스트 방법</h4>
<pre>
주소창에 값을 직접 입력한 후에
\${param.key} 를 이용해서 값 얻어오기
ex) http://localhost:8081/JSPProject3/jstl/condition?val=100
쿼리스트링(?val=100) 부분을 변경하면서 테스트
</pre>
<c:choose>
<c:when test="${param.val > 100 }">
${param.val}은 100보다 크다.
</c:when>
<c:when test="${param.val > 50 }">
${param.val}은 50초과 100 이하 입니다.
</c:when>
<c:otherwise>
${param.val}은 50 이하 입니다.
</c:otherwise>
</c:choose>
<!-- 파라미터에 val가 존재한다면 (존재하지 않을 경우 빈칸)-->
<c:if test="${!empty param.val}">
<c:choose>
<c:when test="${param.val > 100 }">
${param.val}은 100보다 크다.
</c:when>
<c:when test="${param.val > 50 }">
${param.val}은 50초과 100 이하 입니다.
</c:when>
<c:otherwise>
${param.val}은 50 이하 입니다.
</c:otherwise>
</c:choose>
</c:if>
</body>
</html>
- 화면
1) 단일 조건문
2) 연속된 조건문
- Servlet Container [ 3 ]
→ LoopServlet
package edu.kh.jsp.jstl.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import edu.kh.jsp.model.dto.Book;
@WebServlet("/jstl/loop")
public class LoopServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<Book> bookList = new ArrayList<>();
bookList.add(new Book("소년이 온다", "한강", 15000));
bookList.add(new Book("어른의 행복은 조용하다", "태수", 18900));
bookList.add(new Book("트렌드 코리아 2025", "김난도", 18000));
bookList.add(new Book("행복하지 않으면 인생은 바뀌지 않는다", "브라이언 트레이시", 17550));
bookList.add(new Book("세이노의 가르침", "세이노", 6200));
req.setAttribute("bookList", bookList);
req.getRequestDispatcher("/WEB-INF/views/jstl/loop.jsp").forward(req, resp);
}
}
- JSP [ 3 ]
→ Loop ( JSTL 반복문 - for )
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSTL 반복문</title>
</head>
<body>
<h1>c:forEach 태그</h1>
<h1>일반 for문 형식으로 사용하기</h1>
<ul>
<c:forEach var="i" begin="1" end="10" step="1">
<li>${i}</li>
</c:forEach>
</ul>
<hr>
<c:forEach var="num" begin="1" end="6">
<h${num}>현재 num 값 : ${num}</h${num}>
</c:forEach>
<hr>
<h3>c:forEach의 step은 양수만 가능!</h3>
<!-- h6 -> h1 반복 출력 -->
<c:forEach var="num" begin="1" end="6">
<h${7-num}>현재 num 값 : ${7-num}</h${7-num}>
</c:forEach>
<hr>
<h3>bookList를 일반 for문으로 다루기</h3>
<pre>
접두사 fn (JSTL-EL)
- fn(function, 기능) : 문자열,컬렉션 관련 메서드를
제공하는 접두사
- 태그 형식이 아닌 EL 형식 -> \${fn:메서드명(코드)}
- \${fn:length(배열 또는 컬렉션 또는 문자열)} : 길이 반환
</pre>
<h4>bookList의 길이 : ${fn:length(bookList)} </h4>
<c:forEach var="i" begin="0" end="${fn:length(bookList)-1}">
<li>${i}번째 인덱스 책 이름 : ${bookList[i].title}</li>
가격 :
<c:choose>
<c:when test="${bookList[i].price >= 18000}">
<span style="color: red">${bookList[i].price}원</span>
</c:when>
<c:otherwise>
${bookList[i].price}원
</c:otherwise>
</c:choose>
</c:forEach>
<hr>
<h1>향상된 for문 형식으로 사용하기</h1>
<ul>
<c:forEach items="${bookList}" var="book">
<li>${book}</li>
</c:forEach>
</ul>
<hr>
<h3>varStatus 속성</h3>
<c:forEach items="${bookList}" var="book" varStatus="vs">
<!-- vs.first : 첫 번째 반복이면 true -->
<c:if test="${vs.first}">
<p>---- 시작 ----</p>
</c:if>
<ul>
<li>현재 인덱스 번호 : ${vs.index}</li>
<li>현재 반복 수(count) : ${vs.count}</li>
<li>현재 요소(var 속성값) : ${book}</li>
<li>현재 요소(vs.current) : ${vs.current}</li>
</ul>
<!-- vs.last : 마지막 반복이면 true -->
<c:if test="${vs.last}">
<p>---- 종료 ----</p>
</c:if>
</c:forEach>
<hr>
<table border="1">
<thead>
<tr>
<th>인덱스</th>
<th>제목</th>
<th>저자</th>
<th>가격(원)</th>
</tr>
</thead>
<tbody>
<c:forEach items="${bookList}" var="book" varStatus="vs">
<tr>
<td>${vs.index}</td>
<!-- 데이터셋 속성(data-) : html의 변수 역할
-> 해당 요소에 고유한 커스텀 값을 지정하고 싶은 경우 사용 -->
<td data-price="${book.price}" class="book-title">${book.title}</td>
<td>${book.writer}</td>
<td>${book.price}(원)</td>
</tr>
</c:forEach>
</tbody>
</table>
<script src="/JSPProject3/resources/js/book.js"></script>
</body>
</html>
- JavaScript
// 제목 클릭 시
// alert(OOO은 XXX원 입니다.) 출력
const bookTitleList = document.querySelectorAll(".book-title");
// bookTitleList == 배열
// -> 요소를 하나씩 꺼낸 경우 == 제목 td 요소
// -> 요소를 하나씩 꺼내서 클릭된 경우라는 이벤트 리스너 추가
// 향상된 for문
for(let title of bookTitleList){
title.addEventListener("click", e => {
// 제목
const t = e.target.innerText;
// 가격
// const p = e.target.nextElementSibling.nextElementSibling.innerText;
// data-price 속성값 얻어오기
const p = e.target.getAttribute("data-price");
// alert(t + " 은(는) " + p + "원 입니다.");
// `${백틱}`
alert(`${t} 은/는 ${p}원 입니다.`);
/* alert("[" + title.innerHTML + "]" + "은(는) "
+ title.nextElementSibling.nextElementSibling.innerHTML + "입니다."); */
})
}
- Java
→ Book Class
package edu.kh.jsp.model.dto;
public class Book {
private String title;
private String writer;
private int price;
public Book() {}
public Book(String title, String writer, int price) {
super();
this.title = title;
this.writer = writer;
this.price = price;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return String.format("%s %s %d", title, writer, price);
}
}
- 화면
1) 일반 for문 형식으로 사용
2) List 객체를 일반 for문으로 다루기
3) List 객체를 향상된 for문 형식으로 사용
4) varStatus 속성
5) 예제
'Server > Servlet & JSP' 카테고리의 다른 글
JSP 기본 개념 - 2 ( Expression Language, scope ) (0) | 2025.01.01 |
---|---|
JSP 기본 개념 - 1 (0) | 2025.01.01 |
Servlet 기본 개념 (0) | 2024.12.31 |