스프링

EL & JSTL

easy-6 2025. 2. 12. 23:16

* El이란?

  - Expression language의 약자이며 jsp 2.0 스펙에 추가된 개념  

 

  - JSP 스크립트의 표현식을 대신하여 속성 값을 쉽게 출력하도록 고안된 언어

 

  -  출력, 반복처리를 태그기반으로 제공

 

  -  기존 Expression Tag(<%= %>) 대체  하여 값을 꺼낼 때 ${}로 표기한다. 

 

4가지의 특성을 가지고  있다 .

 

값을 꺼내 쓰기에 앞서 아래의 4가지 범위가 있다. 

Application scope > Session scope > Request scope > Page scope 

위의 범위는 영역이 큰 순부터 작은 순으로 이루어져있다.

scope 범위

 

동일한 변수명의 다른 값을 가진 변수가 있고, 동일한 스코프의 범위 안에 존재할 때 더 좁은 범위에 있는 값을 가져올 수 있다는 특징이 있다. 

예시로 page와 request 스코프 둘 다에 만족하는 어떠한 값이 존재할 때 , 더 좁은 범위인 page 스코프에 있는 값을 가져온다 .

아래의 예시

1) jsp 화면 

//el. jsp 화면 최상단

<%
// 페이지에서만 사용하는 변수는 지역변수이고 스코프 취급을 하지 않음 
	String data1="페이지 데이터입니다.";
// 따라서 pageContext를 통해서 스코프에 추가를 해야함
pageContext.setAttribute("data1", data1); // 페이지 영역에 데이터 추가 
%>

// el.jsp 화면을 나타내는 부분 

<h3>el scope attribute 다루기 </h3>
	- el은 문자열이지만 숫자 형식이라면 자동으로 형변환을 하여 연산을 한다. <br>
	${requestScope.data1}10<br>  <!-- 스코프를 생략하면 무조건 좁은 범위의 스코프가 선택이 된다. -->
	${data1}<br>  <!-- 스코프를 생략하면 무조건 좁은 범위의 스코프가 선택이 된다. -->
	${pageScope.data1}<br>  <!-- 스코프를 생략하면 무조건 좁은 범위의 스코프가 선택이 된다. -->
	${data2}<br>
	${vo.title}<br>

위에서 , ${data1}의 결과는 requestScope와 pageScope중 더 좁은 범위인 pageScope 에 해당하는 문자열인 "페이지 데이터입니다" 가 출력이 됨.

위의 코드를 통해 스코프의 범위에 따라 값이 어떻게 나오는지 화면을 통해 알 수 있다 .

 

2) 컨트롤러 

package edu.springMVC.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import edu.springMVC.vo.BoardVO;

@Controller
public class ElJSTLController {

	@RequestMapping(value="/el.do")
	public String el(Model model) {
		model.addAttribute("data1","10");
		model.addAttribute("data2",10);
		
		BoardVO vo = new BoardVO();
		vo.setTitle("제목입니다.");
		
		model.addAttribute("vo",vo);
		
		BoardVO vo2 = new BoardVO();
		vo2.setTitle("안녕하세요. 반갑습니다..");
		vo2.setPassword("1234");
		vo2.setWriteDate("2024-11-19");
		vo2.setWriter("홍길동");
		vo2.setContent("첫 번재 게시글을 작성해요 하하하 ");
		
		// request.setAttribute("details", vo2)와 같음
		model.addAttribute("details", vo2);
		// 위 details 안에 들어있는 객체의 데이터를 el.jsp 맨 아래 전부 각 필드값을 출력하세요 
		
		return "el";
	}
}

 

3) controller에서 details객체의 값을 가져오는 화면 

	<hr />
	${details.title}<br>
	${details.password}<br>
	${details.writer}<br>
	${details.writeDate}<br>
	${details.content}<br>

 

기존 jsp와는 다르게 el에서는 스크립틀릿 <%=%>을 사용하지 않고 ${}를 통해서 해당 키의 값을 바로 찍어낼 수 있는 것을 알 수 있다.

또한 controller에 담아놓은 키를 이용해서 , el태그를 사용하면 

해당 객체의 값을 가져올 수 있다.

 

 

el 리터럴 식


위의 el 리터럭 식을 보면 null 은 el 사용시 null로 표시되는게 아니라 빈 문자열로 표시가 되는 것을 알 수 있다 .

 

* JSTL이란? 

  - 반드시 라이브러리가 있어야함

  - pom.xml에 jstl 라이브러리가 있어야 사용이 가능함 

  - 스프링 프레임워크에서는 JSTL 라이브러리를 제공함

  - jstl을 사용하려면 태그립이 필요함 

 

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

위의 코드는 접두어를 나타내는 태그립 ( c로 시작한다는 의미 )

이때 접두어를 나타내는 태그립은 prefix의 값을 바꿀 수 있지만 잘 바꾸지 않음

 

*jstl을 사용하여 영역별 속성(attribute) 선언하기 

 

jstl에서 키를 지정하는 속성은 var , 값을 지정하는 속성은 value이다. 

<h2> jstl을 배워보자 </h2>
<h2> jstl을 사용하여 영역별 속성(attribute) 선언하기</h2>
<!-- 키를 지정하는 속성 > var , 값을 지정하는 속성 value	--> 
<!-- pageContext.setAttribute("name","홍길동")과 같다고 볼 수 있음 -->
<c:set var="name" value="홍길동"/>   
${name}<br>

해당 코드를 통해  <c: set을 선언 후

var로 지정된 키인 name을  el 태그 ( ${} )를 이용해서 키의 값인 홍길동이 출력되는 걸 볼 수 있다.

 

 

 

 

 

 

*jstl에서  scope을 지정하기 

다음은 해당하는 키가 applicationScope, sessionScope,requireScope,pageScope 4개의 영역에서 어디에 존재하는지 

확인 하는 코드이다. 

<%@ taglib url="http://java.sum.com/jsp/jstl/core" prefix="c" %> <!-- 태그립 선언-->
<!-- request.setAttribute("id","honghong") -->
	<c:set var="id" value="honghong" scope="request"/>  <!-- scope > 영역을 지정 -->
	${id}<br>
	${requestScope.id}<br> <!-- requestScope영역에 존재하는 걸 알 수 있음  -->

위에서 마찬가지로 속성을 선언할 때 <c:set을 사용하고 해당하는 var(키)와 value(값)을 선언하는걸 알 수 있다.

 

*jstl에서 el태그 사용하기 

<c:set var="sum" value="10+20"/>
	${sum } <br> 	<!-- 더하는 값이 아닌 문자열 형태로 출력  -->

위의 코드에서 value를 "10+20"으로 선언 후 el태그를 활용해 ${sum}을 출력하게 되면 30이 아닌 문자열 10+20이 출력된다.

10+20 문자열로 출력

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!--  태그립 선언  -->	
	<!-- value 속성에 el을 사용하게 되면 연산의 결과 값을 대입할 수 있다. -->
	<c:set var="sum" value="${10+20}"/>
	${sum } <br> 	<!-- 더하는 값이 아닌 문자열 형태로 출력  -->

따라서 문자열이 계산한 값을 갖고 오고 싶은경우 value에 el 태그를 사용한 value="${10 +20}"의 형태로 작성을 하면 값은 

10+20이 아닌 30을 화면에 보여주게 된다.

10+20의 문자열 형태가 아닌 값 30으로 출력

이처럼 <c: set var ="sum" value="${계산식}" />의 형태로 작성 후 el 태그로 ${sum}을 출력하면 계산식의 결과가 나오는 걸 알 수 있다.

 

*jstl에서 attribute 제거하기 

 

	<h3> attribute 제거하기 </h3>
	<!-- 특정 스코프를 지정하지 않으면, 모든 영역의 sum을 제거함  -->
	<c:remove var="sum"/> 
	${sum }<br>

위의 코드는 sum이 존재하는 스코프의 영역을 지정하지 않고 삭제한 경우에 var이 sum인 객체를 모두 삭제하는 코드이다.

<c: remove var="sum"의 결과

위와 같은 경우 특정 scope에 존재하는 키 var를 삭제하지 않고 전체를 삭제하게 되는데

아래의 코드처럼 scope를 지정하게 된다면 특정 scope를 지정하여 삭제할 수 있다.

<!-- scope 지정시 해당 영역에서 일치하는 attribute 제거함 -->
	<c:remove var="id" scope="page"/>
	id : ${id}<br>

여기서 sum은 pageScope에 존재하지만, 이미 위에서 삭제를 했기 때문에 화면에는 아무것도 나오지 않는다.

 

*attribute 출력하기 

 

여기서 attribue를 출력하는 코드가 있다.

attribute를 출력하는 태그

46번째 라인의 html키에 value값을 작성후 el태그로 html을 출력하면 화면에 아래의 이미지처럼 나오는 걸 볼 수 있다.

소스코드 결과

위의 이미지에서 value 부분에는 html 태그가 먹히는 걸 볼 수 있다. 

이때 누군가 악의적인 스크립트를 value 부분에 작성하면 스크립트가 작동이 된다는 보안적인 문제가 있기 때문에 

<c: out value="${html}"/>을 작성하여 html 및 스크립트를 무력화 시킬 수 있다. 

 

* if문 작성하기 

아래의 코드를 보았을 때 , <c:if test="true"/> , <c:if test="false"/>를 모르고 얼핏 보면 true와 false가 둘 다 출력된다고 생각할 수 있다. 

	<h3> if문 작성하기</h3>
	<c:if test="true"> 
		true 
	</c:if>
	<br>
	<c:if test="false"> 
		false 
	</c:if>

하지만 자바의 if문과 동일하다고 생각한다면 보다 보기 쉽다. 

자바의 if 문에서는 조건문이 true일 때  실행문이 실행이 된다. 

그처럼 위의 코드도 <c:if test="true" />일 때만 true가 실행이 된다. 

또한 test="true" 대신 el식을 넣어서 표현 할 수 있다. 

<!-- 로그인 x -->
<%-- 	<c:if test="${loginUser == null}" > --%>
	<c:if test="${empty loginUser}" >
		<a href="<%=request.getContextPath() %>/login.do">로그인 </a>
	</c:if>
	
	<!-- 로그인 o -->
	<%-- <c:if test="${loginUser != null}"> --%>
	<c:if test="${not empty loginUser}" >
		<a href="<%=request.getContextPath() %>/mypage.do">마이페이지</a>
	</c:if>

위의 코드는 test="true"부분에 el 태그를 이용해서 식을 대입한 내용이다.

empty loginUser 는 loginUser 객체가 비어있을 때 true를 반환하고 ,  loginUser == null 또한 loginUser가 참조할 수 있는게 없기 때문에, 둘 다 해당 객체에 아무것도 없다는 이야기가 되므로 

여기서 loginUser == null과 empty loginUser는 같은 의미이다. 

 

*switch-case문 작성하기 

아래는 jstl의 switch-case 문이다.

<h3> switch문 사용하기 </h3>
	- jstl에서는 else if 문법은 없으므로, switch문을 사용하여 표현한다.<br>
	- choose 태그 안 when의 test 속성이 최초 true인 태그 안의 실행문이 실행된다.<br>
	- when이 모두 false인 경우는 otherwise 태그 안의 실행문이 실행된다.<br>
	<!--
	switch(변수 or 식){
		case 값 : ...
	}	
	  -->
	<!-- true와 false의 연산을 위해 test안에 el 태그를 사용할 수 있다. -->
	
	<c:choose>
		<c:when test="true">
			true1
		</c:when>
		<c:when test="true">
			true2
		</c:when>
	</c:choose>
	<br>
	<c:choose>
		<c:when test="${loginUser != null}">
			<a href="<%=request.getContextPath() %>/mypage.do">마이페이지</a>
		</c:when>
		<c:otherwise>
			<a href="<%=request.getContextPath() %>/login.do">로그인 </a>
		</c:otherwise>
	</c:choose>

위의 코드를 실행하면 

코드 결과

위의 이미처럼 true1을 반환하는 것을 알 수 있다 .

<c:choose>
    <c:when test="true">
        true1
    </c:when>
    <c:when test="true">
        true2
    </c:when>
</c:choose>

의 결과는  true1과 true2가 모두 출력될 수 있다고 생각하지만 일 수 있다고 생각 할 수 있다.

하지만 

JSTL <c:choose> 태그의 동작 원리는 

 if-else if-else 구조와 유사하며, 첫 번째로 조건이 true인 <c:when> 블록만 실행된다.
즉, <c:choose> 내에서는 단 한 개의 <c:when> 블록만 실행되도록 설계되어 있기 때문에, true1만 나오게 된다. 

또한 <c:when>블록과 일치하는 경우가 없는경우 <c:otherwise>가 실행이 된다(switch-case문의 default 느낌).

 

 

* 반복문 사용하기 

	<h3>반복문 사용하기</h3>
	-forEach 태그 사용하여 반복문 실행 <br>
	<!-- begin 시작 end 끝 step 증감연산자 역활  -->
	<c:forEach begin="1" end="10" step="2" var="count"> 
		반복${count} <br>
	</c:forEach>

 

jstl에서 반복문을 사용하는 코드는 <c forEact begin="시작값' end="끝 값" step="건너뛰는 숫자 " var="x번째 반복을 알려주는 값"

 

따라서 위의 코드를 보고 알 수 있는 내용은 

반복을 줄바꿈하여 1부터 10번 화면에 찍을 내용인데 이때 , var을 통해 count의 값을 알려준다 . 

 

해당코드의 결과

// 객체를 이용한 반복문 작성해야함 (미완성)

controller에서 map의 형태로 "members" : members를 받았다고 가정했을 때 , 클라이언트에서 해당 객체의 정보를 반복문을 통해 가져올 수 있다. 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib url="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!-- 태그립 선언 -->
<html>
<head>
</head>
<body>

<!--여러 사용자를 담은 컬렉션 (users)인 경우 -->
<!-- <c:forEach를 사용할 경우에는 반복할 컬렉션인 items와 
각 요소를 저장할 변수 이름인 var 속성이  필요함 -->

<c:forEach var= user items="${users}" >
	이름  : ${user.name) <br/>
    아이디 : ${user.id) <br/>
    나이 : ${user.age) <br/>
 </c:forEach>


<!-- 단일 사용자를 담은 단일 객체인 경우 -->

<c:if test="${loginUser != null}" >
	이름 : ${loginUser.name} <br/>
    아이디 : ${loginUser.id}<br/>
    나이 : ${ginUser.age}<br/>
</c:if>
 
</body>



</html>