-
22-09-21_jsp(2)main 2022. 9. 21. 20:49
B.L의 인터페이스까지 만들었음.
이제 createMember 완성할건데 (아이디 중복검사)
아이디 있음 없음 중복
음 enum의 세가지 상수로만은 세가지 케이스를 표현할 수 없어요.
enum을 수정해줍시다.
package kr.or.ddit.enumpkg; public enum ServiceResult { OK, FAIL, INVALIDPASSWORD, PKDUPLICATED }
이제
UserNotFoundException 를 만듭시다.
package kr.or.ddit.commons.exception; // 이 예외가 발생하면 // 조건 1. 2. 수동으로 제어권 이동을 하지 않더라도 호출한데로 제어권 이동 // 단순히 pk가 없는 exception이 아니라 이렇게 상속받아서 확장하면 회원이 존재하지 않을 때 라는 // 구제적인 상황이 생김 public class UserNotFoundException extends PKNotFoundException { private String memId; //superclass를 이용한 생성자 생성에서 이거 하나만 받아서 생성. public UserNotFoundException(String memId) { this.memId = memId; } @Override public String getMessage() { return String.format("%s 아이디 회원이 존재하지 않음.", this.memId); } }
B.L의 인터페이스
경우의 수에 따라서 설명, 정의를 완성하기.
package kr.or.ddit.member.service; import java.util.List; import kr.or.ddit.enumpkg.ServiceResult; import kr.or.ddit.vo.MemberVO; /** * * 회원 관리(Business Logic Layer), CRUD * */ public interface MemberService { /** * 회원 신규 등록 * @param member * @return {@link ServiceResult, PKDUPLICATED}, {@link ServiceResult, OK}, {@link ServiceResult, FAIL}, */ public ServiceResult createMember(MemberVO member); /** * 회원 상세조회 * @param memId 조회할 회원아이디 * @return 존재하지 않는 경우, {@link UserNotFoundException} 발생. */ public MemberVO retrieveMember(String memId); /** * 회원 목록 조회, 차후 페이징 처리와 검색기능 추가 예정 * @return */ public List<MemberVO> retrieveMemberList(); /** * 회원 정보 수정. * @param member * @return 존재하지 않는 경우, {@link UserNotFoundException} 발생. * {@link ServiceResult, INVALIDPASSWOED}, * {@link ServiceResult, OK}, {@link ServiceResult, FAIL} */ public ServiceResult modifyMember(MemberVO member); // INVALIDPASSWOED가 있다는 건 이 두 로직에서 인증이라는게 발생해야 한다는 것 /** * 회원 정보 제거 * @param member * @return 존재하지 않는 경우, {@link UserNotFoundException} 발생. * {@link ServiceResult, INVALIDPASSWOED}, * {@link ServiceResult, OK}, {@link ServiceResult, FAIL} */ public ServiceResult removeMember(MemberVO member); }
CL, VL과 나머지 model layer의 큰 차이.
CL, VL은 client과 직접 대면, 나머지는 client와 직접 대면하지 않음.
즉, CL, VL은 web에 종속됨. http라는 protocol과 종속되는 구조,
그런데 나머지 model layer는 종속되지 않음. 걍 엑셀로 만들어도 되고 그럼
legacy module이라고 함 그래서, web, protocol 등에 상관없이 동작한다.
즉 지금 상태로 test case를 만들면 test 가 가능하다 .
그런데 CL, VL은 실행하려면 request 받아야 해서 server를 돌려야 가능함. Junit 혼자만으로 test 불가능함.
--
test를 해봅시다
--
dao와 service를 test 해봅시다
1. dao완성
2. case 완성
3. case완성
4. service 완성
---
음 service에 servlet을 만들건데여, 아래는 조건?임
/** * RESTful URI * restful적용하면 GET * /member (GET) * /member/a001 (GET) --> a001한명만 조회하겠다. * /member/a001 (PUT) --> a001한명의 정보를 수정하겠다. * /member/a001 (DELETE) --> a001한명을 탈퇴시키겠다. * * //명령은 다 다른데 주소는 같음. 행위들을 표현하고있음. 이걸 RESTful URI라고 함. * * 지금 당장은 우리가 이런 형태의 URI를 사용할 순 없지만, * non-RESTful URI * 구조로 설계를 해 보자 * /member/memberList.do(GET) * /member/memberView.do?who=a001(GET) * /member/memberInsert.do(GET) ->회원가입 양식을 먼저 제공하겠다. * /member/memberInsert.do(POST) -> 입력된 정보는 개인정보가 포함되어있으니 post로 보내겠다. * /member/memberUpdate.do?who=a001 (GET) -> 한 사람의 정보를 수정하겠다는건데, get은 개인정보 수정 폼을 제공 * /member/memberUpdate.do?who=a001 (POST) -> 입력한 데이터가 server로 넘어가는 것 * /member/memberDelete.do (POST)?who=a001 (POST) ->password있어서 post * * 그런데 로그인 되어 있어야 한다는 전제조건이 있다고 한다면, 뒤에 ?who=a001 는 필요가 없음. * 지금은 로그인이 처리가 안 되어 있으니까 위에 있는 그대로 만들어보자. * *
나머지 모든 코드를 만들어서.. view 를 이용해서... 이 명령을 처리할 수 있는 service, dao의 logic, test, view를 완성해보세요
1. 제일 먼저 daoimpl의 메서드 완성
@Override public List<MemberVO> selectMemberList() { StringBuffer sql = new StringBuffer(); try ( Connection conn = ConnectionFactory.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql.toString()); ){ ResultSet rs = pstmt.executeQuery(); while(rs.next()) { } }catch (SQLException e) { throw new RuntimeException(e); //전형성에서 출발하는 설계방식. 언제나 유사/동일한 구조로 설계됨 //모든 메서드에서 동일한 코드가 종속되고 있음 } return null; }
반복되는 전형적인 코드들은 sql에서 코드조각 저장 했던 것처럼 저장 해 둘 수 있는데요
window-show view-snippets에 가서 등록하면 됩니다.
자세한 방법은 상단의 이미지 참고
어쨌든 반복되서 중복되는 코드가 있다는거.... 뭐.. 알아두라고...
나중에 insert 나 update 같은 쿼리문 이렇게 해서 가져오기
필요한 부분 정리해서 아래처럼 가져오기
SELECT MEM_ID, MEM_NAME, MEM_ADD1, MEM_HP, MEM_MAIL, MEM_MILEAGE, FROM MEMBER;
@Override public List<MemberVO> selectMemberList() { StringBuffer sql = new StringBuffer(); //반복되지 않는 파트1 sql.append(" SELECT "); sql.append(" MEM_ID, MEM_NAME, MEM_ADD1, "); sql.append(" MEM_HP, MEM_MAIL, MEM_MILEAGE "); sql.append(" FROM MEMBER "); try ( Connection conn = ConnectionFactory.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql.toString()); ){ ResultSet rs = pstmt.executeQuery(); //반복되지 않는 파트2 //0이면 조회는 되는데 테이블에 데이터가 없는것. List<MemberVO> memberList = new ArrayList<>(); while(rs.next()) { //조회되는 레코드의 갯수만큼 add가 되어서 memberList의 사이즈와 조회되는 레코드의 갯수 동일 MemberVO member = new MemberVO(); memberList.add(member); member.setMemId(rs.getString("MEM_ID")); member.setMemName(rs.getString("MEM_NAME")); member.setMemAdd1(rs.getString("MEM_ADD1")); member.setMemHp(rs.getString("MEM_HP")); member.setMemMail(rs.getString("MEM_MAIL")); member.setMemMileage(rs.getInt("MEM_MILEAGE")); } return memberList; }catch (SQLException e) { throw new RuntimeException(e); } }
위의 쿼리문을 넣어서 memberDAOImple의 메서드를 완성해 줍니다.
여기서 보면, 반복되지 않는 파트 1, 2를 제외하고는 순서도 내용도 완전히 반복되는 모습을 볼 수 있음.
순서는 바뀌지 않는데, 해당 순서 안에서 해야 하는 작업들만 다른 것. ...
이제
test를 돌려보면
이렇게 테스트를 돌려서, 저게 저렇게 나오면 잘 된거고,
이제 service test 해 줄거예요
error만안나면 된거같아요
serviceImple은 적어줄게 별로 없죠
//의존관계 표현 위해 dao들어와야 함. 이렇게 결합력?이발생함 private MemberDAO dao = new MemberDAOImpl(); List<MemberVO> list = dao.selectMemberList(); @Override public List<MemberVO> retrieveMemberList() { return dao.selectMemberList(); }
dao객체와의 의존관계가 있기 때문에 객체 선언 해 주고,
메서드 저렇게 완성시켜줌.
그리고 servlet이
package kr.or.ddit.member.controller; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import kr.or.ddit.member.service.MemberService; import kr.or.ddit.member.service.MemberServiceImpl; import kr.or.ddit.vo.MemberVO; /** * RESTful URI * restful적용하면 GET * /member (GET) * /member/a001 (GET) --> a001한명만 조회하겠다. * /member/a001 (PUT) --> a001한명의 정보를 수정하겠다. * /member/a001 (DELETE) --> a001한명을 탈퇴시키겠다. * * //명령은 다 다른데 주소는 같음. 행위들을 표현하고있음. 이걸 RESTful URI라고 함. * * 지금 당장은 우리가 이런 형태의 URI를 사용할 순 없지만, * non-RESTful URI * 구조로 설계를 해 보자 * /member/memberList.do(GET) * /member/memberView.do?who=a001(GET) * /member/memberInsert.do(GET) ->회원가입 양식을 먼저 제공하겠다. * /member/memberInsert.do(POST) -> 입력된 정보는 개인정보가 포함되어있으니 post로 보내겠다. * /member/memberUpdate.do?who=a001 (GET) -> 한 사람의 정보를 수정하겠다는건데, get은 개인정보 수정 폼을 제공 * /member/memberUpdate.do?who=a001 (POST) -> 입력한 데이터가 server로 넘어가는 것 * /member/memberDelete.do (POST)?who=a001 (POST) ->password있어서 post * * 그런데 로그인 되어 있어야 한다는 전제조건이 있다고 한다면, 뒤에 ?who=a001 는 필요가 없음. * 지금은 로그인이 처리가 안 되어 있으니까 위에 있는 그대로 만들어보자. * * */ @WebServlet("/member/memberList.do") public class MemberListServlet extends HttpServlet{ private MemberService service = new MemberServiceImpl(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<MemberVO> memberList = service.retrieveMemberList(); req.setAttribute("memberList", memberList); String commandPage = "/WEB-INF/views/member/memberList.jsp"; req.setAttribute("commandPage", commandPage); String viewName = "/WEB-INF/views/template.jsp"; req.getRequestDispatcher(viewName).forward(req, resp); } }
이렇게 하면 됨
--
jsp....
<%@page import="kr.or.ddit.vo.MemberVO"%> <%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <table class="table table-bordered"> <thead> <tr> <th>회원아이디</th> <th>회원명</th> <th>이메일</th> <th>휴대폰</th> <th>지역</th> <th>마일리지</th> </tr> </thead> <tbody> <% List<MemberVO> memberList = (List)request.getAttribute("memberList"); if(memberList.isEmpty()){ %> <tr> <td colspan="6">회원이 없음.</td> </tr> <% }else{ for(MemberVO member : memberList){ %> <tr> <td><%=member.getMemId() %></td> <td><%=member.getMemName() %></td> <td><%=member.getMemMail() %></td> <td><%=member.getMemHp() %></td> <td><%=member.getMemAdd1() %></td> <td><%=member.getMemMileage() %></td> </tr> <% } } %> <td>회원아이디</td> <td>회원명</td> <td>이메일</td> <td>휴대폰</td> <td>지역</td> <td>마일리지</td> </tbody> </table>
--
jsp. 화면이 잘 나오네요
이제 페이징 처리 하실거래여
--
이제 위에 jsp 소스에서
이부분을 이렇게 속성으 ㄹ주면, 저 tr을 누르면 memId에 따라서 아래 td 정보들이 나올 수 있도록 a tag 처럼 정해 준 거예요 --> 이게 미션인듯
이제 이거 처리할거래요 ---
??
뭔가 미션을 주심 ㅎ...
위에 미션인듯 한거.. 저거 누르면 정보가 뜨게 하는거인듯함.
회원이름을 누르면 modal창으로 상세정보 띄우기
뭐지
--
https://getbootstrap.com/docs/5.1/components/modal/#fullscreen-modal
Modal
Use Bootstrap’s JavaScript modal plugin to add dialogs to your site for lightboxes, user notifications, or completely custom content.
getbootstrap.com
--
modal은 안 떳지만 정상적으로 생겼음... modal이 닫혀도 정보가 남아있음. modal을 닫으면 clear해서 없애주는 event를 정해줘야 함... https://getbootstrap.com/docs/5.1/components/modal/#events
Modal
Use Bootstrap’s JavaScript modal plugin to add dialogs to your site for lightboxes, user notifications, or completely custom content.
getbootstrap.com
hide : 닫히기 전까지 발생하는 이벤트
hidden : modal이 완전히 닫힌 후에 발생하는 이벤트
<%@page import="kr.or.ddit.vo.MemberVO"%> <%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <div></div> <table class="table table-bordered"> <thead> <tr> <th>회원아이디</th> <th>회원명</th> <th>이메일</th> <th>휴대폰</th> <th>지역</th> <th>마일리지</th> </tr> </thead> <tbody> <% List<MemberVO> memberList = (List)request.getAttribute("memberList"); if(memberList.isEmpty()){ %> <tr> <td colspan="6">회원이 없음.</td> </tr> <% }else{ for(MemberVO member : memberList){ %> <tr class = "dataTr" data-who="<%=member.getMemId() %>" data-bs-toggle="modal" data-bs-target="#exampleModal"> <td><%=member.getMemId() %></td> <td><%=member.getMemName() %></td> <td><%=member.getMemMail() %></td> <td><%=member.getMemHp() %></td> <td><%=member.getMemAdd1() %></td> <td><%=member.getMemMileage() %></td> </tr> <% } } %> <td>회원아이디</td> <td>회원명</td> <td>이메일</td> <td>휴대폰</td> <td>지역</td> <td>마일리지</td> </tbody> </table> <!-- 전송이벤트 발생 위해 사용, get 방식으로 넘김 --> <form id="viewForm" action="<%=request.getContextPath()%>/member/memberview.do"> <input type="hidden" name="who" /> </form> <!-- Modal --> <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">Modal title</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <!-- 응답데이터 넣어줘야 --> <div class="modal-body"> ... </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div> <script type="text/javascript"> let viewModal = $("#exampleModal").on("hidden.bs.modal", function(event){ //안에 들은 inner들을 다 지우라.... $(this).find(".modal-body").empty(); //event를 잡아서 특정 handler 안에서 처리중임 이 방식이 EDD, 이벤트 주도형.... }); let viewForm = $("#viewForm").on("submit",function(event){ event.preventDefault(); //동기요청 취소 //들어가는 데이터가 form에서 다 가져와야 함 let url = this.action; let method = this.method; let data = $(this).serialize(); //ajaxForm 적용, --미션1번 $.ajax({ url : url, method : method, data : data, dataType : "html", success : function(resp) { viewModal.find(".modal-body").html(resp); viewModal.modal('show'); }, //경우에따라 404나 500 에러 날 수도 있음. modal로 띄워보자 error : function(errorResp) { console.log(errorResp.status); viewModal.find(".modal-body").html(errorResp.responseText); viewModal.modal('show'); } }); return false; }); //DOM트리 전체에 이벤트 특정타겟지칭 $(document).on("click",".dataTr",function(event){ let who = $(this).data('who'); viewForm.find('[name=who]').val(who); viewForm.submit(); viewForm.get(0).reset(); <%--뒤로가기했을때 담아놨던걸 없애줘야 하기 때문... --%> <%-- location.href ="<%=request.getContextPath()%>/member/memberview.do?who"+who; 대신 다른 거 써보자, who를 직접 입력해주지 않기 위해서 --%> }); /* $(".dataTr").on("click",function(){ alert("alert window."); location.href(서블릿) // /member/memberList.do 절대경로로 이동 }) */ </script>
일단세이브. tr의 클래스 없애서 다른 방식으로 할 것
오늘 테스트코드쓰면서 TDD 테스트 주도형 방법론 써 와쓴ㄴ데
EDD, 이벤트 주도형 방법론도 쓸 수 있음
js파일을 따로 쪼개서 처리하고 이거저거 더했어요
<%@page import="kr.or.ddit.vo.MemberVO"%> <%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <div></div> <table class="table table-bordered"> <thead> <tr> <th>회원아이디</th> <th>회원명</th> <th>이메일</th> <th>휴대폰</th> <th>지역</th> <th>마일리지</th> </tr> </thead> <tbody> <% List<MemberVO> memberList = (List)request.getAttribute("memberList"); if(memberList.isEmpty()){ %> <tr> <td colspan="6">회원이 없음.</td> </tr> <% }else{ for(MemberVO member : memberList){ %> <tr data-who="<%=member.getMemId() %>" data-bs-toggle="modal" data-bs-target="#exampleModal"> <td><%=member.getMemId() %></td> <td><%=member.getMemName() %></td> <td><%=member.getMemMail() %></td> <td><%=member.getMemHp() %></td> <td><%=member.getMemAdd1() %></td> <td><%=member.getMemMileage() %></td> </tr> <% } } %> <td>회원아이디</td> <td>회원명</td> <td>이메일</td> <td>휴대폰</td> <td>지역</td> <td>마일리지</td> </tbody> </table> <!-- 전송이벤트 발생 위해 사용, get 방식으로 넘김 --> <form id="viewForm" action="<%=request.getContextPath()%>/member/memberview.do"> <input type="hidden" name="who" /> </form> <!-- Modal --> <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">Modal title</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <!-- 응답데이터 넣어줘야 --> <div class="modal-body"> ... </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div> <script type="text/javascript" src ='<%=request.getContextPath() %>/resources/js/member/memberList.js'> <%-- //class지워서 동작 안 함 //DOM트리 전체에 이벤트 특정타겟지칭 $(document).on("click",".dataTr",function(event){ let who = $(this).data('who'); viewForm.find('[name=who]').val(who); viewForm.submit(); viewForm.get(0).reset(); <%--뒤로가기했을때 담아놨던걸 없애줘야 하기 때문... --%> <%-- location.href ="<%=request.getContextPath()%>/member/memberview.do?who"+who; 대신 다른 거 써보자, who를 직접 입력해주지 않기 위해서 });--%> /* $(".dataTr").on("click",function(){ alert("alert window."); location.href(서블릿) // /member/memberList.do 절대경로로 이동 }) */ </script>
--js
<%=request.getContextPath() %>
이거 넣기 귀찮죠. 나중에는 Listener 라는 개념으로 처리할거예요
--to_char; formatting --to_date; parsing --to_number; parsing SELECT mem_id, mem_pass, mem_name, mem_regno1, mem_regno2, TO_CHAR(mem_bir,'YYYY-MM-DD') mem_bir, mem_zip, mem_add1, mem_add2, mem_hometel, mem_comtel, mem_hp, mem_mail, mem_job, mem_like, mem_memorial, TO_CHAR(mem_memorialday,'YYYY-MM-DD') mem_memorialday, mem_mileage, mem_delete FROM member WHERE MEM_ID = 'a001';
--member.setMemId(rs.getString("MEM_ID")); SELECT LOWER(TABLE_NAME) || '.set' || REPLACE(INITCAP(COLUMN_NAME),'_','') || '(rs.get' || DECODE(DATA_TYPE, 'NUMBER', 'Int', 'String') || '("' || COLUMN_NAME || '"));' FROM COLS WHERE TABLE_NAME = 'MEMBER';
여기 comment도 다 scheme 구조임... COMMENT ON COLUMN MEMBER.MEM_ID IS '회원아이디';
이렇게 달 수 있음
SELECT * FROM user_col_comments where table_name ='MEMBER';
COMMENT ON COLUMN MEMBER.MEM_ID IS '회원아이디'; --<tr> -- <th>회원아이디</th> -- <td><%=member.getMemId() %></td> --</tr> SELECT '<tr><th>' || NVL(comments, COLUMN_NAME)|| --있는 애는 회원아이디 없는 애는 이름이 들어감 '</th><td>' || '<%='|| LOWER(TABLE_NAME) || '.get' || REPLACE(INITCAP(column_name),'_','') || '() %></td></tr>' FROM user_col_comments where table_name ='MEMBER';
html 소스를 만들자요
scheme를 활용하기 위해 dictionary 구조를 활용하는 방법이예요...
'main' 카테고리의 다른 글
22-09-23_jsp(1) (1) 2022.09.23 정규식 편하게하는 라이브러리 (0) 2022.09.23 22-09-21_jsp(1) (0) 2022.09.21 22-09-20_jsp (2) 2022.09.20 22-09-20_python (0) 2022.09.20