자바 Spring

23-02-08) spring 9강 - 게시판 구현 2 (수정, 삭제, 조회수-쿠키 , 이전글/다음글 기능) , MySQL 쿼리문 limit 기능, 페이징 구현(creteria 클래스)

JadeStone 2023. 2. 8. 20:03

# 컨트롤러에 요청(ex- /notice_view , notice_modify)은 다르게 오지만 처리결과는 같은 때 한번에 묶어서 처리하는 법

   (그냥 이런것도 있구나 하고 넘어가기)

 

컨트롤러에서

아래와 같이 두 페이지에대한 처리가 똑같다면 한번에 묶어서 처리가 가능.

	//상세화면
	@RequestMapping("/notice_view")
	public String notice_view(@RequestParam("tno") int tno,
							  Model model) {
		//클릭한 글 번호에 대한 내용을 조회
		TripVO vo = tripService.getContent(tno);
		model.addAttribute("vo", vo);
        
		return "trip/notice_view";
	}
		
	}
	
	//수정화면
	@RequestMapping("/notice_modify")
	public String notice_modify(@RequestParam("tno") int tno,
								Model model) { 
		
		//클릭한 글 번호에 대한 내용을 조회
		TripVO vo = tripService.getContent(tno);
		model.addAttribute("vo", vo);
		
		return "trip/notice_modify";
	}

메서드 반환부분이 void라는거는 들어온 경로 그대로 보내준다는 의미

	//수정, 상세 화면이 완전 동일하다면
	@RequestMapping({"notice_view", "notice_modify"})
	public void notice_view(@RequestParam("tno") int tno,
							Model model) {
 	TripVO vo = tripService.getContent(tno);
		model.addAttribute("vo", vo);
	}

 

# 글수정 기능

- 글을 수정하고자 하면 해당 글을 데이터베이스에서 식별하기 위해서 pk (데이터베이스에서 글의 번호)값이 필요.

 화면에서는 이 pk값을 넘겨주어야 함.

-수정페이지에서 수정 버튼을 눌르고  글내용이 변경된 수정페이지로 다시 넘어갈 때에도 

해당 글에대한  pk값( tno )이 꼭 넘어와야 함.

하지만 수정페이지에서  화면에는 이 tno 값이 보이면 안되기 때문에 

<input type="hidden" name="tno" value="${vo.tno }"/> 

이렇게 히든 태그를 이용해서 tno 값을 넘겨주어야 함.

( form 태그를 통해 post방식으로 값을 넘겼음)

 

*컨트롤러

	//글수정
	@RequestMapping(value="/modifyForm", method = RequestMethod.POST)
	public String modifyFrom(TripVO vo,
							 RedirectAttributes ra) {
		
		//업데이트작업 - 화면에서는 tno가 필요하기 때문에 hidden태그를 이용해서 넘겨주세요.
		int result = tripService.noticeModify(vo);
		String msg = result == 1 ? "문의사항이 수정되었습니다" : "수정에 실패했습니다";
		ra.addFlashAttribute("msg", msg);
		
		return "redirect:/trip/notice_view?tno=" + vo.getTno();
	}

* 서비스 , mapper 부분

- 메서드 : 

public int noticeModify(TripVO vo); //수정

- mapper 구현부 ( 쿼리문 )

	<update id="noticeModify" parameterType="TripVO">
		update trip
		set tripdate = #{tripdate},
		title = #{title},
		content = #{content}
		where tno = #{tno}
	</update>

 

 

#글삭제 기능

*삭제는 반드시 post 방식으로 데이터(tno - 글의 pk값)를 전달해야됨.

*notice_view (상세화면 페이지)

 - form 방식으로  tno를 전달하기 위해 form 태그로 내용들을 감싸줌.

 

   ★ 중요 포인트 : 자바스크립트 부분에서  form 태그를 직접 다루기 위해서 name 값을 주었음. 

     form 태그는 document객체의 자식태그로서 document객체를 통해서 접근가능 ! ★

 

 - tno (글의 pk값)가 화면에 직접 보이면 안되기 때문에  hidden 태그로  값을 담아줌.

 - 글 삭제 버튼(a태그) 를 클릭하면 자바스크립트에서 noticeDelete() 를 실행하게 함.  

 - 하단부에 script 태그에 자바스크립트 noticeDelete 메서드 구현

   1. a 태그의 고유 특성인 누르면 화면을 바로 넘어가는 것을 강제로 실행하지 않게 처리해줌 

      event.preventDefault();

   2. document.폼태그의name값.submit() ;  을 함으로써  데이터를 가지고 post방식으로 화면을 넘어가게 함. 

       이때 컨트롤러에 보내는 요청은 form 태그 action 속성의 값인  deleteForm

		<!--  
		삭제시는 post로 동작하는데
		hidden이용해서 삭제에 필요한 키값을 전달해줍니다.
		js를 이용해서 form을 전송
		-->
		<form action="deleteForm" method="post" name="actionForm">
		
		<input type="hidden" name="tno" value="${vo.tno }">	
        
        내용들
        
        	<p class="btn_line txt_right">
				<a href="javascript:;" class="btn_bbs" onclick="noticeDelete()">글삭제</a>
			</p>
        
         내용들 
         
         </form>
         <!-- //bodytext_area -->
         
     <script>
		function noticeDelete() {
			//a링크 고유이벤트중지
			event.preventDefault();
			//폼형식으로 삭제 - document.form이름
			if(confirm("정말 지울거에요?ㅠㅠ")) {
				document.actionForm.submit();
			}
		}	
	</script>

* 구현할 메서드 

public int noticeDelete(int tno); //삭제

삭제 성공시 반환값이 1이 나오는데 이를 반환하도록 함. (반환타입 int)

- mapper 에서 구현

	<delete id="noticeDelete" parameterType="int">
		delete from trip where tno =#{tno}
	</delete>

*컨트롤러

	//글삭제
	@RequestMapping(value="deleteForm", method = RequestMethod.POST)
	public String deleteForm(@RequestParam("tno") int tno,
							 RedirectAttributes ra) {
		
		/*
		 * service, mapper 에는 noticeDelete 메서드로 삭제를 진행 
		 * 삭제 이후에는 list화면으로 이동해주면 됩니다.
		 */
		int result = tripService.noticeDelete(tno);
		String msg = result == 1 ? "삭제 되었습니다" : "삭제에 실패했습니다";
		ra.addFlashAttribute("msg", msg);
		
		return "redirect:/trip/notice_list";
	}

 

# 조회수 기능  ( 쿠키  관련 내용도 있음)

 

* 메서드

public void upHit(int tno); //조회수

 

* mapper.xml 에서 메서드 구현

	<update id="upHit">
		update trip
		set hit = hit + 1
		where tno = #{tno}
	</update>

*컨트롤러

- 상세페이지를 들어올 때 조회수가  오르게됨.

 그러나 조회수가 중복해서 계속 올라가지 않는 방법도 생각해봐야함. 

 

 -쿠키를 통한 조회수 제어-

매개변수로 HttpServletResponse 객체와 HttpServletRequest 객체가 필요.

컨트롤러에서 만든 쿠키를 담아서 페이지로 보내기 위해 Response 이용

화면에서 넘어온 쿠키를 갖고 작업을 하기 위해 Request를 받는것.

 

HttpServlet 애들 참고 링크:

https://duckgugong.tistory.com/23   

 

	//상세화면
	@RequestMapping("/notice_view")
	public String notice_view(@RequestParam("tno") int tno,
							  Model model,
							  HttpServletResponse response,
							  HttpServletRequest request) {
		//클릭한 글 번호에 대한 내용을 조회
		TripVO vo = tripService.getContent(tno);
		model.addAttribute("vo", vo);
		
		//조회수 - Cookie or 세션 이용해서 조회수 중복 방지
		tripService.upHit(tno);
		
		Cookie cookie = new Cookie("key", "1");
		cookie.setMaxAge(30);
		response.addCookie(cookie);
        
        //이전글 다음글
		ArrayList<TripVO> list = tripService.getPrevNext(tno);
		model.addAttribute("list", list);
		
		return "trip/notice_view";
	}

 

 

 

# 이전글 , 다음글  기능 추가

 

*메서드

public ArrayList<TripVO> getPrevNext(int tno); //이전글, 다음글

이전글과 다음글의 데이터를 찾을 기준을 잡기위해서 현재 글의 pk값인 tno 를 매개변수로 받음.

반환타입은 TripVO 타입을 담는 ArrayList  -> 글 2개를 담아서 반환. 

 

* 잠깐 보고가기

-MySQL 에서만 제공하는  limit  기능  ★★★

 조건에 맞는 데이터 행들 중에서 지정한 숫자만큼의 데이터만 보이도록 해줌.

select tno from trip where tno < 3 order by tno desc limit 1;
select tno from trip where tno > 3 limit 1;

select * from trip
where tno in ((select tno from trip where tno < 3 order by tno desc limit 1),
			  (select tno from trip where tno > 3 limit 1));

where tno > 3 limit 1; 의미해석:

tno가 3보다 큰 데이터들 중에서 첫번째만 가져온다.

 

*  TripMapper.xml  - >  메서드 구현 (sql 문 부분)

-  쿼리문 안에  있는 < 부등호는  xml , html 에서 태그로 인식되어

   오류를 일으킴.

   쿼리문을  <![CDATA[ 쿼리문 ]]> 이렇게 안에 들어가게 해줌으로써

   [] 안에 있는 내용을 순수한 문자열로 인식하도록 해줘야함.

	<!-- 이전글, 다음글 -->
	<!-- xml or html에서 부등호는 태그로 인식이 되는데, CDATA는 순수한 문자열 형태로 인식을 시킴 -->
	<select id="getPrevNext" resultType="TripVO">
		<![CDATA[
		select * from trip
		where tno in ((select tno from trip where tno < #{tno} order by tno desc limit1),
					  (select tno from trip where tno > #{tno} limit 1))
		]]>
	</select>

* 컨트롤러 (이전글 다음글 부분 보기)

- 반환받은 list를 모델에 담아서 상세화면 페이지로 다시 넘어감.

	//상세화면
	@RequestMapping("/notice_view")
	public String notice_view(@RequestParam("tno") int tno,
							  Model model,
							  HttpServletResponse response,
							  HttpServletRequest request) {
		
        //클릭한 글 번호에 대한 내용을 조회
		TripVO vo = tripService.getContent(tno);
		model.addAttribute("vo", vo);
        
        //-----------------------------------
		//이전글 다음글
		ArrayList<TripVO> list = tripService.getPrevNext(tno);
		model.addAttribute("list", list);
		//-----------------------------
        
		//조회수 - Cookie or 세션 이용해서 조회수 중복 방지
		tripService.upHit(tno);
		
//		Cookie cookie = new Cookie("key", "1");
//		cookie.setMaxAge(30);
//		response.addCookie(cookie);
		
		return "trip/notice_view";
	}

*상세화면 페이지 (notice_view)

- <c:if test=  실행 조건 써주는 부분>

- list 의 길이를 알아야 조건문의 처리를 다르게 해줄 수 있음. 

  jstl 에서 list의 길이를 얻어오는 법

 1. 상단부에 jstl 선언

      <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

 2. ${fn:length(list)}  ->   list의 길이 반환

- 상세화면이 맨 처음글 , 맨 마지막글 이라면 리스트에 담을 수 있는 글은 1개 등등

 상황에 맞게 조건문을 잘 걸어줘야함.  

 글의 pk (tno)값 을 기준으로 이전글과 다음글을 잘 구분해서 조건문을 작성해주도록 하자. (생각 잘 해야하는 부분!!) 

			<ul class="near_list mt20">
				
				<!-- 
				1. 글이 2개인 경우 - 이전글 < 현재글 인 경우 이전글
				2. 글이 1개인 경우 - 리스트 길이가 1이고, 글 < 현재글 인 경우는 다음글이 없음
				3. 글이 0개인 경우 - list 가 없으니 애초에 반복문이 돌지 않을것.
				 -->
				 
				 
				 	<c:forEach var="data" items="${list }">
					 	<c:if test="${fn:length(list) == 1 and data.tno < vo.tno }">
					 		<li><h4 class="prev">다음글</h4>은 없습니다</li>
					 	</c:if>
					 	
				 		<c:if test="${data.tno > vo.tno }">
				 			<li><h4 class="prev">다음글</h4><a href="notice_view?tno=${data.tno }">추석 연휴 티켓/투어 배송 및 직접 수령 안내${data.tno }</a></li>
				 		</c:if>	
				 		<c:if test="${data.tno < vo.tno }">
				 			<li><h4 class="next">이전글</h4><a href="notice_view?tno=${data.tno }">이번 여름 휴가 제주 갈까? 미션 투어 (여행경비 50만원 지원)${data.tno }</a></li>
				 		</c:if>	
				 		
				 		<c:if test="${fn:length(list) == 1 and data.tno > vo.tno }">
				 			<li><h4 class="prev">이전글</h4>은 없습니다</li>
				 		</c:if>
				 	</c:forEach>
				<!-- 
				<li><h4 class="prev">다음글</h4><a href="javascript:;">추석 연휴 티켓/투어 배송 및 직접 수령 안내</a></li>		
				<li><h4 class="next">이전글</h4><a href="javascript:;">이번 여름 휴가 제주 갈까? 미션 투어 (여행경비 50만원 지원)</a></li>
				 -->
			</ul>

 

 

 

<페이징 구현>

아래 빨간부분과 같은 기능

 

# 페이지별 보여줄 글을 가져오는 쿼리문 (MySQL)

*MySQL에서는 limit 라는 특별한 기능을 제공해줌 

- limit  지정해준 숫자만큼의 데이터를 가져와 줌.

  아래에서 limit 사용에대한 해석

  limit 0, 10   ->  1번부터 시작, 10개의 데이터

  limit 10, 10 ->  11번부터 시작, 10개의 데이터

select *
from trip order by tno desc limit 0, 10; -- 1번에서 10개의 데이터 

select *
from trip order by tno desc limit 10, 10; -- 11번에서 10개의 데이터 

select *
from trip order by tno desc limit 10, 50; -- 11번에서 50개의 데이터

 

# criteria 클래스

* 페이징을 처리하는 기준을 다룸.

-  mysql의 limit 함수 쿼리문에 

    limit 시작번호 , 가져올데이터 개수   이 두가지 부분의 값을 처리해줌.