-
[Java] SpringBoot 프로젝트 : 프로필 사진 기능 구현(2) - 이미지 불러오기(Oracle DB, BLOB, MyBatis)프로젝트/기능 정리 2023. 6. 9. 21:48
목적
- 회원제 웹사이트 개발시 거의 필수적으로 사용되는 프로필 사진 등록 및 수정 기능 구현
- 프로필 이미지 등록 관련 DB, Java, JS 처리 총 정리
- 일부 받아서 쓴 코드 리뷰
지난 글에 이어서 이번에는 회원이 등록한 이미지가 있을 때 DB에서 어떻게 꺼내오는지 살펴보자.
데이터 흐름도는 다음과 같다.
[데이터 흐름도]
프로필이미지 데이터 흐름도 지난 글에서 jsp가 MypageController에 요청을 보냈을 때 일어나는 처리까지 살펴봤다.
이제 회원이 등록해둔 프로필 이미지가 있는 경우 DB에서 불러오는 과정을 보자.
[Controller - photoView]
@RequestMapping("/mypage/photoView.do") public String getProfile(HttpSession session, HttpServletRequest request, Model model) { // 로그인한 회원 정보 세션에서 가져오기 MemberVO user = (MemberVO)session.getAttribute("user"); if(user == null) { //로그인하지 않은 경우 //현재 서버가 돌아가고 있는 디렉토리에서 기본 이미지 불러와서 FileUtil처리 byte[] readbyte = FileUtil.getBytes(request.getServletContext().getRealPath("/image_bundle/default_img.png")); //처리된 이미지 바이트 배열, 파일명 모델에 담기 model.addAttribute("imageFile", readbyte); model.addAttribute("filename", "default_img.png"); }else { //로그인한 경우 //주입 받은 서비스를 통해 DB에 회원번호를 넘겨주고 회원정보 VO에 담기 MemberVO memberVO = mypageService.selectMember(user.getMem_num()); //viewProfile 메소드에 전달 viewProfile(memberVO,request,model); } //imageView라는 AbstractView로 리턴 return "imageView"; }
로그인 한 경우 주입 받은 서비스를 통해 DB에 회원 번호를 넘겨주고 DB에 등록되어 있던 회원정보를 memberVO에 담는다.
DB에는 다음과 같은 데이터들을 저장할 수 있게 해뒀다.
회원 DB Column 회원이 프로필 이미지를 등록하면 BLOB 타입으로 mem_photo에 저장되고, 파일명이 mem_photo_name에 저장된다.
배울 당시 무작정 따라해서 이미지를 보여줄 때 꼭 파일명이 필요한줄 알고 DB에 따로 저장했다.
받은 코드를 하나씩 뜯어보니 이미지를 화면에 띄우는 것까지는 파일명이 꼭 필요하지 않은 것 같다(해당 파일을 다운로드 하는데는 필요할 수도 있다. 근데 이것도 하드코딩으로 그냥 지정해버릴 수 있는 것 같음).
그래도 이미지 바이트 배열과 파일명을 함께 처리하면서 파일명만 가지고도 회원이 등록한 프로필 이미지가 있는지 확인할 수 있다는 이점이 있다.
암튼 DB에서 받아온 VO를 이전 글에서 설명했던 viewProfile 메소드에 전달해줬다.
[viewProfile 메소드]
상기하기 위해 다시 보면 코드는 다음과 같다.
public void viewProfile(MemberVO member, HttpServletRequest request, Model model) { if(member == null || member.getMem_photo_name() == null) { byte[] readbyte = FileUtil.getBytes(request.getServletContext().getRealPath("/image_bundle/default_img.png")); model.addAttribute("imageFile", readbyte); model.addAttribute("filename", "default_img.png"); }else { model.addAttribute("imageFile", member.getMem_photo()); model.addAttribute("filename", member.getMem_photo_name()); } }
전달 받은 VO에 파일명(mem_photo_name)이 있는지 확인 후 있으면 모델에 회원이 등록해둔 이미지 바이트 배열과 파일명을 담아줬다.
여기서는 왜 FileUtil 클래스를 이용해 바이트 배열로 변환하지 않고 바로 모델에 담는지 의문을 가질 수 있다.
설명을 위해 잠시 딴 길로 새보자.
[MemberVO]
회원 데이터를 어딘가에 전달하고, 전달받기 위해 이 값들을 담아둘 객체를 만들어서 사용했다. 이게 MemberVO다.
MemberVO에 다양한 값들을 담을 수 있도록 했지만 여기선 이미지 처리와 관련된 변수와 getter/setter만 보자.
MemberVO는 다음과 같다.
public class MemberVO { private int mem_num; private byte[] mem_photo; private String mem_photo_name; //회원번호 public int getMem_num() { return mem_num; } public void setMem_num(int mem_num) { this.mem_num = mem_num; } //이미지 바이트 배열 public byte[] getMem_photo() { return mem_photo; } public void setMem_photo(byte[] mem_photo) { this.mem_photo = mem_photo; } //이미지 파일명 public String getMem_photo_name() { return mem_photo_name; } public void setMem_photo_name(String mem_photo_name) { this.mem_photo_name = mem_photo_name; } //이미지 업로드시 public void setUpload(MultipartFile upload) throws IOException{ //MultipartFile -> byte[] setMem_photo(upload.getBytes()); //파일 이름 setMem_photo_name(upload.getOriginalFilename()); } }
선언된 변수의 타입을 보면 알겠지만 private byte[] mem_photo; 즉 바이트 배열로 선언되어 있다.
후에 이미지 등록 및 수정을 정리하면서 setUpload에 대해 또 다루겠지만, 미리 설명하자면 폼에서 이미지 전송 시 setUpload가 호출된다.
인자로 전달받은 파일.getBytes()한 바이트 배열을 mem_photo에 넣어주고, mem_photo_name에 파일명을 넣어준다.
이렇게 바이트 배열로 전환한 데이터를 DB에 전달해주고, DB는 해당 데이터를 BLOB타입으로 선언된 컬럼에 저장한다.
BLOB는 Binary Large Object의 약자로 바이너리(이진) 대용량 데이터를 처리할 수 있는 타입이다.
약간 의문이 생기지 않는가.. DB는 BLOB 타입으로 선언된 컬럼에 데이터를 넣는데 왜 Java쪽에선 바이트 배열로 변환해서 전달하는지..
정리하며 뭔가 이상해서 찾아봤다.
[MyBatis..?]
답은 아래 포스트에서 찾았다(감사합니다..).
출처 : https://logical-code.tistory.com/103
설명하자면, 기본적으로 SpringBoot 프로젝트를 하면서 MyBatis를 사용했는데 MyBatis에 대한 설명은 다음과 같다.
MyBaits홈페이지 소개글 쉽게 JDBC(Java Database Connectivity: 자바 언어로 데이터베이스에 접속할 수 있도록 하는 자바API. 출처: 위키백과)로 개발할 경우 코드도 길어지고 쿼리문도 반복해서 써줘야되고 이것저것 서비스 처리랑 뒤섞여서 복잡해지는데 그런걸 편하게 도와주는 프레임워크라고 생각하면 될 것 같다. 궁금하면 들어가서 직접 보자..여기서
암튼 이 MyBatis의 타입핸들러가 자동으로 Java의 바이트 배열을 JDBC의 BLOB타입으로 매핑해준다는 거다.
덕분에 그냥 Java쪽에서 이미지를 바이트 배열로 변환한 뒤, 쿼리문 작성해서 넘겨주면 DB에 BLOB타입으로 저장되고, 반대로 BLOB타입도 바이트 배열에 담아 꺼내올 수 있게 된 것이다.
돌아와서 이제 주입 받은 서비스 흐름만 적고 넘어가자.
[Mapper]
@Mapper public interface MemberMapper { //회원정보 가져오기 @Select("SELECT * FROM member m JOIN member_detail d ON m.mem_num=d.mem_num JOIN member_history h ON m.mem_num = h.mem_num WHERE m.mem_num=#{mem_num}") public MemberVO selectMember(Integer mem_num); }
[Service]
public interface MypageService { public MemberVO selectMember(Integer mem_num); }
[ServiceImpl]
@Service @Transactional public class MypageServiceImpl implements MypageService{ @Autowired private MypageMapper mypageMapper; @Override public MemberVO selectMember(Integer mem_num) { return mypageMapper.selectMember(mem_num); } }
[Controller]
@Controller public class MypageController { @Autowired private MypageService mypageService; }
이전 글과 지금까지의 흐름을 정리하면 이렇다.
- jsp에서 photoView.do 호출
- 요청 받은 컨트롤러는 회원의 로그인 여부 확인
- 로그인 안한 경우 : FileUtil 클래스에 기본 이미지 경로 보낸 후 이미지 바이트 배열로 변환해서 모델에 저장
- 로그인 한 경우 : 회원이 등록한 프로필 이미지가 DB에 있으면 (바이트 배열에 담아)꺼내서 모델에 저장, 없으면 위와 동일
이제 마지막으로 컨트롤러가 리턴하는 AbstractView인 imageView 클래스만 확인하면 페이지에 프로필 이미지가 뜨는걸 볼 수 있다.
다음 글에서 이어 작성해보겠다.
'프로젝트 > 기능 정리' 카테고리의 다른 글