본문 바로가기

프로젝트

[SpringBoot] 경매사이트 만들기 - 좋아요 기능(Ajax)

이번엔 좋아요 기능에 대한 포스팅을 해보겠다ㅎㅎ

 

먼저 ajax로 좋아요 기능을 만들기 위해 추가된 부분

1. actionBoard.js 에 좋아요 js 추가.

2. MyPickApiController -> RestController

3. MyPickService

4. MyPickRepository

5. MyPickUpdateVO

 

결과

좋아요 클릭 전
좋아요 클릭 후


1. 좋아요 기능이 있는 부분의 jsp

actionDetailPostForm.jsp

<!-- 로그인 하면 principal객체 생성 -->
<sec:authorize access="isAuthenticated()">
	<sec:authentication property="principal" var="principal"/>
</sec:authorize>

<c:set var="post" value="${requestScope.actionPost }"></c:set>
<c:set var="pickCnt" value="${requestScope.totalPickCount }"></c:set>
<c:set var="memberPick" value="${requestScope.memberPick }"></c:set>

<c:choose>
	<c:when test="${not empty principal}">
		<input type="hidden" id="postNo" value="${post.actionBoardNo }">
		<span id="pickView"> 
        		<c:choose>
				<c:when test="${memberPick == false}">
					<input type="image" id="no-pick" src="bootstrap/img/kdg/no-heart.png" onclick="pick(false)">
				</c:when>
				<c:otherwise>
					<input type="image" id="yes-pick" src="bootstrap/img/kdg/yes-heart.png" onclick="pick(true)">
				</c:otherwise>
			</c:choose>
		</span>
		<span id="pickResult">${pickCnt}</span>
	</c:when>
	<c:otherwise>
		<img alt="좋아요" src="bootstrap/img/kdg/no-heart.png">
		<span>${pickCnt}</span>
	</c:otherwise>
</c:choose>

좋아요 jsp 부분이고 input 태그의 타입을 image로 설정해서 해당 이미지를 클릭했을때 매개변수(false || true)와 함께 js 쪽으로 넘어가게 만들었다.

그리고 input 태그는 현재 유저가 해당 게시물에 좋아요를 클릭했었는지 안했었는지에 따라 다르게 나타나도록 했다.

 

 

2. 좋아요 ajax부분

actionBoard.js

/*********************************
경매게시판 좋아요!
**********************************/
function pick(flag){
	let data={
			actionBoardNo:$("#postNo").val(),
			flag:flag,
			};
	
	$.ajax({
			type: "POST",
			url: "updatePick",
			data:JSON.stringify(data),//위의 data는 js객체라 이렇게 json으로 변경해줘야함.(글고 http body데이터임)
			contentType: "application/json; charset=utf-8",//body데이터가 어떤 타입인지(MIME)
			//dataType:"json",//응답이 왔을때의 데이터가 json모양이면 js객체로 변환.
			success: function(result) {
				//alert(result);
				if(flag==false){
					document.getElementById("pickView").innerHTML='<input type="image" id="yes-pick" src="bootstrap/img/kdg/yes-heart.png" onclick="pick(true)">';
				}else{
					document.getElementById("pickView").innerHTML='<input type="image" id="no-pick" src="bootstrap/img/kdg/no-heart.png" onclick="pick(false)">';
				}
				document.getElementById("pickResult").innerHTML = result;
			},
			error: function(error, jqXHR, textStatus, errorThrown) {
				console.log(error);
				console.log(jqXHR);
				console.log(textStatus);
				console.log(errorThrown);
				alert("에러났어요. 다시 시도해주세요.");
			}
		});//ajax
}//pick(flag) 함수

input태그에서 매개변수로 받은 flag 변수, jsp부분에서 hidden으로 감춰놓은 postNo(게시물번호) 를 data 변수로 묶어서 선언해 준다. 이후 ajax의 url, type, data을 넣어주고 data는 json으로 송신 할것이기 때문에 JSON.stringfy()로 js 객체를 json으로 바꿔주는 작업을 해준다.

그리고 ajax 통신은 기본적으로 body 부분에 데이터를 실어서 보내고, 이 body 데이터가 어떤 데이터인지 서버에서 알아먹을수 있게 contentType을 넣어줘야한다.

그리고 통신에 성공하면 sucess, 실패하면 error 부분으로 넘어가게 된다.

 

sucess

현재 flag가 false라면 빈하트에서 하트를 누른상태다. -> 하트를 채워준다.

현재 flag가 true 라면 꽉찬하트에서 하트를 누른상태다. -> 하트를 비워준다.

result값으로 해당 개시물의 좋아요 총개수 데이터를 받고 그 결과값을 pickResult부분에 넣어준다.

error

에러에 대한 정보를 console에 띄운다.

 

 

3. 좋아요 컨트롤러(RestController)

MyPickApiController.java

package org.my.toyproject.controller.api;

import ...

@RestController
public class MyPickApiController {
	
	@Autowired
	private MyPickService myPickService;
	
	
	@Secured("ROLE_MEMBER")
	@PostMapping("updatePick")
	public ResponseEntity<Integer> updatePick(@AuthenticationPrincipal PrincipalDetail principal,
			@RequestBody MyPickUpdateVO myPickUpdateVO) {
		
		//빈하트에서 눌렀을때 꽉찬 하트로 변경.
		if(!myPickUpdateVO.isFlag()) myPickService.savePick(myPickUpdateVO,principal);
		
		//꽉찬 하트에서 눌렀을때 빈하트로 변경.
		else myPickService.deletePick(myPickUpdateVO,principal);
		
		//지금 게시물의 좋아요 정보
		List<MyPick> pickList = myPickService.actionBoardNoByPcikcnt(myPickUpdateVO.getActionBoardNo());
		int cnt = pickList.size();//총개수
		
		return new ResponseEntity<Integer>(cnt,HttpStatus.OK);
	}
}

jsp부분에서 로그인한 유저만 좋아요를 클릭 할수 있게 했지만, 서버쪽에서도 @Secured로 회원만 기능을 수행할 수 있도록 했다. 그리고 매개변수로 받는 데이터는 json 이기 때문에 VO(dto)형태 혹은 Map의 형태로 받아줘야 한다. 이때 나는 VO를 만들어서 데이터를 받았다.

그리고 principal 변수는 시큐리티 세션에 저장돼있는 로그인한 유저의 정보를 갖고오는 역할을 한다.

(한마디로 PrincipalDetail로 감싸져있는 Member가 들어있는거임.)

 

이후 받은 데이터의 flag값을 확인해서 MyPick 테이블에 값을 넣어주는 서비스를 할지,

삭제하는 서비스를 할지에 대한 분기를 거친뒤, 클라이언트 쪽에서 result값(좋아요 총개수)을 넘겨주기 위해 해당 게시물에 대한 좋아요 전체 정보를 조회하고 총개수를 반환하도록 했다.

 

MyPickUpdateVO.java

package org.my.toyproject.vo;

import ...

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MyPickUpdateVO {
	private long actionBoardNo;
	private boolean flag;
}

 

 

4. 좋아요 서비스

MyPickService.java

package org.my.toyproject.service;

import ...

@Service
public class MyPickService {

	@Autowired
	private ActionBoardService actionBoardService;
	
	@Autowired
	private MemberService memberService;
	
	@Autowired
	private MyPickRepository myPickRepository;
	
	//좋아요 추가
	@Transactional
	public void savePick(MyPickUpdateVO myPickUpdateVO, PrincipalDetail principal) {
		ActionBoard actionBoard = actionBoardService.findPostByActionBoardNo(myPickUpdateVO.getActionBoardNo());
		Member member = memberService.findMemberById(principal.getMemberNo());
		MyPick myPick = MyPick.builder()
				.actionBoard(actionBoard)
				.member(member)
				.build();
		myPickRepository.save(myPick);
	}

	
	//좋아요 삭제
	@Transactional
	public void deletePick(MyPickUpdateVO myPickUpdateVO, PrincipalDetail principal) {
		Member member = memberService.findMemberById(principal.getMemberNo());
		ActionBoard actionBoard = actionBoardService.findPostByActionBoardNo(myPickUpdateVO.getActionBoardNo());
		
		MyPick myPick = myPickRepository.findByMemberAndActionBoard(member, actionBoard);
		myPickRepository.delete(myPick);
	}
	
	
	//해당 게시물에 대한 좋아요 조회
	@Transactional
	public List<MyPick> actionBoardNoByPcikcnt(long actionBoardNo) {
		ActionBoard actionBoard = actionBoardService.findPostByActionBoardNo(actionBoardNo);
		List<MyPick> list = myPickRepository.findByActionBoard(actionBoard);
		return list;
	}
}

savePick

회원이 빈하트를 클릭했을때 MyPick 테이블에 값을 넣어주고, 하트를 채워주기 위한 서비스 부분이다.

JPA의 save api를 사용해서 insert를 진행했고, MyPick 객체를 셋업하기 위해서 게시물 번호와 멤버 번호를 이용해 이전에 AcitonBoardService, MemberService에서 만들어둔 메서드로 각 정보를 조회 및 반환 받아서 MyPick 객체에 넣어줬다.

 

deletePick

회원이 꽉찬 하트를 클릭했을때 MyPick 테이블에 값을 삭제하고, 빈하트를 만들어주기 위한 서비스 부분이다.

JPA의 delete api를 사용했다. 그리고 이때는 어떤 데이터를 지울껀지 정확히 해야한다고 생각해서 savePick 메서드처럼 MyPick객체를 찾은 뒤 해당 정보에 대한 데이터를 지워줬다.

(게시물번호와 멤버번호에 해당하는 MyPick 객체를 찾기 위해 repository에 새로운 메서드 만듦.)

 

actionBoardNoByPickcnt

게시물번호에 해당하는 모든 좋아요 데이터를 리스트로 반환받는 메서드다.

(이부분도 마찬가지로 repository에 메서드 추가함.)

 

 

5. 좋아요 repository

MyPickRepository.java

package org.my.toyproject.repository;

import ...

public interface MyPickRepository extends JpaRepository<MyPick, Long>{
	MyPick findByMemberAndActionBoard(Member memberNo, ActionBoard actionBoardNo);
	List<MyPick> findByActionBoard(ActionBoard actionBoardNo);
}

Service쪽에서 사용한 repository 메서드들을 정의한 부분이다.

 

 


★ 이슈사항

1.문제
Ajax에서 MyPickApiController 쪽으로 json data를 받을때 @RequestBody로 받게 되는데 이때, 그냥 문자열로 받을라고 하니깐 안받아짐. 한마디로 데이터 타입이 안맞았기 때문..
해결
매개변수를 받는 방법이 HashMap과 VO를 만들어서 받는 방법이 있었음.
VO를 만들어서 데이터를 받았다!

 

2. 문제
하트를 클릭했을때 서버와 통신후 클라이언트쪽에서 STATUSTEXT PARSERERROR 에러가 발생.
이유는 dataType을 json으로 해놨는데 서버쪽에서 보내는 데이터는 Integer라서 에러가남.
해결.
dataType을 지워버려서 해결했다.(json모양으로 안받겠다는뜻.)

(만약 json 형태로 받으려고 한다면 VO를 하나 만들어서 서버에서 VO를 return해주면 됨! 혹은 Map)

 

 

★ 다음에 할꺼!

게시판 리스트 페이징

결제 시스템 !!

 

ps. 혹시 놓친 부분 있는지 다시한번 확인해보기.