2024. 5. 11. 21:04ㆍ웹개발
유튜브를 보면 좋아요 버튼을 눌러주면 버튼만 바뀔 뿐 영상이 멈추거나 페이지가 새로 로드되지 않는다.
이는 비동기 처리, 병렬적으로 태스크를 수행하는 것이다.
동기와 비동기의 차이는 동기는 직렬적으로 태스크를 수행한 것으로, 요청 후 응답을 받아야만 작동이 된다.
하지만 비동기는 요청을 보내고 바로 다음 태스크가 진행되는 것이다. 그래서 유튜브 영상이 끊이지 않고도 다른 기능을 수행할 수 있는 것이다.
스프링부트에서 ajax를 활용한 좋아요를 구상하기 위해서는 서버측에선 테이블(Entity), 레포시토리(Repository),
서비스(Service), 컨트롤러(Controller)가 필요했다.
<좋아요 테이블>
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString
@Table(name = "likes")
@SequenceGenerator(
name = "likes_SEQ",
sequenceName = "likes_SEQ",
initialValue = 1,
allocationSize = 1
)
public class LikesEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "likes_SEQ")
private Long likesid;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "userid")
private UsersEntity users;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rid")
private RecipeEntity recipe;
//likesid는 생성자가 필요 없어서 @AllArgsConstructor 사용 X
public LikesEntity(UsersEntity users, RecipeEntity recipe) {
this.users = users;
this.recipe = recipe;
}
}
좋아요 테이블에서는 회원 정보(userid)와 좋아요를 적용할 레시피(rid)와 연관관계를 맺었다.
<Repository>
public interface LikeRepository extends JpaRepository<LikesEntity, Long> {
//있는지 없는지 검토
boolean existsByUsersAndRecipe(UsersEntity users, RecipeEntity recipe);
}
boolean을 사용하여 좋아요가 적용 되었는지, 안 되었는지를 판별했다.
<Service>
@Service
@RequiredArgsConstructor
@Transactional
public class LikeService {
private final RecipeService recipeService;
private final RecipeRepository recipeRepository;
private final LikeRepository likeRepository;
public boolean addLike(int rid, UsersEntity users) {
RecipeEntity recipe = recipeRepository.findByRid(rid);
if (!likeRepository.existsByUsersAndRecipe(users, recipe)) {
//호출되면 board에 있는 count 증가
recipe.setRgoodcnt(recipe.getRgoodcnt()+1);
// likeRepository에 userid 값이랑 recipe값 저장해버림
likeRepository.save(new LikesEntity(users, recipe));
return true;
}
else {
recipe.setRgoodcnt(recipe.getRgoodcnt()-1);
likeRepository.deleteByUsersAndRecipe(users, recipe);
return false;
}
}
public boolean existLike(int rid, UsersEntity users) throws Exception {
RecipeEntity recipe = recipeRepository.findByRid(rid);
if (likeRepository.existsByUsersAndRecipe(users, recipe)) {
return true; //존재하면 true 값
} else {
return false; //없으면 false 값
}
}
}
<Controller>
@RestController
@RequiredArgsConstructor
public class LikeController {
private final LikeService likeService;
private final UsersService usersService;
@PostMapping("/api/likes/up/{rid}")
public boolean addLike( HttpSession session, @PathVariable("rid")@Positive int rid) throws Exception { //@PathVariable("rid")는 Spring MVC에서 경로 변수를 받아들이는 방법 / @Positive는 Bean Validation API의 어노테이션 중 하나입니다. 이 어노테이션은 해당 필드 값이 0보다 큰지를 검사
//회원 번호을 불러옴
int userid = (int) session.getAttribute("userId");
UsersEntity users = usersService.getUserByUserId(userid);
return likeService.addLike(rid, users);
}
}
※ 이번 프로젝트에서는 세션으로 회원정보를 가져왔지만 이는 보안에 취약점이 많다고 했다. 다음부터는 보안에 신경을 많이 써야 할 것 같다.
<html>
<div layout:fragment="content">
...
<div id="goodAndBook" style="text-align: center; margin-bottom: 20px"> <!-- 좋아요, 북마크 버튼 -->
<input type="hidden" id="recipeID" th:value="${recipeDTO.getRid()}">
<!-- 좋아요 버튼 -->
<button id="likeButton1" th:if="${noLogin != null}" data-bs-toggle="modal" data-bs-target="#plzLogin" style="width: 50px; height: 50px;">
<i id="likeIcon1" class="fa fa-heart-o" style="font-size: 24px;"></i> <!-- 로그인 안 한 사용자에게는 빈 하트 아이콘 표시 -->
</button>
<button id="likeButton" onclick="rgoodAjax()" th:if="${noLogin == null}" style="width: 50px; height: 50px;">
<i id="likeIcon" th:class="${setLike} ? 'fa fa-heart' : 'fa fa-heart-o'" style="font-size: 24px;"></i>
</button>
</div>
...
<javascript>
const rgoodAjax = () => {
const rid = $('#recipeID').val();
$.ajax({
data: {"rid" : rid },
type: "post",
url: "/api/likes/up/" + rid,
success: (data, textStatus, xhr) => {
// HTTP 응답 상태 코드를 확인하여 성공 여부를 판단
if (xhr.status === 200) {
// 좋아요 상태에 따라 아이콘 변경
const likeIcon = $('#likeIcon');
if (data === true) {
likeIcon.removeClass('fa-heart-o').addClass('fa-heart');
} else {
likeIcon.removeClass('fa-heart').addClass('fa-heart-o');
}
}
},
error: (error) => {
// 에러 처리
}
})
}
</javascript>
</div>
타임리프 레이아웃을 사용하면 자바스크립트는 <div layout:fragment="content"> 영역 안에 작성해야 한다. 그것을 모르고 영역 밖에다 작성하니, 네트워크에서 서버로 전송을 하지를 못하여 3일 밤낮을 구글과 지피티를 뒤져봤으나 해결을 못해 포기할 즈음 겨우 찾아냈다.
'웹개발' 카테고리의 다른 글
스프링부트) 회원가입 네이버 이메일 인증 2 (0) | 2024.05.29 |
---|---|
스프링부트) 회원가입 네이버 이메일 인증 1 (0) | 2024.05.29 |
스프링부트) 회원가입 번호인증(2) (0) | 2024.05.23 |
스프링부트) 회원가입 번호인증(1) (0) | 2024.05.23 |
스프링부트) 회원가입 시 우편번호 검색 (0) | 2024.05.13 |