학원/복기

[MVC] 커스텀 태그(Cutom Tag)

조가루 2023. 7. 14. 00:45

커스텀 태그(Cutom Tag) 

커스텀 태그는 JSP 문서에서 스크립트 요소 대신 사용하기 위해 프로그래머가 생성한 태그이다.
 

순서

1.태그 클래스를 작성 

2.TLD 파일에 커스텀 태그를 등록

3.JSP 문서에서 커스터 태그 사용
 


 

1 .태그 클래스를 작성한다.

 
태그 클래스는 JSP 문서에서 커스텀 태그를 사용할 경우 호출될 메소드가 선언된 클래스이다.
태그 클래스는 TagSupport 클래스, BodyTagSupport 클래스, SimpleTagSupport 클래스 중 하나를 상속받아 작성한다.
커스텀 태그 사용시 호출되는 메소드는 부모클래스의 메소드를 오버라이드 선언하여 작성한다.
 
태그 속성과 태그 내용이 없는 커스텀 태그의 클래스 HelloTag를 작성해보자.

//태그 속성과 태그 내용이 없는 커스텀 태그의 클래스
public class HelloTag extends TagSupport {
	private static final long serialVersionUID = 1L;
	
    //코드 작성
    // ...
	
	
}

 
1) 생성자 생성

    //JSP 문서에서 커스텀 태그를 최초로 사용할 경우 태그 클래스를 객체로 생성하기 위해 한번만 호출된다.
    public HelloTag() {
        System.out.println("HellogTag 클래스의 기본 생성자 호출 - 객체 생성");
    }

 
2) 태그를 사용할 때 호출될 메소드 선언
 
아래 3가지의 메소드를 모두 오버라이드 할 필요는 없고, 필요한 것만 오버라이드 해주면 된다.

	//JSP 문서에서 커스텀 태그의 시작태그를 사용할 때마다 호출되는 메소드
	@Override
	public int doStartTag() throws JspException {
		System.out.println("HellogTag doStartTag() 메소드 호출");
		return super.doStartTag();
	}
	
	//JSP 문서에서 커스텀 태그의 태그내용을 사용할 때마다 호출되는 메소드
	@Override
	public int doAfterBody() throws JspException {
		System.out.println("HellogTag doAfterBody() 메소드 호출");
		return super.doAfterBody();
	}
	
	//JSP 문서에서 커스텀 태그의 종료태그를 사용할 때마다 호출되는 메소드
	@Override
	public int doEndTag() throws JspException {
		System.out.println("HellogTag doEndTag() 메소드 호출");
		return super.doEndTag();
	}

 
 

2. TLD 파일에 커스텀 태그를 등록한다.

 
tag : 커스텀 태그를 등록하기 위한 엘리먼트 
name : 커스텀 태그의 이름을 설정하기 위한 엘리먼트 
tag-class : 커스텀 태그 사용시 생설될 객체의 태그 클래스를 설정하기 위한 엘리먼트
body-content : 커스텀 태그에서 사용할 태그내용을 설정하기 위한 엘리먼트
→ empty : 태그내용이 없는 커스텀 태그를 생성하기 위한 엘리먼트 값
 
[custom.tld]

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd ">
  <description>단순한 형태의 커스텀 태그 구현</description>
  <tlib-version>1.0</tlib-version>
  <short-name>simple</short-name>
  <uri>http://www.itwill.xyz/mvc/custom</uri>
  
  <tag>
  	<name>hello</name>
  	<tag-class>xyz.itwill.custom.HelloTag</tag-class>
  	<body-content>empty</body-content>
  </tag>
</taglib>

 

 
 3.JSP 문서에서 커스텀 태그 사용하기

 

 
taglib 디렉티브를 사용하여 TLD 파일을 제공받아야 JSP 문서에서 커스텀 태그를 사용할 수 있다.

<%@taglib prefix="simple" uri="http://www.itwill.xyz/mvc/custom"%>

 

메소드가 호출되는 것을 확인할 수 있다.

 
태그내용이 없는 태그는 시작태그와 종료태그를 같이 표현할 수 있다.

<!--<simple:hello></simple:hello>-->
<simple:hello/>

 


 
HelloTag 클래스에서 부모클래스로부터 제공받은 Pagecontext 객체(pageContext)를 이용하여, 웹프로그램 작성에 필요한 객체를 반환받아 명령을 작성해보자.
 
pageContext.getOut() : 응답문서를 생성하기 위한 출력스트림(JspWriter 객체)을 반환하는 메소드 
 
doStartTag() 메소드의 반환값(정수값)은 부모클래스에서 제공되는 상수를 사용하면 된다.
→ SKIP_BODY, EVAL_BODY_ENCLUDE 중 하나를 선택하여 반환하면 된다.
 
SKIP_BODY : 태그내용을 클라이언트에게 전달하지 않을 경우 사용하는 상수 (기본값) 
EVAL_BODY_ENCLUDE : 태그내용을 클라이언트에게 전달할 경우 사용하는 상수
 

		@Override
		public int doStartTag() throws JspException {
		//System.out.println("HellogTag doStartTag() 메소드 호출");
		
		try {
			//부모클래스로부터 제공받은 Pagecontext 객체를 이용하여 웹프로그램에 작성에
			//필요한 객체를 반환받아 명령 작성
			pageContext.getOut().println("<div>안녕하세요.</div>");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//doStartTag() 메소드의 반환값(정수값) : 부모클래스에서 제공되는 상수를 사용
		return SKIP_BODY;
	}

 
 
doAfterBody() 메소드의 반환값(정수값)은 부모클래스에서 제공되는 상수를 사용하면 된다.
→ SKIP_BODY, EVAL_BODY_AGAIN 중 하나를 선택하여 반환하면 된다.
 
SKIP_BODY : 태그내용을 다시 전달하지 않을 경우 사용하는 상수 (기본값)
EVAL_BODY_AGAIN : 태그내용을 클라이언트에게 다시 전달하는 경우 사용하는 상수 
 

//JSP 문서에서 커스텀 태그의 태그내용을 사용할 때마다 호출되는 메소드
	@Override
	public int doAfterBody() throws JspException {
		//System.out.println("HellogTag doAfterBody() 메소드 호출");
		
	
		return SKIP_BODY;
	}

 
 
doEndTag() 메소드의 반환값(정수값)은 부모클래스에서 제공되는 상수를 사용하면 된다.
→ SKIP_PAGE, EVAL_PAGE 중 하나를 선택하여 반환하면 된다.
 
SKIP_PAGE : 종료태그 실행 후 JSP 문서를 강제로 종료할 경우 사용하는 상수
EVAL_PAGE : 종료태그 실행 후 JSP 문서를 계속 실행할 경우 사용하는 상수 
 

	//JSP 문서에서 커스텀 태그의 종료태그를 사용할 때마다 호출되는 메소드
	@Override
	public int doEndTag() throws JspException {
		//System.out.println("HellogTag doEndTag() 메소드 호출");
		
		return EVAL_PAGE;
	}

 
HelloTag 클래스 전체 소스코드

package xyz.itwill.custom;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

//태그 속성과 태그 내용이 없는 커스텀 태그의 클래스
public class HelloTag extends TagSupport {
	private static final long serialVersionUID = 1L;
	
	//JSP 문서에서 커스텀 태그를 최초로 사용할 경우 태그 클래스를 객체로 생성하기 위해 한번만 호출된다.
	public HelloTag() {
		//System.out.println("HellogTag 클래스의 기본 생성자 호출 - 객체 생성");
	}
	
	//JSP 문서에서 커스텀 태그의 시작태그를 사용할 때마다 호출되는 메소드
	@Override
	public int doStartTag() throws JspException {
		//System.out.println("HellogTag doStartTag() 메소드 호출");
		
		try {
			//부모클래스로부터 제공받은 Pagecontext 객체를 이용하여 웹프로그램에 작성에
			//필요한 객체를 반환받아 명령 작성
			pageContext.getOut().println("<div>안녕하세요.</div>");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//doStartTag() 메소드의 반환값(정수값) : 부모클래스에서 제공되는 상수를 사용
		
		return SKIP_BODY;
	}
	
	//JSP 문서에서 커스텀 태그의 태그내용을 사용할 때마다 호출되는 메소드
	@Override
	public int doAfterBody() throws JspException {
		//System.out.println("HellogTag doAfterBody() 메소드 호출");
	
		return SKIP_BODY;
	}
	
	//JSP 문서에서 커스텀 태그의 종료태그를 사용할 때마다 호출되는 메소드
	@Override
	public int doEndTag() throws JspException {
		//System.out.println("HellogTag doEndTag() 메소드 호출");
		
		return EVAL_PAGE;
	}
}

 
 
 
hello_tag.jsp 을 다시 실행해보자 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="simple" uri="http://www.itwill.xyz/mvc/custom"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MVC</title>
</head>
<body>
	<h1>Custom Tag - NoAttribute And NoBody</h1>
	<hr>
	<!--<simple:hello></simple:hello>-->
	<simple:hello/>
	<simple:hello/>
	<simple:hello/>
</body>
</html>

출력결과

 
 
 


이번엔 태그 속성이 있으며 태그 내용은 없는 커스텀 태그 클래스를 만들어보자.
 

public class HelloMessageTag extends TagSupport{
	private static final long serialVersionUID = 1L;
	
	//코드 작성
    	//...
	
}

 
 
1) 커스텀 태그의 속성값을 저장하기 위한 필드를 선언한다.
(이 때, 커스텀 태그의 속성명과 같은 이름으로 필드를 선언해야 한다.)

private String name;

 
2) 생성자에서 객체 생성에 필요한 초기화 작업 관련 명령을 작성해준다. - 필드의 초기값 설정 
→ 커스텀 태그 사용시 속성을 생략할 경우 기본적으로 사용될 속성값으로 필드에 저장하기 위해 사용한다.
하지만 커스텀 태그의 속성이 필수인 경우엔 필드 기본값 설정을 생략한다.
 

public HelloMessageTag() {
		//커스텀 태그 사용시 속성을 생략할 경우 기본적으로 사용될 속성값으로 필드에 저장하기 위해 사용
		// => 커스텀 태그의 속성이 필수인 경우 필드 기본값 설정 생략
		name = "홍길동";
	}

 
3) Setter Getter 메소드 생성
 
커스텀 태그 사용시 태그 속성을 사용하여 속성값을 설정할 경우엔 Setter 메소드가 자동 호출된다.

	public String getName() {
		return name;
	}
	
	//커스텀 태그 사용시 태그 속성을 사용하여 속성값을 설정할 경우엔 Setter 메소드가 자동 호출
	public void setName(String name) {
		this.name = name;
	}

 
 
4) 커스텀 태그 사용시 실행될 명령을 작성할 메소드(즉, 필요한 메소드)만 오버라이드 선언해준다.
오버라이드 선언하지 않은 메소드는 부모클래스의 명령이 없는 메소드를 호출한다. 

	@Override
	public int doStartTag() throws JspException {
		try {
			if(name.equals("홍길동")) {
				pageContext.getOut().println("<h3>관리자님, 안녕하세요.</h3>");
			} else {
				pageContext.getOut().println("<h3>"+name+"님, 안녕하세요.</h3>");
			}
		} catch (IOException e) {
			throw new JspException(e.getMessage());
		}
		return SKIP_BODY;
	}

 
HelloMessageTag 클래스 전체 소스코드

package xyz.itwill.custom;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

public class HelloMessageTag extends TagSupport{
	private static final long serialVersionUID = 1L;
	
	private String name;
	
	public HelloMessageTag() {
		//커스텀 태그 사용시 속성을 생략할 경우 기본적으로 사용될 속성값으로 필드에 저장하기 위해 사용
		// => 커스텀 태그의 속성이 필수인 경우 필드 기본값 설정 생략
		name = "홍길동";
	}

	public String getName() {
		return name;
	}
	
	//커스텀 태그 사용시 태그 속성을 사용하여 속성값을 설정할 경우엔 Setter 메소드가 자동 호출
	public void setName(String name) {
		this.name = name;
	}
	
	
	//커스텀 태그 사용시 실행될 명령을 작성할 메소드만 오버라이드 선언
	@Override
	public int doStartTag() throws JspException {
		try {
			if(name.equals("홍길동")) {
				pageContext.getOut().println("<h3>관리자님, 안녕하세요.</h3>");
			} else {
				pageContext.getOut().println("<h3>"+name+"님, 안녕하세요.</h3>");
			}
		} catch (IOException e) {
			throw new JspException(e.getMessage());
		}
		return SKIP_BODY;
	}
	
}

 
 
tld 파일에 커스텀 태그를 추가해주자. 기존의 tld 파일에 추가해도 된다.
 
[custom.tld]
 
attribute : 태그 속성을 등록하기 위한 엘리먼트
name : 태그의 속성명을 설정하기 위한 엘리먼트
→ 태그 클래스의 필드명과 같은 이름으로 커스텀 태그의 속성명을 설정해주어야 한다.

<tag>
  	<name>helloMessage</name>
  	<tag-class>xyz.itwill.custom.HelloMessageTag</tag-class>
  	<body-content>empty</body-content>
  	<attribute>
  		<name>name</name>
  	</attribute>
 </tag>

 
 
JSP 문서를 생성해 커스텀 태그를 이용해보자.
 
[hello_message_tag.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="simple" uri="http://www.itwill.xyz/mvc/custom"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MVC</title>
</head>
<body>
	<h1>Custom Tag - AnyAttribute And Nobody</h1>
	<hr>
	<!-- 커스텀 태그의 속성을 생략한 경우 태그 클래스의 생성자에서 설정한 기본값을 속성값으로 설정-->
	<simple:helloMessage/>
	
	<!-- 커스텀 태그의 속성을 사용하여 속성값을 변경한 경우 태그 클래스의 Setter 메소드를 자동 호출 -->
	<!-- => 커스텀 태그의 속성값을 저장하기 위한 필드의 Setter 메소드가 없는 경우 에러 발생 -->
	<simple:helloMessage name="홍길동"/>
	
	<simple:helloMessage name="임꺽정"/>
</body>
</html>

 

 

tld 파일에서 required 속성을 사용해보자
 
required 속성 : 커스텀 태그의 속성에 대한 필수 여부를 설정하기 위한 엘리먼트
  false : 선택적인 속성(기본값), true : 필수적인 속성

<tag>
  	<name>helloMessage</name>
  	<tag-class>xyz.itwill.custom.HelloMessageTag</tag-class>
  	<body-content>empty</body-content>
  	<attribute>
  		<name>name</name>
  		<required>true</required>
  	</attribute>
 </tag>

 
 
다시 JSP 문서에서 커스텀 태그를 사용해보자.
 
커스텀 태그의 속성을 필수로 설정한 경우에는 속성 생략시 에러가 발생한다. 

<simple:helloMessage/> <!--에러발생-->

 
존재하지 않는 속성을 사용하면 에러가 발생한다.

<simple:helloMessage name="임꺽정" class="aaa"/><!--에러발생-->

 


태그 속성과 태그 내용 모두 있는 커스텀 태그 클래스를 만들어보자.
 

package xyz.itwill.custom;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

//태그 속성과 태그 내용이 있는 커스텀 태그 클래스
public class HelloBodyTag extends TagSupport {
	private static final long serialVersionUID = 1L;
	
	private boolean test;
	
	public boolean isTest() {
		return test;
	}

	public void setTest(boolean test) {
		this.test = test;
	}
	
	@Override
	public int doStartTag() throws JspException {
		try {
			if(test) {
				pageContext.getOut().println("<h3>");
			} else {
				pageContext.getOut().println("<p>");
			}
		} catch (IOException e) {
			throw new JspException(e.getMessage());
		}
		return EVAL_BODY_INCLUDE;
	}
	
	@Override
	public int doEndTag() throws JspException {
		try {
			if(test) {
				pageContext.getOut().println("님, 안녕하세요.</h3>");
			} else {
				pageContext.getOut().println("님, 반갑습니다.</p>");
			}
		} catch (IOException e) {
			throw new JspException(e.getMessage());
		}
		return EVAL_PAGE;
	}
	
}

 
 
tld 파일에서 커스텀 태그를 등록해주자
 
body-content 엘리먼트값으로 [JSP]를 설정하면 태그내용으로 JSP 명령 사용이 가능하다.

  <tag>
  	<name>helloBody</name>
  	<tag-class>xyz.itwill.custom.HelloBodyTag</tag-class>
  	<body-content>JSP</body-content>
  	<attribute>
  		<name>test</name>
  		<required>true</required>
  	</attribute>
  </tag>

 
 
JSP 문서에서 등록한 커스텀 태그를 사용해보자
 
[hello_body_tag.jsp]
 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="simple" uri="http://www.itwill.xyz/mvc/custom"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MVC</title>
</head>
<body>
	<h1>Custom Tag - AnyAttribute And Nobody</h1>
	<hr>
	<simple:helloBody test="true">홍길동</simple:helloBody>
	<simple:helloBody test="false">임꺽정</simple:helloBody>
	<hr>
	<%
		String name="전우치";
		request.setAttribute("name", name);
	%>
	<simple:helloBody test="true"><%=name %></simple:helloBody>
	<simple:helloBody test="false">${name }</simple:helloBody>
</body>
</html>

 
커스텀 태그의 속성값은 <%%> 나 ${} 를 사용하면 에러가 발생한다.

<%
	boolean result=true;
	request.setAttribute("result", false);
%>
<simple:helloBody test="<%=result %>">장길산</simple:helloBody>
<simple:helloBody test="${result }">홍경래</simple:helloBody>

 
만약 사용하고 싶으면 rtexprvalue 엘리먼트를 이용하면 된다.
rtexprvalue : 커스텀 태그의 속성값으로 JSP 표현식 또는 EL 사용 여부를 설정하기 위한 엘리먼트
-> false : 사용 불가능 (기본값) , true : 사용 가능

 <tag>
  	<name>helloBody</name>
  	<tag-class>xyz.itwill.custom.HelloBodyTag</tag-class>
  	<body-content>JSP</body-content>
  	<attribute>
  		<name>test</name>
  		<required>true</required>
  		<rtexprvalue>true</rtexprvalue>
  	</attribute>
 </tag>

 
 
rtexprvalue 엘리먼트 설정 후 다시 실행해보면 출력이 잘 되는것을 확인할 수 있다.