🍁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 > 10 = ${b > 10}</div>
<div>b > 10 = ${b gt 10}</div>
<div>b < 10 = ${b < 10}</div>
<div>b < 10 = ${b lt 10}</div>
<div>b >= 10 = ${b >= 10}</div>
<div>b >= 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/
아파치 톰캣 공식 홈페이지에서 다운로드 받을 수도 있지만, 다른 곳에서 다운로드 받아 보도록 하자.
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으로 고치라는 의미이다.
컬렉션에 대해서는 위 글을 참고한다.
변수 삭제하기
<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를 반환한다. 이 루프가 첫 번째인지, 마지막 번째인지를 구별할 때 이를 사용할 수있다.