Member를 만들던 것과 비슷하므로 추가적인 개념이 있다면 그 구분에 대해서만 설명하겠습니다.

TeamFounder = Team의 요구사항과 같다고 할 수 있습니다.

  1. TeamFounder는 하나의 Team만 만들 수 있습니다.
  2. TeamFounder는 Member가 신청한 리스트를 확인할 수 있으며 수락이 가능합니다.
  3. TeamFounder는 Team해체가 가능합니다.
  4. TeamFounder는 League 참가 신청을 할 수 있습니다.
  5. LeagueHost가 수락을 해야 League에 참가가 완료됩니다.
  6. 여러 리그에 신청이 가능하지만 한 League에만 참석이 가능합니다.

1. TeamService, TeamServiceImpl

  • TeamRepository 추가
public interface TeamRepository extends JpaRepository<Team, Long> {

    @Query("select t from Team t where t.name= :teamName")
    Optional<Team> findByName(@Param("teamName") String teamName);

}
  • TeamDto 
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TeamDto {

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class CreateOrUpdateTeamDto{
        public String name;
        public int size;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class RestTeamDto {
        public String teamName;
        public Integer maxSize;
        public Integer nowSize;
    }

}
  • 추가 사항을 활용하여 Team 만들기
@Override
    public Long createTeam(String name, int size, Long userId) {
        User user = userRepository.findById(userId).get();
        checkDuplicateTeamName(name);
        Team team = Team.createTeam(user, name, size);
        return teamRepository.save(team).getId();
    }

	private void checkDuplicateTeamName(String name) {
        teamRepository.findByName(name).ifPresent(
                a -> {
                    throw new DuplicateTeamNameException("이미 같은 이름의 팀이 있습니다.");
                }
        );
    }

 

  • Team.createTeam()을 이용하여 User(TeamFounder)와의 연관 관계를 설정하며, Team을 저장해줍니다.
  • 단 중복되는 Team이름을 예외 처리는 해줍니다. 

  • RequestTeamRepository query 추가
@Query("select re from RequestTeam  re join fetch re.team where re.team.id= :teamId")
List<RequestTeam> findByTeamId(@Param("teamId") Long teamId, Sort sort);
  • 추가 사항을 활용하여 Team에게 Member들이 신청한 리스트 보기
@Override
public List<RequestTeamDto> findRequestList(Long teamId) { //팀에 신청한 request 목록
    return requestTeamRepository.findByTeamId(teamId, Sort.by(Sort.Direction.DESC
            , "createdTime")).stream().map(a -> new RequestTeamDto(a.getRequest(),a.getTeam().getName()))
            .collect(Collectors.toList());
}
  •  fetch join을 통하여 최적화합니다.
  • Sort를 이용하여 요청한 시간 순서대로 RequestTeam을 받고 stream().map()을 사용하고, Entity 외부 노출을 숨기기 위해 List<RequestTeamDto>로 변경해줍니다.

  • RequestTeamRepository query 추가
@Query("select re from RequestTeam re join fetch re.user join fetch re.team where re.id= :requestId")
Optional<RequestTeam> findWithFetchJoinUserById(@Param("requestId")Long requestId);
  • RequestTeam 비즈니스 로직 추가
public void acceptRequest(){
        request=true;
        team.setUser(user);
        team.addTeam(user);
        team.increaseSize();
}
  • Team 비즈니스 로직 추가
public void increaseSize(){
    nowSize++;
}
  • AlreadyAcceptMemberException 추가
public class AlreadyAcceptMemberException extends RuntimeException{
    public AlreadyAcceptMemberException() {
        super();
    }

    public AlreadyAcceptMemberException(String message) {
        super(message);
    }

    public AlreadyAcceptMemberException(String message, Throwable cause) {
        super(message, cause);
    }

    public AlreadyAcceptMemberException(Throwable cause) {
        super(cause);
    }

    protected AlreadyAcceptMemberException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
  • 추사 사항을 활용하여 Team이 Member의 가입 신청을 받는 로직입니다.
@Override
    public void acceptMember(Long requestTeamId) {
        RequestTeam requestTeam = requestTeamRepository.findWithFetchJoinUserById(requestTeamId).orElseThrow(
                () -> {
                    throw new NotExistRequestException("존재하지 않는 요청입니다.");
                }
        );
        User user = requestTeam.getUser();
        if (user.getTeam()!=null){
            throw new AlreadyTeamException("이미 팀에 속해있습니다.");
        }
        if(requestTeam.getRequest()){
            throw new AlreadyAcceptMemberException("이미 승인된 요청입니다.");
        }
        requestTeam.acceptRequest();
    }
  • fetch join을 이용하여 최적화합니다.
  • 만약 요청을 한 Member가 팀을 이미 가지고 있다면 여러 팀에 속할 수 없으므로 예외를 터트립니다.
  • requestTeam은 영속성 콘테스트에서 관리하는 객체이므로 requestTeam과 team에 대한 비즈니스 로직을 이용하여 dirty checking을 이용해줍니다.

  • Team에 비즈니스 로직을 추가해줍니다.
public void removeTeam(){
    for (User user: userList){
        user.setTeam(null);
    }
    userList=null;
}

 

  • 추가 사항을 활용하여 자신의 팀을 삭제하는 로직입니다. 
@Override
public void deleteTeam(Long teamId) {
    Team team = teamRepository.findById(teamId).get();
    team.removeTeam();
    teamRepository.deleteById(teamId);
}
  • 마찬가지로 dirty checking을 이용합니다.

  • Team에 비즈니스 로직을 추가합니다.
public void update(String name, int size){
        this.name=name;
        this.maxSize=size;
    }
  • 추가 사항을 활용하여 TeamFounder가 자신의 팀의 이름이나 팀 규모를 바꾸는 것입니다.
@Override
    public Long updateTeam(String name, int size, Long teamId, Long userId) {
        User user = userRepository.findById(userId).get();
        Team team = teamRepository.findById(teamId).get();
        checkDuplicateTeamName(name);
        team.update(name,size);
        return team.getId();
    }

  • TeamRepository query 추가
@Query("select t from Team  t where t.maxSize>t.nowSize")
List<Team> findByRestSeat();
  • 위의 query를 통해 현재 팀 최대 규모를 다 채우지 못한 팀들의 리스트를 불러오는 로직입니다.
@Override
    public List<TeamDto.RestTeamDto> findRestTeamSeat() {
        return teamRepository.findByRestSeat().stream().map( a -> new TeamDto.RestTeamDto(a.getName(),a.getMaxSize(),a.getNowSize()))
                .collect(Collectors.toList());
    }

  • NotMatchingTeamNameException 추가
public class NotMatchingTeamNameException extends RuntimeException{
    public NotMatchingTeamNameException() {
        super();
    }

    public NotMatchingTeamNameException(String message) {
        super(message);
    }

    public NotMatchingTeamNameException(String message, Throwable cause) {
        super(message, cause);
    }

    public NotMatchingTeamNameException(Throwable cause) {
        super(cause);
    }

    protected NotMatchingTeamNameException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
  • Team의 이름을 가지고 검색을 하는 요청을 수행하는 로직입니다. 
@Override
public TeamDto.RestTeamDto findTeamByName(String teamName) {
    Team team = teamRepository.findByName(teamName).orElseThrow(
            () -> {
                    throw new NotMatchingTeamNameException("일치하는 이름의 팀이 없습니다.");
            }
    );

    return new TeamDto.RestTeamDto(team.getName(),team.getMaxSize(),team.getNowSize());
}
  • 검색한 팀의 이름이 없을 경우를 대비하여 예외를 작성합니다.

  • LeagueRepository 추가
public interface LeagueRepository extends JpaRepository<League, Long> {

}
  • RequestLeagueRepository 추가
public interface RequestLeagueRepository extends JpaRepository<RequestLeague, Long> {

    @Query("select re from RequestLeague re join fetch re.team where re.team.id= :teamId")
    Optional<RequestLeague> findByTeamId(@Param("teamId") Long teamId);

}
  • DuplicateRequestToLeagueException 추가
public class DuplicateRequestToLeagueException extends RuntimeException{
    public DuplicateRequestToLeagueException() {
        super();
    }

    public DuplicateRequestToLeagueException(String message) {
        super(message);
    }

    public DuplicateRequestToLeagueException(String message, Throwable cause) {
        super(message, cause);
    }

    public DuplicateRequestToLeagueException(Throwable cause) {
        super(cause);
    }

    protected DuplicateRequestToLeagueException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
  • 위의 추가한 사항들을 활용하여 Team이 Leauge에게 참가 신청을 하는 로직입니다. League에게 신청을 하므로 League와 RequestLeague Repository들이 필요합니다.
@Override
public Long requestLeague(Long leagueId, Long teamId) {
    League league = leagueRepository.findById(leagueId).get();
    Team team = teamRepository.findById(teamId).get();

    RequestLeague requestLeague = RequestLeague.createRequestLeague(team, league);

    if(team.getLeague()!=null){
        throw new AlreadyTeamException("이미 리그에 참가하고 있습니다.");
    }
    requestLeagueRepository.findByTeamId(teamId).ifPresent(
            a -> {
                throw new DuplicateRequestToLeagueException("이미 리그에 신청한 기록이 있습니다.");
            }
    );

    return requestLeagueRepository.save(requestLeague).getId();
}
  • Team이 이미 League에 속해있다면 예외를 터트립니다.
  • findByTeamId()를 fetch join을 이용하여 최적화하며, 이미 신청한 기록이 있다면 예외를 터트립니다.

  • RequestLeagueRepository query 추가
@Query(" select re from RequestLeague re join fetch re.team join fetch re.league where re.team.id= :teamId and " +
        "re.league.id= :leagueId")
Optional<RequestLeague> findByMemberIdAndTeamId(@Param("teamId") Long teamId, @Param("leagueId") Long leagueId);
  • RequestLeague의 비즈니스 로직 추가
public void removeRequest(){
    league.getRequestLeagueList().remove(this);
    team.getRequestLeagueList().remove(this);
}
  • 추가한 사항을 활용하여 Team이 League에 참가 신청을 취소할 수 있습니다.
@Override
public void withdrawalLeagueRequest(Long teamId, Long leagueId) {
    RequestLeague requestLeague = requestLeagueRepository.findByMemberIdAndTeamId(leagueId, teamId).get();
    requestLeague.removeRequest();
    requestLeagueRepository.deleteById(requestLeague.getId());
}
  • fetch join을 이용하여 최적화 하며, dirty checking을 이용하여 상태를 변경합니다.
  • requestLeagueRepository.deleteById()를 이용하여 삭제합니다.

2. TeamService Test code (Member Test code와 유사하므로 설명은 생략하겠습니다.)

@SpringBootTest
@Transactional
class TeamServiceImplTest {



    @Autowired
    TeamRepository teamRepository;
    @Autowired
    TeamService teamService;
    @Autowired
    UserRepository userRepository;
    @Autowired
    MemberService memberService;


    @Test
    void createTeamSuccess(){
        Address address = getAddress();
        User user = getTeamFounder1(address);
        Long teamFounderId = userRepository.save(user).getId();

        Long teamId = teamService.createTeam("상명", 10, teamFounderId);

        Team team = teamRepository.findById(teamId).get();

        assertThat(team.getMaxSize()).isEqualTo(10);
        assertThat(team.getEmail()).isEqualTo(user.getEmail());
        assertThat(team.getUserList().size()).isEqualTo(1);
    }

    @Test
    void createTeamFailDuplicateTeamName(){
        Address address = getAddress();
        User user = getTeamFounder1(address);
        Long teamFounderId1 = userRepository.save(user).getId();
        User user2 = getTeamFounder2(address);
        Long teamFounderId2 = userRepository.save(user2).getId();
        Long teamId = teamService.createTeam("상명", 10, teamFounderId1);

        assertThatThrownBy(() -> teamService.createTeam("상명",10,teamFounderId2))
                .isInstanceOf(DuplicateTeamNameException.class);
    }
    @Test
    void DeleteTeam(){
        Address address = getAddress();
        User user = getTeamFounder1(address);
        Long teamFounderId1 = userRepository.save(user).getId();
        Long teamId = teamService.createTeam("상명", 10, teamFounderId1);

        teamService.deleteTeam(teamId);

        assertThatThrownBy(() ->teamRepository.findById(teamId).orElseThrow(
                () ->
                {
                    throw new IllegalStateException("없는 팀입니다.");
                }
        )).isInstanceOf(IllegalStateException.class);
    }

    @Test
    void updateTeam(){
        Address address = getAddress();
        User user = getTeamFounder1(address);
        Long teamFounderId1 = userRepository.save(user).getId();
        Long teamId1 = teamService.createTeam("상명", 10, teamFounderId1);
        User user2 = getTeamFounder2(address);
        Long teamFounderId2 = userRepository.save(user2).getId();
        Long teamId2 = teamService.createTeam("국민", 10, teamFounderId1);

        Long updateId = teamService.updateTeam("경복", 10, teamId2, teamFounderId2);
        Team team = teamRepository.findById(updateId).get();

        assertThat(team.getEmail()).isEqualTo(user2.getEmail());
        assertThat(team.getName()).isEqualTo("경복");
    }

    @Test
    void updateTeamFailDuplicateName(){
        Address address = getAddress();
        User user = getTeamFounder1(address);
        Long teamFounderId1 = userRepository.save(user).getId();
        Long teamId1 = teamService.createTeam("상명", 10, teamFounderId1);
        User user2 = getTeamFounder2(address);
        Long teamFounderId2 = userRepository.save(user2).getId();
        Long teamId2 = teamService.createTeam("국민", 10, teamFounderId2);

        assertThatThrownBy(() -> teamService.updateTeam("상명", 10, teamId2, teamFounderId1))
                .isInstanceOf(DuplicateTeamNameException.class);
    }

    @Test
    void acceptRequest(){
        Address address = getAddress();
        User user1 = getTeamFounder1(address);
        Long teamFounderId = userRepository.save(user1).getId();
        Long teamId = teamService.createTeam("상명", 10, teamFounderId);
        User user2 = getMember(address);
        Long memberId = userRepository.save(user2).getId();
        Long requestTeamId = memberService.requestTeam(teamId, memberId);
        teamService.acceptMember(requestTeamId);
        Team team = teamRepository.findById(teamId).get();
        User resultUser = userRepository.findById(memberId).get();
        assertThat(resultUser.getTeam().getId()).isEqualTo(teamId);
        assertThat(resultUser.getTeam()).isEqualTo(team);
        assertThat(resultUser.getTeam().getNowSize()).isEqualTo(1);
    }

    @Test
    void findRequest(){
        Address address = getAddress();
        User user1 = getTeamFounder1(address);
        Long teamFounderId = userRepository.save(user1).getId();
        Long teamId = teamService.createTeam("상명", 10, teamFounderId);
        User user2 = getMember(address);
        Long memberId = userRepository.save(user2).getId();
        Long requestTeamId = memberService.requestTeam(teamId, memberId);

        List<RequestTeamDto> result = teamService.findRequestList(teamId);

        assertThat(result.size()).isEqualTo(1);
        assertThat(result.get(0).MemberName).isEqualTo("kim");
    }
    @Test
    void findRestTeamSeat(){
        Address address = getAddress();
        User user = getTeamFounder1(address);
        Long teamFounderId1 = userRepository.save(user).getId();
        Long teamId1 = teamService.createTeam("상명", 1, teamFounderId1);
        User user2 = getTeamFounder2(address);
        Long teamFounderId2 = userRepository.save(user2).getId();
        Long teamId2 = teamService.createTeam("국민", 10, teamFounderId2);
        User user3 = getMember(address);
        Long memberId = userRepository.save(user3).getId();
        Long requestTeamId = memberService.requestTeam(teamId1, memberId);
        teamService.acceptMember(requestTeamId);

        List<TeamDto.RestTeamDto> result = teamService.findRestTeamSeat();

        assertThat(result.size()).isEqualTo(1);
        assertThat(result.get(0).getTeamName()).isEqualTo("국민");
    }

    @Test
    void findTeamByNameSuccess(){
        Address address = getAddress();
        User user = getTeamFounder1(address);
        Long teamFounderId1 = userRepository.save(user).getId();
        Long teamId1 = teamService.createTeam("상명", 10, teamFounderId1);
        TeamDto.RestTeamDto result = teamService.findTeamByName("상명");

        assertThat(result.getTeamName()).isEqualTo("상명");
        assertThat(result.getMaxSize()).isEqualTo(10);
        assertThat(result.getNowSize()).isEqualTo(0);
    }

    @Test
    void findTeamByNameFailNotExistName(){
        Address address = getAddress();
        User user = getTeamFounder1(address);
        Long teamFounderId1 = userRepository.save(user).getId();
        Long teamId1 = teamService.createTeam("상명", 10, teamFounderId1);

        assertThatThrownBy(() -> teamService.findTeamByName("국민"))
                .isInstanceOf(NotMatchingTeamNameException.class);
    }


    private Address getAddress() {
        return Address.builder()
                .street("s")
                .city("a")
                .build();
    }
    private User getTeamFounder1(Address address) {
        return User.createUser("rkdlem48", "asdf", "kim", address, ROLE.TEAM_FOUNDER);
    }
    private User getTeamFounder2(Address address){
        return User.createUser("rkdlem49", "asdf", "kim", address, ROLE.TEAM_FOUNDER);
    }
    private User getMember(Address address){
        return User.createUser("rkdlem48", "asdf", "kim", address, ROLE.MEMBER);
    }
}

지금까지 TeamService에서 실행되는 로직들과 로직에 필요한 Repository, query 들을 만들어봤습니다. Member를 만들 때와 유사하여 설명을 많이 생략한 점 이해해주시면 감사하겠습니다. 다음으로는 TeamController, TeamControllerAdvice, TeamInterceptor를 만들어보겠습니다. 감사합니다. 

 

모든 코드는 아래 링크에서 확인 가능합니다.

https://github.com/rlaehdals/blogProject 

 

GitHub - rlaehdals/blogProject

Contribute to rlaehdals/blogProject development by creating an account on GitHub.

github.com