크루에 참여 및 탈퇴 기능과 크루 세팅하는 부분에 대해서 정리하고자 한다.
크루 세팅은 크루 공개/비공개, 크루원 모집 혹은 모집X인지, 크루 활동이 종료되었는지 아닌지를 설정하는 코드를 정리하고자 한다.
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Builder
public class Crew extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user; // 크루장 - 로그인한 user
private String name; // 크루이름
private boolean type; // true-온라인, false-오프라인
private boolean cost; // true-유료탑승, false-무료탑승
private boolean isRecruiting; // true - 모집중, false - 모집X,
private boolean isPublished; // true - 공개O, false - 공개X
private boolean isClosed; // true - 종료
private String thumbnail; //이미지 경로 , 처음엔 기본 이미지 -> 크루 설정을 통해 배너 수정 가능
@Lob // varchar보다 클 경우 사용하는 어노테이션
private String description; // 항해 목적
@Lob // varchar보다 클 경우 사용하는 어노테이션
private String wisher; // 원하는 선원
@Lob // varchar보다 클 경우 사용하는 어노테이션
private String plan; // 크루즈 설명
@ManyToMany // 크루에 속한 팀원
@JoinTable(name = "user_crew")
private List<User> users = new ArrayList<>();
@OneToMany(mappedBy = "crew")
private List<Like> likes = new ArrayList<>();
// 크루 가입
public void addUser(User user){
if(!this.users.contains(user)) {
this.users.add(user);
}
}
// 크루 탈퇴
public void removeUser(User user) {
if (this.users.contains(user)) {
this.users.remove(user);
}
}
// 크루에 가입이 가능한지
public boolean isJoinable(User user) { // (1)
return this.isPublished && this.isRecruiting && !this.users.contains(user) && !this.isClosed;
}
// 크루 멤버인지
public boolean isMember(User user) { // (2)
if (this.user.equals(user)){ // 크루장인 경우
return true;
}
return this.users.contains(user); // 크루원인 경우
}
// 크루장
public void setUser(User user){
this.user = user;
}
public void update(CrewUpdateRequestDto crewUpdateRequestDto){
this.name = crewUpdateRequestDto.getName();
this.type = crewUpdateRequestDto.isType();
this.cost = crewUpdateRequestDto.isCost();
this.description = crewUpdateRequestDto.getDescription();
this.wisher = crewUpdateRequestDto.getWisher();
this.plan = crewUpdateRequestDto.getPlan();
}
public void setPublished(){
if (this.isPublished) {
this.isPublished = false;
} else {
this.isPublished = true;
}
}
public void setRecruited(){
if (this.isRecruiting) {
this.isRecruiting = false;
} else {
this.isRecruiting = true;
}
}
public void setClosed(){
if (this.isClosed) {
this.isClosed = false;
} else {
this.isClosed = true;
}
}
}
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class CrewSaveRequestDto {
private String name; // 크루이름
private boolean type; // true-온라인, false-오프라인
private boolean cost; // true-유료탑승, false-무료탑승
private String description; // 크루즈 설명
private String wisher; // 원하는 선원
private String plan; // 크루즈 설명
public Crew toEntity(){
return Crew.builder()
.name(name)
.type(type)
.cost(cost)
.wisher(wisher)
.plan(plan)
.description(description)
// 이 부분
.isPublished(true) // 처음 crew를 만들 땐 true
.isRecruiting(true) // 처음 crew를 만들 땐 true
.isClosed(false) // 처음 crew를 만들 땐 false
//
.build();
}
}
@Getter
@NoArgsConstructor
public class CrewResponseDto {
private Long id; // 크루 조회할 때 사용
private String leader; // 크루장
private String name; // 크루명
private boolean type; // true-온라인, false-오프라인
private boolean cost; // true-유료탑승, false-무료탑승
private String description; // 크루즈 설명
private String wisher; // 원하는 선원
private String plan; // 크루즈 설명
private boolean isRecruit; // true - 모집중, false - 모집X
private boolean isPublished; // true - 공개O, false - 공개X
private boolean isClosed;
private List<UserResponseDto> users;
public CrewResponseDto(Crew crew) {
this.id = crew.getId();
this.leader = crew.getUser().getNickname();
this.name = crew.getName();
this.type = crew.isType();
this.cost = crew.isCost();
this.description = crew.getDescription();
this.wisher = crew.getWisher();
this.plan = crew.getPlan();
this.isRecruit = crew.isRecruiting();
this.isPublished = crew.isPublished();
this.isClosed = crew.isClosed();
this.users = crew.getUsers().stream().map(UserResponseDto::new).collect(Collectors.toList());
}
}
public class CrewService {
private final CrewRepository crewRepository;
...
// 크루 참가
public void addUser(Long id, User user){
Crew crew = crewRepository.findById(id).orElseThrow(IllegalArgumentException::new);
if (crew.isJoinable(user)){
crew.addUser(user);
}
}
public void leaveCrew(Long id, User user){
Crew crew = crewRepository.findById(id).orElseThrow(IllegalArgumentException::new);
if (crew.isMember(user)) {
crew.removeUser(user);
}
}
@Transactional
public void update(Long id, CrewUpdateRequestDto crewUpdateRequestDto){
Crew crew = crewRepository.findById(id).orElseThrow(IllegalArgumentException::new);
crew.update(crewUpdateRequestDto); // 이렇게 엔티티로 Dto를 넘겨도 되나?
}
// 크루 세팅 - 크루 공개 여부
public void setPublished(Long id){
Crew crew = crewRepository.findById(id).orElseThrow(IllegalArgumentException::new);
crew.setPublished();
}
// 크루 세팅 - 크루원 모집 여부
public void setRecruited(Long id){
Crew crew = crewRepository.findById(id).orElseThrow(IllegalArgumentException::new);
crew.setRecruited();
}
// 크루 세팅 - 크루 종료 여부
public void setClosed(Long id){
Crew crew = crewRepository.findById(id).orElseThrow(IllegalArgumentException::new);
crew.setClosed();
}
}
@Controller
@RequiredArgsConstructor
@Transactional
@Slf4j
public class CrewViewController {
private final CrewService crewService;
// 크루 참가 신청
@GetMapping("/crews/{id}/join")
public String joinCrew(HttpSession session, @PathVariable Long id) {
User loginUser = (User) session.getAttribute("loginUser");
crewService.addUser(id, loginUser);
return "redirect:/crews/" + id;
}
// 크루 탈퇴
@GetMapping("/crews/{id}/leave")
public String leaveCrew(HttpSession session, @PathVariable Long id) {
User loginUser = (User) session.getAttribute("loginUser");
crewService.leaveCrew(id, loginUser);
return "redirect:/crews/" + id;
}
// 크루 detailView
@GetMapping("/crews/{id}/log") // 활동일지
public String crewActivity(@PathVariable Long id, Model model, HttpSession session) {
User user = (User) session.getAttribute("loginUser");
CrewResponseDto crewResponseDto = crewService.findById(id);
UserResponseDto loginUser = new UserResponseDto(user);
model.addAttribute("loginUser", loginUser);
model.addAttribute("crewResponseDto", crewResponseDto);
return "crew/log";
}
@GetMapping("/crews/{id}/meeting") // 모임
public String crewMeeting(@PathVariable Long id, Model model, HttpSession session) {
User user = (User) session.getAttribute("loginUser");
CrewResponseDto crewResponseDto = crewService.findById(id);
UserResponseDto loginUser = new UserResponseDto(user);
model.addAttribute("loginUser", loginUser);
model.addAttribute("crewResponseDto", crewResponseDto);
return "crew/meeting";
}
@GetMapping("/crews/{id}/setting") // 설정 -> Crew Update
public String crewSetting(@PathVariable Long id, Model model, HttpSession session) {
User user = (User) session.getAttribute("loginUser");
CrewResponseDto crewResponseDto = crewService.findById(id);
UserResponseDto loginUser = new UserResponseDto(user);
model.addAttribute("loginUser", loginUser);
model.addAttribute("crewResponseDto", crewResponseDto);
model.addAttribute("crewUpdateRequestDto", new CrewUpdateRequestDto());
return "crew/setting";
}
@PostMapping("/crews/{id}/setting") // 설정 -> Crew Update
public String crewSetting(@PathVariable Long id,
@ModelAttribute CrewUpdateRequestDto crewUpdateRequestDto) {
crewService.update(id, crewUpdateRequestDto);
return "redirect:/crews/" + id;
}
// 크루 공개 여부
@PostMapping("/crews/{id}/published")
public String publishedCrew(@PathVariable Long id) {
crewService.setPublished(id);
return "redirect:/crews/" + id + "/setting";
}
// 크루 인원 모집 여부
@PostMapping("/crews/{id}/recruit")
public String recruitCrew(@PathVariable Long id) {
crewService.setRecruited(id);
return "redirect:/crews/" + id + "/setting";
}
// 크루 종료 여부
@PostMapping("/crews/{id}/close")
public String closeCrew(@PathVariable Long id) {
crewService.setClosed(id);
return "redirect:/crews/" + id + "/setting";
}
}
참고로 Thymeleaf는 바로 이전 게시글인 크루 CRUD와 똑같아서 크루 세팅이나 참가, 탈퇴와 관련된 부분만 가져오겠다
...
<h2>👥크루원</h2>
<p th:text="|크루장 : ${crewResponseDto.leader}|"></p>
<div class="member" th:each="member : ${crewResponseDto.getUsers()}">
<p th:text="|크루원 : ${member.nickname}|"></p>
</div>
...
...
<div class="crew-setting">
<!--크루 공개-->
<form th:action="@{/crews/{id}/published(id=${crewResponseDto.id})}" method="post">
<button type="submit" th:text="${crewResponseDto.published} ? '크루 비공개' : '크루원 공개'">크루 비공개</button>
</form>
<!--크루 인원 모집 -->
<form th:action="@{/crews/{id}/recruit(id=${crewResponseDto.id})}" method="post">
<button type="submit" th:text="${crewResponseDto.recruit} ? '크루원 모집 중단' : '크루원 모집하기'"></button>
</form>
<!--크루 종료, 활동일지 등 이런걸 추억으로 남기고 싶을 땐 종료하고 마이페이지 관심 크루, 종료 크루, 참여 크루 보여주자....-->
<form th:action="@{/crews/{id}/close(id=${crewResponseDto.id})}" method="post">
<button type="submit" th:text="${crewResponseDto.closed} ? '크루 활성화' : '크루 활동 종료'"></button>
</form>
...
</div> <!--crew-setting-->
...
<nav th:replace="~{fragment/fragment :: crewDetailnav(${loginUser.name}, ${crewResponseDto.leader},
${crewResponseDto.users.contains(loginUser)}, ${crewResponseDto.recruit} )}"></nav>
<button type="button" th:if="${loginUserNicname == crewLeaderName}"
th:onclick="|location.href='@{/crews/{crewId}/setting(crewId=${crewResponseDto.id})}'|">
크루 설정
</button>
<button type="button" th:if="${isMember}"
th:onclick="|location.href='@{/crews/{crewId}/leave(crewId=${crewResponseDto.id})}'|">
크루 탈퇴
</button>
<button type="button" th:if="${!isMember and loginUserNicname != crewLeaderName and isRecruit}"
th:onclick="|location.href='@{/crews/{crewId}/join(crewId=${crewResponseDto.id})}'|">
크루 가입
</button>