본문 바로가기
Spring/JPA + Security

[Spring Data JPA] 구독하기 구현하기 - 3 구독정보 모달 구현, Mysql 스칼라 서브쿼리 만들기

by pyogowoon 2023. 1. 11.

 

 

 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문을 많이 짜야했을것

머리박아가면서 만들어보자.

 

 

 

댓글