이번에는 회원만 이용할 수 있는 게시판 만들기
1. 테이블 만들기
mysql
+ inserted 컬럼 추가!
2. Mapper (interface, xml) 만들기 (new - interface, new - file)
MemberMapper.java
package org.zerock.mapper.project1;
import java.util.List;
import org.zerock.domain.project1.MemberVO;
public interface MemberMapper {
public int insert(MemberVO member);
public MemberVO select(String id);
public int update(MemberVO member);
public int delete(String id);
public List<MemberVO> list();
}
MemberMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.project1.MemberMapper">
<insert id="insert">
INSERT INTO
Member (id, password, email, address)
VALUES
(#{id}, #{password}, #{email}, #{address})
</insert>
<select id="select"
resultType="org.zerock.domain.project1.MemberVO">
SELECT id, password, email, address, inserted
FROM Member
WHERE id = #{id}
</select>
<update id="update">
UPDATE Member
SET
password = #{password},
email = #{email},
address = #{address}
WHERE id = #{id}
</update>
<delete id="delete">
DELETE FROM Member WHERE id = #{id}
</delete>
<select id="list"
resultType="org.zerock.domain.project1.MemberVO">
SELECT
id, password, email, address, inserted
FROM Member
ORDER BY inserted DESC
</select>
(mapper.xml 파일 상단 코드는 BoardMapper.xml 파일에서 복붙_동일하므로)
MemberVO
package org.zerock.domain.project1;
import java.time.LocalDateTime;
import lombok.Data;
@Data
public class MemberVO {
private String id;
private String password;
private String email;
private String address;
private LocalDateTime inserted;
}
3. test 파일 만들기
new - JUnit Test Case (name: MemberMapperTest)
* BoardMapperTest 에서 annotation 복붙
MemberMapperTest
package org.zerock.mapper.project1;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.zerock.domain.project1.MemberVO;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class MemberMapperTest {
@Autowired
public MemberMapper mapper;
@Test
public void insertTest() {
MemberVO vo = new MemberVO();
vo.setId("newMember1");
vo.setPassword("newpassword");
vo.setAddress("seoul");
vo.setEmail("member1@gmail.com");
int cnt = mapper.insert(vo);
assertEquals(1,cnt);
}
}
MemberMapperTest 수정
@Test
public void insertTest() {
MemberVO vo = new MemberVO();
vo.setId("newMember1" + (new Date()).getTime());
vo.setPassword("newpassword");
vo.setAddress("seoul");
vo.setEmail("member1@gmail.com");
int cnt = mapper.insert(vo);
assertEquals(1,cnt);
}
MemberMapperTest 수정
@Test
public void mapperTest() {
String id = "member" + (new Date()).getTime();
String password = "newpassword" +(new Date()).getTime();
String address = "jeju" + (new Date()).getTime();
String email = id + "@gmail.com";
MemberVO vo = new MemberVO();
vo.setId(id);
vo.setPassword(password);
vo.setAddress(address);
vo.setEmail(email);
// insert test
int cnt = mapper.insert(vo);
assertEquals(1,cnt);
// select test
MemberVO s = mapper.select(id);
assertEquals(id, s.getId());
assertEquals(password, s.getPassword());
assertEquals(address, s.getAddress());
assertEquals(email, s.getEmail());
// update test
String newPassword = "newPassword";
String newAddress = "newAddress";
LocalDateTime inserted = s.getInserted();
s.setPassword(newPassword);
s.setAddress(newAddress);
cnt = mapper.update(s);
assertEquals(1,cnt);
MemberVO t = mapper.select(id);
assertEquals(id, t.getId());
assertEquals(newPassword, t.getPassword());
assertEquals(newAddress, t.getAddress());
assertEquals(email, t.getEmail());
assertEquals(inserted, t.getInserted());
// list test (지우기 전)
List<MemberVO> list1 = mapper.list();
int size1 = list1.size();
for(MemberVO item : list1) {
assertNotNull(item.getId());
assertNotNull(item.getAddress());
assertNotNull(item.getPassword());
assertNotNull(item.getEmail());
assertNotNull(item.getInserted());
}
// delete test
cnt = mapper.delete(id);
assertEquals(1, cnt);
assertNull(mapper.select(id));
// list test (지운 후)
List<MemberVO> list2 = mapper.list();
int size2 = list2.size();
assertEquals(size1 - 1, size2);
}
4. Service 만들기 (new - class)
MemberService
package org.zerock.service.project1;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.zerock.domain.project1.MemberVO;
import org.zerock.mapper.project1.MemberMapper;
import lombok.Setter;
@Service
public class MemberService {
@Setter(onMethod_=@Autowired)
private MemberMapper mapper;
public MemberVO read(String id) {
return mapper.select(id);
}
public boolean register(MemberVO member) {
return mapper.insert(member) == 1;
}
public boolean modify(MemberVO member) {
return mapper.update(member) == 1;
}
public boolean remove(String id) {
return mapper.delete(id) == 1;
}
public List<MemberVO> getList() {
return mapper.list();
}
}
5. Controller 만들기 (new - class)
MemberController
package org.zerock.controller.project1;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/member")
public class MemberController {
@GetMapping("/signup")
public void signup() {
}
}
6. jsp 파일 만들기
views > member 안에 new - other - jsp 파일 생성
signup.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="b" tagdir="/WEB-INF/tags/board"%>
<% request.setCharacterEncoding("utf-8"); %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="<%= request.getContextPath() %>/resource/css/icon/css/all.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
<title>Insert title here</title>
</head>
<body>
<b:navBar></b:navBar>
<!-- .container>.row>.col>h1{회원 가입} -->
<div class="container">
<div class="row">
<div class="col">
<h1>회원 가입</h1>
<!-- form>.form-group*4>label[for=input$]+input.form-control#input$[required]^button.btn.btn-outline-primary{가입} -->
<form method="post">
<div class="form-group">
<label for="input1">아이디</label>
<input type="text" class="form-control" id="input1" required name="id">
</div>
<div class="form-group">
<label for="input2">패스워드</label>
<input type="password" class="form-control" id="input2" required name="password">
</div>
<div class="form-group">
<label for="input3">이메일</label>
<input type="email" class="form-control" id="input3" required name="email">
</div>
<div class="form-group">
<label for="input4">주소</label>
<input type="text" class="form-control" id="input4" required name="address">
</div>
<button class="btn btn-outline-primary">가입</button>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
</body>
</html>
navBar.tag 수정
요청 : /board/list
/member/signup
MemberController 추가
@Setter(onMethod_=@Autowired)
private MemberService service;
@PostMapping("/signup")
public String signup(MemberVO member, RedirectAttributes rttr) {
boolean ok = service.register(member);
if(ok) {
rttr.addFlashAttribute("result", "회원가입이 완료되었습니다.");
return "redirect:/board/list";
} else {
return "redirect:/member/signup";
}
}
양식을 작성하고 가입하면 /board/list 로 리디렉트되고, 화면에 모달이 뜸
mysql 에서 가입한 정보가 추가된 것을 확인할 수 있음
만약, id (PRIMARY KEY) 가 중복되게 입력된다면? → 예외처리해주기
MemberController 수정
@PostMapping("/signup")
public String signup(MemberVO member, RedirectAttributes rttr, Model model) {
MemberVO m = service.read(member.getId());
if(m==null) {
boolean ok = service.register(member);
if(ok) {
rttr.addFlashAttribute("result", "회원가입이 완료되었습니다.");
return "redirect:/board/list";
} else {
return "redirect:/member/signup";
}
}else {
model.addAttribute("alertMessage", "중복된 아이디입니다.");
return null;
}
}
signup.jsp 추가
https://getbootstrap.com/docs/4.6/components/alerts/
<c:if test="${not empty alertMessage}">
<div class="alert alert-warning">${alertMessage }</div>
</c:if>
이미 있는 아이디를 입력하면 다음과 같이 표시됨
이 화면에서 기존에 입력한 정보가 입력창에서 사라지지 않고 유지되도록 하기 위해서 다음과 같은 작업을 함
MemberController 수정
@PostMapping("/signup")
public String signup(@ModelAttribute("member") MemberVO member, RedirectAttributes rttr, Model model) {
MemberVO m = service.read(member.getId());
if(m==null) {
boolean ok = service.register(member);
if(ok) {
rttr.addFlashAttribute("result", "회원가입이 완료되었습니다.");
return "redirect:/board/list";
} else {
return "redirect:/member/signup";
}
}else {
model.addAttribute("alertMessage", "중복된 아이디입니다.");
return null;
}
}
@ModelAttribute("member")
signup.jsp 수정
<form method="post">
<div class="form-group">
<label for="input1">아이디</label>
<input type="text" class="form-control" id="input1" required name="id" value="${member.id }">
</div>
<div class="form-group">
<label for="input2">패스워드</label>
<input type="password" class="form-control" id="input2" required name="password" value="${member.password }">
</div>
<div class="form-group">
<label for="input3">이메일</label>
<input type="email" class="form-control" id="input3" required name="email" value="${member.email }">
</div>
<div class="form-group">
<label for="input4">주소</label>
<input type="text" class="form-control" id="input4" required name="address" value="${member.address }">
</div>
<button class="btn btn-outline-primary">가입</button>
</form>
value attribute 추가 작성
로그인
navBar.tag 추가
<c:url value="/member/login" var="loginUrl"></c:url>
<li class="nav-item active">
<a class="nav-link" href="${loginUrl }">로그인</a>
</li>
MemberController 추가
@GetMapping("/login")
public void login() {
}
포워딩하는 역할 뿐임
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="b" tagdir="/WEB-INF/tags/board"%>
<% request.setCharacterEncoding("utf-8"); %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="<%= request.getContextPath() %>/resource/css/icon/css/all.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
<title>Insert title here</title>
</head>
<body>
<b:navBar></b:navBar>
<div class="container">
<div class="row">
<div class="col">
<h1>로그인</h1>
<!-- form>.form-group*2>label[for=input$]+input.form-control[name][required]^button.btn.btn-outline-primary{로그인} -->
<form method="post">
<div class="form-group">
<label for="input1">아이디</label>
<input type="text" class="form-control" name="id" required="">
</div>
<div class="form-group">
<label for="input2">패스워드</label>
<input type="password" class="form-control" name="password" required="">
</div>
<button class="btn btn-outline-primary">로그인</button>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
</body>
</html>
MemberController 추가
@PostMapping("/login")
public String login(String id, String password, HttpSession session) {
// service 사용하여 아이디로 Member vo 얻고
MemberVO vo = service.read(id);
if(vo == null) {
return null;
}
// 얻어온 Member vo 의 패스워드와 입력한 패스워드가 같은 지 확인
boolean correctPassword = password.equals(vo.getPassword());
// Member vo 가 null 이거나 패스워드가 일치하지 않으면 로그인 실패
if(!correctPassword) {
return null;
}
// Member vo 가 null 이 아니고 패스워드가 일치하면 로그인 성공
session.setAttribute("loggedInMember", vo);
return "redirect:/board/list";
}
로그인 실패 시 같은 경로로 포워딩
로그인 성공 시 list 로 리디렉트
session.setAttribute("loggedInMember", vo) - 로그인 상태
로그아웃
navBar.tag 수정
<c:url value="/member/logout" var="logoutUrl"></c:url>
<c:if test="${empty sessionScope.loggedInMember }">
<li class="nav-item active">
<a class="nav-link" href="${signupUrl }">회원가입</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="${loginUrl }">로그인</a>
</li>
</c:if>
<c:if test="${not empty sessionScope.loggedInMember }">
<li class="nav-item active">
<a class="nav-link" href="${logoutUrl }">로그아웃</a>
</li>
</c:if>
로그인되지 않은 상태 → 회원가입, 로그인
로그인 된 상태 → 로그아웃
로그인 전
로그인 후
MemberController 추가
@RequestMapping("/logout")
public String logout(HttpSession session) {
// 세션 invalidate
session.invalidate();
// /board/list redirect
return "redirect:/board/list";
}
회원정보보기
navBar.tag 추가
<c:url value="/member/info" var="memberInfoUrl"></c:url>
<li class="nav-item active">
<a class="nav-link" href="${memberInfoUrl }">회원정보보기 </a>
</li>
로그인 상태일 때만 표시되어야 하므로 로그아웃과 같이 위치
MemberController 추가
@GetMapping("/info")
public String info(HttpSession session) {
MemberVO vo = (MemberVO) session.getAttribute("loggedInMember");
// 로그아웃 상태
if(vo == null) {
return "redirect"/member/login";
}
// 로그인 상태
return null;
}
info.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="b" tagdir="/WEB-INF/tags/board"%>
<% request.setCharacterEncoding("utf-8"); %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="<%= request.getContextPath() %>/resource/css/icon/css/all.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
<title>Insert title here</title>
</head>
<body>
<b:navBar></b:navBar>
<!-- .container>.row>.col>h1{회원정보} -->
<div class="container">
<div class="row">
<div class="col">
<h1>회원정보</h1>
<!-- form>.form-group*4>label[for=input$]+input.form-control[name][value] -->
<form method="post" id="infoForm">
<div class="form-group">
<label for="input1">아이디</label>
<input type="text" required id="input1" class="form-control" name="id" value="${sessionScope.loggedInMember.id }" readonly>
</div>
<div class="form-group">
<label for="input2">패스워드</label>
<input type="password" required id="input2" class="form-control" name="password" value="${sessionScope.loggedInMember.password }">
</div>
<div class="form-group">
<label for="input3">이메일</label>
<input type="email" required id="input3" class="form-control" name="email" value="${sessionScope.loggedInMember.email }">
</div>
<div class="form-group">
<label for="input4">주소</label>
<input type="text" required id="input4" class="form-control" name="address" value="${sessionScope.loggedInMember.address }">
</div>
<!-- button.btn.btn-outline-secondary{수정}+button.btn.btn-outline-danger{삭제} -->
<button class="btn btn-outline-secondary">수정</button>
<button class="btn btn-outline-danger">삭제</button>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
</body>
</html>
MemberController 추가
@PostMapping("/info")
public String info(MemberVO member, HttpSession session, RedirectAttributes rttr) {
MemberVO vo = (MemberVO) session.getAttribute("loggedInMember");
// 로그아웃 상태
if (vo == null) {
return "redirect:/member/login";
}
// 로그인 상태
boolean ok = service.modify(member);
if (ok) {
rttr.addFlashAttribute("result", "회원정보가 변경되었습니다.");
session.setAttribute("loggedInMember", service.read(member.getId()));
} else {
rttr.addFlashAttribute("result", "회원정보가 변경되지 않았습니다.");
}
return "redirect:/board/list";
}
회원정보 수정 시, 정보가 수정되고 list 화면으로 리디렉트 되며 모달이 표시됨
삭제 시, 아직 삭제되지 않음 - 해결 ↓
info.jsp 추가
<script>
$(document).ready(function() {
// 수정버튼 또는 삭제버튼 클릭 시 form의 action 속성값 변경
const infoForm = $("#infoForm");
const modifyButton = $("#modifyButton");
const removeButton = $("#removeButton");
modifyButton.click(function(e) {
e.preventDefault();
infoForm.attr("action", "");
infoForm.submit();
});
removeButton.click(function(e) {
e.preventDefault();
if (confirm("탈퇴하시겠습니까?")) {
infoForm.attr("action", "remove");
infoForm.submit();
}
});
});
</script>
+ 수정 버튼에 id="modifyButton", 삭제 버튼에 id="removeButton" 작성
MemberController 추가
@PostMapping("/remove")
public String remove(String id, HttpSession session, RedirectAttributes rttr) {
MemberVO vo = (MemberVO) session.getAttribute("loggedInMember");
// 로그아웃 상태
if (vo == null) {
return "redirect:/member/login";
}
// 로그인 상태
service.remove(id);
session.invalidate();
rttr.addFlashAttribute("result", "회원 탈퇴하였습니다.");
return "redirect:/board/list";
}
삭제 시 회원탈퇴
계정 정보가 테이블에서 삭제됨
'course 2021 > Spring' 카테고리의 다른 글
Spring10 - 12/15 (0) | 2022.01.03 |
---|---|
Spring09 - 12/14 (0) | 2021.12.27 |
Spring07 - 12/10 (0) | 2021.12.23 |
Spring06 - 12/09 (0) | 2021.12.21 |
Spring05 - 12/08(controller09~11) (0) | 2021.12.21 |