TIL 0627

먼지·2024년 6월 27일

Today I Learned

목록 보기
86/89
post-thumbnail

kr.spring.member.controller

Member Controller

회원 정보 수정 폼 호출

세션에 저장된 user의 정보를 가져온다.

	@GetMapping("/member/update")
	public String formUpdate(HttpSession session, Model model) {
		// 세션에 저장된 user의 정보를 가져온다
		MemberVO user = (MemberVO)session.getAttribute("user");
		
		MemberVO memberVO = memberService.selectMember(user.getMem_num());
		model.addAttribute("memberVO",memberVO);
		
		return "memberModify";
	}

회원 정보 수정 처리

유효성 검사에서 오류가 발생한 경우, 회원 수정 폼 페이지인 memberModify로 다시 이동

if(result.hasErrors()) {
			return "memberModify";
		}

세션에서 현재 사용자 정보를 가져와 user 변수에 저장한다.
세션에서 가져온 사용자 정보의 회원 번호(mem_num)를 현재 수정하려는 memberVO 객체에 설정

MemberVO user = (MemberVO)session.getAttribute("user");
		memberVO.setMem_num(user.getMem_num());

세션에 저장된 사용자 정보도 업데이트한다. 새로운 닉네임과 이메일을 세션의 user 객체에 반영한다.

user.setNick_name(memberVO.getNick_name());
user.setEmail(memberVO.getEmail());
@PostMapping("/member/update")
	public String postMethodName(@Valid MemberVO memberVO, BindingResult result,HttpSession session) {
		log.debug("<< 회원 정보 수정 >> : " + memberVO);
		
		if(result.hasErrors()) {
			return "memberModify";
		}
		
		MemberVO user = (MemberVO)session.getAttribute("user");
		memberVO.setMem_num(user.getMem_num());
		
		memberService.updateMember(memberVO);
		
		user.setNick_name(memberVO.getNick_name());
		user.setEmail(memberVO.getEmail());
		
		return "redirect:/member/myPage";
	}

프로필 사진 출력하기

사용자가 로그인하지 않은 경우(user가 null인 경우), 기본 프로필 이미지를 불러오는 getBasicProfileImage 메서드를 호출한다.

if(user == null) {
			getBasicProfileImage(request, model);
	}

사용자가 로그인한 경우, 세션에서 가져온 사용자 정보의 회원 번호(mem_num)를 이용해 memberService에서 해당 회원의 정보를 memberVO에 저장하고, viewProfile 메서드를 호출하여 프로필 사진을 처리한다.

else {
	MemberVO memberVO = memberService.selectMember(user.getMem_num());
	viewProfile(memberVO, request, model);
}
@GetMapping("/member/photoView")
	public String getProfile(HttpSession session, HttpServletRequest request, Model model) {
		// session에 저장된 user의 정보를 불러온다
		MemberVO user = (MemberVO)session.getAttribute("user");
		log.debug("<< 프로필 사진 출력 >> : " + user);
		
		
		if(user == null) {
			// 로그인이 되지 않은 경우
			getBasicProfileImage(request, model);
		} else {
			// 로그인이 된 경우
			// user를 통해서 mem_num을 받아와서 데이터를 읽어와서 처리한다
			MemberVO memberVO = memberService.selectMember(user.getMem_num());
			viewProfile(memberVO, request, model);
		}
		
		return "imageView";
	}

프로필 사진 처리를 위한 공통코드

if (memberVO == null || memberVO.getPhoto_name() == null)

memberVO가 null이거나 memberVO 객체에 저장된 프로필 사진 파일 이름(photo_name)이 null인 경우, 데이터베이스에 저장된 프로필 사진이 없다고 간주한다.
getBasicProfileImage(request, model) 메서드를 호출하여 기본 프로필 이미지를 설정한다.

else {
	model.addAttribute("imageFile", memberVO.getPhoto());
	model.addAttribute("filename", memberVO.getPhoto_name());
}

memberVO 객체에 프로필 사진 파일 이름이 존재하는 경우, 업로드된 프로필 이미지를 읽어온다.
model.addAttribute("imageFile", memberVO.getPhoto())
memberVO 객체에서 프로필 사진 파일의 실제 데이터(photo)를 가져와 model 객체에 imageFile이라는 이름으로 추가한다.
model.addAttribute("filename", memberVO.getPhoto_name())
memberVO 객체에서 프로필 사진 파일 이름(photo_name)을 가져와 model 객체에 filename이라는 이름으로 추가한다.

public void viewProfile(MemberVO memberVO, HttpServletRequest request, Model model) {
		if (memberVO == null || memberVO.getPhoto_name() ==  null) {
			// DB에 저장된 프로필 사진이 없기 때문에 기본 이미지를 띄운다.
			getBasicProfileImage(request, model);
		} else {
			// 업로드된 프로필 이미지 읽어오기
			model.addAttribute("imageFile", memberVO.getPhoto());
			model.addAttribute("filename", memberVO.getPhoto_name());
		}
	}

기본 이미지 읽어오기

request.getServletContext().getRealPath("/image_bundle/face.png")
웹 애플리케이션의 실제 파일 시스템 경로를 반환한다. 여기서는 /image_bundle/face.png 경로의 실제 위치를 가져온다.

FileUtil.getBytes(...)
지정된 파일 경로에서 파일을 읽어 바이트 배열로 반환한다.
FileUtil 클래스는 파일 읽기를 위한 유틸리티 클래스이다.

model.addAttribute("imageFile", readbyte)
기본 프로필 이미지 파일의 바이트 배열(readbyte)을 imageFile이라는 이름으로 model 객체에 추가한다.
model.addAttribute("filename", "face.png")
기본 프로필 이미지 파일의 이름(face.png)을 filename이라는 이름으로 model 객체에 추가한다.

public void getBasicProfileImage(HttpServletRequest request, Model model) {
																		// 실제 경로를 지정
		byte[] readbyte = FileUtil.getBytes(request.getServletContext().getRealPath("/image_bundle/face.png"));
		model.addAttribute("imageFile", readbyte);
		model.addAttribute("filename", "face.png");
	}

비밀번호 변경 폼 호출

@GetMapping("/member/changePassword")
	public String formChangePassword() {
		return "memberChangePassword";
	}

캡챠 이미지 호출하기

String clientId = "개인이 발급 받는 코드";
String clientSecret = "개인이 발급 받는 코드";
		
String code = "0";	// 키 발급시 0, 캡챠 이미지 비교시 1로 세팅
String key_apiURL="https://openapi.naver.com/v1/captcha/nkey?code=" + code;

clientIdclientSecret은 네이버 개발자 센터에서 발급받은 개인 인증 정보다. 캡챠 이미지를 가져오려면 네이버 개발자 센터에서 발급받아 입력해주면 된다.
code = "0"
캡차 키를 발급받기 위한 설정이다. 0은 키 발급을 의미한다.
key_apiURL
캡차 키를 발급받기 위한 네이버 API URL이다.

Map<String, String> requestHeaders = new HashMap<String, String>();
requestHeaders.put("X-Naver-Client-Id", clientId);
requestHeaders.put("X-Naver-Client-Secret", clientSecret);
    
String responseBody = CaptchaUtil.get(key_apiURL, requestHeaders);
    
log.debug("<< responseBody >> : " + responseBody);

requestHeaders
API 요청 시 필요한 헤더 정보를 설정한다. 여기에는 clientId와 clientSecret이 포함된다.
CaptchaUtil.get(key_apiURL, requestHeaders)
네이버 캡차 API를 호출하여 키 발급을 요청한다. 응답 내용은 responseBody에 저장된다.

JSONObject jObject = new JSONObject(responseBody);
    try {
        String key = jObject.getString("key");
        session.setAttribute("captcha_key", key);
        
        String apiURL = "https://openapi.naver.com/v1/captcha/ncaptcha.bin?key=" + key;
        
        Map<String, String> requestHeaders2 = new HashMap<String, String>();
        requestHeaders2.put("X-Naver-Client-Id", clientId);
        requestHeaders2.put("X-Naver-Client-Secret", clientSecret);
        
        byte[] response_byte = CaptchaUtil.getCaptchaImage(apiURL, requestHeaders2);
        
        model.addAttribute("imageFile", response_byte);
        model.addAttribute("filename", "captcha.jpg");
        
    } catch (Exception e) {
        log.error(e.toString());
    } finally {
        
    }
    
    return "imageView";
}

JSONObject jObject = new JSONObject(responseBody)
JSON 응답을 파싱하여 JSONObject 객체로 변환한다.
String key = jObject.getString("key")
응답 JSON에서 캡차 키를 추출한다.
session.setAttribute("captcha_key", key)
추출한 캡차 키를 세션에 저장한다. 캡차 검증 시 동일한 키를 사용해야 하기 때문이다.
String apiURL = "https://openapi.naver.com/v1/captcha/ncaptcha.bin?key=" + key
캡차 이미지를 요청하기 위한 API URL을 설정한다.
Map<String, String> requestHeaders2 = new HashMap<String, String>()
두 번째 API 요청을 위한 헤더 정보를 설정한다.
byte[] response_byte = CaptchaUtil.getCaptchaImage(apiURL, requestHeaders2)
캡차 이미지를 바이트 배열로 가져온다.
model.addAttribute("imageFile", response_byte
캡차 이미지 데이터를 model 객체에 imageFile이라는 이름으로 추가
model.addAttribute("filename", "captcha.jpg")
캡차 이미지 파일 이름을 model 객체에 filename이라는 이름으로 추가
catch (Exception e)
예외가 발생하면 로그에 오류를 기록한다.
return "imageView"
최종적으로 imageView라는 뷰를 반환합니다. 이 뷰는 캡차 이미지를 출력한다.

	@GetMapping("/member/getCaptcha")
	public String getCaptcha(Model model, HttpSession session) {
		// 내 정보
		String clientId = "개인이 발급 받는 코드";
		String clientSecret = "개인이 발급 받는 코드";
		
		String code = "0";	// 키 발급시 0, 캡챠 이미지 비교시 1로 세팅
		String key_apiURL="https://openapi.naver.com/v1/captcha/nkey?code=" + code;
		
		Map<String,String> requestHeaders= new HashMap<String, String>();
		requestHeaders.put("X-Naver-Client-Id", clientId);
		requestHeaders.put("X-Naver-Client-Secret", clientSecret);
		
		String responseBody = CaptchaUtil.get(key_apiURL, requestHeaders);
		
		log.debug("<< responseBody >> : " + responseBody);
		
		JSONObject jObject = new JSONObject(responseBody);
		try {
			// key -> https://openapi.naver.com/v1/captcha/nkey 얘를 호출해서 받은 key 값을 의미
			String key = jObject.getString("key");
			// 캡챠 이미지와 동일하게 전송해야 하기 때문에 세션에 보관
			session.setAttribute("captcha_key", key);
			
			String apiURL = "https://openapi.naver.com/v1/captcha/ncaptcha.bin?key=" + key;
			
			Map<String,String> requestHeaders2= new HashMap<String, String>();
			requestHeaders.put("X-Naver-Client-Id", clientId);
			requestHeaders.put("X-Naver-Client-Secret", clientSecret);
			
			// byte 배열에 image 넣어주기 (불러온 정보들)
			byte[] response_byte = CaptchaUtil.getCaptchaImage(apiURL, requestHeaders2);
			
			model.addAttribute("imageFile",response_byte);
			model.addAttribute("filename","captcha.jpg");
			
		} catch (Exception e) {
			log.error(e.toString());
		} finally {
			
		}
		
		return "imageView";
	}

kr.spring.member.controller

Member Ajax Controller

	@PostMapping("/member/updateMyPhoto")
	@ResponseBody
	public Map<String, String> processProfile(MemberVO memberVO, HttpSession session){
		log.debug("<<프로필 사진 변경>> : " + memberVO);
		
		Map<String, String> mapAjax = new HashMap<String, String>();
		
		MemberVO user = (MemberVO)session.getAttribute("user");
		
		if(user == null) {
			mapAjax.put("result", "logout");
		}
		else {
			memberVO.setMem_num(user.getMem_num());
			memberService.updateProfile(memberVO);
			
			mapAjax.put("result", "success");
		}
		
		return mapAjax;
	}

WEB-INF > views > member

memberModify.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!-- 회원 정보 수정 시작 -->
<div class="page-main">
	<h2>회원 정보 수정</h2>
	<form:form action="update" id="member_modify" modelAttribute="memberVO">
		<ul>
			<li>
				<form:label path="name">이름</form:label>
				<form:input path="name" />
				<form:errors element="div" path="name" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="nick_name">닉네임</form:label>
				<form:input path="nick_name"/>
			</li>
			
			<li>
				<form:label path="phone">전화 번호</form:label>
				<form:input path="phone" placeholder="010********" />
				<form:errors element="div" path="phone" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="email">이메일</form:label>
				<form:input path="email"/>
				<form:errors element="div" path="email" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="zipcode">우편번호</form:label>
				<form:input path="zipcode"/>
				<input type="button" onclick="execDaumPostcode()" value="우편번호 찾기" class="default-btn">
				<form:errors element="div" path="zipcode" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="address1">주소</form:label>
				<form:input path="address1"/>
				<form:errors element="div" path="address1" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="address2">상세 주소</form:label>
				<form:input path="address2"/>
				<form:errors element="div" path="address2" cssClass="error-color-reg"/>
			</li>
		</ul>
		<div class="align-center">
			<form:button class="default-btn">수정</form:button>
			<input type="button" value="홈으로"
			  class="default-btn"
			  onclick="location.href='myPage'">
		</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 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>
<!-- 회원 정보 수정 종료 -->

Member Change Password.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<div class="page-main">
	<h2>비밀번호 변경</h2>
	<form:form action="changePassword" id="member_change" modelAttribute="memberVO">
		<form:hidden path="mem_num"/>
		<ul>
			<li>
				<form:label path="now_passwd">현재 비밀번호</form:label>
				<form:password path="now_passwd" />
				<form:errors element="div" path="now_passwd" cssClass="error-color-reg"/>
			</li>
			<li>
				<form:label path="passwd">새 비밀번호</form:label>
				<form:password path="passwd" />
				<form:errors element="div" path="passwd" cssClass="error-color-reg"/>
			</li>
			<li>
				<label for="confirm_passwd">새 비밀번호 확인</label>
				<input type="password" id="confirm_passwd">
				<div id="message_password"></div>
			</li>
			<li>
				<div id="captcha_div">
					<img src="getCaptcha" id="captcha_img" width="200" height="90">
				</div>
				<input type="button" value="새로고침" id="reload_btn">
				<script type="text/javascript">
					$(function(){
						$('#reload_btn').click(function(){
							$.ajax({
								url:'getCaptcha',
								type:'get',
								success:function(){
									$('#captcha_div').load(location.href+' #captcha_div');
								},
								error:function(){
									alert('네트워크 오류가 발생하였습니다.');
								}
							});
						});
					});
				</script>
			</li>
			<li>
				<form:label path="captcha_chars">문자 확인</form:label>
				<form:input path="captcha_chars" />
				<form:errors element="div" path="captcha_chars" cssClass="error-color-reg"/>
			</li>
		</ul>
		<div class="align-center">
			<form:button class="default-btn">수정</form:button>
			<input type="button" value="MyPage" class="default-btn" onclick="location.href='myPage'">
		</div>   
	</form:form>
</div>

WEB-INF > tiles-def

Member.xml

<definition name="memberModify" extends="myPage">
		<put-attribute name="title" value="Member Update Page"/>
		<put-attribute name="body" value="/WEB-INF/views/member/memberModify.jsp" />
</definition>

<definition name="memberChangePassword" extends="myPage">
		<put-attribute name="title" value="Member Change Password Page"/>
		<put-attribute name="body" value="/WEB-INF/views/member/memberChangePassword.jsp" />
</definition>

kr.spring.member.vo

Member VO

VO에 해당 내용 추가하기

// 비밀번호 변경에만 조건 체크
	@Pattern(regexp = "^[0-9a-zA-Z]$")
	private String captcha_chars;

kr.spring.util

File Util

public static byte[] getBytes(String path)
지정된 경로의 파일을 읽어 바이트 배열로 반환하는 정적 메서드입니다.
String path
파일의 경로를 나타내는 문자열
FileInputStream fis = null;
파일 입력 스트림 객체를 초기화한다.
byte[] readbyte = null;
파일 데이터를 저장할 바이트 배열을 초기화
fis = new FileInputStream(path);
지정된 경로의 파일을 읽기 위해 FileInputStream 객체를 생성
readbyte = new byte[fis.available()];
파일 크기만큼의 바이트 배열을 생성한다. fis.available() 메서드는 읽을 수 있는 바이트 수를 반환한다
fis.read(readbyte);
파일의 데이터를 바이트 배열에 읽어온다.

package kr.spring.util;

import java.io.FileInputStream;
import java.io.IOException;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class FileUtil {
	// 지정한 경로의 파일을 읽어들여 byte 배열로 변환해준다
	public static byte[] getBytes(String path) {
		FileInputStream fis = null;
		byte[] readbyte = null;
		try {
			fis = new FileInputStream(path);
			readbyte = new byte[fis.available()];
			fis.read(readbyte);
		} catch (Exception e) {
			log.error(e.toString());
		} finally {
			if(fis != null) try {fis.close();}catch (IOException e) {}
		}
		
		return readbyte;
	}
}

Captcha Util

package kr.spring.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

public class CaptchaUtil {
	//key 구하기 {"key":"dfde25dd"} 형식
	public static String get(String apiUrl, Map<String, String> requestHeaders){
		HttpURLConnection con = connect(apiUrl);
		try {
			con.setRequestMethod("GET");
			for(Map.Entry<String, String> header :requestHeaders.entrySet()) {
				con.setRequestProperty(header.getKey(), header.getValue());
			}

			int responseCode = con.getResponseCode();
			if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 호출
				return readBody(con.getInputStream());
			} else { // 에러 발생
				return readBody(con.getErrorStream());
			}
		} catch (IOException e) {
			throw new RuntimeException("API 요청과 응답 실패", e);
		} finally {
			con.disconnect();
		}
	}
	
	//캡차 이미지 반환
	public static byte[] getCaptchaImage(String apiUrl, Map<String, String> requestHeaders){
        HttpURLConnection con = connect(apiUrl);
        try {
            con.setRequestMethod("GET");
            for(Map.Entry<String, String> header :requestHeaders.entrySet()) {
                con.setRequestProperty(header.getKey(), header.getValue());
            }

            int responseCode = con.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 호출
                return getImage(con.getInputStream());
            } 
            else { // 에러 발생
            //    return error(con.getErrorStream());
            	return null;
            }
        } catch (IOException e) {
            throw new RuntimeException("API 요청과 응답 실패", e);
        } finally {
            con.disconnect();
        }
    }
	
	private static byte[] getImage(InputStream is){
        try{
        	byte[] bytes = new byte[is.available()];
            is.read(bytes);
            return bytes;
        } catch (IOException e) {
            throw new RuntimeException("이미지 캡차 파일 생성에 실패 했습니다.",e);
        }finally {
        	if(is!=null)try {is.close();} catch (IOException e) {}
        }
    }

	private static HttpURLConnection connect(String apiUrl){
		try {
			URL url = new URL(apiUrl);
			return (HttpURLConnection)url.openConnection();
		} catch (MalformedURLException e) {
			throw new RuntimeException("API URL이 잘못되었습니다. : " + apiUrl, e);
		} catch (IOException e) {
			throw new RuntimeException("연결이 실패했습니다. : " + apiUrl, e);
		}
	}

	private static String readBody(InputStream body){
		InputStreamReader streamReader = new InputStreamReader(body);

		try (BufferedReader lineReader = new BufferedReader(streamReader)) {
			StringBuilder responseBody = new StringBuilder();

			String line;
			while ((line = lineReader.readLine()) != null) {
				responseBody.append(line);
			}

			return responseBody.toString();
		} catch (IOException e) {
			throw new RuntimeException("API 응답을 읽는데 실패했습니다.", e);
		}
	}
}

kr.spring.view

Image View

AbstractView 클래스의 추상 메서드를 구현하여, 뷰를 렌더링하는 로직을 정의한다.

byte[] file = (byte[]) model.get("imageFile")
모델에서 이미지 파일 데이터를 가져온다.

String filename = (String) model.get("filename")
모델에서 파일 이름을 가져온다.

response.setContentType(ext)
response.setContentLength(file.length)
응답의 콘텐츠 타입과 길이를 설정한다.

String file_name = new String(filename.getBytes("utf-8"), "iso-8859-1")
파일 이름을 UTF-8에서 ISO-8859-1로 변환하여 설정합니다.

response.setHeader("Content-Disposition", "attachment; filename=\"" + file_name + "\";");
response.setHeader("Content-Transfer-Encoding", "binary");
응답 헤더를 설정하여 파일이 첨부 파일로 다운로드되도록 한다.

OutputStream out = response.getOutputStream()
응답 출력 스트림을 가져온다.

InputStream input = null 입력 스트림을 초기화한다.

input = new ByteArrayInputStream(file)
바이트 배열을 입력 스트림으로 변환한다.

IOUtils.copy(input, out)
입력 스트림의 데이터를 출력 스트림으로 복사한다.

package kr.spring.view;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;

@Component
public class imageView extends AbstractView{

	@Override
	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		
		// 바이트 배열에 모델에 저장해둔 imageFile을 저장시킨다
		byte[] file = (byte[]) model.get("imageFile");
		// filename도 저장시켰기 때문에 불러온다
		String filename = (String)model.get("filename");
		
		String ext = filename.substring(filename.lastIndexOf("."));
		
		if(ext.equalsIgnoreCase(".gif")) {
			ext="image/gif";
		} else if(ext.equalsIgnoreCase(".png")) {
			ext = "image/png";
		} else {
			ext="image/jpeg";
		}
		
		response.setContentType(ext);
		response.setContentLength(file.length);
		
		String file_name = new String(filename.getBytes("utf-8"),"iso-8859-1");
		
		response.setHeader("Content-Disposition", "attachment; filename=\"" + file_name +"\";");
		response.setHeader("Content-Transfer-Encoding", "binary");
		
		OutputStream out = response.getOutputStream();
		InputStream input = null;
		
		try {
			input = new ByteArrayInputStream(file);
			IOUtils.copy(input, out);
			out.flush();
		} finally {
			if(out != null) out.close();
			if(input != null) input.close();
		}
	}

}

resources > static > js

member.profile.js

$(function(){
	/* My Page 프로필 사진 등록 및 수정 */	
	
	// 수정 버튼 이벤트 처리
	$('#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;
		}
		if(my_photo.size > 1024 * 1024){
			alert(Math.round(my_photo.size/1024)+'kbytes(1024kbytes 까지만 업로드 가능합니다!)');
			$('.my-photo').attr('src', photo_path);
			$(this).val('');
			return;
		}
		
		// 이미지 미리보기 처리하기
		const 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;
		}
		
		// 서버에 전송할 파일을 선택한다
		// 객체를 만들 때는 주소 변경이 되지 않도록 const를 사용
		const form_data = new FormData();
		form_data.append('upload',my_photo);
		$.ajax({
			url: '../member/updateMyPhoto',
			type: 'post',
			data: form_data,
			dataType: 'json',
			contentType:false,
			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('네트워크 오류가 발생하였습니다.');
			}		
		});
	});	// end of Click - 파일 전송
	// 취소 버튼 처리
	$('#photo_reset').click(function(){
		$('.my-photo').attr('src',photo_path);
		$('#upload').val('');
		$('#photo_choice').hide();
		$('#photo_btn').show();
	});	// end of Click - 취소 버튼 처리
	
});

kr.spring.member.dao

Member Mapper

// 자동 로그인 처리
public void updateAu_id(String au_id, Long mem_num);
public void selectAu_id(String au_id);
public void deleteAu_id(Long mem_num);
	
// 비밀번호 찾기
public void updateRandomPassword(MemberVO member);
	
// 플필 이미지 업데이트
@Update("UPDATE spmember_detail SET photo=#{photo}, photo_name=#{photo_name} WHERE mem_num=#{mem_num}")
	public void updateProfile(MemberVO member);
// 회원 비밀번호 수정
@Update("UPDATE spmember_detail SET passwd=#{passwd} WHERE mem_num=#{mem_num}")
	public void updatePassword(MemberVO member);
profile
Lucky Things🍀

0개의 댓글