🌿SPRING/🌱연습[SPRING]

[SPRING] 모임 태그 - 생성 시 태그 같이 넣기 (1)

디카페인라떼 2022. 9. 28. 16:08

기본 CRUD를 다 만들고나서 태그기능도 있으면 좋을 것 같아서 추가하려다가 삽질을 엄청했다.

전에 했던 프로젝트에선 인스타그램에서 처럼 #를 추가해서 그걸로 검색하는 걸 했었는데 그때는 따로 DB에 저장하지 않기 때문에 로직때문에 고생하지않았었다.. 이번에도 괜찮을 줄 알았지만..

역시 세상은 녹록치 않았다...

 

모임 (게시글) 생성 시에 이미 DB에 있는 태그들을 선택해서 같이 생성하고

수정 시에 태그도 같이 수정 가능하고

태그에 따라서 검색이 가능한 기능을 만들고 싶었다.


일단은 기본 생성을 만들어 두고, DB Table에 태그 Value 들을 넣어두었다.

그리고 매핑 관계를 보려고 하니.. 모임과 태그는 다대다 관계였다. (erd를 만들때 까지만해도 다대일 이라고 생각했지만 만들다 보니 여러개의 태그가 필요할 것 같았다..!)

 

2022.09.26 - [SPRING] - [SPRING][JPA] 다대다 매핑 N:M

 

[SPRING][JPA] 다대다 매핑 N:M

참고 블로그 더보기 https://ict-nroo.tistory.com/127 [JPA] @ManyToMany, 다대다[N:M] 관계 다대다[N:M] 실무에선 사용하지 않는 것을 추천한다. 사용하면 안되는 이유를 학습하자. 관계형 데이터베이스는 정규.

wearegolden.tistory.com

(공부할 때 보기만했지 어떻게 처리해야할지는 안보았기때문에 새로 정리했다.)

 

👉 즉 중간에 연결 테이블을 하나 엔티티로 만들어두고 다대일, 일대다 형태로 사용해야했다.

 

하지만 이과정에서 연결 테이블이라는 게 낯설어서 인지 로직이 쉽게 세워지지 않았다... = 삽질했다^^

 


1. Entity 설정
  • MeetingTagConnection Entity 를 만들어주고 Meeting과 Tag에 모두 매핑을 해준다.
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class MeetingTagConnection {

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

  @ManyToOne
  @JoinColumn(name = "meeting_id")
  private Meeting meeting;

  @ManyToOne
  @JoinColumn(name = "tag_id")
  private TagMeeting tagMeeting;
  
    public MeetingTagConnection(Meeting meeting, Tag tag) {
    this.meeting = meeting;
    this.tag = tag;
  }
  • Meeting Entity 에도 양방향 매핑을 해준다
//모임 태그
@OneToMany(mappedBy = "meeting",cascade = CascadeType.REMOVE,orphanRemoval = true)
@JsonIgnore
private Set<MeetingTagConnection> meetingTagConnectionList = new HashSet<>();

  public void setMeetingTagConnectionList (Set<MeetingTagConnection> meetingTagConnections){
    this.meetingTagConnectionList = meetingTagConnections;
  }
  • Tag 에도 양방향 매핑!
@OneToMany(mappedBy = "tagMeeting")
@JsonIgnore
private List<MeetingTagConnection> meetingTagConnectionList = new ArrayList<>();

🚨양방향 매핑 시 꼭 (mappedBy ="") 를 넣어서 누가 양방향의 주인인지를 설정해주어야한다!!

🚨안 넣는 경우 단방향만 여러개 설정되어서 컴파일을 하고나면 조인 테이블이 엄청 늘어있는 걸 확인할 수 있다.

더보기

이것 때문에도 조금 삽질을 했는데 다 해결하고 나니 상기내용과 관련된 백기선님의 강의가 유튜브에 떴다.

무서운 세상이야..

https://www.youtube.com/watch?v=hsSc5epPXDs 

(섬네일부터 너무 뼈맞았다)

 

2. DTO
  • Tag Response Dto
public TagResponseDto(Tag tag){
  this.id = tag.getId();
  this.name = tag.getName();
}
  • Request Dto
private final List<Long> tagIds;

👉 태그 id 들을 받아서 태그를 조회한 후  Connection에 저장!

  • Response Dto
private final List<TagResponseDto> tags = new ArrayList<>();

👉 응답값에는 추가한 태그들의 목록이 나오도록 설정

for(MeetingTagConnection meetingTagConnection : meeting.getMeetingTagConnectionList()){
  Tag tag = meetingTagConnection.getTag();
  TagResponseDto tagResponseDto = new TagResponseDto(tag);
  tags.add(tagResponseDto);

👉 리스트이므로 for문을 써서 넣어주었다. 엔티티에는 connection만이 매핑되어 있어서 이부분이 많이 헷갈렸다..

🤯 (람다식을 어서 배워야겠다..)

👀 meeting.getMeetingTagConnectionList 에 저장된 값을 들을 하나씩 꺼내서 

meetingTagConnection에 저장된 tag들을 다 꺼낸다음  태그ResponseDto 객체를 생성해서 meetingTagConnection에서 꺼낸 tag들을 저장한다음 리스트에 넣어준다!

 

3. Service
  • Meeting Service
private void addMeetingTagConnection(MeetingRequestDto requestDto, Meeting meeting) {
  Set<MeetingTagConnection> meetingTagConnectionList = new HashSet<>(); //1
  for (Long tagId : requestDto.getTagIds()) { //2
    Tag tag = tagRepository.findById(tagId) //3
        .orElseThrow(NullPointerException::new);
    MeetingTagConnection meetingTagConnection = new MeetingTagConnection(meeting, tagMeeting); //4
    meetingTagConnection = meetingTagConnectionRepository.save(meetingTagConnection); //5
    meetingTagConnectionList.add(meetingTagConnection); //6
  }

  meeting.setMeetingTagConnectionList(meetingTagConnectionList);//7
}

👉 태그를 넣는 메소드를 따로 만들어주었다.

  1. 연결테이블 리스트 (중복방지를 위해 Set으로 지정) 를 만들어주고
  2. requestDto에서 받은 TagIds를 하나씩 tagId에 넣어주고
  3. 그걸로 Tag를 찾아준다. 
  4. 연결테이블 객체에 해당 모임과 태그를 넣어서 생성
  5. 생성된 연결테이블 객체를 db에 저장하고
  6. 1에서 만든 리스트에 넣어주고
  7. Meeting에 넣어주고 저장!
  • CreateMeeting Service 
//모임 생성
@Transactional
public MeetingResponseDto createMeeting(MeetingRequestDto requestDto, Member member,
    MultipartFile image) {

  String meetingImage = null;

  if (image != null && !image.isEmpty()) {
    try {
      meetingImage = s3Upload.uploadFiles(image, "images");
      log.info(meetingImage);
    } catch (IOException e) {
      log.error(e.getMessage());
    }
  }

  Meeting meeting = new Meeting(requestDto, member, meetingImage); // 모임 객체 생성
  Crew crew = new Crew(member, meeting);// 모임장 크루에 넣기
  meeting.addCrew(crew);

  //모임 태그 추가
  addMeetingTagConnection(requestDto, meeting);

  meetingRepository.save(meeting);
  crewRepository.save(crew);
  return new MeetingResponseDto(meeting);

}

👉 기존에 만들어둔 모임생성 로직에 아까만든 메소드를 넣어준다!

 

4.Controller
  • 기존과 동일 

 


Postman API Test
  • requestDto

👉 태그 아이디들을 리스트 형태로 담아서 보낸다

 

  • responseDto

👉 태그 id에 해당하는 태그명들이 같이 출력된다. 

 

👉 연결 테이블에도 두 값이 잘 저장 되어있는걸 확인할 수 있다.