기능 구현
오늘은 친구 기능과 북마크 기능을 구현하였다.
친구 기능
친구 기능은 구현할 때 테이블 설계에 두 가지 선택 사항이 있었다.
- 친구 테이블 하나로 할지
- 친구 테이블과 친구 요청 테이블을 분리할지
이번에는 친구 테이블 하나로 관리를 해줬다. 요청한 유저의 아이디와 요청받은 유저의 아이디, 그리고 enum으로 Waiting과 accepted상태를 관리해주었다.
유저 테이블과 친구 테이블은 1:N관계로 설계했다.
처음에는 url를 만들 때 애를 먹었던게 친구 삭제와 친구 요청 거절이 적고 보니까 똑같이 /api/friends/{id} 였다. 그래서 고민 끝에 요청, 요청 거절은 이름을 다르게 했다.
public class FriendshipController {
private final FriendshipService friendshipService;
/**
* 다른 유저에게 친구요청
*
* @param userDetails 현재 로그인 유저
* @param requestDto receiver 아이디 필요
* @return
*/
@PostMapping("/api/friend-requests")
public ResponseEntity<Void> addFriend(@AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestBody FriendRequestDto requestDto) {
friendshipService.requestFriendship(userDetails.getUser(), requestDto);
return ResponseEntity.ok()
.build();
}
/**
* 친구요청 수락
*
* @param userDetails 현재 로그인 유저
* @param requesterId 요청한 유저 아이디
* @return
*/
@PutMapping("/api/friend-requests/{requesterId}/accept")
public ResponseEntity<Void> acceptFriendship(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@PathVariable("requesterId") Long requesterId) {
friendshipService.acceptFriendShip(userDetails.getUser(), requesterId);
return ResponseEntity.ok()
.build();
}
/**
* 친구요청 거절
*
* @param userDetails 현재 로그인 유저
* @param requesterId 요청한 유저 아이디
*/
@DeleteMapping("/api/friend-requests/{requesterId}/reject")
public ResponseEntity<Void> rejectFriendRequest(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@PathVariable("requesterId") Long requesterId) {
friendshipService.rejectFriendshipRequest(userDetails.getUser(), requesterId);
return ResponseEntity.ok()
.build();
}
/**
* 친구요청 목록
*
* @param userDetails 현재 로그인 유저
*/
@GetMapping("/api/friend-requests")
public ResponseEntity<List<FriendshipReceiveDto>> getRecieveList(
@AuthenticationPrincipal UserDetailsImpl userDetails) {
return ResponseEntity.ok(friendshipService.getRecieveList(userDetails.getUser()));
}
/**
* 친구목록 가져오기
*
* @param userDetails 현재 로그인 유저
*/
@GetMapping("/api/friends")
public ResponseEntity<List<FriendResponseDto>> getFriends(
@AuthenticationPrincipal UserDetailsImpl userDetails) {
return ResponseEntity.ok(friendshipService.getFriendAll(userDetails.getUser()));
}
/**
* 친구 삭제
*
* @param userDetails 현재 로그인 유저
* @param id 삭제할 친구(유저) 아이디
*/
@DeleteMapping("/api/friends/{id}")
public ResponseEntity<Void> removeFriend(@AuthenticationPrincipal UserDetailsImpl userDetails,
@PathVariable("id") Long id) {
friendshipService.removeFriendship(userDetails.getUser(), id);
return ResponseEntity.ok()
.build();
}
/**
* 모든 친구 요청 및 친구 삭제
*
* @param userDetails
*/
@DeleteMapping("/api/friends")
public ResponseEntity<Void> removeAllFriend(
@AuthenticationPrincipal UserDetailsImpl userDetails) {
friendshipService.removeAllFriendship(userDetails.getUser());
return ResponseEntity.ok()
.build();
}
}
@Service
@RequiredArgsConstructor
@Transactional
public class FriendshipService {
private final UserRepository userRepository;
private final FriendShipRepository friendShipRepository;
/**
* 친구 요청
*
* @param user 현재 로그인 유저
* @param requestDto 친구요청 받을 유저의 아이디
*/
public void requestFriendship(User user, FriendRequestDto requestDto) {
User receiver = getUserOrElseThrow(requestDto.getReceiverId());
Friendship friendship = new Friendship(user, receiver);
friendShipRepository.save(friendship);
}
/**
* 친구요청 거절
*
* @param user 현재 로그인 유저
* @param requesterId 친구요청한 유저의 아이디
*/
public void rejectFriendshipRequest(User user, Long requesterId) {
User requester = getUserOrElseThrow(requesterId);
boolean isExists = friendShipRepository.existsByRequesterAndReceiverAndStatus(requester,
user, FriendShipStatus.ACCEPTED);
if (isExists) {
throw new AlreadyAcceptedException();
}
friendShipRepository.deleteByRequesterAndReceiver(requester, user);
}
/**
* 친구요청 수락
*
* @param user 현재 로그인 유저
* @param requesterId 친구요청한 유저의 아이디
*/
public void acceptFriendShip(User user, Long requesterId) {
User requester = getUserOrElseThrow(requesterId);
Friendship request = friendShipRepository.findByRequesterAndReceiver(requester, user)
.orElseThrow(() -> new NoSuchResourceException("수락할 요청이 없습니다."));
if (request.getStatus() == FriendShipStatus.ACCEPTED) {
throw new AlreadyAcceptedException();
}
Friendship receive = new Friendship(user, requester);
// 서로 요청 상태 accepted로 변경
request.accept();
receive.accept();
// 서로 연결
friendShipRepository.save(receive);
}
/**
* 친구목록 가져오기, ACCEPTED 상태인 친구들만 가져옵니다.
*
* @param user 현재 로그인 유저
*/
@Transactional(readOnly = true)
public List<FriendResponseDto> getFriendAll(User user) {
List<Friendship> friends = friendShipRepository.findAllByReceiverAndStatus(user,
FriendShipStatus.ACCEPTED);
return friends.stream()
.map(x -> new FriendResponseDto(x.getRequester()))
.toList();
}
/**
* 친구삭제 or 요청 거절
*
* @param user 현재 로그인 유저
* @param removeUserId 삭제(거절)할 친구(유저) 아이디
*/
public void removeFriendship(User user, Long removeUserId) {
User removeUser = getUserOrElseThrow(removeUserId);
friendShipRepository.deleteByRequesterAndReceiver(removeUser, user);
friendShipRepository.deleteByRequesterAndReceiver(user, removeUser);
}
/**
* 친구요청 목록
*
* @param user
*/
public List<FriendshipReceiveDto> getRecieveList(User user) {
List<Friendship> findAll = friendShipRepository.findAllByReceiverAndStatus(user,
FriendShipStatus.WAITING);
return findAll.stream()
.map(x -> FriendshipReceiveDto.builder()
.friendshipId(x.getId())
.requesterId(x.getRequester()
.getId())
.receiverId(user.getId())
.build())
.toList();
}
/**
* 친구 요청, 친구 요청받은 것, 친구 관계 삭제
*
* @param user 현재 로그인한 유저
*/
public void removeAllFriendship(User user) {
friendShipRepository.deleteAllByRequesterOrReceiver(user, user);
}
private User getUserOrElseThrow(Long id) {
return userRepository.findById(id)
.orElseThrow(InvalidIdException::new);
}
}
북마크 기능
필수 구현이 끝나서 추가 기능으로 무엇을 할 지 생각해보다가 우리가 하는 컨셉에 북마크 기능이 어울릴 것 같아서 구현을 해보았다. 북마크는 간단하게 유저의 아이디와 보드의 아이디를 가지고 있다.
@RestController
@RequiredArgsConstructor
public class BookmarkController {
private final BookmarkService bookmarkService;
@PostMapping("/api/boards/{id}/bookmarks")
private ResponseEntity<Void> bookmarking(@AuthenticationPrincipal UserDetailsImpl userDetails,
@PathVariable("id") Long id) {
bookmarkService.addBookmark(userDetails.getUser(), id);
return ResponseEntity.status(HttpStatus.CREATED)
.build();
}
@DeleteMapping("/api/boards/{id}/bookmarks")
private ResponseEntity<Void> deleteBookmark(
@AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable("id") Long id) {
bookmarkService.removeBookmark(userDetails.getUser(), id);
return ResponseEntity.ok()
.build();
}
@DeleteMapping("/api/bookmarks")
private ResponseEntity<Void> deleteAllBookmarks(
@AuthenticationPrincipal UserDetailsImpl userDetails) {
bookmarkService.removeAllBookmark(userDetails.getUser());
return ResponseEntity.ok()
.build();
}
@GetMapping("/api/bookmarks")
private ResponseEntity<Page<BoardResponseDto>> getBookmarkBoards(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@PageableDefault(size = 10, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {
Page<BoardResponseDto> page = bookmarkService.getAllBookmarkedBoards(userDetails.getUser(),
pageable);
return ResponseEntity.ok(page);
}
}
@Service
@RequiredArgsConstructor
@Transactional
public class BookmarkService {
private final BookmarkRepository bookmarkRepository;
private final BoardRepository boardRepository;
/**
* 유저의 북마크에 보드를 추가합니다.
*
* @param user 현재 로그인 유저
* @param boardId 추가할 보드 아이디
*/
public void addBookmark(User user, Long boardId) {
Board board = getBoard(boardId);
Bookmark bookmark = new Bookmark(user, board);
bookmarkRepository.save(bookmark);
}
/**
* 유저의 북마크에서 해당 보드를 제거합니다.
*
* @param user 현재 로그인 유저
* @param boardId 제거할 보드 아이디
*/
public void removeBookmark(User user, Long boardId) {
bookmarkRepository.deleteByUserIdAndBoardId(user.getId(), boardId);
}
/**
* 유저가 북마크에 저장한 글들을 페이징 해서 가져옵니다.
*
* @param user 현재 로그인 유저
* @param pageable 페이징
* @return
*/
public Page<BoardResponseDto> getAllBookmarkedBoards(User user, Pageable pageable) {
Page<Bookmark> bookmarks = bookmarkRepository.findAllByUser(user, pageable);
return bookmarks.map(x -> new BoardResponseDto(x.getBoard(), x.getUser()));
}
/**
* 유저가 저장한 모든 북마크를 삭제합니다.
*
* @param user 현재 로그인 유저
*/
public void removeAllBookmark(User user) {
if (!bookmarkRepository.existsByUser(user)) {
throw new NoSuchResourceException();
}
bookmarkRepository.deleteAllByUser(user);
}
private Board getBoard(Long boardId) {
Board board = boardRepository.findById(boardId)
.orElseThrow(InvalidIdException::new);
return board;
}
}
'부트캠프 > Dev' 카테고리의 다른 글
뉴스피드 프로젝트 회고 (2) | 2024.09.06 |
---|---|
뉴스피드 프로젝트 4일차 (1) | 2024.09.05 |
뉴스피드 프로젝트 2일차 (2) | 2024.09.04 |
뉴스피드 프로젝트 1일차 (0) | 2024.09.04 |
필터에서 예외처리 (0) | 2024.08.20 |