728x90
들어가며
여기까지 등록, 조회, 변경이 가능한 회원정보를 구현했고, 변경내역을 저장하며 회원이라는 도메인을 정의했습니다.
이번에는 회원들이 서비스를 이용하면서 다루게 되는 기능들을 만들어 볼 계획입니다.
사용되는 기술은 전과 같습니다.
Java 17
Spring Boot 3.x
MySQL 8.x
요구사항
팔로워와 팔로잉기능을 구현합니다.
단, 자기 자신에 대한 팔로우는 할 수 없습니다.
-> 팔로우를 하는 사람과 받는 사람 모두 "회원"이기 때문에 회원 도메인의 정보를 받아와야 합니다.
-> 따라서 다른 도메인과 연결고리 역할을 하는 클래스가 필요합니다.
그리고 팔로우 목록을 조회하는 기능을 구현합니다.
팔로우 기능
@Service
@RequiredArgsConstructor
public class FollowMemberUsecase {
private final MemberReadService memberReadService;
private final FollowWriteService followWriteService;
public void excute(Long fromId, Long toId) {
var fromMember = memberReadService.getMember(fromId);
var toMember = memberReadService.getMember(toId);
followWriteService.createFollow(fromMember, toMember);
}
}
Usecase를 두면서 도메인 간의 흐름을 제어해줍니다. 앞서 Read와 Write로 서비스를 구분했었기 때문에 Usecase에 주입하는 과정에서 명료하게 전달할 수 있고 가독성도 좋습니다.
Usecase는 별도의 로직은 가지지 않고 member도메인의 값을 follow도메인에서 사용할 수 있도록 이어주기만 합니다.
@Service
@RequiredArgsConstructor
public class FollowWriteService {
private final FollowRepository followRepository;
public Follow createFollow(MemberDto fromMember, MemberDto toMember) {
Assert.isTrue(!fromMember.id().equals(toMember.id()), "자기 자신을 팔로우 할 수 없습니다.");
var follow = Follow.builder()
.fromMemberId(fromMember.id())
.toMemberId(toMember.id())
.build();
return followRepository.save(follow);
}
}
Assert.isTrue로 유효성 검사 후에 빌더 패턴을 사용해 follow 엔터티를 생성하고 Repository.save메서드로 데이터베이스에 저장하였습니다.
빌더 패턴에 대해서:
@RequiredArgsConstructor
@RestController
@RequestMapping("/follows")
public class FollowController {
private final FollowMemberUsecase followMemberUsecase;
@PostMapping("/{fromId}/{toId}")
public void createFollow(@PathVariable Long fromId, @PathVariable Long toId) {
followMemberUsecase.excute(fromId, toId);
}
}
간단하게 컨트롤러 작성 후 Swagger UI로 동작을 확인해보니, 잘 동작하고 있습니다.
팔로우 목록 조회
private static final RowMapper<Follow> rowMapper = (rs, rowNum) -> Follow.builder()
.id(rs.getLong("id"))
.fromMemberId(rs.getLong("fromMemberId"))
.toMemberId(rs.getLong("toMemberId"))
.createdAt(rs.getObject("createdAt", LocalDate.class))
.build();
public List<Follow> findAllByFromMemberId(Long fromMemberId) {
var sql = "select * from " + Table + " where fromMemberId = :fromMemberId";
var params = new MapSqlParameterSource().addValue("fromMemberId", fromMemberId);
return jdbcTemplate.query(sql, params, rowMapper);
}
Repository에서는 RowMapper와 NamedParameterJdbcTemplate를 통해 follow 객체를 생성하고 엔티티를 설정합니다.
즉, fromMemberId를 기준으로 데이터베이스에서 팔로우 목록을 조회하고, 조회된 결과를 Follow 엔티티로 변환하여 반환합니다.
똑같은 방법으로 toMemberId에 대한 follow 객체도 생성합니다.
@Service
@RequiredArgsConstructor
public class FollowReadService {
private final FollowRepository followRepository;
public List<Follow> getFollowing(Long memberId) {
return followRepository.findAllByFromMemberId(memberId);
}
public List<Follow> getFollower(Long memberId) {
return followRepository.findAllByToMemberId(memberId);
}
}
FromMemberId를 기준으로 하는 getFollowing 메서드와 ToMemberId를 기준으로 하는 getFollower메서드를 Service에서 정의합니다.
public List<MemberDto> excute(Long memberId) {
/*
memberId -> following을 조회
*/
var followings = followReadService.getFollowing(memberId);
var followingMemberIds = followings.stream()
.map(Follow::getToMemberId)
.toList();
return memberReadService.getMembers(followingMemberIds);
}
memberId(fromMemberId)에 해당하는 회원이 팔로우하는 회원들의 목록을 가져옵니다.
followings 목록에서 각각의 팔로우 정보에서 toMemberId를 추출하여 리스트로 만듭니다.
toMemberId리스트를 이용해서 얻은 정보를 memberReadService의 getMembers 메서드를 통해 리스트를 반환합니다.
주어진 회원이 팔로우하는 회원들의 정보를 조회하고, 이를 MemberDto로 변환하여 반환하고 있습니다.
@GetMapping("/members/{fromId}")
public List<MemberDto> getFollowing(@PathVariable Long fromId) {
return getFollowingUsecase.excute(fromId);
}
}
컨트롤러에서는 Usecase에서 정의한 excute메서드를 통해 fromId 요청을 처리하고 있습니다.
만약 fromId에 대해 빈 리스트가 반환된다면?
public List<Member> findAllByIdIn(List<Long> ids) {
if (ids.isEmpty()) {
return List.of();
}
finalAllByIdn에 if 함수를 통해 빈 리스트 반환 값에 대해 []를 반환하여 오류를 막았습니다.
'DB(MySQL, MongoDB, Redis, Kafka) > MySQL' 카테고리의 다른 글
MySQL 페이지네이션 최적화 (1) | 2024.01.01 |
---|---|
MySQL 조회 최적화를 위한 인덱스 테스트 [EazyRandom] (0) | 2023.12.29 |
MySQL 테스트를 위한 SNS서비스 회원이름 변경 및 변경내역 조회 (0) | 2023.12.27 |
MySQL 테스트를 위한 SNS서비스 회원정보 조회 (0) | 2023.12.26 |
MySQL 테스트를 위한 SNS서비스 회원정보 등록 (0) | 2023.12.26 |