JSP/Servlet - EL, JSTL!
Scope (저장공간)
EL을 알아보기 전에 Scope에 대해 먼저 알아야 한다.
WEB은 클라이언트가 요청을 하고 서버가 응답으로 이루어진다.
서버가 요청을 MVC패턴으로 처리하다 보면 서버의 각 서블릿 객체에서 데이터를 전달하고 View역할을 하는 jsp파일에서 데이터를 출력한다.
즉 MVC패턴으로 처리하면 서버의 여러 서블릿 객체에서 클라이언트의 요청과 응답할 데이터를 이리저리 이동시킨 다는 뜻이다.
이 데이터를 이동시키는 특별한 저장공간이 있는데 이를 Scope라 한다.
아래 예제를 통해 알아보자.
list.jsp
에선 ${param.delete}
형식으로 delete
값을 가져오고
delete.jsp
에선 ${delete}
형식으로 delete
값을 가져온다.
/* list.jsp */
var delete_st = "${param.delete}";
if(delete_st == "success")
{
alert("Delete " + delete_st);
}
else if (delete_st == "fail") {
alert("Delete " + delete_st);
}
...
/* delete.jsp */
var delete_st = "${delete}"
if(delete_st == "success")
{
alert("Delete " + delete_st);
}
else if (delete_st == "fail") {
alert("Delete " + delete_st);
}
${param.delete}
을 jsp로 변환하면 <%= request.getParamter("delete") %>
${delete}
을 jsp로 변환하면 <%= request.getAttribute("delete") %>
이다.
왜 이런 차이가 있는지 알려면 JSP 영역을 이해해야 한다.
JSP영역
사실 데이터를 넘기는 방법이 여러가지 있으며 이 방법에 따라 사용하는 공간이 달라지는데 공간이 4가지가 된다.
-
pageScope
→ 페이지Scope에 접근 -
request Scope
→ 리퀘스트Scope에 접근 -
sessionScope
→ 세션Scope에 접근 -
applicationScope
→ 어플리케이션Scope에 접근
추가로 url에 붙여 파라미터 형식으로 보내는 방법이 있다.(이녀석은 저장 공간이라고 표현하기에는 부족하다….)
이 4가지 공간은 각각의 영역을 갖고 있는데 다음과 같다.
영역 | 설명 | 사용객체 |
---|---|---|
page 영역 |
하나의 JSP페이지와 관련된 영역 | pageContext 객체 |
request 영역 |
하나의 요청과 관련된 영역 | request 객체 |
session 영역 |
하나의 웹 브라우저와 관련된 영역 | response 객체 |
application 영역 |
하나의 웹 애플리케이션과 관련된 영역 | application 객체 |
각 영역은 각각의 기본객체들로 접근할 수 있다.
page
영역이 제일 작고 application
영역이 가장 크다.
EL을 사용하면 이 각 영역을 간편하게 접근하여 값을 가져올 수 있다.
<%
String name = "admin";
pageContext.setAttribute("pc_name", name);
request.setAttribute("age", 20);
session.setAttribute("addr", "강남구");
application.setAttribute("tel", "010-1111-2222");
%>
이름: <%= name %> / ${ pc_name } <br>
나이: ${ age } <br>
주소: ${ addr } <br>
번호: ${ tel } <br>
출력값
웹 애플리케이션 영역 이름: admin / admin
나이: 20
주소: 강남구
번호: 010-1111-2222
스크립코드안
String name = "admin"
은 어느 영역에도 속해있지 않다.
EL태그는 가장 작은 범위 영역에서 큰 범위 영역으로 찾아나가기 때문에 name
으로 값을 찾을 수 있다.
다른영역끼리 속성 name
이 같을경우도 있는데 그럴경우 앞에 scope
명을 붙이면 된다.
주소: ${ sessionScope.addr }
이제 위의 ${param.delete}
과 ${delete}
를 보면 이해가 갈듯 하다.
EL에서 ${delete}
라는 변수를 page
→ request
→ session
→ application
영역순으로 EL변수를 찾는다.
반면 ${param.delete}
은 url뒤에 붙어오는 파라미터를 찾아오기 때문에 위의 4개 영역과는 관련이 없다.
반대로 위의 4개 영역 역시 url뒤에 파라미터와 관련없다.
request.getAttribute()
와request.getParameter()
메서드 기능이 다른것과 같다.
또한 request.setAttribute
로 객체를 저장하여 전송한 경우 ${dto.name}
이런식으로 사용했는데 만약 dto
클래스에 getName
메서드가 정의되어 있지 않다면 오류난다.
이는 EL의 문법중 하나로 ${dto.name}
은 사실 ${dto.getName()}
을 호출하는 것과 같다.(물론 get
메서드 형식은 통용되는 것을 사용해야 오류 안남)
EL(Expression Language)
jsp에서 자바코딩을 좀더 간결하게 하기 위해 사용되는 언어,
다음과 같은 기능을 제공한다.
-
JSP 네가지 기본 객체(
request
,response
)가 제공하는 영역의 속성 사용 -
수치연산, 관계연산, 논리연산자 제공
-
자바클래스, 메서드 호출기능 제공
-
쿠키, 기본객체의 속성 등 JSP를 위한 표현언어의 기본객체 제공
-
람다식을 이용한 함수 정의와 실행
EL은 ${ expr }
형식으로 사용한다.
$
달러 기호와 {}
대괄호가 EL
스크립트임을 표시한다.
나중에 코어 태그와 EL을 사용해 제어문 처리를 하는데 조건문을 사용할 때 변수 타입을 알아야한다.
EL에선 다음 5가지의 데이터 타입이 존재한다.
Boolean, 정수, 실수, 문자열, Null타입
EL을 java가 아닌 이상한 언어라 생각할 수 있는데
EL역시 간단히 사용하기 위한 도구일 뿐이지 사실 Java문법중 하나이다.
Boolean
의 경우 true
, false
실수의 경우 java.lang.Double
정수의 경우 java.lang.Long
문자열의 경우 java.lang.String
타입을 가진다.
EL 기본객체
JSP에서 바로 접근가능한 기본객체들이 있었다.
request
, response
, session
, application
등…
재스퍼가 jsp를 servlet
객체로 변환시킬 때 이런 기본객체들을 자동 생성하기에 사용할 수 있었던 것이었는데…
EL에서도 바로 접근할 수 있는 객체들이 있다.
EL 기본객체 | 설 명 |
---|---|
pageContext |
JSP page 자체를 나타낸다. pageContext 를 통해 각종 기본객체를 가져올 수 있다. |
pageScope |
pageContext 기본 객체에 저장된 속성의 <속성, 값> 매핑을 저장한 Map 객체 |
requestScope |
request 기본 객체에 저장된 속성의 <속성, 값> 매핑을 저장한 Map 객체 |
sessionScope |
session 기본 객체에 저장된 속성의 <속성, 값> 매핑을 저장한 Map 객체 |
applicationScope |
application 기본 객체에 저장된 속성의 <속성, 값> 매핑을 저장한 Map 객체 |
param |
요청 파라미터의 <파라미터이름, 값> 매핑을 저장한 Map 객체. 파라미터 값의 타입은 String 으로서, request.getParameter(이름) 의 결과와 동일하다. |
paramValues |
요청 파라미터의 <파라미터이름, 값배열> 매핑을 저장한 Map 객체. 값의 타입은 String[] 으로서, request.getParameterValues(이름) 의 결과와 동일하다. |
header |
요청 정보의 <헤더이름, 값> 매핑을 저장한 Map 객체, request.getHeader(이름) 의 결과와 동일하다. |
headerValues |
요청 정보의 <헤더이름, 값 배열> 매핑을 저장한 Map 객체. request.getHeaders(이름) 의 결과와 동일하다. |
cookie |
<쿠키 이름, Cookie> 지정한 Map 객체. request.getCookies() 로 구한 Cookie배열로부터 매핑을 생성한다. |
initParam |
초기화 파라미터의 <이름, 값> 매핑을 저장한 Map 객체, application.getInitParameter(이름) 의 결과와 동일하다. |
이중 위에서 소개했던 4가지 역역 pageScope
, requestScope
, sessionScope
, applicationScope
은 굳이 명시하지 않아도 가장 작은 범위인 pageScope
부터 돌며 안의 속성을 접근할 수 있다.
모든 기본객체의 형식이 Map
객체이며 key
값을 통해 value
값을 얻어올 수 있다.
${ pageScope.key }
이런식으로…. pageScope
는 생략가능함으로 ${ key }
키값을 통해 바로 value
를 가져올 수 있다.
pageContext
객체는 jsp에서 제공하는 기본객체로 다른 jsp와 1:1 매칭되는 객체로 다른 기본객체를 함수를 통해 반환할 수 있다.
EL
에서 접근할 수 있는 JSP의 기본객체는 pageContext
뿐이다.
만약 request
와 session
과 같은 기본객체에 접근하고 싶다면 pageContext
를 통해 얻어올 수 있다.
request
를 통해 url
을 가져오는 request.getRequestURI()
메서드를 EL을 통해 사용하고 싶다면
url: ${ pageContext.request.requestURI }
이런식으로 사용가능하다.
EL
문법에 따라 requestURI
이 getRequestURI()
메서드를 호출하는 것 과 같다.
application
이나 session
같은 기본객체를 얻어와 메서드 호출도 가능하다.
pageContext
: https://kouzie.github.io/jsp/JSP-버퍼,-application,-exception,-웹-기본구조,-모듈화/#기본객체—pagecontext
또한 request.getHeader()
메서드를 통해 header
정보를 읽어왔었는데 EL 태그는 header
라는 EL기본객체로 제공되기 때문에 바로 접근 가능하다.
name: ${ param.name }<br>
url: ${ pageContext.request.requestURI }<br>
referor: ${ header.referer }
위와 같은 EL태그는 재스퍼에 의해 java코딩으로 다음과 같이 변경된다.
out.write("\tname: ");
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${ param.name }", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null));
out.write("<br>\r\n");
out.write("\turl: ");
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${ pageContext.request.requestURI }", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null));
out.write("<br>\r\n");
out.write("\treferor: ");
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${ header.referer }", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null));
out.write("\r\n");
EL 산술 연산자
${ 3 + 5 }
${ 3 * 5 }
${ 3 / 5 }
${ 3 div 5 }
${ 3 % 5 }
${ 3 mod 5 }
${ 10/8 }
${ null + null }
${ null + 1 }
출력값
8
15
0.6
0.6
3
3
1.25
0
1
정수 실수 상관없이 사칙연산을 모두 수행한다.
결과값도 마찬가지.
참고로 ${ 0 / 10 }
0을 나누는 연산 또한 예외처리 되었는지 오류를 내보내지 않고 0.0을 출력한다.
EL empty 연산자
${ empty null } <
${ empty "" }
${ empty 0 }
출력값
true
true
false
아래와 같이 컬렉션 객체에 empty 연산자를 사용하는 경우
${ empty list }
list
가 비어있어도 true
반환한다.
EL 비교연산자
일반적인 비교연산자 >
, <
, ==
, !=
등을 사용할 수 도 있고 EL의 비교연산자 표현식을 사용하여 비교연산이 가능하다.
표현식 | 비교 |
---|---|
a gt b |
a > b |
a lt b |
a < b |
a ge b |
a >= b |
a le b |
a <= b |
a eq b |
a == b |
a ne b |
a != b |
EL 세미콜론 연산자와 할당연산자
세미콜론 연산자는 EL을 사용하여 출력할 때 ;
을 사용해 여러값을 {}
대괄호 안에 넣어 마지막 값만 출력한다.
${ 10; 20; 30; 40 }
출력값: 40
마지막값만 출려하기 위한 연산자로 쓸모 없어 보이지만 할당연산자와 사용하면 유용하다.
EL에서 연산 혹은 출력을 위한 변수를 만들고 싶다면 할당연산자를 사용하지 않고는 다음과 같이 request
객체에 담아서 사용해야한다.
<%
int age = 20;
request.setAttribute("age", age);
%>
${ age }
이런식으로 사용했는데 할당 연산자를 사용하면 request
에 담을 필요 없이 바로 선언, 초기화 가능하다.
${ age = 20 }<br>
${ age }<br>
단 이렇게 사용하면 20이 2번 출력된다….
이때 세미콜론 연산자와 같이 사용하면 간편하다.
${ age = 20; "" }
${ age }<br>
${ name = "hong"; "" }
${ name }<br>
${ m=[1, 2, 3]; "" }
${ m[0] }<br>
출력값
20
hong
1
초기화시 뒤에 세미콜론과 함께 빈 문자열을 붙여 출력시키지 않는다.
EL - 객체의 메서드 호출하기
EL2.2
버전부터 추가된 기능으로 생성된 객체의 메서드를 직접 호출 가능하다.
먼저 java Resources
폴더에 다음 클래스를 정의
package days13;
public class Fruit {
String fruit;
public void setFruit(String fruit) {
this.fruit = fruit;
}
public String getFruitName() {
return "과일 이름은 " + fruit + "입니다.";
}
}
위 클레스로 객체를 생성하고 EL을 사용해 getFruitName()
메서드를 호출하자.
<%
Fruit fruit = new Fruit();
fruit.setFruit("사과");
request.setAttribute("f", fruit);
%>
${ f.setFruit("배") }
${ f.getFruitName() }
출력값
과일 이름은 배입니다.
참고: jsp 2.1 이하버전에선 컴파일 에러난다.
EL - 정적 메서드 호출하기
정적메서드를 호출하는 방법은 2가지 있다.
jsp2.1
이전 버전 호출방법jsp2.2
이후 버전 호출방법
물론 jsp2.2
버전에서 jsp2.1
버전의 사용방법으로 정적메서르들 호출해도 된다.
jsp2.1
이전 버전으로는 TLD(Tag Library Description)을 사용해서 정적메서드와 매칭된 파일을 별도로 작성후 web.xml
에 등록해야한다.
먼저 간단한 정적메서드를 정의하자.
public class FormatUtil {
public static String number(long number, String pattern) {
DecimalFormat format = new DecimalFormat(pattern);
return format.format(number);
}
}
DecimalFormat
을 사용해 원하는 포멧으로 정수를 문자열로 출력하는 static
메서드이다.
number()
메서드를 태그처럼 사용할 수 있도록 TLD
파일을 작성하도록 하자.
확장자는 .tld
이고 파일명은 el-functions.tld
이다.
이 tld파일은 /WEB-INF/tlds/
, /WEB-INF/jsp/
폴더에 위치시켜야 한다.(안그러면 오류남)
<?xml version="1.0" encoding="UTF-8" ?>
<taglib
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<description>EL에서 함수실행</description>
<tlib-version>1.0</tlib-version>
<short-name>ELfunctions</short-name>
<function>
<description>숫자 포멧팅</description>
<name>formatNumber</name>
<function-class>com.util.FormatUtil</function-class>
<function-signature>
java.lang.String number(long, java.lang.String)
</function-signature>
</function>
</taglib>
xml형식으로 작성되며 <function>
태그를 통해 클래스와 메서드를 태그로 쓸 수 있도록 설정한다.
정적메서드를 사용하고 싶은 jsp페이지에 다음과 같은
tablib
디렉티브를 선언하면 된다.
<%@ taglib prefix="elfunc" uri="/WEB-INF/tlds/el-functions.tld" %>
그리고 지시자에서 정의한 접두사 elfunc
와 함께 사용하면 된다.
${ price = 12345; "" }
베추 가격은 ${ elfunc:formatNumber(price, "#,##0") } 입니다.
사실 jsp2.2
이후 버전에선 위처럼 복잡하게 정적메서드를 호출하지 않는다.
그냥 객체의 메서드 호출하듯이 사용하면 된다.
page 디렉티브로 FormatUtil
클래스를 import하고
<%@page import="com.util.FormatUtil"%>
객체 메서드 호출하듯이 사용하면 된다.
${ price = 12345; "" }
베추 가격은 ${ FormatUtil.number(price, "#,##0") } 입니다.
출력값
베추 가격은 12,345 입니다.
EL 비활성화
EL을 사용하고 싶지 않다면, 물론 그럴일은 없겠지만 사용하고 싶지않다면 기능을 끌 수 있다.
${ }
는 더이상 EL 문법이 아닌 문자열로 취급된다.
비활성화 하는 방법은 web.xml
의 <jsp-config>
태그안에서 설정하는 것 과
page
디렉티브에서 isELIgnored
설정을 true
로 세팅하는 방법이 있다.
web.xml에서 설정하는 것은 다음과 같다.
<!-- web.xml -->
<jsp-config>
...
...
<jsp-property-group>
<url-pattern>/TestELIgnore/*</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>
</jsp-config>
<jsp-confi>
<jsp-property-grou>
<url-pattern>
<el-ignored>
4개 태그를 통해 url-mapping에 해당하는 모든 파일에 EL문법을 무효화 한다.
2번째 방법인 page디렉티브를 사용하는 방법은 다음과 같다.
<%@ page isELIgnored="true" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
isELIgnored="true"
를 통해 해당 jsp 페이지에선 더이상 EL을 사용하지 못하도록 설정한다.
JSTL
스트립트릿, 표현부를 if
문, while
문등 조건문과 같이 사용하면 코드가 지저분하고 유지보수하기 힘들어 진다.
이를 해결하기 위해 JSTL
(표준 태그 라이브러리)을 사용한다.
직관적이고 간결한 코드를 제공한다.
EL과 JSTL을 같이 사용하면 jsp의 스크립트릿으로 코딩하는 것 보다 길이가 훨씬 짧아진다.
JSTL을 사용하기 위해 라이브러리를 받아야 하는데 아래 주소에서 다운 가능하다.
https://search.maven.org/artifact/jstl/jstl/1.2/jar
다운받아 jdbc드라이버와 같이 WEB-INF/lib/
위치에 저장하면 된다.
태그의 종류
라이브러리 | 하위 기능 | 접두어 | 관련 URI |
---|---|---|---|
코어 | 변수 지원 흐름 제어 URL 처리 |
c | http://java.sun.com/jsp/jstl/core |
XML | XML 코어 흐름제어 XML 변환 |
x | http://java.sun.com/jsp/jstl/xml |
국제화 | 지역 메세지 형식 숫자 및 날짜 형식 |
fmt | http://java.sun.com/jsp/jstl/fmt |
데이터베이스 | SQL | sql | http://java.sun.com/jsp/jstl/sql |
함수 | 콜렉션 처리 String 처리 |
fn | http://java.sun.com/jsp/jstl/functions |
JSTL에서 제공하는 태그의 종류는 코어, XML, 국제화, 데이터베이스, 함수 등의 태그가 있지만
변수지원, 흐름제어, URL처리 역할을 하는 코어태그에 대해 먼저 알아보자.
코어태그
코어태그 라이브러리를 사용하려면 html
태그 위에 다음과 같은 taglib
디렉티브(지시자)를 추가해야 한다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
prefix
는 접두사로 앞으로 코어태그를 사용하려면 태그시작이 prefix
로 시작해야 한다.
prefix="ctag"
라면<ctag:if>
형식으로 사용.
코어 태그에서 지원하는 기능별 사용하는 태그는 아래와 같다.
기능분류 | 태그 | 설명 |
---|---|---|
변수 지원 | set |
JSP에서 사용될 변수를 설정한다. |
- | remove |
설정한 변수를 제거한다. |
흐름 제어 | if |
조건에 따라 내부 코드를 수행한다. |
- | choose |
다중 조건을 처리할 때 사용한다. |
- | forEach |
컬렉션이나 Map 의 각 항목을 처리할 때 사용한다. |
- | forTokens |
구분자로 분리된 각각의 토큰을 처리할 때 사용한다. |
URL 처리 | import |
URL을 사용하여 다른 자원의 결과를 삽입한다. |
- | redirect |
지정한 경로로 리다이렉트 한다. |
- | url |
URL을 재작성 한다. |
기타 태그 | catch |
익셉션 처리에 사용된다. |
- | out |
JspWriter에 내용을 알맞게 처리한 후 출력한다. |
변수지원 태그 <c:set>
, <c:remove>
<c:set>
태그는 EL로 사용할 수 있는 변수를 만들기 위해 사용하는 태그이다.
EL로 사용할 수 있는 변수란 것은 page
, request
, session
, application
4개 scope에 저장되어 있는 변수를 말함
지금까지 EL로 사용할 수 있는 변수를 만들기 위해서 다음과 같이 코딩하였다.
<%
String name = "admin";
request.setAttribute("name", name);
%>
name: ${ name }
<c:set>
코어태그를 사용하면 아래와 같이 EL에서 사용할 수 있는 변수를 만들 수 있다.
<c:set var="name" value="홍길동"></c:set>
name: ${ name }
<c:set>
코어태그에 사용되는 속성은 3가지다.
var
, value
, scope
<c:set var="변수명" value="값" scope="page" ></c:set>
var
는 사용할 변수명, value
는 변수에 해당하는 값이 들어간다.
scope
속성은 위에서 말한 4가지 영역(page, request, session, application)을 지정할 수 있으며 기본값은 page이다.
<c:set>
태그도 일종의 자바 라이브러리일 뿐 사실상 pageContext.setAttribute("name", "홍길동")
을 실행하는 것 과 같다.
value
속성 값으로 <%= %>
, ${ }
을 사용가능하며, 정적텍스트 값이 올 수 있다.
즉 문자열만 지정할 수 있는 것 이 아니라 Object
를 변수값으로 지정 할 수 있다는 뜻.
참고: 변수값은 value속성을 사용해 초기화 할 수 있지만
<c:set>
태그 사이에 값을 지정해 초기화 할 수 있다.
<c:set var="name" value="홍길동"></c:set>
<c:set var="name">홍길동</c:set>
<c:set>
태그의 target
, property
속성
위에서 <c:set>
태그로 EL변수를 생성하는 방법을 알아보았는데
<c:set>
태그로 EL변수(자바빈, Map)의 프로퍼티 값을 지정할 때에도 사용한다.
https://kouzie.github.io/jsp/JSP-포워딩-액션태그,-자바빈,-쿠키/#jspusebean-액션태그-자바빈java-beans
MemberInfo
라는 자바빈 객체를 <c:set>
태그로 초기화 해보자.
jsp 스크립트릿으로 초기화 하는 방법…
<%
MemberInfo minfo = new MemberInfo();
minfo.setId("admin");
%>
minfo: <%= minfo.getId() %> <br>
<c:set>
태그로 초기화 하는 방법….
<%
MemberInfo minfo2 = new MemberInfo();
%>
<c:set target="<%= minfo2 %>" property="id" value="admin2"></c:set>
minfo2: <%= minfo2.getId() %>
출력값
minfo: admin
minfo2: admin2
<c:set>
태그를 사용해서 초기화하는 것 이기 때문에 scope에 minfo2
객체가 저장되진 않는다.
이번엔 Map
객체안의 속성을 추가, 초기화 해보자.
<%
Map<String, String> prop = new HashMap<>();
prop.put("id", "admin");
%>
<c:set target="<%= prop %>" property="name">관리자</c:set>
prop.id: <%= prop.get("id") %><br>
prop.name: <%= prop.get("name") %>
출력값
prop.id: admin
prop.name: 관리자
Map객체를 초기화 할 때 <c:set>
태그의 property
속성을 key
값을 가리킨다.
<c:remove>
태그
<c:set var="name" value="admin" scope="page"></c:set>
<c:set var="name" value="관리자" scope="request"></c:set>
${ name }
<c:remove var="name"/>
${ name }
아무것도 출력되지 않는다… 4영역에서 name을 모두 삭제하기 때문.
흐름제어 태그 <c:if>
, <c:choose>
, <c:when>
,<c:otherwise>
, <c:forEach>
if-else
, switch
, for
문 등 을 역할을 하는 코어태그로 다음과 같은 종류가 있다.
<c:if>
태그
자바의 if
비슷한 기능을 제공,
단 if-else
와 같은 기능을 할수 없다.(else
가 없다니….)
<c:if test="조건">
...(몸체)
</c:if>
조건으로 다음과 같은 값이 들어갈 수 있다.
true
: 몸체내용 수행
false
: 몸체내용 수행하지 않음
some String
: 몸체내용 수행하지 않음
${ expr }
: EL결과값이 true
일경우 몸체내용 수행
<%= expr %>
: 표현식 결과값이 true
인 경우 몸체내용 수행
아래의 jsp코드를 코어태그를 사용하여 간결하게 변경해보자.
<%
if(elist == null) {
...
...(java)
%>
...
...(html)
<%
}
%>
위의 jsp코드를 JSTL을 사용하면 아래와 같이 사용 가능.
<c:if test="${ elist == null} ">
...
...
</c:if>
스크립트릿의 표현식으로도 사용 가능하다.
<c:if test="<%= elist.isEmpty() %>">
<c:choose>
, <c:when>
,<c:otherwise>
태그
자바의 switch
와 비슷한 역할을 하는 코어태그,
3개 태그를 사용해 if-else
역할을 하기도 한다.
전체적인 구조는 아래와 같다.
<c:choose>
<c:when test="조건1">
...
...
</c:when>
<c:when test="조건2">
...
...
</c:when>
<c:when test="조건3">
...
...
</c:when>
<c:otherwise>
...(모든 조건에 부합하지 않을 때)
...
</c:otherwise>
</c:choose>
c:choose
가 swtich
역할
c:choose
이 case
역할
c:otherwise
가 default
역할이다.
사실 c:when
에 조건식이 들억가지 때문에 switch
문보다 if..else if..else
구문과 더 비슷하다.
간단한 예제
<%@ 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>c:choose예제</title>
</head>
<body>
<form action="">
국어점수: <input type="text" name="kor"/>
</form>
성적:
<c:choose>
<c:when test="${ param.kor ge 90 && param.kor le 100 }">
수
</c:when>
<c:when test="${ param.kor ge 80}">
우
</c:when>
<c:when test="${ param.kor ge 70}">
미
</c:when>
<c:when test="${ param.kor ge 60}">
양
</c:when>
<c:when test="${ param.kor lt 0 || param.kor gt 100}">
점수 잘못 입력
</c:when>
<c:when test="${ empty param.kor }">
미입력
</c:when>
<c:otherwise>
가
</c:otherwise>
</c:choose>
</body>
</html>
<c:forEach>
태그
자바의 for문기능도 있으면서
iterator
처리와 비슷한 기능을 하는 코어태그
<c:forEach var="i" begin="1" end="10" step="2">
번호: ${i} <br>
</c:forEach>
출력값
번호: 1
번호: 3
번호: 5
번호: 7
번호: 9
가장 기본적 기능을 하는 <c:forEach>
,
step
속성으로 증가분을 변경가능하다.(생략시 1씩 증가)
<c:forEach>
는 단순한 for문기능 뿐 아니라
Collection
또는 Map
에 저장되어있는 요소를 순차적으로 꺼내사용 가능하다.(while
문과 iterator
와같이)
item
에 collection
을, var
에 꺼내서 사용할 요소명을 지정할 수 있다.
<%
HashMap<String, String> map = new HashMap<>();
map.put("id", "admin");
map.put("name", "홍길동");
map.put("tel", "010-1111-2222");
map.put("addr", "서울");
request.setAttribute("map", map);
%>
<c:forEach items="${ map }" var="entry" >
<li>
${ entry.key } - ${ entry.value}
</li>
</c:forEach>
출력값
name - 홍길동
tel - 010-1111-2222
id - admin
addr - 서울
주의사항: EL로 map객체를 사용하고 싶다면 꼭 Scope에 속성으로 추가해주자.
의 모든 요소를 돌고싶지 않다면 begin
, end
, step
를 추가하자
<%
HashMap<String, String> map = new HashMap<>();
map.put("id", "admin");
map.put("name", "홍길동");
map.put("tel", "010-1111-2222");
map.put("addr", "서울");
request.setAttribute("map", map);
%>
<c:forEach items="${ map }" var="entry" begin="2" end="3">
<li>
${ entry.key } - ${ entry.value}
</li>
</c:forEach>
출력값
id - admin
addr - 서울
여기서 주의할 점은 2번째, 3번째로 put()
한 name
, tel
이 출력되지 않고 id
, addr
가 출력되었단 점이다.
Map
객체는 순서유지를 하지 않기 때문…
순서유지가 되지 않는
Collection
의 경우begin
,end
는 무의미하다…
꼭 컬렉션 객체가 아니더라도 forEach
태그를 사용할 수 있다.
<c:set var="m" value="<%= new int[]{1, 5, 3, 2, 4} %>"/>
<c:forEach items="${ m }" var="n" varStatus="status">
m[${ status.index }] = ${ n }<br>
</c:forEach>
출력값
m[0] = 1
m[1] = 5
m[2] = 3
m[3] = 2
m[4] = 4
참고사이트: https://www.tutorialspoint.com/jsp/jsp_standard_tag_library.htm
<c:forEach>
status 속성
${status.current}
현재 for문의 해당하는 번호
${status.index}
0부터의 순서
${status.count}
1부터의 순서, 개발자 편의를 위해서 나눠놓았나?
${status.first}
첫 번째인지 여부, 처음이라면 true반환
${status.last}
마지막인지 여부, 마지막이라면 true반환
${status.begin}
forEach의 begin속성값
${status.end}
forEach의 end속성값
${status.step}
forEach의 step속성값
<%@page import="employee.EmpDeptDTO"%>
<%@page import="java.util.ArrayList"%>
<%@page import="com.util.DBConn"%>
<%@page import="java.sql.ResultSet"%>
<%@page import="java.sql.PreparedStatement"%>
<%@page import="java.sql.Connection"%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
String sql_emp = "select * from emp ";
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs_emp = null;
try {
con = DBConn.getConnection();
pstmt = con.prepareStatement(sql_emp);
rs_emp = pstmt.executeQuery();
EmpDeptDTO dto = null;
ArrayList<EmpDeptDTO> list = new ArrayList<>();
while (rs_emp.next()) {
dto = new EmpDeptDTO();
dto.setComm(rs_emp.getInt("comm"));
dto.setDeptno(rs_emp.getInt("deptno"));
dto.setEmpno(rs_emp.getInt("empno"));
dto.setEname(rs_emp.getString("ename"));
dto.setHiredate(rs_emp.getDate("hiredate"));
dto.setJob(rs_emp.getString("job"));
dto.setMgr(rs_emp.getInt("mgr"));
dto.setSal(rs_emp.getInt("sal"));
list.add(dto);
}
request.setAttribute("list", list);
}
catch (Exception e) {
System.out.println("> TestEmpInfo.doGet() \n" + e.toString());
}
finally {
if (rs_emp != null) {
try {
rs_emp.close();
}
catch (Exception e) {
}
if (pstmt != null)
try {
pstmt.close();
}
catch (Exception e) {
}
if (con != null)
try {
DBConn.close();
}
catch (Exception e) {
}
}
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>emp사원 목록</title>
</head>
<body>
<table border="1" width="600px">
<tr>
<th>deptno</th>
<th>ename</th>
<th>job</th>
<th>hiredate</th>
</tr>
<c:if test="${ empty list }">
<tr>
<td colspan="5">사원이 존재하지 않습니다.</td>
</tr>
</c:if>
<c:if test="${ not empty list }">
<c:forEach items="${ list }" varStatus="status">
<c:choose>
<c:when test="${ status.first }"><tr style="background-color: blue"></c:when>
<c:when test="${ status.last }"><tr style="background-color: aqua"></c:when>
<c:when test="${ status.current.deptno eq 10 }"><tr style="background-color: red"></c:when>
<c:when test="${ status.current.deptno eq 30 }"><tr style="background-color: yellow"></c:when>
<c:otherwise><tr></c:otherwise>
</c:choose>
<td>${status.index }(${ status.count })</td>
<td>${status.current.deptno }</td>
<td>${status.current.ename }</td>
<td>${status.current.job }</td>
<td>${status.current.hiredate }</td>
</tr>
</c:forEach>
</c:if>
</table>
</body>
</html>
status.first
의 경우 background-color: blue
status.last
의 경우 background-color: aqua
그리고 각각 deptno가 10, 30인 사원들의 배경색을 칠하였다.
<c:forEach>
의 var
속성을 사용해서 컬렉션에서 요소를 꺼내올 수 있지만 status.current
을 사용해서 현재 <c:forEach>
가 가리키고 있는 객체를 가져올 수 있다.
url제어 태그 <c:url>
, <c:redirect>
url제어 태그를 사용해 URL생성하고 리다이렉트 할 수 있다.
코어태그를 사용해 아래 url로 리다이렉트 할 수 있도록 구현해보자.
https:/search.daum.net/search?w=blog&q=공원
먼저 뒤의 파라미터를 제거한 url을 아래와 같이 <c:url>
태그로 생성.
<c:url value="https://search.daum.net/search" var="searchUrl">
</c:url>
그리고 <c:redirect url="${ searchUrl }"/>
형식으로 리다이렉트 시킬 수 있다.
<c:param>
태그를 사용하면 url뒤에 파라미터 붙이는 기능을 수행 할 수 있다.
<c:redirect url="${ searchUrl }">
<c:param name="w" value="blog"></c:param>
<c:param name="q" value="공원"></c:param>
</c:redirect>
URL제어태그 거의 쓸일 없다….
기타 코어 태그
<c:forToken>
StringTokenizer
클래스와 비슷한 역할을 하는 태그
String name = "소지섭,이동석,김동현,차인표,장동건,강동원";
StringTokenizer st = new StringTokenizer(name, ",");
while(st.hasMoreTokens())
{
String token = st.nextToken();
System.out.print(token);
}
출력
소지섭이동석김동현차인표장동건강동원
이런역할을 하는 클래스였는데 <c:forToken>
을 사용해서 똑같이 구현해보자.
<ul>
<c:forTokens items="소지섭,이동석,김동현,차인표,장동건,강동원" delims="," var="token">
<li>${ token }</li>
</c:forTokens>
</ul>
출력값
소지섭
이동석
김동현
차인표
장동건
강동원
보통 DB에서 문자열을 한줄형태로 받을 일이 별로 없겠지만 알아두면 좋을듯….
<c:out>
jsp의 기본객체인 out
객체 역할을 하는 태그이다.
(서블릿에선 PrintWriter out = request.getWriter()
가져왔던 그 객체….)
jsp에선 기본 제공되기때문에 굳이 <c:out>
태그로 사용할 일이 별로 없지만 특이한 기능이 있다.
value
속성과 default
속성이 있는데 value
속성에 값이 있다면 value
로 출력하고 value
에 값이 없다면 default
에 설정한 값이 출력된다.
또한 escapeXml
속성을 사용하여 아래 특수문자를 html에서 사용가능한 변환된 형태로 변경한다.
문자 | 변환된 형태 |
---|---|
< |
< |
> |
> |
& |
& |
' |
' |
" |
" |
<c:set var="m" value="<%= new int[]{ 1, 5, 3, 2, 4 } %>"/>
<c:forEach var="i" begin="1" end="9">
<c:out value="${ m[i] }" escapeXml="true" default="데이터가 없습니다."/><br>
</c:forEach>
출력값
5
3
2
4
데이터가 없습니다.
데이터가 없습니다.
데이터가 없습니다.
데이터가 없습니다.
데이터가 없습니다.
그리고 FileReader 객체를 value
로 설정하면 <c:out>
가 알아서 파일객체안의 데이터를 읽어 출력한다.
<%
FileReader reader = null;
try {
String path = request.getParameter("path");
reader = new FileReader(getServletContext().getRealPath(path));
%>
<pre>
소스코드 = <%= path %>
<c:out value="<%= reader %>" escapeXml="true"></c:out>
<c:catch var="ex">
태그 안에서 예외가 발생했다면 발생된 예외를 EL변수 ex에 저장한다.
<%
FileReader reader = null;
try {
String path = request.getParameter("path");
reader = new FileReader(getServletContext().getRealPath(path));
%>
<pre>
소스코드 = <%= path %>
<c:out value="<%= reader %>" escapeXml="true"></c:out>
</pre>
<%
}
catch(IOException ex) {
%>
에러: <%= ex.getMessage() %>
<%
}
finally {
if(reader != null) {
try {
reader.close();
}
catch(IOException ex) {}
}
}
%>
국제화 태그
국제화 태그는 특정 지역에 따라 알맞은 메시지를 출력해야 할 때 사용한다.
그뿐 아니라 원하는 날짜포멧, 숫자포멧으로 날짜데이터, 숫자데이터를 출력할 수 있다.
국제화 태그를 사용하기 위해선 아래 지시자를 추가해야 하며 fmt
접두사를 사용한다.
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:setLocale>
, <fmt:requestEncoding>
<fmt:setLocale>
태그는 국제화 태그들이 사용할 로케일을 지정한다.
<fmt:setLocale value="ko" scope="session"/>
<fmt:setLocale value="en" scope="session"/>
이걸 사용하기 전까지 우린 JVM기본 로케일, 혹은 web.xml파일에서 설정한 로케일을 기본 로케일로 사용하고 있었다.
사실 웹 브라우저가 설정한(알려준) 로케일에 맞게 메세지를 출력하기 때문에 <fmt:setLocale>
태그 사용할 일은 거의 없다.
<fmt:requestEncoding>
은 우리가 지금까지 많이 사용했던 request.setCharacterEncoding("UTF-8")
을 호출하는 것과 같다.
<fmt:requestEncoding value="utf-8">
사실 필터에서 인코딩을 설정하기 때문에 <fmt:requestEncoding>
태그도 거의 사용할 일 이 없다.
숫자 및 날짜 포멧팅 태그
기능 | Tag | 설명 |
---|---|---|
숫자 → 문자열 | <fmt:formatNumber> |
숫자를 formatting |
문자열 → 숫자 | <fmt:parseNumber> |
문자열로 표시된 날짜를 분석해서 숫자로 변환 |
날짜 → 문자열 | <fmt:formatDate> |
Date 객체를 formatting |
문자열 → 날짜 | <fmt:parseDate> |
문자열로 표시된 날짜를 분석해서 Date 객체로 변환 |
먼저 숫자 포멧팅인 <fmt:formatNumber>
, <fmt:parseNumber>
태그를 알아보자.
<fmt:formatNumber>
, <fmt:parseNumber>
<c:set var="price" value="10000"></c:set>
숫자 : <fmt:formatNumber value="${ price }" type="number" />
통화 : <fmt:formatNumber value="${ price }" type="currency" currencySymbol="$" /> <br>
퍼센트: <fmt:formatNumber value="0.57" type="percent" groupingUsed="false"/> <br>
패턴: <fmt:formatNumber value="${ price }" type="number" pattern="0000000.00"/> <br>
반올림 가능: <fmt:formatNumber value="1.1578" pattern="#.###" />
출력값
숫자 : 10,000
통화 : $10,000.00
퍼센트: 54%
패턴: 0010000.00
반올림 가능: 1.158
<fmt:formatNumber>
속성값을 하나씩 알아가 보자.
value
는 EL 변수를 사용하거나 숫자형식의 문자열이 들어간다.
type
은 보다싶이 3가지 종류가 있다. number, currency, percent.
통화(currency)는 currencySymbol
속성과 같이 사용하면 숫자 앞에 통화심볼을 붙일 수 있다.
퍼센트(percent)의 경우 기존 숫자에 *100을 한 후 %
기호를 붙인다.
groupingUsed
속성 기본값이 true
인데 false
로 변경할 경우 ,
컴마를 찍지 않는다.
pattern
속성은 DecimalFormat
클래스에 정의되어 있는 패턴을 사용한다.
숫자포멧: https://gmby.tistory.com/entry/DecimalFormat-다루기
포멧을 사용해 변환된 문자열을 var
속성을 사용해 저장 가능하다.
<c:set var="price" value="10000"></c:set>
<fmt:formatNumber value="${ price }" var="numberType" scope="request" />
숫자 : ${ numberType }
출력값
숫자 : 10,000
주의사항: var 속성을 사용하면 바로 출력되지 않는다.
문자열을 숫자로 변경하는 <fmt:parseNumber>
태그를 알아보자.
<c:set value="1,100.12" var="money"></c:set>
<fmt:parseNumber value="${ money }" pattern="#,###" /> <br>
<fmt:parseNumber value="${ money }" pattern="0,000.00" integerOnly="true"/> <br>
<fmt:parseNumber value="$128,398" type="currency" pattern="$#,###" /> <br>
출력값
1100.12
1100
128398
integerOnly
속을 true
로 설정하면 소수점을 버릴 수 있다.
<fmt:formatDate>
, <fmt:parseDate>
이제 문자열을 날짜로, 날짜를 문자열로 포멧팅 하는 태그를 알아보자.
포멧팅 하는 범위를 년월일과 시간을 따로 잡을 수 있고 같이 잡을 수 있다.
먼저 년 월 일만 포멧팅 하도록 type
을 date
로 설정하자.
<fmt:formatDate value="${ now }" type="date" dateStyle="full"/><br>
<fmt:formatDate value="${ now }" type="date" dateStyle="short"/><br>
<fmt:formatDate value="${ now }" type="date" dateStyle="medium"/><br>
<fmt:formatDate value="${ now }" type="date" dateStyle="long"/><br>
<fmt:formatDate value="${ now }" type="date" dateStyle="default"/><br>
출력값
2019년 5월 15일 수요일
1. 5. 15
2. 5. 15
2019년 5월 15일 (수)
1. 5. 15
dateStyle
은 5가지 종류가 있고 default
와 medium
가 같은 형식임을 알 수 있다.
시간을 포멧팅하도록 type
을 time
으로 설정
<fmt:formatDate value="${ now }" type="time" timeStyle="full"/><br>
<fmt:formatDate value="${ now }" type="time" timeStyle="short"/><br>
<fmt:formatDate value="${ now }" type="time" timeStyle="medium"/><br>
<fmt:formatDate value="${ now }" type="time" timeStyle="long"/><br>
<fmt:formatDate value="${ now }" type="time" timeStyle="default"/><br>
출력값
오후 3시 34분 08초 KST
오후 3:34
오후 3:34:08
오후 3시 34분 08초
오후 3:34:08
timeStyle
도 5가지가 있으며 medium
과 default
가 같은 형식이다.
time
과 date
를 한번에 출력하고 싶다면 type
을 both
로 설정
<fmt:formatDate value="${ now }" type="both" dateStyle="full" timeStyle="full"/><br>
<fmt:formatDate value="${ now }" type="both" dateStyle="short" timeStyle="short"/><br>
<fmt:formatDate value="${ now }" type="both" dateStyle="medium" timeStyle="medium"/><br>
<fmt:formatDate value="${ now }" type="both" dateStyle="long" timeStyle="long"/><br>
<fmt:formatDate value="${ now }" type="both" dateStyle="default" timeStyle="default"/><br>
출력값
2019년 5월 15일 수요일 오후 3시 35분 16초 KST
1. 5. 15 오후 3:35
2. 5. 15 오후 3:35:16
2019년 5월 15일 (수) 오후 3시 35분 16초
1. 5. 15 오후 3:35:16
이미 정해져 있는 양식 외에 pattern
을 지정할 수 있다.
<fmt:formatDate value="${ now }" pattern="yyyy MM dd HH:mm:ss"/><br>
출력값
2019 05 15 23:35:16
날짜 포멧 참고: https://kouzie.github.io/java/java-제네릭,-와일드카드,-Date,-Calendar,-SimpleDateFormat!/#simpledateformat
<fmt:formatDate>
도 마찬가지로 var
속성과 scope
속성을 사용 가능하다.
<fmt:formatDate value="${ now }" type="both" dateStyle="default" timeStyle="default" var="date" scope="request"/>
이번엔 문자열에서 날짜데이터로 바꾸는 <fmt:parseDate>
을 알아보자.
<fmt:parseDate>
의 속성으론 다음 속성들이 들어갈 수 있다.
속성|표현식/EL|타입|설명
:—–:|:—–:|:—–:|:—–:
value
|사용가능|java.util.Date
|파싱할 문자열
type
|사용가능|String
|날짜, 시간 또는 둘 다 포맷팅 할지의 여부를 지정한다. time, date, both중 한 가지 값을 가질 수 있으며, 기본값은 date이다.
dateStyle
|사용가능|String
|날짜에 대해 미리 정의된 포맷팅 스타일을 지정한다. default, short, medium, long, full 중 한가지 값을 가질 수 있으며, 기본값은 default이다
timeStyle
|사용가능|String
|시간에 대해 미리 정의된 포맷팅 스타일을 지정한다. default, short, medium, long, full 중 한가지 값을 가질 수 있으며, 기본값은 default이다.
pattern
|사용가능|String
|직접 파싱할 때 사용할 양식을 지정한다. java.text.DateFormat
에 있는 양식을 사용한다.
다음과 같이 정의된 문자열을 Date
객체로 만들어보자.
<c:set value="2019년 05월 10일" var="d"></c:set>
<fmt:parseDate value="${ d }" pattern="yyyy년 mm월 dd일" var="date"/> <br>
${ date }
출력값
Thu Jan 10 00:05:00 KST 2019
Date
의 toString()
메서드 호출값이 출력된다.
JSTL 함수태그
EL 2.3에서부터 객체의 함수를 직접 호출할 수 있게되며 효율성이 떨어졌지만 있다는 것은 알고 넘어가자.
먼저 JSTL함수태그를 사용하려면 아래 디렉티브를 추가해야 한다.
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
사용 형식을 아래와 같다 .
${ fn:함수명(매개변수) }
함수 | 설명 |
---|---|
length(obj) |
obj가 Conllection 인 경우 저장된 항목의 개수를, 문자인 경우 문자열의 길이를 반환, 배열의 길이를 반환 |
toUpperCase(str) |
str 을 대문자로 변환 |
toLowerCase(str) |
str 을 소문자로 변환 |
substring(str, idx1, idx2) |
str.substring(idx1, idx2) 의 결과를 반환, idx2 가 -1일 경우 str.substring(idx1) 과 동일 |
substringAfter(str1, str2) |
str1 에서 str1 에 포함되어 있는 str2 이후의 문자열을 구함 |
substringBefore(str1, str2) |
str1 에서 str1 에 포함되어 있는 str2 이전의 문자열을 구함 |
trim(str) |
str 좌우의 공백 문자를 제거 |
replace(str, src, dest) |
str 에 있는 src 를 dest 로 변환 |
indexOf(str1, str2) |
str1 에서 str2 가 위치한 인덱스를 구함 |
startsWith(str1, str2) |
str1 이 str2 로 시작할 경우 true , 그렇지 않을 경우 false 를 반환 |
endsWith(str1, str2) |
str1 이 str2 로 끝나는 경우 true , 그렇지 안을 경우 false 를 반환 |
contains(str1, str2) |
st1이 str2를 포함하고 있을 경우 true를 반환 |
containslgnoreCase(str1, str2) |
대소문자 구분없이 str1 이 str2 를 포함하고 있을 경우 true 를 반환 |
split(str1, str2) |
str2 로 명시한 글자를 기준으로 str1 을 분리해서 배열로 반환 |
join(array, str2) |
array 에 저장된 문자열을 합침, 각 문자열의 사이에는 str2 가 붙음 |
escapeXml(str) |
XML의 객체 참조에 해당하는 특수문자를 처리함 |
쓸만한 함수로 ${ fn:length(..) }
가 있다.
배열의 길이를 구할 때 arr.length
형식으로 length
라는 public final
필드를 접근하였지만 EL에선 get, set
메서드가 정의되어 있지 않는 이상 필드에 접근할 수 없다.
배열에 getLength()
메서드가 정의되어있을 일은 없으니
EL로 배열 길이를 구하려면 JSTL 함수중 length
함수를 사용해야 한다.
<c:set var="m" value="<%= new int[]{1, 5, 3, 2, 4} %>"/>
배열 크기: ${ fn:length(m) }
배열이 scope
에 저장되어 있을 경우 얻기 까다로운 배열 길이를 JSTL함수로 쉽게 얻을 수 있다.
<%
int[] m = {1, 5, 3, 2, 4};
%>
<%= m.length %>
물론 배열이 scope
에 저장되어 있지 않고 위와 같은 상황이라면 그냥 스크립트릿 사용하면 된다.