ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 161220 Spring ex.02
    Spring 2020. 12. 16. 10:13

    필요한 파일 추가

    src/main/java

    com.kh.myspringstuoy02.model.domain

    • Board.java
    • BoardReply.java

     

    com.kh.myspringstuoy02.model.service

    • BoardService.java (interface)
    • BoardServiceImpl.jav

     

     

    com.kh.myspringstuoy02.model.dao

    • BoardDao.java

     

     

    com.kh.myspringstuoy02.controller

    • BoardController.java

    src/main/resources

    mappers

    • board-mapper.xml
    • boardReply-mapper.xml

    mybatis-config.xml

     


    src/main/webapp/WEB-INF/views

    jsp파일들


    작업 순서

     

    pom.xml에서 버전을 설정하고, 필요한 기능들을 추가한다.

     

    java-version: 1.8

    org.springframework-version: 4.2.4.

     

    <ojdbc lib>

    <!-- ojdbc 라이브러리 -->
    		<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc6 -->
    		<dependency>
    			<groupId>com.oracle.database.jdbc</groupId>
    			<artifactId>ojdbc6</artifactId>
    			<version>11.2.0.4</version>
    		</dependency>

     

    <MyBatis>

    <!-- MyBatis -->
    		<dependency>
    			<groupId>org.mybatis</groupId>
    			<artifactId>mybatis</artifactId>
    			<version>3.4.5</version>
    		</dependency>
    		<dependency>
    			<groupId>org.mybatis</groupId>
    			<artifactId>mybatis-spring</artifactId>
    			<version>1.3.1</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-jdbc</artifactId>
    			<version>${org.springframework-version}</version>
    		</dependency>

     

    <DBCP>

    <!-- DBCP -->
    		<dependency>
    			<groupId>commons-dbcp</groupId>
    			<artifactId>commons-dbcp</artifactId>
    			<version>1.4</version>
    		</dependency>

     

    <fileUpload>

    <!-- FileUpload -->
    		<dependency>
    			<groupId>commons-fileupload</groupId>
    			<artifactId>commons-fileupload</artifactId>
    			<version>1.3.3</version>
    		</dependency>
    		<dependency>
    			<groupId>commons-io</groupId>
    			<artifactId>commons-io</artifactId>
    			<version>2.6</version>
    		</dependency>

     

    <org.aspectj>

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    		<dependency>
    			<groupId>org.aspectj</groupId>
    			<artifactId>aspectjweaver</artifactId>
    			<version>${org.aspectj-version}</version>
    			<scope>runtime</scope>
    		</dependency>

     

    <cglib>

    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    		<dependency>
    			<groupId>cglib</groupId>
    			<artifactId>cglib</artifactId>
    			<version>2.2</version>
    		</dependency>

     

    com.kh.myspringstuoy.model.domain

    Board.java

     

    @Component 설정 >> 왜?

    	private static final long serialVersionUID = 20001L;
    	private String board_num;
    	private String board_writer;
    	private String board_title;
    	private String board_content;
    	private String board_file;
    	private String board_pwd;
    	private int read_count;
    	private Date regDate;

    필요한 메소드들을 추가한다.

    (인자값들을 다르게 하여 Board라는 이름의 method를 여러 개 생성한다.)

    	public Board() {
    		
    	}
    	
    	public Board(String board_num, String board_pwd) {
    		super();
    		this.board_num = board_num;
    		this.board_pwd = board_pwd;
    	}
    	
    	public Board(String board_writer, String board_title, String board_content, String board_file,
    			String board_pwd) {
    		super();
    		this.board_writer = board_writer;
    		this.board_title = board_title;
    		this.board_content = board_content;
    		this.board_file = board_file;
    		this.board_pwd = board_pwd;
    	}
    	
    	public Board(String board_num, String board_writer, String board_title, String board_content, String board_file,
    			String board_pwd, int read_count, Date regDate) {
    		super();
    		this.board_num = board_num;
    		this.board_writer = board_writer;
    		this.board_title = board_title;
    		this.board_content = board_content;
    		this.board_file = board_file;
    		this.board_pwd = board_pwd;
    		this.read_count = read_count;
    		this.regDate = regDate;
    	}

    toString 메소드 >> 나중에 debug할 때 쓰기 위함이다.

    + getter & setter를 추가한다.


    BoardReply.java

     

    @Component 설정

    	private String comment_id;
    	private String board_num;
    	private String comment_name;
    	private String comment_pwd;
    	private String comments;
    	private Date regdate;
    	public BoardReply() {
    		
    	}
    	
    	public BoardReply(String comment_id, String comment_pwd, String comments) {
    		super();
    		this.comment_id = comment_id;
    		this.comment_pwd = comment_pwd;
    		this.comments = comments;
    	}
    
    	public BoardReply(String comment_id, String comment_pwd) {
    		this.comment_id = comment_id;
    		this.comment_pwd = comment_pwd;
    	}
    
    	public BoardReply(String comment_id, String board_num, String comment_name, String comment_pwd, String comments,
    			Date regdate) {
    		this.comment_id = comment_id;
    		this.board_num = board_num;
    		this.comment_name = comment_name;
    		this.comment_pwd = comment_pwd;
    		this.comments = comments;
    		this.regdate = regdate;
    	}
    
    	public BoardReply(String board_num, String comment_name, String comment_pwd, String comments) {
    		this.board_num = board_num;
    		this.comment_name = comment_name;
    		this.comment_pwd = comment_pwd;
    		this.comments = comments;
    	}

    toString + getter & setter 설정

     


    이제 board-mapper.xml로 가서 실제 sql문을 입력해야 한다.

     

    알맞게 설정되어 생성된 board-mapper.xml로 들어가서 먼저 mapper의 namespace를 설정한다.

    namespace="Board"로 설정이 되면, 후에 Dao에서 Board. 으로 sql문을 호출할 수 있다.

     

    resultMap에  type="Board" id="resultBoard" props을 주고,

    그 안에 id와 result를 선언한다.

    id는 PK에게 주고 나머지는 result로 선언 >> 아마

    id, result들에게 property, column을 각각 주는데, 헷갈리지 않게 일치하는 것이 좋다.

    property는 vo의 필드명, column은 말 그대로 db의 칼럼명으로 작성한다.

    	<resultMap type="Board" id="resultBoard">
      	<!-- property에는 vo의 필드명, column에는 db table 의 칼럼명이 온다 -->
      	<id property="board_num" column="board_num"/>
      	<result property="board_writer" column="board_writer"/>
      	<result property="board_title" column="board_title"/>
      	<result property="board_content" column="board_content"/>
      	<result property="board_file" column="board_file"/>
      	<result property="board_pwd" column="board_pwd"/>
      	<result property="read_count" column="read_count"/>
      	<result property="regDate" column="regDate"/>
     	</resultMap>

    그 후, 필요한 sql 문들을 작성한다.

     

    결과값이 숫자로 나오는 전체 게시글 조회를 위한 sql문들은 int로 resultType을 갖는다.

    <!-- 전체 게시글 개수 조회 -->
      <select id="listCount" resultType="int">
      	select count(*) from board
      </select>

     

    반면, 게시글을 조회하는 것은 전체 Board를 끌어와야하니, resultType 역시 Board로 설정한다.

    <!-- 단일 게시글 조회 -->
    	<select id="searchOne" parameterType="string" resultType="Board"> <!-- 가지고 들어오는 게 있어서 parameterType -->
    		select * from board where board_num = #{board_num} <!-- DAO에서 던져주는 값이래, dao 아직 설정안되어있는 듯? -->
    	</select>

     

    게시글을 CRUD하는 것들은 조회하는 것이 아니기에 resultType은 필요로하지 않지만 가지고 들어가는 parameterType이 필요하다.

    삭제나 게시글 조회 수 증가는 가지고 가는 값이 board_num 뿐이기에 parameterType이 string이지만,

    수정은 Board 대부분을 가지고 가야하기에 parameterType도 Board이다.

    <!-- 게시글 조회 수 증가 -->
    	<update id="addReadCount" parameterType="string" flushCache="true" statementType="PREPARED"> <!-- 가지고오는 board_num의 type -->
    		update board set read_count=(read_count+1)
    		where board_num=#{board_num}
    	</update>
    <!-- 게시글 수정 -->
    	<update id="updateBoard" parameterType="Board" flushCache="true" statementType="PREPARED"> <!-- 여러가지 param들을 하나씩 꺼내서 보려 flushCache -->
    		update board set board_title=#{board_title}, board_content=#{board_content}, board_file=#{board_file}
    		where board_num=#{board_num} and board_pwd=#{board_pwd}
    	</update>
    <!-- 게시글 삭제 -->
    	<delete id="deleteBoard" parameterType="string" flushCache="true" statementType="PREPARED"> <!-- update,delete는 resultType이 무조건 int라 따로 명시하지 않는다. -->
    		delete from board where board_num=#{board_num}
    	</delete>

    flushCache는 뭐지?

     

    게시글 목록 조회도 다른 sql문들이랑 포멧이 다르지만 단순 활용이다.

    Board의 list를 뽑아내야하기에 arrayList를 가지고 

    resultMap은 뭐라고 해야할지 모르겠다.

    <!-- 게시글 목록 조회 -->
      <select id="selectList" resultType="arraylist" resultMap="resultBoard">
    		select * from board
    	</select>  

     


    mybatis-config.xml

     

    mybats-config.xml은 domain과 mapper.xml을 연결해주는 연결고리 역할을 한다.

    먼저 jdbcTypeForNull을 세팅하고,

    	<settings>
    		<setting name="jdbcTypeForNull" value="NULL" />
    	</settings>

    typeAlias를 통해 alias를 선언하고 mapper를 선언한다.

    	<typeAliases>
    		<typeAlias type="com.kh.myspringstuoy03.model.domain.Board"
    			alias="Board" />
    		<typeAlias
    			type="com.kh.myspringstuoy03.model.domain.BoardReply"
    			alias="BoardReply" />
    	</typeAliases>
    	<mappers>
    		<mapper resource="mappers/board-mapper.xml" />
    		<mapper resource="mappers/boardReply-mapper.xml" />
    	</mappers>	

    이제 Dao에서 sqlSession을 통해 mapper에서 선언했던 sql문들을 불러와야 한다.

     

    @Repository("bDao")로 설정

     

    class 안에 @Autowired를 통해 SqlSession을 불러온다.

     

    board-mapper.xml에서 정의되었던 sql문을 가져와 하나씩 method로 선언한다.

    먼저, 전체 게시글 개수를 확인하는 sql문

    <!-- 전체 게시글 개수 조회 -->
      <select id="listCount" resultType="int">
      	select count(*) from board
      </select>

    	public int listCount() {
    		return sqls.selectOne("Board.listCount");
    	}

    param을 필요로 하지 않고, resultType이 int이니 int로 만들어 준다.


    <!-- 단일 게시글 조회 -->
    	<select id="searchOne" parameterType="string" resultType="Board"> <!-- 가지고 들어오는 게 있어서 parameterType -->
    		select * from board where board_num = #{board_num} <!-- DAO에서 던져주는 값이래, dao 아직 설정안되어있는 듯? -->
    	</select>

    	public Board selectOne(String board_num){
    		return sqlSession.selectOne("Board.searchOne", board_num); //mapper에 id부분이랑 같아야
    	}

    param으로 string값인 board_num을 필요로 하기 때문에, param에 String board_num을 주었고, returnType 역시 resultType에 맞게 Board로 일치시켰다. method에 있는 board_num을 가지고 가야 하기에, selectOne 안에도 statement와 더불어 board_num도 함께 보낸다.

     

    같은 맥락으로 조회수 증가도 마찬가지이다.

    <!-- 게시글 조회 수 증가 -->
    	<update id="addReadCount" parameterType="string" flushCache="true" statementType="PREPARED"> <!-- 가지고오는 board_num의 type -->
    		update board set read_count=(read_count+1)
    		where board_num=#{board_num}
    	</update>

    	public int addReadCount(String board_num){
    		return sqlSession.update("Board.addReadCount", board_num); //mapper에 id부분이랑 같아야
    	}

    다만 sql문에서 resultType이 정해지지 않았을 때에는 보통 int로 선언하는 듯 하다.

     

    <!-- 게시글 삭제 -->
    	<delete id="deleteBoard" parameterType="string" flushCache="true" statementType="PREPARED"> <!-- update,delete는 resultType이 무조건 int라 따로 명시하지 않는다. -->
    		delete from board where board_num=#{board_num}
    	</delete>

    	public int deleteBoard(String board_num){
    		return sqlSession.delete("Board.deleteBoard", board_num); //mapper에 id부분이랑 같아야
    	}

    수정은 정의된 param값이 Board로, 이전 sql문들과 다르다. 하지만 그 부분만 인지하고 있다면, 그냥 활용일 뿐이다.

    <!-- 게시글 수정 -->
    	<update id="updateBoard" parameterType="Board" flushCache="true" statementType="PREPARED"> <!-- 여러가지 param들을 하나씩 꺼내서 보려 flushCache -->
    		update board set board_title=#{board_title}, board_content=#{board_content}, board_file=#{board_file}
    		where board_num=#{board_num} and board_pwd=#{board_pwd}
    	</update>

    	public int updateBoard(Board b){
    		return sqlSession.update("Board.updateBoard", b); //mapper에 id부분이랑 같아야
    	}

    resultType과 resultMap이 제일 특이한 (resultMap은 얘만 가지고 있긴 하다.) 친구이다.

    그래도 활용이라고 생각하면 쉽다.

    <!-- 게시글 목록 조회 -->
      <select id="selectList" resultType="arraylist" resultMap="resultBoard">
    		select * from board
    	</select>  

    	public List<Board> selectList(){ 
    		return sqlSession.selectList("Board.selectList");
    	}

    인자값도 없어서 생각해야하는 건 returnType인데, 그 역시 arrayList라고 떡하니 나와있기에, List<Board>로 선언해준다.


    Service에선 단순히 Dao에서 표현한 것들을 선언만 해준다.

    interface니까

     

    	 int listCount();
    	
    	 int insertBoard(Board b);
    	
    	 List<Board> selectList();
    	
    	 Board selectOne(String board_num);
    	
    //	 int addReadCount(String board_num); 이건 어차피 selectOne할 때마다 실행시키기 위한거라
    	
    	 Board updateBoard(Board b);
    	
    	 int deleteBoard(String board_num);

    중요한건 impl이다.

     

    먼저 @Service("bService")를 넣어준다. >> 아마 bService 호출할 때, 여기로 오기 위한 것 같은데,,, 확실치 않아.

     

    그 후, class 안에 @Autowired로 BoardDao를 private으로 가져온다.

    	@Autowired
    	private BoardDao bDao;
    

     

    그 후엔, bDao와 Service에서 이름을 맞춰놨기에, 해당 method마다 bDao.method로 return값을 자연스럽게 맞추면 된다.

    하지만 1가지 updateBoard는 return값이 Board이기에 그대로하면 오류가 발생한다.

    bDao.updateBoard는 int를 return하기에 일치하지 않기 때문이다. 여기선 selectOne이 Board를  return하는 것을 이용한다.

     

    먼저 int result를 선언과 동시에 bDao.updateBoard(b)로 정의하여 결과값을 넣어놓는다.

    하지만, 이 때 변화가 있을 수도 있고, 없을 수도 있기에 경우의 수를 나눈다.

    	public Board updateBoard(Board b) { //Board b 는 업데이트 하려고 실어온 Board //update성공하면 변경된 값들 출력되게끔 의도
    		//밑에 오류가 발생하면 위에 것도 실행되지 않는 것이 transaction의 특징
    		// TODO Auto-generated method stub
    		int result = bDao.updateBoard(b);
    		System.out.println("result 1 : "+ result);
    		if(result > 0) {
    			System.out.println("result 2 : "+ result);
    			b = bDao.selectOne(b.getBoard_num());
    		} else {
    //			throw new MemberNotFoundException();
    			System.out.println("result 3 : "+ result);
    			b = null;
    		}
    		return b;
    	}

    이제 중요한 Controller이다.

     

    class에 @Controller를 붙여주고,

    (@RequestMapping(value="/board")도 가능한데 생략해도 된다.)

     

    @Autowired로 BoardService를 가져온다

    @Controller
    @RequestMapping(value = "/board") // 이렇게 또 걸어도 된다.
    public class BoardController {
    	@Autowired
    	private BoardService bService;

    근데 여기서 import하는 건 interface더라,,, 왜 그런지 모르겠어.

     

    @Requestmapping(value="/writeForm.do", method = RequestMethod.GET)

    >> 의미는 writeForm.do라는 주소로 get방식을 통해 접근하면 밑의 코드들을 지나 return값을 보여주는 것.

    // 게시글 작성 페이지
    		@RequestMapping(value = "/writeForm.do", method = RequestMethod.GET)
    		public String boardInsertForm(ModelAndView mv) {
    			return "board/writeForm"; // view페이지에서 작성 후 form action = "bInsert.do" 로 들어오도록 설계.
    		}

    숙지가 필요한 개념: ModelAndView


     

    fileUpload기능이 추가가되면, 필요한 것들이 많아진다.

    //작성된 글을 insert
    		@RequestMapping(value="/bInsert.do", method = RequestMethod.POST)
    		public ModelAndView boardInsert(Board b, @RequestParam(name="upfile") MultipartFile report, HttpServletRequest request, ModelAndView mv) {
    			
    			//첨부파일을 저장하는 코드
    			if(report != null && !report.equals("")) {
    				saveFile(report, request);
    			}
    			b.setBoard_file(report.getOriginalFilename());
    			
    			bService.insertBoard(b);
    			mv.setViewName("redirect:bList.do");
    			return mv;
    		}

    여기서 saveFile은 이후에 따로 정의가 필요한 메소드이다.

    //게시글 수정
    		@RequestMapping(value="/bUpdate.do", method=RequestMethod.POST)
    		public ModelAndView boardUpdate(Board b, @RequestParam(name="upfile") MultipartFile report, HttpServletRequest request, ModelAndView mv) {
    			if(report != null && !report.equals("")) {
    				removeFile(b.getBoard_file(), request);
    				saveFile(report, request);
    				b.setBoard_file(report.getOriginalFilename());
    			}
    			
    			if(bService.updateBoard(b)!=null) {
    				mv.addObject("board_num", bService.updateBoard(b).getBoard_num());
    				mv.setViewName("redirect:bDetail.do");
    			} else {
    				//이전화면으로 이동?
    			}
    			
    			return mv;
    		}

    여기선 수정이라 기존에 있던 파일을 삭제하는 작업도 필요하기에 removeFile 메소드를 넣는다.

     


     

    이것들은 fileUpload와 관련없는 것들.

    단순히 mv를 통해 값들을 싣고 뿌려준다.

    //게시글 리스트 출력
    		@RequestMapping(value="/bList.do")
    		public ModelAndView boardListService(ModelAndView mv) {
    			mv.addObject("list", bService.selectList());
    			mv.setViewName("board/blist");
    			return mv;
    		}
    		
    		//게시글 Detail
    		@RequestMapping(value="/bDetail.do")
    		public ModelAndView boardDetail(@RequestParam(name="board_num") String board_num, 
    				@RequestParam(name="page", defaultValue="1") int page, ModelAndView mv) {
    			mv.addObject("board", bService.selectOne(board_num));
    			mv.setViewName("board/boardDetail");
    			return mv;
    		}
    		
    		@RequestMapping(value="/bRenew.do")
    		public ModelAndView boardDetail(@RequestParam(name="board_num") String board_num, ModelAndView mv) {
    			mv.addObject("board", bService.selectOne(board_num));
    			mv.setViewName("board/boardRenew");
    			return mv;
    		}

     saveFile과 removeFile 정의

    		private void saveFile(MultipartFile report, HttpServletRequest request) {
    			String root = request.getSession().getServletContext().getRealPath("resources");
    			String savePath = root + "\\uploadFiles";
    			File folder = new File(savePath);
    			if(!folder.exists()) {
    				folder.mkdir();
    			}
    			String filePath = null;
    			try {
    				// 파일 저장
    							System.out.println(report.getOriginalFilename() + "을 저장합니다.");
    							System.out.println("저장 경로 : " + savePath);
    
    							filePath = folder + "\\" + report.getOriginalFilename();
    							report.transferTo(new File(filePath)); // 파일을 저장한다
    							System.out.println("파일 명 : " + report.getOriginalFilename());
    							System.out.println("파일 경로 : " + filePath);
    							System.out.println("파일 전송이 완료되었습니다.");
    						} catch (Exception e) {
    							System.out.println("파일 전송 에러 : " + e.getMessage());
    						}
    		}
    		
    		private void removeFile(String board_file, HttpServletRequest request) {
    			String root = request.getSession().getServletContext().getRealPath("resources");
    			String savePath = root + "\\uploadFiles";
    			String filePath = savePath + "\\" + board_file;
    			try { //삭제 시에 오류 날 가능성 다분해서 try - catch문
    				System.out.println(board_file + "을 삭제합니다.");
    				System.out.println("기존 저장 경로: "+filePath);
    			File delFile = new File(filePath); //찾아 나온 file을 delFile에 넣는다.
    			delFile.delete();
    			} catch(Exception e) {
    				System.out.println("파일 삭제 에러: " + e.getMessage());
    			}
    			
    			
    		}

    view 파일들

    writeForm.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>게시글 작성</title>
    </head>
    <body>
    <form action="bInsert.do" method="post" enctype="multipart/form-data">
    		<table align="center">
    			<tr>
    				<td>제목</td>
    				<td><input type="text" name="board_title"></td>
    			</tr>
    			<tr>
    				<td>작성자</td>
    				<td><input type="text" name="board_writer"></td>
    			</tr>
    			<tr>
    				<td>첨부파일</td>
    				<!-- 원래 vo에 있는 이름 board_file 을 사용하게 되면 String 형태여야 함. file 형태로 가져가야 하므로 name을 vo의 field명과 다르게 지정함. -->
    				<td><input type="file" name="upfile"></td>
    			</tr>
    			<tr>
    				<td>글비밀번호</td>
    				<td><input type="text" name="board_pwd"></td>
    			</tr>
    			<tr>
    				<td>내용</td>
    				<td><input type="text" name="board_content"></td>
    			</tr>
    			<tr>
    				<td colspan="2" align="center">
    				<input type="submit" value="등록하기"> &nbsp;&nbsp;
    				<a href="bList.do">목록으로</a>
    				</td>
    			</tr>
    		</table>
    	</form>
    
    </body>
    </html>

    form을 넣어 submit 시에 bInsert.do로 이동하게끔 설정하고, 목록으로 갈 때에는 bList.do로 이동하게 설정했다.


    boardDetail.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>게시글 상세 조회</title>
    </head>
    <body>
    	
    		<!-- writeForm 복붙 -->
    	<table align="center">
    			<tr>
    				<th colspan="2">${board.board_num }번 글 상세보기</th> <!-- addObject에서 'board'라고 선언 -->
    			</tr>
    			<tr>
    				<td>제목</td>
    				<td>${board.board_title }</td>
    			</tr>
    			<tr>
    				<td>작성자</td>
    				<td>${board.board_writer }</td>
    			</tr>
    			<tr>
    				<td>첨부파일</td>
    				<!-- 원래 vo에 있는 이름 board_file 을 사용하게 되면 String 형태여야 함. file 형태로 가져가야 하므로 name을 vo의 field명과 다르게 지정함. -->
    				<td><c:if test="${empty board.board_file }"> 
    				첨부된 파일이 없습니다.
    				</c:if>
    				<c:if test="${!empty board.board_file }">
    				<a href="${pageContext.request.contextPath }/resources/uploadFiles/${board.board_file }" download>>${board.board_file }</a> <!-- click을 하면 이미지가 보여지게끔 -->
    				<!-- contextPath까지하면 myspringstuoy01 -->
    				</c:if> <!-- choose else if 를 안쓰니까 이렇게 구성 -->
    				</td>
    			</tr>
    			<tr>
    				<td>내용</td>
    				<td>${board.board_content }</td>
    			</tr>
    			<tr>
    				<td colspan="2" align="center">
    				<c:url var="bdel" value="bDelete.do">
    					<c:param name="board_num" value="${board.board_num }"/>
    				</c:url>
    				<c:url var="brenew" value="bRenew.do">
    					<c:param name="board_num" value="${board.board_num }"/>
    				</c:url>
    				<!-- var는 이름 -->
    				<a href="${brenew }">수정하기</a> 
    				&nbsp;&nbsp;
    				<a href="${bdel }">삭제하기</a>  <!-- c:url에 걸려있는 value로 들어가게 된다. -->
    				&nbsp;&nbsp;
    				<a href="bList.do">목록으로</a>
    				</td>
    			</tr>
    		</table>
    
    
    </body>
    </html>

    mv에 addObject로 넣어놨던 것들로 jstl 이용!

    특이한 점은 c:url을 사용하여 param을 들고 이동할 수 있게 설정해놨다는 점이다.


    boardRenew.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <script src="${pageContext.request.contextPath }/resources/js/jquery-3.5.1.js">
    //webapp까지의 경로
    
    </script>
    <title>Insert title here</title>
    </head>
    <body>
    <form action="bUpdate.do" method="post" enctype="multipart/form-data" name="renewForm">
    		<input type="hidden" name="board_num" value="${board.board_num }">
    		<input type="hidden" name="board_file" value="${board.board_file }">
    		<table align="center">
    			<tr>
    				<td>제목</td>
    				<td><input type="text" name="board_title" value="${board.board_title }"></td>
    			</tr>
    			<tr>
    				<td>작성자</td>
    				<td><input type="text" name="board_writer" value="${board.board_writer }"></td>
    			</tr>
    			<tr>
    			<td>첨부파일</td>
    				<!-- 원래 vo에 있는 이름 board_file 을 사용하게 되면 String 형태여야 함. file 형태로 가져가야 하므로 name을 vo의 field명과 다르게 지정함. -->
    				<td>
    				<c:if test="${empty board.board_file }"> 
    				첨부된 파일이 없습니다.
    				</c:if>
    				<c:if test="${!empty board.board_file }">
    				<a href="${pageContext.request.contextPath }/resources/uploadFiles/${board.board_file }">${board.board_file }</a> <!-- click을 하면 이미지가 보여지게끔 -->
    				<!-- contextPath까지하면 myspringstuoy01 -->
    				</c:if> <!-- choose else if 를 안쓰니까 이렇게 구성 -->
    				</td>
    			</tr>
    			<tr>
    				<td>변경할 첨부파일</td>
    				<!-- 원래 vo에 있는 이름 board_file 을 사용하게 되면 String 형태여야 함. file 형태로 가져가야 하므로 name을 vo의 field명과 다르게 지정함. -->
    				<td><input type="file" name="upfile" multiple></td>
    			</tr>
    			<tr>
    				<td>글비밀번호</td>
    				<td><input type="password" name="board_pwd"></td>
    			</tr>
    			<tr>
    				<td>내용</td>
    				<td><input type="text" name="board_content" value="${board.board_content }"></td>
    			</tr>
    			<tr>
    				<td colspan="2" align="center">
    				<input type="submit" value="수정하기"> &nbsp;&nbsp;
    				<a href="bList.do">목록으로</a>
    				</td>
    			</tr>
    		</table>
    	</form>
    </body>
    <script type="text/javascript">
    
    	$(function(){
    		//비밀번호 똑같은지 확인
    		$('form[name=renewForm]').on('submit', function(event){
    			if($('input[name=board_pwd]').val() != "${board.board_pwd}"){
    				alert("비밀번호가 일치하지 않습니다.");
    				event.preventDefault(); //지금까지 쌓여있는 이벤트 모조리 삭제, submit 이벤트도 삭제된다.
    			} else {
    				return true; //남아있는 다음 동작들 실행
    			}
    		});
    		
    	});
    
    </script>
    
    
    
    </html>

    수정하는 페이지기에 input box를 많이 넣어놨다.

    다만 value를 이미 이전에 집어넣어 놨던 것들로 설정하여 볼 수 있게 만들었다.

    js를 이용하여 비밀번호가 일치하지 않으면 화면이 넘어가지 않게끔 설정했다,


    blist.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    	pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>게시판 목록</title>
    <link href="${pageContext.request.contextPath}/resources/css/style.css"
    	rel="stylesheet" type="text/css" />
    <script type="text/javascript"
    	src="${pageContext.request.contextPath}/resources/js/jquery- 3.2.1.min.js"></script>
    <script type="text/javascript">
    	$(function() {
    		$('form[name=listForm]').on(
    				'submit',
    				function(e) {
    					if ($('input[name=keyword]').val() == null
    							|| $('input[name=keyword]').val() == "") {
    						alert("검색어를 입력해 주세요");
    						e.preventDefault();
    					} else {
    						return true;
    					}
    				});
    	});
    	function showInsertForm() {
    		location.href = "writeForm.do";
    	}
    </script>
    </head>
    <body bgcolor="#FFEFD5">
    	<h1>Spring MVC 서버</h1>
    	<hr>
    	<p align="center">
    	<h3>게 시 판</h3>
    	<form method="get" name="listForm" action="blist.do">
    		<input type="hidden" name="page" value="${currentPage}"> <input
    			type="text" name="keyword"> <input type="submit" value="검색">
    	</form>
    	<table>
    		<tr>
    			<td align="right" colspan="5"><input type="button" value="전체목록"
    				onclick="window.location='blist.do'"> <input type="button"
    				value="글쓰기" onclick="window.location='writeForm.do'"></td>
    		</tr>
    		<tr bgcolor="#FFD1B7">
    			<td align="center" width="60">번호</td>
    			<td align="center" width="380">제목</td>
    			<td align="center" width="100">작성자</td>
    			<td align="center" width="100">작성일</td>
    			<td align="center" width="60">조회수</td>
    		</tr>
    		<!-- 글이 없을 경우 -->
    		<c:if test="${listCount eq 0}">
    			<tr>
    				<td colspan="6" align="center"><br> <br> 게시판에 저장된 글이
    					없습니다.<br> <br></td>
    			</tr>
    		</c:if>
    		<c:if test="${listCount ne 0}">
    			<c:forEach var="vo" items="${list}" varStatus="status">
    				<tr>
    					<td align="center">${status.count}</td>
    					<td align="left"><a
    						href="bDetail.do?board_num=${vo.board_num}">
    							&nbsp;${vo.board_title} </a></td>
    					<td align="center">${vo.board_writer}</td>
    					<td align="center">${vo.regDate}</td>
    					<td align="center">${vo.read_count}</td>
    				</tr>
    			</c:forEach>
    		</c:if>
    		<!-- 앞 페이지 번호 처리 -->
    		<tr align="center" height="20">
    			<td colspan="5"><c:if test="${currentPage <= 1}">
    			[이전]&nbsp; </c:if> <c:if test="${currentPage > 1}">
    					<c:url var="blistST" value="blist.do">
    						<c:param name="page" value="${currentPage-1}" />
    					</c:url>
    					<a href="${blistST}">[이전]</a>
    				</c:if> <!-- 끝 페이지 번호 처리 --> <c:set var="endPage" value="${maxPage}" /> <c:forEach
    					var="p" begin="${startPage+1}" end="${endPage}">
    					<c:if test="${p eq currentPage}">
    						<font color="red" size="4"><b>[${p}]</b></font>
    					</c:if>
    					<c:if test="${p ne currentPage}">
    						<c:url var="blistchk" value="blist.do">
    							<c:param name="page" value="${p}" />
    						</c:url>
    						<a href="${blistchk}">${p}</a>
    					</c:if>
    				</c:forEach> 
    				<c:if test="${currentPage >= maxPage}"> [다음] </c:if> 
    				<c:if test="${currentPage < maxPage}">
    					<c:url var="blistEND" value="blist.do">
    						<c:param name="page" value="${currentPage+1}" />
    					</c:url>
    					<a href="${blistEND}">[다음]</a>
    				</c:if></td>
    		</tr>
    	</table>
    </body>
    </html>

    root-context.xml

     

    원래 여기에 뭐 좀 넣는데, servlet-context.xml에 넣어도 상관없어서 거기다가 다 때려박았다.

     


    servlet-context.xml >> 용도가 뭘까??

     

    <!-- DBCP 연결 -->
    	<beans:bean id="dataSource"
    		class="org.apache.commons.dbcp.BasicDataSource">
    		<beans:property name="driverClassName"
    			value="oracle.jdbc.driver.OracleDriver" />
    		<beans:property name="url"
    			value="jdbc:oracle:thin:@localhost:1521:xe" />
    		<beans:property name="username" value="scott" />
    		<beans:property name="password" value="tiger" />
    	</beans:bean>
    	<beans:bean id="sqlSessionFactory"
    		class="org.mybatis.spring.SqlSessionFactoryBean">
    		<beans:property name="dataSource" ref="dataSource" />
    		<beans:property name="configLocation"
    			value="classpath:mybatis-config.xml" />
    	</beans:bean>
    
    	<beans:bean id="sqlSession"
    		class="org.mybatis.spring.SqlSessionTemplate">
    		<beans:constructor-arg ref="sqlSessionFactory" />
    	</beans:bean>
    

    tx, aop 체크하고

    	<!-- TransactionManager를 정의하여 Commit, Rollback을 제어한다 -->
    	<beans:bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<beans:property name="dataSource" ref="dataSource" />
    	</beans:bean>
    	<!-- Transaction 의 대상 설정 -->
    	<tx:advice id="txAdvice"
    		transaction-manager="transactionManager">
    		<tx:attributes>
    			<!-- Transaction의 대상이 되는 메소드를 정의한다 -->
    			<tx:method name="select*" read-only="true"
    				rollback-for="Exception" />
    			<tx:method name="insert*" rollback-for="Exception" />
    			<tx:method name="update*" rollback-for="Exception" />
    			<tx:method name="delete*" rollback-for="Exception" />
    		</tx:attributes>
    	</tx:advice>
    	<!-- <tx:annotation-driven/>  -->
    	<!-- AOP를 통해 Transaction의 pointcut 설정 -->
    	<aop:config proxy-target-class="true">
    		<aop:pointcut id="daoMethod"
    			expression="execution(* com.stuoy.myspringstuoy01..*Impl.*(..)))" />
    		<aop:advisor id="transactionAdvisor"
    			pointcut-ref="daoMethod" advice-ref="txAdvice" />
    	</aop:config>
    

    파일 업로드를 위한 빈

    <!-- Multipart 파일 업로드 관련 bean 등록 -->
    	<beans:bean id="multipartResolver"
    		class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
    	

    web.xml

     

    한글 깨짐 방지를 위한 UTF-8 추가

     

    <!-- UFT-8 인코딩 -->
    	<filter>
    		<filter-name>encodingFilter</filter-name>
    		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    		<init-param>
    			<param-name>encoding</param-name>
    			<param-value>UTF-8</param-value>
    		</init-param>
    	</filter>
    	<filter-mapping>
    		<filter-name>encodingFilter</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>

     

    댓글

Designed by Tistory.