12. 표현 언어와 JSTL

JSP에서 자바 코드를 완전히 제거하기 위한 표현 언어(EL)와 JSTL(JSP Standard Tag Library)의 문법과 활용법을 다룬다.


1. 표현 언어(EL)란

표현 언어(Expression Language)는 JSP 2.0부터 도입된 데이터 출력 기능이다. 기존 표현식(<%= %>)의 자바 코드가 복잡해지는 문제를 해결하기 위해 등장했다.

기본 형식

${표현식 또는 값}

표현식 vs EL 비교

<%-- 기존 표현식 --%>
<%= request.getAttribute("name") %>
<%= ((MemberVO)request.getAttribute("member")).getName() %>

<%-- 표현 언어 --%>
${name}
${member.name}

EL의 특징

특징설명
간결한 문법${} 안에 속성명만 작성
내장 객체 지원EL 전용 내장 객체 제공
연산자 지원산술, 비교, 논리, empty 연산자
빈 속성 접근. 또는 []로 프로퍼티 접근
자동 형변환문자열을 숫자로 자동 변환
JSP 페이지에서 EL을 사용하려면 페이지 디렉티브에 isELIgnored="false"를 설정해야 한다. JSP 2.0 이상에서는 기본값이 false이므로 별도 설정 없이 사용 가능하다.

2. EL의 자료형과 연산자

자료형

자료형예시설명
불리언true, false참/거짓
정수42, -70~9, 음수는 - 사용
실수3.14, 1.4e5소수점, 지수 표현 가능
문자열'hello', "hello"따옴표로 감싸서 사용
nullnull값 없음

연산자

종류연산자문자 표현
산술+, -, *, /, %div, mod
비교==, !=, <, >, <=, >=eq, ne, lt, gt, le, ge
논리&&, ||, !and, or, not
emptyempty 값null이거나 빈 값이면 true
조건조건 ? 값1 : 값2삼항 연산자

연산자 사용 예시

<%-- 산술 연산 --%>
<p>합계: ${10 + 20}</p>
<p>나머지: ${10 mod 3}</p>

<%-- 비교 연산 --%>
<p>같은가: ${name eq "홍길동"}</p>
<p>큰가: ${score gt 90}</p>

<%-- 논리 연산 --%>
<p>${age ge 19 and age le 65}</p>

<%-- empty 연산 --%>
<p>비어있나: ${empty memberList}</p>

<%-- 삼항 연산 --%>
<p>${score ge 60 ? "합격" : "불합격"}</p>
JSP에서 <> 기호는 태그로 인식될 수 있다. EL에서는 비교 연산 시 문자 표현(lt, gt, le, ge)을 사용하는 것이 안전하다.

3. EL 내장 객체

EL은 JSP 내장 객체와 별도로 ${} 안에서 사용할 수 있는 전용 내장 객체를 제공한다.

내장 객체 목록

구분내장 객체설명
스코프pageScopepage 영역 바인딩 객체
requestScoperequest 바인딩 객체
sessionScopesession 바인딩 객체
applicationScopeapplication 바인딩 객체
요청 파라미터param단일 값 (getParameter)
paramValues복수 값 (getParameterValues)
헤더header요청 헤더 단일 값
headerValues요청 헤더 배열 값
쿠키cookie쿠키 이름으로 값 조회
JSPpageContextpageContext 객체 참조
초기화initParam컨텍스트 초기화 파라미터

사용 예시

<%-- 요청 파라미터 --%>
<p>아이디: ${param.id}</p>
<p>취미: ${paramValues.hobby[0]}</p>

<%-- 스코프별 속성 접근 --%>
<p>${requestScope.membersList}</p>
<p>${sessionScope.loginUser}</p>

<%-- 헤더 정보 --%>
<p>브라우저: ${header["User-Agent"]}</p>

<%-- 컨텍스트 경로 --%>
<p>${pageContext.request.contextPath}</p>

<%-- 쿠키 --%>
<p>${cookie.JSESSIONID.value}</p>

스코프 우선순위

같은 이름의 속성이 여러 스코프에 바인딩되어 있으면 좁은 범위가 우선한다.

  EL 속성 검색 순서

  ${name} 접근 시

  page → request → session → application
  (좁음)                        (넓음)
  우선순위 높음 ──────► 우선순위 낮음
<%-- 특정 스코프를 지정하여 접근 --%>
<p>${requestScope.name}</p>
<p>${sessionScope.name}</p>

4. EL에서 객체 접근

빈 속성 접근

<%-- . 연산자 사용 --%>
${member.name}

<%-- [] 연산자 사용 --%>
${member["name"]}

Collection 접근

<%-- List 접근 (인덱스) --%>
${memberList[0].name}
${memberList[1].email}

<%-- Map 접근 (키) --%>
${memberMap.hong}
${memberMap["admin"]}

has-a 관계 접근

객체가 다른 객체를 속성으로 가지는 경우 .으로 체이닝한다.

<%-- 부서 정보를 가진 회원 --%>
${member.department.name}
${member.department.location}
  has-a 관계 접근

  member
  ┌──────────────────┐
  │ name: "홍길동"    │
  │ department ──────┼──┐
  └──────────────────┘  │
                        ▼
                  department
                  ┌────────────────┐
                  │ name: "개발팀"  │
                  │ location: "3층"│
                  └────────────────┘

  ${member.department.name} → "개발팀"

5. 커스텀 태그와 JSTL

액션 태그와 EL을 사용해도 조건문과 반복문에서는 여전히 자바 코드가 필요하다. 이를 해결하기 위해 **JSTL(JSP Standard Tag Library)**이 등장했다.

커스텀 태그의 종류

종류설명
JSTL표준화된 태그 라이브러리
프레임워크 태그스프링, 스트러츠 등에서 제공
개발자 태그필요에 따라 직접 작성

JSTL 라이브러리 종류

라이브러리접두어기능
코어c변수, 흐름 제어, 반복문, URL
국제화fmt지역, 메시지, 숫자·날짜 형식
XMLxXML 파싱, 흐름 제어
데이터베이스sqlSQL 실행
함수fn문자열 처리 함수

6. Core 태그 라이브러리

가장 많이 사용되는 JSTL 라이브러리로, 변수 선언·조건문·반복문을 태그로 처리한다.

taglib 선언 (필수)

<%@ taglib prefix="c"
    uri="http://java.sun.com/jsp/jstl/core" %>

Core 태그 목록

기능태그설명
변수<c:set>변수 설정
<c:remove>변수 제거
출력<c:out>값 출력 (XSS 방지)
조건<c:if>단일 조건문
<c:choose>다중 조건문 (switch)
반복<c:forEach>반복문
<c:forTokens>구분자 기반 반복
URL<c:import>외부 자원 포함
<c:redirect>리다이렉트
<c:url>URL 생성
예외<c:catch>예외 처리

<c:set> - 변수 설정

<%-- 변수 선언 --%>
<c:set var="name" value="홍길동" />
<c:set var="age" value="25" />

<%-- 스코프 지정 --%>
<c:set var="loginId" value="admin"
    scope="session" />

<%-- 빈 속성에 값 설정 --%>
<c:set target="${member}" property="name"
    value="홍길동" />

<c:if> - 조건문

<c:if test="${score ge 90}">
    <p>A등급입니다.</p>
</c:if>

<c:if test="${empty memberList}">
    <p>등록된 회원이 없습니다.</p>
</c:if>
<c:if>에는 else가 없다. else 처리가 필요하면 <c:choose>를 사용해야 한다.

<c:choose> - 다중 조건문

<c:choose>
    <c:when test="${score ge 90}">
        <p>A등급</p>
    </c:when>
    <c:when test="${score ge 80}">
        <p>B등급</p>
    </c:when>
    <c:when test="${score ge 70}">
        <p>C등급</p>
    </c:when>
    <c:otherwise>
        <p>F등급</p>
    </c:otherwise>
</c:choose>

<c:forEach> - 반복문

<%-- 기본 반복 (1~10) --%>
<c:forEach var="i" begin="1" end="10" step="1">
    <p>${i}</p>
</c:forEach>

<%-- 컬렉션 반복 --%>
<c:forEach var="member" items="${memberList}">
    <tr>
        <td>${member.id}</td>
        <td>${member.name}</td>
        <td>${member.email}</td>
    </tr>
</c:forEach>

<%-- varStatus로 상태 정보 사용 --%>
<c:forEach var="member" items="${memberList}"
    varStatus="status">
    <tr class="${status.index mod 2 == 0
        ? 'even' : 'odd'}">
        <td>${status.count}</td>
        <td>${member.name}</td>
    </tr>
</c:forEach>

forEach varStatus 속성

속성설명
index0부터 시작하는 인덱스
count1부터 시작하는 반복 횟수
first첫 번째 반복이면 true
last마지막 반복이면 true
current현재 항목

<c:forTokens> - 구분자 기반 반복

<c:forTokens var="color"
    items="빨강,주황,노랑,초록,파랑"
    delims=",">
    <p>${color}</p>
</c:forTokens>

<c:out> - 안전한 출력

<%-- HTML 태그를 이스케이프하여 출력 --%>
<c:out value="${userInput}"
    escapeXml="true" />

<%-- 값이 null일 때 기본값 출력 --%>
<c:out value="${name}" default="이름 없음" />
사용자 입력값을 출력할 때는 반드시 <c:out>을 사용해야 한다. ${}만 사용하면 XSS(Cross-Site Scripting) 공격에 취약하다. <c:out>은 HTML 특수문자를 자동으로 이스케이프한다.

자바 코드 vs JSTL 비교

<%-- 자바 코드 방식 --%>
<%
    List<MemberVO> list = (List<MemberVO>)
        request.getAttribute("memberList");
    if (list != null) {
        for (MemberVO m : list) {
%>
    <tr>
        <td><%= m.getName() %></td>
    </tr>
<%
        }
    }
%>

<%-- EL + JSTL 방식 --%>
<c:forEach var="m" items="${memberList}">
    <tr>
        <td>${m.name}</td>
    </tr>
</c:forEach>

7. 포매팅 태그 라이브러리

숫자와 날짜를 원하는 형식으로 출력하는 태그 라이브러리다.

taglib 선언

<%@ taglib prefix="fmt"
    uri="http://java.sun.com/jsp/jstl/fmt" %>

<fmt:formatNumber> - 숫자 포매팅

<%-- 천 단위 구분 --%>
<fmt:formatNumber value="1234567"
    groupingUsed="true" />
<%-- 출력: 1,234,567 --%>

<%-- 통화 형식 --%>
<fmt:formatNumber value="50000"
    type="currency" currencyCode="KRW" />
<%-- 출력: ₩50,000 --%>

<%-- 퍼센트 --%>
<fmt:formatNumber value="0.85"
    type="percent" />
<%-- 출력: 85% --%>

<%-- 패턴 지정 --%>
<fmt:formatNumber value="3.14159"
    pattern="#.##" />
<%-- 출력: 3.14 --%>

<fmt:formatDate> - 날짜 포매팅

<%-- 날짜만 --%>
<fmt:formatDate value="${now}"
    type="date" dateStyle="long" />
<%-- 출력: 2026년 3월 2일 --%>

<%-- 시간만 --%>
<fmt:formatDate value="${now}"
    type="time" />

<%-- 날짜 + 시간 --%>
<fmt:formatDate value="${now}"
    type="both" />

<%-- 커스텀 패턴 --%>
<fmt:formatDate value="${now}"
    pattern="yyyy-MM-dd HH:mm:ss" />
<%-- 출력: 2026-03-02 14:30:00 --%>

formatNumber 주요 속성

속성설명
value포매팅할 숫자
typenumber, currency, percent
pattern출력 패턴 (DecimalFormat)
groupingUsed천 단위 구분 여부 (기본: true)
currencyCode통화 코드 (KRW, USD 등)

formatDate 주요 속성

속성설명
value포매팅할 날짜
typedate, time, both
dateStylefull, long, medium, short
pattern출력 패턴 (SimpleDateFormat)
timeZone시간대 지정

8. 다국어 태그 라이브러리

다국어 기능을 구현할 때 사용하는 태그 라이브러리다.

주요 태그

태그설명
<fmt:setLocale>언어(Locale) 지정
<fmt:setBundle>리소스 번들 지정
<fmt:message>번들에서 메시지 조회
<fmt:requestEncoding>요청 인코딩 지정

사용 예시

<%-- 한국어 로케일 설정 --%>
<fmt:setLocale value="ko_KR" />
<fmt:setBundle basename="messages"
    var="msg" />

<%-- 메시지 출력 --%>
<fmt:message key="greeting" bundle="${msg}" />

리소스 번들 파일(messages_ko.properties)에 한글을 저장할 때는 아스키 코드로 변환해야 한다. JDK의 native2ascii 도구를 사용한다.


9. 문자열 처리 함수

JSTL은 문자열을 처리하는 함수 라이브러리도 제공한다.

taglib 선언

<%@ taglib prefix="fn"
    uri="http://java.sun.com/jsp/jstl/functions" %>

주요 함수

함수반환설명
fn:contains(A, B)booleanA에 B가 포함되어 있는지
fn:endsWith(A, B)booleanA가 B로 끝나는지
fn:indexOf(A, B)intA에서 B의 첫 위치
fn:length(A)intA의 길이
fn:replace(A, B, C)StringA에서 B를 C로 변환
fn:toLowerCase(A)String소문자로 변환
fn:toUpperCase(A)String대문자로 변환
fn:substring(A, B, C)StringA의 B~C 인덱스 부분 문자열
fn:split(A, B)String[]A를 B 기준으로 분리
fn:trim(A)String앞뒤 공백 제거

사용 예시

<c:set var="text" value="Hello JSTL World" />

<p>길이: ${fn:length(text)}</p>
<p>소문자: ${fn:toLowerCase(text)}</p>
<p>포함: ${fn:contains(text, "JSTL")}</p>
<p>부분: ${fn:substring(text, 6, 10)}</p>
<p>변환: ${fn:replace(text, "World", "JSP")}</p>

요약

표현식 → EL 전환

기존 (표현식)EL
<%= request.getAttribute("name") %>${name}
<%= request.getParameter("id") %>${param.id}
<%= session.getAttribute("user") %>${sessionScope.user}
<%= member.getName() %>${member.name}

자바 코드 → JSTL 전환

기존 (자바 코드)JSTL
if (조건) { }<c:if test="${조건}">
if-else<c:choose> + <c:when>
for (변수 : 컬렉션)<c:forEach>
out.print(값)<c:out value="${값}">
변수 = 값<c:set var="" value="">

JSTL 라이브러리 정리

라이브러리URI 접미어접두어주요 기능
Core/corec변수, 조건, 반복
포매팅/fmtfmt숫자·날짜 형식
함수/functionsfn문자열 처리