- 1219 WebServer 9일차2023년 12월 19일 19시 57분 35초에 업로드 된 글입니다.작성자: 삶은고구마
1218은 테스트때문에 진도를 안나감..
목차
- AdminMemberListServlet
기능 통합 (검색, 페이지바기능 합침)
- BoardDetailServlet 작업
- BoardCreateServlet 추가
BoardDetailServlet
게시판 상세 페이지 작업을 진행하였다.
말 그대로, 게시판 목록에서 내가 원하는 제목을 클릭했을 때 해당 게시글의 세부사항을 볼 수 있는 페이지 작업을 진행.
구성요소는 위에서부터 제목/ 작성자 아이디/ 내용 / 조회수/ 작성일 이다.
글을 썼을 때는 분명
띄우기1
띄우기2
이런식으로 개행을 했는데 ..?
막상 값을 받아와 출력하면 한줄로 이어져있다.
console.log("boardCreate.js - 게시글 작성시 유효성 검사하는 js페이지"); document.boardCreateFrm.addEventListener('submit',()=> { const frm = e.target; const title = frm.title; const content = frm.content; //제목 유효성 검사 if(!/^.+$/.test(title.value.trim())) { alert('제목을 반드시 작성해주세요.'); e.preventDefault(); return; //제출막기.. } //내용 유효성 검사 //정규표현식의 .에는 \n이 포함되지 않는다. if(!/^(.|\n)+$/.test(content.value.trim())) { alert('내용을 반드시 작성해주세요.'); e.preventDefault(); return; //제출막기.. } });
유효성 검사 시 \n이 포함되지 않아 문자열에 그대로 반영되면서 개행이 진행되지 않은 것 같은데[?
utilClass를 따로 만들어 내용문자열에 포함된 \n을 <br>로 바꿔주는 메소드를 만들었다.
public static String convertLineFeedToBr(String str) { return str.replaceAll("\n","<br/>"); }
그리고 detailServelt에서 해당 메소드를 사용해서 개행 처리를 한다.
해당 내용이 포함된 BoardDetailServlet 단이다.
@WebServlet("/board/BoardDetail") public class BoardDetailServlet extends HttpServlet { private BoardService boardService = new BoardService(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.사용자 입력값 처리 long id = Long.parseLong(req.getParameter("id")); System.out.println(id); //2.업무로직 BoardVo board = boardService.findById(id); //개행문자(\n) -> <br> board.setContent(HelloMvcUtils.convertLineFeedToBr(board.getContent())); req.setAttribute("board",board); System.out.println(board); //3.forward req.getRequestDispatcher("/WEB-INF/views/board/boardDetail.jsp").forward(req,resp); } }
수정 후.
내가 원하는대로 보기 좋게 바뀌었다.
개발노트 :: JSTL 날짜 포맷 <fmt:formatDate> <fmt:parseDate>
JSTL 날짜 포맷 fmt:parseDate - 문자열 -> Date 타입으로 변경 fmt:formatDate - Date 타입 -> 문자열으로 변경 1. JSTL ${date1} 결과값 Tue Jan 01 00:00:00 KST 2019 2. JSTL ${date2} 결과값 2019-01-01 3. pattern 형식 y(The year) 년
jang2r.tistory.com
작성일 날짜 변환받아온 board.regDate를 yy/MM/dd HH:mm형식으로 바로 변경할 수 없다.
중간에 형변환을 한 번 더 해야 가능함.
즉, String->date형->원하는형 으로 진행한다.
fmt:parseDate - String형을 받아 원하는 포맷 Date형으로 변환
fmt:formatDate - Date형을 받아 원하는 포맷으로 변환
작성일 <fmt:parseDate value="${board.regDate}" pattern="yyyy-MM-dd'T'HH:mm" var="regDate"/> <fmt:formatDate value="${regDate}" pattern="yy/MM/dd HH:mm"/>
그 외에 상세 페이지에 작성시 첨부한 첨부파일 갯수와 파일명도 보여줄 수 있는데
이건 BoardCreateServlet에서 정리하겠다.
아래는 boardDetail.jsp의 일부인데,
c:foreach를 사용해 attachments의 갯수만큼 반복문을 실행하여 갯수만큼 첨부파일명을 보여주는 코드다.
<c:forEach items = "${board.attachments}" var="attach"> <a href="#" class="flex items-center text-blue-600 hover:underline"> <img src="../images/file.png" class="w-[16px] mr-1"> ${attach.originalFilename} </a>
BoardCreateServlet
boardCreate.jsp 에서 정말 중요한 코드가 있음.
제목,내용,첨부파일등을 전송해주는 form을 아래 형식으로 작성해줘야 한다
1.name은 각자 원하는 것으로 네이밍하면 되고
2.method는 post방식이다. (insert - dml)
3.enctype = "multipart/form-data"를 꼭 써줘야 한다.
input file형식을 전송할 때 필요한 속성값이며, 여러개를 첨부할 수 있다.
★주의점★
이 방법을 사용하면 req.getParameter로 값을 받아 올 수 없다. 확인하면 null값이다.
정 받고 싶다면 해당 멀티파트리퀘스트객체. getParameter로 받아 올 수있다 하는데 나중에 테스트 해보겠다.
<form name="boardCreateFrm" method="post" enctype="multipart/form-data">
유익한 링크들.
https://m.blog.naver.com/bb_/222134798237
[JAVA] 멀티파트 폼(multipart/form-data)으로 넘긴 파라미터 request.getParameter 로 받기
[JAVA] 멀티파트 폼(multipart/form-data)으로 넘긴 파라미터 request.getParameter 로 받기 멀티파트...
blog.naver.com
[JSP] - 파일 enctype="multipart/form-data" 사용 시 request.getParameter null 해결방법
○ 파일 enctype="multipart/form-data" 사용 시 request.getParameter null 해결방법 파일이나 이미지 처리 폼에서 enctype="multipart/form-data" 을 사용하는데,대부분 파일이나 이미지는 게시물 작성같은 다른 입력
riucc.tistory.com
@WebServlet("/board/boardCreate") public class BoardCreateServlet extends HttpServlet { private BoardService boardService = new BoardService(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/WEB-INF/views/board/boardCreate.jsp").forward(req,resp); } /** * 파일 업로드 처리.. * 1.commons-io,commons-fileupload 의존성 추가(pom.xml) * 2.form[method=post][enctype=multipart/form-data] * 3.DiskFileItemFacroty / ServletFileUpload 요청처리 * - 저장경로 * - 파일 최대 크기 * * * * @param req * @param resp * @throws ServletException * @throws IOException */ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.사용자 입력값 처리 및 파일 업로드 File repository = new File("C:\\Workspaces\\web_server_workspace\\helloo-mvc\\src\\main\\webapp\\upload\\board"); int sizeThreshold = 10 * 1024 * 1024; //10MB DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setRepository(repository); factory.setSizeThreshold(sizeThreshold); BoardVo board = new BoardVo(); //실제 요청을 핸들링할 객체.. ServletFileUpload s = new ServletFileUpload(factory); try { //전송된 값을 하나의 FileItem으로 처리 List<FileItem> fileItemList = s.parseRequest(req); for(FileItem item: fileItemList) { String name = item.getFieldName();//input의 name if(item.isFormField()){ //일반 텍스트 : board 객체에 설정 String value = item.getString("utf-8"); System.out.println(name+"/"+value); //Board객체에 설정자 로직 구현.. board.setValue(name,value); } else { //파일 : 서버 컴에 저장 파일정보를 attachment 객체로 만들어서 db에 저장 if(item.getSize() > 0) { System.out.println(name); String originalFileName = item.getName(); System.out.println("원본 파일명: " + originalFileName); System.out.println("파일크기: " + item.getSize() + "byte"); int dotIndex = originalFileName.lastIndexOf("."); String ext = dotIndex >- 1? originalFileName.substring(dotIndex) : ""; System.out.println("extextextextextext: " + ext ); //.png UUID uuid = UUID.randomUUID();//랜덤 uuid 발급 b9c2bc5f-9a0c-4e7f-9d87-7d6feb70e244 String renamedFileName = uuid+ext; //저장된 파일명(덮어쓰기방지,인코딩이슈방지) 위에 발급된 uuid에 원본의 확장자명을 붙여서 새 파일명만든다. System.out.println("새 파일명 : "+renamedFileName); //서버 컴퓨터 파일 저장 File upFile = new File(repository,renamedFileName); item.write(upFile); //Attachment 객체 생성 Attachment attach = new Attachment(); //현재 할 수 있는건 원본명과, 새이름 set. attach.setOriginalFilename(originalFileName); attach.setRenamedFilename(renamedFileName); board.addAttachment(attach); } } } } catch (Exception e) { throw new RuntimeException(e); } System.out.println("board+attach : " +board);//board객체,attach객체 모두 확인되어야함. //2.업무로직 int result = boardService.insertBoard(board); req.getSession().setAttribute("msg","게시글을 성공적으로 등록하였습니다"); //3.게시판 목록페이지로 redirect resp.sendRedirect(req.getContextPath()+"/board/boardList"); } }
- 기존 board 객체 클래스에는 첨부파일에 관련된 필드가 없었다.
- board entity는 테이블과 1대1매칭되는 관계라, 새로운 "첨부파일 관련 필드" 를 추가하기 보다는,
- 첨부파일에 관련된 Attachment 테이블과 1대1로 매칭되는 Attachment 클래스를 만든후
(pk,board id,원본파일명,새파일명,등록일)
- board를 상속하는 확장클래스인 boardVo를 만든다.
예전에 jdbc 수업 때 주소록 클래스를 이런식으로 만들었다.
1대 N의 관계 일 때 이런 구조로 클래스를 짜면 될 것 같다.
한 회원은 여러 주소를 가질 수 있다 , 한 게시글은 첨부파일을 안가질 수도, 1개,2개 등 여러 개 가질 수있다.
/** * vo 클래스 value object * - immutable한 value객체(수정하지않는,불변) * - entity 클래스를 확장한 객체 * - 기존 entity는 수정하지말고 상속하여 확장/사용 */ public class BoardVo extends Board { //보드를 상속하고, 원래 보드에 없는 첨부파일 갯수 필드를 하나 추가함 private int attachCount; private List<Attachment> attachments = new ArrayList<>(); . . . 생략
기존 board 객체를 사용한 메소드는 boardVo로 변경해주고,
BoardService에서 insertBoard(게시글 추가기능)은 아래와 같이 작성해준다.
첨부파일은 n개 일 수도있으니 반복문을 사용하여 insert해준다.
그리고 dml의 특성상, 전부 성공 commit / 전부 실패 rollback 처리를 해줘야 한다.
보드객체/첨부파일객체를 따로 insert하여 모두 성공했을 때만 session.commit 처리를 해야한다.
=>만약 보드객체값만 성공하고 첨부파일은 실패한다면 일관성에 위배 되지않을까?
그럴바엔 아예 실패로 처리해서 게시글 자체가 insert되지 않도록 해야한다.
:★하나의 세션,하나의 메소드 안에서 관리하기
public int insertBoard(BoardVo board) { int result = 0; SqlSession session = getSqlSession(); try { //board + attach 둘 다 insert하고 commit or rollback 처리 해야함 //board 테이블에 등록 result = boardDao.insertBoard(session,board); System.out.println("board get id())" + board.getId()); //attach 테이블에 등록 List<Attachment> attachments = board.getAttachments(); if(!attachments.isEmpty()) { for(Attachment attach : attachments) { attach.setBoardId(board.getId()); result = boardDao.insertAttachment(session,attach); } } session.commit(); } catch(Exception e) { //에러가 발생할 경우 롤백하기 session.rollback(); throw e; } finally { session.close(); } return result; }
BoardDao
//게시글추가 public int insertBoard(SqlSession session, Board board) { return session.insert("board.insertBoard",board); } //첨부파일추가 public int insertAttachment(SqlSession session, Attachment attach) { return session.insert("board.insertAttachment",attach); }
board-mapper.xml
<insert id="insertBoard"> insert into board (id, title, member_id, content) values( seq_board_id.nextval, #{title}, #{memberId}, #{content} ) <selectKey order="AFTER" resultType="_int" keyProperty="id"> select seq_board_id.currval from dual </selectKey> </insert> <insert id="insertAttachment"> insert into attachment(id,board_id,original_filename,renamed_filename) values( seq_attachment_id.nextval, #{boardId}, #{originalFilename}, #{renamedFilename} ) </insert>
board insert 후 해당 board의 pk값인 id를 fk로 써야 하는 attachment를 insert 해야 하는데
(어느 게시글에 무슨 첨부파일이 있는지 알아야 하니까..^^)
selectKey 태그를 사용하면 board insert 이후(after) 해당 id 값을 알 수 있다.
그리고 이 값을 사용해 attachment insert 시 사용 할 수 있다!
boardService에서 attach.setBoardId(board.getId());를 보면
board의 아이디를 받아 attach에 setBoardId 한다. 그리고 다음 줄에서 바로 dao insertAttachment 진행.
System.out.println("board get id())" + board.getId()); //attach 테이블에 등록 List<Attachment> attachments = board.getAttachments(); if(!attachments.isEmpty()) { for(Attachment attach : attachments) { attach.setBoardId(board.getId()); result = boardDao.insertAttachment(session,attach); } }
selectKey 에 관한 도움글
https://yookeun.github.io/java/2014/07/11/mybatis-selectkey/
mybatis에서 selectKey 사용법
DB작업을 하다보면 먼저 사전에 어떤 키값을 가져와서 증가시켜서 입력하거나 혹은 입력후에 증가된 키값을 가져올 필요가 있다. 이럴때 mybatis에서 제공하는 selectKey를 이용하면 별도의 쿼리로
yookeun.github.io
구조 '공부 > web-server' 카테고리의 다른 글
0111 ubuntu 설치 - 설정 (0) 2024.01.11 1220 WebServer 10일차 (0) 2023.12.20 1215 WebServer 8일차 (0) 2023.12.15 1214 WebServer 7일차 (1) 2023.12.14 1212 WEB SERVER 5일차 (0) 2023.12.12 다음글이 없습니다.이전글이 없습니다.댓글