01. mybatis를 활용한 회원관리 - 회원수정
회원수정화면
th:href=”@{/member/modifyMember(memberId=${l.memberId})}”
➡️ Controller에서 해당 쿼리스트링을 받을 수 있다. (memberId=수정하려는id)
[코드]memberList.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/default}"> <head> <link rel="stylesheet" type="text/css" th:href="@{/css/table.css}"> </head> <th:block layout:fragment="customContents"> <table> <thead> <tr> <th>회원아이디</th> <th>회원비밀번호</th> <th>회원이름</th> <th>회원등급</th> <th>회원이메일</th> <th>회원주소</th> <th>회원등록날짜</th> <th>수정</th> <th>삭제</th> </tr> </thead> <tbody> <tr th:if="${#lists.isEmpty(memberList)}"> <td colspan="7">등록된 회원의 정보가 없습니다.</td> </tr> <tr th:unless="${#lists.isEmpty(memberList)}" th:each="l : ${memberList}"> <td th:text="${l.memberId}"></td> <td th:text="${l.memberPw}"></td> <td th:text="${l.memberName}"></td> <td th:text="${l.memberLevel}"></td> <td th:text="${l.memberEmail}"></td> <td th:text="${l.memberAddr}"></td> <td th:text="${l.memberRegDate}"></td> <td> <a th:href="@{/member/modifyMember(memberId=${l.memberId})}">수정</a> </td> <td> <a th:href="@{#}">삭제</a> </td> </tr> </tbody> </table> </th:block> </html>
➡️ @RequestParam 로 쿼리스트링을 받는다.
➡️ 수정화면으로 주소를 이동한다.
[코드]Controller.java
@GetMapping("/modifyMember") public String modifyMember(@RequestParam(value="memberId") String memberId, Model model) { model.addAttribute("title", "회원수정"); return "member/modifyMember"; }
➡️ 해당 회원의 정보를 조회하는 쿼리
➡️ 회원 등급을 관리자, 판매자, 구매자로 나타내도록 회원 등급 목록을 조회하는 쿼리
➡️ Mapper.xml에서 resultMap
- 해당 클래스(dto)의 프로퍼티가 테이블의 어떤 컬럼과 매칭될지를 정의해준다.
- id 태그는 테이블의 기본키, result 태그는 테이블의 일반컬럼, 외래키
[코드]Mapper.xml
<!-- Map : 키와 값 / type에는 dto에 들어가야기 때문에 Member --> <!-- mybatis DTO 와 ResultSet의 결과값을 미리 매핑 정의 --> <resultMap type="Member" id="memberResultMap"> <!-- id태그는 조회시 테이블의 PK(기본키) --> <!-- result태그는 조회시 테이블의 일반컬럼 혹은 외래키 --> <id column="m_id" property="memberId"/> <result column="m_pw" property="memberPw"></result> <result column="m_name" property="memberName"></result> <result column="m_level" property="memberLevel"></result> <result column="m_email" property="memberEmail"></result> <result column="m_addr" property="memberAddr"></result> <result column="m_reg_date" property="memberRegDate"></result> </resultMap> <!-- 생략 --> <!-- 정의한 resultMap을 가져온다. --> <select id="getMemberInfoById" parameterType="String" resultMap="memberResultMap"> /* 회원별 상세조회 */ SELECT m.m_id, m.m_pw, m.m_name, m.m_level, m.m_email, m.m_addr, m.m_reg_date FROM tb_member AS m WHERE m.m_id = #{memberId}; </select> <select id="getMemberLevelList" resultType="MemberLevel"> /* 회원등급 조회 */ SELECT l.level_num AS levelNum, l.level_name AS levelName FROM tb_member_level AS l </select>
[코드]Mapper.java
// 회원별 상세조회 public Member getMemberInfoById(String memberId); // 회원등급 조회 public List<MemberLevel> getMemberLevelList();
➡️ Mapper에서 선언한 메서드를 호출해서 결과값을 반환한다.
[코드]Service.java
/** * 회원별 상세조회 * @param memberId(회원아이디) * @return Member (회원정보) */ public Member getMemberById(String memberId) { Member memberInfo = memberMapper.getMemberInfoById(memberId); return memberInfo; } /** * 회원등급 조회 * @return */ public List<MemberLevel> getMemberLevelList() { List<MemberLevel> memberLevelList = memberMapper.getMemberLevelList(); return memberLevelList; }
➡️ Service에서 선언한 메서드를 호출 후 결과값을 model에 넘겨준다.
[코드]Controller.java
@GetMapping("/modifyMember") public String modifyMember(@RequestParam(value="memberId") String memberId, Model model) { // 회원 상세조회 Member memberInfo = memberService.getMemberById(memberId); // 회원등급 목록조회 List<MemberLevel> memberLevelList = memberService.getMemberLevelList(); model.addAttribute("title", "회원수정"); model.addAttribute("memberInfo", memberInfo); model.addAttribute("memberLevelList", memberLevelList); return "member/modifyMember"; }
➡️ form으로 화면을 구성하고, 타임리프를 활용해서 전달받은 데이터를 화면에 출력한다.
[코드]modifyMember.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/default}"> <head> <link rel="stylesheet" type="text/css" th:href="@{/css/table.css}"> <style> input { width: 98%; height: 23px; } select { width: 98%; text-align: center; height: 23px; } #modifyMemberBtn, #resetBtn { width: 49%; height: 25px; } </style> </head> <th:block layout:fragment="customContents"> <form id="modifyMemberForm" th:action="@{/member/modifyMember}" method="post"> <table th:object="${memberInfo}"> <tbody> <tr> <td> <label for="memberId">회원아이디</label> </td> <td> <!-- th:value="${memberInfo.memberId}" --> <input type="text" id="memberId" name="memberId" th:value="*{memberId}" placeholder="회원의 아이디를 입력해주세요." readonly="readonly"/> </td> </tr> <tr> <td> <label for="memberPw">회원비밀번호</label> </td> <td> <input type="text" id="memberPw" name="memberPw" th:value="*{memberPw}" placeholder="회원의 비밀번호를 입력해주세요."/> </td> </tr> <tr> <td> <label for="memberName">회원이름</label> </td> <td> <input type="text" id="memberName" name="memberName" th:value="*{memberName}" placeholder="회원의 이름을 입력해주세요."/> </td> </tr> <tr> <td> <label for="memberLevel">회원등급</label> </td> <td> <select id="memberLevel" name="memberLevel"> <th:block th:unless="${#lists.isEmpty(memberLevelList)}" th:each="l : ${memberLevelList}"> <option th:value="${l.levelNum}" th:selected="${l.levelNum} == *{memberLevel}">[[${l.levelName}]]</option> </th:block> </select> </td> </tr> <tr> <td> <label for="memberEmail">회원이메일</label> </td> <td> <input type="text" id="memberEmail" name="memberEmail" th:value="*{memberEmail}" placeholder="회원의 이메일을 입력해주세요."/> </td> </tr> <tr> <td> <label for="memberAddr">회원주소</label> </td> <td> <input type="text" id="memberAddr" name="memberAddr" th:value="*{memberAddr}" placeholder="회원의 주소를 입력해주세요."/> </td> </tr> <tr> <td colspan="2"> <button type="submit" id="modifyMemberBtn">수정완료</button> <button type="reset" id="resetBtn">입력취소</button> </td> </tr> </tbody> </table> </form> </th:block> </html>
회원수정처리
<form id="modifyMemberForm" th:action="@{/member/modifyMember}" method="post">
➡️ @PostMapping(”/modifyMember”) 로 post방식으로 요청 받은 주소를 처리한다.
➡️ return 화면으로는 수정처리 완료 후 다시 회원목록 화면으로 리다이렉트를 해준다.
[코드]Controller.java
@PostMapping("/modifyMember") public String modifyMember(Member member) { return "redirect:/member/memberList"; }
수정처리를 위한 쿼리를 작성 후 인터페이스와 연결 시켜준다.
➡️ trim : 동적 쿼리를 작성하기 위한 태그입니다.
- prefix : 맨 앞에 해당 값을 붙여준다.
- prefixOverrides : 맨 앞에 해당 값이 있으면 삭제한다.
- suffix : 맨 뒤에 해당 값을 붙여준다.
- suffixOverrides : 맨 뒤에 해당 값이 있으면 삭제한다.
🗣 set, where는 태그만 써줘도 동적으로 쿼리가 실행된다. (trim 태그를 대체)
[코드]Mapper.xml
<update id="modifyMember" parameterType="Member"> /* 회원 수정 처리 */ UPDATE tb_member <trim prefix="SET" suffixOverrides=","> <!--<set>--> <if test="memberPw != null and memberPw != ''"> m_pw = #{memberPw}, </if> <if test="memberName != null and memberName != ''"> m_name = #{memberName}, </if> <if test="memberLevel != null and memberLevel != ''"> m_level = #{memberLevel}, </if> <if test="memberEmail != null and memberEmail != ''"> m_email = #{memberEmail}, </if> <if test="memberAddr != null and memberAddr != ''"> m_addr = #{memberAddr} </if> <!--</set>--> </trim> WHERE m_id = #{memberId}; </update>
[코드]Mapper.java
// 회원 수정 처리 public int modifyMember(Member member);
mapper에 생성한 메서드를 호출하고, 반환 타입을 확인 후 리턴을 해준다.
➡️ @Transactional 을 통해서 트랜잭션 처리를 해준다.
[코드]Service.java
public int modifyMember(Member member) { int result = memberMapper.modifyMember(member); return result; }
Service에 생성한 메서드를 호출한다.
➡️ 수정처리가 실행되고, 화면이 회원목록으로 이동 된다.
[코드]Controller.java
@PostMapping("/modifyMember") public String modifyMember(Member member) { memberService.modifyMember(member); return "redirect:/member/memberList"; }
02. mybatis를 활용한 회원관리 - 회원탈퇴
회원탈퇴화면
th:href=”@{/member/removeMember(memberId=${l.memberId})}”
➡️ Controller에서 해당 쿼리스트링을 받을 수 있다. (memberId=수정하려는id)
➡️ GET방식으로 전달받은 데이터를 @RequestParam을 통해서 매개변수에 담아준다.
➡️ 데이터(memberId)를 model에 담아서 view로 전달한다.
[코드]Controller.java
@GetMapping("/removeMember") public String removeMember(@RequestParam(value = "memberId") String memberId, Model model) { model.addAttribute("title", "회원탈퇴"); model.addAttribute("memberId", memberId); return "member/removeMember"; }
➡️ 비밀번호를 입력하고, 해당 회원의 정보와 일치하면 탈퇴처리한다.
➡️ 폼으로 구성하고, 전달받은 데이터를 타임리프로 화면에 출력한다.
- 비밀번호만 입력하는 폼으로 구성하여, 아이디 값은 hidden 속성으로 감추고, value에 값을 설정한다.
[코드]removeMember.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/default}"> <head> <link rel="stylesheet" type="text/css" th:href="@{/css/table.css}"> <style> input { width: 98%; height: 23px; } select { width: 98%; text-align: center; height: 23px; } #removeMemberBtn, #resetBtn { width: 49%; height: 25px; } </style> </head> <th:block layout:fragment="customContents"> <form id="removeMemberForm" th:action="@{/member/removeMember}" method="post"> <table> <tbody> <tr> <td> <label for="memberPw">회원비밀번호</label> </td> <td> <input type="hidden" name="memberId" th:value="${memberId}"> <input type="text" id="memberPw" name="memberPw" placeholder="회원의 비밀번호를 입력해주세요."/> </td> </tr> <tr> <td colspan="2"> <button type="submit" id="removeMemberBtn">탈퇴완료</button> <button type="reset" id="resetBtn">입력취소</button> </td> </tr> </tbody> </table> </form> </th:block> </html>
회원탈퇴처리
2) 회원 테이블과 관련 있는 테이블의 정보를 먼저 삭제한다.
3) 마지막으로 회원 테이블에 있는 해당 회원의 아이디를 삭제한다.
➡️ 테이블 관계
- 권한 별 회원 테이블과 연결된 테이블
관리자 → 로그인 내역
판매자 → 로그인 내역, 주문이력, 상품
구매자 → 로그인 내역, 주문이력
➡️ 권한별 테이블 삭제 순서
- 관리자 : 로그인내역 → 회원
- 판매자 : 주문이력 → 상품 → 로그인내역 → 회원
- 구매자 : 주문이력 → 로그인내역 → 회원
(공통 : 로그인내역, 회원)
<form id="removeMemberForm" th:action="@{/member/removeMember}" method="post">
➡️ @PostMapping(”/removeMember”) 로 post방식으로 요청 받은 주소를 처리한다.
➡️ @RequestParam에 아이디와, 비밀번호를 받는다.
➡️ return 화면으로는 탈퇴처리 완료 후 다시 회원목록 화면으로 리다이렉트를 해준다.
🗣 RedirectAttributes
- redirect 시 데이터를 전달하는 방법이다. (리다이렉트가 될때)
- string 형태와 map, list, vo 등의 object 형태로 넘겨줄 수 있다.
[코드]Controller.java
@PostMapping("/removeMember") public String removeMember(@RequestParam(value = "memberId") String memberId, @RequestParam(value = "memberPw") String memberPw, RedirectAttributes reAttr) { reAttr.addAttribute("memberId", memberId); return "redirect:/member/memberList"; }
➡️ 탈퇴처리시 회원 검증을 하고, 비밀번호가 일치해야 탈퇴처리가 가능하다.
➡️ Map을 이용해서 검증여부(boolean타입)와 검증이 완료된 회원객체(Member타입)를 담아서 리턴 해준다.
- 성공시 : 검증여부, 객체 / 실패시 : 검증여부
🗣 Map에는 여러가지 데이터 타입이 담길 수 있다.
[코드]Service.java
public Map<String, Object> isValidMember(String memberId, String memberPw) { Map<String, Object> resultMap = new HashMap<String, Object>(); boolean isValid = false; // 회원 검증 Member member = memberMapper.getMemberInfoById(memberId); if (member != null) { String checkPw = member.getMemberPw(); if (checkPw.equals(memberPw)) { isValid = true; resultMap.put("memberInfo", member); } } resultMap.put("isValid", isValid); return resultMap; }
➡️ Service에서 작성한 로직을 호출한다.
➡️ 회원 검증 성공시, 실패시 리다이렉트 경로를 다르게 설정해준다.
- 검증 실패시 RedirectAttributes 클래스를 통해서 메세지를 넘겨준다.
[코드]Controller.java
@PostMapping("/removeMember") public String removeMember(@RequestParam(value = "memberId") String memberId, @RequestParam(value = "memberPw") String memberPw, RedirectAttributes reAttr) { // 회원 여부 검증 Map<String, Object> isVaildMap = memberService.isValidMember(memberId, memberPw); boolean isValid = (boolean) isVaildMap.get("isValid"); // 비밀번호 일치 회원탈퇴 if(isValid) { Member memberInfo = (Member) isVaildMap.get("memberInfo"); return "redirect:/member/memberList"; } reAttr.addAttribute("memberId", memberId); reAttr.addAttribute("msg", "회원의 정보가 일치하지 않습니다."); return "redirect:/member/removeMember"; }
➡️ 회원 → 구매자별 구매이력 삭제, 로그인 이력 삭제, 회원삭제
➡️ 상품 → 판매자가 등록한 상품코드별 주문이력 삭제, 판매자별 상품 삭제
- MemberMapper와 GoodsMapper에 나눠서 쿼리문과 각각의 mapper를 연결시켜준다.
데이터 삭제처리를 위한 쿼리를 작성 후 인터페이스와 연결시켜준다.
➡️ 구매이력, 로그인 이력, 회원 삭제 쿼리 작성
[코드]MemberMapper.xml
<delete id="removeOrderById" parameterType="String"> /* 구매자별 구매이력 삭제 */ DELETE FROM tb_order WHERE o_id = #{memberId} </delete> <delete id="removeLoginHistoryById" parameterType="String"> /* 로그인 이력 삭제 */ DELETE FROM tb_login WHERE login_id = #{memberId} </delete> <delete id="removeMemberById" parameterType="String"> /* 회원 삭제 */ DELETE FROM tb_member WHERE m_id = #{memberId} </delete>
[코드]MemberMapper.java
// 구매자별 구매이력 삭제 public int removeOrderById(String memberId); // 로그인 이력 삭제 public int removeLoginHistoryById(String memberId); // 회원 삭제 public int removeMemberById(String memberId);
상품정보를 담는 dto를 만든다.
➡️ Goods dto에 member 정보를 가져오고 싶다.
- 상품에서 바라봤을 때 상품과 회원과는 1:1 관계이다.
- 회원에서 바라봤을 때 회원과 상품은 1:N 관계이다.
🗣 private Member sellerInfo(Member정보 1:1관계시)를 추가한다.
[코드]Goods.java
package ksmart.mybatis.dto; import lombok.Data; @Data public class Goods { private String goodsCode; private String goodsName; private int goodsPrice; private String goodsSellerId; private String goodsRegDate; // 판매자의 정보 (1:1) // Member dto private Member sellerInfo; }
상품과 관련된 Mapper를 새롭게 생성한다.
➡️ 1) 판매자가 등록한 상품코드별 주문이력 삭제 쿼리
2) 판매자별 상품 삭제 쿼리
🗣 DELETE시 원래는 컬럼을 명시를 안하는데, 조인을 하면 별칭을 써줘야한다. 별칭을 썼을 때는 컬럼에 별칭을 넣어줘야 한다.
[코드]GoodsMapper.xml
<delete id="removeOrederByGCode" parameterType="String"> /* 판매자가 등록한 상품코드별 주문이력 삭제 */ DELETE o FROM tb_goods AS g INNER JOIN tb_order AS o ON g.g_code = o.o_g_code WHERE g.g_seller_id = #{sellerId} </delete> <delete id="removeGoodsById" parameterType="String"> /* 판매자별 상품 삭제 */ DELETE FROM tb_goods WHERE g_seller_id = #{sellerId}; </delete>
[코드]GoodsMapper.java
// 판매자가 등록한 상품코드별 주문이력 삭제 public int removeOrederByGCode(String sellerId); // 판매자별 상품 삭제 public int removeGoodsById(String sellerId);
회원등급별로 탈퇴가 처리되는 로직을 작성한다.
➡️ 매개변수에 회원 커맨드 객체를 받아서 회원의 정보를 한번에 불러온다. (커맨트 객체 → dto에 set 되어있는 데이터)
➡️ 회원의 정보에서 아이디와 등급을 가져와서 해당 권한이 일치하면 mapper의 메소드를 아이디 값을 통해서 호출한다.
[코드]Service.java
/** * 회원등급별 탈퇴 처리 * @param memberInfo */ public void removeMember(Member memberInfo) { if(memberInfo != null) { String memberId = memberInfo.getMemberId(); int memberLevel = memberInfo.getMemberLevel(); switch (memberLevel) { // 판매자일 경우 삭제처리 case 2 -> { goodsMapper.removeOrederByGCode(memberId); goodsMapper.removeGoodsById(memberId); } // 구매자일 경우 삭제처리 case 3 -> { memberMapper.removeOrderById(memberId); } } // 공통 삭제처리 부분 memberMapper.removeLoginHistoryById(memberId); memberMapper.removeMemberById(memberId); } }
➡️ 회원의 정보를 삭제하는 service 메서드를 호출한다. (비밀번호 일치가 완료 됐을 경우에)
➡️ 호출 후 리턴으로 회원 목록화면이 리다이렉트된다.
[코드]Controller.java
// 비밀번호 일치 회원 탈퇴 if(isValid) { Member memberInfo = (Member) isValidMap.get("memberInfo"); // ========= 기존 컨트롤러에서 추가되는 부분 ======== // 회원 탈퇴 메서드 호출 memberService.removeMember(memberInfo); return "redirect:/member/memberList"; }
tag : #Spring #Mybatis #Controller #Service #Mapper #SQL #ERD #커맨드객체
Uploaded by N2T