1번이 로그인해서 2번의 구독정보를 보고있다고 가정하자,
조건
1. 구독 정보를 클릭 시 2번 유저를 구독한 상황인 다른 유저들이 나와야 한다.
2. 로그인한 1번유저가 만약 제 3자인 3번유저가 구독되어있는 상황이라면 구독취소 버튼이, 아니라면 구독하기 버튼이 나와야한다.
3. 1번유저가 3번유저 구독정보를 눌렀는데 본인인 1번유저도 나오면 안된다.
그렇다면 필요한건 이 여러가지의 데이터를 옮길 DTO와 DB쿼리가 필요하다.
profile.jsp
<div class="subscribe">
<ul>
<li><a href=""> 게시물<span>${dto.imageCount}</span>
</a></li>
<li><a href="javascript:subscribeInfoModalOpen();"> 구독정보<span>${dto.subscribeCount}</span>
</a></li>
</ul>
</div>
.
.
.
.
.
.
<!-- 구독정보 모달 -->
<div class="modal-subscribe">
<div class="subscribe">
<div class="subscribe-header">
<span>구독정보</span>
<button onclick="modalClose()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="subscribe-list" id="subscribeModalList">
<div class="subscribe__item" id="subscribeModalItem-2">
<div class="subscribe__img">
<img src="#" onerror="this.src='/images/person.jpeg'"/>
</div>
<div class="subscribe__text">
<h2>ssar</h2>
</div>
<div class="subscribe__btn">
<button class="cta blue" onclick="toggleSubscribeModal(this)">구독취소</button>
</div>
</div>
</div>
</div>
</div>
<!-- 구독정보 모달 end-->
구독정보를 클릭하면 javascript를 통해 modal이 오픈된다.
profile.js
function subscribeInfoModalOpen() {
$(".modal-subscribe").css("display", "flex");
}
function getSubscribeModalItem() {
}
SubscribeDto
package com.pyo.yourspick.web.dto.subscribe;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class SubscribeDto {
private int id;
private String username;
private String profileImageUrl;
private Integer subscribeState;
private Integer equalUserState;
}
모달 클릭시 필요한 데이터를 옮길 DTO를 만든다.
userId는 접속자 id고 username은 말그대로 유저네임, profileImageUrl 은 프로필 사진, subscribeState 구독 상황,
equalUserState 는 접속자유저 와 구독정보에 뜬 유저가 같은사람이면 구독하기나 구독취소 버튼이 뜨면 안돼므로 만듬
그렇다면 이제 Controller 를 만들어야 하는데, Data를 주고받기 때문에 apiController 로 만들거고
원래 만들어놓은 UserApiController 에 만든다.
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RequiredArgsConstructor
@RestController
public class UserApiController {
private final UserService userService;
private final SubscribeService subscribeSerivce;
.
.
.
.
.
.
.
@GetMapping("api/user/{pageUserId}/subscribe")
public ResponseEntity<?> subscribeList(@PathVariable int pageUserId, @AuthenticationPrincipal PrincipalDetails principalDetails){
List<SubscribeDto> subscribeDto = subscribeSerivce.구독리스트( principalDetails.getUser().getId(),pageUserId);
return new ResponseEntity<>(new CMRespDto<>(1,"구독 정보 리스트 가져오기 성공",subscribeDto), HttpStatus.OK);
}
}
이제 DB 쿼리를 만들어야 하는데, SELECT 를 여러번 해야 하기떄문에 스칼라 서브쿼리를 이용할것이다.
*왜 int 가 아닌 Integer 인가 -> mariaDB에서 select 에 true값을 줬을때 int는 못받는 경우가 있어서 Integer 사용
1번유저가 로그인해서 2번 유저의 구독정보를 본다고 가정하자,
2번 유저의 모달을 눌렀을 때 정보가 2개나오는 상황이라고 가정하면 쿼리가
SELECT * FROM subscribe WHERE fromUserId= 2;
이런식으로 하면될것이다
--만약 2번의 구독정보를 보고 1과 3이 2번을 구독하고있고 이를 추출하고싶다면 --
SELECT * FROM user WHERE id = 1 OR id = 3;
이렇게 할텐데
-- 이를 조인하고, (user.id = subscribe.toUserId) 구독모달에 필요한 내용만 보게 한다면
SELECT u.id, u.username, u.profileImageUrl
FROM user u INNER JOIN subscribe s
ON u.id = s.toUserId
WHERE s.fromUserId = 2;
-- 그럼 이1번유저 의 구독 여부가 필요한데, 1번이 로그인 했고 화면에 1번,3번이 떠있다고 가정하면 --
SELECT true FROM subscribe WHERE fromUserId = 1 AND toUserId = 3;
---- 가상칼럼 추가 ------
SELECT u.id, u.username, u.profileImageUrl, 1 subscribeState
FROM user u INNER JOIN subscribe s
ON u.id = s.toUserId
WHERE s.fromUserId = 2;
이런식으로 select 뒤에 1,하고 칼럼명 적어 가상 칼럼을 생성해보았다.
id | username | profileImageUrl | subscribeState |
1 | ssar | (null) | 1 |
3 | love | (null) | 1 |
이런식으로 생성된다.
그렇다면 현재 로그인(id)이 1이라면 1의 subscribe는 null을 띄우고 3은 그대로 1을 띄워야 한다.
----------- 스칼라 서브쿼리 ( SELECT절에 SELECT가 하나 더 나오는것) ★★ 중요하다 --------------------
스칼라 서브쿼리의 SELECT 절의 SELECT는 무조건 단일 행이 나와야한다 (1칸쓸거니까)
SELECT u.id, u.username, u.profileImageUrl,
(SELECT u.id) subscribeState
FROM user u INNER JOIN subscribe s
ON u.id = s.toUserId
WHERE s.fromUserId = 2;
이런식으로 됨 (위에는 예제) 즉 스칼라 서브쿼리절의 select에는 from이 붙을수도있고 안붙을수도있다
그렇다면 스칼라 서브쿼리 형식의 구독 여부 완성 쿼리를 만들어보면
--구독 여부 완성 쿼리--
SELECT u.id, u.username, u.profileImageUrl,
( SELECT true FROM subscribe WHERE fromUserId = 1 AND toUserId = u.id) subscribeState
FROM user u INNER JOIN subscribe s
ON u.id = s.toUserId
WHERE s.fromUserId = 2;
이런식으로 서브쿼리절의 단일행 리턴을 준수해서 쿼리를 만들고 또한 서브쿼리절은 위에서 사용한 1,3의 구독여부 true를 그대로 가져왔고 toUserId = 3을 u.id로 변경한 것
id | username | profileImageUrl | subscribeState |
1 | ssar | null | null |
3 | love | null | 1 |
그 다음 동일유저인지 판단하는 쿼리도 하나 필요한데,
-- 동일유저 판단 쿼리 --
SELECT u.id, u.username, u.profileImageUrl,
(SELECT true FROM subscribe WHERE fromUserId = 1 AND toUserId = u.id) subscribeState,
(1=u.id) equalUserState
FROM user u INNER JOIN subscribe s
ON u.id = s.toUserId
WHERE s.fromUserId = 2;
이렇게 하면
id | username | profileImageUrl | subscribeState | equalUserState |
1 | ssar | null | null | 1 |
3 | love | null | 1 | 0 |
쿼리 끝
SELECT u.id, u.username, u.profileImageUrl,
if ((SELECT 1 FROM subscribe WHERE fromUserId = 1 AND toUserId = u.id), 1,0) subscribeState,
if ((1=u.id), 1, 0) equalUserState
FROM user u INNER JOIN subscribe s
ON u.id = s.toUserId
WHERE s.fromUserId = 2;
if 문 추가, true -> 1 변경
스칼라 서브쿼리는 매우매우어렵다.
이렇게 짜지않았다면 코드에 for문을 많이 짜야했을것
머리박아가면서 만들어보자.
'Spring > JPA + Security' 카테고리의 다른 글
[Spring Data JPA] 구독하기 구현하기 -5 구독 정보 모달 Ajax 연동하기 , javascript를 통한 동적인 뷰 렌더링 (0) | 2023.01.11 |
---|---|
[Spring Data JPA] 구독하기 구현하기 -4 구독리스트 구현, JPQL 사용한 Native Query (0) | 2023.01.11 |
[Spring Data JPA] 구독하기 구현하기 - 2 Ajax 연결하기 (0) | 2023.01.11 |
[Spring Data JPA] 로그인 상태에 따라 다른 구독버튼 / 구독하기 - 1-1 (0) | 2023.01.10 |
[Spring Data JPA] 구독하기 구현하기 - 1 구독 정보 DB 완성 및 구현 (0) | 2023.01.10 |
댓글