-
Notifications
You must be signed in to change notification settings - Fork 78
[Spring Data JPA] 김민지 4-6단계 미션 제출합니다. #190
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
base: mmm307955
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,3 +193,164 @@ public class MissionStepTest { | |
} | ||
} | ||
``` | ||
## 🚀 4단계 - JPA 전환 | ||
### 요구사항 | ||
- [x] JPA를 활용하여 데이터베이스에 접근하도록 수정하세요. | ||
### 세부 요구사항 | ||
#### gradle 의존성 추가 | ||
- [x] build.gradle 파일을 이용하여 다음 의존성을 대체하세요. | ||
- [x] as is: spring-boot-stater-jdbc | ||
- [x] to be: spring-boot-starter-data-jpa | ||
#### 엔티티 매핑 | ||
- [x] 다른 클래스를 의존하지 않는 클래스 먼저 엔티티 설정을 하세요. | ||
- [x] ex) Theme나 Time 등 | ||
#### 연관관계 매핑 | ||
- [x] 다른 클래스에 의존하는 클래스는 연관관계 매핑을 추가로 하세요. | ||
- [x] ex) Reservation은 Member나 Theme 등의 객체에 의존합니다. | ||
### 요구사항 테스트 | ||
```java | ||
@DataJpaTest | ||
public class JpaTest { | ||
@Autowired | ||
private TestEntityManager entityManager; | ||
|
||
@Autowired | ||
private TimeRepository timeRepository; | ||
|
||
@Test | ||
void 사단계() { | ||
Time time = new Time("10:00"); | ||
entityManager.persist(time); | ||
entityManager.flush(); | ||
|
||
Time persistTime = timeRepository.findById(time.getId()).orElse(null); | ||
|
||
assertThat(persistTime.getTime()).isEqualTo(time.getTime()); | ||
} | ||
} | ||
|
||
``` | ||
|
||
## 🚀 5단계 - 내 예약 목록 조회 | ||
### 요구사항 | ||
- [x] 내 예약 목록을 조회하는 API를 구현하세요. | ||
### 내 예약 목록 기능 | ||
- [x] 아래의 request와 response 요구사항에 따라 기능을 구현하세요. | ||
#### Request | ||
``` | ||
GET /reservations-mine HTTP/1.1 | ||
cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwibmFtZSI6IuyWtOuTnOuvvCIsInJvbGUiOiJBRE1JTiJ9.vcK93ONRQYPFCxT5KleSM6b7cl1FE-neSLKaFyslsZM | ||
host: localhost:8080 | ||
``` | ||
#### Response | ||
``` | ||
HTTP/1.1 200 | ||
Content-Type: application/json | ||
|
||
[ | ||
{ | ||
"reservationId": 1, | ||
"theme": "테마1", | ||
"date": "2024-03-01", | ||
"time": "10:00", | ||
"status": "예약" | ||
}, | ||
{ | ||
"reservationId": 2, | ||
"theme": "테마2", | ||
"date": "2024-03-01", | ||
"time": "12:00", | ||
"status": "예약" | ||
}, | ||
{ | ||
"reservationId": 3, | ||
"theme": "테마3", | ||
"date": "2024-03-01", | ||
"time": "14:00", | ||
"status": "예약" | ||
} | ||
] | ||
|
||
``` | ||
### 요구사항 테스트 | ||
```java | ||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) | ||
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) | ||
public class MissionStepTest { | ||
@Test | ||
void 오단계() { | ||
String adminToken = createToken("[email protected]", "password"); | ||
|
||
List<MyReservationResponse> reservations = RestAssured.given().log().all() | ||
.cookie("token", adminToken) | ||
.get("/reservations-mine") | ||
.then().log().all() | ||
.statusCode(200) | ||
.extract().jsonPath().getList(".", MyReservationResponse.class); | ||
|
||
assertThat(reservations).hasSize(3); | ||
} | ||
} | ||
``` | ||
|
||
## 🚀 6단계 - 예약 대기 기능 | ||
### 요구사항 | ||
- [x] 예약 대기 요청 기능을 구현하세요. | ||
- [x] 예약 대기 취소 기능도 함께 구현하세요. | ||
- [x] 내 예약 목록 조회 시 예약 대기 목록도 함께 포함하세요. | ||
- [x] 중복 예약이 불가능 하도록 구현하세요. | ||
|
||
> ⚠️ 심화 요구사항 - 내 예약 목록의 예약 대기 상태에 몇 번째 대기인지도 함께 표시하세요. | ||
#### 예약 대기 요청 | ||
 | ||
#### 내 예약 목록에서 조회 & 예약 대기 취소 | ||
 | ||
### 요구사항 테스트 | ||
```java | ||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) | ||
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) | ||
public class MissionStepTest { | ||
@Test | ||
void 육단계() { | ||
String brownToken = createToken("[email protected]", "password"); | ||
|
||
Map<String, String> params = new HashMap<>(); | ||
params.put("date", "2024-03-01"); | ||
params.put("time", "1"); | ||
params.put("theme", "1"); | ||
|
||
// 예약 대기 생성 | ||
WaitingResponse waiting = RestAssured.given().log().all() | ||
.body(params) | ||
.cookie("token", brownToken) | ||
.contentType(ContentType.JSON) | ||
.post("/waitings") | ||
.then().log().all() | ||
.statusCode(201) | ||
.extract().as(WaitingResponse.class); | ||
|
||
// 내 예약 목록 조회 | ||
List<MyReservationResponse> myReservations = RestAssured.given().log().all() | ||
.body(params) | ||
.cookie("token", brownToken) | ||
.contentType(ContentType.JSON) | ||
.get("/reservations-mine") | ||
.then().log().all() | ||
.statusCode(200) | ||
.extract().jsonPath().getList(".", MyReservationResponse.class); | ||
|
||
// 예약 대기 상태 확인 | ||
String status = myReservations.stream() | ||
.filter(it -> it.getId() == waiting.getId()) | ||
.filter(it -> !it.getStatus().equals("예약")) | ||
.findFirst() | ||
.map(it -> it.getStatus()) | ||
.orElse(null); | ||
|
||
assertThat(status).isEqualTo("1번째 예약대기"); | ||
} | ||
} | ||
|
||
|
||
``` | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package roomescape.member; | ||
|
||
import java.util.Optional; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public interface MemberRepository extends JpaRepository<Member, Long> { | ||
Optional<Member> findByEmailAndPassword(String email, String password); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,15 +4,16 @@ | |
|
||
@Service | ||
public class MemberService { | ||
private MemberDao memberDao; | ||
private final MemberRepository memberRepository; | ||
|
||
public MemberService(MemberDao memberDao) { | ||
this.memberDao = memberDao; | ||
public MemberService(MemberRepository memberRepository) { | ||
this.memberRepository = memberRepository; | ||
} | ||
|
||
public MemberResponse createMember(MemberRequest memberRequest) { | ||
Member member = memberDao.save(new Member(memberRequest.getName(), memberRequest.getEmail(), | ||
memberRequest.getPassword(), "USER")); | ||
Member member = memberRepository.save( | ||
new Member(memberRequest.getName(), memberRequest.getEmail(), | ||
memberRequest.getPassword(), "USER")); | ||
Comment on lines
-7
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 데이터 접근 계층에 대한 변경이 있었을 뿐인데, 이와 연관 없는 비즈니스 계층까지 변경이 발생했네요. 코드 변경이 많아질 수록, 실수로 인해 의도치 않은 버그의 발생 확률이 높아지고, 연관이 없는 코드의 변경이 많아질 수록, 명확한 변경 사항의 추적이 힘들어질 거에요. 또한 JPA의 학습 곡선이 너무 높거나, 내부 기술 제약으로 인해 다시 JdbcTemplate을 사용한 코드로 되돌려야 한다면, 다시 이러한 변경이 발생할 것 같아요. 그렇다면 데이터 접근 계층의 구조를 어떻게 설계하고 사용했어야 이러한 연관 없는 변경을 막을 수 있을까요? |
||
return new MemberResponse(member.getId(), member.getName(), member.getEmail()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package roomescape.reservation; | ||
|
||
import roomescape.waiting.Waiting; | ||
|
||
public record MyReservationResponse( | ||
Long id, | ||
String theme, | ||
String date, | ||
String time, | ||
String status | ||
) { | ||
public static MyReservationResponse from(Reservation reservation) { | ||
return new MyReservationResponse( | ||
reservation.getId(), | ||
reservation.getTheme().getName(), | ||
reservation.getDate(), | ||
reservation.getTime().getTime(), | ||
"예약" | ||
); | ||
} | ||
|
||
public static MyReservationResponse from(Waiting waiting, Long rank) { | ||
return new MyReservationResponse( | ||
waiting.getId(), | ||
waiting.getTheme().getName(), | ||
waiting.getDate(), | ||
waiting.getTime().getTime(), | ||
(rank + 1) + "번째 예약대기" | ||
); | ||
} | ||
} |
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.
@GeneratedValue
의strategy
속성을 지정하지 않는다면 기본 값은 어떤게 사용되나요?또한 strategy의 종류는 어떤게 있는지 알아봐도 좋을 것 같아요.