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

Step3. 지하철 구간 관리 #498

Open
wants to merge 12 commits into
base: nara1030
Choose a base branch
from
Open

Conversation

nara1030
Copy link

안녕하세요 유라님
많이 늦었습니다.
말씀드린대로 테스트도 테스트거니와, JPA가 쉽게 이해가 가지 않아서요.
이번에 커밋 메세지도 좀 두서없는데 양해부탁드립니다..

제 생각에 제대로 구현한 게 아닌데, 어쨌든 어제 메세지 나눈 것과 고민한 부분에 대해 적어보겠습니다.


  1. Line - Section
    저는 다음과 같이 도메인간 단방향 연관관계를 맺고자 하였습니다.

Line > Section > Station

처음엔 Line이 Station에 대한 직접 의존을 하고 있었으나, 구간 등록/삭제 기능을 구현하며 Line은 List

을 갖도록 변경해주었습니다. 어제 말씀해주실 때 Section은 Line에 종속된 관계이니 Section에서 Line도 참조해야 한다고 하셨는데, 그럼 양방향 연관관계를 사용해야 한다는 하신건지 궁금합니다.

또한 h2-console로 확인해보니 의도치 않은 LINE-SECTION 테이블이 생성된 것이 확인되어 이를 지워주기 위해 양방향 연관관계로 Section에 Line 필드를 추가하였는데, 아예 실행시 에러가 나더라구요...

  1. Section - Station
    OneToOne이 아닌 ManyToOne이 맞을 거 같다고 조언해주셨는데요. 테스트 코드는 어떤 방식으로 해도 성공하고, h2-console로 확인해보아도 테이블 구조는 같더라구요. 차이를 확인해보려면 데이터를 직접 넣어보면서 확인해야 될까요? 테스트에서 h2-console을 확인해보려고 했는데 방법을 잘 못 찾겠는데 혹시 어떤 식으로 테스트 해야할지 궁급합니다.

  2. 도메인(엔티티)에 로직을 작성해야 한다고 배웠는데요. 현재 구간 등록/삭제의 경우 Line 엔티티가 아닌 LineService에 로직이 존재합니다. addSection 및 deleteSection 메소드처럼 서비스 레이어에 로직이 작성되어 있는데요. Repository를 타야해서 불가피한 측면도 있으나, 만약 이런 경우가 아니라면 해당 로직들을 Entity 내부로 옮겨주는 게 맞겠죠?

듣기로 @transactional 내부에 작성해야 영속성 컨텍스트에 등록?이 된다고 이해했는데요. 그럼 도메인 로직들은 Entity가 아닌 @transactional이 붙어있는 서비스 레이어에 작성하는 게 맞는 건가 헷갈립니다.

  1. 마지막으로 영속성 컨텍스트라는 개념을 처음 접하다 보니 조금 헷갈리는데요. 위에서 말씀 드린 대로 도메인 연관 구조는 Line > Section > Station을 생각했는데요. 따라서 라인을 추가하기 위해서는 구간을 먼저 생성해주어야 하고, 구간이 생성되기 위해서는 역이 먼저 생성되어야 합니다. 실제로 라인 생성 로직(LineService.toEntity)을 보면 Station을 먼저 조회함으로써 1차 캐시에 올리고, Section을 생성해 저장하고, 그 후에 Line을 저장해준다고 작성하였습니다.

그런데 의문은 이를테면 구간 삭제의 경우(LineService.deleteSection)에 현재는 라인을 조회하고, 해당하는 라인의 연관되어 있는 마지막 구간을 삭제하는 식으로 되어 있는데요. 이 경우에 1차 캐시 뭐 이런 걸 생각하지 않고 작성한거라 DB에 제대로 반영이 되고 있는 건지 모르겠습니다.


글이 긴데 읽어주셔서 감사합니다.
좋은 오후 보내세요!

nara1030 added 12 commits July 20, 2022 17:30
- Dto(ErrorResponse)에 Getter 필수
  Failure in @ExceptionHandler
  nextstep.subway.error.GlobalExceptionHandler#handleLineNotFoundException(LineNotFoundException)
- @OnetoOne 어노테이션 확인
- 기존에 DTO(LineRequest)에서 엔티티로 변경해주는 로직 Service단으로
  이동(DTO에서 DB 접속 비상식적)
- findStationByStationId 메소드가 StationService가 아닌 LineService에
  있는 이유는 순환참조 방지 위해서
- Line 테이블 입력 시 다음 에러 발생
  (The given id must not be null!)
- Line 테이블 입력 시 Section Id 널 이슈 해결
- getLine(downStation.id, 1) 컴파일 에러 발생으로 get을 getString으로 변경
- 제대로 구간 추가되는지 임시로 콘솔 로그로 확인하였고 toString 메소드 추가(LineService.addSection)
- addSectionToNonPresentLine 메소드를 예로 들면,
  Given 조건인 추가할 구간에 해당하는 역을 생성하지 않아도 구간 추가가 가능하다는 것이 로직의 구멍
- 즉, 도메인은 Line > Section > Station의 단방향 의존을 갖도록 의도하였는데,
  실질적으로 Station이 없어도 Line이 생성되는 이슈 존재
- LineAcceptance.deleteSection 메소드 then절 변경(반환값 생성)
- 구간 추가/삭제 시 구간이 아닌 라인(LineResponse)을 리턴하는 게 맞는지
  고민
= 구간 등록/삭제 시 SectionIsNotLastSequenceOfLine 예외 함께 사용함으로
분리 필요
@dbeod2 dbeod2 self-requested a review July 28, 2022 23:55
Copy link

@dbeod2 dbeod2 left a comment

Choose a reason for hiding this comment

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

안녕하세요 :-)

3단계 지하철 구간 관리 진행해주신 부분 확인했습니다.

우선 질문 주신 부분에 대해서 답변은 별도 코멘트로 남겨두었습니다.
아직 중간단계이신 것 같아서 몇가지 코멘트만 드렸는데 확인부탁드려요! 🙏

private Long downStationId;

@OneToMany
private List<Section> sections = new ArrayList<>();
Copy link

Choose a reason for hiding this comment

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

Section 컬렉션에 대해서 일급컬렉션으로 리팩토링해보면 어떨까요? 컬렉션에 대한 비지니스 로직이 일급컬렉션으로 모여서 응집도를 높일 수 있어요. 아래글을 참고해보세요.

https://jojoldu.tistory.com/412

Station upStation = findStationByStationId(lineRequest.getUpStationId());
Station downStation = findStationByStationId(lineRequest.getDownStationId());
Section section = new Section(upStation, downStation, lineRequest.getDistance());
sectionRepository.save(section);
Copy link

Choose a reason for hiding this comment

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

코드상 line entity에서 section.add를 통해 구간을 저장하고 있는데요.
sectionRepository.save를 사용해주셨군요 🤔

질문 주신 답변에 영속성 전이 관련되서 링크 드렸는데요. 확인해보시면 좋을 것 같아요! 🙏

throw new SectionAlreadyHasStationException(downStationId);
});

// System.out.println("+++++++++");
Copy link

Choose a reason for hiding this comment

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

주석은 제거해주세요 🙏

Comment on lines +94 to +105
List<Section> sections = line.getSections();
if (sections.get(sections.size() - 1).getDownStation().getId() != sectionRequest.getUpStationId()) {
throw new SectionIsNotLastSequenceOfLine();
}
Long downStationId = sectionRequest.getDownStationId();
line.getSections().stream()
.filter(section -> section.getDownStation().getId() == downStationId || section.getUpStation().getId() == downStationId)
.findFirst()
.ifPresent(section -> {
throw new SectionAlreadyHasStationException(downStationId);
});

Copy link

Choose a reason for hiding this comment

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

예외처리 너무 잘해주셨네요!
한가지 의견드려보자면 예외처리도 비즈니스 로직이라면 비즈니스 로직일 것 같은데요.entity에서 담당하는건 어떨까요?
실제 비지니스 로직이 entity에 있다면 변경이 있는 경우에도 영향도가 적어질 수 있어요.

}

@DeleteMapping("/lines/{id}/sections")
public ResponseEntity<LineResponse> deleteSection(@PathVariable Long id, @RequestParam Long stationId) {
Copy link

Choose a reason for hiding this comment

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

구간 삭제 api보면return값이� 없습니다. :-)
확인 부탁드립니다.

스크린샷 2022-07-29 오후 12 18 52

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
Copy link

Choose a reason for hiding this comment

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

💯 💯


public class LengthOfLineIsOneException extends RuntimeException {
private final ErrorCode errorCode = ErrorCode.LENGTH_OF_LINE_IS_ONE_ERR;
private String errorMessage = "%s번 노선의 구간은 유일하므로 삭제할 수 없습니다.";
Copy link

Choose a reason for hiding this comment

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

오류메시지에 정확한 내용이 들어가서 오류를 쉽게 찾을 수 있겠네요 👍


import org.springframework.data.jpa.repository.JpaRepository;

public interface SectionRepository extends JpaRepository<Section, Long> {
Copy link

Choose a reason for hiding this comment

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

SectionRepository가 필요없을 것 같아요 ~ 🙏

@dbeod2
Copy link

dbeod2 commented Jul 29, 2022

A1. 넵 양방향 연관관계를 사용해야 한다는 말이었습니다 ~
A2,A4. H2-console을 확인해 보시려고 하는건가요? 데이터가 저장되는 위치가 있을 것 같기는한데. 관련되서 링크 전달드려볼께요! https://imspear.tistory.com/80
A3. 네, Line과 Section의 관계 설정 후 영속성 전이를 활용해보시면 구간 등록/삭제의 경우는 entity에서 로직을 수행 할 수 있습니다. 참고 URL 드립니다. https://parkhyeokjin.github.io/jpa/2019/11/06/JPA-chap8.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants