-
Notifications
You must be signed in to change notification settings - Fork 306
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
E2e step2 #877
base: jaejeong1
Are you sure you want to change the base?
E2e step2 #877
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요. 재정님
일부 이번 미션에 요구사항에 맞지 않은 부분들이 있어서 request change 하도록 하겠습니다.
관련해서 코멘트를 달아두었으니 미션 내용을 다시 한번 확인해 보면 좋을 것 같아요!
@PostMapping("/lines") | ||
public ResponseEntity<LineResponse> createLine(@RequestBody LineRequest lineRequest) { | ||
LineResponse line = lineService.saveLine(lineRequest); | ||
return ResponseEntity.created(URI.create("/lines/" + line.getId())).body(line); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가장 중요한 요구사항 구현에 미스가 있었군요.. 참고해서 수정해보겠습니다! 감사합니다 :)
@Transactional | ||
public void execute() { | ||
// 쓰기 지연 저장소에 남은 SQL을 마저 수행 | ||
entityManager.flush(); | ||
// 연관 관계 맵핑된 테이블이 있는 경우 참조 무결성을 해제해줘야 Trancate 가능 | ||
entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY FALSE").executeUpdate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup 잘 만들어 주셨네요. 👍
@Transactional | ||
public LineResponse saveLine(LineRequest lineRequest) { | ||
Line line = lineRepository.save(new Line(lineRequest.getName(), lineRequest.getColor())); | ||
return createLineResponse(line); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API 스펙을 미션 요구사항에 맞게 변경하면 아마 LineService의 로직들이 조금 더 복잡해질 거예요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Builder 패턴 적용해 Line 엔티티 생성 과정의 유연성을 확보했습니다!
/** | ||
* 지하철 노선 생성 | ||
* 노선 생성 시 상행종점역과 하행종점역을 등록 | ||
* When 지하철 노선을 생성하면 | ||
* Then 지하철 노선 목록 조회 시 생성한 노선을 찾을 수 있다 | ||
*/ | ||
@DisplayName("지하철 노선을 생성한다.") | ||
@Test | ||
void 지하철_노선_생성_테스트() { | ||
// when & then | ||
지하철_노선_생성(LINE_SHINBUNDANG, COLOR_CODE_RED); | ||
|
||
// then | ||
List<String> lineNames = getJsonPathList(지하철_노선_목록_조회(), PARAM_NAME); | ||
assertThat(lineNames).containsAnyOf(LINE_SHINBUNDANG); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시나리오와 테스트가 일치하지 않고 있어요. 😭
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시나리오 요구사항에 맞게 테스트와 클라이언트 수정하였습니다!
private List<String> getJsonPathList(ExtractableResponse<Response> response, String path) { | ||
return response.jsonPath().getList(path, String.class); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
응답을 조금 더 편하게 deserialize할 수도 있어요.
https://github.com/rest-assured/rest-assured/wiki/Usage#content-type-based-deserialization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
더 나은 방안 제시해주셔서 감사합니다.
해당 건을 살펴보고 제네릭 메서드로 선언해 클래스 타입을 주입받고,
이 제네릭 메서드를 통해 LineResponse 클래스로 변환하는 방식으로 직접 적용해보았는데,
현재 케이스는 name 필드만 추출해 검사가 필요한 케이스로, 기존 대비 코드 양이 더 많아지는 결과가 나와 제시해주신 방안은 적용하지 않았습니다.
혹시 조언주신 의도가 제가 적용한 방법과 다르다면 관련해 피드백 주시면 감사하겠습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
피드백 반영
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 재정님 😀
다음 단계로 넘어가기 전에 확인해 보면 도움이 될 것 같은 코멘트를 몇 개 남겨두었습니다!
금방 해결 가능하실 것 같아요.
확인해 보시고 다시 한번 리뷰 요청 주세요!
Line line = lineRepository.save(Line.builder() | ||
.name(lineRequest.getName()) | ||
.color(lineRequest.getColor()) | ||
.upStationId(lineRequest.getUpStationId()) | ||
.downStationId(lineRequest.getDownStationId()) | ||
.distance(lineRequest.getDistance()) | ||
.build()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
request에서 받은 stationId를 그대로 Line에 주입하고 있는데요.
request에 등록되어 있지 않은 역이 들어오면 어떻게 될까요?
예를 들어 99999라는 id가 들어오면 어떻게 될까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재는 upStation, downStation 관련 아무런 매핑이 없기 때문에, 등록되어 있지 않은 역이 들어와도 알 수 있는 방법이 없습니다.
request에서 전달받은 stationId를 기준으로 해당 Id를 가진 Station이 존재하는지 확인 후 존재하지 않는다면 Fail 처리하고,
존재한다면 해당 엔티티 객체를 Line 엔티티에 주입해준뒤 stationId를 기준으로 joinColumn 매핑을 해 외래키로 관리해야할 것 입니다.
@Builder | ||
@RequiredArgsConstructor | ||
public class LineCreateRequest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Builder
는 어떤 방식으로 사용하는 게 좋은지, 어떤 부분에서 조심해야 하는지 찾아보면 좋을 것 같아요.
찾아보신 내용을 공유해 주세요. 😀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public class LineResponse { | ||
private final Long id; | ||
private final String name; | ||
private final String color; | ||
private final List<Station> stations; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Station
은 엔티티인데요.
response에 엔티티를 이용해 응답을 하면 어떤 문제들이 있을까요? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Entity는 실제 DB 테이블과 매핑되고, 따라서 이를 기준으로 테이블이 생성되고 스키마가 변경됩니다.
Entity 객체가 변경되면 DB 데이터 또한 변경되므로, Entity를 요청이나 응답으로 전달하면 안되고 이런 경우 DTO를 사용해야합니다.
또한, Entity를 그대로 응답으로 넘기게 되면 불필요하거나 노출되면 안되는 정보(id 등)까지 노출될 수 있고, 이를 막기 위한 로직을 따로 구현해야 합니다.
DTO는 레이어 간 데이터 전송용으로 사용되며 별도 비즈니스 로직을 포함하지 않은채, Getter와 (생성자 또는 Setter)를 제공해 데이터를 전달할 수 있도록 해줍니다.
@Entity | ||
@NoArgsConstructor | ||
public class Station { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
엔티티는 왜 기본 생성자가 필요한가요?
사용되지 않는 생성자들은 어떻게 접근을 제어할 수 있을까요? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하이버네이트에서 런타임 프록시 객체 생성 시 필요합니다.
해당 케이스가 사용되는 대표적인 예시는 지연 로딩인데, 엔티티 생성 시 프록시 객체를 생성해 반환하고, 실제 엔티티에 접근이 필요할 때 영속성 컨텍스트에 DB 조회를 요청해 실제 엔티티 객체를 생성 후 프록시 객체에서 실제 엔티티 객체를 참조하도록 해 데이터를 가져오는 매커니즘입니다.
프록시 객체는 실제 엔티티 객체와 동일하게 사용할 수 있어야하기 때문에 실제 엔티티를 상속받아 만들어집니다.
이때 상속된 자식 클래스는 부모 클래스의 생성자를 super를 통해 호출해야하는데 기본 생성자가 없다면 실패합니다.
따라서, 기본 생성자는 필요하고, 접근제어자는 private이 아닌 public 또는 protected여야 합니다.
이 기본 생성자는 프록시 객체 생성을 위한 것이지, 실제 비즈니스 로직에서 사용할 용도가 아니니 public이 아닌 protected로 선언해 접근을 제어하면 될 것입니다.
void 지하철_노선_생성_테스트() { | ||
// When 지하철 노선을 생성하면 | ||
지하철_노선_생성(LINE_SHINBUNDANG, COLOR_CODE_RED, UPSTATION_ID, DOWNSTATION_ID, DISTANCE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
노선 생성을 하려면 역이 등록되어 있어야 하지 않을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞습니다. 역을 먼저 등록한 후 반환된 Id를 노선 생성 시 전달하도록 변경하겠습니다.
인수 테스트 격리 방안 별 장단점