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);
}
댓글남기기