Programming/JSP

[JSP] EL (Expression Language), JSTL (JSP Standard Tag Library)

Isaac S. Lee 2023. 10. 19. 12:46

🍁EL (Expression Language)


EL과 JSTL을 사용하면 구문이 단순해지고, 가독성이 향상된다.

이들은 서로 다른 기술이지만, 섞어서 사용한다.

슬로건은 "JSP에서 되도록 자바를 쓰지 말자!"이다.

 

EL 표현식의 사용

${}

Expression(<%= %>)과 EL 언어는 완전히 다른 것이지만, 이름이 같다는 점에서 목적이 같다는 것을 알 수 있다.

EL 언어는 <%= %> 기능을 대신하기 위해서 만들어진 언어이다.

 

<%@ 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>
<link rel="stylesheet" href="http://pinnpublic.dothome.co.kr/cdn/example-min.css">
<style>
	
</style>
</head>
<body>
	<h1>EL</h1>
	
	<%
		int a = 10;
		pageContext.setAttribute("b", 20);
		request.setAttribute("c", 30);
	%>
	
	<h2>표현식</h2>
	<div>a: <%= a %></div>
	<div>b: <%= pageContext.getAttribute("b") %></div>
	<div>c: <%= request.getAttribute("c") %></div>
	
	<h2>EL</h2>
	<div>a: ${a}</div>
	<div>b: ${b}</div>
	<div>c: ${c}</div>
	
	<%-- <div>b: ${pageContext.getAttribute("b")}</div> --%>
	<%-- <div>c: ${request.getAttribute("c")}</div> --%>

	<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
	<script src="http://pinnpublic.dothome.co.kr/cdn/example-min.js"></script>
	<script>
	
	</script>
</body>
</html>

기존에 사용하던 표현식을 EL을 사용하여 바꿀 수 있다.

그런데 어떤 건 출력이 되고 어떤 건 출력이 안 된다. 왜 그런 걸까?

 

EL의 목적

EL은 내장 객체(pageContext, request, session, applicaion) 안에 있는 데이터만 전용으로 출력하는 구문이다.

EL은 일반 자원(지역 변수, 멤버 변수)은 출력할 수 없다. a는 일반 자원이기 때문에 출력이 안 되고, 내장 객체인 b와 c만 출력되는 것이다.

일반 자원을 화면에 출력할 일이 별로 없기 때문에 실제로는 큰 문제가 되지 않는다.

 

EL 연산 기능

<h3>EL 연산 기능</h3>
<div>b + 10 = ?</div>
<div>b + 10 = <%= (int)pageContext.getAttribute("b") + 10 %></div>
<div>b + 10 = ${b} + 10</div>
<div>b + 10 = ${b + 10}</div>
<div>b + 10 = ${b + 10}</div>
<div>b - 10 = ${b - 10}</div>
<div>b * 10 = ${b * 10}</div>
<div>b / 10 = ${b / 10}</div>
<div>b % 10 = ${b % 10}</div>
<div>b mod 10 = ${b mod 10}</div>

<hr>

<div>b &gt; 10 = ${b > 10}</div>
<div>b &gt; 10 = ${b gt 10}</div>
<div>b &lt; 10 = ${b < 10}</div>
<div>b &lt; 10 = ${b lt 10}</div>
<div>b &gt;= 10 = ${b >= 10}</div>
<div>b &gt;= 10 = ${b ge 10}</div>
<div>b == 20 = ${b == 20}</div>
<div>b == 20 = ${b eq 20}</div>
<div>b != 20 = ${b != 20}</div>
<div>b != 20 = ${b ne 20}</div>

 

논리 연산자, 삼항 연산자, equal

<div>${true && true}</div>
<div>${false || true}</div>
<div>${!true}</div>

<div>${true and true}</div>
<div>${false or true}</div>
<div>${not true}</div>

<hr>

<div>${b > 0 ? "양수" : "음수"}</div>

<hr>

<div>${"문자열".equals("문자열")}</div>
<div>${"문자열" == "문자열"}</div>
<div>${"문자열" == '문자열'}</div>

연산식이 만들어지는 이유는 '+'가 더하기 연산자가 아니라 HTML의 PCDATA이기 때문이다.

모든 걸 중괄호 안에 넣어야 연산자로 인식한다.

'%' 연산자는 'mod'라는 키워드로도 연산이 가능하다. 가끔 특수문자 연산자를 사용할 수 없는 경우 영어로 치환해서 사용해야 하므로 키워드로 연산하는 방법을 알아두는 것이 좋다.

 

쇼트 서킷 (Short-circuit)

  • false && true
  • true || true

Dead code가 발생하는 이유는 쇼트 서킷 현상 때문이다.

true && true는 왼쪽이 true면 and연산자는 오른쪽이 true일 때 false가 되는데, false && true일 때 무조건 false이기 때문이다. 그렇기 때문에 프로그램의 속도를 더 빠르게 하기 위해서 쇼트 서킷 현상이 발생하는 것이다.

true || true가 Dead code인 이유도 왼쪽이 true면 오른쪽의 결과에 상관없이 무조건 true이기 때문이다.

 

객체 출력

HashMap

<%
    HashMap<String,Integer> score = new HashMap<String,Integer>();
    score.put("kor", 100);
    score.put("eng", 90);
    score.put("math", 80);

    pageContext.setAttribute("score", score);
%>

<h3>객체 출력 (HashMap)</h3>
<div>국어: <%= score.get("kor") %></div>
<div>영어: <%= score.get("eng") %></div>
<div>수학: <%= score.get("math") %></div>

<div>국어: ${score.get("kor")}</div> <!-- 사용X -->
<div>영어: ${score["eng"]}</div>
<div>수학: ${score.math}</div>

EL 언어는 멤버 접근을 할 때, 배열처럼 인덱서를 지원하기 때문에 get 메소드를 사용하는 것보다 가독성이 높고 훨씬 편리하다.

이때 멤버 접근 연산자 '.'를 사용할 수 있다. 멤버 접근 연산자에 한글을 사용하면 깨지기 때문에 오류가 발생한다.

 

일반 객체

package com.test.jsp;

public class Score {
	private int kor;
	private int eng;
	private int math;
	
	public int getKor() {
		return kor;
	}
	public void setKor(int kor) {
		this.kor = kor;
	}
	public int getEng() {
		return eng;
	}
	public void setEng(int eng) {
		this.eng = eng;
	}
	public int getMath() {
		return math;
	}
	public void setMath(int math) {
		this.math = math;
	}
}
<%
    Score score = new Score();

    score.setKor(95);
    score.setEng(80);
    score.setMath(90);

    pageContext.setAttribute("score", score);
%>

<h3>객체 출력 (일반 객체)</h3>
<div>국어: <% %></div>
<div>국어: ${score.getKor()}</div>
<div>영어: ${score.["eng"]}</div>
<div>수학: ${score.math}</div>
<div>총점: ${score.kor + score.eng + score.math}</div>

private는 외부에서 접근할 수 없다. 따라서 score.["eng"]와 score.math에서 접근하는 eng와 math는 public인 getter 메서드 이름이다.

  • kor > get + kor > getKor > getKor()

kor을 만나면 get(get + kor)을 붙이고, 이를 캐멀표기법(getKor)으로 바꾸고, 메서드(getKor())로 만드는 과정이 실행된다.

 

EL의 내장 객체 우선순위

<%
    pageContext.setAttribute("color", "tomato");
    request.setAttribute("color", "gold");
    session.setAttribute("color", "cornflowerblue");
    application.setAttribute("color", "yellowgreen");
%>

<div style="background-color:${color};">${color}</div>
<div style="background-color:${requestScope.color};">${color}</div>
<div style="background-color:${sessionScope.color};">${color}</div>
<div style="background-color:${applicationScope.color};">${color}</div>

  • 부모와 자식의 충돌: 자식
  • 넓은 범위와 좁은 범위의 충돌: 좁은 범위

범위가 가장 좁은 게 가장 특성이 강하다는 의미이므로, 좁은 범위일수록, 구체적일수록 충돌이 날 경우에 이기게 된다.

현재 color가 충돌이 난 상태인데, 내장 객체에서 범위가 좁다는 건 생명 주기가 짧다는 것을 의미한다. 그래서 생명 주기가 가장 짧은 pageContext가 이기게 된다.

 

  • pageContext > request > session > application

EL은 생명 주기 순서대로 순차적인 탐색을 하지만, 내장 객체명을 Scope와 함께 적으면 해당 내장 객체에 대한 데이터를 가져올 수 있다.

 

🍁JSTL (JSP Standard Tag Library)


JSTL은 이름은 표준 태그 라이브러리지만, 실제로는 확장 태그(액션 태그)이다.

자바의 프로그래밍 기능을 태그로 구현(표현)한다.

 

jar 파일 다운로드

https://tomcat.apache.org/taglibs/standard/

 

Apache Taglibs - Apache Standard Taglib: JSP[tm] Standard Tag Library (JSTL) implementations

<!-- Copyright 1999-2011 The Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/

tomcat.apache.org

아파치 톰캣 공식 홈페이지에서 다운로드 받을 수도 있지만, 다른 곳에서 다운로드 받아 보도록 하자.

mvnrepository는 각종 jar 파일을 다운로드 받기 좋은 곳이다.

 

https://mvnrepository.com/artifact/javax.servlet/jstl/1.2

Files에서 jar(404KB)를 다운로드 받고, 이를 lib 폴더에 넣는다.

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

JSP 지시자를 작성한다. 이때 "c"는 core의 약자를 의미한다.

 

JSTL의 사용

JSTL 태그들은 "c"라는 접두어를 붙이도록 되어있다. 마크업 언어에서는 이를 네임스페이스라고 부른다.

JSTL에는 set이라는 단독 태그가 존재한다. 시작 태그만 작성할 수 있는 단독 태그는 HTML 뿐이므로, 다른 언어는 끝 태그나 '/'를 반드시 사용해야 한다.

 

변수 선언

<c:set var="c" value="30" /> <!-- JSTML 변수 -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="http://pinnpublic.dothome.co.kr/cdn/example-min.css">
</head>
<body>
	<h1>JSTL</h1>
	
	<h2>변수 선언</h2>
	<%
		int a = 10; //지역 변수
		
		pageContext.setAttribute("b", 20); //내장 객체 변수
	%>

	<c:set var="c" value="30" /> <!-- JSTML 변수 -->
	
	<div>a: <%= a %></div>
	<%-- <div>a: ${a}</div> --%>
	
	<div>b: <%= pageContext.getAttribute("b") %></div>
	<div>b: ${b}</div>
	
	<%-- <div>c: <%= c %></div> --%>
	<div>c: ${c}</div>
	<div>c: ${pageContext.getAttribute("c")}</div>
	
	<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
	<script src="http://pinnpublic.dothome.co.kr/cdn/example-min.js"></script>
</body>
</html>

JSTL 변수는 내장 객체 변수이다. 따라서 pageContext.setAttribute("c", 30);<c:set var="c" value="30" />로 표현한 것이라고 볼 수 있다. 차이는 전자는 자바로 구현한 것이라면, 후자는 태그로 구현한 것이다. 나중에 Java를 사용하지 않고 EL과 JSTL을 사용하기 때문에 바꿔 나가야 한다.

 

변수 수정하기 (값 바꾸기)

<c:set var="c" value="300" />

Map 형태의 컬렉션의 특징은 같은 키 값으로 데이터를 넣으면 선언이 아니라 수정이 된다는 점이다.

JSTL은 선언과 수정의 구문이 같다.

그래서 동일한 c값을 찾아서 300으로 고치라는 의미이다.

 

 

[Java] 컬렉션 (Collection)

💡JCF, Java Collection Framework 컬렉션은 자료구조이자 무언가를 모아놓은 집합(배열)이며, 기존의 배열에서 향상된 배열을 의미한다. 기존의 순수 배열에서 사용법을 개량하거나 용도에 따른 입출

isaac-christian.tistory.com

컬렉션에 대해서는 위 글을 참고한다.

 

변수 삭제하기

<c:remove var="c" />

변수는 생명주기가 다해서 소멸하는 것 외에는 삭제할 수 없는데, 변수가 아니라 정확히는 Map의 요소이기 때문에 삭제가 가능하다.

<div>c: ${empty c}</div>

empty라는 연산자를 사용하여 c가 있을 경우 true, false를 반환하도록 할 수 있는데, remove를 한 상태이므로 true를 반환한다.

 

조건문

<c:set var="num" value="10" />
<div>num: ${num}</div>

<c:if test="조건">
	참 실행 블럭
</c:if>
<c:set var="num" value="10" />
<div>num: ${num}</div>

<c:if test="${num > 0}">
	<div>${num}은 양수입니다.</div>
</c:if>

조건문의 여는 괄호와 닫는 괄호가 없기 때문에 JSTL은 시작 태그와 끝 태그로 대신한다.

 

choose when문

<c:choose>
	<c:when test="${num > 0}">양수</c:when>
	<c:when test="${num < 0}">음수</c:when>
	<c:otherwise>제로</c:otherwise>
</c:choose>

태그로는 else 절을 지원하지 않으므로 음수일 경우 조건을 반대로 하여 if문을 새로 만들어야 한다.

이를 보완하기 위해 Oracle의 Case End문과 비슷한 기능을 지원한다.

 

반복문

<% for (int i=0; i<10; i++) { %>
<div>숫자: <%= i %> </div>
<% } %>

<c:forEach var="i" begin="0" end="9" step="1">
	<div>숫자: ${i}</div>
</c:forEach>

forEach 내장 객체 변수로 루프 변수 i를 만들 수 있다.

step에는 1이상의 정수만 들어갈 수 있으므로 10부터 0으로 내림차순으로 내려가는 게 불가능하다.

step 증감식에 음수를 줄 수 없더라도 내부에서 조작하면 되므로 큰 불편함은 없다.

 

향상된 for문

<c:set var="n" value="0" /> <!-- index 대체 -->

<table>
    <tr>
        <th>이름</th>
        <th>index</th>
        <th>n</th>
        <th>count</th>
        <th>current</th>
        <th>first</th>
        <th>last</th>
    </tr>
    <!-- for (String name: names) -->
    <c:forEach var="name" items="${names}" varStatus="status">
    <tr>
        <td>${name}</td>
        <td>${status.index}</td>
        <td>${n}</td>
        <td>${status.count}</td>
        <td>${status.current}</td>
        <td>${status.first}</td>
        <td>${status.last}</td>
    </tr>
    <c:set var="n" value="${n + 1}" />
    </c:forEach>
</table>

varStatus로 여러가지 정보를 얻어오는 게 가능하다. 처음에는 덤프된 상태이므로 멤버 접근 연산자를 사용하여 정보를 얻어온다.

current는 현재 가져온 item 값을 의미한다. first와 last는 첫 번째 요소와 마지막 요소에 대해 각각 true를 반환한다. 이 루프가 첫 번째인지, 마지막 번째인지를 구별할 때 이를 사용할 수있다.