-
Notifications
You must be signed in to change notification settings - Fork 0
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
로빈 #1
base: robinjoon
Are you sure you want to change the base?
로빈 #1
Conversation
* docs(README): 요구사항 문서 작성 Co-authored-by: jinchiim <[email protected]> * feat(AdminController): 관리자 메인페이지 불러오는 기능 구현 - 메인 페이지의 경로를 매핑하는지 확인하는 단위 테스트 작성 - 메인 페이지 어드민 접속 확인 통합 테스트 작성 Co-authored-by: jinchiim <[email protected]> * docs(README): 기능 명세서 최종 확인 및 1단계 제출 Co-authored-by: jinchiim <[email protected]> * docs(README): 2단계 요구사항 및 API 문서 작성 Co-authored-by: jinchiim <[email protected]> * fix(reservation): href 경로 수정 Co-authored-by: jinchiim <[email protected]> * style(AdminControllerTest): 개행 삭제 Co-authored-by: jinchiim <[email protected]> * feat(Reservation): Reservation 도메인 생성 - Get 메서드 생성 - 생성자 생성 Co-authored-by: jinchiim <[email protected]> * feat(ReservationResponse): 응답 객체 생성 Co-authored-by: jinchiim <[email protected]> * feat(ReservationController): 예약자의 정보를 가져오는 API 구현 - 예약자 정보를 잘 불러오는지 확인하는 테스트 구현 - response 객체로 바꾸는 메서드 구현 Co-authored-by: jinchiim <[email protected]> * feat(AdminController): 예약 페이지에 접속하는 메서드 구현 - 관리자 예약 페이지가 잘 접속되는지 확인하는 테스트 구현 Co-authored-by: jinchiim <[email protected]> * test(AdminController): 누락된 예약 정보 테스트 추가 Co-authored-by: jinchiim <[email protected]> * docs(README): 2단계 기능 명세서 확인 및 제출 Co-authored-by: jinchiim <[email protected]> * docs(README): 3단계 요구사항 및 API 문서 작성 Co-authored-by: jinchiim <[email protected]> * refactor(Reservation): id 원시값으로 변경 Co-authored-by: jinchiim <[email protected]> * feat(ReservationRequest): request Dto 생성 Co-authored-by: jinchiim <[email protected]> * feat(ReservationController): 예약 저장 메서드 구현 - 예약 페이지가 잘 동작하는지 확인하는 테스트 작성 - 예약 정보를 잘 저장하는지 확인하는 테스트 작성 Co-authored-by: jinchiim <[email protected]> * feat(ReservationController): 예약 삭제 메서드 구현 - 예약 페이지가 잘 동작하는지 확인하는 테스트 추가 작성 - 예약 정보를 잘 삭제하는지 확인하는 테스트 작성 Co-authored-by: jinchiim <[email protected]> * feat(ReservationController): @controller를 @RestController 로 변경 - ResponseEntity 삭제 Co-authored-by: jinchiim <[email protected]> * fix(ReservationController): 날짜 별로 정렬하도록 수정 - 날짜를 기준으로 비교를 잘 하는지 테스트 작성 - Reservation의 날짜를 비교하는 메서드 작성 Co-authored-by: jinchiim <[email protected]> * fix(index): href 경로 변경 Co-authored-by: jinchiim <[email protected]> * style(reservation): html depth 수정 Co-authored-by: jinchiim <[email protected]> * refactor(ReservationStorage): 예약 정보를 저장하는 Storage 생성 - 그에 따른 Controller 및 테스트 수정 Co-authored-by: jinchiim <[email protected]> * refactor(ReservationStorage): DirtiesContext 삭제 - Storage를 init 해주는 과정 추가 - removeAll 메서드 추가 - 그에 따른 Controller 및 테스트 수정 Co-authored-by: jinchiim <[email protected]> * refactor(ReservationStorage): 생성자 추가 Co-authored-by: jinchiim <[email protected]> * refactor(AdminIntegrationTest): 저장소 초기화 방식 변경 - Test에서 ReservationStorage Bean 을 따로 설정하도록 설정 클래스 추가 Co-authored-by: jinchiim <[email protected]> * style(RoomEscapeApplication): 클래스 이름 변경 Co-authored-by: jinchiim <[email protected]> * refactor(ReservationStorage): removeAll 메서드 삭제 Co-authored-by: jinchiim <[email protected]> * style(ReservationController): 개행 삭제 Co-authored-by: jinchiim <[email protected]> * fix(TimeFormatterConfig): 응답에 시간이 잘못된 형식으로 표현되는 것 수정 Co-authored-by: jinchiim <[email protected]> * refactor(ReservationStorage): 메서드 분리 Co-authored-by: jinchiim <[email protected]> * docs(README): 3단계 기능 명세서 확인 및 제출 Co-authored-by: jinchiim <[email protected]> * style(reservation): html 탭 문자 제거 Co-authored-by: jinchiim <[email protected]> * style(reservation): html 탭 문자 제거 Co-authored-by: jinchiim <[email protected]> * refactor(ReservationController): RequestMapper 어노테이션 추가 Co-authored-by: jinchiim <[email protected]> --------- Co-authored-by: jinchiim <[email protected]>
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.
ROW BEAN~
잘 구현해주셨네요 👍 고생하셨습니다.
|
||
# API 명세 | ||
|
||
## 예약 조회 API |
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.
resource 마다 request, response 명세를 꼼꼼하게 잘 작성해주셨네요 👍
private Reservation fromRequest(ReservationRequest reservationRequest) { | ||
long id = 1L; | ||
String name = reservationRequest.name(); | ||
LocalDate date = reservationRequest.date(); |
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.
req res dto 변환 로직들을 안으로 숨길 수도 있을 것 같아요
} | ||
|
||
@Override | ||
public void delete(long reservationId) { |
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.
기능이 null을 필요로 하는게 아니면 Long을 사용할 필요가 없죠
long 사용 👍
} | ||
|
||
@DeleteMapping("/{reservationId}") | ||
public void delete(@PathVariable long reservationId) { |
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.
long 사용 👍
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class TimeFormatterConfig { |
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.
이 클래스는 무슨 역할을 하는건가요? (궁금)
Spring Boot 에서 LocalDate, LocalTime, LocalDateTime 등의 일부 타입을 JSON 으로 바꿔주는 규칙을 이미 만들어두고 있는데, 이를 커스텀하기 위한 설정을 해주는 클래스에요. https://www.baeldung.com/spring-boot-customize-jackson-objectmapper
CollectionReservationRepository collectionReservationRepository = new CollectionReservationRepository(); | ||
ReservationController reservationController = new ReservationController(collectionReservationRepository); |
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.
하위 로빈 🙌
간결하게 잘짜여진 코드 같네 👍
로빈과 이야기해보고 싶은 부분 코멘트 남겨보았으
내가 스프링 실력이 부족해서 질문들 위주로 구성해보았는데 잘 부탁합니다 🙇🏻♂️
남은 미션들도 화이팅 👊
### Request | ||
|
||
> GET /reservations HTTP/1.1 | ||
|
||
### Response | ||
|
||
> HTTP/1.1 200 | ||
> | ||
> Content-Type: application/json | ||
|
||
``` JSON | ||
[ | ||
{ | ||
"id": 1, | ||
"name": "브라운", | ||
"date": "2023-01-01", | ||
"time": "10:00" | ||
}, | ||
{ | ||
"id": 2, | ||
"name": "브라운", | ||
"date": "2023-01-02", | ||
"time": "11:00" | ||
} | ||
] | ||
``` |
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.
자세한 명세 👍
@Configuration | ||
public class TimeFormatterConfig { | ||
private static final String TIME_FORMAT = "HH:mm"; | ||
|
||
@Bean | ||
public Jackson2ObjectMapperBuilderCustomizer localTimeSerializerCustomizer() { | ||
return builder -> builder.serializers(new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT))); | ||
} | ||
} |
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.
시간을 표시하는 것은 사용하는 맥락마다 달라지기 보단 공통으로 사용되는 경우가 많다고 하드라
이렇게 전역적으로 관리하는 경우가 많다고 들었으 👍
로빈의 고민이 보이는 멋진 부분이네
@Controller | ||
@RequestMapping("/admin") |
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.
망쵸한테도 물어봤던 부분인데 이렇게 공통되는 경로를 빼놓는 것에 대해서 어떻게 생각해?
어떤 사람은 전역적으로 검색 (cmd + shift + f)하기 어렵다고 지양하는 사람들도 있더라고
- 전역 검색으로 /admin/reservation을 검색할 수 없음
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 ReservationResponse toResponse(Reservation reservation) { | ||
return new ReservationResponse(reservation.getId(), | ||
reservation.getName(), reservation.getDate(), reservation.getTime()); | ||
} |
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.
ReservationResponse.from(reservation)과 같은 코드는 어떻게 생각!?
나는 개인적으로 위처럼 사용하는 것이 컨트롤러 코드를 깔끔히 한다고 생각
toResponse는 엄밀히 말하면 컨트롤러의 책임이 아니지 않을까 🤔
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.
ReservationResponse.from(reservation)과 같은 코드는 어떻게 생각!? 나는 개인적으로 위처럼 사용하는 것이 컨트롤러 코드를 깔끔히 한다고 생각 toResponse는 엄밀히 말하면 컨트롤러의 책임이 아니지 않을까 🤔
글쎄, 컨트롤러가 자신이 응답해야 하는 형태로 데이터를 가공하는건데, Controller의 역할로 부여하는 것이 그렇게 이상한 건 아닌 것 같아.
public class Reservation implements Comparable<Reservation> { | ||
private final long id; | ||
private final String name; | ||
private final LocalDateTime dateTime; |
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.
엔티티랑 도메인을 구분하지 않았군 👍 나는 엄밀하게 구분되어야 한다고 생각했는데 대부분의 경우 그렇지 않나봐 (매핑 과정만 많아지는 단점)
지금 시점에서는 이를 구분하는게 아무 의미가 없이 구조만 복잡해지는 것 같아.
KeyHolder keyHolder = new GeneratedKeyHolder(); | ||
Reservation reservation = fromRequest(reservationRequest); | ||
jdbcTemplate.update(con -> { | ||
PreparedStatement preparedStatement = con.prepareStatement( | ||
"insert into reservation (name, date,time) values ( ?,?,? )", new String[]{"id"}); | ||
preparedStatement.setString(1, reservation.getName()); | ||
preparedStatement.setDate(2, Date.valueOf(reservation.getDate())); | ||
preparedStatement.setTime(3, Time.valueOf(reservation.getTime())); | ||
return preparedStatement; | ||
}, keyHolder); | ||
return new Reservation(keyHolder.getKey().longValue(), reservation); |
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.
jdbcTemplate의 기술적인 한계인 것 같음
SimpleJdbcInsert라는 키워드 있던데 로빈은 이미 알고 있을 것 같지만 혹시나 해서 공유!
|
||
class AdminControllerTest { | ||
@Test | ||
@DisplayName("관리자 메인 페이지 경로를 정해진 경로로 매핑한다.") | ||
void mainPage() { | ||
AdminController adminController = new AdminController(); | ||
String mainPage = adminController.mainPage(); | ||
Assertions.assertThat(mainPage) | ||
.isEqualTo("admin/index"); | ||
} | ||
|
||
@Test | ||
@DisplayName("관리자 예약 정보 페이지 경로를 정해진 경로로 매핑한다.") | ||
void reservationPage() { | ||
AdminController adminController = new AdminController(); | ||
String reservationPage = adminController.reservationPage(); | ||
Assertions.assertThat(reservationPage) | ||
.isEqualTo("admin/reservation-legacy"); | ||
} | ||
} |
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.
오 뷰 컨트롤러테스트를 이렇게 바라볼수도 있구나 새로운 관점 확인하고 갑니다.
근데 이러한 테스트에 대해서 로빈은 어떻게 생각해? 컨트롤러의 여러 기능이 테스트되고 있지 않은 것 같아 (뷰 렌더링 등등..) 이는 우리가 테스트할 영역이 아니라고 생각하는지 궁금하군 💭
뷰를 랜더링하는 부분은 우리가 개발하는 부분이 아니라, 우리가 사용하는 외부 의존성인 "스프링 프레임워크"의 역할이라고 생각해. 따라서 이를 테스트 하는 것은 통합 테스트 혹은 E2E 테스트로 바라봐야겠지. 그런데, 리뷰 남겨준 테스트 코드는 단위 테스트기 때문에 그건 검증 대상이 아니라고 생각해.
RestAssured.given().log().all() | ||
.contentType(ContentType.JSON) | ||
.body(params) | ||
.when().post("/reservations") | ||
.then().log().all() | ||
.statusCode(200) | ||
.body("id", is(1)); | ||
|
||
RestAssured.given().log().all() | ||
.when().get("/reservations") | ||
.then().log().all() | ||
.statusCode(200) | ||
.body("size()", is(1)); | ||
|
||
RestAssured.given().log().all() | ||
.when().delete("/reservations/1") | ||
.then().log().all() | ||
.statusCode(200); | ||
|
||
RestAssured.given().log().all() | ||
.when().get("/reservations") | ||
.then().log().all() | ||
.statusCode(200) | ||
.body("size()", is(0)); |
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.
이 테스트 어떻게 구분할 수 있을까 🤔 여러 행동을 테스트하고 있는데 혹시 로빈의 아이디어 있을까? 난 모름 (진짜 모름)
애초에 여러 행위를 복합적으로 테스트하라고 만든 테스트야. 각 행위가 연속적으로 수행되었을 때에도 정상적으로 수행되는지 확인하는 거지.
public class CollectionReservationRepository implements ReservationRepository { | ||
private final List<Reservation> reservations; | ||
private final AtomicLong atomicLong; |
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.
테스트에서 사용되는 레포지토리인듯!?
fake 객체 같은 것이라고 이해하면 되나?
그렇기엔 사용되는 부분이 어디인지 찾기 힘드네
로빈의 의도를 알려줄 수 있을까?
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.
테스트에서 사용되는 레포지토리인듯!? fake 객체 같은 것이라고 이해하면 되나?
그렇기엔 사용되는 부분이 어디인지 찾기 힘드네 로빈의 의도를 알려줄 수 있을까?
ControllerTest 에서는 실제 DB와 연결되는 Repository가 사용되는 것이 아니라 이 레퍼지토리를 사용해. 이를 통해서 Repository가 정상적으로 동작할 때, Controller가 잘 동작하는지 확인할 수 있는거지.
No description provided.