Kim VamPa

[Spring][쇼핑몰 프로젝트][30] (이미지 존재) 상품 정보 삭제 코드 보완 본문

스프링 프레임워크/쇼핑몰 프로젝트

[Spring][쇼핑몰 프로젝트][30] (이미지 존재) 상품 정보 삭제 코드 보완

Kim VamPa 2021. 8. 11. 11:31
728x90
반응형
프로젝트 Github : https://github.com/sjinjin7/Blog_Project
프로젝트 포스팅 색인(index) : https://kimvampa.tistory.com/188

 

목표

이미지가 있는 상품 정보 삭제 시 발생하는 에러 해결

 

 현 프로젝트의 상황에서 이미지 정보가 없는 상품의 정보는 삭제가 정상적으로 동작을 합니다. 하지만 이미지를 가지고 있는 상품 정보를 삭제 시 아래와 같은 에러가 납니다. 이번 포스팅에선 이 에러를 해결하는 것을 목표로 합니다.

 

그림 0-1

 

그림 0-2

 

 

순서

1. 에러 이유

2. 방안 및 방향

3. 서버 파일 삭제

4. DB 데이터 삭제 (상품 정보, 이미지 정보)

5. 테스트

 

 

 

 

1. 에러 이유

 

 에러 창을 읽어보시면 "무결성 제약조건" 위반으로 인해서 발생하였다고 출력이 됩니다. 이는 'vam_image' 테이블의 "bookId' 컬럼이 'vam_book'테이블의 'bookId'컬럼을 참조하도록 외래키 설정을 해놓았기 때문입니다.

 

 풀어서 설명을 하면 현재의 상품 정보 삭제는 'vam_book' 테이블의 지정된 한 행을 삭제하도록 되어있습니다. 해당 상품에 이미지가 있는 경우, 'vam_image'의 하나 혹은 둘 이상의 행들이 존재하고 이 행들은 'vam_book' 테이블의 하나의 행을 참조를 하고 있는 상태입니다. (정확히는 'vam_image' 테이블의 'bookId'컬럼이 'vam_book'테이블의 'bookId' 컬럼을 참조) 이러한 상황에서 참조되고 있는 'vam_book'테이블의 행을 지우게 된다면, 'vam_book'테이블을 참조하고 있던 'vam_image' 행들이 참조할 수 있는 값 자체가 없어지게 됩니다. 따라서 '무결성 제약조건' 중 "외래키는 참조할 수 없는 값을 가질 수 없다"는 규칙을 위반하게 됩니다. 

 

2. 방안 및 방향

방안

 

  이러한 에러를 해결하기 위한 방법은 '참조되고 있는 테이블'을 삭제하기 전에 '참조하고 잇는 테이블을' 같이 혹은 먼저 삭제해주는 겁니다. 구체적으로 방법은 다음과 같습니다.

 

 첫 번째, 먼저 'vam_image' 테이블의 행을 삭제 한 뒤, 'vam_book' 테이블의 행을 삭제해주면 됩니다. 

 두 번째, 외래키 설정 시 "ON DELETE CASCADE" 설정을 추가적으로 해주는 것입니다.

 

 저는 첫 번째 방식으로 진행을 하겠습니다.

 

한 가지 더 고려해야 할 사항이 있습니다. 서버에 저장돼 어 있는 이미지 파일의 삭제입니다. 배치 프로그램을 이미 설정을 해놓았지 않냐고 생각을 할 수 있지만 현재 작성해둔 코드는 어제자의 파일만 대상으로 삭제가 되도록 작성되어 있습니다. 따라서 해당 상황에 적용될 배치 프로그램을 작성하거나, 상품 정보가 삭제될대 같이 서버의 이미지 파일도 삭제되도록 해야 합니다. 저는 상품 정보가 삭제될 때 서버 이미지 파일도 같이 삭제되도록 구현할 것입니다. 

 

 정리를 하면 기존에 '상품 정보 삭제'를 하게 되었다면 '상품에 대한 정보'만 삭제가 되었습니다. 이미지 기능이 추가됨에 따라 우리가 새롭게 보완할 '상품 정보 삭제'는 '상품에 대한 정보', 'DB에 저장된 이미지 정보', '서버에 저장된 이미지 파일'을 삭제해주어야 합니다.

 

방향

 

해당 작업은 기존의 상품 삭제 매핑 메서드(goodsDeletePOST)를 활용하여 진행할 것이며, 로직은 다양한 방식으로 구현을 할 수 있지만 저의 경우 먼저 '서버에 저장된 이미지 파일 삭제'를 먼저 진행한 후 'DB 상품 정보 삭제', 'DB에 저장된 이미지 정보 삭제' 처리하도록 구현해보겠습니다.

 

 1. 서버 저장 이미지 삭제

 2. DB 정보 삭제(상품, 이미지)

 

 

3. 서버 파일 삭제

 파일을 삭제하기 위해선 이미지 파일에 대한 정보가 필요로 합니다. 따라서 'bookId' 데이터를 활용하여 상품 이미지 정보를 DB로부터 가져올 수 있도록 Mapper, Service 메서드를 작성하겠습니다.

 

AdminMapper.java

 

 'bookId'를 조건으로 행을 가져와야 하기 때문에 'bookId'를 파라미터로 하는 메서드 선언부를 작성합니다. 반환 타입은 여러 행의 정보를 반환받을 수 있도록 List 타입으로 지정하였습니다.

 

	/* 지정 상품 이미지 정보 얻기 */
	public List<AttachImageVO> getAttachInfo(int bookId);

 

그림 3-1

 

 

 

AdminMapper.xml

 

 AdminMapper 클래스에서 선언한 메서드가 실행할 쿼리문을 작성해줍니다.

 

	<!-- 지정 상품 이미지 정보 얻기 -->
	<select id="getAttachInfo" resultType="com.vam.model.AttachImageVO">
	
		select * from vam_image where bookId = #{bookId}
	
	</select>

 

그림 3-2

 

 

 

Mapper 테스트

 

 작성한 Mapper메서드가 정상적으로 동작하는지 확인을 하기 위해서 AdminMapperTests 클래스에 아래의 코드를 작성하여 Junit 테스트를 진행합니다.

 

	/* 지정 상품 이미지 정보 얻기 */
	@Test
	public void getAttachInfoTest() {
		
		int bookId = 3141;
		
		List<AttachImageVO> list = mapper.getAttachInfo(bookId);
		
		System.out.println("list : " + list);
		
	}

 

그림 3-3

 

그림 3-4

 

 

 

AdminService.java

 

 Mapper 메서드를 호출해주는 Service 단계의 메서드를 작성해주겠습니다. AdminService.java 인터페이스에 아래의 선언부를 작성해줍니다.

 

	/* 지정 상품 이미지 정보 얻기 */
	public List<AttachImageVO> getAttachInfo(int bookId);

 

그림 3-5

 

 

 

AdminServiceImpl.java

 

 앞서 선언한 메서드 선언부를 오버라이딩하여 구현부를 작성해줍니다.

 

	/* 지정 상품 이미지 정보 얻기 */
	@Override
	public List<AttachImageVO> getAttachInfo(int bookId) {
		
		log.info("getAttachInfo........");
		
		return adminMapper.getAttachInfo(bookId);
	}

 

그림 3-6

 

 

 

 

AdminController.java

 

 기존 "/admin/goodsDelete" url 매핑 메서드(goodsDeletePOST)에 코드를 추가하겠습니다. 서버 파일 삭제를 가장 먼저 처리하도록 만들 것이기 때문에 구현부 최상단에 작성해주겠습니다.

 

그림 3-7

 

 

 먼저 이미지 정보를 반환해주는 service 메서드를 호출하고 반환받은 값을 List 타입의 fileList 변수에 저장해줍니다.

 

		List<AttachImageVO> fileList = adminService.getAttachInfo(bookId);

 

 변수 fileList에는 상품에 대한 이미지가 존재한다면 AttachImageVO 객체를 요소로 가지는 List 객체가 저장되어 있을 것이고, 이미지가 존재하지 않는다면 null일 것입니다. 이미지가 없다면 굳이 서버 파일 삭제 코드들이 실행이 될 필요가 없기 때문에, 이미지가 존재 시 코드가 실행이 될 수 있도록 if문을 작성해줍니다.

 

		List<AttachImageVO> fileList = adminService.getAttachInfo(bookId);
		
		if(fileList != null) {

		}

 

 파일을 존재할 경우에 DB로부터 가져온 이미지 정보를 활용하여 Path 객체를 생성하고, 해당 Path객체를 File 객체로 변환하여 delete() 메서드를 호출하여 파일을 삭제할 것입니다. 이러한 로직은 이미 배치 프로그램에 사용하였기 때문에 기존 코드를 활용하여 아래와 같이 작성을 하였습니다.

 

		List<AttachImageVO> fileList = adminService.getAttachInfo(bookId);
		
		if(fileList != null) {
			
			List<Path> pathList = new ArrayList();
			
			fileList.forEach(vo ->{
				
				// 원본 이미지
				Path path = Paths.get("C:\\upload", vo.getUploadPath(), vo.getUuid() + "_" + vo.getFileName());
				pathList.add(path);
				
				// 섬네일 이미지
				path = Paths.get("C:\\upload", vo.getUploadPath(), "s_" + vo.getUuid()+"_" + vo.getFileName());
				pathList.add(path);
				
			});
			
			pathList.forEach(path ->{
				path.toFile().delete();
			});
			
		}

 

그림 3-8

 

 

4. DB 데이터 삭제 (상품 정보, 이미지 정보)

 

 이번엔 DB의 데이터를 삭제하는 코드를 추가해줄 차례입니다. 기존 코드를 보면 '상품 정보 삭제'를 수행하는 Service 메서드 가있는데 해당 메서드에서 '이미지 정보 삭제'에 대한 작업도 수행하도록 만들어 줄 것입니다. 그렇다면 '이미지 정보 삭제'를 수행하는 Mapper 메서드가 필요로 한데 이는 앞서 작성한 deleteImageAll() 메서드를 사용할 것입니다.

 

 AdminServiceImpl.java 클래스의 "goodsDelete()" 메서드의 구현 부에 '이미지 정보 삭제'를 수행하는 Mapper 메서드(deleteImageAll)를 호출하는 코드를 작성합니다.

 

		adminMapper.deleteImageAll(bookId);

 

 

 

 그리고 두 개의 쿼리가 실행이 되기 때문에 트랜잭션 처리를 해주어야 합니다. 이를 위해 @Transactional 어노테이션을 추가해줍니다.

 

그림 4-2

 

 

5. 테스트

 작성한 코드가 정상적으로 동작하는지 확인을 합니다. 확인할 점은 '상품 정보'가 DB에서 삭제되었는지, '이미지 정보'가 DB에서 삭제되었는지, 서버에 저장된 '이미지 파일'이 삭제되었는지입니다.

 

그림 5-1

 

그림 5-2

 

그림 5-3

 

그림 5-4

 

그림 5-5

 

그림 5-6

 

그림 5-7

 

그림 5-8

 

그림 5-9

 

 

REFERENCE

  • 코드로배우는 스프링 웹 프로젝트(남가람북스)
  •  

 

 

 

DATE

  • 2020.08.11

 

728x90
반응형
Comments