Luver Duck 2022. 9. 1. 22:35

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";
	}
}