-
Notifications
You must be signed in to change notification settings - Fork 0
레디 방탈출 예약 관리 1 ~ 6 단계 #3
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: reddevilmidzy
Are you sure you want to change the base?
Changes from all commits
47a5417
91636e7
fe6c2b4
366736c
a2c158f
401ed5e
9bceeea
8b4a779
3e15676
bae93d1
296bcfc
bdb14c6
684ba2f
4296b21
7eb4d31
8fbaf55
335c725
f9ca722
cde4dbd
1b67fb1
087d49e
27818d7
3e1dc45
ec84b87
1e55564
771aa3d
521c7ee
00aa82a
066bfe1
e15440a
d1cae20
19a70a8
aa5e221
92a74e6
7ed40ca
b69aaab
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 |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| # 방탈출 예약 관리 | ||
|
|
||
| ## API 명세 | ||
|
|
||
| ### 예약 조회 API | ||
|
|
||
| **Request** | ||
|
|
||
| ```http request | ||
| GET /reservations HTTP/1.1 | ||
| ``` | ||
|
|
||
| <br> | ||
|
|
||
| **Response** | ||
|
|
||
| ``` | ||
| HTTP/1.1 200 | ||
| Content-Type: application/json | ||
| [ | ||
| { | ||
| "id": 1, | ||
| "name": "브라운", | ||
| "date": "2023-01-01", | ||
| "time": "10:00" | ||
| }, | ||
| { | ||
| "id": 2, | ||
| "name": "브라운", | ||
| "date": "2023-01-02", | ||
| "time": "11:00" | ||
| } | ||
| ] | ||
| ``` | ||
| <br> | ||
|
|
||
| ### 예약 추가 API | ||
|
|
||
| **Request** | ||
|
|
||
| ```http request | ||
| POST /reservations HTTP/1.1 | ||
| content-type: application/json | ||
| { | ||
| "date": "2023-08-05", | ||
| "name": "브라운", | ||
| "time": "15:40" | ||
| } | ||
| ``` | ||
|
|
||
| <br> | ||
|
|
||
| **Response** | ||
|
|
||
| ``` | ||
| HTTP/1.1 201 | ||
| Location: reservations/{id} | ||
| Content-Type: application/json | ||
| { | ||
| "id": 1, | ||
| "name": "브라운", | ||
| "date": "2023-08-05", | ||
| "time": "15:40" | ||
| } | ||
| ``` | ||
|
|
||
| <br> | ||
|
|
||
| ### 예약 취소 API | ||
|
|
||
| **Request** | ||
|
|
||
| ```http request | ||
| DELETE /reservations/1 HTTP/1.1 | ||
| ``` | ||
|
|
||
| **Response** | ||
| ``` | ||
| HTTP/1.1 204 | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,9 +17,11 @@ dependencies { | |
| implementation 'org.springframework.boot:spring-boot-starter-web' | ||
| implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' | ||
| implementation 'org.springframework.boot:spring-boot-starter-jdbc' | ||
| implementation 'org.springframework.boot:spring-boot-devtools' | ||
| runtimeOnly 'com.h2database:h2' | ||
| testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
| testImplementation 'io.rest-assured:rest-assured:5.3.1' | ||
| testRuntimeOnly 'com.h2database:h2' | ||
|
Comment on lines
+20
to
+24
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. 각각 라이브러리가 있는 것과 없는 것에는 어떤 차이가 있어? |
||
| } | ||
|
|
||
| test { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,8 @@ | |
|
|
||
| @SpringBootApplication | ||
| public class RoomescapeApplication { | ||
| public static void main(String[] args) { | ||
|
|
||
| public static void main(final String[] args) { | ||
| SpringApplication.run(RoomescapeApplication.class, args); | ||
| } | ||
|
|
||
| } | ||
|
Comment on lines
6
to
12
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.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package roomescape.controller; | ||
|
|
||
| import org.springframework.stereotype.Controller; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
|
|
||
| @Controller | ||
| @RequestMapping("/admin") | ||
| public class AdminController { | ||
|
|
||
| @GetMapping | ||
| public String home() { | ||
| return "/admin/index"; | ||
| } | ||
|
|
||
| @GetMapping("/reservation") | ||
| public String reservation() { | ||
| return "/admin/reservation-legacy"; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package roomescape.controller; | ||
|
|
||
| import java.net.URI; | ||
| import java.util.List; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.DeleteMapping; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import org.springframework.web.util.UriComponentsBuilder; | ||
| import roomescape.dto.ReservationRequest; | ||
| import roomescape.dto.ReservationResponse; | ||
| import roomescape.service.ReservationService; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/reservations") | ||
| public class ReservationController { | ||
|
|
||
| private final ReservationService reservationService; | ||
|
|
||
| public ReservationController(final ReservationService reservationService) { | ||
| this.reservationService = reservationService; | ||
| } | ||
|
|
||
| @GetMapping | ||
| public List<ReservationResponse> getReservations() { | ||
| return reservationService.findAll(); | ||
| } | ||
|
|
||
| @PostMapping | ||
| public ResponseEntity<ReservationResponse> save(@RequestBody final ReservationRequest reservationRequest) { | ||
| final ReservationResponse reservationResponse = reservationService.save(reservationRequest); | ||
| final URI uri = UriComponentsBuilder.fromPath("/reservations/{id}") | ||
| .buildAndExpand(reservationResponse.id()) | ||
| .toUri(); | ||
| return ResponseEntity.created(uri) | ||
|
Comment on lines
+36
to
+39
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. 레디가 내 리뷰에도 남겨줬던것 같은데, 나는 오히려 해당 표현이 불필요하게 길어진다는 느낌을 받는 것 같아. |
||
| .body(reservationResponse); | ||
| } | ||
|
|
||
| @DeleteMapping("/{id}") | ||
| public ResponseEntity<Void> delete(@PathVariable("id") final long id) { | ||
| try { | ||
| reservationService.remove(id); | ||
| return ResponseEntity.noContent() | ||
| .build(); | ||
| } catch (final IllegalArgumentException exception) { | ||
| return ResponseEntity.notFound() | ||
| .build(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package roomescape.dto; | ||
|
|
||
| import roomescape.model.Reservation; | ||
|
|
||
| public record ReservationRequest(String name, String date, String time) { | ||
|
|
||
| public Reservation toReservation(final Long id) { | ||
| return new Reservation(id, name, date, time); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,15 @@ | ||||||||||||||||||||||||||||||||
| package roomescape.dto; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import java.time.format.DateTimeFormatter; | ||||||||||||||||||||||||||||||||
| import roomescape.model.Reservation; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public record ReservationResponse(Long id, String name, String date, String time) { | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public static ReservationResponse from(final Reservation reservation) { | ||||||||||||||||||||||||||||||||
| final String date = reservation.getDate().format(DateTimeFormatter.ISO_DATE); | ||||||||||||||||||||||||||||||||
| final String time = reservation.getTime().format(TIME_FORMATTER); | ||||||||||||||||||||||||||||||||
| return new ReservationResponse(reservation.getId(), reservation.getName(), date, time); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+14
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. 약간 억지(?)일수도 있다고 생각하는데, ISO_DATE 보다는 직접 글자로 명시해주는게 더 직관적일수 있다 생각하는데, 레디는 어떻게 생각해?
Suggested change
|
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| package roomescape.model; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.time.LocalTime; | ||
| import java.util.Objects; | ||
|
|
||
| public class Reservation { | ||
|
|
||
| private final Long id; | ||
| private final String name; | ||
| private final LocalDate date; | ||
| private final LocalTime time; | ||
|
|
||
| private Reservation(final Long id, final String name, final LocalDate date, final LocalTime time) { | ||
| this.id = id; | ||
| this.name = name; | ||
| this.date = date; | ||
| this.time = time; | ||
| } | ||
|
|
||
| public Reservation(final Long id, final String name, final String date, final String time) { | ||
| this(id, name, LocalDate.parse(date), LocalTime.parse(time)); | ||
| } | ||
|
|
||
| public static Reservation create(final String name, final String date, final String time) { | ||
| return new Reservation(null, name, date, time); | ||
| } | ||
|
Comment on lines
+25
to
+27
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. 생성자가 아닌 정적 팩토리 메서드를 만든 이유가 있을까? 그리고 메서드 명을 create로 한 것도 이유가 궁금해! |
||
|
|
||
| public Reservation toReservation(final long id) { | ||
| return new Reservation(id, name, date, time); | ||
| } | ||
|
Comment on lines
+29
to
+31
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. 개인적으로 이 메서드명도 의미가 명확하지 않다고 느껴지는 것 같아! |
||
|
|
||
| public Long getId() { | ||
| return id; | ||
| } | ||
|
|
||
| public String getName() { | ||
| return name; | ||
| } | ||
|
|
||
| public LocalDate getDate() { | ||
| return date; | ||
| } | ||
|
|
||
| public LocalTime getTime() { | ||
| return time; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(final Object target) { | ||
| if (this == target) { | ||
| return true; | ||
| } | ||
| if (target == null || getClass() != target.getClass()) { | ||
| return false; | ||
| } | ||
| final Reservation reservation = (Reservation) target; | ||
| return Objects.equals(getId(), reservation.getId()) && Objects.equals(getName(), reservation.getName()) | ||
| && Objects.equals(getDate(), reservation.getDate()) && Objects.equals(getTime(), | ||
| reservation.getTime()); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(getId(), getName(), getDate(), getTime()); | ||
| } | ||
|
Comment on lines
+49
to
+66
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. 적절한 동등성 부여인지 고민이 필요할 것 같아! id만 같으면 동등한 객체로 보아도 괜찮지 않을까? |
||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "Reservation{" + | ||
| "id=" + id + | ||
| ", name='" + name + '\'' + | ||
| ", date=" + date + | ||
| ", time=" + time + | ||
| '}'; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| package roomescape.repository; | ||
|
|
||
| import java.sql.Date; | ||
| import java.sql.PreparedStatement; | ||
| import java.sql.Time; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.Optional; | ||
| import javax.sql.DataSource; | ||
| import org.springframework.dao.EmptyResultDataAccessException; | ||
| import org.springframework.jdbc.core.JdbcTemplate; | ||
| import org.springframework.jdbc.support.GeneratedKeyHolder; | ||
| import org.springframework.jdbc.support.KeyHolder; | ||
| import org.springframework.stereotype.Repository; | ||
| import roomescape.model.Reservation; | ||
|
|
||
| @Repository | ||
| public class ReservationDao { | ||
|
|
||
| private final JdbcTemplate jdbcTemplate; | ||
|
|
||
| public ReservationDao(final DataSource dataSource) { | ||
| this.jdbcTemplate = new JdbcTemplate(dataSource); | ||
| } | ||
|
|
||
| public Reservation save(final Reservation reservation) { | ||
| final String sql = "INSERT INTO reservation (name, date, time) VALUES (?, ?, ?)"; | ||
| final KeyHolder keyHolder = new GeneratedKeyHolder(); | ||
|
|
||
| jdbcTemplate.update(connection -> { | ||
| final PreparedStatement preparedStatement = connection.prepareStatement(sql, 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); | ||
|
|
||
| final long id = Objects.requireNonNull(keyHolder.getKey()).longValue(); | ||
| return reservation.toReservation(id); | ||
| } | ||
|
|
||
| public Optional<Reservation> findById(final long id) { | ||
| try { | ||
| final Reservation reservation = jdbcTemplate.queryForObject( | ||
| "SELECT id, name, date, time FROM reservation WHERE id = ?", | ||
| (resultSet, rowNum) -> new Reservation( | ||
| resultSet.getLong("id"), | ||
| resultSet.getString("name"), | ||
| resultSet.getString("date"), | ||
| resultSet.getString("time") | ||
| ), id); | ||
| return Optional.ofNullable(reservation); | ||
| } catch (final EmptyResultDataAccessException exception) { | ||
| return Optional.empty(); | ||
| } | ||
| } | ||
|
Comment on lines
+52
to
+56
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.
|
||
|
|
||
| public List<Reservation> findAll() { | ||
| return jdbcTemplate.query( | ||
| "SELECT id, name, date, time FROM reservation", | ||
| (resultSet, rowNum) -> new Reservation( | ||
| resultSet.getLong("id"), | ||
| resultSet.getString("name"), | ||
| resultSet.getString("date"), | ||
| resultSet.getString("time") | ||
| )); | ||
| } | ||
|
|
||
| public void remove(final long id) { | ||
| final String sql = "DELETE FROM reservation WHERE id = ?"; | ||
| jdbcTemplate.update(sql, 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.
👍 👍 👍