ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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;
    }

     

     

    이전 글과 지금까지의 흐름을 정리하면 이렇다.

    1. jsp에서 photoView.do 호출
    2. 요청 받은 컨트롤러는 회원의 로그인 여부 확인
    3. 로그인 안한 경우 : FileUtil 클래스에 기본 이미지 경로 보낸 후 이미지 바이트 배열로 변환해서 모델에 저장
    4. 로그인 한 경우 : 회원이 등록한 프로필 이미지가 DB에 있으면 (바이트 배열에 담아)꺼내서 모델에 저장, 없으면 위와 동일

     

    이제 마지막으로 컨트롤러가 리턴하는 AbstractView인 imageView 클래스만 확인하면 페이지에 프로필 이미지가 뜨는걸 볼 수 있다.

     

    다음 글에서 이어 작성해보겠다.

Designed by Tistory.