day29 - 0902
URL redirection
- 강제 이동 처리
- @RequestMapping 적용 메소드의 리턴 경로가 'redirect:' 로 시작하면 서버에서 해당 경로로 강제 이동 시킨다
- 'redirect:' 뒤의 문자열이 '/' 로 시작하면, 웹 어플리케이션을 기준으로 절대 경로로 인식한다
- 'redirect:' 뒤의 문자열이 '/' 로 시작하지 않으면 현재 경로를 기준으로 상대 경로로 인식한다
- POST 요청의 반복을 방지하기 위해 POST 요청 후 반드시 redirect 처리를 해야 한다
JdbcTemplate 클래스를 이용한 CRUD 기능 구현
준비. Model이 될 클래스 정의 (DTO 또는 VO)
1. Dao(Interface 형태)에서 추상 메소드 선언
2. DaoImpl(class 형태)에서 추상 메소드 구현
3. Controller에서 등록 페이지 Mapping 설정
준비 - Model이 될 클래스 정의 (DTO 또는 VO)
- POCKET_MONSTER 테이블 컬럼명 확인
- DTO 클래스 생성 : 필드, 생성자, getter&setter 포함 (toString 오버라이딩은 선택 사항)
package com.kh.springhome.entity;
import java.sql.Date;
public class MusicDto {
// 필드
int musicNo;
String musicTitle;
String musicArtist;
String musicAlbum;
int musicPlay;
Date releaseTitle;
// 생성자
public MusicDto() {
super();
}
// getter & setter
public int getMusicNo() {
return musicNo;
}
public void setMusicNo(int musicNo) {
this.musicNo = musicNo;
}
public String getMusicTitle() {
return musicTitle;
}
public void setMusicTitle(String musicTitle) {
this.musicTitle = musicTitle;
}
public String getMusicArtist() {
return musicArtist;
}
public void setMusicArtist(String musicArtist) {
this.musicArtist = musicArtist;
}
public String getMusicAlbum() {
return musicAlbum;
}
public void setMusicAlbum(String musicAlbum) {
this.musicAlbum = musicAlbum;
}
public int getMusicPlay() {
return musicPlay;
}
public void setMusicPlay(int musicPlay) {
this.musicPlay = musicPlay;
}
public Date getReleaseTitle() {
return releaseTitle;
}
public void setReleaseTitle(Date releaseTitle) {
this.releaseTitle = releaseTitle;
}
// toString 오버라이딩
@Override
public String toString() {
return "MusicDto [musicNo=" + musicNo + ", musicTitle=" + musicTitle + ", musicArtist=" + musicArtist
+ ", musicAlbum=" + musicAlbum + ", musicPlay=" + musicPlay + ", releaseTitle=" + releaseTitle + "]";
}
}
MUSIC 테이블 - 등록(INSERT)
1. Dao(Interface 형태)에서 추상 메소드 선언
- ResultSet을 반환받아야 하는 조회(SELECT)와 달리 등록(INSERT)는 명령 실행 후 반환해야 할 값이 없으므로
반환형은 없으며(void) 매개변수로는 MusicDto의 인스턴스 musicDto를 사용한다
package com.kh.springhome.repository;
public interface MusicDao {
// 추상 메소드 - 등록(insert)
void insert(MusicDto musicDto);
}
2. DaoImpl(class 형태)에서 추상 메소드 구현
- SQL문을 실행하기 위해 가장 먼저 JdbcTemplate 클래스의 인스턴스를 스프링 IoC 컨테이너에 등록해야 한다
- 등록(insert) 메소드 구현
1) SQL문 생성
2) SQL문의 바인드 변수(?)에 들어갈 매개변수가 포함된 Object 배열 생성
3) JdbcTemplate의 update(String sql, Object... args) 메소드 실행
package com.kh.springhome.repository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.kh.springhome.entity.MusicDto;
@Repository
public class MusicDaoImpl implements MusicDao {
// 의존성 주입
@Autowired
JdbcTemplate jdbcTemplate;
// 추상 메소드 오버라이딩 - 등록(insert)
@Override
public void insert(MusicDto musicDto) {
// 1) SQL문 생성
String sql = "insert into music("
+ "music_no, "
+ "music_title, "
+ "music_artist, "
+ "music_album, "
+ "music_play, "
+ "release_title) "
+ "values(music_seq.nextval, ?, ?, ?, 0, ?)";
// 2) SQL문의 바인드 변수(?)에 들어갈 매개변수 배열 생성
Object[] param = new Object[] {musicDto.getMusicTitle(), musicDto.getMusicArtist(),
musicDto.getMusicAlbum(), musicDto.getReleaseTitle()};
// 3) JdbcTemplate의 update(String sql, Object... args) 메소드 실행
jdbcTemplate.update(sql, param);
}
}
3. Controller에서 Mapping 설정
1) 등록 Mapping (미완성 Controller)
localhost:포트번호/music/insert 인 URL로 접속할 때
/WEB-INF/views/music/insert.jsp 경로에 있는 view를 띄우도록 주소를 Mapping한다
package com.kh.springhome.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.kh.springhome.entity.MusicDto;
import com.kh.springhome.repository.MusicDao;
@Controller
@RequestMapping("/music")
public class MusicController {
@Autowired
private MusicDao musicDao;
// 1. 등록 (insert)
// 1-1. 등록 Mapping
@GetMapping("/insert")
public String insert() {
// 등록 페이지 (insert.jsp)로 연결
// return "/WEB-INF/views/music/insert.jsp";
return "music/insert";
}
// 1-2. 등록 Mapping에 DTO 전달 및 DB 처리
// DB에서 등록(insert) 처리 후 등록 완료 Mapping으로 이동
@PostMapping("/insert")
public String insert(@ModelAttribute MusicDto musicDto) {
// 1) DB에서 등록(insert) 실행
musicDao.insert(musicDto);
// 2) 등록 처리 후 등록 완료(insert_success) Mapping으로 강제이동(redirect) (데이터 중복 방지)
return "redirect:insert_success";
}
// 1-3. 등록 완료 Mapping
@GetMapping("/insert_success")
public String insertSuccess() {
// 등록 완료 페이지(insertResult.jsp)로 연결
// return "/WEB-INF/views/music/insertSuccess";
return "music/insertSuccess";
}
}
2) View에서 표시 형식 정의
- View에서 값을 입력한 후 insert 페이지에 post 방식으로 전송한다
- <form>의 action 속성은 값을 전송할 주소, method 속성은 전송 방식을 의미한다
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>음원 등록</title>
</head>
<body>
<div align = "center">
<h1>음원 등록</h1>
<form action = "insert" method = "post">
제목 : <input name = "musicTitle" placeholder = "음원명" type = "text" required><br><br>
가수 : <input name = "musicArtist" placeholder = "아티스트명" type = "text" required><br><br>
앨범 : <input name = "musicAlbum" placeholder = "앨범명" type = "text" required><br><br>
발매일 : <input name = "releaseTitle" placeholder = "발매일" type = "date" required><br><br>
<button>등록</button>
</form>
</div>
</body>
</html>
<div></div> 태그 : 웹 페이지의 전체적인 레이아웃 정의
- align : 정렬 (left, right, center)
<form></form> 태그 : 백엔드에 전송할 값을 정의
- action : form 데이터가 전송되는 백엔드 URL
- method : form 데이터를 전송할 방식 (GET / POST)
<input> 태그 : form 내부에 위치하며 사용자 입력을 받는 역할
- type : 입력의 종류 (text, date 등)
- name : 입력의 변수명
- placeholder : 미입력 상태에서 표시할 문구
- required : 필수 입력 항목으로 설정
<button></button> 태그 : 클릭할 수 버튼 정의
- type : 버튼의 종류 (submit - form 전송용)
3) DB 처리 및 redirect (완성된 Controller)
- View에서 POST로 전송받은 데이터를 통해 DB 처리 수행
- 매개변수에 @RequestParam 또는 @ModelAttribute 어노테이션을 붙여 View에서 전송한 값을 받을 수 있다
package com.kh.springhome.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.kh.springhome.entity.MusicDto;
import com.kh.springhome.repository.MusicDao;
@Controller
@RequestMapping("/music")
public class MusicController {
@Autowired
private MusicDao musicDao;
// 등록 (insert)
// 1) 등록 Mapping
@GetMapping("/insert")
public String insert() {
// 등록 페이지(insert.jsp)로 연결
// return "/WEB-INF/views/music/insert.jsp";
return "music/insert";
}
// 2) View에서 등록 Mapping으로 DTO 전달 및 DB 처리
@PostMapping("/insert")
// View의 form에서 입력한 데이터를 MusicDto 타입의 musicDto로 수신
public String insert(@ModelAttribute MusicDto musicDto) {
// 1) View에서 수신받은 musicDto를 매개변수로 등록(insert) 메소드 실행
musicDao.insert(musicDto);
// 2) 등록 후 등록 완료(insert_success) Mapping으로 강제이동(redirect) (데이터 중복 방지)
return "redirect:insert_success";
}
// 1-3. 등록 완료 Mapping
@GetMapping("/insert_success")
public String insertSuccess() {
// 등록 완료 페이지(insertResult.jsp)로 연결
// return "/WEB-INF/views/music/insertSuccess";
return "music/insertSuccess";
}
}
등록(INSERT)의 응용 - 회원 가입
준비 - Model이 될 클래스 정의 (DTO 또는 VO)
- MEMBER 테이블 생성
- 회원 아이디 : member_id, 5 ~ 20자 영문 소문자, 숫자와 특수기호(_), (-)만 사용 가능 - 회원 비밀번호 : member_pw, 8~16자 영문 대 소문자, 숫자, 특수문자(!, @, #, $)를 각각 1개 이상 사용 - 닉네임 : member_nick, 한글로 시작하며 한글 or 숫자 가능, 총 10자 이내 - 생년월일 : member_birth, YYYY-MM-DD, 날짜 형식 - 전화번호 : member_tel, 대시 제외하고 010XXXXXXXX 형태 - 이메일 : member_email, 100byte 이내, @ 반드시 포함 - 주소 - 우편 주소 : member_post, 5 ~ 6자리 - 기본 주소 : member_base_address, 한글 50자 - 상세 주소 : member_detail_address, 한글 50자 - 포인트 : member_point, 0 이상 설정 - 등급 : member_grade, 일반, VIP, 관리자 중 하나 - 가입일시 : member_join, 날짜이며 현재 시간으로 자동 설정 - 접속일시 : member_login, 로그인 할 때의 시간으로 자동 설정 ** 회원 아이디가 기본키 ** 전화번호, 이메일, 주소는 선택 입력 가능 ** 접속일시는 가입시 설정하지 않음 |
-- 테이블 생성
create table member(
member_id varchar2(20) primary key
check(regexp_like(member_id, '^[a-z0-9-_]{5,20}$')),
member_pw varchar2(16) not null check(
regexp_like(member_pw, '^[a-zA-Z0-9!@#$]{8,16}$')
and
regexp_like(member_pw, '[a-z]')
and
regexp_like(member_pw, '[A-Z]')
and
regexp_like(member_pw, '[0-9]')
and
regexp_like(member_pw, '[!@#$]')
),
member_nick varchar2(30) not null unique check(regexp_like(member_nick, '^[가-힣][가-힣0-9]{0,9}$')),
member_birth date not null,
member_tel char(11) check(regexp_like(member_tel, '^010[0-9]{8}$')),
member_email varchar2(100) check(regexp_like(member_email, '@')),
member_post varchar2(6) check(regexp_like(member_post, '^[0-9]{5,6}$')),
member_base_address varchar2(150),
member_detail_address varchar2(150),
member_point number default 0 not null check(member_point >= 0),
member_grade varchar2(9) default '일반' not null check(member_grade in ('일반', 'VIP', '관리자')),
member_join date default sysdate not null,
member_login date
);
- MemberDto 생성 : toString 오버라이딩시 비밀번호(memberPw)는 보안상 제외한다
package com.kh.springhome.entity;
import java.sql.Date;
public class MemberDto {
// 필드
private String memberId;
private String memberPw;
private String memberNick;
private Date memberBirth;
private String memberTel;
private String memberEmail;
private String memberPost;
private String memberBaseAddress;
private String memberDetailAddress;
private int memberPoint;
private String memberGrade;
private Date memberJoin;
private Date memberLogin;
// 생성자
public MemberDto() {
super();
}
// getter & setter
public String getMemberId() {
return memberId;
}
public void setMemberId(String memberId) {
this.memberId = memberId;
}
public String getMemberPw() {
return memberPw;
}
public void setMemberPw(String memberPw) {
this.memberPw = memberPw;
}
public String getMemberNick() {
return memberNick;
}
public void setMemberNick(String memberNick) {
this.memberNick = memberNick;
}
public Date getMemberBirth() {
return memberBirth;
}
public void setMemberBirth(Date memberBirth) {
this.memberBirth = memberBirth;
}
public String getMemberTel() {
return memberTel;
}
public void setMemberTel(String memberTel) {
this.memberTel = memberTel;
}
public String getMemberEmail() {
return memberEmail;
}
public void setMemberEmail(String memberEmail) {
this.memberEmail = memberEmail;
}
public String getMemberPost() {
return memberPost;
}
public void setMemberPost(String memberPost) {
this.memberPost = memberPost;
}
public String getMemberBaseAddress() {
return memberBaseAddress;
}
public void setMemberBaseAddress(String memberBaseAddress) {
this.memberBaseAddress = memberBaseAddress;
}
public String getMemberDetailAddress() {
return memberDetailAddress;
}
public void setMemberDetailAddress(String memberDetailAddress) {
this.memberDetailAddress = memberDetailAddress;
}
public int getMemberPoint() {
return memberPoint;
}
public void setMemberPoint(int memberPoint) {
this.memberPoint = memberPoint;
}
public String getMemberGrade() {
return memberGrade;
}
public void setMemberGrade(String memberGrade) {
this.memberGrade = memberGrade;
}
public Date getMemberJoin() {
return memberJoin;
}
public void setMemberJoin(Date memberJoin) {
this.memberJoin = memberJoin;
}
public Date getMemberLogin() {
return memberLogin;
}
public void setMemberLogin(Date memberLogin) {
this.memberLogin = memberLogin;
}
// toString 오버라이딩 - memberPw(비밀번호)는 보안을 위해 제외할 것
@Override
public String toString() {
return "MemberDto [memberId=" + memberId
+ ", memberNick=" + memberNick
+ ", memberBirth=" + memberBirth
+ ", memberTel=" + memberTel
+ ", memberEmail=" + memberEmail
+ ", memberPost=" + memberPost
+ ", memberBaseAddress=" + memberBaseAddress
+ ", memberDetailAddress=" + memberDetailAddress
+ ", memberPoint=" + memberPoint
+ ", memberGrade=" + memberGrade
+ ", memberJoin=" + memberJoin
+ ", memberLogin=" + memberLogin + "]";
}
}
1. Dao(Interface 형태)에서 추상 메소드 선언
package com.kh.springhome.repository;
import java.util.List;
import com.kh.springhome.entity.MemberDto;
public interface MemberDao {
// 추상 메소드 - 회원 가입
void join(MemberDto memberDto);
}
2. DaoImpl(class 형태)에서 추상 메소드 구현
package com.kh.springhome.repository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.kh.springhome.entity.MemberDto;
@Repository
public class MemberDaoImpl implements MemberDao {
@Autowired
private JdbcTemplate jdbcTemplate;
// 추상 메소드 오버라이딩 - 회원가입
@Override
public void join(MemberDto memberDto) {
// 테이블 생성시 default를 설정한 컬럼(point, grade, join, login)의 값은 입력하지 않아도 된다
String sql = "insert into member("
+ "member_id, "
+ "member_pw, "
+ "member_nick, "
+ "member_birth, "
+ "member_tel, "
+ "member_email, "
+ "member_post, "
+ "member_base_address, "
+ "member_detail_address) "
+ "values(?, ?, ?, ?, ?, ?, ?, ?, ?)";
Object[] param = new Object[] {
memberDto.getMemberId(),
memberDto.getMemberPw(),
memberDto.getMemberNick(),
memberDto.getMemberBirth(),
memberDto.getMemberTel(),
memberDto.getMemberEmail(),
memberDto.getMemberPost(),
memberDto.getMemberBaseAddress(),
memberDto.getMemberDetailAddress()
// 관리자가 설정해야 할 항목
// memberDto.getMemberPoint(),
// memberDto.getMemberGrade(),
// memberDto.getMemberJoin(),
// 로그인시 설정해야 할 항목 (일단 null)
// memberDto.getMemberLogin()
};
jdbcTemplate.update(sql, param);
}
}
3. Controller에서 Mapping 설정
1) localhost:포트번호/member/join 인 URL로 접속할 때
/WEB-INF/views/pocketmon/join.jsp 경로에 있는 view를 띄우도록 주소를 Mapping한다 (미완성 Controller)
package com.kh.springhome.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.kh.springhome.entity.MemberDto;
import com.kh.springhome.repository.MemberDao;
@Controller
@RequestMapping("/member")
public class MemberController {
@Autowired
private MemberDao memberDao;
// 회원가입(join)
// 1) 회원가입 Mapping
@GetMapping("/join")
public String join() {
// 회원가입 페이지(join.jsp)로 연결
// return "/WEB-INF/views/member/join.jsp";
return "member/join";
}
}
2) 회원가입 페이지에서 값을 입력한 후 회원가입 Mapping에 post 방식으로 전송한다
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 가입</title>
</head>
<body>
<div align = "center">
<h1>가입 정보 입력</h1>
<form action = "insert" method = "post">
아이디 : <input name ="memberId" placeholder = "아이디 입력" type = "text" required><br><br>
<!-- 비밀번호는 type을 password로 한다 -->
비밀번호 : <input name ="memberPw" placeholder = "비밀번호 입력" type = "password" required ><br><br>
닉네임 : <input name ="memberNick" placeholder = "닉네임 입력" type = "text" required><br><br>
생년월일 : <input name ="memberBirth" placeholder = "생년월일 입력" type = "date" required><br><br>
연락처(선택) : <input name ="memberTel" placeholder = "연락처 입력" type = "text"><br><br>
이메일(선택) : <input name ="memberEmail" placeholder = "이메일 입력" type = "text"><br><br>
우편번호(선택) : <input name ="memberPost" placeholder = "우편주소 입력" type = "text" maxlength = "6" size = "6"><br><br>
기본주소(선택) : <input name ="memberBaseAddress" placeholder = "기본주소 입력" type = "text"><br><br>
상세주소(선택) : <input name ="memberDetailAddress" placeholder = "상세주소 입력" type = "text"><br><br>
<button>등록</button>
</form>
</div>
</body>
</html>
3) 회원가입 view에서 POST로 전송받은 memberDto를 통해 DB 처리 및 가입완료 Mapping으로 return (완성된 Controller)
@Controller
@RequestMapping("/member")
public class MemberController {
@Autowired
private MemberDao memberDao;
// 1. 회원 가입
// 1-1. 회원 가입 Mapping
@GetMapping("/join")
public String join() {
// 회원 가입 페이지(join.jsp)로 연결
// return "/WEB-INF/views/member/insert.jsp";
return "member/join";
}
// 1-2. 회원 가입 Mapping에 DTO 전달 및 DB 처리
@PostMapping("/join")
public String join(@ModelAttribute MemberDto memberDto) {
// DB에서 등록(INSERT) 실행
memberDao.join(memberDto);
// 등록(INSERT) 처리 후 등록 완료(join_success) Mapping으로 강제 이동(redirect) (새로고침 시 중복 입력 방지)
return "redirect:join_success";
}
// 1-3. 회원 가입 완료 Mapping
@GetMapping("/join_success")
public String joinSuccess() {
// 회원 가입 완료 페이지(joinSuccess.jsp)로 연결
// return "/WEB-INF/views/member/joinSuccess.jsp";
return "member/joinSuccess";
}
}