카테고리 없음

1.18 (tiles-글수정,삭제,인터셉트,ck에디터,좋아요초기설정)

쿠룽지 2024. 1. 18. 23:44
728x90
반응형

 

 

 

<복습>

 

 

게시판 정렬 기능 = xml파일에서 동적 sql을 사용해서 동작시켰음

동적sql = 상황에 따라 where, order by 등 여러 동적 처리 시 사용하는 sql문

 

 


 

 

 

<게시판 글 수정하기>

 

 

xml파일에서 sql문 작성하기

kr.spring.board.dao

BoardMapper.xml

<!-- 글 수정 -->
<update id="updateBoard" parameterType="boardVO">
    UPDATE spboard SET
        <if test="filename != null">
        filename=#{filename},
        </if>
        title=#{title},
        content=#{content},
        ip=#{ip},
        modify_date=SYSDATE
    WHERE board_num=#{board_num}
</update>

alias 이용하여 boardVO

 

 

 

 

 

Service 파일 코드 작성

kr.spring.board.service

BoardServiceImpl

@Override
public void updateBoard(BoardVO board){
	boardMapper.updateBoard(board);
}

 

 

 

 

 

 

Controller 에서 호출하기

kr.spring.board.controller

BoardController

	//수정 폼에서 전송된 데이터 처리
	@PostMapping("/board/update")
	public String submitUpdate(@Valid BoardVO boardVO, BindingResult result,
		HttpServletRequest request, Model model) throws IllegalStateException, IOException {
		log.debug("<<글 수정>> : " + boardVO);
		
		//유효성 체크 결과 오류가 있으면 폼 재호출
		if(result.hasErrors()) {
			//title 또는 content가 입력되지 않아 유효성 체크에 걸리면 파일 정보를 잃어버리기 때문에
			//폼을 재호출하기 전에 다시 셋팅을 해주어야 함
			BoardVO vo = boardService.selectBoard(boardVO.getBoard_num());
			boardVO.setFilename(vo.getFilename());
			
			return "boardModify";
		}
		
		//db에 저장된 파일 정보 구하기
		BoardVO db_board = boardService.selectBoard(boardVO.getBoard_num());
		
		//파일명 셋팅(두번째인자(파일명)-upload를 넣어줘야함)
		boardVO.setFilename(FileUtil.createFile(request, boardVO.getUpload()));
		//IP 셋팅
		boardVO.setIp(request.getRemoteAddr());
		//글 수정
		boardService.updateBoard(boardVO);
		
		//전송된 파일이 있을 경우 이전 파일 삭제
		if(boardVO.getUpload() != null && !boardVO.getUpload().isEmpty()) {
			//수정 전 파일 삭제 처리
			FileUtil.removeFile(request, db_board.getFilename());
		}
		
		//view에 표시할 메시지 지정
		model.addAttribute("message", "글 수정 완료!!");
		model.addAttribute("url", 
			request.getContextPath()+"/board/detail?board_num="+boardVO.getBoard_num());
		
		return "common/resultAlert";
	}

1.

add throws declaration 눌러줘야함

 

2.

이전 파일 삭제하는 구문 추가 (업데이트 하기 전 파일정보 읽어오고 파일 삭제)

//db에 저장된 파일 정보 구하기
//전송된 파일이 있을 경우 이전 파일 삭제

두개 동시에 작성

 

--> 파일이 있는 글 수정 시 파일을 삭제하면 webapp/upload 경로에 있던 파일도 함께 삭제되는걸 볼 수 있음

 

 


 

 

 

 

 

<게시판 글 삭제하기>

 

 

sql문이 짧기 때문에 interface 파일에서 어노테이션으로 명시

kr.spring.board.dao

BoardMapper.java

@Delete("DELETE FROM spboard WHERE board_num=#{board_num}")
public void deleteBoard(int board_num);

 

 

 

 

 

 

Service 파일 코드 작성

kr.spring.board.service

BoardServiceImpl

@Override
public void deleteBoard(int board_num) {
    boardMapper.deleteBoard(board_num);
}

 

 

 

 

 

 

Controller 에서 호출하기

kr.spring.board.controller

BoardController

	/*===========================
	 * 게시판 글 삭제
	 * ==========================*/
	@RequestMapping("/board/delete")
	public String submitDelete(@RequestParam int board_num, HttpServletRequest request) {
		log.debug("<<게시판 글 삭제 board_num>> : " + board_num);
		
		//쓰레기 정보 없애기 위해 삭제 전에 구하고 없애야함
		//DB에 저장된 파일 정보 구하기 (request전달->경로 알아내고 삭제)
		BoardVO db_board = boardService.selectBoard(board_num);
		
		//글 삭제
		boardService.deleteBoard(board_num);
		
		if(db_board.getFilename() != null) {
			//파일 삭제
			FileUtil.removeFile(request, db_board.getFilename());
		}
		
		return "redirect:/board/list";
	}

1.

selectBoard를 사용하여 db에 저장된 파일 정도 구함 ->

글 삭제 ->

만약 db_board에 파일이름이 있다면 db_board의 파일도 삭제

 

2.

board_num은 꼭 전달되어야 하기 때문에 @RequestParam 사용

 

 

 


 

 

 

 

<Intercept 추가하기>

 

 

 

보안적으로도 좋고, 간편함 (요청했을 때 요청을 낚아챈다는 뜻)
로그인안한 상태에서는 회원제 서비스는 접근 x, 회원제 서비스 아닌거는 접근 o 할 수 있게 해줌
aop보다 인터셉트가 좀더 편하다 함 

 

또한, intercept는 여러 개를 만들어서 활용할 수가 있음
현재 만들려는건 loginCheck intercept
로그인 체크하고 로그인이 안됐으면 로그인폼 호출하는 형식으로 생성

 

 

 

src/main/java
kr.spring.interceptor (package)
LoginCheckInterceptor (class)

package kr.spring.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor{

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
		log.debug("<<LoginCheckInterceptor 진입>>");
		
		HttpSession session = request.getSession();
		//로그인 여부 검사
		if(session.getAttribute("user")==null) {
			//로그인이 되지 않은 상태
			response.sendRedirect(request.getContextPath()+"/member/login");
			return false; //요청한 페이지가 호출되지 않음
		}

		return true; //요청한 페이지 호출
	}
}

1.
HandlerIntercept implements

2.
@Slf4j
lombok 이용해서 log처리

3.
request를 이용, 세션에 로그인 정보가 등록되어 있으면 return true 없으면 login 폼 호출, false
return---------------------
true => 우리가 요청한 페이지 호출
false => 요청한 페이지 X



근데 이렇게 파일 생성만 하면 하면 낚아챌 수가 없음 그래서 어떤 메뉴가 비회원이고 어떤 메뉴가 회원 기능인지 다 설정을 해야 할 수 있다 함

 

 

 

 

 

 

 

설정파일에서 인터셉트 등록하기

kr.spring.config

AppConfig

public class AppConfig implements WebMvcConfigurer {
	private LoginCheckInterceptor loginCheck;
	
	@Bean
	public LoginCheckInterceptor interceptor2() {
		loginCheck = new LoginCheckInterceptor();
		return loginCheck;
	}
	
	//intercept 등록
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		//LoginCheckInterceptor 설정
		registry.addInterceptor(loginCheck)
				.addPathPatterns("/member/myPage")
				.addPathPatterns("/board/write")
				.addPathPatterns("/board/update")
				.addPathPatterns("/board/delete");
	}

1.

LoginCheckInterceptor import해서 변수 생성

2.
자동로그인 시에도 interceptor를 사용할거라 2라고 지정

3.
interceptor 설정에 등록이 되어야 하기 때문에 InterceptorRegistry를 이용하여 등록
addInterceptors에서 명시한 정보들은 그것들만 체크한다는 뜻 (mypage, write, update, delete)

 

=============================

실행 후 url을

http://localhost:8000/board/delete?board_num=43
http://localhost:8000/member/myPage

이런식으로 입력하면 에러 대신 로그인 창이 뜨면 됨

 

 

 


 

 

 

 

<CK Editor 넣기>

 

 

 

https://ckeditor.com/

 

WYSIWYG HTML Editor with Collaborative Rich Text Editing

Rock-solid, Free WYSIWYG Editor with Collaborative Editing, 200+ features, Full Documentation and Support. Trusted by 20k+ companies.

ckeditor.com

html (상세페이지 링크를 많이 해야하기 때문에) 이 필요하다 --> easy work editor 사용해서 쓰면 됨 (예-네이버에디터, 썸머노트, ckeditor 등)

 

 

 

 

layout을 얹는 기본 틀에 css 링크 tiles로 넣기

layout_basic.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><tiles:getAsString name="title"/></title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/layout.css">
<tiles:insertAttribute name="css" ignore="true"/>
</head>
<body>
<div id="main">
	<div id="main_header">
		<tiles:insertAttribute name="header"/>
	</div>
	<div id="main_body">
		<tiles:insertAttribute name="body"/>
	</div>
	<div id="main_footer">
		<tiles:insertAttribute name="footer"/>
	</div>
</div>
</body>
</html>

<tiles:insertAttribute name="css" ignore="true"/>

ck editor 추가

 

하지만 editor는 member에 필요없고 board에만 필요하기 때문에 동적으로 추가해야 함 (tiles를 추가하는 방식)
또한, member엔 필요가 없기 때문에 이 css가 없더라도 에러가 나지 않도록 ignore="true"를 넣음

(모든 jsp에 적용하지 않을 때에는 ignore="true" 기재)

 

 

 

 

 

설정파일 수정

board.xml

<definition name="boardWrite" extends="main">
    <put-attribute name="title" value="글쓰기"/>
    <put-attribute name="css" value="/WEB-INF/views/board/boardCSS.jsp"/>
    <put-attribute name="body" value="/WEB-INF/views/board/boardWrite.jsp"/>
</definition>


<definition name="boardModify" extends="main">
    <put-attribute name="title" value="글 수정"/>
    <put-attribute name="css" value="/WEB-INF/views/board/boardCSS.jsp"/>
    <put-attribute name="body" value="/WEB-INF/views/board/boardModify.jsp"/>
</definition>

설정파일에 있는 definition 중 boardWrite와 Modify만 css추가

 

 

 

 

 

 

 

새 css 를 추가한 view 생성

views

board

boardCSS.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<style>
.ck-editor__editable_inline{
	min-height:250px;
}
</style>

=============================
<페이지 소스보기 비교>

main
<link rel="stylesheet" href="/css/layout.css">

 


로그인 후 글쓰기 클릭시
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<style>
.ck-editor__editable_inline{
min-height:250px;
}
</style>

board에만 추가가 된게 보임 (동적으로 추가한 것)

 

 

 

+++
선생님이 mbox에서 준 파일 4개 내려받기


ckeditor.js
ckeditor.js.map
uploadAdapter.jsp
videoAdapter.js

다운받고 static js 에 넣기

 

 

 


 

 

 

 

<ck editor를 이용한 이미지 첨부 작업>

 

 

 

 

기본 설정


이미지 업로드하는 것도 ck editor가 해주는 게 아님

xhr.open('POST', location.protocol + '//' + location.host + '/common/imageUploader.do', true);

여기에 있는 '/common/imageUploader.do' 얘가 이미지 업로드를 해줌

 

--> 기존 파일에 맞춰서 /common/imageUploader 로 변경

 

 

 

그래서 경로를 지정하고 구현도 직접 해줘야함

얘도 context 경로로 인식하도록 할거라 webapp에 경로 생성

 

 

webapp

image_upload (folder) 생성

 

 

 

 

 

 

공통으로 사용하는 기능 구현 클래스 생성

kr.spring.main.controller (package)
CommonController (class)

package kr.spring.main.controller;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import kr.spring.member.vo.MemberVO;
import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
public class CommonController {
	@RequestMapping("/common/imageUploader")
	@ResponseBody
	public Map<String,Object> uploadImage(MultipartFile upload,
						HttpSession session,
						HttpServletRequest request,
						HttpServletResponse response)throws Exception{
		//업로드할 폴더 경로 (아까 만든 파일/ 절대경로)
		String realFolder = session.getServletContext().getRealPath("/image_upload");
		//업로드할 파일 이름
		String org_filename = upload.getOriginalFilename();
		String str_filename = System.currentTimeMillis() + "_" + org_filename;
		
		log.debug("<<원본 파일명>> : " + org_filename);
		log.debug("<<저장할 파일명>> : " + str_filename);
		
		//중간 경로 생성
		String sub_path;
		MemberVO user = (MemberVO)session.getAttribute("user");
		if(user == null) {
			sub_path = "general";
		}else {
			sub_path = String.valueOf(user.getMem_num());
		}
		
		//전체 경로 생성
		String file_path = realFolder + "/" + sub_path + "/" + str_filename;
		log.debug("<<파일 경로>> : " + file_path);
		
		File f = new File(file_path);
		if(!f.exists()) { //경로가 없을 경우
			f.mkdirs(); //상위 경로가 없을 경우 없는 경로 만들기
		}
		//희망하는 경로에 파일 저장
		upload.transferTo(f);
		
		/*
		 * 상위 경로를 생성해야 하기 때문에 mkdirs() 사용
		 * mkdir()은 상위 경로 생성 못함 헷갈리지 않게 주의
		 */
		
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("uploaded", true);
		map.put("url", request.getContextPath()+"/image_upload/"+sub_path+"/"+str_filename);
		
		return map;
	}
}

@ResponseBody
json 문자열로 받는다는 뜻..?

1.
파일명이 겹치지 않게 하기 위해 난수 대신 연월일시분초를 파일 이름에 붙임
System.currentTimeMillis()
==> 연월일시분초 (long타입으로 명시)

2.
한 폴더에 파일이 너무 많은걸 대비해서 중간경로 생성
중간경로는 로그인한 사람의 회원번호를 활용하도록 코드를 작성함

만약 로그인 한 후 장시간 자리비움 시 로그아웃이 되는데 ajax로 보냈을 때 user가 null이 되기 때문에 (로그아웃 상태)

그땐 제너럴 폴더로 보내려고 함 (사실상 삭제긴 한데 그냥 백업용)

3.
2에서 로그인한 사람일 경우 (중간경로가 있을 경우) 문제가 없는데 경로가 없으면 만들어야함

그때 상위경로도 생성해야 하는 것

=============================

이제 저장하고 호출하면 ajax 동작해서 글쓰기 폼에 사진이 보여짐

그리고 실행하면
webapp
image_upload
1 << 회원번호 (중간경로)
이 안에 사진 파일이 들어가있음

ckeditor로 올린 사진은 외부에서 읽어오는 거기 때문에 contextroot가 바뀌면 못읽어옴
또 글을 삭제할 때 content만 삭제되는거고 경로에 있는 사진은 삭제하지 못해서 쓰레기가 쌓임 방법은 있는데 확장을 거기까진 안할거라함





--정리--

파일 업로드 하는 방식
1) 기존 방식
업로드 경로에 들어감 (filename이란 컬럼에 들어감)

2) ck editor로 사진 넣는 방식
컬럼생성x 링크가 걸려서 첨부된 정보가 content에 저장되는 것



ck 에디터로 사진 넣는 방법
1. 업로드할 폴더 (절대경로) 지정
2. 업로드할 파일이름 객체 생성
3. 중간경로 생성 (user 회원번호)
4. 전체경로 생성 (절대경로/중간경로/파일이름)
5. 희망 경로(upload) 에 파일 저장
6. map에 uploaded, url 지정
7. return

 

 

 

 


 

 

 

<ck editor를 이용한 update (수정) 작업>

 


view 수정
boardModify.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>    
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 내용 시작 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/ckeditor.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/uploadAdapter.js"></script>
<div class="page-main">
   <h2>글수정</h2>
   <form:form action="update" modelAttribute="boardVO" id="update_form" enctype="multipart/form-data">
   <form:hidden path="board_num"/>
      <form:errors element="div" cssClass="error-color"/>
      <ul>
         <li>
            <form:label path="title">제목</form:label>
            <form:input path="title"/>
            <form:errors path="title" cssClass="error-color"/>
         </li>
         <li><b>내용</b></li>
         <li>
            <form:textarea path="content"/>
            <form:errors path="content" cssClass="error-color"/>
            <script>
				function MyCustomUploadAdapterPlugin(editor){
					editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
						return new UploadAdapter(loader);
					}
				}
				
				//데이터를 넣어야하기 때문에 #content를 찾음 (에러 발생시 console에 찍기)
				ClassicEditor
					.create(document.querySelector('#content'),{
						extraPlugins:[MyCustomUploadAdapterPlugin]
					})
					.then(editor => {
						window.editor = editor;
					})
					.catch(error => {
						console.error(error);
					});
			</script>
         </li>
         <li>
            <form:label path="upload">파일 업로드</form:label>
            <input type="file" name="upload" id="upload">
            <c:if test="${!empty boardVO.filename}">
               <div id="file_detail">(${boardVO.filename})파일이 등록되어 있습니다.
                  <input type="button" value="파일삭제" id="file_del">
               </div>
               <script type="text/javascript">
                  $(function(){
                     $('#file_del').click(function(){
                        let choice = confirm('삭제하시겠습니까?');
                        if(choice){
                           $.ajax({
                              url:'deleteFile',
                              data:{board_num:${boardVO.board_num}},
                              type:'post',
                              dataType:'json',
                              success:function(param){
                                 if(param.result == 'logout'){
                                    alert('로그인 후 사용하세요');
                                 }else if(param.result == 'success'){
                                    $('#file_detail').hide();
                                 }else{
                                    alert('파일삭제 오류 발생');
                                 }
                              },
                              error:function(){
                                 alert('네트워크 오류 발생');
                              }
                           });
                        }
                     });
                  });
               </script>
            </c:if>
         </li>
      </ul>
      <div class="align-center">
         <form:button>전송</form:button>
         <input type="button" value="글 상세" onclick="location.href='detail?board_num=${boardVO.board_num}'">
      </div>
   </form:form>
</div>
<!-- 내용 끝 -->

수정폼도 기존에 ck editor 폼 + 작성한 사진이 그대로 떠있으면 됨

+수정 기능도 정상 작동

 

 

 

 


 

 

 

 

<ck editor를 이용한 유튜브 링크 삽입 작업>

 

 

 

 

ck editor안에서 유튜브 링크를 넣으면 수정중인 폼은 물론 상세 페이지에도 안나옴 (but 페이지 소스보기엔 있음)

>> 그럴 때에는 상세페이지에 videoAdapter.js 링크만 걸어도 출력 가능

 

 

 


views
board
boardView.jsp

<!-- 내용 시작 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/videoAdapter.js"></script>
<div class="page-main">

기존 jquery와 videoAdapter 링크 추가

 

=============================

상세페이지로 들어가면 유튜브 보임

 

 

부모글 완료

좋아요-댓글-채팅 순으로 학습


 

 

 

 

<좋아요 표시 기본 설정 작업>

 

 

 

 

테이블 생성

table.sql

--게시판 좋아요
create table spboard_fav(
 board_num number not null,
 mem_num number not null,
 constraint fav_spboard_fk1 foreign key (board_num) references spboard (board_num),
 constraint fav_spmember_fk2 foreign key (mem_num) references spmember (mem_num)
);

 

 

 

 

 

 

 

자바빈 생성

kr.spring.board.vo

BoardFavVO (class)

package kr.spring.board.vo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class BoardFavVO {
	private int board_num;
	private int mem_num;
	
}

 

 

 

 

 

 

DAO (interface) 생성

BoardMapper.java (기존 파일에 추가)

//좋아요
public BoardFavVO selectFav(BoardFavVO fav); //BoardFavVO에 담기
public int selectFavCount(int board_num);
public void insertFav(BoardFavVO fav);
public void deleteFav(BoardFavVO boardFav);
@Delete("DELETE FROM spboard_fav WHERE board_num=#{board_num}")
public void deleteFavByBoardNum(int board_num); //부모글 지울 때 좋아요도 함께 삭제

deleteFavByBoardNum은 부모글 지울 때 삭제하지 않으면 예외가 일어나기 때문에 먼저 어노테이션을 이용해 작성

 

 

 

 

 

 

+++++

부모글을 지울 때에는 자식글을 먼저 지우고 지워야함

그래서 delete board에 deleteFavByBoardNum을 넣음

 

 

기존 Board 게시글 삭제 메서드 수정

BoardServiceImpl

@Override
public void deleteBoard(int board_num) {
    //부모글 좋아요 삭제
    boardMapper.deleteFavByBoardNum(board_num);
    //부모글 삭제
    boardMapper.deleteBoard(board_num);
}

 

 

 


 

 

 

<화면에 좋아요 수 표시하기>

 

 

 

좋아요는 토글 형태이기 때문에 작업 시 데이터 읽어오는 파트, insert-delete 파트 이렇게 쌍을 이뤄서

작업하면 된다고 하심

 

 

 

BoardMapper.java

//좋아요
@Select("SELECT * FROM spboard_fav WHERE board_num=#{board_num} AND mem_num=#{mem_num}")
public BoardFavVO selectFav(BoardFavVO fav); //BoardFavVO에 담기
@Select("SELECT COUNT(*) FROM spboard_fav WHERE board_num=#{board_num}")
public int selectFavCount(int board_num);
public void insertFav(BoardFavVO fav);
public void deleteFav(BoardFavVO boardFav);
@Delete("DELETE FROM spboard_fav WHERE board_num=#{board_num}")
public void deleteFavByBoardNum(int board_num); //부모글 지울 때 좋아요도 함께 삭제

=============================
게시판 화면 첫 목록에도 좋아요 수가 나와야 하기 때문에 조인 후 넣어야함

 

 

 

 

 

목록 부분 좋아요 수 화면 표시 추가

BoardMapper.xml

<!-- 게시판 전체 목록/검색 목록 -->
<select id="selectList" parameterType="map" resultType="boardVO">
    SELECT
      *
    FROM (SELECT
            a.*,
            rownum rnum
        FROM (SELECT
                board_num,
                <![CDATA[
                REPLACE(REPLACE(title,'<','&lt;'),'>','&gt;') title,
                ]]>
                hit,
                reg_date,
                mem_num,
                id,
                nick_name,
                fav_cnt
              FROM spboard
              LEFT OUTER JOIN (SELECT COUNT(*) fav_cnt, board_num FROM 
              spboard_fav GROUP BY board_num) USING(board_num)
              JOIN spmember USING(mem_num)
              <include refid="boardSearch"></include>
              <include refid="boardOrder"></include>)a)
    <![CDATA[
    WHERE rnum >= #{start} AND rnum <= #{end}
    ]]>
</select>

<!-- 정렬 sql태그 -->
<sql id="boardOrder">
    <if test="order == 1">
        ORDER BY board_num DESC
    </if>
    <if test="order == 2">
        ORDER BY hit DESC
    </if>
    <if test="order == 3">
        ORDER BY fav_cnt DESC NULLS LAST
    </if>
    <if test="order == 4">
        ORDER BY re_cnt DESC NULLS LAST
    </if>
</sql>

=============================
좋아요를 하나도 못받은 것도 (null) 표시되게 해야하기 때문에 LEFT OUTER JOIN 사용
board_num 별로 group by (게시글 별)
=============================
정렬 시에도 좋아요가 null 값인 것을 마지막으로 하는 코드 추가

 

 

 

 

 

 

BoardService.java

//좋아요
public BoardFavVO selectFav(BoardFavVO fav); //BoardFavVO에 담기
public int selectFavCount(int board_num);
public void insertFav(BoardFavVO fav);
public void deleteFav(BoardFavVO boardFav);

BoardMapper.java에 있는 것 복붙하면 됨
부모글 지울때 좋아요는 이미 사용했기 때문에 필요없음

 

 

 

 

 

Impl에 좋아요 메서드 추가

BoardServiceImpl

package kr.spring.board.service;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import kr.spring.board.dao.BoardMapper;
import kr.spring.board.vo.BoardFavVO;
import kr.spring.board.vo.BoardVO;

@Service
@Transactional
public class BoardServiceImpl implements BoardService{
	@Autowired
	private BoardMapper boardMapper;
	
	@Override
	public List<BoardVO> selectList(Map<String, Object> map) {
		return boardMapper.selectList(map);
	}

	@Override
	public int selectRowCount(Map<String, Object> map) {
		return boardMapper.selectRowCount(map);
	}

	@Override
	public void insertBoard(BoardVO board) {
		boardMapper.insertBoard(board);
	}

	@Override
	public BoardVO selectBoard(int board_num) {
		return boardMapper.selectBoard(board_num);
	}

	@Override
	public void updateHit(int board_num) {
		boardMapper.updateHit(board_num);
	}

	@Override
	public void updateBoard(BoardVO board) {
		boardMapper.updateBoard(board);
	}

	@Override
	public void deleteBoard(int board_num) {
		//부모글 좋아요 삭제
		boardMapper.deleteFavByBoardNum(board_num);
		//부모글 삭제
		boardMapper.deleteBoard(board_num);
	}

	@Override
	public void deleteFile(int board_num) {
		boardMapper.deleteFile(board_num);
	}

	/*----------------좋아요--------------------------*/
	
	@Override
	public BoardFavVO selectFav(BoardFavVO fav) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int selectFavCount(int board_num) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void insertFav(BoardFavVO fav) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void deleteFav(BoardFavVO boardFav) {
		// TODO Auto-generated method stub
		
	}
}
728x90
반응형