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

[Spring Core] 김의진 미션 제출합니다. #392

Open
wants to merge 4 commits into
base: sansan20535
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@

## 🧐고민사항

😈 "/"로 요청을 보냈는데 아무것도 인식하지 못하는 문제
> 💡thymeleaf의존성 추가 후 해결
>

😈 "/"로 요청을 보냈는데 아무것도 인식하지 못하는 문제
> 💡thymeleaf의존성 추가 후 해결
>
> 템플릿 엔진 없이 렌더링 하는 방법 : https://bottom-to-top.tistory.com/38

# 🚀2단계 - 예약 조회
Expand All @@ -24,13 +23,14 @@

- [x] /reservation 렌더링
- [x] 예약 목록 조회 API 구현

---

## 🧐고민사항

😈 builder 패턴을 인식하지 못하는 상태
> 💡lombok 의존성 수정

>
> 참고 : https://ururuwave.tistory.com/66

# 🚀3단계 - 예약 추가/취소
Expand All @@ -42,6 +42,8 @@
- [x] 예약 추가 API 구현
- [x] 예약 삭제 API 구현

---

# 🚀4단계 - 예외 처리

---
Expand All @@ -52,6 +54,7 @@
- [x] 예약 삭제 시 식별자로 저장된 예약을 찾을 수 없는 경우 예외 처리

## 🧐고민사항

😈삭제할 예약이 없는 경우는 NOT_FOUND를 반환하는 게 알맞다고 생각했다. 하지만 요청을 보낼 때 없는 번호로 보낸다면 이 상황에서는 BAD_REQUEST가 맞다고도 생각했다.
> 이에 대한 고민이 더 필요할 것 같다.

Expand Down Expand Up @@ -101,3 +104,32 @@
😈 update vs batchUpdate

😈 keyHolder? PreparedStatement? NotSerializableException?

# 🚀8단계 - 시간 관리 기능

---

## 🧐고민사항

😈 Service에 데이터를 저장하고 불러오는 DB관련이 기능이 있는 것이 어색함
> DAO객체를 만들어 이곳에서 처리하도록 수정
> Reservation도 이처럼 수정할 예정


# 🚀9단계 - 기존 코드 수정

---

## 🔧구현사항

- [ ] 스키마 수정
- [ ] 예약 페이지 파일 수정
- [ ] 예약 클래스 수정
- [ ] 예약 쿼리 수정
Comment on lines +125 to +128

Choose a reason for hiding this comment

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

요런 부분은 [x] 로 체크 표시를 할 수 있어요


---

## 🧐고민사항

😈 기존 형식으로 들어올 때 오류발생
> 컨트롤러 단에서 처리
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class ReservationException extends RuntimeException {
private final String message;
private final HttpStatus httpStatus;

protected ReservationException(final ErrorMessage errorMessage) {
public ReservationException(final ErrorMessage errorMessage) {
super(errorMessage.getMessage());
this.message = errorMessage.getMessage();
this.httpStatus = errorMessage.getHttpStatus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,46 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import roomescape.api.reservations.dto.request.ReservationRegisterRequest;
import roomescape.api.reservations.dto.response.ReservationResponse;
import roomescape.api.reservations.service.ReservationsService;
import roomescape.db.entity.ReservationsEntity;

import java.net.URI;
import java.util.List;

@RestController
@RequestMapping("/reservations")
@RequiredArgsConstructor
public class ReservationsController {
public class ReservationController {

private final ReservationsService reservationsService;

@GetMapping
public ResponseEntity<List<ReservationsEntity>> getReservations() {
return ResponseEntity.status(HttpStatus.OK).body(reservationsService.getReservations());
public ResponseEntity<List<ReservationResponse>> getReservations() {
return ResponseEntity.status(HttpStatus.OK)
.body(reservationsService.getReservations());
}

Comment on lines +22 to 26

Choose a reason for hiding this comment

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

저희 회사에서 가장 많이 보는 코드 형태네요 👍
저는 개인적으로 해주신 Service 에서 응답을 만들어주는 것을 선호해요

@PostMapping
public ResponseEntity<Void> createReservation(
public ResponseEntity<ReservationResponse> createReservation(
@RequestBody @Valid final ReservationRegisterRequest reservationRegisterRequest
) {

final long reservationsId = reservationsService.createReservations(
final ReservationResponse reservationResponse = reservationsService.createReservations(
reservationRegisterRequest.name(),
reservationRegisterRequest.date(),
reservationRegisterRequest.time()
reservationRegisterRequest.timeId()
);

return ResponseEntity.created(URI.create("/reservations/" + reservationsId)).build();
return ResponseEntity.status(HttpStatus.CREATED)
.header("Location", "/reservations/" + reservationResponse.id())
.body(reservationResponse);
}

@DeleteMapping("/{reservationId}")
public ResponseEntity<Void> deleteReservation(
@PathVariable(name = "reservationId") final long reservationId
) {

reservationsService.deleteReservations(reservationId);

return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null);
return ResponseEntity.status(HttpStatus.NO_CONTENT)
.body(null);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package roomescape.api.reservations.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record ReservationRegisterRequest(
@NotBlank(message = "날짜가 비어있습니다.")
String date,
@NotBlank(message = "이름이 비어있습니다.")
String name,
@NotBlank(message = "시간이 비어있습니다.")
String time
@NotNull(message = "시간이 비어있습니다.")
Long timeId
Comment on lines +11 to +12

Choose a reason for hiding this comment

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

약간 궁금한 부분인데요
time 으로 받는 것이 아닌 timeId 를 두게 된 이유가 있을까요?

) {
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package roomescape.api.reservations.dto.response;

public record ReservationResponse(
long id,
String name,
String date,
String time
) {

public static ReservationResponse of(final long id, final String name, final String date, final String time) {
return new ReservationResponse(id, name, date, time);
}
}
Original file line number Diff line number Diff line change
@@ -1,56 +1,44 @@
package roomescape.api.reservations.service;

import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import roomescape.db.entity.ReservationsEntity;
import roomescape.api.reservations.dto.response.ReservationResponse;
import roomescape.db.reservation.dao.ReservationDao;
import roomescape.db.reservation.entity.ReservationEntity;

import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.List;

@Service
@RequiredArgsConstructor
public class ReservationsService {

private final JdbcTemplate jdbcTemplate;
private final ReservationDao reservationDao;

Choose a reason for hiding this comment

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

아마 요구사항에 dao 와 repository 를 구분하라는 내용이 있었던 것 같은데요 이 부분이 맞을까요? (제가 정확하게 몰라서 잘못 알고 있을 수도 있을 것 같아요)
아니라도 혹시 이 2가지는 어떤 차이가 있다고 생각하시나요?
학습을 위해서 repository 를 여기에 넣어보려고 시도해봐도 좋을 것 같아요!


@Transactional(readOnly = true)
public List<ReservationsEntity> getReservations() {
final String sql = "SELECT id, name, date, time FROM reservation";

return jdbcTemplate.query(
sql, (resultSet, rowNum) -> ReservationsEntity.builder()
.id(resultSet.getLong("id"))
.name(resultSet.getString("name"))
.date(resultSet.getString("date"))
.time(resultSet.getString("time"))
.build());
public List<ReservationResponse> getReservations() {
return reservationDao.getReservations().stream()
.map(reservationsEntity -> ReservationResponse.of(
reservationsEntity.getId(),
reservationsEntity.getName(),
reservationsEntity.getDate(),
reservationsEntity.getTime().getTime()))
.toList();
}

@Transactional
public long createReservations(final String name, final String date, final String time) {
final String sql = "INSERT INTO reservation(name, date, time) VALUES (?, ?, ?)";
final KeyHolder keyHolder = new GeneratedKeyHolder();

jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, name);
ps.setString(2, date);
ps.setString(3, time);
return ps;
}, keyHolder);

return keyHolder.getKey().longValue();
public ReservationResponse createReservations(final String name, final String date, final Long timeId) {
final ReservationEntity reservationEntity = reservationDao.createReservations(name, date, timeId);
return ReservationResponse.of(
reservationEntity.getId(),
reservationEntity.getName(),
reservationEntity.getDate(),
reservationEntity.getTime().getTime()
);
}

@Transactional
public void deleteReservations(final long reservationId) {
final String sql = "DELETE FROM reservation WHERE id = ?";

jdbcTemplate.update(sql, reservationId);
reservationDao.deleteReservations(reservationId);
}
}
46 changes: 46 additions & 0 deletions src/main/java/roomescape/api/times/controller/TimeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package roomescape.api.times.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import roomescape.api.times.dto.request.TimeRegisterRequest;
import roomescape.api.times.dto.response.TimeResponse;
import roomescape.api.times.service.TimeService;

import java.util.List;

@RestController
@RequestMapping("/times")
@RequiredArgsConstructor
public class TimeController {

private final TimeService timeService;

@GetMapping
public ResponseEntity<List<TimeResponse>> getTimes() {
return ResponseEntity.status(HttpStatus.OK)
.body(timeService.getTimes());
}

@PostMapping
public ResponseEntity<TimeResponse> createTime(
@RequestBody final TimeRegisterRequest timeRegisterRequest
) {
final TimeResponse timeResponse = timeService.createTime(
timeRegisterRequest.time()
);
return ResponseEntity.status(HttpStatus.CREATED)
.header("Location", "/times/" + timeResponse.id())
.body(timeResponse);
}

@DeleteMapping("/{timeId}")
public ResponseEntity<Void> deleteTime(
@PathVariable(name = "timeId") final long timeId
) {
timeService.deleteTime(timeId);
return ResponseEntity.status(HttpStatus.NO_CONTENT)
.body(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package roomescape.api.times.dto.request;

public record TimeRegisterRequest(
String time
) {
}
11 changes: 11 additions & 0 deletions src/main/java/roomescape/api/times/dto/response/TimeResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package roomescape.api.times.dto.response;

public record TimeResponse(
long id,
String time
) {

public static TimeResponse of(final long id, final String time) {
return new TimeResponse(id, time);
}
}
35 changes: 35 additions & 0 deletions src/main/java/roomescape/api/times/service/TimeService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package roomescape.api.times.service;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import roomescape.api.times.dto.response.TimeResponse;
import roomescape.db.time.dao.TimeDao;
import roomescape.db.time.entity.TimeEntity;

import java.util.List;

@Service
@RequiredArgsConstructor
public class TimeService {

private final TimeDao timeDao;

@Transactional(readOnly = true)
public List<TimeResponse> getTimes() {
return timeDao.getTimes().stream()
.map(timeEntity -> TimeResponse.of(timeEntity.getId(), timeEntity.getTime()))
.toList();
}

@Transactional
public TimeResponse createTime(final String time) {
final TimeEntity timeEntity = timeDao.createTime(time);
return TimeResponse.of(timeEntity.getId(), timeEntity.getTime());
}

@Transactional
public void deleteTime(final long timeId) {
timeDao.deleteTime(timeId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ public String renderingHomePage() {

@GetMapping("/reservation")
public String renderingReservationPage() {
return "reservation";
return "new-reservation";
}

@GetMapping("/time")
public String renderingTimePage() {
return "time";
}

}
Loading