diff --git a/README.md b/README.md new file mode 100644 index 000000000..17704f9e4 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +### 컨트롤러 (Controller) +- `AdminController`: `@Controller`를 사용하여 웹 페이지를 반환합니다. 사용자가 특정 경로로 접속했을 때, 해당하는 HTML 파일을 렌더링하여 보여줍니다. + - `/`: `home.html` (홈 페이지) + - `/reservation`: `reservation.html` (예약 관리 페이지) +- `ReservationController`: `@RestController`를 사용하여 RESTful API를 제공합니다. HTTP 요청을 받아 `ReservationService`를 호출하고, 그 결과를 JSON 형태로 반환합니다. + - `GET /reservations`: 모든 예약 목록을 조회합니다. + - `POST /reservations`: 새로운 예약을 생성합니다. + - `DELETE /reservations/{id}`: 특정 예약을 삭제합니다. + +### 서비스 (Service) +- `ReservationService`: 핵심 비즈니스 로직을 담당합니다. + - 예약 데이터를 `List` 형태로 메모리에 저장하고 관리합니다. + - `AtomicLong`을 사용하여 예약 ID를 순차적으로 생성합니다. + - 주요 메서드: + - `getAllReservations()`: 전체 예약 목록을 반환합니다. + - `addReservation()`: 새로운 예약을 리스트에 추가하고, 생성된 예약 객체를 반환합니다. + - `deleteReservation()`: ID를 기준으로 예약을 찾아 리스트에서 삭제합니다. 존재하지 않는 ID일 경우 `IllegalArgumentException`을 발생시킵니다. + +## 📝 API 엔드포인트 + +| Method | URL | 설명 | +| --- | --- | --- | +| `GET` | `/reservations` | 모든 예약을 조회합니다. | +| `POST` | `/reservations` | 새로운 예약을 생성합니다. | +| `DELETE` | `/reservations/{id}` | 특정 ID의 예약을 삭제합니다. | + +### `POST /reservations` 요청 예시 + +**Request Body:** +```json +{ + "name": "woowahan", + "date": "2025-11-13", + "time": "14:00" +} +``` + +**Response (201 Created):** +```json +{ + "id": 1, + "name": "woowahan", + "date": "2025-11-13", + "time": "14:00" +} +``` + +## 📄 페이지 + +- **홈**: `http://localhost:8080/` +- **예약 관리**: `http://localhost:8080/reservation` diff --git a/src/main/java/roomescape/RoomescapeApplication.java b/src/main/java/roomescape/RoomescapeApplication.java index 702706791..2ca0f743f 100644 --- a/src/main/java/roomescape/RoomescapeApplication.java +++ b/src/main/java/roomescape/RoomescapeApplication.java @@ -8,5 +8,4 @@ public class RoomescapeApplication { public static void main(String[] args) { SpringApplication.run(RoomescapeApplication.class, args); } - } diff --git a/src/main/java/roomescape/controller/AdminController.java b/src/main/java/roomescape/controller/AdminController.java index 5ff1dc3fe..dcd686ea4 100644 --- a/src/main/java/roomescape/controller/AdminController.java +++ b/src/main/java/roomescape/controller/AdminController.java @@ -12,8 +12,7 @@ public String home() { } @GetMapping("/reservation") - public String reservation() - { + public String reservation() { return "reservation"; } } diff --git a/src/main/java/roomescape/controller/GlobalExceptionHandler.java b/src/main/java/roomescape/controller/GlobalExceptionHandler.java index 5a70e2333..f8e55ba52 100644 --- a/src/main/java/roomescape/controller/GlobalExceptionHandler.java +++ b/src/main/java/roomescape/controller/GlobalExceptionHandler.java @@ -1,4 +1,4 @@ -package roomescape.controller; // (또는 roomescape.exception) +package roomescape.controller; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -19,8 +19,8 @@ public ResponseEntity> handleValidationExceptions( ex.getBindingResult() .getFieldErrors() .forEach(error -> - errors.put(error.getField(), error.getDefaultMessage()) - ); + errors.put(error.getField(), error.getDefaultMessage()) + ); return ResponseEntity.badRequest().body(errors); } diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 6788c3327..18ff306d7 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -25,7 +25,7 @@ public ReservationController(ReservationService service) { @GetMapping public List getAllReservations() { - return service.getAllReservations().stream() + return service.getAllReservations().stream() .map(ReservationResponse::from) // (::from은 ReservationResponse::from과 동일) .toList(); } @@ -41,11 +41,7 @@ public ResponseEntity createReservation( @Valid @RequestBody ReservationCreateRequest requestDto ) { - Reservation reservationToCreate = new Reservation( - requestDto.name(), - requestDto.date(), - requestDto.time() - ); + Reservation reservationToCreate = requestDto.toEntity(); Reservation savedReservation = service.addReservation(reservationToCreate); diff --git a/src/main/java/roomescape/dto/ReservationCreateRequest.java b/src/main/java/roomescape/dto/ReservationCreateRequest.java index 454f2b5d8..e926981e1 100644 --- a/src/main/java/roomescape/dto/ReservationCreateRequest.java +++ b/src/main/java/roomescape/dto/ReservationCreateRequest.java @@ -1,6 +1,12 @@ package roomescape.dto; +import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import roomescape.model.Reservation; + +import java.time.LocalDate; +import java.time.LocalTime; public record ReservationCreateRequest( @@ -8,11 +14,17 @@ public record ReservationCreateRequest( @NotBlank(message = "이름은 필수 항목입니다.") String name, - @NotBlank(message = "날짜는 필수 항목입니다.") - String date, + @NotNull(message = "날짜는 필수 항목입니다.") + @JsonFormat(pattern = "yyyy-MM-dd") + LocalDate date, - @NotBlank(message = "시간은 필수 항목입니다.") - String time + @NotNull(message = "시간은 필수 항목입니다.") + @JsonFormat(pattern = "HH:mm") + LocalTime time ) { - + public Reservation toEntity() { + return new Reservation(name, date, time); + } } + + diff --git a/src/main/java/roomescape/dto/ReservationDelete.java b/src/main/java/roomescape/dto/ReservationDelete.java deleted file mode 100644 index ca28d10a6..000000000 --- a/src/main/java/roomescape/dto/ReservationDelete.java +++ /dev/null @@ -1,11 +0,0 @@ -package roomescape.dto; - -public record ReservationDelete ( - int id, - String name, - String date, - String time -){ - - -} diff --git a/src/main/java/roomescape/dto/ReservationResponse.java b/src/main/java/roomescape/dto/ReservationResponse.java index c745fca75..599b2f589 100644 --- a/src/main/java/roomescape/dto/ReservationResponse.java +++ b/src/main/java/roomescape/dto/ReservationResponse.java @@ -1,20 +1,23 @@ package roomescape.dto; import roomescape.model.Reservation; + +import java.time.LocalDate; +import java.time.LocalTime; + public record ReservationResponse( - long id, - String name, - String date, - String time - ) - { - public static ReservationResponse from(Reservation reservation) { - return new ReservationResponse( - reservation.getId(), - reservation.getName(), - reservation.getDate(), - reservation.getTime() - ); - } + long id, + String name, + LocalDate date, + LocalTime time +) { + public static ReservationResponse from(Reservation reservation) { + return new ReservationResponse( + reservation.getId(), + reservation.getName(), + reservation.getDate(), + reservation.getTime() + ); } +} diff --git a/src/main/java/roomescape/model/Reservation.java b/src/main/java/roomescape/model/Reservation.java index 767743d98..336082c9e 100644 --- a/src/main/java/roomescape/model/Reservation.java +++ b/src/main/java/roomescape/model/Reservation.java @@ -1,37 +1,27 @@ package roomescape.model; -public class Reservation -{ - Long id; - String name; - String date; - String time; +import java.time.LocalDate; +import java.time.LocalTime; +public class Reservation { + private Long id; + private String name; + private LocalDate date; + private LocalTime time; - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getDate() { - return date; - } - - public String getTime() { - return time; - } - - public Reservation(String name, String date, String time) { + public Reservation(Long id, String name, LocalDate date, LocalTime time) { + this.id = id; this.name = name; this.date = date; this.time = time; } - public Reservation(Long id, String name, String date, String time) { - this(name, date, time); - this.id = id; + public Reservation(String name, LocalDate date, LocalTime time) { + this(null, name, date, time); } + + public Long getId() { return id; } + public String getName() { return name; } + public LocalDate getDate() { return date; } + public LocalTime getTime() { return time; } } diff --git a/src/main/java/roomescape/repository/ReservationRepository.java b/src/main/java/roomescape/repository/ReservationRepository.java new file mode 100644 index 000000000..79cf25d56 --- /dev/null +++ b/src/main/java/roomescape/repository/ReservationRepository.java @@ -0,0 +1,62 @@ +package roomescape.repository; + +import org.springframework.stereotype.Repository; +import roomescape.model.Reservation; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +@Repository +public class ReservationRepository { + + private final List reservations = new ArrayList<>(); + private final AtomicLong counter = new AtomicLong(); + + public ReservationRepository() { + this.save(new Reservation( + null, + "브라운", + LocalDate.of(2025, 1, 1), + LocalTime.of(12, 0) + )); + + this.save(new Reservation( + null, + "코니", + LocalDate.of(2025, 1, 2), + LocalTime.of(11, 0) + )); + } + + public List findAll() { + return new ArrayList<>(reservations); + } + + public Reservation save(Reservation reservation) { + Reservation savedReservation = new Reservation( + counter.incrementAndGet(), + reservation.getName(), + reservation.getDate(), + reservation.getTime() + ); + reservations.add(savedReservation); + return savedReservation; + } + + public boolean existsById(Long id) { + return reservations.stream() + .anyMatch(reservation -> reservation.getId().equals(id)); + } + + public void deleteById(Long id) { + reservations.removeIf(reservation -> reservation.getId().equals(id)); + } + + public void clear() { + reservations.clear(); + counter.set(0L); + } +} diff --git a/src/main/java/roomescape/service/ReservationService.java b/src/main/java/roomescape/service/ReservationService.java index 8f60e5af5..b760fb189 100644 --- a/src/main/java/roomescape/service/ReservationService.java +++ b/src/main/java/roomescape/service/ReservationService.java @@ -1,7 +1,9 @@ package roomescape.service; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import roomescape.model.Reservation; +import roomescape.repository.ReservationRepository; import java.util.ArrayList; import java.util.List; @@ -10,45 +12,34 @@ @Service public class ReservationService { - private final List reservations = new ArrayList<>(); + private final ReservationRepository reservationRepository; - private final AtomicLong counter = new AtomicLong(); - - public ReservationService() { - reservations.add(new Reservation(counter.incrementAndGet(), "브라운", "2025-01-01", "10:00")); - reservations.add(new Reservation(counter.incrementAndGet(), "코니", "2025-01-02", "11:00")); + @Autowired + public ReservationService(ReservationRepository reservationRepository) { + this.reservationRepository = reservationRepository; } public List getAllReservations() { - return reservations; + return reservationRepository.findAll(); } public Reservation addReservation(Reservation newReservation) { - Reservation savedReservation = new Reservation( - counter.incrementAndGet(), // 새 ID 발급 - newReservation.getName(), - newReservation.getDate(), - newReservation.getTime() - ); - reservations.add(savedReservation); - return savedReservation; - } + return reservationRepository.save(newReservation); + } public void deleteReservation(Long id) { - boolean exists = reservations.stream() - .anyMatch(reservation -> reservation.getId().equals(id)); + boolean exists = reservationRepository.existsById(id); if (!exists) { throw new IllegalArgumentException("존재하지 않는 예약 ID입니다: " + id); } - reservations.removeIf(reservation -> reservation.getId().equals(id)); + reservationRepository.deleteById(id); } public void clear() { - reservations.clear(); - counter.set(0L); //테스트를 위한 코드 입니다. + reservationRepository.clear(); } }