-
[Java] SpringBoot 프로젝트 : 프로필 사진 기능 구현(3) - 추상 클래스(AbstractView, Tiles, Base64)프로젝트/기능 정리 2023. 6. 10. 16:45
목적
- 회원제 웹사이트 개발시 거의 필수적으로 사용되는 프로필 사진 등록 및 수정 기능 구현
- 프로필 이미지 등록 관련 DB, Java, JS 처리 총 정리
- 일부 받아서 쓴 코드 리뷰
지난 글에선 jsp가 컨트롤러에 이미지를 요청하면, 컨트롤러에서 기본이미지 또는 DB에서 꺼낸 이미지를 바이트 배열로 변환하여 모델에 저장하는 과정까지 살펴봤다.
이번 글에서는 컨트롤러가 마지막에 리턴하는 AbstractView인 imageView 클래스를 살펴보자.
데이터 흐름도는 다음과 같다.
[데이터 흐름도]
프로필이미지 데이터 흐름도 상기를 위해 컨트롤러의 photoView.do로 매핑된 부분의 코드를 다시 보자.
[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"; }
이전 글에서 설명한 처리과정들을 다 따라왔다면, 마지막에 view를 리턴하기 전에 모델에 아래 두 가지 데이터가 들어 있음을 이해할 것이다.
- imageFile이라는 key에 기본이미지 또는 DB에 있던 이미지가 byte[]로 저장
- filename이라는 key에 기본이미지 파일명 또는 DB에 있던 이미지 파일명 String으로 저장
보통 Spring 프레임워크를 사용해서 MVC2패턴대로 코드를 작성하면, 요청 받은 컨트롤러는 어떤 서비스 로직들을 처리한 뒤 model에 view에서 사용할 데이터를 담고 view를 리턴해야 한다.
여기서 리턴되는 view는 논리적 view 명칭으로, 난 Tiles를 사용했기 때문에 해당 view명으로 정의된 tiles definition을 TilesViewResolver가 찾아서 매핑된 템플릿을 보여주게 된다.
짧게 코드로 예시를 보면 다음과 같다.
[AppConfig.java]
//자바코드 기반 설정 클래스 @Configuration public class AppConfig implements WebMvcConfigurer{ //타일스 설정 @Bean public TilesConfigurer tilesConfigurer() { final TilesConfigurer configurer = new TilesConfigurer(); //아래 경로에 xml 설정 파일을 넣음 configurer.setDefinitions(new String[] { "/WEB-INF/tiles-def/main.xml", }); configurer.setCheckRefresh(true); return configurer; } @Bean public TilesViewResolver tilesViewResolver() { final TilesViewResolver tilesViewResolver = new TilesViewResolver(); tilesViewResolver.setViewClass(TilesView.class); return tilesViewResolver; } }
위와 같이 자바코드로 타일스 설정을 해줬다.
[main.xml]
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd"> <tiles-definitions> <definition name="main" template="/WEB-INF/views/template/layout.jsp"> <put-attribute name="title" value="메인페이지"/> <put-attribute name="header" value="/WEB-INF/views/template/header.jsp"/> <put-attribute name="body" value="/WEB-INF/views/main/main.jsp"/> <put-attribute name="footer" value="/WEB-INF/views/template/footer.jsp"/> </definition> </tiles-definitions>
위와 같이 타일스 설정을 해주고 타일스 설정 xml파일을 만들어 템플릿 경로를 지정해주면 템플릿 내 반복되는 요소(메뉴바, 헤더, 푸터 등)들을 매 페이지마다 작성해줄 필요 없이 타일을 끼워맞추듯 사용할 수 있다.
타일스 관련 정보가 더 궁금하면 찾아보도록 하자..
중요한 것은 위에서 리턴하고 있는 imageView는 클래스로 어딘가 탬플릿에 매핑된 view의 명칭이 아니다. 즉, view가 아니라는 것..
이해를 돕기 위해 imageView 클래스 코드를 먼저 보자.
이 클래스도 배울 때 받아서 그대로 쓴건데 테스트 해보니 이미지를 페이지에 표시하는데 꼭 필요하지 않은(내 생각엔..) 코드들이 있어 제거한 코드만 보겠다.
[imageView 클래스]
@Component public class ImageView extends AbstractView { @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { byte[] file = (byte[]) model.get("imageFile"); String filename = (String) model.get("filename"); //파일 이름으로부터 확장자 뽑아내고 //response의 콘텐트 타입 설정, 헤더 설정 등 해주는 코드 있었음 OutputStream out = response.getOutputStream(); InputStream input = null; try { input = new ByteArrayInputStream(file); IOUtils.copy(input, out); out.flush(); }finally { if(out!=null) out.close(); if(input!=null) input.close(); } } }
imageView 클래스는 AbstractView를 상속 받은 클래스다.
이 AbstractView 클래스를 상속 받은 클래스는 view로 취급된다.
설명을 보자.
AbstractView 클래스 설명 View 구현을 위한 추상 기본 클래스라고 되어있다.
하위 클래스는 JavaBeans이어야 하고, 정적 attribute를 정해주는 다양한 방법을 통해 그걸 view에서 사용할 수 있게 해준다고 한다.
쉽게 Java 클래스지만 view처럼 작동하도록 만들어주는 클래스라고 보면될 것 같다.
찾아보니 파일 다운로드 기능을 만들 때 AbstractView를 상속받은 클래스를 만들어 구현하는 방식으로 많이 쓰이는 것 같다.
여러 활용을 보고 싶으면 다음 포스트가 도움이 될 것 같다.
참고 : https://backendcode.tistory.com/118
작성하다보니 어차피 바이트 배열은 가지고 있고 그것만 모델에 담아서 view로 넘겨주면 이미지를 띄우는 것은 가능할 것 같다는 생각이 들었다.
이건 뒤에서 코드를 좀 바꾸면서 알아보도록 하고 일단 받은 코드부터 보자.
흐름은 다음과 같다.
- 모델에 담겨있던 데이터 꺼내서 변수에 할당
- response를 통해 요청한 client에 배열 데이터를 보내기 위해 OutputStream 생성
- 변수에 담긴 바이트 배열을 읽기 위해 ByteArrayInputStream에 인자 넘겨주면서 생성
- 읽은 배열 OutputStream에 복사
- OutputStream 버퍼에 담긴 데이터 flush해서 강제 출력
이 과정을 거치면 드디어 jsp에서 이 컨트롤러를 호출한 곳에 이미지가 표시된다.
[결과화면]
결과 화면 [Byte[] → Base64]
아까 위에서 얘기했듯 이미지를 화면에 띄우는 것까지는 굳이 AbstractView를 이용하지 않아도 될 것 같다는 생각이 들었다.
글 작성을 위해 이것저것 찾아보면서 img태그에 대해 알게된 것이 하나 있는데 그것부터 보자.
나중에 이미지 전송 코드를 보면서 다룰텐데, 회원이 이미지를 등록할 때 미리보기 기능을 구현한 부분이 있다.
JS에서 FileReader 객체를 생성한 뒤 회원이 업로드 한 파일을 읽어서 img태그 src 속성에 할당해주는데 어떻게 작동하는지 궁금해서 콘솔에 찍어봤다.
console화면 FileReader가 읽은 결과를 찍어보니 위와 같이 알 수 없는(?) 긴 문자열이 나왔다.
이게 src 속성에 들어가면 이미지가 나오는건가 싶어서 그대로 복사한 뒤 src 속성에 넣어봤다.
나온다.. 저 앞에 data:image/png;base64, 이것도 들어가야 정상적으로 이미지가 나오길래 찾아봤다.
Base64는 8비트 이진 데이터를 문자코드에 영향 받지 않는 공통 ASCII영역 문자들로만 이루어진 문자열로 바꾸는 인코딩 방식이라고 한다(출처: 위키백과).
좀 더 지금 작업과 직결되는 과정을 다룬 포스트가 있어 아래 첨부하니 확인해보면 좋을 것 같다.
참고 : https://zyngirok.com/entry/HTML-img-base64-encode
암튼 img태그의 src 속성은 이런 base64방식으로 인코딩된 데이터를 읽을 수 있다.
그럼 아까 Java쪽에서 구한 이미지 바이트 배열을 base64 인코딩으로 변환해서 모델에 담은 뒤 view로 전송하면 굳이 AbstractView 클래스를 거치지 않고 처리할 수 있지 않을까..?
해봤다.
[Controller]
그냥 일반 view를 리턴하는 컨트롤러에서 다음과 같은 작업을 해줬다.
//마이페이지 메인 호출 @RequestMapping("/mypage/myPageMain.do") public String test(){ MemberVO user = (MemberVO)session.getAttribute("user"); //회원 정보 VO에 담기 MemberVO member = mypageService.selectMember(user.getMem_num()); //등록된 이미지 가져오기 byte[] mem_photo = member.getMem_photo(); //byte64 인코딩으로 변환 String byte64 = Base64.getEncoder().encodeToString(mem_photo); //모델에 담기 model.addAttribute("imageFile", byte64); return "myPageMain"; //타일스 설정값 }
java.util 패키지에 Base64 클래스가 있어 그걸 사용하면 된다.
[JSP]
jsp에서는 그냥 img태그 하나 만들고 다음과 같이 써줬다.
<img src = "data:image/png;base64, ${imageFile}">
[결과 화면]
든든.. 잘나온다.
암튼 이렇게 해서 DB에서 꺼내온 바이트 배열이미지를 Base64 인코딩 방식으로 변환해서 화면에 표시하는 방법까지 알아봤다.
앞에 파일 형식이랑 인코딩 방식 문자열도 Java단에서 종류에 따라 조건문으로 분기해서 처리하면 쓸만할지도 모르겠다.
이제 jsp에서 이미지를 요청하면 컨트롤러에서 여러 처리를 거쳐 최종적으로 화면에 이미지가 표시되는 과정을 모두 정리했다.
다음 글부터는 흐름도의 하단부에 해당하는 회원이 프로필 이미지를 등록하거나 수정할 때 처리과정을 살펴보자.
'프로젝트 > 기능 정리' 카테고리의 다른 글