54. 구독정보 뷰 렌더링-구독정보 완성하기
UserProfileDto.java
package com.cos.photogramstart.web.dto.user;
import com.cos.photogramstart.domain.user.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserProfileDto {
private boolean PageOwnerState;
private int imageCount;
private boolean subscribeState;
private int subscribeCount;
private User user;
}
SubscribeRepository.java
package com.cos.photogramstart.domain.subscribe;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
public interface SubscribeRepository extends JpaRepository<Subscribe, Integer>{
@Modifying // INSERT, DELETE, UPDATE를 네이티브 쿼리로 작성하러면 해당 어노테이션 필요!!
@Query(value = "INSERT INTO subscribe(fromUserId, toUserId, createDate) VALUES(:fromUserId,:toUserId,now())",nativeQuery = true)
void mSubscribe(int fromUserId, int toUserId);
@Modifying
@Query(value = "DELETE FROM subscribe WHERE fromUserId=:fromUserId AND toUserId = :toUserId",nativeQuery = true)
void mUnSubscribe(int fromUserId, int toUserId);
@Query(value="SELECT COUNT(*) FROM subscribe WHERE fromUserId = :principallId AND toUserId = :pageUserId",nativeQuery=true)
int mSubscribeState(int principallId, int pageUserId);
@Query(value="SELECT COUNT(*) FROM subscribe WHERE fromUserId = :pageUserId",nativeQuery=true)
int mSubscribeCount(int pageUserId);
}
UserService.java
package com.cos.photogramstart.service;
import java.util.function.Supplier;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.cos.photogramstart.domain.subscribe.SubscribeRepository;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;
import com.cos.photogramstart.handler.ex.CustomException;
import com.cos.photogramstart.handler.ex.CustomValidationException;
import com.cos.photogramstart.web.dto.user.UserProfileDto;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final SubscribeRepository subscribeRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Transactional(readOnly=true) // select는 readOnly = true
public UserProfileDto 회원프로필(int pageUserId, int principalId) {
UserProfileDto dto = new UserProfileDto();
// SELECT * FROM image WHERE userId = :userId;
User userEntity = userRepository.findById(pageUserId).orElseThrow(()->{
throw new CustomException("해당 프로필 페이지는 없는 페이지입니다.");
});
dto.setUser(userEntity);
dto.setPageOwnerState(pageUserId == principalId);
dto.setImageCount(userEntity.getImages().size());
int subscribeState = subscribeRepository.mSubscribeState(principalId, pageUserId);
int subscribeCount = subscribeRepository.mSubscribeCount(pageUserId);
dto.setSubscribeCount(subscribeCount);
dto.setSubscribeState(subscribeState == 1);
return dto;
}
@Transactional
public User 회원수정(int id, User user) {
// 1. 영속화
// 1. 무조건 찾았다. 걱정마 get() // 2. 못찾았어 익섹션 발동시킬게 orElseThrow()
User userEntity = userRepository.findById(id).orElseThrow(()->{return new CustomValidationException("찾을 수 없는 ID 입니다.");});
// 2. 영속화된 오브젝트를 수정 - 더티체킹 (업데이트 완료)
userEntity.setName(user.getName());
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
userEntity.setPassword(encPassword);
userEntity.setBio(user.getBio());
userEntity.setWebsite(user.getWebsite());
userEntity.setPhone(user.getPhone());
userEntity.setGender(user.getGender());
return userEntity;
} // 더티체킹이 일어나서 업데이트가 완료됨.
}
profile.jsp
<c:choose>
<c:when test="${dto.isPageOwnerState}">
<button class="cta" onclick="location.href='/image/upload'">사진등록</button>
</c:when>
<c:otherwise>
<c:choose>
<c:when test = "${dto.subscribeState}}">
<button class="cta blue" onclick="toggleSubscribe(this)">구독취소</button>
</c:when>
<c:otherwise>
<button class="cta" onclick="toggleSubscribe(this)">구독하기</button>
</c:otherwise>
</c:choose>
</c:otherwise>
</c:choose>
55. 구독정보 뷰 렌더링-구독 및 구독취소 구현하기
profile.jsp
<c:choose>
<c:when test="${dto.isPageOwnerState}">
<button class="cta" onclick="location.href='/image/upload'">사진등록</button>
</c:when>
<c:otherwise>
<c:choose>
<c:when test = "${dto.subscribeState}}">
<button class="cta blue" onclick="toggleSubscribe(${dto.user.id},this)">구독취소</button>
</c:when>
<c:otherwise>
<button class="cta" onclick="toggleSubscribe(${dto.user.id},this)">구독하기</button>
</c:otherwise>
</c:choose>
</c:otherwise>
</c:choose>
profile.js
function toggleSubscribe(toUserId, obj) {
if ($(obj).text() === "구독취소") {
$.axjx({
type:"delete",
url:"/api/subscribe/" + toUserId,
dataType:"json"
}).done(res=>{
$(obj).text("구독하기");
$(obj).toggleClass("blue");
}).fail(error=>{
console.log("구독취소실패",error);
});
} else {
$.axjx({
type:"post",
url:"/api/subscribe/" + toUserId,
dataType:"json"
}).done(res=>{
$(obj).text("구독취소");
$(obj).toggleClass("blue");
}).fail(error=>{
console.log("구독하기실패",error);
});
}
}
header.jsp
<li class="navi-item"><a href="/user/${principal.user.id }">
56. 구독정보 뷰 렌더링-구독 모달 정보를 위한 API 만들기
com.cos.photogramstart.web.dto.subscribe.SubscribeDto.java
package com.cos.photogramstart.web.dto.subscribe;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SubscribeDto {
private int userId;
private String username;
private String profileImageUrl;
private Integer subscribeState;
private Integer equalUserState;
}
UserApiController.java
private final SubscribeService subscribeService;
@GetMapping("/api/user/{pageUserId}/subscribe")
public ResponseEntity<?> subscribeList(@PathVariable int pageUserId, @AuthenticationPrincipal PrincipalDatails principalDatails){
List<SubscribeDto> subscribeDto = subscribeService.구독리스트(principalDatails.getUser().getId(),pageUserId);
return new ResponseEntity<>(new CMRespDto<>(1,"구독자 정보 리스트 가져오기 성공",subscribeDto),HttpStatus.OK);
}
SubscribeService.java
private final SubscribeRepository subscribeRepository;
@Transactional(readOnly=true)
public List<SubscribeDto> 구독리스트(int principalId, int pageUserId){
return null;
}
57. 구독정보 뷰 렌더링-조인과 스칼라 서브쿼리
- 로그인(1 ssar) — 구독정보(2 cos)
— 1번화 3번의 정보(toUserId)가 구독 모달에 출력
SELECT * FROM subscibe
SELECT * FROM user;
SELECT * FROM subscribe WHERE fromUserId = 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,3)
SELECT true FROM subscribe WHERE fromUserId = 1 AND toUserId = 1;
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 u.id, u.username, u.profileImageUrl,
(SELECT COUNT(*)) subscribeState
FROM user u INNER JOIN subscribe s
ON u.id = s.toUserId
WHERE s.fromUserId = 2;
— 구독 여부 완성 쿼리
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;
— 동일 유저인지 판단 쿼리
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;
58. 구독정보 뷰 렌더링-QLRM 라이브러리로 DTO 매핑하기
SubscribeRepository에서 못짬 (return이 다름)
service에서 직접 native 쿼리를 짤꺼임
SubscribeService.java
package com.cos.photogramstart.service;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.qlrm.mapper.JpaResultMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.cos.photogramstart.domain.subscribe.SubscribeRepository;
import com.cos.photogramstart.handler.ex.CustomApiException;
import com.cos.photogramstart.web.dto.subscribe.SubscribeDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class SubscribeService {
private final SubscribeRepository subscribeRepository;
private final EntityManager em; // Repository는 EntityManager를 구현해서 만들어져 있는 구현체
@Transactional(readOnly=true)
public List<SubscribeDto> 구독리스트(int principalId, int pageUserId){
// 쿼리 준비
StringBuffer sb = new StringBuffer();
sb.append("SELECT u.id, u.username, u.profileImageUrl, ");
sb.append("if ((SELECT 1 FROM subscribe WHERE fromUserId = ? AND toUserId = u.id),1,0) subscribeState, ");
sb.append("if((?=u.id),1,0) equalUserState ");
sb.append("FROM user u INNER JOIN subscribe s ");
sb.append("ON u.id = s.toUserId ");
sb.append("WHERE s.fromUserId = ?"); // 세미콜론 첨부하면 안됨
// 1. 물음표 principalId
// 2. 물음표 principalId
// 3. 마지막 물음표 pageUserId
// 쿼리 완성
Query query = em.createNativeQuery(sb.toString())
.setParameter(1, principalId)
.setParameter(2, principalId)
.setParameter(3, pageUserId);
// 쿼리 실행 (qlrm 라이브러리 필요 = DTO에 DB결과를 매핑하기 위해)
JpaResultMapper result = new JpaResultMapper();
List<SubscribeDto> subscribeDtos = result.list(query, SubscribeDto.class);
return subscribeDtos;
}
@Transactional
public void 구독하기(int fromUserId, int toUserId) {
try {
subscribeRepository.mSubscribe(fromUserId, toUserId);
} catch (Exception e) {
throw new CustomApiException("이미 구독을 하였습니다.");
}
}
@Transactional
public void 구독취소하기(int fromUserId, int toUserId) {
subscribeRepository.mUnSubscribe(fromUserId, toUserId);
}
}
59. 구독정보 뷰 렌더링-구독모달 정보 렌더링 완료
profile.js
function getSubscribeModalItem(u) {
let item = `<div class="subscribe__item" id="subscribeModalItem-${u.userId}">
<div class="subscribe__img">
<img src="/upload/${u.profileImageUrl}" alt="" onerror="this.src='/images/person.jpeg'"/>
</div>
<div class="subscribe__text">
<h2>${u.username}</h2>
</div>
<div class="subscribe__btn">`;
if (!u.equalState) {
if (u.subscribeState) {
item += `<button class="cta blue" onclick="toggleSubscribe(this)">구독취소</button>`;
} else {
item += `<button class="cta" onclick="toggleSubscribe(this)">구독하기</button>`;
}
}
item += `
</div>
</div>`;
return item;
}
60. 구독정보 뷰 렌더링-구독 모달 정보에서 구독 및 구독취소 구현하기
profile.js
function getSubscribeModalItem(u) {
let item = `<div class="subscribe__item" id="subscribeModalItem-${u.userId}">
<div class="subscribe__img">
<img src="/upload/${u.profileImageUrl}" alt="" onerror="this.src='/images/person.jpeg'"/>
</div>
<div class="subscribe__text">
<h2>${u.username}</h2>
</div>
<div class="subscribe__btn">`;
if (!u.equalState) {
if (u.subscribeState) {
item += `<button class="cta blue" onclick="toggleSubscribe(${u.userId}, this)">구독취소</button>`;
} else {
item += `<button class="cta" onclick="toggleSubscribe(${u.userId}, this)">구독하기</button>`;
}
}
item += `
</div>
</div>`;
return item;
}
'포토그램' 카테고리의 다른 글
[Chapter 8] 좋아요 구현하기 (0) | 2024.02.25 |
---|---|
[Chapter 7] 스토리 페이지 (0) | 2024.02.22 |
[Chapter 5] 프로필 페이지 (0) | 2024.02.22 |
[Chapter 4] 구독하기 (0) | 2024.02.22 |
[Chapter 3 Part 2] 포토그램 인증 (0) | 2024.02.22 |