본문 바로가기
WEB/✿Spring Framework

[Spring] 🔗10)Spring 실습1

by W_W_Woody 2022. 3. 8.

pom.xml

jackson, 파일업로드 라이브러리 추가.

파일 다운로드가 먼저 동작되게 하기 위해서 servlet-context.xml에서 viewResolver추가하고, 우선순위를 넣어준다.

servlet-context.xml

타일스 설정
오토스캔 설정

템플릿 작업을 해준다.

layout.jsp

<%@ taglib prefix="tiles"  uri="http://tiles.apache.org/tags-tiles"  %>

<link rel="stylesheet"  href=${pageContext.request.contextPath}/resources/css/style.css" >

<tiles:getAsString name="  "/>

<tiles:insertAttribute name="  "/>

 

tiles-def 작업을 하고

<main.xml>

controller작업을 해준다.

이 때 servlet-context.xml에서 설정작업이 되어있어야한다.

기본 테스트 완료!


공통 선 작업

table.sql작업을 해주고

1. MemberVO 작업

 여기서 @어노테이션을 이용하여 유효성체크하는 방법 이용할 것

2. MemberMapper인터페이스(DAO) 생성

2-1. 자동으로 매핑하니까 클래스 안만든다.

3. MemberService인터페이스 생성

 여기서 dao호출하니까 메서드 명을 같이 하는게 헷갈리지않으므로 MemberMapper인터페이스(DAO)에서 복사해온다.

 여러메서드를조합해서 하나의 트랜잭션을 처리하므로 필요없는 것들은 지운다.

3-1. 클래스(MemberServiceImple)에 구현

  MemberMapper(dao)주입받기

4. MemberController생성

  MemberService 주입

  자바스크립트가 아니라 @어노테이션으로 유효성 체크를 하기 때문에 자바빈 초기화 작업을 해준다,

5. root-context.xml 설정

  mybatis 루트 재설정, 자동스캔 루트 재설정.


1. MemberVO 작업

테이블에는 없지만

변수(비밀번호 변경시 현재 비밀번호를 저장하는 용도로 사용)를 하나 적어놓는다.

메서드(비밀번호 일치 여부 체크)도 넣는다.

*BLOB(binary large object) :

일반적으로 데이터베이스에 보관되는 자료는 텍스트 형태이고,

blob 의 경우 텍스트가 아닌 이진 데이터 형태를 그대로 보관하기 때문에 사진이나 파일, 동영상과 같은 자료도 데이터베이스에 보관할 수가 있다.

 

MultipartFile타입으로 받으면 blob타입으로 변환시켜야 하기 때문에
setUpload()에서 바이트배열로 만들면 MyBatis가 이미지 blob처리를 한다.
그렇게 되면 기존에 upload폴더에 넣는 것도 가능하고 blob처리하는 것도 가능하다.

 

toString을 Generate할 때 byte[] 타입의 데이터는 제외(너무 길게나와서 프로그램이 느려진다.)

2. MemberMapper인터페이스(DAO) 생성

3. MemberService인터페이스 생성

3-1. 클래스(MemberServiceImple)에 구현

4. MemberController생성

5. root-context.xml에서 mybatis설정해준다.


회원 등록

1) 회원 등록 폼 호출

1. MemberController에서 회원등록폼 호출

2. 타일즈 설정

2-1. main.xml을 복제하여 member.xml을 새로 만들고

2-2. servlet-context.xml에서 링크를 걸어준다.

3. memberRegister.jsp 생성 및 작성

4. validation.properties를 작성해놓는다.

4-1. header.jsp 작성

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 조건체크를 위해 jstl -->

<!-- 상단 시작 -->
<h2 class="align-center">Star Hompy</h2>
<div class="align-right">
	<!-- 조건체크 -->
	<c:if test="${!empty user_num}">
		[<span>${user_id}</span>]
		<a href="${pageContext.request.contextPath}/member/logout.do">로그아웃</a>
	</c:if>
	
	<c:if test="${empty user_num}">
		<a href="${pageContext.request.contextPath}/member/registerUser.do">회원가입</a>
		<a href="${pageContext.request.contextPath}/member/login.do">로그인</a>
	</c:if>
	
	<c:if test="${!empty user_num && user_auth==2}">
		<a href="${pageContext.request.contextPath}/member/myPage.do">마이페이지</a>
	</c:if>
	
	<a href="${pageContext.request.contextPath}">홈으로</a>
</div>
<!-- 상단 끝 -->

2) 회원등록 처리

1. MemberMapper (DAO)에서 등록작업을 한다.

2. Service(MemberServiceImpl.java)

자바빈(VO)에 넣어준다.

3. Controller(MemberController.java)

 

3) 아이디 중복 체크 *Ajax통신

1. MemberMapper (DAO)에서 등록작업을 한다.

2. Service(MemberServiceImpl.java)

3. Controller(MemberAjaxController.java)

@ResponseBody 로 JSON문자열을 만든다.

4. 제이쿼리 작업을 하니까

5. memberRegister.jsp

<script src="${pageContext.request.contextPath}/resources/js/jquery-3.6.0.min.js"></script>

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!-- 중앙 컨텐츠 시작 -->
<script src="${pageContext.request.contextPath}/resources/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
	$(function () {
		let checkId = 0;
		
		//아이디 중복체크
		$('#confirmId').click(function () {
			if($('#id').val().trim()==''){
				$('#message_id').css('color','red').text('아이디를 입력하세요');
				$('#id').val('').focus();
				return; //submit이 아니라 함수만 빠져나가도록?
			}
			$.ajax({
				url:'confirmId.do',
				type:'post',
				data:{id:$('#id').val()},
				dataType:'json',
				cache:false,
				timeout:30000,
				success:function(param){
					if(param.result=='idNotFound'){
						$('#message_id').css('color','#000').text('등록 가능 ID');
						checkId = 1;
					}else if (param.result=='idDuplicated') {
						$('#message_id').css('color','red').text('중복된 ID');
						$('#id').val('').focus(); //새로입력할 수 있도록 지워줌
						checkId = 0;
					}else if (param.result=='notMatchPattern') {
						$('#message_id').css('color','red').text('영문,숫자 4~12');
						$('#id').val('').focus();
						checkId=0;
					}else{
						checkId=0;
						alert('ID중복체크 오류');
					}
				},
				error:function(){
					checkId = 0;
					alert('네트워크 오류 발생')
				}
			})
		})//end of click
		
		//아이디 중복 안내 메세지 초기화 및 아이디 중복 값 초기화
		$('#register_form #id').keyup(function () {
			checkId=0;
			$('#message_id').text('');
		});
		
		//submit이벤트 발생 시 아이디 중복 체크 여부 확인
		$('#register_form').submit(function () {
			if(checkId==0){
				$('#message_id').css('color','red').text('아이디 중복 체크 필수');
				if($('#id').val().trim()==''){
					$('#id').val('').focus();
				}
				return false;
			}
		});
	});
</script>
<div class="page-main">
	<h2>회원가입</h2> 		<!-- modelAttribute에 자바빈 초기화 한거 들어옴 -->
	<form:form modelAttribute="memberVO" action="registerUser.do"
				id="register_form">
		<form:errors element="div" cssClass="error-color"/><!-- 필드가없는 에러 -->
		<ul>
			<li>
				<form:label path="id">아이디</form:label>
				<form:input path="id" placeholder="영문, 숫자 4~12 허용"/>
				<input type="button" id="confirmId" value="ID중복체크">
				<span id="message_id"></span> <!-- 자바스크립트로 체크한 메세지 보여지도록 -->
				<form:errors path="id" cssClass="error-color"/>
			</li>
			<li>
				<form:label path="name">이름</form:label>
				<form:input path="name"/>
				<form:errors path="name" cssClass="error-color"/>
			</li>
			<li>
				<form:label path="passwd">비밀번호</form:label>
				<form:password path="passwd" placeholder="영문, 숫자 4~12 허용"/>
				<form:errors path="passwd" cssClass="error-color"/>
			</li>
			<li>
				<form:label path="phone">전화번호</form:label>
				<form:input path="phone"/>
				<form:errors path="phone" cssClass="error-color"/>
			</li>
			<li>
				<form:label path="email">이메일</form:label>
				<form:input path="email"/>
				<form:errors path="email" cssClass="error-color"/>
			</li>
			<li>
				<form:label path="zipcode">우편번호</form:label>
				<form:input path="zipcode"/>
				<input type="button" onclick="sample2_execDaumPostcode()" value="우편번호 찾기"/>
				<form:errors path="zipcode" cssClass="error-color"/>
			</li>
			<li>
				<form:label path="address1">주소</form:label>
				<form:input path="address1"/>
				<form:errors path="address1" cssClass="error-color"/>
			</li>
			<li>
				<form:label path="address2">상세주소</form:label>
				<form:input path="address2"/>
				<form:errors path="address2" cssClass="error-color"/>
			</li>
		</ul>
		<div class="align-center">
			<form:button>전송</form:button>
			<input type="button" value="홈으로 "
					onclick="location.href='${pageContext.request.contextPath}/main/main.do'">
		</div>
	</form:form>
	
<!-- 우편번호 스크립트 시작 -->
<!-- iOS에서는 position:fixed 버그가 있음, 적용하는 사이트에 맞게 position:absolute 등을 이용하여 top,left값 조정 필요 -->
<div id="layer" style="display:none;position:fixed;overflow:hidden;z-index:1;-webkit-overflow-scrolling:touch;">
<img src="//t1.daumcdn.net/postcode/resource/images/close.png" id="btnCloseLayer" style="cursor:pointer;position:absolute;right:-3px;top:-3px;z-index:1" onclick="closeDaumPostcode()" alt="닫기 버튼">
</div>

<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
    // 우편번호 찾기 화면을 넣을 element
    var element_layer = document.getElementById('layer');

    function closeDaumPostcode() {
        // iframe을 넣은 element를 안보이게 한다.
        element_layer.style.display = 'none';
    }

    function sample2_execDaumPostcode() {
        new daum.Postcode({
            oncomplete: function(data) {
                // 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.

                // 각 주소의 노출 규칙에 따라 주소를 조합한다.
                // 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
                var addr = ''; // 주소 변수
                var extraAddr = ''; // 참고항목 변수

                //사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
                if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
                    addr = data.roadAddress;
                } else { // 사용자가 지번 주소를 선택했을 경우(J)
                    addr = data.jibunAddress;
                }

                // 사용자가 선택한 주소가 도로명 타입일때 참고항목을 조합한다.
                if(data.userSelectedType === 'R'){
                    // 법정동명이 있을 경우 추가한다. (법정리는 제외)
                    // 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
                    if(data.bname !== '' && /[동|로|가]$/g.test(data.bname)){
                        extraAddr += data.bname;
                    }
                    // 건물명이 있고, 공동주택일 경우 추가한다.
                    if(data.buildingName !== '' && data.apartment === 'Y'){
                        extraAddr += (extraAddr !== '' ? ', ' + data.buildingName : data.buildingName);
                    }
                    // 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
                    if(extraAddr !== ''){
                        extraAddr = ' (' + extraAddr + ')';
                    }
                    //(주의)address1에 참고항목이 보여지도록 수정
                    // 조합된 참고항목을 해당 필드에 넣는다.
                    //(수정) document.getElementById("address2").value = extraAddr;
                
                } 
                //(수정) else {
                //(수정)    document.getElementById("address2").value = '';
                //(수정) }

                // 우편번호와 주소 정보를 해당 필드에 넣는다.
                document.getElementById('zipcode').value = data.zonecode;
                //(수정) + extraAddr를 추가해서 address1에 참고항목이 보여지도록 수정
                document.getElementById("address1").value = addr + extraAddr;
                // 커서를 상세주소 필드로 이동한다.
                document.getElementById("address2").focus();

                // iframe을 넣은 element를 안보이게 한다.
                // (autoClose:false 기능을 이용한다면, 아래 코드를 제거해야 화면에서 사라지지 않는다.)
                element_layer.style.display = 'none';
            },
            width : '100%',
            height : '100%',
            maxSuggestItems : 5
        }).embed(element_layer);

        // iframe을 넣은 element를 보이게 한다.
        element_layer.style.display = 'block';

        // iframe을 넣은 element의 위치를 화면의 가운데로 이동시킨다.
        initLayerPosition();
    }

    // 브라우저의 크기 변경에 따라 레이어를 가운데로 이동시키고자 하실때에는
    // resize이벤트나, orientationchange이벤트를 이용하여 값이 변경될때마다 아래 함수를 실행 시켜 주시거나,
    // 직접 element_layer의 top,left값을 수정해 주시면 됩니다.
    function initLayerPosition(){
        var width = 300; //우편번호서비스가 들어갈 element의 width
        var height = 400; //우편번호서비스가 들어갈 element의 height
        var borderWidth = 5; //샘플에서 사용하는 border의 두께

        // 위에서 선언한 값들을 실제 element에 넣는다.
        element_layer.style.width = width + 'px';
        element_layer.style.height = height + 'px';
        element_layer.style.border = borderWidth + 'px solid';
        // 실행되는 순간의 화면 너비와 높이 값을 가져와서 중앙에 뜰 수 있도록 위치를 계산한다.
        element_layer.style.left = (((window.innerWidth || document.documentElement.clientWidth) - width)/2 - borderWidth) + 'px';
        element_layer.style.top = (((window.innerHeight || document.documentElement.clientHeight) - height)/2 - borderWidth) + 'px';
    }
</script>
<!-- 우편번호 스크립트 끝 -->

</div>
<!-- 중앙 컨텐츠 끝-->

회원 로그인

1) 로그인 폼 호출

1. Controller(MemberController.java)

2. member.xml

tiles 설정 

3. meberLogin.jsp

폼 구현 화면

2) 로그인 체크

1. 사용자 정의 예외 클래스(AuthCheckException.java) 생성

2. Controller(MemberController.java)

hasFieldErrors()

회원 로그아웃

1. Controller(MemberController.java)

session.invalidate();

회원 마이페이지

1. MemberMapper (DAO)에서 등록작업을 한다.

마이페이지를 띄우려면 한 건의 레코드를 읽어와야한다.

2. Service(MemberServiceImpl.java)

3. Controller(MemberController.java)

이 때 회원제 서비스 이므로 로그인여부 체크를 해줘야하는데,

interceptor방식을 이용하면.

코드에 반복적으로 작성하지 않아도 체크되는 장점이있다.

 

4. member.xml

tiles 설정 

5. memberView.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 조건체크를 위해 jstl -->

<!-- 중앙 컨텐츠 시작 -->
<div class="page-main">
	<h2>프로필 사진</h2>
	<ul>
		<li></li>
	</ul>
	
	<h2>회원 상세 정보</h2>
	<ul>
		<li>이름 : ${member.name}</li>
		<li>전화번호 : ${member.phone}</li>
		<li>이메일: ${member.phone}</li>
		<li>우편번호: ${member.zipcode}</li>
		<li>주소 : ${member.address1}</li>
		<li>상세 주소 : ${member.address2}</li>
		<li>가입 날짜  : ${member.reg_date}</li>
		<c:if test="${!empty member.modify_date}">
			<li>정보 수정일 : ${member.modify_date}</li>
		</c:if>
	</ul>
	
	<hr size="1" width="98%">
	
	<p class="align-right">
		<input type="button" value="회원정보 수정" onclick="location.href='update.do'">
		<input type="button" value="비밀번호 변경" onclick="location.href='changePassword.do'">
		<input type="button" value="회원탈퇴" onclick="location.href='delete.do'">
	</p>
	
</div>
<!-- 중앙 컨텐츠 끝 -->

interceptor 방식 

회원제 서비스 URL이 동작되기 전에 요청을 낚아채서 로그인 판별을 한다.
HandlerInterceptorAdapter를 상속받아야 기능구현이 가능하다.

preHandle도 재정의 한다.

servelt-context.xml 에서 interceptors설정을 해준다.

회원 정보수정

1) 수정폼

1. Controller(MemberController.java)

2. member.xml

tiles 설정 

3. memberModify.jsp

회원번호는 hidden으로 받아올 필요 없음

(로그인이 된 상태에서 사용되니까 Session에서 받아오면 되므로)

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!-- 중앙 컨텐츠 시작 -->
<div class="page-main">
	<h2>회원 정보 수정</h2>
	<form:form modelAttribute="memberVO" action="update.do" id="modify_form">
		<form:errors element="div" cssClass="error-color"/>
	<ul>
		<li>
			<form:label path="name">이름</form:label>
			<form:input path="name"/>
			<form:errors path="name" cssClass="error-color"/>
		</li>
		<li>
			<form:label path="phone">전화번호</form:label>
			<form:input path="phone"/>
			<form:errors path="phone" cssClass="error-color"/>
		</li>
		<li>
			<form:label path="email">이메일</form:label>
			<form:input path="email"/>
			<form:errors path="email" cssClass="error-color"/>
		</li>
		<li>
			<form:label path="zipcode">우편번호</form:label>
			<form:input path="zipcode"/>
			<input type="button" onclick="sample2_execDaumPostcode()" value="우편번호 찾기"/>
			<form:errors path="zipcode" cssClass="error-color"/>
		</li>
		<li>
			<form:label path="address1">주소</form:label>
			<form:input path="address1"/>
			<form:errors path="address1" cssClass="error-color"/>
		</li>
		<li>
			<form:label path="address2">상세 주소</form:label>
			<form:input path="address2"/>
			<form:errors path="address2" cssClass="error-color"/>
		</li>
	</ul>
	
	<div class="align-center">
		<form:button>전송</form:button>
		<input type="button" value="홈으로"
				onclick ="location.href='${pageContext.request.contextPath}/main/main.do'">
	</div>
	
	</form:form>
	
	<!-- 우편번호 스크립트 시작 -->
	<!-- iOS에서는 position:fixed 버그가 있음, 적용하는 사이트에 맞게 position:absolute 등을 이용하여 top,left값 조정 필요 -->
	<div id="layer" style="display:none;position:fixed;overflow:hidden;z-index:1;-webkit-overflow-scrolling:touch;">
	<img src="//t1.daumcdn.net/postcode/resource/images/close.png" id="btnCloseLayer" style="cursor:pointer;position:absolute;right:-3px;top:-3px;z-index:1" onclick="closeDaumPostcode()" alt="닫기 버튼">
	</div>
	
	<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
	<script>
	    // 우편번호 찾기 화면을 넣을 element
	    var element_layer = document.getElementById('layer');
	
	    function closeDaumPostcode() {
	        // iframe을 넣은 element를 안보이게 한다.
	        element_layer.style.display = 'none';
	    }
	
	    function sample2_execDaumPostcode() {
	        new daum.Postcode({
	            oncomplete: function(data) {
	                // 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
	
	                // 각 주소의 노출 규칙에 따라 주소를 조합한다.
	                // 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
	                var addr = ''; // 주소 변수
	                var extraAddr = ''; // 참고항목 변수
	
	                //사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
	                if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
	                    addr = data.roadAddress;
	                } else { // 사용자가 지번 주소를 선택했을 경우(J)
	                    addr = data.jibunAddress;
	                }
	
	                // 사용자가 선택한 주소가 도로명 타입일때 참고항목을 조합한다.
	                if(data.userSelectedType === 'R'){
	                    // 법정동명이 있을 경우 추가한다. (법정리는 제외)
	                    // 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
	                    if(data.bname !== '' && /[동|로|가]$/g.test(data.bname)){
	                        extraAddr += data.bname;
	                    }
	                    // 건물명이 있고, 공동주택일 경우 추가한다.
	                    if(data.buildingName !== '' && data.apartment === 'Y'){
	                        extraAddr += (extraAddr !== '' ? ', ' + data.buildingName : data.buildingName);
	                    }
	                    // 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
	                    if(extraAddr !== ''){
	                        extraAddr = ' (' + extraAddr + ')';
	                    }
	                    //(주의)address1에 참고항목이 보여지도록 수정
	                    // 조합된 참고항목을 해당 필드에 넣는다.
	                    //(수정) document.getElementById("address2").value = extraAddr;
	                
	                } 
	                //(수정) else {
	                //(수정)    document.getElementById("address2").value = '';
	                //(수정) }
	
	                // 우편번호와 주소 정보를 해당 필드에 넣는다.
	                document.getElementById('zipcode').value = data.zonecode;
	                //(수정) + extraAddr를 추가해서 address1에 참고항목이 보여지도록 수정
	                document.getElementById("address1").value = addr + extraAddr;
	                // 커서를 상세주소 필드로 이동한다.
	                document.getElementById("address2").focus();
	
	                // iframe을 넣은 element를 안보이게 한다.
	                // (autoClose:false 기능을 이용한다면, 아래 코드를 제거해야 화면에서 사라지지 않는다.)
	                element_layer.style.display = 'none';
	            },
	            width : '100%',
	            height : '100%',
	            maxSuggestItems : 5
	        }).embed(element_layer);
	
	        // iframe을 넣은 element를 보이게 한다.
	        element_layer.style.display = 'block';
	
	        // iframe을 넣은 element의 위치를 화면의 가운데로 이동시킨다.
	        initLayerPosition();
	    }
	
	    // 브라우저의 크기 변경에 따라 레이어를 가운데로 이동시키고자 하실때에는
	    // resize이벤트나, orientationchange이벤트를 이용하여 값이 변경될때마다 아래 함수를 실행 시켜 주시거나,
	    // 직접 element_layer의 top,left값을 수정해 주시면 됩니다.
	    function initLayerPosition(){
	        var width = 300; //우편번호서비스가 들어갈 element의 width
	        var height = 400; //우편번호서비스가 들어갈 element의 height
	        var borderWidth = 5; //샘플에서 사용하는 border의 두께
	
	        // 위에서 선언한 값들을 실제 element에 넣는다.
	        element_layer.style.width = width + 'px';
	        element_layer.style.height = height + 'px';
	        element_layer.style.border = borderWidth + 'px solid';
	        // 실행되는 순간의 화면 너비와 높이 값을 가져와서 중앙에 뜰 수 있도록 위치를 계산한다.
	        element_layer.style.left = (((window.innerWidth || document.documentElement.clientWidth) - width)/2 - borderWidth) + 'px';
	        element_layer.style.top = (((window.innerHeight || document.documentElement.clientHeight) - height)/2 - borderWidth) + 'px';
	    }
	</script>
	<!-- 우편번호 스크립트 끝 -->
	
</div>
<!-- 중앙 컨텐츠 끝 -->

2) 수정폼에서 전송된 데이터처리

1. MemberMapper.java (DAO)에서 등록작업을 한다.

2. Service(MemberServiceImpl.java)

3. Controller(MemberController.java)

회원 비밀번호 변경

1) 비밀번호 변경 폼

1. Controller(MemberController.java)

2. member.xml

tiles 설정 

3. memberChangePassword.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!-- 중앙 컨텐츠 시작 -->
<script src="${pageContext.request.contextPath}/resources/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
	$(function() {
		//비밀번호 변경 체크 keyup
		$('#passwd').keyup(function() {
			if($('#confirm_passwd').val()!='' && $('#confirm_passwd').val()!=$(this).val()) {
				$('#message_id').text('비밀번호 불일치').css('color','red');
			}else if ($('#confirm_passwd').val()!='' && $('#confirm_passwd').val()==$(this).val()) {
				$('#message_id').text('비밀번호 일치').css('color','#000');
			}
		});
		$('#confirm_passwd').keyup(function() {
			if($('#passwd').val()!='' && $('#passwd').val()!=$(this).val()){
				$('#message_id').text('비밀번호 불일치').css('color','red');
			}else if ($('#passwd').val()!='' && $('#passwd').val()==$(this).val()) {
				$('#message_id').text('비밀번호 일치').css('color','#000');
			}
		});
		
		$('#change_form').submit(function() {
			if($('#now_passwd').val().trim()==''){
				alert('현재 비밀번호를 입력하세요!');
				$('#now_passwd').val('').focus();
				return false;
			}
			if($('#passwd').val().trim()==''){
				alert('변경할 비밀번호를 비밀번호를 입력하세요!');
				$('#passwd').val('').focus();
				return false;
			}
			if($('#confirm_passwd').val().trim()==''){
				alert('변경할  비밀번호 확인을 입력하세요!');
				$('#confirm_passwd').val('').focus();
				return false;
			}
			if($('#passwd').val()!=$('#confirm_passwd').val()){
				$('#message_id').text('비밀번호 불일치').css('color','red');
				return false;
			}
		});
	});
</script>
<div>
	<h2>비밀번호 변경</h2>
	<form:form modelAttribute="memberVO" action="changePassword.do" id="change_form">
		<form:errors element="div" cssClass="error-color"/>
		<ul>
			<li>
				<form:label path="now_passwd">현재 비밀번호</form:label>
				<form:password path="now_passwd"/>
				<form:errors path="now_passwd" cssClass="error-color"/>
			</li>
			<li>
				<form:label path="passwd">변경할 비밀번호</form:label>
				<form:password path="passwd"/>
				<form:errors path="passwd" cssClass="error-color"/>
			</li>
			<li>
				<label for="confirm_passwd">변경할 비밀번호 확인</label>
				<input type="password" id="confirm_passwd">
				<span id="message_id"></span>
			</li>
		</ul>
		<div class="align-center">
			<form:button>전송</form:button>
			<input type="button" value="홈으로" onclick="location.href='${pageContext.request.contextPath}/main/main.do'">
		</div>
	</form:form>
</div>
<!-- 중앙 컨텐츠 끝 -->

2) 비밀번호 변경 폼에서 전송된 데이터 처리

1. MemberMapper.java (DAO)에서 등록작업을 한다.

2. Service(MemberServiceImpl.java)

3. Controller(MemberController.java)

4. servlet-context.xml에서 interceptor설정을 해준다.

회원 탈퇴

1)회원 탈퇴 폼

1. Controller(MemberController.java)

2. member.xml

tiles 설정 

3. memberDelete.jsp

4. servlet-context.xml에서 interceptor설정을 해준다.

회원제 서비스 이므로

2)회원 탈퇴 폼

1. MemberMapper.java (DAO)에서 등록작업을 한다.

두 개의 메서드이므로 Service에서 트랜잭션 처리를 해준다.

2. Service(MemberServiceImpl.java)

3. Controller(MemberController.java)

회원 이미지 출력 *Ajax통신

1. header.jsp 작업 

2. 업로드 폴더와 이미지 생성

3. style.css 작업

4. MemberMapper.java (DAO)에서 등록작업을 한다.

생성되어져 있기 때문에 UPDATE수행

 

5. Service(MemberServiceImpl.java)

6. Controller(MemberAjaxController.java)

7. Controller(MemberController.java)

이미지를 읽어와서 img태그의 src에 넣어주는 작업

upload폴더에 넣는 것은 파일명을 링크만 시키면 되지만,

바이트배열로 되어있는 데이터니까 스트림을 형성해서 src에 보내는 작업을 해야한다.

 membercontroller에 메서드를 만든다. 

stream을 만들어서 보내주니까 일반 jsp호출/ tiles설정 하는 것이 아니다.

빈의 이름("imageView")을 통해서 

뷰의 이름과 일치하면 호출되도록 한다.

 

8. ImageView.java

ImageView.java
0.00MB

Map객체가 실어 나르는 역할을 한다.

"imageFile"에 바이트 배열이 전달되고

"filename"에 String이 전달

스트림을 만들어서 클라이언트에 전송한다.

클라이언트가 그 스트림을 받아서 표시한다( img태그에 적용됨)

 

9. servlet-context.xml

"imageView"가 동작을 하면서 화면에 보여져야하는데

컨테이너에 등록이 되어있어야한다.

(빈 생성이 되어야 호출이 가능하기때문)

10. memberView.jsp

 

 

 

 

 

 

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 조건체크를 위해 jstl -->
<script src="${pageContext.request.contextPath}/resources/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
	$(function() {
		//프로필 사진 업로드
		$('#photo_btn').click(function() {
			$('#photo_choice').show();
			$(this).hide(); //버튼은 가린다
		});

		//취소했을 때 원상 복귀용(처음 화면에 보여지는 이미지 읽어서 보관)
		let photo_path = $('.my-photo').attr('src');
		let my_photo;
		$('#upload').change(function() { //미리보기 처리
					my_photo = this.files[0];
					if (!my_photo) { //파일객체가없으면 
						$('.my-photo').attr('src', photo_path);//원래이미지로 셋팅하고
						return; //change핸들러를 빠져나감
					}
					if (my_photo.size > 1024 * 1024) {
						alert(Math.round(my_photo.size / 1024)
								+ 'kbytes(1024kbytes까지만 업로드 가능)');
						$('.my-photo').attr('src', photo_path);
						$(this).val('');
						return;
					}
					//이미지 미리보기
					var reader = new FileReader();
					reader.readAsDataURL(my_photo);
					//이미지를 다읽어오면 이벤트가 발생하니까
					reader.onload = function() {
						$('.my-photo').attr('src', reader.result);
					};
				});//end of change
		//전송
		$('#photo_submit').click(function() {
			if ($('#upload').val() == '') {
				alert('파일을 선택하세요!');
				$('#upload').focus();
				return;
			}
			//정상적으로 선택했을 시 파일 전송
			var form_data = new FormData();
			form_data.append('upload', my_photo);//('파라미터명'을 꼭 upload라고 해야한다. 바꾸고싶다면 VO의 프로퍼티명을 바꿔야함)
									   //my_photo에 파일정보 담겨있음
			$.ajax({
				url : 'updateMyphoto.do',
				type : 'post',
				data : form_data,
				dataType : 'json',//반환타입
				contentType : false,//파일업로드할 때 contentType:false
				enctype : 'multipart/form-data',//파일업로드할 때 enctype지정
				processData : false,
				success : function(param) {
					if (param.result == 'logout') {
						alert('로그인 후 사용하세요!');
					} else if (param.result == 'success') {
						alert('프로필 사진이 수정되었습니다!');
						photo_path = $('.my-photo').attr('src');
						//초기화작업
						$('#upload').val('');
						$('#photo_choice').hide();
						$('#photo_btn').show();
					} else {
						alert('파일 전송 오류 발생');
					}
				},
				error : function() {
					alert('네트워크 오류 발생 FILE');
				}
			});
		});//end of click

		//초기화작업
		$('#photo_reset').click(function() {
			$('.my-photo').attr('src', photo_path);
			$('#upload').val(''); //파일명지운다
			$('#photo_choice').hide();
			$('#photo_btn').show();
		});

	});
</script>

<!-- 중앙 컨텐츠 시작 -->
<div class="page-main">
	<h2>프로필 사진</h2>
	<ul>
		<li>
			<c:if test="${empty member.photo_name}">
			<img src="${pageContext.request.contextPath}/resources/images/face.png"
				 width="200" height="200" class="my-photo">
			</c:if>
			<c:if test="${!empty member.photo_name}">
			<img src="${pageContext.request.contextPath}/member/photoView.do"
				 width="200" height="200" class="my-photo">
			</c:if>
		</li>
		<li>
			<div class="align-center">
				<input type="button" value="수정" id="photo_btn">
			</div>
			<div id="photo_choice" style="display: none;">
				<input type="file" id="upload" accept="image/gif,image/png,image/jpeg">
				<input type="button" value="전송" id="photo_submit">
				<input type="button" value="취소" id="photo_reset">
			</div>
		</li>
	</ul>
	
	<h2>회원 상세 정보</h2>
	<ul>
		<li>이름 : ${member.name}</li>
		<li>전화번호 : ${member.phone}</li>
		<li>이메일: ${member.phone}</li>
		<li>우편번호: ${member.zipcode}</li>
		<li>주소 : ${member.address1}</li>
		<li>상세 주소 : ${member.address2}</li>
		<li>가입 날짜  : ${member.reg_date}</li>
		<c:if test="${!empty member.modify_date}">
			<li>정보 수정일 : ${member.modify_date}</li>
		</c:if>
	</ul>
	
	<hr size="1" width="98%">
	
	<p class="align-right">
		<input type="button" value="회원정보 수정" onclick="location.href='update.do'">
		<input type="button" value="비밀번호 변경" onclick="location.href='changePassword.do'">
		<input type="button" value="회원탈퇴" onclick="location.href='delete.do'">
	</p>
	
</div>
<!-- 중앙 컨텐츠 끝 -->

DB에 BLOB로 저장된 것 확인

DB테이블에 저장하는 것이 만능적이진 않다. 파일의 용량이 커지면 느려진다.

상용서비스 시 지침에 따른다.


 

 

관리자

AdminCheckInterceptors(관리자용 로그인 여부 체크)

notice.jsp 작성하고 sevlet-context.xml 에 interceptor등록

회원관리 목록(관리자)

MemberAdminController.java

MemberMapper.xml

ServiceImpl

Controller(MemberAdminController)

tiles설정(member.xml)

jsp(admin_memberList.jsp)

회원정보 수정(관리자)

1)회원정보 수정 폼

Mapper(MemberMapper) 사용자쪽에서 미리 만든mapper 공유해서 쓴다

Controller(MemberAdminController)

tiles설정(member.xml)

jsp(admin_memberModify.jsp)

접속한 사람의 정보를 바꾸는 게 아니라 관리자가 개별적인 회원정보를 바꾸는 것이기 때문에  mem_num이 필요

관리자는 권한을 바꿀 수 없으므로 hidden으로 값만 넘긴다.

2)수정폼에서 전송된 데이터처리

1.MemberMapper.xml

2.ServiceImpl

3.Controller(MemberAdminController)

 

 

 

 

'WEB > ✿Spring Framework' 카테고리의 다른 글

[Spring] 🔗10)Spring 실습2  (0) 2022.03.12
[Spring] 🔗9) Tiles Framework  (0) 2022.03.07
[Spring] 🔗8) MyBatis  (0) 2022.03.05
[Spring] 🔗6) 로깅처리  (0) 2022.03.05
[Spring] 🔗4) Spring MVC  (0) 2022.02.25

댓글