[Spring][쇼핑몰 프로젝트] 26. 업로드 이미지 삭제

YB·2023년 3월 2일
0

쇼핑몰

목록 보기
40/40

목표

이미지 삭제 기능 구현이며 서버 단계에서 이미지 삭제를 처리하는 url매핑 메서드 구현을 목표로 합니다.

1. 방향

이미지 삭제 기능을 수행하는 url매핑 메서드를 작성할 것입니다. 이미지 파일 삭제를 위해 가장 핵심이 되는 메서드는 File클래스의 delete()입니다.

이 메서드를 사용하기 위해선 File클래스를 인스턴스화 하여 사용해야 합니다. 삭제될 파일을 대상으로 하는 File객체를 생성하기 위해선 생성자에 대한 파일의 경로인 문자열 데이털르 파라미터로 부여해야 합니다. 따라서 삭제할 대상의 파일의 경로가 필요로 하기 때문에 url매핑 메서드의 파라미터로 파일경로(fileName)을 부여할 것입니다.

파일 삭제를 수행하는 url매핑 메서드는 뷰에서 비동기 방식으로 요청을 하고 수행 결과를 요청하기 때문에 비동기 요청을 수행하도록 작성해주어야 합니다. 2가지 방식이 있습니다. 첫 번째는 ResponseBody어노테이션입니다. 두 번째는 ResponseEntity객체를 반환 타입으로 사용하는 것입니다. 코드 수행 결과에 따라 다른 상태 코드를 전송하기 위해서 ResponseEntity를 사용할 것입니다.

2. 메서드 작성

이미지 파일 삭제를 수행하는 url매핑 메서는 AdminController.java에 작성을 하겠습니다. 이미지 파일 삭제를 수행하는 사람은 관리자만이 할 수 있도록 하기 위함입니다.

전달 방식은 POST방식으로 하였으며 url은 'deleteFile'로 작성을 하였습니다. 앞서 말하였듯이 파일의 경로 및 이름을 전달받기 위해 String타입의 fileName변수 파라미터로 부여하였으며 반환 타입은 ResponseEntity로 하였고 HTTP Body에 String데이터를 추가하기 위해 타입 매개 변수로서 String을 부여하였습니다.

url매핑 메서드의 구현부에 File타입의 참조 변수를 선언하고 null로 초기화해줍니다.

	/* 이미지 파일 삭제 */
	@PostMapping("/deleteFile")
	public ResponseEntity<String> deleteFile(String fileName) {
		
		logger.info("deleteFile........" + fileName);
		
		File file = null;
		
		return null;
		
	}
  • 앞으로 작성할 메서드들이 URLDecoder.decode(), File.delete()가 있는데 두 개 모두 예외를 발생시킬 가능성이 큰 메서드입니다. 따라서 try-catch문을 먼저 작성해주겠습니다.

  • URLDecoder.decode 적용 및 썸네일 파일 삭제를 진행할 것입니다. 진행할 뷰(view)단계에서의 작업에서는 업로드 시 출력되는 미리 보기 이미지의 우측 상단의 x 표시의 <div>태그를 사용자가 클릭을 하면 서버에 삭제를 요청하도록 구현을 할 것입니다. 이 과정에서 파일 이름 및 경로에 대한 데이터를 보내도록 구성을 할 것인데, 해당 데이터는 encodeURIComponent()메서드를 통해 UTF-8로 인코딩 된 데이터 그대로 전송되도록 할 것입니다. 문제는 File객체를 생성하기 위해 파라미터로 부여할 문자열(String)데이터는 슬래시 혹은 역슬래시를 구분자로 하는 경로여야 하는데, 해당 구분자들이 UTF-8로 인코딩 되었기 때문에 구분자들이 "%5", "%2F"따위의 문자로 변경되어 있기 때문에 fileName변수에 담긴 데이터 자체만으로는 삭제 대상인 파일을 지정해 줄 수 없습니다. 따라서 fileName에 담긴 데이터를 디코딩(Decoding)해주어야 하는데 이를 위해 사용할 메서드가 URL Decoder클래스의 decode()메서드입니다. decode()메서드는 static메서드이기 때문에 인스턴스화 없이 사용이 가능합니다. 첫 번째 파라미터는 디코딩할 대상 문자열 데이터입니다. 두 번째 파라미터는 대상 문자열 데이터가 어떠한 타입으로 인코딩 되었는지에 대한 정보를 부여합니다. 리턴 타입은 디코딩된 문자열(String)데이터입니다.

  • 업로드 당시 이미지 원본 파일과 썸네일 이미지를 생성 및 저장하였습니다. 따라서 앞서 썸네일 이미지를 삭제하였기 때문에 이번엔 원본 파일을 삭제해주겠습니다. 원본 파일 대상 File객체를 생성해주기 위해서 파일 경로 문자열 데이터가 필요로 합니다. 썸네일 파일의 File객체를 생성하였을 때와 같이 경로와 fileName데이터를 합친 후 String클래스의 replace를 통해 "s_"문자를 없애주어도 되지만, 이미 풀 경로의 정보를 가지고 있는 File 객체(file)이 존재하기 때문에 해당 객체를 활용하겠습니다. File클래스의 getAbsolutePath()메서드를 사용할 것인데 해당 메서드를 호출하면 대상 File객체의 경로를 문자열(String)타입의 데이터로 반환을 해줍니다. String타입의 originFileName변수를 선언한 후 원본 파일 경로를 문자열(String)데이터로 초기화합니다. 추가적으로 원본 파일 경로가 정상적인 값을 가지는지 확인을 하기 위해서 logger()메서드를 작성하였습니다. 본 파일을 대상으로 하는 File객체를 생성 후 이를 기존에 선언하고 사용하였던 file 참조 변수가 참조하도록 합니다. 썸네일 이미지 삭제와 동일하게 원본 파일 이미지를 삭제하도록 delete()메서드를 호출합니다.

  • try문이 예외가 발생하지 않은 것은 정상적으로 삭제 작업을 수행했다는 것이기 때문에 성공 상태 코드와 함께 성공과 관련된 문자열을 뷰로 전송해주도록 rturn문을 작성합니다.

		try {
			
			/* 썸네일 파일 삭제 */
			file = new File("d:\\upload\\" + URLDecoder.decode(fileName, "UTF-8"));
			
			file.delete();
			
			/* 원본 파일 삭제 */
			String originFileName = file.getAbsolutePath().replace("s_", "");
			
			logger.info("originFileName : " + originFileName);
			
			file = new File(originFileName);
			
			file.delete();
			
		} catch(Exception e) {
			
			e.printStackTrace();
			
			return new ResponseEntity<String>("fail", HttpStatus.NOT_IMPLEMENTED);
			
		}
		
		return new ResponseEntity<String>("success", HttpStatus.OK);

3. 삭제 버튼 태그 data 속성 삽입

업로드 이미지 출력에서 이미지를 업로드를 하면 태그가 추가되어 썸네일 이미지가 뷰에 출력되도록 하였습니다. 새로 출력되는 태그들 중 이미지 삭제를 위해 만든 class속성 값이 imgDeleteBtn인 <div>태그아 있는데, 해당 태그에 파일 경로 데이터를 심어 놓을 것입니다. 이를 위해 data속성을 활용하겠습니다.

  • 해당 <div>태그에 data-file속성을 추가해줄 것인데, 속성 값은 출력되는 파일의 경로입니다. 파일의 경로는 이미 UTF-8로 인코딩해둔 fileCallPath변수에 저장을 해두었기 때문에 이 값을 그대로 사용하겠습니다. 기존 <div>태그 문자열 값을 수정해줍니다.

str += "<div class='imgDeleteBtn' data-file='" + fileCallPath + "'>x</div>";

4. 삭제 메서드 작성

미리 보기 이미지 제거 및 서버에 이미지 파일 삭제 요청을 수행하는 코드를 작성할 차례입니다. 이 기능이 수행되어야 할 곳이 한 곳이라면 코드를 바로 작성해도 되지만, 해당 기능이 적용되어야 할 상황이 두 가지입니다.

첫 번째는 미리 보기 이미지의 'x'를 클릭하였을 때 삭제가 수행되어야 합니다. 두 번째 이미지를 삭제하지 않은 상태에서 파일 <input>태그를 클릭하여 업로드 할 파일을 선택하였을 때입니다. 두 번째 상황에서 파일 삭제 처리를 해주지 않는다면 두 가지의 파일이 저장되게 되고 미리 보기 이미지도 두 가지가 출력됩니다. 이는 한 개의 파일만 올릴 수 있도록 하고자 하는 의도와 반대되는 상황이기 때문에 사용자가 이미지를 선택할 경우 파일 삭제 처리를 해주도록 하겠습니다.

두 상황 모두 동일한 삭제처리를 필요로 하기 때문에 중복된 코드를 피하기 위하여 삭제를 처리하는 메서드를 선언 및 구현하여 이를 호출하는 방식으로 처리하겠습니다.

메서드가 처리할 작업순서는 '서버에 파일 삭제 요청' => '서버로부터 응답에 따른 처리'입니다. '서버로부터 응답에 따른 처리'는 성공의 경우 파일이 삭제되었기 때문에 미리 보기 태그를 지우는 작업을 수행하도록 할 것이고, 실패의 경우 실패하였다는 경고가 뜨게 할 것입니다.

<script>코드 내에 삭제처리를 위한 메서드를 선언합니다.

  • 두 개의 변수를 선언하고 하나의 변수에는 삭제 <div>태그에 심더운 썸네일 파일 경로 데이터를 대입하고, 나머지 변수에는 이미지 파일 업로스 시 출력되는 미리 보기 이미지를 감싸고 있는 result_card <div>태그를 대입합니다.(<div>태그는 접근을 용이하게 하고자 변수를 선언하였습니다.)

  • 파일의 삭제를 요청하는 ajax코드를 작성합니다.

    • url : 파일 삭제를 수행하는 url을 작성하였습니다.
    • data : 객체 초기화를 활용하여 fileName 속성명에 targetFile(이미지 파일 경로)속성 값을 부여하였습니다. 서버의 메서드 파라미터에 String fileName을 선언하였기 때문에 스프링에서 해당 데이터를 매핑해 줄 것입니다.
    • type : 서버에 요청 방식입니다. 'POST'를 지정해주었습니다.
    • dataType : 전송하는 targetFile은 문자 데이터이기 때문에 'text'를 지정해주었습니다.
    • success : 성공할 경우 실행되는 속성입니다.
    • error : 요청이 실패 혹은 에러일 경우 실행되는 속성입니다.
  • 파일 삭제를 성공한 경우 미리 보기 이미지를 삭제해 주고 파일 <input>태그를 초기화해주어야 합니다. success속성의 속성 값인 콜백 함수 구현부에 result_card<div>태그를 지워주고 <input>태그를 초기화해주는 코드를 작성합니다. 더불어 파일 실패의 경우 경고창을 띄우기 위해 error속성 값인 콜백 함수 구현부에 경고창을 띄우는 코드를 작성합니다.

/* 파일 삭제 메서드 */
function deleteFile(){
	
	let targetFile = $(".imgDeleteBtn").data("file");
	
	let targetDiv = $("#result_card");
	
	$.ajax({
		url: '/admin/deleteFile',
		data : {fileName : targetFile},
		dataType : 'text',
		type : 'POST',
		success : function(result){
			console.log(result);
			
			targetDiv.remove();
			$("input[type='file']").val("");
			
		},
		error : function(result){
			console.log(result);
			
			alert("파일을 삭제하지 못하였습니다.")
		}
	});
}

5. 메서드 작용

첫 번째 경우 'x'버튼을 클릭한 경우 '파일 삭제'가 동작하도록 작업하겠습니다.

'x'가 작성된 태그를 클릭하였을 경우 동작하는 메서드를 작성해줍니다. 한 가지 주의할 점은 아래와 같은 메서드는 'x'를 클릭하였을 때 동작을 하지 않습니다. 'x'가 출력되어 있는 <div>태그는 웹 페이지가 완전히 렌더링 된 이후 Javascript코드를 통해 새롭게 출력된(동적으로 출력된) 태그이기 때문입니다. 따라서 on()을 사용하여 아래와 같이 작성해주어야 합니다.

기존 렌더링 될 때 추가되어 있는 '#uploadResult' <div>태그를 식별자로 하여 그 내부에 있는 'imgDeleteBtn'<div>태그를 클릭(click)하였을 때 콜백 함수가 호출된다는 의미입니다. 콜백 함수 구현부에 파일 삭제를 수행하는 메서드를 호출합니다.

/* 이미지 삭제 버튼 동작 */
$("#uploadResult").on("click", ".imgDeleteBtn", function(e){
	
	deleteFile();
	
});

두 번째 경우 이미지가 등록될 때 파일이 이미 존재를 한다면 삭제를 처리한 후 서버에 이미지 업로드 요청을 수행하도록 해주어야 합니다. 따라서 이미지 등록을 수행하는 메서드인 $("input[type='file']").on("change", function(e){ 의 구현부 최상단에 삭제에 대한 코드를 적용해 줄 것입니다.

기존 이미지 파일이 저장되었을 때 삭제가 이루어져야 합니다. 미리 보기 태그가 존재 하는지 존재 하지 않는지를 통해서 판단할 수 있기 때문에 if문을 활용하여 미리보기 이미지 태그의 존재 유무에 따라서 deleteFile()메서드를 호출하도록 아래와 같이 코드를 작성합니다.

	/* 이미지 존재시 삭제 */
	if($(".imgDeleteBtn").length > 0){
		deleteFile();
	}

6. 테스트




profile
개인이 공부한걸 작성하는 블로그입니다..

0개의 댓글