Day 57. MVC2 : 파일 업로드 기능

ho_c·2022년 5월 7일
0

국비교육

목록 보기
55/71
post-thumbnail
post-custom-banner

1. JSP와 AJAX의 차이

컨트롤러의 sendRedirect forward를 사용해서 JSP에 데이터를 넘기는 것과 컨트롤러가 AJAX로 데이터를 전달하는 것은 큰 차이가 있다.

흔히 오해하는 것이 둘 다 클라이언트-서버에서 데이터를 넘긴다고 생각하는데, 전자는 서버 내에서만의 행위이고, 후자는 서버가 클라이언트에 넘겨주는 것이다.

즉, JSP 내의 EL, JSTL은 프론트가 아닌 서버에서 실행되는 코드들로, 인터프리터에 의해 자바 코드로 변환되는 것이다. 따라서 애초에 직렬화를 통한 데이터 교환이 아니라서 자바 메서드 역시 실행이 가능한 것이다..

반대로 AJAX가 서버에서 넘겨받는 것은 아무리 객체라도 그 안에 설정된 값들을 넘겨받는 것이다. 따라서 객체 내의 메서드를 사용해서 클라이언트 자체적으로 실행된다는 것은 무리가 있다.

결과적으로 프로그램 간의 전달은 ‘값’이 가는 것이다. 물론 절대적이진 않다. 같은 프로그래밍 언어 사이에선 미리 만들어놔서 값을 받아서 실행하는 것이 가능한데, 이 역시 데이터 내에 메서드를 넣는 건 아니다.


2. 파일 업로드 기능

게시판에서 파일이 빠지면 섭하다. 파일 업로드가 있어야지, 특정 정보를 받을 수도 있고 또는 글 작성 안에다가 사진을 넣는 거, 영상 넣기 등등 다 파일 업로드 기능이다.

그래서 오늘은 파일 업로드를 구현하는데, 몇 가지 고려사항이 있다.

  • 파일이 보내질 때는 String으로 간다. 받는 쪽에선 이를 분석한다.
  • 일반 request로는 서버에서 분석이 안 된다.
  • 파일은 서버 내의 컴퓨터에 저장된다. (DB에 저장하면 DB 부하가 늘어난다)
  • 파일은 POST로만 보낼 수 있다.

[ 구현 과정 ]

1) 클라이언트 설정

<form action="/upload.file" method="post" enctype="multipart/form-data">
	<input type="text" name=name> <input type="file" name=file>
	<input type=submit>
</form>

일단 기본 과정은 form-submit을 활용한 요청이랑 다르지 않다..

다만 전송 방식을 post로 설정해줘야 하며, 인코딩 타입을 multipart/form-data로 지정해줘야만 파일을 서버로 보낼 수 있다. 이때, name을 통해 들어가는 파라미터들도 같이 전달된다.

하지만 문제는 위 예시에서 name, file을 구별할 수 없다는 것이다. 이를 위해서 multipart/form-data을 지정해주는 것이다. 자세한 건 서버에서 알아보자.


2) 라이브러리 설치

클라이언트에서 전달된 request객체는 Tomcat이 가지고 있지만, multipart/form-data 형식을 Tomcat은 분석할 수 없다. 따라서 이를 분석해줄 서브파티 라이브러리를 설치해야 한다.

  • COS.jar
  • Apache Commons File Upload

크게 두가지가 있는데, 일단 편의성은 COS가 좋다. 하지만 스프링 프레임워크로 넘어가면 후자를 더 잘 지원해주는데, 우리는 스프링을 아직 안 사용하니 전자를 사용하기로 하였다.


3) 서버에서 분석 후 저장

뭐가 됐든 서버에서 분석을 못할 뿐, 전송만 하면 들어간다. 그럼 이제 서버에서 분석을 할 수 있도록 기본 설정을 해보자.


(1) 업로드 제한

업로드 하는 파일의 최대 크기를 정한다.

int maxSize = 1024*1024*1024 //  2^3 == 10MB

(2) 저장 경로 선택

파일은 DB가 아닌 서버에 저장된다. 따라서 서버 자체의 RealPath 내부에 저장된다.

실제 경로 문제
여기서 RealPath는 실제 서버가 실행되는 위치를 의미한다. 구체적으로 서버 프로그램이 실행되면 현재 작성 중인 프로젝트에서 서버가 실행되지 않는다.

서버 프로그램이 실행되면, 실행에 필요한 파일을 실제 서버가 실행되는 장소로 복사해서 실행한다. 그리고 이 장소를 Real Path라고 한다.

다만 소스 코드 상에서 그 위치를 관리하려면 메서드로 서버의 위치를 끌어와야 한다.

String savePath = request.getServletContext().getRealPath(“files”);

위 코드를 사용하게 되면, RealPath의 기본 경로를 String으로 반환한다. 그리고 인자값으로 디렉토리명을 넣어주면, 기본경로/‘files’를 반환한다.

(3) 디렉토리 유무 확인 : File io 사용

위에서 경로를 끌어왔다고 해당 디렉토리가 경로 안에 존재하는 것이 아니다. 따라서 없을 경우를 대비해 만들어줘야 하는데, 코드 상에선 File 객체를 사용한다.

// 객체 생성
File filePath = new File(savePath);

// 경로 안에 없으면 디렉토리 생성
if(!filePath.exists()) {filePath.mkdir();}

(4) MultipartRequest

앞에서 request 자체는 톰캣이 받아서 가지고 있다고 하였다. 문제는 클라이언트가 전송한 Multipart/form-data 는 HttpServletRequest가 분석을 못한는 것이다. 이를 위해 라이브러리를 다운받아줬으니, 기존 리퀘스트 객체를 업그레이드 해줘야한다.

MultipartRequest multi = new MultipartRequest(request, savePath, maxSize, "UTF8", new DefaultFileRenamePolicy());

인자값

① 업그레이드할 request 객체의 참조변수 : request
② 데이터 저장 경로 : savePath
③ 업로드 파일의 최대 사이즈 : maxSize
④ encoding 타입 : "UTF8"
⑤ 기존 파일과 이름이 겹칠 경우 이름 변경 : new DefaultFileRenamePolicy

이제 동시에 업로드 된 파일이 저장된 경로에 지정된 사이즈 제한 안에서 저장하며, 같은 이름의 경우 뒤에 (n)을 붙여준다. 중복 시 사용할 이름의 변경은 ⑤을 상속받아 커스텀할 수 있다.


4) DB에 파일 정보 저장

일단 DB 얘기를 하면 DB에 파일 저장은 가능하다. 하지만 웹서버 - DB 가 아닌 DB 하나에 여러 웹 서버가 연결되는 경우도 존재하는데, 이리 되면 DB에 부하가 실린다. 따라서 파일 정보만 DB에 보관하는 것을 추천한다.

파일 정보는 크게 4가지가 있다.

  • 저장 경로
  • 파일 시퀀스 : 몇 번째 파일인지.
  • 파일 이름 : 서버 측에서 저장되는 이름, 클라이언트에서 저장하는 이름
  • 파일이 첨부된 글의 시퀀스 : 몇 번째 글에 저장된 파일인지.

위에서 본인이 만드는 서비스의 정책에 따라서 다룰 정보는 바꾸면 된다. 여기서 저장 경로는 현재 다루지 않고, 사용한다면 savePath를 끌어오면 되고, 글의 시퀀스는 getParameter 로 꺼내면 된다. 그리고 파일 시퀀스는 DB에 쿼리문 날리면서 시퀀스로 자동으로 부여된다.

(1) 파일 이름 끌어오기

그럼 컨트롤러에서 진행할 일은 파일 이름을 끌어오는 것이다.

// 클라이언트가 업로드하는 원본 파일 이름
String oriName = multi.getOriginalFileName("file");

//서버측에 저장되는 시스템 파일 이름
String sysName = multi.getFilesystemName("file");

(2) DB 쿼리 입력

dao.insertFileInfo(new FilesDTO(0, oriName, sysName, parent_seq));
profile
기록을 쌓아갑니다.
post-custom-banner

0개의 댓글