01. mybatis를 활용한 회원관리 - 회원조회
회원 목록 조회
[코드]Member.java
package ex.mybatis.dto; public class Member { private String memberId; private String memberPw; private String memberName; private int memberLevel; private String memberEmail; private String memberAddr; private String memberRegDate; 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 getMemberName() { return memberName; } public void setMemberName(String memberName) { this.memberName = memberName; } public int getMemberLevel() { return memberLevel; } public void setMemberLevel(int memberLevel) { this.memberLevel = memberLevel; } public String getMemberEmail() { return memberEmail; } public void setMemberEmail(String memberEmail) { this.memberEmail = memberEmail; } public String getMemberAddr() { return memberAddr; } public void setMemberAddr(String memberAddr) { this.memberAddr = memberAddr; } public String getMemberRegDate() { return memberRegDate; } public void setMemberRegDate(String memberRegDate) { this.memberRegDate = memberRegDate; } @Override public String toString() { final StringBuilder sb = new StringBuilder("Member{"); sb.append("memberId='").append(memberId).append('\''); sb.append(", memberPw='").append(memberPw).append('\''); sb.append(", memberName='").append(memberName).append('\''); sb.append(", memberLevel='").append(memberLevel).append('\''); sb.append(", memberEmail='").append(memberEmail).append('\''); sb.append(", memberAddr='").append(memberAddr).append('\''); sb.append(", memberRegDate='").append(memberRegDate).append('\''); sb.append('}'); return sb.toString(); } }
➡️ mapper.java → 인터페이스 역할
➡️ resource에 mapper.xml에 SQL문 작성
<mapper namespace=””> : 인터페이스의 위치(경로)를 설정한다.
<select id=”” resultType=””> : id(메소드명과 일치), resultType(리턴타입과 일치)
[코드]MemberMapper.java
package ex.mybatis.mapper; import ex.mybatis.dto.Member; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface MemberMapper { // 회원 목록 조회 public List<Member> getMemberList(); }
[코드]MemberMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="ex.mybatis.mapper.MemberMapper"> <select id="getMemberList" resultType="Member"> <!--회원목록 조회--> SELECT m_id AS memberId, m_pw AS memberPw, m_name AS memberName, m_level AS memberLevel, m_email AS memberEmail, m_addr AS memberAddr, m_reg_date AS memberRegDate FROM tb_member AS m; </select> </mapper>
➡️ @Transcational
- 트랜잭션 처리.
- 동시 접근하는 여러 프로그램 간 격리를 제공.
- 특정 실행단위에서 오류 발생시 전체 실행 내용을 rollback해주는 기능이다.
[코드]MemberService.java
package ex.mybatis.service; import ex.mybatis.dto.Member; import ex.mybatis.mapper.MemberMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * @Transactional : 기본적인 트랜잭션 처리 A(원자성) C(일관성) I(고립성) D(영속성) * SQLException dao 관련된 작업 rollback * 정상적으로 수행이 되었다면 commit */ @Service @Transactional // 클래스 선언 위치 : 클래스 내부에 한번에 적용하고 싶을 떄 public class MemberService { private static final Logger log = LoggerFactory.getLogger(MemberService.class); public final MemberMapper memberMapper; public MemberService(MemberMapper memberMapper) { this.memberMapper = memberMapper; } // @Transactional // 메소드 선언 위치 : 세세하게 메소드마다 적용하고 싶을 때 public List<Member> getMemberList() { List<Member> memberList = memberMapper.getMemberList(); log.info("memberList: {}", memberList); return memberList; } }
➡️ @RequestMapping : 공통적인 url을 class에 @RequestMapping으로 설정해준다.
[코드]MemberController.java
package ex.mybatis.controller; import ex.mybatis.dto.Member; import ex.mybatis.service.MemberService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; @Controller // 주소 분기 및 맵핑 @RequestMapping("/member") // 공통적인 url public class MemberController { public final MemberService memberService; public MemberController(MemberService memberService) { this.memberService = memberService; } /** * 회원목록 조회 * @param model * @return */ @GetMapping("/memberList") // /member/memberList public String getMemberList(Model model) { List<Member> memberList = memberService.getMemberList(); model.addAttribute("title", "회원목록"); model.addAttribute("memberList", memberList); return "member/memberList"; } }
[코드]memberList.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <table> <thead> <tr> <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> </tr> </tbody> </table> </html>
그 다음 MemberService.java에서 기존에 선언했던 getMemberList() 메소드안에 권한이름 배열을 하나 만들고,
memberList를 순회해서 level 숫자를 가져와서 숫자에 맞는 권한이름을 memberLevelName에 세팅한다.
// dado 호출
List<Member> memberList = memberMapper.getMemberList();
String [] memberLevelArr = {"관리자", "판매자", "구매자", "테스트"};
if(memberList != null) {
memberList.froEach(member -> {
int memberLevel = member.getMemberLevel();
String memberLevelName = memberLevelArr[memberLevel-1];
member.setMemberLevelName(memberLevelName);
});
}
02. mybatis를 활용한 회원관리 - 회원가입
회원 가입(회원 등급 조회, 아이디 체크)
➡️ lombok 라이브러리 추가
- dto 생성시 @Data 를 해주면 getter, setter, tostring이 자동으로 생성된다.
[코드]MemberLevel.java (lombok 사용)
package ex.mybatis.dto; import lombok.Data; /** * @Data : get, set, tostring 자동생성 * @Setter : set만 생성 */ @Data // lombok public class MemberLevel { private int levelNum; private String levelName; private String levalRegDate; }
➡️ 회원 등급 조회, 아이디 체크 → select 쿼리
➡️ 회원 가입 → insert 쿼리
[코드]Mapper.xml
<select id="getMemberLevel" resultType="MemberLevel"> /* 회원등급 조회 */ SELECT l.level_num AS levelNum, l.level_name AS levelName FROM tb_member_level AS l </select> <select id="idCheck" resultType="boolean" parameterType="String"> /* 아이디 여부 */ SELECT IF(COUNT(m.m_id), 1, 0) FROM tb_member AS m WHERE m.m_id = #{memberId} </select> <insert id="addMember" parameterType="Member"> /* 회원가입 */ INSERT INTO tb_member (m_id, m_pw, m_name, m_level, m_email, m_addr, m_reg_date) VALUES (#{memberId}, #{memberPw}, #{memberName}, #{memberLevel}, #{memberEmail}, #{memberAddr}, CURDATE()) </insert>
[코드]Mapper.java
// 회원 등급 조회 public List<MemberLevel> getMemberLevel(); // 아이디 체크 public boolean idCheck(String memberId); // 회원가입 public int addMember(Member member);
[코드]MemberService.java
public List<MemberLevel> getMemberLevel() { // dao 호출 List<MemberLevel> memberLevelList = memberMapper.getMemberLevel(); return memberLevelList; } public void addMember(Member member) { // dao 호출 memberMapper.addMember(member); }
➡️ lombok : Slf4j, AllArgsConstructor
➡️ @PostMapping : POST방식으로 요청을 처리하겠다.
비동기통신을 하기 위해서는 클라이언트에서 서버로 요청 메세지를 보낼 때, 본문에 데이터를 담아서 보내야 하고, 서버에서 클라이언트로 응답을 보낼 때에도 본문에 데이터를 담아서 보내야 한다. 즉, 요청본문(requestBody), 응답본문(responseBody)
➡️ @RequestBody : json 기반의 HTTP Body를 자바 객체로 변환
➡️ @ResponseBody : 자바 객체를 json 기반의 HTTP Body로 변환
➡️ @RestController : 자동으로 @ResponseBody가 붙게 된다.
[코드]MemberController.java (lombok 사용)
package ksmart.mybatis.controller; import jakarta.annotation.PostConstruct; import ksmart.mybatis.dto.Member; import ksmart.mybatis.dto.MemberLevel; import ksmart.mybatis.mapper.MemberMapper; import ksmart.mybatis.service.MemberService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import java.util.List; @Controller //사용자 요청과 응답을 처리 @Slf4j // 롬복 @RequestMapping("/member") @AllArgsConstructor // 생성자 메소드 생성안해도 된다. public class MemberController { private final MemberService memberService; private final MemberMapper memberMapper; @PostConstruct public void memberControllerInit() { System.out.println("memberController 생성"); } /** * 회원목록 조회 * @param model * @return */ @GetMapping("/memberList") public String getMemberList(Model model) { List<Member> memberList = memberService.getMemberList(); model.addAttribute("title", "회원목록"); model.addAttribute("memberList", memberList); return "member/memberList"; } /** * 회원가입 화면(폼) * @param model * @return */ @GetMapping("/addMember") public String addMemeber(Model model) { List<MemberLevel> memberLevelList = memberService.getMemberLevelList(); log.info("회원등급조회 : {}", memberLevelList); model.addAttribute("title", "회원가입"); model.addAttribute("memberLevelList", memberLevelList); return "member/addMember"; } /** * 아이디 중복 체크 * @param memberId * @return */ @PostMapping("/idCheck") @ResponseBody public boolean idCheck(@RequestParam(value = "memberId") String memberId) { log.info("id 중복체크 : {}", memberId); boolean result = memberMapper.idCheck(memberId); log.info("id 중복체크 결과값:{}", result); return result; } @PostMapping("/addMember") public String addMember(Member member) { log.info("회원가입시 입력정보 : {}", member); memberService.addMember(member); // response.sendRedirect("/member/memberList"); // spring framework mvc에서는 controller의 리턴값에 redirect : 키워드로 작성 // redirect: 키워드를 작성할 경우 그 다음의 문자열은 html파일 논리 경로가 아닌 주소를 의미 return "redirect:/member/memberList"; } }
➡️ get, post 요청 / 유효성 검사, ajax 비동기 통신
[코드]addMember.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; } #idCheck { width: 99%; height: 25px; } #addMemberBtn, #resetBtn { width: 49%; height: 25px; } </style> </head> <th:block layout:fragment="customJs"> <script th:src="@{/js/jquery-3.7.0.js}"></script> <script> // 제이쿼리 사용. 클릭 이벤트 $('#idCheck').click(function () { const $memberId = $('#memberId'); let memberId = $memberId.val(); // 유효성 검사 let valid = validCheck($memberId); if(!valid) return false; // ajax 호출 const request = $.ajax({ url: '/member/idCheck', method: 'POST', data: { 'memberId' : memberId }, dataType: 'text' // json은 해당 타입을 유지해준다. }); // http 요청 성공시 (boolean 반환값) request.done(function( response ) { // json이면 if(response) { } if(response == 'true') { alert('이미 등록된 아이디입니다.'); $memberId.val(''); $memberId.focus(); return false; }else{ $('#addMemberForm input').prop('disabled', false); $('#addMemberForm select').prop('disabled', false); $('#addMemberForm button').prop('disabled', false); } // dataType이 text로 넘어와서 String으로 찍힌다. // console.log(typeof response); console.log(response); }); // http 요청 실패시 request.fail(function( jqXHR, textStatus ) { alert( "Request failed: " + textStatus ); }); }); // 유효성 검사 함수 const validCheck = element => { let data = $(element).val(); let eleId = $(element).attr('id'); if(typeof data == 'undefined' || data == null || data == '') { let cate = $(`label[for="${eleId}"]`).text(); alert(`${cate} 필수 입력항목입니다.`); $(element).focus(); return false; } return true; } // 회원아이디 변경 시 이벤트 처리 $('#memberId').change(function () { $('#addMemberForm input').not('#memberId').val(''); $('#addMemberForm input').not('#memberId').prop('disabled', true); $('#addMemberForm select').prop('disabled', true); $('#addMemberForm button').not('#idCheck').prop('disabled', true); }); // 회원가입 버튼 시 이벤트(유효성검사) $('#addMemberBtn').click(function () { const validateEle = $('#addMemberForm input').not('#memberId'); let isSubmit = false; validateEle.each((idx, item) => { isSubmit = validCheck(item); return isSubmit; }); if(isSubmit) { console.log('전송'); $('#addMemberForm').submit(); } }); // 입력 취소 버튼 클릭시 이벤트 처리 $('#resetBtn').click(function (e) { // 태그가 가지고 있는 기본 이벤트 동작 방지 e.preventDefault(); $('#addMemberForm input').not('#memberId').val(''); }); </script> </th:block> <th:block layout:fragment="customContents"> <form id="addMemberForm" th:action="@{/member/addMember}" method="post"> <table> <tbody> <tr> <td> <label for="memberId">회원아이디</label> </td> <td> <input type="text" id="memberId" name="memberId" placeholder="회원의 아이디를 입력해주세요."/> </td> <td> <button type="button" id="idCheck">아이디 중복체크</button> </td> </tr> <tr> <td> <label for="memberPw">회원비밀번호</label> </td> <td colspan="2"> <input type="text" id="memberPw" name="memberPw" placeholder="회원의 비밀번호를 입력해주세요." disabled="disabled"/> </td> </tr> <tr> <td> <label for="memberName">회원이름</label> </td> <td colspan="2"> <input type="text" id="memberName" name="memberName" placeholder="회원의 이름을 입력해주세요." disabled="disabled"/> </td> </tr> <tr> <td> <label for="memberLevel">회원등급</label> </td> <td colspan="2"> <select id="memberLevel" name="memberLevel" disabled="disabled"> <th:block th:unless="${#lists.isEmpty(memberLevelList)}" th:each="l : ${memberLevelList}"> <option th:value="${l.levelNum}">[[${l.levelName}]]</option> </th:block> <th:block th:if="${#lists.isEmpty(memberLevelList)}"> <option th:value="">등록된 회원의 등급이 없습니다.</option> </th:block> </select> </td> </tr> <tr> <td> <label for="memberEmail">회원이메일</label> </td> <td colspan="2"> <input type="text" id="memberEmail" name="memberEmail" placeholder="회원의 이메일을 입력해주세요." disabled="disabled"/> </td> </tr> <tr> <td> <label for="memberAddr">회원주소</label> </td> <td colspan="2"> <input type="text" id="memberAddr" name="memberAddr" placeholder="회원의 주소를 입력해주세요." disabled="disabled"/> </td> </tr> <tr> <td colspan="3"> <button type="button" id="addMemberBtn" disabled="disabled">회원가입</button> <button type="reset" id="resetBtn" disabled="disabled">입력취소</button> </td> </tr> </tbody> </table> </form> </th:block> </html>
tag : #Spring #Mybatis #Controller #Service #Mapper #SQL #Lombok
Uploaded by N2T