딸기말차
[Java web] 13. MVC 실습 본문
엔코아 플레이데이터(Encore Playdata) Backend 2기 백엔드 개발 부트캠프 (playdata.io)
백엔드 개발 부트캠프
백엔드 기초부터 배포까지! 매력있는 백엔드 개발자 포트폴리오를 완성하여 취업하세요.
playdata.io
1. BoardController
해당 Controller의 구조는 클라이언트가 @WebServlet에 설정한 urlPattern에 해당하는 경로에 접근한다면,
request.getPathInfo() 를 통해 path를 추출하여 조건문을 통해 해당 path를 대조 후 동작을 실행하는 구조로 되어있다.
실행하는 동작은 총 9가지로, 다음과 같다.
1. action == null
2. action.equals("/listArticles.do")
3. action.equals("/articleForm.do")
4. action.equals("/addArticle.do")
5. action.equals("/viewArticle.do")
6. action.equals("/modArticle.do")
7. action.equals("/removeArticle.do")
8. action.equals("/replyForm.do")
9. action.equals("/addReply.do")
2. action == null, action.equals("/listArticles.do")
해당 동작은 서버를 처음 실행 시켰을 경우, listArticles.do로 들어왔을 경우에 동작하는 페이지로 GET 방식을 통해 메인페이지로 이동하는 동작을 수행한다.
1) request.getParameter를 통해 필요한 파라미터를 가져온다.
2) 가져온 파라미터가 null이면 default 값으로 1을, 아니면 가져온 값을 사용한다.
3) DAO가 필요로 하는 파라미터를 Service로 보내기 위해 Map에 담는다.
4) DAO에 접근하기 위해 Service Method를 실행한다.
5) 쿼리의 실행 결과를 return 받아, Model 역할을 하는 request 객체에 담고 JSP로 넘겨준다.
if (action == null) {
/* request 객체에서 section, pageNum을 가져온다. */
String _section = request.getParameter("section");
String _pageNum = request.getParameter("pageNum");
/* request 객체에서 가져온 section, pageNum이 null이면 1, 아니면 가져온 값으로 설정한다. */
int section = Integer.parseInt(((_section == null) ? "1" : _section));
int pageNum = Integer.parseInt(((_pageNum == null) ? "1" : _pageNum));
/* Service를 통해 DAO에 접근, 쿼리를 실행하기 위한 객체를 만든다. */
Map<String, Integer> pagingMap = new HashMap<String, Integer>();
pagingMap.put("section", section);
pagingMap.put("pageNum", pageNum);
/* Service를 통해 DAO에 접근, 쿼리 실행 결과를 담는다. */
Map<String, Object> articlesMap = boardService.listArticles(pagingMap);
articlesMap.put("section", section);
articlesMap.put("pageNum", pageNum);
/* 쿼리 실행 결과를 Model 역할을 하는 request 객체에 담아, jsp로 넘겨준다. */
request.setAttribute("articlesMap", articlesMap);
nextPage = "/board8/listArticles.jsp";
}
3. action.equals("/articleForm.do")
해당 동작은 articleForm.do에 접근했을 경우, GET 방식을 통해 Form 양식을 작성하기 위한 페이지로 이동하는 동작을 수행한다.
1) GET 방식을 통해 Form 양식을 작성할 JSP로 이동하기 위한 경로를 저장한다.
else if (action.equals("/articleForm.do")) {
/* GET 방식을 통해 Form 양식을 작성할 JSP에 접근한다. */
nextPage = "/board8/articleForm.jsp";
}
4. action.equals("/addArticle.do")
해당 동작은 addArticle.do에 접근했을 경우, 새로 작성한 글을 POST 방식으로 받아 DB에 저장하는 동작을 수행한다.
1) 파일 업로드를 하기 위해, upload 메서드에 request 객체와 response 객체를 파라미터로 넘겨준다.
2) upload 메서드의 결과 중 사용할 데이터를 추출한다.
3) DAO의 파라미터로 사용할 articleVO 객체를 생성한다.
4) DAO에 접근하여 쿼리를 실행하기 위해 Service Method를 실행한다.
5) 업로드를 하기 위한 이미지 파일의 이름이 null이나 공백이 아니라면,
6) 현재 파일을 임시저장하고 있는 temp 폴더의 경로를 String 변수에 저장한다.
7) 파일을 실제로 저장할 경로를 String 변수에 저장한다.
8) 파일을 실제로 저장할 경로에 새 폴더를 만든다.
9) 파일을 임시저장하고 있는 temp 폴더로 부터 새로 만든 폴더로 파일을 옮겨 담는다.
10) 파일 업로드를 완료한 후 알람창을 띄우고, 메인 페이지인 listArticles.do로 이동한다.
else if (action.equals("/addArticle.do")) {
int articleNO = 0;
/* 파일 업로드를 하기 위해, upload 메서드에 request 객체와 response 객체를 파라미터로 넘겨준다. */
Map<String, String> articleMap = upload(request, response);
/* upload 메서드의 결과 중 사용할 데이터를 추출한다. */
String title = articleMap.get("title");
String content = articleMap.get("content");
String imageFileName = articleMap.get("imageFileName");
/* DAO의 파라미터로 사용할 articleVO 객체를 생성한다. */
articleVO.setParentNO(0);
articleVO.setId("hong");
articleVO.setTitle(title);
articleVO.setContent(content);
articleVO.setImageFileName(imageFileName);
/* DAO에 접근하여 쿼리를 실행하기 위해 Service Method를 실행한다. */
articleNO = boardService.addArticle(articleVO);
/* 업로드를 하기 위한 이미지 파일의 이름이 null이나 공백이 아니라면 */
if (imageFileName != null && imageFileName.length() != 0) {
/* 현재 파일을 임시 저장하고 있는 temp 폴더의 경로 */
File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
/* 파일을 실제로 저장할 폴더의 경로 */
File destDir = new File(ARTICLE_IMAGE_REPO + "\\" + articleNO);
/* 파일을 실제로 저장할 폴더의 경로에 새 폴더를 만든다. */
destDir.mkdirs();
/* 파일을 임시저장하고 있는 temp 폴더로 부터 새로 만든 폴더로 파일을 옮겨 담는다. */
FileUtils.moveFileToDirectory(srcFile, destDir, true);
}
/* 파일 업로드를 완료한 후 알람창을 띄우고, 메인 페이지인 listArticles.do로 이동한다. */
PrintWriter pw = response.getWriter();
pw.print("<script>" + " alert('새글을 추가했습니다.');" + " location.href='" + request.getContextPath()
+ "/board/listArticles.do';" + "</script>");
return;
}
5. action.equals("/viewArticle.do")
해당 동작은 viewArticle.do에 접근했을 경우, 글 번호를 GET 방식으로 받아 DB에 접근, 선택한 게시글의 상세 정보를 가져와 JSP로 이동하는 동작을 수행한다.
1) 선택한 게시글의 상세 정보를 불러오기 위해, 게시글 번호를 가져온다.
2) 게시글 번호를 통해 DAO 내 쿼리를 실행하기 위해, Service Method로 접근한다.
3) return 받은 쿼리의 실행 결과를 request 객체에 담아, JSP로 보낸다.
else if (action.equals("/viewArticle.do")) {
/* 선택한 게시글의 상세 정보를 불러오기 위해, 게시글 번호를 가져온다. */
String articleNO = request.getParameter("articleNO");
/* 게시글 번호를 통해 DAO 내 쿼리를 실행하기 위해, Service Method로 접근한다. */
articleVO = boardService.viewArticle(Integer.parseInt(articleNO));
/* return 받은 쿼리의 실행 결과를 request 객체에 담아, JSP로 보낸다. */
request.setAttribute("article", articleVO);
nextPage = "/board8/viewArticle.jsp";
}
6. action.equals("/modArticle.do")
해당 동작은 modArticle.do에 접근했을 경우, 수정할 글 정보를 POST 방식으로 받아 DB에 접근, UPDATE를 실행하는 동작을 수행한다.
1) 수정한 게시글의 파일을 업로드하기 위해, upload 메서드에 request 객체와 response 객체를 파라미터로 넘겨준다.
2) 웹에서 데이터는 String 형태로 이동되기 때문에, 전달받은 게시글 번호를 int 타입으로 변환한다.
3) DAO가 필요로하는 파라미터를 만들기 위해, articleNO에 데이터를 담는다.
4) DAO 내 쿼리를 실행하기 위해 articleNO 객체를 Service Method의 파라미터로 담아 전달한다.
5) 수정하려는 이미지 파일의 이름이 null이나 공백이 아니라면,
6) 기존의 파일 이름을 가져온다.
7) temp 폴더에 임시저장한 새로 업로드할 파일의 경로를 String 변수에 저장한다.
8) 실제 파일을 저장할 경로를 String 변수에 저장한다.
9) 파일을 실제로 저장할 폴더의 경로에 articleNO 값을 이름으로 하는 새 폴더를 만든다.
10) 파일을 temp 폴더에서 실제로 파일을 저장할 경로로 이동한다.
11) 기존 파일의 경로를 File 객체에 담은 후, 삭제한다.
12) 수정 후 alert창을 띄워주고, 상세보기 페이지로 다시 이동한다.
else if (action.equals("/modArticle.do")) {
/* 수정한 게시글의 파일을 업로드하기 위해, upload 메서드에 request 객체와 response 객체를 파라미터로 넘겨준다. */
Map<String, String> articleMap = upload(request, response);
/* 웹에서 데이터는 String 형태로 이동되기 때문에, 전달받은 게시글 번호를 int 타입으로 변환한다. */
int articleNO = Integer.parseInt(articleMap.get("articleNO"));
/* DAO가 필요로하는 파라미터를 만들기 위해, articleNO에 데이터를 담는다. */
String title = articleMap.get("title");
String content = articleMap.get("content");
String imageFileName = articleMap.get("imageFileName");
articleVO.setArticleNO(articleNO);
articleVO.setParentNO(0);
articleVO.setId("hong");
articleVO.setTitle(title);
articleVO.setContent(content);
articleVO.setImageFileName(imageFileName);
/* DAO 내 쿼리를 실행하기 위해 articleNO 객체를 Service Method의 파라미터로 담아 전달한다. */
boardService.modArticle(articleVO);
/* 수정하려는 이미지 파일의 이름이 null이나 공백이 아니라면, */
if (imageFileName != null && imageFileName.length() != 0) {
/* 기존의 파일 이름을 가져온다. */
String originalFileName = articleMap.get("originalFileName");
/* temp 폴더에 임시저장한 새로 업로드할 파일의 경로 */
File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
/* 실제 파일을 저장할 경로 */
File destDir = new File(ARTICLE_IMAGE_REPO + "\\" + articleNO);
/* 파일을 실제로 저장할 폴더의 경로에 articleNO 값을 이름으로 하는 새 폴더를 만든다. */
destDir.mkdirs();
/* 파일을 temp 폴더에서 실제로 파일을 저장할 경로로 이동한다. */
FileUtils.moveFileToDirectory(srcFile, destDir, true);
/* 기존 파일의 경로를 File 객체에 담은 후, 삭제한다. */
File oldFile = new File(ARTICLE_IMAGE_REPO + "\\" + articleNO + "\\" + originalFileName);
oldFile.delete();
}
/* 수정 후 alert창을 띄워주고, 상세보기 페이지로 다시 이동한다. */
PrintWriter pw = response.getWriter();
pw.print("<script>" + " alert('글을 수정했습니다.');" + " location.href='" + request.getContextPath()
+ "/board/viewArticle.do?articleNO=" + articleNO + "';" + "</script>");
return;
}
7. action.equals("/removeArticle.do")
해당 동작은 removeArticle.do에 접근했을 경우, GET 방식으로 받은 글 번호를 통해 DB에 접근해 게시글을 삭제하고, 게시글의 이미지를 저장한 폴더 또한 삭제하는 동작을 수행한다.
1) 선택한 게시글을 삭제하기 위해 해당 게시글의 번호를 받아오고, 웹 상에서 데이터는 String 형태로 이동되기 때문에 int 형으로 파싱한다.
2) 게시글 번호를 파라미터로 DAO에 접근하여 데이터를 삭제하기 위해 Service Method를 호출한다.
3) 삭제 쿼리메서드의 return 값으로 글 번호를 받아오고, 해당 글번호 이름으로 된 폴더에 접근한다.
4) 글번호 이름으로 된 폴더를 삭제한다.
5) 삭제를 완료한 후 alert창을 띄우고, listArticle.do로 이동한다.
else if (action.equals("/removeArticle.do")) {
/* 선택한 게시글을 삭제하기 위해 해당 게시글의 번호를 받아오고, 웹 상에서 데이터는 String 형태로 이동되기 때문에 int 형으로 파싱한다. */
int articleNO = Integer.parseInt(request.getParameter("articleNO"));
/* 게시글 번호를 파라미터로 DAO에 접근하여 데이터를 삭제하기 위해 Service Method를 호출한다. */
List<Integer> articleNOList = boardService.removeArticle(articleNO);
/* 삭제 쿼리메서드의 return 값으로 글 번호를 받아오고, 해당 글번호 이름으로 된 폴더에 접근한다. */
for (int _articleNO : articleNOList) {
File imgDir = new File(ARTICLE_IMAGE_REPO + "\\" + _articleNO);
/* 글번호 이름으로 된 폴더를 삭제한다. */
if (imgDir.exists()) {
FileUtils.deleteDirectory(imgDir);
}
}
/* 삭제를 완료한 후 alert창을 띄우고, listArticle.do로 이동한다. */
PrintWriter pw = response.getWriter();
pw.print("<script>" + " alert('글을 삭제했습니다.');" + " location.href='" + request.getContextPath()
+ "/board/listArticles.do';" + "</script>");
return;
}
8. action.equals("/replyForm.do")
해당 동작은 replyForm.do에 접근했을 경우, GET 방식으로 받은 부모 글 번호를 session에 저장한 후 답글을 작성하는 Form이 있는 페이지로 이동하는 동작을 수행한다.
1) 답글을 다려면 필요한 부모 게시글의 번호를 가져와, int형으로 파싱한다.
2) 답글을 작성하는 Form 양식이 있는 JSP로 이동하기 전, 부모 게시글 번호를 활용하기 위해 session에 저장한다.
else if (action.equals("/replyForm.do")) {
/* 답글을 다려면 필요한 부모 게시글의 번호를 가져와, int형으로 파싱한다. */
int parentNO = Integer.parseInt(request.getParameter("parentNO"));
/* 답글을 작성하는 Form 양식이 있는 JSP로 이동하기 전, 부모 게시글 번호를 활용하기 위해 session에 저장한다. */
session = request.getSession();
session.setAttribute("parentNO", parentNO);
nextPage = "/board06/replyForm.jsp";
}
9. action.equals("/addReply.do")
해당 동작은 addReply.do에 접근했을 경우, POST 방식으로 받은 답글 내용을 DB에 저장하는 동작을 수행한다.
1) 부모 글 번호를 가져오기 위해 session에 접근, 데이터를 추출 후 필요없어진 데이터를 session에서 삭제한다.
2) 답글 작성 시 파일 업로드를 하기위해, upload 메서드를 실행해 request, response 객체를 파라미터로 넘겨준다.
3) DAO의 쿼리메서드를 실행하기 위해 필요한 데이터를 Map에서 추출후, 데이터를 저장한다.
4) Service Method에 저장한 파라미터를 넘겨 DAO에 접근, 쿼리 실행 결과를 담는다.
5) 업로드할 이미지 파일이 null이 아니고 이미지 파일의 이름이 공백이 아니라면,
6) 업로드할 파일을 임시저장할 temp 폴더의 경로를 String 변수에 저장한다.
7) 파일을 실제 저장할 경로를 String 변수에 저장한다.
8) 실제 저장할 경로에 articleNO의 값을 이름으로 하는 폴더를 생성한다.
9) 임시저장 폴더에서 실제 저장할 폴더로 파일을 옮긴다.
10) 답글을 단 후, alert 창을 띄운 후 viewArticle.do로 이동한다.
else if (action.equals("/addReply.do")) {
/* 부모 글 번호를 가져오기 위해 session에 접근, 데이터를 추출 후 필요없어진 데이터를 session에서 삭제한다. */
session = request.getSession();
int parentNO = (Integer) session.getAttribute("parentNO");
session.removeAttribute("parentNO");
/* 답글 작성 시 파일 업로드를 하기위해, upload 메서드를 실행해 request, response 객체를 파라미터로 넘겨준다. */
Map<String, String> articleMap = upload(request, response);
/* DAO의 쿼리메서드를 실행하기 위해 필요한 데이터를 Map에서 추출후, 데이터를 저장한다. */
String title = articleMap.get("title");
String content = articleMap.get("content");
String imageFileName = articleMap.get("imageFileName");
articleVO.setParentNO(parentNO);
articleVO.setId("lee");
articleVO.setTitle(title);
articleVO.setContent(content);
articleVO.setImageFileName(imageFileName);
/* Service Method에 저장한 파라미터를 넘겨 DAO에 접근, 쿼리 실행 결과를 담는다. */
int articleNO = boardService.addReply(articleVO);
/* 업로드할 이미지 파일이 null이 아니고 이미지 파일의 이름이 공백이 아니라면, */
if (imageFileName != null && imageFileName.length() != 0) {
/* 업로드할 파일을 임시저장할 temp 폴더의 경로 */
File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
/* 파일을 실제 저장할 경로 */
File destDir = new File(ARTICLE_IMAGE_REPO + "\\" + articleNO);
/* 실제 저장할 경로에 articleNO의 값을 이름으로 하는 폴더를 생성한다. */
destDir.mkdirs();
/* 임시저장 폴더에서 실제 저장할 폴더로 파일을 옮긴다. */
FileUtils.moveFileToDirectory(srcFile, destDir, true);
}
/* 답글을 단 후, alert 창을 띄운 후 viewArticle.do로 이동한다. */
PrintWriter pw = response.getWriter();
pw.print("<script>" + " alert('답글을 추가했습니다.');" + " location.href='" + request.getContextPath()
+ "/board/viewArticle.do?articleNO=" + articleNO + "';" + "</script>");
return;
}
10. Map<String, String> upload()
파일을 업로드하기 위한 메서드로, 해당 메서드의 동작 순서는 다음과 같다.
1) 파일 데이터를 저장 후 return 하기 위한 Map 선언
2) 외부에서 읽어온 파일의 인코딩 유형을 고정하기 위한 String 변수
3) 업로드할 파일이 저장되어있는 경로를 File 객체에 저장
4) 임시파일이 저장 될 경로와 메모리에 보관 될 최대 크기 설정
5) 한번에 업로드 할 수 있는 전체 파일의 크기 및 각 파일별로 업로드할 수 있는 파일의 크기 설정
6) 업로드할 파일을 분할해 List에 담기 위해 parseRequest를 통해 파싱
7) 분할 된 파일을 하나씩 읽어오기 위한 반복문 실행
8) 가져온 파일의 일부의 사이즈가 0보다 크다면 즉, 파일이 존재한다면,
9) 파일이 위치하는 경로를 통채로 가져와 파일의 명을 추출하기위해 마지막 \\의 index를 찾는다.
10) 만약 idx가 -1이라면, 즉, 파일의 경로가 \\ 로 끝나지 않는다면 (OS에 따라 / 로 경로가 잡히는 경우가 존재한다.)
11) 찾은 idx는 \\ 혹은 / 이기 때문에, + 1 해서 파일의 이름을 가져온다.
12) 파일 이름을 map에 저장한다.
13) 파일 업로드를 위해 File 객체에 임시파일 경로 저장, write를 통해 실제 파일 업로드를 실행한다.
private Map<String, String> upload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/* 파일 데이터를 저장 후 return 하기 위한 Map */
Map<String, String> articleMap = new HashMap<String, String>();
/* 외부에서 읽어온 파일의 인코딩 유형을 고정하기 위해 */
String encoding = "utf-8";
/* 업로드할 파일이 저장되어있는 경로 */
File currentDirPath = new File(ARTICLE_IMAGE_REPO);
/* 임시파일이 저장 될 경로와 메모리에 보관 될 최대 크기 설정 */
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(currentDirPath); // 업로드할 파일의 경로
factory.setSizeThreshold(1024 * 1024); // 파일의 최대 크기 고정
/* 한번에 업로드 할 수 있는 전체 파일의 크기 및 각 파일별로 업로드할 수 있는 파일의 크기 설정 */
ServletFileUpload upload = new ServletFileUpload(factory);
try {
/* 업로드할 파일을 분할해 List에 담기 위한 parseRequest */
List<FileItem> items = upload.parseRequest(request);
/* 분할 된 파일을 하나씩 읽어오기 위한 반복문 */
for (int i = 0; i < items.size(); i++) {
FileItem fileItem = items.get(i);
if (fileItem.isFormField()) {
System.out.println(fileItem.getFieldName() + "=" + fileItem.getString(encoding));
articleMap.put(fileItem.getFieldName(), fileItem.getString(encoding));
} else {
System.out.println("파라미터명:" + fileItem.getFieldName());
System.out.println("파일크기:" + fileItem.getSize() + "bytes");
/* 가져온 파일의 일부의 사이즈가 0보다 크다면 즉, 파일이 존재한다면 */
if (fileItem.getSize() > 0) {
/* 파일이 위치하는 경로를 통채로 가져와 \\로 분할, 파일의 명을 추출하기위해 */
int idx = fileItem.getName().lastIndexOf("\\");
/* 만약 idx가 -1이라면, 즉, 파일의 경로가 \\ 로 끝나지 않는다면 (OS에 따라 / 로 경로가 잡히는 경우가 존재한다) */
if (idx == -1) {
idx = fileItem.getName().lastIndexOf("/");
}
/* 찾은 idx는 \\ 혹은 / 이기 때문에, + 1 해서 파일의 이름을 가져온다. */
String fileName = fileItem.getName().substring(idx + 1);
System.out.println("파일명:" + fileName);
/* 파일 이름을 map에 저장 */
articleMap.put(fileItem.getFieldName(), fileName); // 익스플로러에서 업로드 파일의 경로 제거 후 map에 파일명 저장);
/* 파일 업로드를 위해 File 객체에 임시파일 경로 저장, write를 통해 실제 파일 업로드 */
File uploadFile = new File(currentDirPath + "\\temp\\" + fileName);
fileItem.write(uploadFile);
}
}
}
}
11. 페이징
페이징 구현을 위해서는 현재 페이지의 번호와, section 번호가 필요하다.
1. Section
section은 내가 보여줄 총 페이지 개수의 집합을 의미한다.
2. Page Num
페이지 번호는 내가 보고있는 페이지가 현재 section의 몇 번째 페이지에 있는지를 의미한다.
3. 페이징 기능 구현 원리 (1 section = 10 page 의 경우)
1) 하단에 보이는 숫자는 현재 페이지 번호를 의미하고, 한 페이지마다 10개의 글을 표시하는 경우
2) 이런 페이지가 10개가 모여있으면, 한 개의 section이라고 한다.
3) 즉, 현재 한개의 section은 100개의 게시글을 가지고 있다. 예를들어, 첫 번째 section은 1페이지부터 10페이지까지 100개의 글을, 두 번째 section은 11페이지부터 20페이지까지 100개의 글을 가지고 있는 구조이다.
4) 따라서 사용자가 글 목록 페이지에서 2를 클릭하면 브라우저는 서버에 section 값으로 1을, pageNum 값으로 2를 전송한다.
5) 그리고 글 목록에는 첫 번째 section의 두 번째 page에 해당하는 글인 11~20번 게시글을 DB 테이블에서 조회한 후 표시한다.
12. 페이징을 위한 Query
페이징 기능을 구현하기 위해 Inner(Sub) Query와 오라클에서 제공하는 가상 컬럼인 ROWNUM을 이용한다.
ROWNUM은 SELECT로 조회 된 레코드 목록에 대해 오라클 자체에서 순서를 부여하여, 레코드 번호를 순서대로 할당한다.
1. section과 pageNum으로 글 목록을 조회하는 SQL문
select * from (
select ROWNUM as recNum, LVL, articleNO, parentNO, title, content, id, writedate from (
select LEVEL as LVL, articleNO, parentNO, title, content, id, writedate FROM t_board
START WITH parentNO = 0
CONNECT BY PRIOR articleNO=parentNO
ORDER SIBLINGS BY articleNO DESC
)
)
where recNum between (section - 1) * 100 + (pageNum - 1) * 10 + 1 and (section - 1) * 100 + pageNum * 10;
해당 쿼리는 sub query를 사용한 Query로, 해당 query를 해석할 땐 가장 안쪽에 있는 query부터 해석하면 된다.
1) LEVEL이 포함 된 계층형 SQL문을 통해 게시글을 계층별로 SELECT
2) SELECT 된 데이터 레코드(한 게시글)의 ROWNUM(recNum)이 표시 되도록 칼럼 추가 후 SELECT
3) section과 pageNum 값으로 WHERE 절의 recNum 범위를 정한 후, SELECT 된 글 중 해당하는 값이 있는 경우 최종적으로 SELECT
* WHERE 절 조건에 100을 곱한 이유
한 section 내에 100개의 게시글을 저장할 것이기 때문에 다음 section으로 넘어가기 위해서는 100을 곱해야한다.
2. WHERE 절 값 대입 예시
1) section = 1, pageNum = 1 의 경우
0 + 1 and 0 + 10 -> 1 and 10 : 1번부터 10번 게시글
2) section = 1, pageNum = 3 의 경우
0 + 21 and 0 + 30 -> 21 and 30 : 21번부터 30번 게시글
3) section = 1, pageNum = 7 의 경우
0 + 61 and 0 + 70 -> 61 and 70 : 61번부터 70번까지 게시글
4) section = 2, pageNum = 1 의 경우
100 + 1 and 100 + 10 -> 101 and 110 : 101번부터 110번 게시글
* MySQL의 경우 : LIMIT 게시글 IDX, 읽을 데이터 개수
1) SELECT * FROM t_board LIMIT 0, 10;
1페이지 (x - 1) * 10 = 0 : 1번 Page Num을 누를 시 x에 1을 대입
2) SELECT * FROM t_board LIMIT 10, 10;
2페이지 (x - 1) * 10 = 10 : 2번 Page Num을 누를 시 x에 2를 대입
3) SELECT * FROM t_board LIMIT 20, 10;
3페이지 (x - 1) * 10 = 20 : 3번 Page Num을 누를 시 x에 3을 대입
13. 25일차 후기
많은 게시판들에 들어가는 파일 업로드 기능과 페이징 기능이 추가 된 게시판으로, 해당 기능들은 고정 된 코드이기 때문에 전부 외울 필요는 없다.
하지만 기능을 사용하기 위해선 해당 코드가 어떠한 순서로 동작하는지 흐름을 이해할 수 있어야하기 때문에, 반드시 한번은 어떤 구조로 코드가 흘러가는지 읽어볼 필요가 있다고 생각한다.
'Bootcamp > Java web' 카테고리의 다른 글
[Java web] 14. Mini_Project_3 (0) | 2023.08.10 |
---|---|
[Java web] 12. MVC, 외래키, Oracle 함수, 계층형 쿼리 (1) | 2023.08.02 |
[Java web] 11. File Up/Download, JQuery, Ajax, Json (0) | 2023.08.01 |
[Java web] 10. Cookie, Session, Filter, Listener (0) | 2023.07.28 |
[Java web] 9. Servlet Context, Servlet Config (0) | 2023.07.27 |