Wanna be Brilliant Full-Stack Developer

2/17 SPringBoot 프로필 페이지 유저 사진 변경! 본문

Back-End/Spring Boot

2/17 SPringBoot 프로필 페이지 유저 사진 변경!

Flashpacker 2022. 2. 17. 16:02

목표

프로필 사진 등록!

1. PageUserId, pincipalId를 비교해서 같을 때만 동작하기

2. 이미지를 put방식(ajax)으로 서버로 전송하기

- FormData 객체 이용


// (3) 유저 프로파일 사진 변경 (완)
function profileImageUpload() {
	
	
	$("#userProfileImageInput").click();

	$("#userProfileImageInput").on("change", (e) => {
		let f = e.target.files[0];

		if (!f.type.match("image.*")) {
			alert("이미지를 등록해야 합니다.");
			return;
		}

		// 사진 전송 성공시 이미지 변경
		let reader = new FileReader();
		reader.onload = (e) => {
			$("#userProfileImage").attr("src", e.target.result);
		}
		reader.readAsDataURL(f); // 이 코드 실행시 reader.onload 실행됨.
	});
}


// (4) 사용자 정보 메뉴 열기 닫기
function popup(obj) {
	$(obj).css("display", "flex");
}

function closePopup(obj) {
	$(obj).css("display", "none");
}


// (5) 사용자 정보(회원정보, 로그아웃, 닫기) 모달
function modalInfo() {
	$(".modal-info").css("display", "none");
}

// (6) 사용자 프로파일 이미지 메뉴(사진업로드, 취소) 모달
function modalImage() {
	$(".modal-image").css("display", "none");
}

여기서부터 변경을 시작하려고한다!

// (3) 유저 프로파일 사진 변경 (완)
function profileImageUpload (pageUserId, principalId) {
	
	//console.log("pageUserId",pageUserId);
	//console.log("principalId",principalId);
	
	if(pageUserId != principalId) {
		alert("프로필 사진을 수정할 수 없는 유저입니다.");
		return;
		
	}
	
	$("#userProfileImageInput").click();

	$("#userProfileImageInput").on("change", (e) => {
		let f = e.target.files[0];

		if (!f.type.match("image.*")) {
			alert("이미지를 등록해야 합니다.");
			return;
		}

		// 사진 전송 성공시 이미지 변경
		let reader = new FileReader();
		reader.onload = (e) => {
			$("#userProfileImage").attr("src", e.target.result);
		}
		reader.readAsDataURL(f); // 이 코드 실행시 reader.onload 실행됨.
	});
}

pageUserId와 principalId를 비교해서 같지 아니하면 프로필 사진을 수정할수 없도록 만들어놨다. 

 

이제 같은 유저인 경우 서버에 이미지를 전송하는 코드를 만들어보려고한다 

이 이미지가 어디에 있냐면 

<form id="userProfileImageForm">
					<input type="file" name="profileImageFile" style="display: none;"
						id="userProfileImageInput" />
				</form>

이 form 태그에 있는것을 애를 가지고 와야하는데 가져와야하는 방법이 간단한데

사진을 전송할것이니까 데이터 전송타입이 ENC타입이 MultipartForm데이터 타입이 되어야한다!

        

	// 서버에 이미지를 전송
		let profileImageForm = $("#userProfileImageForm")[0];
		console.log(profileImageForm);

이제 form 데이터를 전송하려면 formData 객체로 받아야한다. 

여기서 말하는 formData는 어떤 데이터를 말하는것이냐면 

FormData 객체를 이용하면 form태그의 필드와 그 값을 나타내는 일련의 key/value 쌍을 담을 수 있다. 

// (3) 유저 프로파일 사진 변경 (완)
function profileImageUpload(pageUserId, principalId) {

	//console.log("pageUserId",pageUserId);
	//console.log("principalId",principalId);

	if (pageUserId != principalId) {
		alert("프로필 사진을 수정할 수 없는 유저입니다.");
		return;

	}

	$("#userProfileImageInput").click();

	$("#userProfileImageInput").on("change", (e) => {
		let f = e.target.files[0];

		if (!f.type.match("image.*")) {
			alert("이미지를 등록해야 합니다.");
			return;
		}

		// 서버에 이미지를 전송
		let profileImageForm = $("#userProfileImageForm")[0];
		console.log(profileImageForm);

		// FormData 객체를 이용하면 form태그의 필드와 그 값을 나타내는 일련의 key/value 쌍을 담을 수 있다. 
		let formData = new FormData(profileImageForm);

		$.ajax({
			type: "put",
			url: `/api/user/${principalId}/profileImageUrl`,
			data: fromData,
			contentType: false, //필수 : x-www-form-urlencoded로 파싱되는 것을 방지
			processData: false, // 필수: contentType을 false를 줬을떄 QueryString자동설정됨. 해제 
			enctype: "multipart/form-data",
			dataType: "json"
		}).done(res => {
			// 사진 전송 성공시 이미지 변경
			let reader = new FileReader();
			reader.onload = (e) => {
				$("#userProfileImage").attr("src", e.target.result);
			}
			reader.readAsDataURL(f); // 이 코드 실행시 reader.onload 실행됨.
		}).fail(error => {
				console.log("오류",error);
		})


	});
}


// (4) 사용자 정보 메뉴 열기 닫기
function popup(obj) {
	$(obj).css("display", "flex");
}

function closePopup(obj) {
	$(obj).css("display", "none");
}


// (5) 사용자 정보(회원정보, 로그아웃, 닫기) 모달
function modalInfo() {
	$(".modal-info").css("display", "none");
}

// (6) 사용자 프로파일 이미지 메뉴(사진업로드, 취소) 모달
function modalImage() {
	$(".modal-image").css("display", "none");
}

// (7) 구독자 정보 모달 닫기
function modalClose() {
	$(".modal-subscribe").css("display", "none");
	location.reload();
}

그러면 이제 API만 만들면된다! 

 

@PutMapping("/api/user/{principalId}/profileImageUrl")
public ResponseEntity<?> profileImageUrlUpdate(@PathVariable int principalId, MultipartFile file) {

} 변수 이미지를 정하는것이 중요한데 

@PutMapping("/api/user/{principalId}/profileImageUrl")
public ResponseEntity profileImageUrlUpdate(@PathVariable int principalId, MultipartFile profileImageFile {

	@PutMapping("/api/user/{principalId}/profileImageUrl")
	public ResponseEntity<?> profileImageUrlUpdate(@PathVariable int principalId, MultipartFile profileImageFile,
			@AuthenticationPrincipal PrincipalDetails principalDetails) {
		User userEntity =	userService.회원프로필사진변경(principalId,profileImageFile);
		principalDetails.setUser(userEntity); //세션변경
		return new ResponseEntity<>(new CMRespDto<>(1, "프로필사진변경 성공",null),HttpStatus.OK);
	}

이렇게 완성하고 회원프로필사진변경을 해야하는데 유저서비스에가서 

package com.cos.photogramstart.service;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.cos.photogramstart.domain.subscribe.SubscribeRepository;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;
import com.cos.photogramstart.handler.ex.CustomApiException;
import com.cos.photogramstart.handler.ex.CustomException;
import com.cos.photogramstart.handler.ex.CustomValidationApiException;
import com.cos.photogramstart.web.dto.user.UserProfileDto;

import lombok.RequiredArgsConstructor;


@RequiredArgsConstructor
@Service
public class UserService {

	private final UserRepository userRepository;
	private final SubscribeRepository subscribeRepository;
	private final BCryptPasswordEncoder bCryptPasswordEncoder;
	
	
	@Value("${file.path}")
	private String uploadFolder;
	
	@Transactional
	public User 회원프로필사진변경(int principalId, MultipartFile profileImageFile) {
		UUID uuid  = UUID.randomUUID();	 // uuid
		String imageFileName = uuid+ "_" + profileImageFile.getOriginalFilename(); // 1.jpg
		System.out.println("이미지 파일이름: "+ imageFileName);
		
		Path imageFilePath = Paths.get(uploadFolder+imageFileName);
		
		//통신, I/O -> 예외 
		try {
			Files.write(imageFilePath, profileImageFile.getBytes());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		User userEntity = userRepository.findById(principalId).orElseThrow(()->{
			throw new CustomApiException("유저를 찾을 수 없습니다.");
		});
		userEntity.setProfileImageUrl(imageFileName);
		
		return userEntity;
	} // 더티체킹으로 업데이트됨 
	
	
	@Transactional(readOnly = true)
	public UserProfileDto 회원프로필(int pageUserId, int principalId) {
		UserProfileDto dto = new UserProfileDto();
		
		//SELECT * FROM image WHERE userId = :userId;
		User userEntity = userRepository.findById(pageUserId).orElseThrow(()-> {
			throw new CustomException("해당 프로필 페이지는 없는 페이지입니다.");
		});
		
		dto.setUser(userEntity);
		dto.setPageOwnerState(pageUserId == principalId);
		dto.setImageCount(userEntity.getImages().size());
		
		int subscribeState = subscribeRepository.mSubscribeState(principalId, pageUserId);
		int subscribeCount = subscribeRepository.mSubscribeCount(pageUserId);
		
		dto.setSubscribeState(subscribeState == 1);
		dto.setSubscribeCount(subscribeCount);
		
		//좋아요 카운트 추가하기
		userEntity.getImages().forEach((image)->{
			image.setLikeCount(image.getLikes().size());
		});
		
		return dto;
	}
	
	
	
	@Transactional
	public User 회원수정(int id, User user ) {
		//1. 영속화
		//1. 무조건 찾았다. 걱정마 get() 2. 못찾았어 익섹션 발통시킬때 orElseThrow()
		User userEntity = userRepository.findById(id).orElseThrow(() -> {return new CustomValidationApiException("찾을 수 없는 id입니다."); });
			
	
	
		//2. 영속화된 오브젝트를 수정 - 수정이 완료되면 더티체킹 ( 업데이트 완료) 
		userEntity.setName(user.getName());
		
		String rawPassword = user.getPassword();
		String encPassword = bCryptPasswordEncoder.encode(rawPassword);
		
		userEntity.setPassword(encPassword);
		userEntity.setBio(user.getBio());
		userEntity.setWebsite(user.getWebsite());
		userEntity.setPhone(user.getPhone());
		userEntity.setGender(user.getGender());
		return userEntity;
		
	}//더티체킹이 일어나서 업데이트가 완료됨. 
}
				<img class="profile-image" src="/upload/${dto.user.profileImageUrl}"
					onerror="this.src='/images/person.jpeg'" id="userProfileImage" />

form태그 데이터(key = value) 를 전송할떄는 serialize를 사용하면되고 사진전송할때는 formData로 하면된다.