티스토리 뷰
[WEB] 회원가입 폼 만들기 / getParameterValues() , trim(), join(), contains()
Happyoon ~ 2021. 11. 25. 02:48" 테이블 만들기 "
form_test 테이블
CREATE TABLE form_test(
nick VARCHAR2(20) PRIMARY KEY,
email CHAR(3),
concern VARCHAR2(10),
lan VARCHAR2(30),
comm CLOB
);
signup.jsp 페이지에서 폼 전송되는 내용을 아래의 테이블에 저장되는 기능을 구현해 보세요.
lan 칼럼에는 선택하지 않으면 NULL 을 넣고
Java 를 선택하면 Java
Java 와 Python 을 선택하면 Java,Python
Java 와 Python 과 C++ 를 선택하면 Java,Python,C++
문자열이 저장되도록 하세요.
" 회원가입 폼 만들기 "
Step03_form/users/signup_form.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>index.jsp</title>
</head>
<body>
<div class= "container">
<h1>회원 가입 폼 입니다.</h1>
<form action="signup.jsp" method="get">
닉네임 <input type="text" name="nick" /><br /> 이메일 수신 여부 <label>
<input type="radio" name="email" value="yes" checked /> 네
</label> <label> <input type="radio" name="email" value="no" /> 아니요
</label> <br /> 관심사 <select name="concern">
<option value="">선택</option>
<option value="game">게임</option>
<option value="movie">영화</option>
<option value="etc">기타</option>
</select> <br /> 배우고 싶은 언어 체크 <label> <input type="checkbox"
name="language" value="Java" /> 자바
</label> <label> <input type="checkbox" name="language"
value="Python" /> 파이썬
</label> <label> <input type="checkbox" name="language" value="C++" />
C++
</label> <br /> 남기고 싶은 말
<textarea name="comment" cols="30" rows="10"></textarea>
<br />
<button type="submit">가입</button>
</form>
</div>
</body>
</html>
signup_form.jsp는 WebContent의 users 폴더에 위치한다.
따라서, signup.jsp는 users폴더에 위치해야 한다.
실행 결과
닉네임 : String
이메일 수신 여부 : radio
관심사 : select
배우고 싶은 언어 : checkbox
남기고 싶은 말 : String
이메일 수신 여부, 배우고 싶은 언어를 fieldset으로 묶어주자.
또한 부트스트랩을 이용하여 CSS를 적용해보자.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/users/signup_form.jsp</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.min.css" />
<style>
.legend-style{
font-size:16px;
}
</style>
<script src="${pageContext.request.contextPath}/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class= "container">
<!--
signup.jsp 페이지에서 폼 전송되는 내용을 아래의 테이블에 저장되는 기능을 구현해 보세요.
lan 칼럼에는 선택하지 않으면 NULL 을 넣고
Java 를 선택하면 Java
Java 와 Python 을 선택하면 Java,Python
Java 와 Python 과 C++ 를 선택하면 Java,Python,C++
문자열이 저장되도록 하세요.
CREATE TABLE form_test(
nick VARCHAR2(20) PRIMARY KEY,
email CHAR(3),
concern VARCHAR2(10),
lan VARCHAR2(30),
comm CLOB
);
-->
<h1>회원 가입 폼 입니다.</h1>
<form action="signup.jsp" method="get">
<div class = "mb-3">
<label class="form-label" for="nick">닉네임</label>
<input class="form-control" type="text" name="nick" />
</div>
<fieldset>
<legend class="legend-style">이메일 수신 여부</legend>
<div class="form-check form-check-inline">
<input class="form-check-input" id="emailYes" type="radio" name="email" value="yes" checked/>
<label class="form-check-label" for="emailYes">네</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" id="emailNo" type="radio" name="email" value="no" />
<label class="form-check-label" for="emailNo">아니요</label>
</div>
</fieldset>
<div class="mb-3">
<label class="form-label" for="concern">관심사</label>
<select class="form-select" name="concern" id="concern">
<option value="">선택</option>
<option value="game">게임</option>
<option value="movie">영화</option>
<option value="etc">기타</option>
</select>
</div>
<fieldset>
<legend class="legend-style">배우고 싶은 언어</legend>
<div class="form-check">
<input class="form-check-input" id="language1" type="checkbox" name="language" value="Java"/>
<label class="form-check-label" for="language1">자바</label>
</div>
<div class="form-check">
<input class="form-check-input" id="language2" type="checkbox" name="language" value="Python"/>
<label class="form-check-label" for="language2">파이썬</label>
</div>
<div class="form-check">
<input class="form-check-input" id="language3" type="checkbox" name="language" value="C++"/>
<label class="form-check-label" for="language3">C++</label>
</div>
</fieldset>
<br />
<div class="mb-3">
<label class ="form-label" for="comment">남기고 싶은 말</label>
<textarea class="form-control" name="comment" id="comment"></textarea>
</div>
<button class="btn btn-success type="submit">가입</button>
<button class="btn btn-warning type="reset">취소</button>
</form>
</div>
</body>
</html>
결과
floating, button CSS 적용한 signup_form2.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/users/signup_form2.jsp</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.min.css" />
<style>
.legend-style{
font-size: 16px;
}
</style>
<script src="${pageContext.request.contextPath}/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container">
<!--
signup.jsp 페이지에서 폼 전송되는 내용을 아래의 테이블에 저장되는 기능을 구현해 보세요.
lan 칼럼에는 선택하지 않으면 NULL 을 넣고
Java 를 선택하면 Java
Java 와 Python 을 선택하면 Java,Python
Java 와 Python 과 C++ 를 선택하면 Java,Python,C++
문자열이 저장되도록 하세요.
CREATE TABLE form_test(
nick VARCHAR2(20) PRIMARY KEY,
email CHAR(3),
concern VARCHAR2(10),
lan VARCHAR2(30),
comm CLOB
);
-->
<h1>회원 가입 폼 입니다.</h1>
<form action="signup.jsp" method="get">
<!--
input 요소를 label 요소보다 먼저 배치하고 placehoder 속성도 추가해야 form-floating 이 동작한다.
-->
<div class="form-floating mb-3">
<input class="form-control" type="text" name="nick" id="nick" placeholder="닉네임 입력..."/>
<label class="form-label" for="nick">닉네임</label>
</div>
<fieldset>
<legend class="legend-style">이메일 수신여부</legend>
<div class="btn-group mb-3">
<input class="btn-check" id="emailYes" type="radio" name="email" value="yes" checked/>
<label class="btn btn-outline-success btn-sm" for="emailYes">네</label>
<input class="btn-check" id="emailNo" type="radio" name="email" value="no" />
<label class="btn btn-outline-danger btn-sm" for="emailNo">아니요</label>
</div>
</fieldset>
<div class="form-floating mb-3">
<select class="form-select" name="concern" id="concern">
<option value="">선택</option>
<option value="game">게임</option>
<option value="movie">영화</option>
<option value="etc">기타</option>
</select>
<label class="form-label" for="concern">관심사</label>
</div>
<fieldset>
<legend class="legend-style">배우고 싶은 언어</legend>
<div class="btn-group mb-3">
<input class="btn-check" id="language1" type="checkbox" name="language" value="Java"/>
<label class="btn btn-outline-secondary btn-sm" for="language1">자바</label>
<input class="btn-check" id="language2" type="checkbox" name="language" value="Python"/>
<label class="btn btn-outline-secondary btn-sm" for="language2">파이썬</label>
<input class="btn-check" id="language3" type="checkbox" name="language" value="C++"/>
<label class="btn btn-outline-secondary btn-sm" for="language3">C++</label>
</div>
</fieldset>
<div class="form-floating mb-3">
<textarea style="height:150px;" placeholder="leave comment!" class="form-control" id="comment" name="comment" ></textarea>
<label class="form-label" for="comment">남기고 싶은말</label>
</div>
<button class="btn btn-success" type="submit">가입</button>
<button class="btn btn-warning" type="reset">취소</button>
</form>
</div>
</body>
</html>
결과
" DTO / DAO 만들기 "
UsersDto.java
package test.users.dto;
public class UsersDto {
private String nick;
private String email;
private String concern;
private String lan;
private String comm;
//디폴트 생성자
public UsersDto() {
}
public UsersDto(String nick, String email, String concern, String lan, String comm) {
super();
this.nick = nick;
this.email = email;
this.concern = concern;
this.lan = lan;
this.comm = comm;
}
public String getNick() {
return nick;
}
public void setNick(String nick) {
this.nick = nick;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getConcern() {
return concern;
}
public void setConcern(String concern) {
this.concern = concern;
}
public String getLan() {
return lan;
}
public void setLan(String lan) {
this.lan = lan;
}
public String getComm() {
return comm;
}
public void setComm(String comm) {
this.comm = comm;
}
}
UsersDao.java
package test.users.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import test.users.dto.UsersDto;
import test.util.DbcpBean;
public class UsersDao {
private static UsersDao dao;
private UsersDao() {}
public static UsersDao getInstance() {
if(dao==null) {
dao=new UsersDao();
}
return dao;
}
//회원정보를 DB 에 저장하는 메소드
public boolean insert(UsersDto dto) {
//필요한 객체를 담을 지역 변수 미리 만들기
Connection conn = null;
PreparedStatement pstmt = null;
int flag = 0;
try {
//Connection 객체의 참조값 얻어오기
conn = new DbcpBean().getConn();
//실행할 sql 문의 뼈대 미리 준비하기
String sql = "INSERT INTO form_test"
+ " (nick, email, concern, lan, comm)"
+ " VALUES( ?, ?, ?, ?, ?)";
//PreparedStatement 객체의 참조값 얻어오기
pstmt = conn.prepareStatement(sql);
//? 에 필요한값 바인딩하기
pstmt.setString(1, dto.getNick());
pstmt.setString(2, dto.getEmail());
pstmt.setString(3, dto.getConcern());
pstmt.setString(4, dto.getLan());
pstmt.setString(5, dto.getComm());
//sql 문 실행하기 (INSERT, UPDATE, DELETE)
flag = pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (SQLException e) {
}
}
if (flag > 0) {
return true;
} else {
return false;
}
}
//인자로 전달되는 nick 을 이용해서 해당회원 한명의 정보를 리턴하는 메소드
public UsersDto getData(String nick) {
UsersDto dto=null;
//필요한 객체를 담을 지역 변수 미리 만들기
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
//Connection 객체의 참조값 얻어오기
conn = new DbcpBean().getConn();
//실행할 sql 문의 뼈대 미리 준비하기
String sql = "SELECT email, concern, lan, comm"
+ " FROM form_test"
+ " WHERE nick=?";
//PreparedStatement 객체의 참조값 얻어오기
pstmt = conn.prepareStatement(sql);
//? 에 필요한값 바인딩하기
pstmt.setString(1, nick);
//select 문 수행하고 결과를 ResultSet 으로 받아오기
rs = pstmt.executeQuery();
while (rs.next()) {
dto=new UsersDto();
dto.setNick(nick);
dto.setEmail(rs.getString("email"));
dto.setConcern(rs.getString("concern"));
dto.setLan(rs.getString("lan"));
dto.setComm(rs.getString("comm"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (rs != null)
rs.close();
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (SQLException e) {
}
}
return dto;
}
}
회원 정보를 DB에 저장하는 메소드, nick을 인자로해서 해당 회원 한명의 정보를 리턴하는 메소드 구현
" 폼에서 입력받은 값 DB에 저장하기 "
Step03_form/users/signup.jsp
1. 인코딩 설정과 getParameter로 파라미터 값 읽어오기(language, comment 제외)
<%
//한글 깨지지 않도록
request.setCharacterEncoding("utf-8");
//닉네임 읽어오기
String nick=request.getParameter("nick");
//이메일 수신여부 "yes" or "no"
String email=request.getParameter("email");
//관심사 읽어오기 "" or "game" or "movie" or "etc"
String concern=request.getParameter("concern");
%>
예시
signup_from.jsp에서 getParameter안에 들어가는 인자는 name 속성이고 getParameter로 읽어오는 값은 value이다.
<input type="radio" name="email" value="yes" checked /> 네
2. language 파라미터 값 읽어오기
- checkbox 는 동일한 파라미터 명으로 여러개의 값이 넘어올 가능성이 있기때문
getParameterValues("파라미터명") 메소드를 이용해서 값을 읽어와야 한다.
- 체크된 체크박스가 하나도 없으면 null 일수도 있다. (NullPointerException 주의!)
(null 체크는 아래 코드에서!)
- null 이 아니라면 language 는 ["Java"] or ["Python"] or ["C++"] or ["Java","Python"] or ["Java","C++"] or ["Python","C++"] or ["Java","Phthon","C++"] 중에 하나이다.
String[] language=request.getParameterValues("language");
3. DB에 저장하기 위해 배열을 문자열로 변환하기
DB 에 저장할때 배열째로 저장할수 없으니 배열에 저장된 내용을 문자열 1줄로 변환한다.
ex)
Java,Phthon,C++
or
Java/Python/C++
...
1줄로 변환한후 DB 에 하나의 칼럼에 저장한다.
- 변환하는 방법 => join() 메소드
String result = String.join("구분자", String 배열);
String savedLan = "";
if(language!=null){
savedLan = String.join(",", language);
System.out.println(savedLan);
}
4. comment 읽어오기
남기고 싶은말은 textarea 에 입력했기 때문에 개행기호(\r\n)도 함께 읽어온다
(운영체제마다 개행기호가 다른데 윈도우는 \r\n이다.)
String comment=request.getParameter("comment");
5. DB에 저장하기 / insert
//DB 에 저장하기
UsersDto dto=new UsersDto();
dto.setNick(nick);
dto.setEmail(email);
dto.setConcern(concern);
dto.setLan(savedLan);
dto.setComm(comment);
boolean isSuccess=UsersDao.getInstance().insert(dto);
전체 코드 / signup.jsp
<%@page import="test.users.dao.UsersDao"%>
<%@page import="test.users.dto.UsersDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//한글 깨지지 않도록
request.setCharacterEncoding("utf-8");
//닉네임 읽어오기
String nick=request.getParameter("nick");
//이메일 수신여부 "yes" or "no"
String email=request.getParameter("email");
//관심사 읽어오기 "" or "game" or "movie" or "etc"
String concern=request.getParameter("concern");
/*
배우고 싶은 언어 읽어오기
- checkbox 는 동일한 파라미터 명으로 여러개의 값이 넘어올 가능성이 있기때문에
.getParameterValues("파라미터명") 메소드를 이용해서 값을 읽어와야 한다.
- 체크된 체크박스가 하나도 없으면 null 일수도 있다. (NullPointerException 주의!)
- null 이 아니라면 language 는
["Java"] or ["Python"] or ["C++"] or ["Java","Phtyon"] or ["Java","C++"]
or ["Python","C++"] or ["Java","Phthon","C++"] 중에 하나이다.
*/
String[] language=request.getParameterValues("language");
/*
DB 에 저장할때 배열째로 저장할수 없으니 배열에 저장된 내용을 문자열 1줄로 변환한다.
ex)
Java,Phthon,C++
or
Java/Python/C++
...
1줄로 변환한후 DB 에 하나의 칼럼에 저장한다.
- 변환하는 방법
String result = String.join("구분자", String 배열);
*/
String savedLan = "";
if(language!=null){
savedLan = String.join(",", language);
System.out.println(savedLan);
}
//남기고 싶은말 textarea 에 입력했기 때문에 개행기호(\r\n)도 함께 읽어온다
String comment=request.getParameter("comment");
//DB 에 저장하기
UsersDto dto=new UsersDto();
dto.setNick(nick);
dto.setEmail(email);
dto.setConcern(concern);
dto.setLan(savedLan);
dto.setComm(comment);
boolean isSuccess=UsersDao.getInstance().insert(dto);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/users/signup.jsp</title>
</head>
<body>
<div class="container">
<h1>알림</h1>
<%if(isSuccess){ %>
<p>
<strong><%=nick %></strong> 회원님 가입 되었습니다.
<a href="info.jsp?nick=<%=nick%>">가입정보 확인하기</a>
</p>
<%}else{ %>
<p>
가입 실패!
<a href="signup_form.jsp">다시 시도</a>
</p>
<%} %>
</div>
</body>
</html>
" 입력한 폼 내용 폼 형태에 출력하기 "
1. "nick" 에 해당되는 회원정보 읽어오고, null 체크
<%
//파라미터로 넘어오는 nick
String nick=request.getParameter("nick");
//1. nick 에 해당되는 회원정보를 읽어와서
UsersDto dto=UsersDao.getInstance().getData(nick);
if(dto.getConcern()==null){
dto.setConcern("");
}
if(dto.getLan()==null){
dto.setLan("");
}
//2. 가입정보를 응답한다.
System.out.println(dto.getEmail());
%>
※ 주의! ※
Java에서 ""와 null은 엄연히 다르지만,(참조값의 유무) 오라클에서 ""와 null은 모두 null이다.
따라서, ""이면 db에 추가된 것을 불러올 때 nullPointerException이 발생할 수 있기 때문에, 이에 대한 체크도 해주어야 한다.
2. 이메일 수신 여부 출력 (radio)
이메일 수신 여부
<label>
<input type="radio" name="email" value="yes" <%=dto.getEmail().equals("yes") ? "checked":"" %>/> 네
</label>
<label>
<input type="radio" name="email" value="no" <%=dto.getEmail().trim().equals("no") ? "checked":"" %>/> 아니요
</label>
<br/>
삼항 연산자를 이용하여 checked 되어야 할 때를 구분한다.
또한, email을 char형으로 db에 저장하여, value가 "no"일 때도 뒤에 공백이 들어가므로.trim() 메소드로 공백을 제거한 후 equals() 메소드로 value와 비교한다.
3. concern 출력 (select)
<select name="concern">
<option value="">선택</option>
<option value="game" <%=dto.getConcern().equals("game") ? "selected":"" %>>게임</option>
<option value="movie" <%=dto.getConcern().equals("movie") ? "selected":"" %>>영화</option>
<option value="etc" <%=dto.getConcern().equals("etc") ? "selected":"" %>>기타</option>
</select>
"선택"으로 제출 시 value가 ""이므로 db에 null로 저장되어 db에서 정보를 불러올 때 nullPointerException이 발생한다.
따라서, 위의 자바코드 부분에서 null 체크를 해주었다.
마찬가지로, 삼항연산자를 이용하여 value와 equals()로 비교한 후, 동일하다면 selected 되도록 프로그래밍한다.
4. 배우고 싶은 언어 출력 (checkbox)
<label>
<input type="checkbox" name="language" value="Java" <%=dto.getLan().contains("Java") ? "checked":"" %>/> 자바
</label>
<label>
<input type="checkbox" name="language" value="Python" <%=dto.getLan().contains("Python") ? "checked":"" %>/> 파이썬
</label>
<label>
<input type="checkbox" name="language" value="C++" <%=dto.getLan().contains("C++") ? "checked":"" %>/> C++
</label>
<br/>
dto.getLan()을 하면 "Java,Python" 등의 문자열 형식으로 불러온다.
따라서, contains() 메소드를 활용하여 해당 value값이 있으면 checked 되도록 프로그래밍한다.
5. 남기고 싶은 말 출력 (String)
<textarea name="comment" cols="30" rows="10"><%=dto.getComm()==null ? "" : dto.getComm() %></textarea>
그냥 =dto.getComm()을 해주어도 되지만, null 일경우 "null"을 출력하므로 삼항연산자를 이용하여 null이면 출력되지 않도록 프로그래밍했다.
전체 코드 / Step03_form/users/info.jsp
<%@page import="test.users.dao.UsersDao"%>
<%@page import="test.users.dto.UsersDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//파라미터로 넘어오는 nick
String nick=request.getParameter("nick");
//1. nick 에 해당되는 회원정보를 읽어와서
UsersDto dto=UsersDao.getInstance().getData(nick);
if(dto.getConcern()==null){
dto.setConcern("");
}
if(dto.getLan()==null){
dto.setLan("");
}
//2. 가입정보를 응답한다.
System.out.println(dto.getEmail());
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/users/info.jsp</title>
</head>
<body>
<div class="container">
<h1>가입된 정보 입니다.</h1>
닉네임
<input type="text" name="nick" value="<%=dto.getNick()%>"/><br/>
이메일 수신 여부
<label>
<input type="radio" name="email" value="yes" <%=dto.getEmail().equals("yes") ? "checked":"" %>/> 네
</label>
<label>
<input type="radio" name="email" value="no" <%=dto.getEmail().trim().equals("no") ? "checked":"" %>/> 아니요
</label>
<br/>
관심사
<select name="concern">
<option value="">선택</option>
<option value="game" <%=dto.getConcern().equals("game") ? "selected":"" %>>게임</option>
<option value="movie" <%=dto.getConcern().equals("movie") ? "selected":"" %>>영화</option>
<option value="etc" <%=dto.getConcern().equals("etc") ? "selected":"" %>>기타</option>
</select>
<br/>
배우고 싶은 언어 체크
<label>
<input type="checkbox" name="language" value="Java" <%=dto.getLan().contains("Java") ? "checked":"" %>/> 자바
</label>
<label>
<input type="checkbox" name="language" value="Python" <%=dto.getLan().contains("Python") ? "checked":"" %>/> 파이썬
</label>
<label>
<input type="checkbox" name="language" value="C++" <%=dto.getLan().contains("C++") ? "checked":"" %>/> C++
</label>
<br/>
남기고 싶은 말
<textarea name="comment" cols="30" rows="10"><%=dto.getComm()==null ? "" : dto.getComm() %></textarea>
</div>
</body>
</html>
결과
'WEB > Java BackEnd' 카테고리의 다른 글
[WEB-jsp/servlet] 로그인 / 로그아웃 구현하기 (0) | 2021.11.30 |
---|---|
[WEB-jsp/servlet] jsp와 servlet에서 session 구현하기 (0) | 2021.11.30 |
[WEB-jsp/servlet] jsp에 jsp 삽입하기 (0) | 2021.11.24 |
[WEB] DBcpBean(DataBase Connection Pool Bean) (0) | 2021.11.24 |
[WEB-jsp/servlet] jsp (0) | 2021.11.24 |
- Total
- Today
- Yesterday
- 프로그래머스
- baekjoon
- 자바스크립트
- python
- Java
- append
- 덱
- CSS
- 브루트 포스
- Case When
- 단계별로풀어보기
- 파이썬
- 백준
- 큐
- Oracle
- javascript
- brute force
- 고득점 키트
- Django
- 스프링
- html
- 문자열
- 정렬
- R
- 장고
- jQuery
- bootstrap
- web
- jsp
- 자바
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |