Kim VamPa

[Spring][쇼핑몰 프로젝트][27] 업로드 이미지 정보 등록 - 2 본문

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

[Spring][쇼핑몰 프로젝트][27] 업로드 이미지 정보 등록 - 2

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

목표

업로드 이미지 정보 DB 등록

 저번 포스팅에선 이미지의 정보를 서버로 전송하는 작업을 하였습니다. 이번 포스팅에선 이미지를 등록하는 Mapper메서드를 작성하고, 기존의 '상품 정보'를 DB에 등록하는 bookEnrll() 메서드에 'bookId'값을 반환하도록 <selectkey> 태그를 추가해줄 것입니다.

 

 

순서

1. 방향

2. imgeEnroll() [Mapper 단계

3. bookEnroll() <selectkey> 적용 [Mapper 단계]

4. bookEnroll() 처리 [Service 단계]

 

 

1. 방향

 가장 먼저 이미지 정보 테이블에 삽입 쿼리를 실행하는 Mapper 메서드 imageEnroll()를 작성할 것입니다. 보통의 경우 imageEnroll() 메서드를 호출하는 Service 단계의 메서드를 새로 작성하겠지만, 이번의 경우 상품 정보 등록 Mapper 메서드를 호출하는 Service 단계의 bookEnroll() 메서드에서 imageEnroll() 메서드를 호출하도록 만들 것입니다.

 

 Service 단계의 bookEnroll() 메서드는 뷰(View)로부터 전달받은 데이터인 BookVO를 활용하여 '상품 정보'와 '이미지 정보'를 DB에 등록하는 작업을 하게 될 것입니다. 그런데 한 가지 문제 점이 있습니다. '이미지 정보'를 DB에 등록하는 쿼리문(imageEnroll())에는'bookId'에 관한 데이터가 반드시 필요로 한데(외래 키 설정 때문) 서버로 부터 전달받은 BookVO 객체에는 'bookId'에 관한 데이터가 없다는 점입니다.

 

 필요로 한 'bookId' 정보는 DB에 등록하는 과정에서 새롭게 값이 부여되는 'bookId'에 대한 정보가 필요로 합니다. 이를 해결하기 위해서 '이미지 정보' 등록 Mapper 메서드(imageEnroll()) 호출에 앞서서 '상품 정보' 등록 Mapper 메서드(bookEnroll())를 먼저 호출을 하도록 하고, 해당 메서드에서는 '상품 정보' 삽입 쿼리를 실행하는 동시에 그 과정에서 새롭게 부여되는 'bookId' 칼럼(Column) 값을 BookVO 객체의 'bookId' 변수에 반환하도록 만들 것입니다('bookId'값을 반환받기 위해 MyBatis의 <selectkey> 태그를 활용합니다). 그리고 반환받은 'bookId'정보를 활용하여 '이미지 정보' DB 등록을 처리하도록 할 것입니다. 

 

그림 1-1

 

 

 

2. imgeEnroll() [Mapper 단계]

 

 이미지 정보 테이블(vam_image)에 데이터를 삽입하는 쿼리로서 아래의 명령문을 사용할 것입니다.

 

insert into vam_image(bookId, fileName, uploadPath, uuid) values (bookId, 'fileName', 'uploadPath', 'uuid');

 

 먼저 AdminMapper.java 인터페이스에 imageEnroll() 메서드 선언부를 작성합니다.

 

	/* 이미지 등록 */
	public void imageEnroll(AttachImageVO vo);

 

 

 파라미터로는 이미지 정보가 담긴 AttachImageVO 클래스를 지정하였습니다. 이미지 정보 경우 BookVO에서 List 자료구조로 보관되는데 Mapper 단계에서 List에 담겨 있는 이미지 정보(AttachImageVO)들을 한 번에 처리할 수는 없습니다. 따라서 Mapper 단계 ImageEnroll() 메서드에서는 하나의 이미지 정보(AttachImageVO)를 처리하도록 하고 Service 단계에서 List에 담겨 있는 이미지 정보(AttachImageVO) 요소수만큼 for문 등을 활용하여 ImageEnroll() 메서드를 호출하도록 할 것입니다. 

 

 


 AdminMapper.xml에 imageEnroll() 메서드가 실행할 쿼리문이 담긴 <insert> 태그를 추가합니다.

 

	<!-- 이미지 등록 -->
	<insert id="imageEnroll">
	
		insert into vam_image(bookId, fileName, uploadPath, uuid) values (#{bookId}, #{fileName}, #{uploadPath}, #{uuid})
	
	</insert>

 

그림 2-2

 


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

 

	/* 이미지 등록 */
	@Test
	public void imageEnrollTest() {
		
		AttachImageVO vo = new AttachImageVO();
		
		vo.setBookId(137);
		vo.setFileName("test");
		vo.setUploadPath("test");
		vo.setUuid("test2");
		
		mapper.imageEnroll(vo);
		
	}

 

그림 2-3

 

그림 2-4

 

그림 2-5

 

 

 

 

 

3. bookEnroll() 적용 [Mapper 단계]

 

 Mapper 단계의 bookEnroll() 메서드 수행 후, 등록한 'bookId' 컬럼(Column)값을 파라미터로 전달된 BookVO객체의 'bookId' 변수에 반환되도록 해주록 하기 위해 AdminMapper.xml 파일의 bookEnroll() 메서드가 수행하는 <insert> 태그에 <selectkey> 태그를 적용해줄 것입니다.

 

 <selectkey>는 자동생성키 컬럼(autoincrement(mysql), IDENTITY(Orace))을 지원하지 않는 데이터베이스에 자동생성키 기능과 비슷한 효과를 구현하기 위해서 사용하거나, 쿼리(예를 들어 insert)에서 수행된 특정 칼럼 값을 반환받기 위해 사용합니다. (자세한 사항은 MyBatis 공식 홈페이지를 참고해주세요.)

 

 bookEnroll()이 수행하는 <insert> 태그 내부에 아래의 <selectkey> 태그와 태그가 수행할 코드를 추가해줍니다.

 

  		<selectKey resultType="int" keyProperty="bookId" order="BEFORE">
  		
  			SELECT MAX(bookId)+1 FROM vam_book
  		
  		</selectKey>

 

그림 3-1

 

 해석을 하면 oreder의 속성 값이 "BEFORE"이기 때문에 '상품 정보'를 등록하는 insert문이 실행되기 전에 <selectkey> 태그 내의 쿼리문이 실행이 되고, 쿼리 문의 결과 값이 keyProperty 속성 값으로 지정한 'bookId'(BookVO 멤버 변수)에 resultType 속성 값으로 지정한 int 타입으로 반환한다는 의미입니다.

 

 <selectKey>태그 내부의 쿼리문은 vam_book 테이블의 'bookId' 칼럼 값 중 가장 큰 값(MAX(bookId))을 가져와서 거기에 +1을 해준 결과를 반환해줍니다. 이는 insert문의 결과로 삽입될 'bookId' 칼럼(Column)의 값이 기존 'bookId'값들 중 가장 큰 값에서 +1 된 값이기 때문입니다.

 

 

* 엄밀히 말하면 해당 쿼리문은 에러를 일으킬 수 있는 쿼리문입니다. 왜냐하면 <selectKey> 태그 내부에 작성한 쿼리문은 현재 테이블에 있는 'bookId' 값 중 가장 큰 값에서 +1을 해오는 값인데, DB에서 처리하는 자동생성키 값과 일치하지 않을 수 있기 때문입니다. 예를 들어 사용자가 상품을 등록하여 'bookId' 값이 300이 가장 큰 값이 되었는데, 새로운 상품을 등록하기 전 최근에 등록한 상품을 지운다면 가장 큰 값은 299가 되기 됩니다. 문제는 DB의 자동생성키 기능으로 인해 다음 생성하는 값은 301인데 반해 우리가 작성한 <selectKey> 태그 내부 쿼리 결과는 300이 되어서 일치하지 않는 문제가 발생할 수 있습니다. 

 이를 해결하기 위해선 제일 최근에 등록된 컬럼 값을 호출해주는 DB의 기능을 사용해주면 됩니다. Oracle과 MySQL의 방식이 달라서, 해당 기능에 대한 설명과 코드를 추가한다면 글이 너무 길어지기 때문에 다음 포스팅에서 진행합니다.


 실제 의도대로 'bookId'값이 반환되는지 Junit 테르트를 통해 확인해보겠습니다. AdminMapperTests.java에 기존에 '상품 등록' Mapper 테스트를 위해 작성한 코드를 활용하겠습니다. 

 

 기존 코드에 작가 아이디(authorId) 값과 카테고리 코드(cateCode)값만 DB에 등록되어 있는 값으로 변경해주었습니다. 추가적으로 Mapper 메서드가 호출되기 전과 후에 Mapper 메서드에 전달하는 BookVO 객체에 무엇이 담겨있는지 Console 창으로 확인을 할 수 있도록 코드를 추가하였습니다.

 

		System.out.println("Before BookVO :" + book);
		
		System.out.println("After BookVO :" + book);

 

그림 3-2

 

 Junit 테스트를 하여 그 결과를 확인해봅니다.

 

그림 3-3

 

그림 3-4

 

그림 3-5

 

테스트 결과를 보시면 Mapper메서드 호출 후 bookId 값이 반환되어져 있는 것을 확인할 수 있습니다.

 

 

 

REFERENCE

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

 

 

 

DATE

  • 2020.06.08

 

728x90
반응형
Comments