JAVA 웹개발 과정 국비 62일차 정리
62일차
※ 배우는 과정이므로 정확하지 않을 수 있습니다.
잘못된 점은 알려주시면 수정하도록 하겠습니다!
1. 게시판 만들기 이어서..
1) 자잘한 이동 버그 수정
- 게시글 목록으로 이동할 때, 현재 페이지 값이 없다면 1로 만들기
- 게시글
수정하기
버튼을 누르면 에러-> 수정한 페이지로 돌아가게 수정cpage
값을 넘겨서 받아옴
- 게시글
삭제하기
버튼을 누르면 에러 -> 수정한 페이지로 돌아가게 수정.cpage
값을 넘겨서 받아옴
- 게시글을 수정한 뒤,
목록으로
버튼을 2번 눌러야 게시판 목록으로 돌아가는 버그 수정.<a href="jacascript:history.back()">
으로 태그를 감싸주니 수정하기 -> 수정완료 두 단계가 되어버려서목록으로
버튼을 2번 눌러야 결국 이전 페이지로 가게 된다.- a태그 대신 js로
location.href="/toBoard.board?cpage="+${cpage};
위치를 직접 줘서 해결.
2. 소스 코드
2. File 업로드
1) 클라이언트 코드
index 페이지에 form태그와 type=file인 input태그를 만들어준다.
name 값을 설정해줘야 submit할 때 서버로 값이 넘어간다.
파일은 get
방식으로 넘기기에는 너무 크기 때문에 post
방식으로 전달한다.
파일의 내용은 binary data이기 때문에 서버에서 파일을 받을 때 request.getParameter
로는 받을 수 없다.
복잡한 데이터를 submit할 때는 enctype="multipart/form-data"
를 적어줘야 request를 만들 때 body에서 string값은 string값 대로 분류하고, 파일 data는 파일 data 대로 분류를 해준다.
즉, 이 코드를 적어주지 않으면 string 값과 파일 data값이 뒤섞여서 전달되게 된다.
파일이 submit 대상으로 포함된다면?
- method는 post 방식이여야만 한다.
- enctype=”multipart/form-data” 적어주기
<form action="/upload.file" method="post">
<input type="file" name="file"><br>
<input type="submit">
</form>
2) 서버 코드 - 1
① 기본 코드 생성
필요없는 코드를 삭제하고, 어노테이션을 *.file
으로 수정한다.
컨트롤러 기본 코드들을 추가하고 /upload.file
을 받을 수 있게 if문을 만든다.
@WebServlet("*.file")
public class FileController extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf8");
String uri = request.getRequestURI();
String ctx = request.getContextPath();
String cmd = uri.substring(ctx.length());
System.out.println(cmd);
if(cmd.equals("/upload.file")){
// 파일 업로드 기능
}
}
② 값을 받아오려면?
클라이언트에서 multipart/form-data
로 인코딩되어 값이 넘어오기 때문에, 서버에서 기존 request
로는 그 값을 받을 수 없다.
따라서 인코딩 된 값을 분석해주는 라이브러리를 이용하여 값은 받아와야 한다.
가장 유명한 라이브러리로 apache file-upload
가 있지만, 이 라이브러리는 spring
과 같이 써야 편하기 때문에 지금은 cos
로 사용하기로 한다.
③ 라이브러리 적용
maven repository
에서 받았던 cos
는 낮은 버전이여서 구글에 cos.jar
를 검색하여 servlets.com/cos
주소를 가지는 공식 홈페이지에 들어가서 cos-20.08.zip
을 다운받았다.
압축을 풀고 WEB-INF
폴더에 lib
폴더에 복붙해준다.
④ 톰캣의 홈 디렉토리와 배포
톰캣은 가동하려면 홈 디렉토리가 필요하다. 홈 디렉토리가 있어야 다른 파일들의 위치를 알 수 있기 때문이다.
이클립스의 프로젝트 폴더를 홈 디렉토리로 착각하지 않도록 주의한다.
이클립스에서 ctrl + f11로 실행하는 순간 톰캣은 서버를 가용하는데 필요한 파일들을 추려서 복사를 한다. 그 곳을 홈으로 잡고 톰캣은 가동을 한다.
코드는 수정했는데 브라우저에서는 반영이 안되었을 때 이러한 복사과정에서의 버그가 일어난 것은 아닌지 의심할 수 있어야 한다.
이런 상황에서는 이클립스 서버에 우클릭을 해서 Clean
을 눌러서 복사되어있는 내용을 다 지워버리고 다시 복사를 받으면 해결이 가능하다.
바로 이 복사하는 과정을 배포(Deployment)
라고 한다.
아래와 같이 코드를 실행해주면 홈 디렉토리 경로가 출력된다.
나의 경우는 아래와 같이 나왔다.
System.out.println(request.getServletContext().getRealPath(""));
E:\2021_09_웹응용과정\workspace_backend\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\Day_03_File\
⑤ 파일의 경로
홈 디렉토리가 있다는 것은 클라이언트가 파일을 저장하거나 다운받을 때 프로젝트 파일에 접근하는 것이 아니라 톰캣의 홈 디렉토리에 접근한다는 것을 의미한다.
savePath
변수는 클라이언트가 파일을 업로드했을 때 파일을 저장할 위치를 받는다.
위에서 언급한대로 프로젝트 파일에 직접적으로 클라이언트가 접근하는 것이 아니기 때문에 savePath
의 위치를 c:/abc/
이렇게 지정해줄 수는 없다.
따라서 savePath
의 경로도 톰캣의 홈 디렉토리 안에 files
폴더를 하나 만들어서 지정해준다.
files
폴더가 없다면 만들어주는 if문도 추가한다.
String savePath = request.getServletContext().getRealPath("files");
File filePath = new File(savePath);
if(!filePath.exists()) {filePath.mkdir();}
⑥ 라이브러리 사용
COS
라이브러리의 MultipartRequest
객체는 컨트롤러에 있는 기본 request
를 라이브러리로 가져온 MultipartRequest
로 업그레이드 시킨다
또한 동시에 업로드 된 파일을 지정 경로에 저장하는 기능도 수행한다.
첫 번째 파라미터로 어떤 객체인지 (어떤 객체 안에 있는 파일인지),
두 번째 파라미터로 파일의 저장 위치,
세 번째 파라미터로 파일의 최대 사이즈(byte기준),
네 번째 파라미터로 인코딩,
마지막 파라미터는 동일한 파일을 올렸을 경우 덮어씌워지지 않도록 이름을 자동으로 바꿔주는 기능(COS
에서 제공)을 넣어준다.
int maxSize = 1024*1024*10; // 10MB
MultipartRequest multi = new MultipartRequest(request, savePath, maxSize, "UTF8", new DefaultFileRenamePolicy());
3) DTO, DAO 생성
① File 테이블에 필요한 속성들
업로드 당시의 원래 파일 이름 oriName
과 클라이언트에게 보여지는 파일 이름이 다를 수도 있기 때문에(중복된 파일 이름이 존재할 수 있기 때문) oriName
은 DB에 저장한다.
또한 파일을 다운로드할 때 서버쪽에 저장 된 이름 sysName
으로 파일을 내보내야 하기 때문에 sysName
도 필요하다.
예를 들면, abc.jpg
파일을 업로드를 했다. 그러나 이미 서버에 abc.jpg
파일이 존재한다면, 서버에서는 새로운 abc.jpg
를 abc(1).jpg
로 저장해야 한다. (중복되면 덮어씌워지므로)
그러나 클라이언트 입장에서는 abc.jpg
를 업로드했는데, 첨부로 보여지는 이름이 abc(1).jpg
이면 이상하게 느껴질 것이다.
따라서 서버는 abc(1).jpg
로 파일을 저장하되, 클라이언트에게 보여줄 때는 abc.jpg
로 보여줘야 한다.
- Original File Name
- 업로드 당시의 원래 파일 이름
- 위의 예에서는
abc.jpg
를 나타낸다. - DB에 저장 필요
- System File Name
- 서버에 저장되는 실제 파일 이름
- 위의 예에서는
abc(1).jpg
를 나타낸다. - DB에 저장 필요
기본적으로 식별자로 seq
번호가 필요할 것이다.
또한, 예를 들면 어떤 게시글에 올라온 파일인지를 알아야하는 것처럼 파일의 parent seq
를 알아야한다.
이 네개의 정보는 필수적으로 들어가 있어야 하는 것이다.
Optional로는 아래와 같은 정보들이 있다.
- 저장 경로
- 업로드 날짜, 시간
- 파일 사이즈
이 정보들을 토대로 files
테이블을 생성한다.
CREATE TABLE files(
seq NUMBER PRIMARY KEY,
oriName VARCHAR(100) NOT NULL,
sysName VARCHAR(100) NOT NULL,
--parentSeq NUMBER REFERENCES board(seq)
parentSeq NUMBER NOT NULL
);
CREATE SEQUENCE files_seq START WITH 1 INCREMENT BY 1 NOCACHE NOMAXVALUE;
② FileDAO, FileDTO 생성
DAO와 DTO를 생성해준다.
4) 서버 코드 - 2
DAO와 DTO도 만들어줬으니 file을 DB에 저장해보자.
예외는 try-catch로 전가한다.
String sysName = multi.getFilesystemName("file");
String oriName = multi.getOriginalFileName("file");
dao.insert(new FileDTO(0,oriName,sysName,0));
3. File 목록 출력과 다운로드
업로드한 파일 목록 출력은 기존 방법들과 다르지 않다.
다만 주의할 점은 AJAX를 이용할 때는 서버쪽에 한글 인코딩 코드 response.setContentType("text/html;charset=utf8");
를 별도로 적어줘야한다.
컨트롤러를 거쳐 다운로드를 하는 방법은 다음 수업때 배울 예정이므로, 이번에는 다이렉트로 링크를 클릭하여 다운받는 간단한 방법을 배웠다.
<!-- 클라이언트 -->
<button id="fileList">파일 명단 보기</button>
<br>
<fieldset>
<legend>파일 목록</legend>
</fieldset>
<script>
$("#fileList").on("click",function(){
$.ajax({
url:"/list.file",
dataType:"json"
}).done(function(resp){
for(let i=0;i<resp.length;i++){
let div = $("<div>");
let anker = $("<a>");
// 다이렉트로 다운
anker.attr("href","/files/"+resp[i].sysName);
anker.text(resp[i].oriName);
div.append(anker);
$("legend").after(div);
}
})
})
</script>
// 서버
else if(cmd.equals("/list.file")) {
List<FileDTO> list = dao.selectAll();
Gson g = new Gson();
String result = g.toJson(list);
// ajax는 한글 처리하는 코드를 따로 써줘야함.
response.setContentType("text/html;charset=utf8");
response.getWriter().append(result);
}
댓글남기기