걸음마부터 달리기
[25.03.14] 캡스톤 중간 개발 오답 본문

내가 팀장이 아니라서 모든 코드가 수정당하고 있는 상태에 나만의 생각을 적어본다.
굳이 이렇게 해야되나? 싶은 것들이 있다.
1) 양방향에 대한 고찰
https://colabear754.tistory.com/142
[JPA] 양방향 연관 관계에서 연관 관계의 주인 설정과 주의 사항
목차 개요 JPA에서는 두 엔티티 사이의 연관 관계를 정의할 때 기본적으로 단방향으로 정의한다. 이로 인해 DB 테이블에서 외래키를 기준으로 하여 조인하는 것으로 두 테이블 간의 연관된 데이
colabear754.tistory.com
난 상당히 양방향에 대해서 꺼려했다.
이유는
1) 단순히 양방향시에 하나의 트랜잭션 안에서 A>B 연결 시 B>A도 객체 연관관계를 무조건 걸어줘야한다는게 마음에 안들었고
2) 이로 인해 편의관계 메서드를 만들기가 귀찮았다.
단방향일때 부모로부터 자식(FK)을 조회해야한다면 그냥 자식에서 전부 뒤져서 찾고 보내주는 것을 선호했다.
// 일대다
// User 와 Post 관계 >> 단방향에서는 Post가 다쪽이니까 Post에 User 필드가 @ManyToOne으로 존재함
// User 를 통해 Post 조회
postRepository.findByUserId(Long userId)
// Post 를 통해 User 조회
UserRepository에서
@Query("SELECT u from User u JOIN Post p WHERE p.userId = :userId")
혹은
PostRepository에서
User findUserByUserId()
모든 것을 단방향으로 때리다보니 부모 , 자식관계에서 부모를 먼저 저장해야된다. (FK 무결성 조건땜에)
A > B > C 순서로 도메인이 엮여있다면 저장 시에 A , B , C 순서로 하면 된다.
나는 그냥 명시적으로 이렇게 하는게 좋긴하다. Cascade를 쓰기 위해 A 와 B를 양방향으로 묶고 , 또한 B와 C도 양방향으로 묶고 Cascade를 써서 넣기에는 내가 머리가 안좋은지는 몰라도 다른 예상치 못한 사이드 이펙트가 많을 것 같았다.
또한 스쳐지나가는 기억으로 인프런의 김 모 강사님은 양방향은 지향하되, 부모를 통해 자식을 참조해야만 한다면 그냥 둘 사이의 연관성을 끊고 Long으로 FK를 직접 만들어 쓰라는 것이다. (난 이게 더 확실하다고 생각한다.)
그래서 이러한 양방향에 대한 고려가 어떨때 나오는가?
Project와 Sprint , Backlog의 관계를 보자. 계층형으로 차례대로 관계를 가지고 있다. 이때 ProjectId를 통해 Backlog를 조회해야 한다고 가정하자.
@Query("SELECT b FROM Backlog b" +
" join fetch b.sprint s" +
" join fetch s.project p" +
" WHERE p.projectId = :projectId")
List<Backlog> findBacklogsByProjectId(@Param("projectId") Long projectId);
+) 추가로 User나 Post 관계에서 어떤 Repository에 메서드를 작성할 것인가? 라는 것도 고민점이다.
상황: PostId로 User 찾기 >> Post만 User를 알고있음.
FK를 가지고 있는 엔티티의 Repository에 위치시키자!
1) 단방향일때
기본적으로 참조를 가지고 있는 엔티티의 Repository에서 하는 것이 정상이다.
2) 양방향일때
FK가 있는 쪽이 주인이니까 주인 엔티티의 Repository에서 만든다.
2) 다대다 중간테이블의 고찰
1번과 이어지는 내용이다.
Schedule 과 User의 다대다 관계에 대해서 생각해보자.
중간테이블로 일대다 , 다대일로 나눴다.
Schedule Id로 걸려있는 User 조회하기
1) 단방향
List<userSchedule> us = userScheduleRepository.findAllBy(scheduleId);
for(us 돌리면서) {
List<> arr = new ArrayList<>
arr.add(us.getUser)
}
하던가
@Query()
2) 양방향
3) DTO 에 대한 고찰
난 기존에 GET 요청을 제외하고도 POST , PATCH 수행 시에 해당 요청에 대한 Response에 해당 내용을 그대로 다시 담아서 보내주었다. 이유는
내용이 수정되었거나 Post 된경우 어차피 그 부분을 포함하여 UI에 프론트가 뿌려야하기 때문에 이것을 위해서는 해당 데이터를 다시 API 호출을 통해 네트워크를 타는것보다는 Request에 대한 Response로 받는게 더 좋을 것이라고 생각했다.
하지만 이는 내 착각이었던 것 같다.
1) 이런 단건에 대해서는 네트워크를 한번 더 탔다고 성능이 유의미한 불리함이 없다.
2) 사실상 프론트에서는 이렇게 받는것보단 그냥 기존의 API를 다시 호출해서 받아오는게 로직적으로 더 편하다.
프론트에 대한 이해가 얕아 발생하는 문제였다.
따라서 GET을 제외하고 나머지는 그냥 Void로 Response에 포함시키지 않고 상태코드로만 주는것이 오히려 프론트 입장에서는 편할 것이다.
4) 로직 처리를 프론트에서 할 것이냐 백엔드에서 할 것이냐
캘린더 처리에 대한 문제였다.
캘린더에 일정 추가 기능을 만들던 와중에 예를 들어 1일 23시 ~ 2일 01시까지 진행하는 일정이 있다고 가정하면 이는 우리 기획 상 1일 ~ 2일 이렇게 UI에서 보일 것이다.
그렇다면 결국 저장은 시 단위로 하지만 프론트에서는 일 단위로 줘야되기때문에 이런 로직처리를 어디서 할것이냐? 에 대한 논쟁이었다.
백엔드 입장인 내가 보기엔 백엔드는 그냥 데이터 저장만 하고 프론트에서도 충분히 할 수 있잖아? 라고 생각했다. (솔직히 귀찮음이 컸다)
하지만 프론트는 UI에 집중하는 것이 옳다. 로직 처리는 최대한 백엔드에서 내부적으로 처리를 하고 프론트는 UI에 집중 할 수 있게 하는 것이 옳은 협업 방식이라고 생각이 바뀌었다.
5) 함수 이름에 대한 고찰
이것도 어떻게 팀끼리 정하느냐의 차이겠지만 , 우리는 Spring Data JPA의 쿼리 메서드 규칙을 따르기로 하였다.
[옵션][요소] (By [ 파라미터 검색 기준] )
예를 들면 프로젝트 Id 로 해당 프로젝트 Id에 걸려있는 스프린트를 찾고자 한다면
getSprintsByProjectId 이런식이다.
get / delete / create / update 로 통일했다.
6) 엔드포인트에 관하여
민망하지만 그냥 협업 과정에서 아무래도 스펙이 잘 정해지지 않았다면 쓸모없는 엔드포인트가 생기기 마련인데...
해당 부분 완료 이후에 쓰지 다 합친 후 쓰지 않는 엔드포인트는 냅둬도 되는거 아닌가? 했다. (지금 생각하면 왜이렇게 생각했지? )
이미 인증 / 인가를 받은 사용자라면 쓰지 않는 엔드포인트더라도 호출을 할 수 있다. 즉 DB에 사용자가 직접 접근이 가능해져버린다.