티스토리 뷰
내장 객체
- 표현 언어에서 제공하는 여러 가지 내장 객체에 대해 알아본다.
- JSP는 기본적으로 내장 객체들을 제공하지만 이 객체들은 표현식에서만 사용할 수 있다.
- 따라서 표현 언어에서는 따로 내장 객체들을 제공한다.
- 표현 언어에서 제공하는 내장 객체들은 ${} 안에서만 사용할 수 있다.
구분 | 내장 객체 | 설명 |
스코프 | pageScope | JSP의 page와 같은 기능을 하고 page 영역에 바인딩된 객체를 참조한다. |
requestScope | JSP의 request와 같은 기능을 하고 request에 바인딩된 객체를 참조한다. | |
sessionScope | JSP의 session과 같은 기능을 하고 session에 바인딩된 객체를 참조한다. | |
applicationScope | JSP의 application과 같은 기능을 하고 application에 바인딩된 객체를 참조한다. | |
요청 매개변수 | param | request.getParameter() 메서드를 호출한 것과 같으며 한 개의 값을 전달하는 요청 매개변수를 처리한다. |
paramValues | request.getParameterValues() 메서드를 호출한 것과 같으며 여러 개의 값을 전달하는 요청 매개변수를 처리한다. |
|
헤더 값 | header | request.getHeader() 메서드를 호출한 것과 같으며 요청 헤더 이름의 정보를 단일 값으로 반환한다. |
headerValues | request.getHeader() 메서드를 호출한 것과 같으며 요청 헤더 이름의 정보를 배열로 반환한다. |
|
쿠키 값 | Cookies | 쿠키 이름의 값을 반환한다. |
JSP 내용 | pageContext | pageContext 객체를 참조할 때 사용한다. |
초기 매개변수 | initParam | 컨텍스트의 초기화 매개변수 이름의 값을 반환한다. |
param 내장 객체
- request.getParameter() 메서드를 호출한 것과 같으며 한 개의 값을 전달하는 요청 매개변수를 처리한다.
param 내장 객체 사용 예
- 회원 가입창에서 회원 정보를 입력하고 JSP로 전송 시 getParameter() 메서드를 이용하지 않고 param 내장 객체를
이용해 전송된 회원 정보를 출력한다.
* memberForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입창</title>
</head>
<body>
<form method="post" action="member1.jsp">
<h1 style="text-align:center">회원가입창</h1>
<table align="center">
<tr>
<td width="200">
<p align="right">아이디</p>
</td>
<td width="400"><input type="text" name="id"></td>
</tr>
<tr>
<td width="200">
<p align="right">비밀번호</p>
</td>
<td width="400"><input type="password" name="pwd"></td>
</tr>
<tr>
<td width="200">
<p align="right">이름</p>
</td>
<td width="400"><input type="text" name="name"></td>
</tr>
<tr>
<td width="200">
<p align="right">이메일</p>
</td>
<td width="400"><input type="email" name="email"></td>
</tr>
<tr>
<td width="200">
<p> </p>
</td>
<td width="400">
<input type="submit" value="가입하기" />
<input type="reset" value="다시입력" />
</td>
</tr>
</table>
</form>
</body>
</html>
* member1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<%
request.setCharacterEncoding("utf-8");
String id = request.getParameter("id");
String pwd = request.getParameter("pwd");
String name = request.getParameter("name");
String email = request.getParameter("email");
%>
<%-- <jsp:setProperty name="m" property="*" /> --%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 목록창</title>
</head>
<body>
<table align="center" width="100%">
<tr align="center" bgcolor="#99ccff">
<td width="7%">아이디</td>
<td width="7%">비밀번호</td>
<td width="5%">이름</td>
<td width="11%">이메일</td>
</tr>
<tr align="center">
<td><%=id %></td>
<td><%=pwd %></td>
<td><%=name %></td>
<td><%=email %></td>
</tr>
<tr align="center">
<td>${param.id }</td>
<td>${param.pwd }</td>
<td>${param.name }</td>
<td>${param.email }</td>
</tr>
</table>
</body>
</html>
- /memberForm.jsp 페이지에서 회원 정보를 입력하고 [가입하기] 버튼을 눌러 본다.
- 결과를 보면 정보가 두 번 출력된 것을 볼 수 있다. 첫 번째 회원 정보는 getParameter() 메서드로 가져온 후 출력한
것이고, 두 번째 회원 정보는 param 내장 객체로 출력한 결과다.
- 따라서 param 내장 객체를 사용하면 굳이 전송된 매개변수를 getParameter() 메서드를 이용하지 않고 바로 매개변수
이름으로 접근해서 값을 얻을 수 있다.
RequestScope 내장 객체
- request 객체와 동일한 기능을 한다.
- RequestScope 사용 예
* memberForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입창</title>
</head>
<body>
<form method="post" action="forward.jsp">
<h1 style="text-align:center">회원가입창</h1>
<table align="center">
<tr>
<td width="200">
<p align="right">아이디</p>
</td>
<td width="400"><input type="text" name="id"></td>
</tr>
<tr>
<td width="200">
<p align="right">비밀번호</p>
</td>
<td width="400"><input type="password" name="pwd"></td>
</tr>
<tr>
<td width="200">
<p align="right">이름</p>
</td>
<td width="400"><input type="text" name="name"></td>
</tr>
<tr>
<td width="200">
<p align="right">이메일</p>
</td>
<td width="400"><input type="email" name="email"></td>
</tr>
<tr>
<td width="200">
<p> </p>
</td>
<td width="400">
<input type="submit" value="가입하기" />
<input type="reset" value="다시입력" />
</td>
</tr>
</table>
</form>
</body>
</html>
* forward.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
request.setCharacterEncoding("utf-8");
request.setAttribute("address", "서울시");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>forward</title>
</head>
<body>
<jsp:forward page="member2.jsp"></jsp:forward>
</body>
</html>
* member2.jsp
- requestScope.address 라는 코드를 사용해 forward.jsp 안에서 request에 설정한 "address"에 대한 값을 가져온다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<%
request.setCharacterEncoding("utf-8");
/* String id = request.getParameter("id");
String pwd = request.getParameter("pwd");
String name = request.getParameter("name");
String email = request.getParameter("email"); */
%>
<%-- <jsp:setProperty name="m" property="*" /> --%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 목록창</title>
</head>
<body>
<table align="center" width="100%">
<tr align="center" bgcolor="#99ccff">
<td width="20%">아이디</td>
<td width="20%">비밀번호</td>
<td width="20%">이름</td>
<td width="20%">이메일</td>
<td width="20%">주소</td>
</tr>
<tr align="center">
<td>${param.id }</td>
<td>${param.pwd }</td>
<td>${param.name }</td>
<td>${param.email }</td>
<td>${requestScope.address }</td>
</tr>
</table>
</body>
</html>
- requestScope를 이용하면 request 객체에 바인딩된 데이터에 접근할 수 있다.
- 마찬가지로 session이나 application 객체에 바인딩된 데이터는 sessionScope나 applicationScope로 접근할 수 있다.
pageContext 내장 객체
- pageContext 객체는 javax.servlet.jsp.pageContext 클래스를 상속해 웹 컨테이너가 JSP 실행 시 자동으로 생성해서
제공하는 내장 객체이다.
- pageContext 객체의 편리한 기능을 알아보자. <a> 태그를 이용해 다른 서블릿이나 JSP를 요청하는 방법은
다음 두 가지이다.
1. 컨텍스트 이름을 직접 입력
<a href="/EL/implicit/memberForm.jsp">회원 가입하기</a>
2. getContextPath() 메서드를 이용해 컨텍스트 이름을가져오는 방법
<a href="<%=request.getContextPath() %>/implicit/memberForm.jsp">회원 가입하기</a>
- 첫 번째 방법은 컨텍스트 이름이 바뀌면 일일이 찾아서 수정해야 하는 단점이 있고, 두 번째 방법은 자바 코드가
사용되므로 화면 작업이 복잡해진다는 단점이 있다.
-그러나 pageContext 객체의 속성인 request의 contextPath 속성을 이용하면 쉽게 컨텍스트 이름을 가져올 수 있다.
pageContext 사용 예
* login.jsp
- <a href="${pageContext.request.contextPath }/implicit/memberForm.jsp">회원가입하기</a> 과 같이 사용해 컨텍스트의
이름을 가져온다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인창</title>
</head>
<body>
<form action="result.jsp">
아이디 : <input type="text" size=20 /><br>
비밀번호 : <input type="password" size=20 /><br>
<input type="submit" value="로그인" />
</form>
<br><br>
<!-- <a href="http://localhost:8082/EL/implicit/memberForm.jsp">회원가입하기</a> -->
<%-- <a href="<%=request.getContextPath() %>/implicit/memberForm.jsp">회원가입하기</a> --%>
<a href="${pageContext.request.contextPath }/implicit/memberForm.jsp">회원가입하기</a>
</body>
</html>
- 로그인 폼 하단에 있는 "회원가입하기" 링크를 클릭하면 정상적으로 회원가입 폼으로 이동한다.
표현 언어 빈 사용
- 표현 언어에서 빈 속에 접근하는 방법은다음과 같다.
${빈이름.속성이름}
- 빈에 회원 정보를 저장한 후 표현 언어를 이용해 빈의 회원 정보를 출력하는 예를 보자.
- 표현 언어에서는 getter를 사용하지 않고, 바로 빈 id 다음에 .(마침표) 연산자를 사용하여 속성에 바로 접근할 수 있다.
* memberForm.jsp
- form 태그 안의 action 값을 "member3.jsp"로 설정한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입창</title>
</head>
<body>
<form method="post" action="member3.jsp">
<h1 style="text-align:center">회원가입창</h1>
<table align="center">
<tr>
<td width="200">
<p align="right">아이디</p>
</td>
<td width="400"><input type="text" name="id"></td>
</tr>
<tr>
<td width="200">
<p align="right">비밀번호</p>
</td>
<td width="400"><input type="password" name="pwd"></td>
</tr>
<tr>
<td width="200">
<p align="right">이름</p>
</td>
<td width="400"><input type="text" name="name"></td>
</tr>
<tr>
<td width="200">
<p align="right">이메일</p>
</td>
<td width="400"><input type="email" name="email"></td>
</tr>
<tr>
<td width="200">
<p> </p>
</td>
<td width="400">
<input type="submit" value="가입하기" />
<input type="reset" value="다시입력" />
</td>
</tr>
</table>
</form>
</body>
</html>
* MemberBean.java
package servlet;
public class MemberBean {
String id;
String pwd;
String name;
String email;
public MemberBean() {}
public MemberBean(String id, String pwd, String name, String email) {
this.id = id;
this.pwd = pwd;
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
* member3.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<%
request.setCharacterEncoding("utf-8");
%>
<jsp:useBean id="m" class="servlet.MemberBean" scope="page" />
<jsp:setProperty name="m" property="*" /> <%-- 전송된 회원 정보를 빈의 속성에 설정 --%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 정보 출력창</title>
</head>
<body>
<table align="center" width="100%">
<tr align="center" bgcolor="#99ccff">
<td width="20%">아이디</td>
<td width="20%">비밀번호</td>
<td width="20%">이름</td>
<td width="20%">이메일</td>
</tr>
<%-- 빈 ID와 속성 이름으로 접근해 회원 정보를 출력 --%>
<tr align="center">
<td>${m.id }</td>
<td>${m.pwd }</td>
<td>${m.name }</td>
<td>${m.email }</td>
</tr>
</table>
</body>
</html>
- 결과는 회원가입 폼에 입력한 아이디, 비밀번호, 이름, 이메일이 memer3.jsp 페이지에 출력될 것이다.
Collection 객체 사용
- 표현 언어에서 Collection 객체에 접근할 때는 다음과 같은 형식을 사용한다.
${Collection객체이름[index].속성이름] }
사용 예
- Collection 객체 중 가장 많이 사용되는 ArrayList에 회원 정보 빈을 저장한 후 출력한다. 회원 가입창에서 전송된 회원
정보를 빈 m1에 저장한 후 다시 ArrayList에 저장한다. 그리고 자바 코드로 두 번째 MemberBean 객체를 생성한 후
회원 정보를 설정하여 ArrayList에 저장한다.그리고 인덱스로 각 속성에 순차적으로 접근해서 ArrayList의 값을 출력한다.
* memberForm.jsp
- form 태그 안의 action 값을 "member4.jsp"로 설정한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입창</title>
</head>
<body>
<form method="post" action="member4.jsp">
<h1 style="text-align:center">회원가입창</h1>
<table align="center">
<tr>
<td width="200">
<p align="right">아이디</p>
</td>
<td width="400"><input type="text" name="id"></td>
</tr>
<tr>
<td width="200">
<p align="right">비밀번호</p>
</td>
<td width="400"><input type="password" name="pwd"></td>
</tr>
<tr>
<td width="200">
<p align="right">이름</p>
</td>
<td width="400"><input type="text" name="name"></td>
</tr>
<tr>
<td width="200">
<p align="right">이메일</p>
</td>
<td width="400"><input type="email" name="email"></td>
</tr>
<tr>
<td width="200">
<p> </p>
</td>
<td width="400">
<input type="submit" value="가입하기" />
<input type="reset" value="다시입력" />
</td>
</tr>
</table>
</form>
</body>
</html>
* member4.jsp
- useBean 액션 태그를 통해 생성한 빈에는 회원가입 폼에서 전송된 정보가 담기고, m2 객체에는 생성자를 통해 생성된 MebmerBean이 저장된다.
- useBean 액션 태그로 ArrayList를 생성하고 m1, m2 객체를 리스트에 추가한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false" import="servlet.MemberBean"%>
<%
request.setCharacterEncoding("utf-8");
%>
<jsp:useBean id="m1" class="servlet.MemberBean" scope="page" />
<jsp:setProperty name="m1" property="*" />
<%-- membersList로 ArrayList 객체를 생성한다. --%>
<jsp:useBean id="membersList" class="java.util.ArrayList" />
<%
MemberBean m2 = new MemberBean("son","1234","손흥민","son@test.com");
membersList.add(m1);
membersList.add(m2);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 정보 출력창</title>
</head>
<body>
<table align="center" width="100%">
<tr align="center" bgcolor="#99ccff">
<td width="20%">아이디</td>
<td width="20%">비밀번호</td>
<td width="20%">이름</td>
<td width="20%">이메일</td>
</tr>
<%-- 빈 ID와 속성 이름으로 접근해 회원 정보를 출력 --%>
<tr align="center">
<td>${membersList[0].id }</td>
<td>${membersList[0].pwd }</td>
<td>${membersList[0].name }</td>
<td>${membersList[0].email }</td>
</tr>
<tr align="center">
<td>${membersList[1].id }</td>
<td>${membersList[1].pwd }</td>
<td>${membersList[1].name }</td>
<td>${membersList[1].email }</td>
</tr>
</table>
</body>
</html>
- /memberForm.jsp 페이지에서 정보를 입력한 후 [가입하기] 버튼을 누르면 member4.jsp 페이지에 입력한 정보와 추가로
생성한 빈에 대한 정보가 출력될 것이다.
HashMap 사용
- 다음은 표현 언어에서 자바 HashMap에 저장된 객체에 접근하는 방법이다.
${HashMap객체이름.name }
* member5.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false" import="servlet.MemberBean"%>
<%
request.setCharacterEncoding("utf-8");
%>
<jsp:useBean id="m1" class="servlet.MemberBean" scope="page" />
<jsp:setProperty name="m1" property="*" />
<%-- membersList로 ArrayList 객체를 생성한다. --%>
<jsp:useBean id="membersList" class="java.util.ArrayList" />
<jsp:useBean id="membersMap" class="java.util.HashMap" />
<%
// HashMap에 key/value 쌍으로 회원 정보를 저장한다.
membersMap.put("id", "park2");
membersMap.put("pwd", "4321");
membersMap.put("name", "박지성");
membersMap.put("email", "park2@test.com");
MemberBean m2 = new MemberBean("son","1234","손흥민","son@test.com");
membersList.add(m1);
membersList.add(m2);
membersMap.put("membersList", membersList);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 정보 출력창</title>
</head>
<body>
<table align="center" width="100%">
<tr align="center" bgcolor="#99ccff">
<td width="20%">아이디</td>
<td width="20%">비밀번호</td>
<td width="20%">이름</td>
<td width="20%">이메일</td>
</tr>
<tr align="center">
<td>${membersMap.id }</td>
<td>${membersMap.pwd }</td>
<td>${membersMap.name }</td>
<td>${membersMap.email }</td>
</tr>
<%-- 빈 ID와 속성 이름으로 접근해 회원 정보를 출력 --%>
<tr align="center">
<td>${membersMap.membersList[0].id }</td>
<td>${membersMap.membersList[0].pwd }</td>
<td>${membersMap.membersList[0].name }</td>
<td>${membersMap.membersList[0].email }</td>
</tr>
<tr align="center">
<td>${membersMap.membersList[1].id }</td>
<td>${membersMap.membersList[1].pwd }</td>
<td>${membersMap.membersList[1].name }</td>
<td>${membersMap.membersList[1].email }</td>
</tr>
</table>
</body>
</html>
- /memberForm.jsp 페이지에서 정보를 입력한 후 [가입하기] 버튼을 누르면 member5.jsp 페이지에 입력한 정보와 추가로
생성한 빈에 대한 정보가 출력될 것이다. memberForm.jsp 속 form 태그 안의 action 값은 "member5.jsp"로 설정한다.
has-a 관계 빈 사용
- 객체가 다른 객체를 속성으로 가지는 경우를 has-a 관계라고 한다.
- 사용 형식은 다음과 같이 '속성 이름'과 .(마침표) 연산자로 자식 속성에 접근하면 된다.
${ 부모빈이름.자식속성이름.속성이름 }
* MebmerBean.java
package servlet;
public class MemberBean {
String id;
String pwd;
String name;
String email;
private Address address;
public MemberBean() {}
public MemberBean(String id, String pwd, String name, String email) {
this.id = id;
this.pwd = pwd;
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
* Address.java
package servlet;
public class Address {
private String city;
private String zipCode;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
}
* member6.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false" import="servlet.MemberBean"%>
<%
request.setCharacterEncoding("utf-8");
%>
<jsp:useBean id="m" class="servlet.MemberBean" scope="page" />
<jsp:setProperty name="m" property="*" />
<jsp:useBean id="addr" class="servlet.Address" scope="page"/>
<jsp:setProperty name="addr" property="city" value="서울" />
<jsp:setProperty name="addr" property="zipCode" value="12345" />
<%
m.setAddress(addr);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 정보 출력창</title>
</head>
<body>
<table align="center" width="100%">
<tr align="center" bgcolor="#99ccff">
<td width="20%">아이디</td>
<td width="20%">비밀번호</td>
<td width="20%">이름</td>
<td width="20%">이메일</td>
<td width="10%">도시</td>
<td width="10%">우편번호</td>
</tr>
<tr align="center">
<td>${m.id }</td>
<td>${m.pwd }</td>
<td>${m.name }</td>
<td>${m.email }</td>
<td><%=m.getAddress().getCity() %></td>
<td><%=m.getAddress().getZipCode() %></td>
</tr>
<tr align="center">
<td>${m.id }</td>
<td>${m.pwd }</td>
<td>${m.name }</td>
<td>${m.email }</td>
<td>${m.address.city }</td>
<td>${m.address.zipCode }</td>
</tr>
</table>
</body>
</html>
- /memberForm.jsp 페이지에서 정보를 입력한 후 [가입하기] 버튼을 누르면 member6.jsp 페이지에 도시와 우편번호가
추가되어 출력될 것이다. memberForm.jsp 속 form 태그 안의 action 값은 "member6.jsp"로 설정한다.
- member6.jsp에서 다른 방법으로 주소와 우편번호를 출력하는데, 첫 번째는 표현식을 이용해 getter를 두 번 호출해 표시했다. 이 방법은 불편하며, 두 번째는 빈 이름만을 이용해 .(마침표) 연산자로 주소 정보를 표시했다.
이는 훨씬 간단하게 코드를 작성할 수 있게 된다.
'JSP' 카테고리의 다른 글
[JSP]커스텀 태그 (0) | 2024.08.15 |
---|---|
[JSP]표현 언어 바인딩 속성 출력 (0) | 2024.08.15 |
[JSP]표현 언어 (0) | 2024.08.15 |
[JSP]액션태그 (0) | 2024.08.14 |
[JSP]Welcome 파일 지정 (0) | 2024.08.14 |
- Total
- Today
- Yesterday
- JSP
- 스프링
- 제이쿼리
- 미들웨어
- el
- Servlet
- react
- script element
- Network
- 서블릿
- CSS
- FMT
- 서브넷팅
- 네트워크
- html css
- a 태그
- Binding
- Session
- Redux
- 세션
- 내장객체
- nodejs
- 리액트
- CSS 속성
- Java Server Page
- Spring MVC
- HTML
- httpServletRequest
- Spring
- javaserverpage
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |