Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[COZY-259] feat : 방로직 전체적 수정 및 방나가기 구현 #111

Merged
merged 6 commits into from
Oct 16, 2024

Conversation

suuu0719
Copy link
Contributor

@suuu0719 suuu0719 commented Oct 11, 2024

#️⃣ 요약 설명

  • 공개방/초대코드 방 만들기 구분
    • 공개방
      • 공개방은 해시태그 1~3개 작성해야함
      • 공개방은 정원 모두 입장할때까지 WAITING 상태
    • 비공개방
      • 해시태그 기능 없음
      • 만들어지는 순간 ENABLE 상태가 된다
    • 방 이름 중복 검사 추가
  • 해시태그
    • Room, RoomHashtag, Hashtag로 엔티티 분리함
    • RoomHashtag에서 Room과 Hashtag 다대다 매핑
  • 방 나가기
    • 모두 나가기 전까지 방은 삭제 되지 않는다.
    • 최후의 1인이 나가는 순간 방이 사라진다 → 이때 데이터를 다 날림
    • isExit 사용, entryStatus에 EXITED 추가 (밑에 자세히..)
  • 방 입장
    • 방나가기를 만들면서 재입장을 고려해야함
    • 재입장하는 경우/처음 입장하는 경우로 구분

📝 작업 내용

공개방/초대코드 방 만들기

// RoomController
@PostMapping("/create-private")
    @Operation(summary = "[바니] 초대코드로 방생성 기능", description = "방이름, 프로필이미지, 인원수를 입력합니다.")
    @SwaggerApiError({
        ErrorStatus._MEMBER_NOT_FOUND,
        ErrorStatus._ROOM_ALREADY_EXISTS
    })
    public ResponseEntity<ApiResponse<RoomCreateResponse>> createRoom(@Valid @RequestBody RoomCreateRequest request,
        @AuthenticationPrincipal MemberDetails memberDetails) {
        RoomCreateResponse response = roomCommandService.createPrivateRoom(request, memberDetails.getMember());
        return ResponseEntity.ok(ApiResponse.onSuccess(response));
    }

    @PostMapping("/create-public")
    @Operation(summary = "[바니]공개 방 생성 기능", description = "방이름, 프로필이미지, 인원수, 해시태그(1-3개)를 입력합니다.")
    @SwaggerApiError({
        ErrorStatus._MEMBER_NOT_FOUND,
        ErrorStatus._ROOM_ALREADY_EXISTS,
        ErrorStatus._DUPLICATE_HASHTAGS
    })
    public ResponseEntity<ApiResponse<RoomCreateResponse>> createPublicRoom(
        @Valid @RequestBody PublicRoomCreateRequest request,
        @AuthenticationPrincipal MemberDetails memberDetails) {
        RoomCreateResponse response = roomCommandService.createPublicRoom(request, memberDetails.getMember());
        return ResponseEntity.ok(ApiResponse.onSuccess(response));
    }
 // RoomCommandService
 // 공개방 코드
public RoomCreateResponse createPrivateRoom(RoomCreateRequest request, Member member) {
        Member creator = memberRepository.findById(member.getId())
            .orElseThrow(() -> new GeneralException(ErrorStatus._MEMBER_NOT_FOUND));

       // 방 입장 대기(PENDING) 상태는 제외하고, 다른 방에 JOIN했을 때만 이미 참여한 방이 있다는 에러 발생하도록 수정 
        if (roomRepository.existsByMemberIdAndStatuses(creator.getId(), RoomStatus.ENABLE,
            RoomStatus.WAITING, EntryStatus.JOINED)) {
            throw new GeneralException(ErrorStatus._ROOM_ALREADY_EXISTS);
        }

        String inviteCode = generateUniqueUppercaseKey();
        Room room = RoomConverter.toPrivateRoom(request, inviteCode);
        room = roomRepository.save(room);
        roomLogCommandService.addRoomLogCreationRoom(room);

        Mate mate = MateConverter.toEntity(room, creator, true);
        mateRepository.save(mate);

        Feed feed = FeedConverter.toEntity(room);
        feedRepository.save(feed);

        return roomQueryService.getRoomById(room.getId(), member.getId());
    }

// 초대코드 방 만들기 코드
public RoomCreateResponse createPublicRoom(PublicRoomCreateRequest request, Member member) {
        Member creator = memberRepository.findById(member.getId())
            .orElseThrow(() -> new GeneralException(ErrorStatus._MEMBER_NOT_FOUND));

        if (roomRepository.existsByMemberIdAndStatuses(creator.getId(), RoomStatus.ENABLE,
            RoomStatus.WAITING, EntryStatus.JOINED)) {
            throw new GeneralException(ErrorStatus._ROOM_ALREADY_EXISTS);
        }

        String inviteCode = generateUniqueUppercaseKey();
        Room room = RoomConverter.toPublicRoom(request, inviteCode);

        // 해시태그 저장 과정
        roomHashtagCommandService.createRoomHashtag(room, request.getHashtags());
        room = roomRepository.save(room);
        roomLogCommandService.addRoomLogCreationRoom(room);

        Mate mate = MateConverter.toEntity(room, creator, true);
        mateRepository.save(mate);

        Feed feed = FeedConverter.toEntity(room);
        feedRepository.save(feed);

        return roomQueryService.getRoomById(room.getId(), member.getId());
    }
 // Hashtag 엔티티
public class Hashtag extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String hashtag;

    public Hashtag(String hashtag) {
        this.hashtag = hashtag;
    }

}
// RoomHashtag 엔티티
public class RoomHashtag extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = LAZY, cascade = CascadeType.ALL)
    private Room room;

    @ManyToOne(fetch = LAZY)
    private Hashtag hashtag;

}

// Room 엔티티 수정
    @Enumerated(EnumType.STRING)
    private RoomType roomType;

    @OneToMany(mappedBy = "room", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<RoomHashtag> roomHashtags;
 // PublicRoomCreateRequest
public class PublicRoomCreateRequest {

    @NotBlank
    @Size(max=12)
    @Pattern(regexp = "^(?!\\s)[가-힣a-zA-Z0-9\\s]+(?<!\\s)$", message = "한글, 영어, 숫자 및 공백만 입력해주세요. 단, 공백은 처음이나 끝에 올 수 없습니다.")
    private String name;
    @NotNull
    @Min(0)
    @Max(15)
    private Integer profileImage;
    @NotNull
    @Min(2)
    @Max(6)
    private Integer maxMateNum;
    @Size(min = 1, max = 3, message = "해시태그는 1개에서 3개까지 입력할 수 있습니다.")
    private List<@NotBlank @Pattern(regexp = "^(?!_)[가-힣a-zA-Z0-9]+(_[가-힣a-zA-Z0-9]+)*(?<!_)$",
        message = "해시태그는 한글, 영어, 숫자 및 '_'만 사용할 수 있으며, '_'는 앞이나 뒤에 올 수 없습니다.") String> hashtags;

}

해시태그 저장 과정

// RoomHashtagCommandService
public void createRoomHashtag(Room room, List<String> hashtags) {
        validateHashtags(hashtags);

        for (String tag : hashtags) {
            Hashtag hashtag = hashtagRepository.findByHashtag(tag)
                .orElseGet( ()-> hashtagRepository.save(new Hashtag(tag)));

            RoomHashtag roomHashtag = RoomHashtagConverter.toRoomHashtag(room, hashtag);
            roomHashtagRepository.save(roomHashtag);
        }

    }

    // 입력한 해시태그 중복 검사
    public void validateHashtags(List<String> hashtags) {
        Set<String> uniqueHashtags = new HashSet<>(hashtags);
        if (uniqueHashtags.size() != hashtags.size()) {
            throw new GeneralException(ErrorStatus._DUPLICATE_HASHTAGS);
        }
    }

// RoomHashtagConverter
public class RoomHashtagConverter {
    public static RoomHashtag toRoomHashtag(Room room, Hashtag hashtag) {
        return RoomHashtag.builder()
            .room(room)
            .hashtag(hashtag)
            .build();
    }
}
// RoomCreateResponse
// Create Response 수정
public class RoomCreateResponse {

    private Long roomId;
    private String name;
    private String inviteCode;
    private Integer profileImage;
    List<CozymateInfoResponse> mateList;
    private RoomType roomType;
    private List<String> hashtags;
}

방 삭제/나가기

// RoomController
@DeleteMapping("/{roomId}")
    @Operation(summary = "[바니] 방 삭제 기능 (방장 권한)", description = "해당 roomId의 방을 삭제합니다.")
    @SwaggerApiError({
        ErrorStatus._MEMBER_NOT_FOUND,
        ErrorStatus._ROOM_NOT_FOUND,
        ErrorStatus._NOT_ROOM_MATE,
        ErrorStatus._ROOM_MANAGER_NOT_FOUND,
        ErrorStatus._NOT_ROOM_MANAGER
    })
    public ResponseEntity<ApiResponse<String>> deleteRoom(@PathVariable Long roomId,
        @AuthenticationPrincipal MemberDetails memberDetails) {
        roomCommandService.deleteRoom(roomId, memberDetails.getMember().getId());
        return ResponseEntity.ok(ApiResponse.onSuccess("방 삭제 완료"));
    }

    @PatchMapping("/{roomId}/quit")
    @Operation(summary = "[바니] 방 나가기 기능", description = "해당 roomId의 방을 나갑니다.")
    @SwaggerApiError({
        ErrorStatus._MEMBER_NOT_FOUND,
        ErrorStatus._ROOM_NOT_FOUND,
        ErrorStatus._NOT_ROOM_MATE
    })
    public ResponseEntity<ApiResponse<String>> quitRoom(@PathVariable Long roomId,
        @AuthenticationPrincipal MemberDetails memberDetails) {
        roomCommandService.quitRoom(roomId, memberDetails.getMember().getId());
        return ResponseEntity.ok(ApiResponse.onSuccess("방 나가기 완료"));
    }
// RoomCommandService
// 1. 방 삭제
public void deleteRoom(Long roomId, Long memberId) {
        memberRepository.findById(memberId)
            .orElseThrow(() -> new GeneralException(ErrorStatus._MEMBER_NOT_FOUND));

        Room room = roomRepository.findById(roomId)
            .orElseThrow(() -> new GeneralException(ErrorStatus._ROOM_NOT_FOUND));

        mateRepository.findByRoomIdAndMemberId(roomId, memberId)
            .orElseThrow(() -> new GeneralException(ErrorStatus._NOT_ROOM_MATE));

        Mate manager = mateRepository.findByRoomIdAndIsRoomManager(roomId, true)
            .orElseThrow(() -> new GeneralException(ErrorStatus._ROOM_MANAGER_NOT_FOUND));
        if (!manager.getMember().getId().equals(memberId)) {
            throw new GeneralException(ErrorStatus._NOT_ROOM_MANAGER);
        }

        // 연관된 Mate, Rule, RoomLog, Feed 엔티티 삭제
        deleteRoomDatas(roomId);
        roomRepository.delete(room);
    }
// 2. 방 나가기
public void quitRoom(Long roomId, Long memberId) {
        memberRepository.findById(memberId)
            .orElseThrow(() -> new GeneralException(ErrorStatus._MEMBER_NOT_FOUND));

        Room room = roomRepository.findById(roomId)
            .orElseThrow(() -> new GeneralException(ErrorStatus._ROOM_NOT_FOUND));

        Mate quittingMate = mateRepository.findByRoomIdAndMemberId(roomId, memberId)
            .orElseThrow(() -> new GeneralException(ErrorStatus._NOT_ROOM_MATE));

        // 이미 나간 방에 대한 예외 처리
        if (quittingMate.getEntryStatus() == EntryStatus.EXITED) {
            throw new GeneralException(ErrorStatus._NOT_ROOM_MATE);
        }

        quittingMate.quit();
        mateRepository.save(quittingMate);
        room.quit();
        roomRepository.save(room);

       // 마지막 룸메이트가 방 나가는 순간 방 데이터 삭제
        if (room.getNumOfArrival()==0) {
            // 연관된 Mate, Rule, RoomLog, Feed 엔티티 삭제
            deleteRoomDatas(roomId);
            roomRepository.delete(room);
        }
    }

// 3. 방 데이터 삭제 메소드
private void deleteRoomDatas(Long roomId) {
        List<Mate> mates = mateRepository.findByRoomId(roomId);
        for (Mate mate : mates) {
            roleRepository.deleteByMateId(mate.getId());
            todoRepository.deleteByMateId(mate.getId());
        }
        mateRepository.deleteByRoomId(roomId);
        ruleRepository.deleteByRoomId(roomId);
        roomLogRepository.deleteByRoomId(roomId);

        // 피드 삭제 로직
        if (feedRepository.existsByRoomId(roomId)) {
            Feed feed = feedRepository.findByRoomId(roomId);
            List<Post> posts = postRepository.findByFeedId(feed.getId());
            for (Post post : posts) {
                postCommentRepository.deleteByPostId(post.getId());
                postImageRepository.deleteByPostId(post.getId());
            }
            postRepository.deleteByFeedId(feed.getId());
            feedRepository.deleteByRoomId(roomId);
        }
    }

Mate의 EntryStatus에 EXITED 추가

public enum EntryStatus {
    PENDING,
    JOINED,
    EXITED
}

재입장을 고려하여 방 입장 기능 수정

// RoomCommandService
public void joinRoom(Long roomId, Long memberId) {
        Member member = memberRepository.findById(memberId)
            .orElseThrow(() -> new GeneralException(ErrorStatus._MEMBER_NOT_FOUND));

        Room room = roomRepository.findById(roomId)
            .orElseThrow(() -> new GeneralException(ErrorStatus._ROOM_NOT_FOUND));

        Optional<Mate> isExistingMate = mateRepository.findByRoomIdAndMemberId(roomId, memberId);

       // 이 방에 JOINED나 PENDING 상태로 참여중이면 이미 참여중이라는 예외
        if (isExistingMate.isPresent()) {
            Mate exitingMate = isExistingMate.get();
            if (exitingMate.getEntryStatus() == EntryStatus.JOINED || exitingMate.getEntryStatus() == EntryStatus.PENDING) {
                throw new GeneralException(ErrorStatus._ROOM_ALREADY_JOINED);
            }
        }
       // ENABLE이나 WAITING상태의 방에 JOINED 상태로 참여중이라면 참여중인 방이 있다는 예외
        if (roomRepository.existsByMemberIdAndStatuses(memberId, RoomStatus.ENABLE,
            RoomStatus.WAITING, EntryStatus.JOINED)) {
            throw new GeneralException(ErrorStatus._ROOM_ALREADY_EXISTS);
        }
       // EntryStatus가 EXITED가 아닌, Active 상태인 mate들의 숫자와 maxMateNum 비교하여 방이 꽉찼는지 검사 
        if (mateRepository.countActiveMatesByRoomId(roomId) >= room.getMaxMateNum()) {
            throw new GeneralException(ErrorStatus._ROOM_FULL);
        }

        if (isExistingMate.isPresent()) {
            // 재입장 처리
            Mate exitingMate = isExistingMate.get();
            exitingMate.setEntryStatus(EntryStatus.JOINED);
            exitingMate.setNotExit();
            mateRepository.save(exitingMate);
            room.arrive();
            room.isRoomFull();
            roomRepository.save(room);
        } else {  // 처음 입장하는 경우 처리
            Mate mate = MateConverter.toEntity(room, member, false);
            mateRepository.save(mate);
            room.arrive();
            room.isRoomFull();
            roomRepository.save(room);
        }

        // Room의 Mate들을 찾아온다
        List<Mate> findRoomMates = mateRepository.findByRoom(room);

        List<Member> memberList = findRoomMates.stream()
            .map(Mate::getMember)
            .filter(findMember -> !findMember.getId().equals(member.getId()))
            .toList();

        eventPublisher.publishEvent(GroupRoomNameWithOutMeTargetDto.create(member, memberList, room,
            NotificationType.JOIN_ROOM));

    }

Mate에 방 퇴장, 재입장 관련 메소드 추가

// Mate
public void setEntryStatus(EntryStatus entryStatus) {
        this.entryStatus = entryStatus;
    }

    public void quit() {
        this.isExit = true;
        setEntryStatus(EntryStatus.EXITED);
    }

    public void setNotExit() {
        this.isExit = false;
   

방 이름 중복 검사

//RoomController
 @GetMapping("/check-roomname")
    @Operation(summary = "[바니] 방 이름 중복 검증", description = "가능하면 true가, 중복시 false가 리턴됩니다.")
    ResponseEntity<ApiResponse<Boolean>> checkRoomName(@RequestParam String roomName) {
        Boolean isValid = roomCommandService.checkRoomName(roomName);
        return ResponseEntity.status(SuccessStatus._OK.getHttpStatus()).body(ApiResponse.onSuccess(isValid));
    }

//RoomCommandService
 public Boolean checkRoomName(String roomName) {
        return roomQueryService.isValidRoomName(roomName);
    }

//RoomQueryService
public Boolean isValidRoomName(String roomName) {
        return !roomRepository.existsByName(roomName);
    }

//RoomRepository
    boolean existsByName(String roomName);

동작 확인

좌심방을 나가보겠습니다

image

image

Room

image

Mate

image

entryStatus에 EXIT을 추가한 이유

  1. isExit를 없애버릴까 하다가 일단은 나가면 true가 되도록 했는데 기획에서 아직 한번 나가면 10분간 못들어오고… 그게 유효한지 모르겠습니다 (델로에게 물어보겠습니다)
  2. 위의 이유로 isExit이 true인데 entryStatus가 JOINED면 entryStatus JOINED인 상태를 검사할때 문제가 생깁니다
  3. 방에서 나갈때 나가는 사람 관련 데이터가 다 날아가면 안돼서 (최후의 1인이 나갈때만 다 날아가야해서) 모두 다 나갈때까지 mateId를 보존해야합니다

그래서 일단 이런 방식으로 했는데… 혹시 더 좋은 방법이 있을까요? 의견 부탁드립니다

아무튼 나가면 isExit가 true가 되고 entryStatus는 EXITED가 되고 EXITED 상태인 방은 참여중인 방으로 안칩니다

참여중인 방이 있는지 여부 조회하면 없다고 나옴

image

방에 나간 멤버와 관련된 데이터들은 그대로 있습니다

image

공개방을 만들어보겠습니다

image

중복된 해시태그 입력했을 때

image

해시태그 3개 넘어가게 작성했을때, 입력 안했을 때

image

image

해시태그가 형식에 안맞을 때 (지금은 해시태그 형식을 임의로 설정해뒀습니다)

image

image

성공적으로 생성되었을때

image

image

todo 생성했고

image

rule도 만들었습니다

방을 나가보겠습니다
image

image
최후의 1인이 나가면서 방 자체가 사라졌고 모든 데이터도 날라갔습니다 (피드,롤,투두 등등)

이제 첫번째로 나갔던 좌심방 방에 다시 들어가보겠습니다

일단 방 정보를 조회하면 나갔던 바니는 뜨지 않는 것을 확인할 수 있습니다 (방정보 조회할때 방 타입과 해시태그도 같이 조회되도록 수정했습니다)

{
  "isSuccess": true,
  "code": "COMMON200",
  "message": "성공입니다.",
  "result": {
    "roomId": 2,
    "name": "코지메이트 좌심방",
    "inviteCode": "23X1383C",
    "profileImage": 14,
    "mateList": [
      {
        "memberId": 5,
        "mateId": 4,
        "nickname": "포비"
      },
      {
        "memberId": 9,
        "mateId": 5,
        "nickname": "무비무비무붑"
      },
      {
        "memberId": 2,
        "mateId": 8,
        "nickname": "베로"
      },
      {
        "memberId": 4,
        "mateId": 13,
        "nickname": "델로"
      },
      {
        "memberId": 72,
        "mateId": 51,
        "nickname": "미리"
      }
    ],
    "roomType": "PRIVATE",
    "hashtags": []
  }

재입장에 성공했습니다
image

image

image
mate, room 모두 정상입니다

💬 리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

@suuu0719 suuu0719 added the enhancement New feature or request label Oct 11, 2024
@suuu0719 suuu0719 self-assigned this Oct 11, 2024
Copy link

리뷰해드려요~

Room.java

  • 새로운 변수 roomType 추가
  • 새로운 변수 roomHashtags 추가

RoomConverter.java

  • 새로운 메서드 toPrivateRoom 추가
  • 새로운 메서드 toPublicRoom 추가

RoomCreateRequest.java

  • 새로운 변수 hashtags 추가

RoomCreateResponse.java

  • 새로운 변수 roomType 추가
  • 새로운 변수 hashtags 추가

RoomRepository.java

  • 새로운 메서드 countActiveMatesByRoomId 추가

RoomCommandService.java

  • 새로운 메서드 createPrivateRoom 추가
  • 새로운 메서드 createPublicRoom 추가
  • 기존 메서드 createRoom 변경
  • 기존 메서드 deleteRoom 변경
  • 기존 메서드 quitRoom 추가

RoomQueryService.java

  • 새로운 변수 roomHashtagRepository 추가
  • 기존 메서드 getRoomById 변경

RoomHashtag.java

  • 새로운 파일 추가

RoomHashtagConverter.java

  • 새로운 파일 추가

RoomHashtagRepository.java

  • 새로운 파일 추가

RoomHashtagCommandService.java

  • 새로운 파일 추가

ErrorStatus.java

  • 새로운 상수 _DUPLICATE_HASHTAGS 추가

추가 개선 사항:

  • Room.java

  • 멤버 변수 isExit 추가

  • 멤버 변수 entryStatus 추가

  • RoomCommandService.java

  • 기존 메서드 joinRoom 변경

  • 기존 메서드 deleteRoom 변경

  • RoomRepository.java

  • 기존 메서드 deleteByRoomIdAndMemberId 추가

  • RoomController.java

  • 기존 메서드 joinRoom 변경

  • 기존 메서드 getCozymateList 변경

  • 기존 메서드 inviteCozymate 변경

  • 기존 메서드 getRequestInvites 변경

  • 기존 메서드 inviteRequest 변경

  • 새로운 메서드 quitRoom 추가

  • RoomConverter.java

  • 기존 메서드 toEntity 변경

  • RoomQueryService.java

  • 기존 메서드 getRoomById 변경

  • RoomRepository.java

  • 기존 메서드 existsByMemberIdAndStatuses 변경

  • RoomHashtag.java

  • 멤버 변수 room 추가

  • 멤버 변수 hashtag 추가

  • RoomHashtagConverter.java

  • 새로운 파일 추가

  • RoomHashtagRepository.java

  • 새로운 파일 추가

  • RoomHashtagCommandService.java

  • 새로운 파일 추가

  • PublicRoomCreateRequest.java

  • 새로운 파일 추가

  • RoomCreateResponse.java

  • 새로운 변수 roomType 추가

  • 새로운 변수 hashtags 추가

  • RoomType.java

  • 새로운 파일 추가

  • RoomStatus.java

  • 새로운 파일 추가

  • RoomType.java

  • 새로운 파일 추가

  • RoomStatus.java

  • 새로운 파일 추가

  • RoomType.java

  • 새로운 파일 추가

  • RoomStatus.java

  • 새로운 파일 추가

@eple0329
Copy link
Member

아오 봇 맘에 안들어

Copy link

리뷰해드려요~

Hashtag.java - Review 1

  • A new entity class Hashtag has been added to the project.
  • The class has fields for id, hashtag, and a constructor with parameters for both fields.
  • The class also has a builder method for creating instances of Hashtag.

RoomHashtag.java - Review 1

  • A new entity class RoomHashtag has been added to the project.
  • The class has fields for id, room, and hashtag, and a constructor with parameters for all three fields.
  • The class also has a builder method for creating instances of RoomHashtag.

RoomRepository.java - Review 1

  • A new method existsByName has been added to the RoomRepository interface.
  • The method takes a String parameter roomName and returns a boolean indicating whether a room with the given name exists.

RoomConverter.java - Review 1

  • Two new methods toPrivateRoom and toPublicRoom have been added to the RoomConverter class.
  • The methods take a RoomCreateRequest object and a String parameter inviteCode, and return a Room object.
  • The toPrivateRoom method sets the roomType field of the Room object to RoomType.PRIVATE, while the toPublicRoom method sets it to RoomType.PUBLIC.

RoomCreateResponse.java - Review 1

  • Two new fields roomType and hashtags have been added to the RoomCreateResponse class.
  • The fields are of type RoomType and List<String> respectively.

RoomType.java - Review 1

  • A new enum class RoomType has been added to the project.
  • The enum class has two constants PUBLIC and PRIVATE.

RoomController.java - Review 1

  • Two new methods createPublicRoom and quitRoom have been added to the RoomController class.
  • The createPublicRoom method takes a PublicRoomCreateRequest object and an AuthenticationPrincipal object, and returns a ResponseEntity object.
  • The quitRoom method takes a Long parameter roomId and an AuthenticationPrincipal object, and returns a ResponseEntity object.

RoomQueryService.java - Review 1

  • A new method isValidRoomName has been added to the RoomQueryService class.
  • The method takes a String parameter roomName and returns a boolean indicating whether the room name is valid.

RoomHashtagRepository.java - Review 1

  • A new interface RoomHashtagRepository has been added to the project.
  • The interface extends JpaRepository and has two methods findHashtagsByRoomId and save.

RoomHashtagCommandService.java - Review 1

  • A new class RoomHashtagCommandService has been added to the project.
  • The class has a constructor that takes HashtagRepository and RoomHashtagRepository objects as parameters.
  • The class has a method createRoomHashtag that takes a Room object and a List<String> parameter hashtags, and saves the room hashtags to the database.
  • The class also has a method validateHashtags that takes a List<String> parameter hashtags and checks for duplicate hashtags.

I hope this helps! Let me know if you have any questions.

Copy link
Member

@eple0329 eple0329 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM~
코맨트 확인해주세용

Comment on lines 27 to 29
public Hashtag(String hashtag) {
this.hashtag = hashtag;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요부분 Builder 어노테이션 사용하면, 빌더 패턴으로 바꿔주세요!
생성자 선언 방식은 일반적으로 잘 사용되지 않는 것으로 알고있습니당.
참고 사이트

Comment on lines +42 to 66
@PostMapping("/create-private")
@Operation(summary = "[바니] 초대코드로 방생성 기능", description = "방이름, 프로필이미지, 인원수를 입력합니다.")
@SwaggerApiError({
ErrorStatus._MEMBER_NOT_FOUND,
ErrorStatus._ROOM_ALREADY_EXISTS
})
public ResponseEntity<ApiResponse<RoomCreateResponse>> createRoom(@Valid @RequestBody RoomCreateRequest request,
@AuthenticationPrincipal MemberDetails memberDetails) {
RoomCreateResponse response = roomCommandService.createRoom(request, memberDetails.getMember());
RoomCreateResponse response = roomCommandService.createPrivateRoom(request, memberDetails.getMember());
return ResponseEntity.ok(ApiResponse.onSuccess(response));
}

@PostMapping("/create-public")
@Operation(summary = "[바니]공개 방 생성 기능", description = "방이름, 프로필이미지, 인원수, 해시태그(1-3개)를 입력합니다.")
@SwaggerApiError({
ErrorStatus._MEMBER_NOT_FOUND,
ErrorStatus._ROOM_ALREADY_EXISTS,
ErrorStatus._DUPLICATE_HASHTAGS
})
public ResponseEntity<ApiResponse<RoomCreateResponse>> createPublicRoom(
@Valid @RequestBody PublicRoomCreateRequest request,
@AuthenticationPrincipal MemberDetails memberDetails) {
RoomCreateResponse response = roomCommandService.createPublicRoom(request, memberDetails.getMember());
return ResponseEntity.ok(ApiResponse.onSuccess(response));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 API를 따로 나눈 이유가 있을까요?
역할이 비슷해서, 공개/비공개로 나뉘는 requestParam을 통해서 구분해도 괜찮을 것 같기도 해서..~
실제로 프론트에서 방 생성이라는 공개/비공개에 상관없이 동일하지 않나요?
이건 지극히 개인적인 생각이라... 바꿔달라! 까지는 아닙니당

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

방만들기가 프론트 화면에서 공개방만들기 / 비공개방 만들기로 나뉘어 있어서 구분해서 만들었습니다!!..

Comment on lines 18 to 32
@NotBlank
@Size(max=12)
@Pattern(regexp = "^(?!\\s)[가-힣a-zA-Z0-9\\s]+(?<!\\s)$", message = "한글, 영어, 숫자 및 공백만 입력해주세요. 단, 공백은 처음이나 끝에 올 수 없습니다.")
private String name;
@NotNull
@Min(0)
@Max(15)
private Integer profileImage;
@NotNull
@Min(2)
@Max(6)
private Integer maxMateNum;
@Size(min = 1, max = 3, message = "해시태그는 1개에서 3개까지 입력할 수 있습니다.")
private List<@NotBlank @Pattern(regexp = "^(?!_)[가-힣a-zA-Z0-9]+(_[가-힣a-zA-Z0-9]+)*(?<!_)$",
message = "해시태그는 한글, 영어, 숫자 및 '_'만 사용할 수 있으며, '_'는 앞이나 뒤에 올 수 없습니다.") String> hashtags;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotNull이나 Min Max 어노테이션등에서도 message로 에러 발생시 표시할 내용 적어주면 좋습니당.

import lombok.Setter;

@Getter
@Setter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setter 필요한가요?


public interface RoomHashtagRepository extends JpaRepository<RoomHashtag, Long> {

@Query("select concat('#', h.hashtag) from RoomHashtag rh join rh.hashtag h where rh.room.id = :roomId")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프론트에서 이거 # 붙이지 말아달래요

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@Entity
public class Hashtag extends BaseTimeEntity {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

잠깐 궁금증인데
Hashtag 테이블을 따로 만들 필요가 있을까요?
그냥 방 정보에 String으로 저장해도 문제없지 않을까? 하는 생각이 있어서
모두의 의견을 들어보고 싶습니당

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

찾아봤을때는 추후에 해시태그로 뭔가를 하는 기능이 확장된다하면 Hashtag 테이블을 따로 만드는게 좋다기에 이렇게 했는데... 따로 둘 필요는 없을까요??

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

흠 Hashtag 종류가 정해져있으면 따로 만드는게 좋은데, 지금은 그냥 입력된 String이 Hashtag라서 중복되는게 많을까? 하는 생각이 들긴 합니다!
일단 이렇게 두고 나중에 변경하시져

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json 활용해서 임베디드 값타입 활용해보는게 어떨까여? 개인적인 의견입니다아

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Json 타입 써서 어캐 활용해요?
어떻게 활용할지 감이 잘 안와서..ㅎ

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class Room{
..
hastag = {
value = ["#금연",..,]
}

}

이런식으로여
물론 hash tag를 따로 클래스 빼서 관리할수 있습니다

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 처음에 구현할때는 값타입으로 했었는데 값타입을 쓰면 해시태그 수정이 좀 어렵고 나중에 해시태그로 방을 필터링 하는게 복잡해진다고 하더라고요.... 근데 지금처럼 하면 테이블이 복잡해지긴 해서......... 어떻게 하는게 좋을지 고민이 되네요

Copy link

리뷰해드려요~

Room.java - Review 1

  • 새로운 필드 roomType가 추가되었습니다.
  • 새로운 메서드 isRoomFull()가 추가되었습니다.
  • 새로운 메서드 quit()가 추가되었습니다.

RoomRepository.java - Review 1

  • 새로운 메서드 countActiveMatesByRoomId()가 추가되었습니다.

RoomController.java - Review 1

  • 새로운 메서드 quitRoom()가 추가되었습니다.
  • 새로운 메서드 checkRoomName()가 추가되었습니다.

RoomConverter.java - Review 1

  • 새로운 메서드 toPrivateRoom()가 추가되었습니다.
  • 새로운 메서드 toPublicRoom()가 추가되었습니다.

RoomCreateRequest.java

  • 새로운 필드 roomType가 추가되지 않았습니다.

RoomCreateResponse.java

  • 새로운 필드 roomType가 추가되었습니다.
  • 새로운 필드 hashtags가 추가되었습니다.

RoomStatus.java

  • 새로운 상수 EXITED가 추가되었습니다.

RoomType.java

  • 새로운 상수 PUBLIC가 추가되었습니다.

RoomRepository.java

  • 새로운 메서드 existsByName()가 추가되었습니다.

RoomCommandService.java

  • 새로운 메서드 createPrivateRoom()가 추가되었습니다.
  • 새로운 메서드 createPublicRoom()가 추가되었습니다.
  • 새로운 메서드 quitRoom()가 추가되었습니다.

RoomQueryService.java

  • 새로운 메서드 isValidRoomName()가 추가되었습니다.

RoomHashtag.java

  • 새로운 클래스 RoomHashtag가 추가되었습니다.

RoomHashtagConverter.java

  • 새로운 클래스 RoomHashtagConverter가 추가되었습니다.

RoomHashtagRepository.java

  • 새로운 클래스 RoomHashtagRepository가 추가되었습니다.

RoomHashtagCommandService.java

  • 새로운 클래스 RoomHashtagCommandService가 추가되었습니다.

추가 개선 사항:

Room.java - Suggestion 1

  • roomType 필드에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomConverter.java - Suggestion 1

  • toPrivateRoom()toPublicRoom() 메서드에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomCreateRequest.java - Suggestion 1

  • roomType 필드에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomCreateResponse.java - Suggestion 1

  • roomTypehashtags 필드에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomStatus.java - Suggestion 1

  • EXITED 상수에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomType.java - Suggestion 1

  • PUBLIC 상수에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomRepository.java - Suggestion 1

  • existsByName() 메서드에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomCommandService.java - Suggestion 1

  • createPrivateRoom()createPublicRoom() 메서드에 대한 정의와 관련된 주석을 추가할 수 있습니다.
  • quitRoom() 메서드에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomQueryService.java - Suggestion 1

  • isValidRoomName() 메서드에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomHashtag.java - Suggestion 1

  • RoomHashtag 클래스에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomHashtagConverter.java - Suggestion 1

  • RoomHashtagConverter 클래스에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomHashtagRepository.java - Suggestion 1

  • RoomHashtagRepository 클래스에 대한 정의와 관련된 주석을 추가할 수 있습니다.

RoomHashtagCommandService.java - Suggestion 1

  • RoomHashtagCommandService 클래스에 대한 정의와 관련된 주석을 추가할 수 있습니다.

Copy link
Contributor

@genius00hwan genius00hwan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM~ 코멘트 남겼습니다.

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@Entity
public class Hashtag extends BaseTimeEntity {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json 활용해서 임베디드 값타입 활용해보는게 어떨까여? 개인적인 의견입니다아

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Room에 대학정보 있어야 할거 같아요 지금은 전체방이 다 조회 되지만 대학정보가 있어야 해당 학교 방 조회에 용이하니..
일단 당장보다는 제가 대학관련 api 올리면 그때 ㄱㄱ하시죠
대학교 없는 방 있을수도 있으니 NON 이라는 대학교 제가 만들어 둘게여

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NON 대학교 있는게 나을까요? 그냥 null로 있어도 될거같은데
null에 대한 처리를 따로 해줘야해서 그런건가용?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넹 nullPointerException을 최대한 지양 하려구여

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@Entity
public class Hashtag extends BaseTimeEntity {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class Room{
..
hastag = {
value = ["#금연",..,]
}

}

이런식으로여
물론 hash tag를 따로 클래스 빼서 관리할수 있습니다

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넹 nullPointerException을 최대한 지양 하려구여

@suuu0719 suuu0719 merged commit 0a3f480 into develop Oct 16, 2024
1 check passed
@suuu0719 suuu0719 deleted the feature/COZY-259 branch October 16, 2024 09:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants