-
Notifications
You must be signed in to change notification settings - Fork 142
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] 김이화 미션 제출합니다. #309
base: ihwag719
Are you sure you want to change the base?
Changes from 11 commits
17cd0d9
13cd4b3
63d8d62
a9d5050
7a8a744
fdfdee3
04f3a9b
60f812f
767e6c9
d2d37fc
8b9792a
2e650e6
6913375
663b475
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 |
---|---|---|
@@ -1,77 +1,40 @@ | ||
package roomescape.controller; | ||
|
||
import org.springframework.http.HttpStatus; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.annotation.*; | ||
import org.springframework.web.context.request.WebRequest; | ||
import roomescape.exception.InvalidReservationException; | ||
import roomescape.exception.NotFoundReservationException; | ||
import roomescape.model.Reservation; | ||
import roomescape.dto.ReservationRequestDto; | ||
import roomescape.dto.ReservationResponseDto; | ||
import roomescape.service.ReservationService; | ||
|
||
import java.net.URI; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
|
||
@Controller | ||
@RestController | ||
@RequestMapping("/reservations") | ||
public class ReservationController { | ||
|
||
private List<Reservation> reservations = new ArrayList<>(); | ||
@Autowired | ||
private final ReservationService reservationService; | ||
|
||
private AtomicLong index = new AtomicLong(1); | ||
|
||
@GetMapping("/reservation") | ||
public String reservation() { | ||
return "reservation"; | ||
public ReservationController(ReservationService reservationService) { | ||
this.reservationService = reservationService; | ||
} | ||
|
||
@GetMapping("/reservations") | ||
@ResponseBody | ||
public List<Reservation> getReservations() { | ||
return reservations; | ||
@GetMapping | ||
public List<ReservationResponseDto> getReservations() { | ||
return reservationService.getAllReservations(); | ||
} | ||
|
||
@PostMapping("/reservations") | ||
@ResponseBody | ||
public ResponseEntity<Reservation> createReservation(@RequestBody Reservation reservation) { | ||
validateReservation(reservation); | ||
|
||
Long id = index.getAndIncrement(); | ||
String name = reservation.getName(); | ||
String date = reservation.getDate(); | ||
String time = reservation.getTime(); | ||
|
||
Reservation newReservation = new Reservation(id, name, date, time); | ||
reservations.add(newReservation); | ||
|
||
return ResponseEntity.created(URI.create("/reservations/" + newReservation.getId())).body(newReservation); | ||
@PostMapping | ||
public ResponseEntity<ReservationResponseDto> createReservation(@RequestBody ReservationRequestDto requestDto) { | ||
ReservationResponseDto responseDto = reservationService.createReservation(requestDto); | ||
return ResponseEntity.created(URI.create("/reservations/" + responseDto.getId())).body(responseDto); | ||
} | ||
|
||
@DeleteMapping("/reservations/{id}") | ||
@ResponseBody | ||
@DeleteMapping("/{id}") | ||
public ResponseEntity<Void> deleteReservation(@PathVariable Long id) { | ||
boolean removed = reservations.removeIf(r -> r.getId() == id); | ||
if (!removed) { | ||
throw new NotFoundReservationException("예약을 찾을 수 없습니다: " + id); | ||
} | ||
reservationService.deleteReservation(id); | ||
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
@ExceptionHandler({InvalidReservationException.class, NotFoundReservationException.class}) | ||
public ResponseEntity<String> handleException(RuntimeException e, WebRequest request) { | ||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); | ||
} | ||
|
||
private void validateReservation(Reservation reservation) { | ||
if (reservation.getName() == null || reservation.getName().isEmpty()) { | ||
throw new InvalidReservationException("이름이 필요합니다."); | ||
} | ||
if (reservation.getDate() == null || reservation.getDate().isEmpty()) { | ||
throw new InvalidReservationException("날짜가 필요합니다."); | ||
} | ||
if (reservation.getTime() == null || reservation.getTime().isEmpty()) { | ||
throw new InvalidReservationException("시간이 필요합니다"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package roomescape.controller; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
import roomescape.domain.Time; | ||
import roomescape.service.TimeService; | ||
|
||
import java.net.URI; | ||
import java.util.List; | ||
|
||
@RestController | ||
@RequestMapping("/times") | ||
public class TimeController { | ||
|
||
@Autowired | ||
private final TimeService timeService; | ||
public TimeController(TimeService timeService) { | ||
this.timeService = timeService; | ||
} | ||
|
||
@GetMapping | ||
public List<Time> getTimes() { | ||
return timeService.getAllTimes(); | ||
} | ||
|
||
@PostMapping | ||
public ResponseEntity<Time> createTime(@RequestBody Time time) { | ||
Time newTime = timeService.createTime(time); | ||
return ResponseEntity.created((URI.create("/times/" + newTime.getId()))).body(newTime); | ||
} | ||
|
||
@DeleteMapping("/{id}") | ||
public ResponseEntity<Void> deleteTime(@PathVariable Long id) { | ||
timeService.deleteTime(id); | ||
return ResponseEntity.noContent().build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package roomescape.controller; | ||
|
||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
|
||
@Controller | ||
@RequestMapping("/reservations") | ||
public class viewController { | ||
@GetMapping("/reservationpage") | ||
public String reservationPage() { | ||
return "new-reservation"; | ||
} | ||
} | ||
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. 클래스 이름은 대문자로 시작하는 것이 관례입니다! 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. 추가로 해당 URI로도 실행했을 때 화면이 표시되나요? 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,80 @@ | ||||||||||||||||||||||||||||
package roomescape.dao; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
import org.springframework.beans.factory.annotation.Autowired; | ||||||||||||||||||||||||||||
import org.springframework.jdbc.core.JdbcTemplate; | ||||||||||||||||||||||||||||
import org.springframework.jdbc.core.RowMapper; | ||||||||||||||||||||||||||||
import org.springframework.jdbc.support.GeneratedKeyHolder; | ||||||||||||||||||||||||||||
import org.springframework.jdbc.support.KeyHolder; | ||||||||||||||||||||||||||||
import org.springframework.stereotype.Repository; | ||||||||||||||||||||||||||||
import roomescape.exception.NotFoundReservationException; | ||||||||||||||||||||||||||||
import roomescape.domain.Reservation; | ||||||||||||||||||||||||||||
import roomescape.domain.Time; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
import java.sql.PreparedStatement; | ||||||||||||||||||||||||||||
import java.util.List; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
@Repository | ||||||||||||||||||||||||||||
public class ReservationDAO { | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
@Autowired | ||||||||||||||||||||||||||||
private JdbcTemplate jdbcTemplate; | ||||||||||||||||||||||||||||
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. 필드 주입을 선택하신 이유가 있나요?
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 ReservationDAO(JdbcTemplate jdbcTemplate, TimeDAO timeDAO) { | ||||||||||||||||||||||||||||
this.jdbcTemplate = jdbcTemplate; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
private final RowMapper<Reservation> rowMapper = (resultSet, rowNum) -> { | ||||||||||||||||||||||||||||
Time time = new Time( | ||||||||||||||||||||||||||||
resultSet.getLong("time_id"), | ||||||||||||||||||||||||||||
resultSet.getString("time_value") | ||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||
return new Reservation( | ||||||||||||||||||||||||||||
resultSet.getLong("reservation_id"), | ||||||||||||||||||||||||||||
resultSet.getString("name"), | ||||||||||||||||||||||||||||
resultSet.getString("date"), | ||||||||||||||||||||||||||||
time | ||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
public List<Reservation> findAllReservations() { | ||||||||||||||||||||||||||||
String sql = "SELECT r.id as reservation_id, r.name, r.date, t.id as time_id, t.time as time_value " + | ||||||||||||||||||||||||||||
"FROM reservation as r " + | ||||||||||||||||||||||||||||
"inner join time as t on r.time_id = t.id"; | ||||||||||||||||||||||||||||
return jdbcTemplate.query(sql, rowMapper); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
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.
Suggested change
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 Reservation insert(Reservation reservation) { | ||||||||||||||||||||||||||||
Time time = reservation.getTime(); | ||||||||||||||||||||||||||||
KeyHolder keyHolder = new GeneratedKeyHolder(); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
jdbcTemplate.update(connection -> { | ||||||||||||||||||||||||||||
PreparedStatement ps = connection.prepareStatement( | ||||||||||||||||||||||||||||
"INSERT INTO time(time) VALUES (?)", new String[]{"id"}); | ||||||||||||||||||||||||||||
ps.setString(1, time.getTime()); | ||||||||||||||||||||||||||||
return ps; | ||||||||||||||||||||||||||||
}, keyHolder); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
Long timeId = keyHolder.getKey().longValue(); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
jdbcTemplate.update(connection -> { | ||||||||||||||||||||||||||||
PreparedStatement ps = connection.prepareStatement( | ||||||||||||||||||||||||||||
"INSERT INTO reservation(name, date, time_id) VALUES (?, ?, ?)", new String[]{"id"}); | ||||||||||||||||||||||||||||
ps.setString(1, reservation.getName()); | ||||||||||||||||||||||||||||
ps.setString(2, reservation.getDate()); | ||||||||||||||||||||||||||||
ps.setLong(3, timeId); | ||||||||||||||||||||||||||||
return ps; | ||||||||||||||||||||||||||||
}, keyHolder); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
Long reservationId = keyHolder.getKey().longValue(); | ||||||||||||||||||||||||||||
return new Reservation(reservationId, reservation.getName(), reservation.getDate(), new Time(timeId, time.getTime())); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
public int delete(Long id) { | ||||||||||||||||||||||||||||
String sql = "DELETE FROM reservation WHERE id = ?"; | ||||||||||||||||||||||||||||
int row = jdbcTemplate.update(sql, id); | ||||||||||||||||||||||||||||
if (row == 0) { | ||||||||||||||||||||||||||||
throw new NotFoundReservationException(id); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
return row; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package roomescape.dao; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.dao.EmptyResultDataAccessException; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.core.RowMapper; | ||
import org.springframework.jdbc.support.GeneratedKeyHolder; | ||
import org.springframework.jdbc.support.KeyHolder; | ||
import org.springframework.stereotype.Repository; | ||
import roomescape.domain.Time; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.util.List; | ||
|
||
@Repository | ||
public class TimeDAO { | ||
|
||
@Autowired | ||
private JdbcTemplate jdbcTemplate; | ||
|
||
public TimeDAO(JdbcTemplate jdbcTemplate) { | ||
this.jdbcTemplate = jdbcTemplate; | ||
} | ||
|
||
private final RowMapper<Time> rowMapper = (resultSet, rowNum) -> { | ||
Time time = new Time( | ||
resultSet.getLong("id"), | ||
resultSet.getString("time")); | ||
return time; | ||
}; | ||
|
||
public List<Time> findAllTime() { | ||
String sql = "SELECT id, time FROM time"; | ||
return jdbcTemplate.query(sql, rowMapper); | ||
} | ||
|
||
public Time findByTimeValue(String timeValue) { | ||
String sql = "SELECT id, time FROM time where time = ?"; | ||
try { | ||
return jdbcTemplate.queryForObject(sql, rowMapper, timeValue); | ||
} catch (EmptyResultDataAccessException e) { | ||
return null; | ||
} | ||
} | ||
|
||
public Time insert(Time time) { | ||
|
||
KeyHolder keyHolder = new GeneratedKeyHolder(); | ||
|
||
jdbcTemplate.update(connection -> { | ||
PreparedStatement ps = connection.prepareStatement( | ||
"INSERT INTO time(time) VALUES (?)", new String[]{"id"}); | ||
ps.setString(1, time.getTime()); | ||
return ps; | ||
}, keyHolder); | ||
|
||
Long id = keyHolder.getKey().longValue(); | ||
return new Time(id, time.getTime()); | ||
} | ||
|
||
public void delete(Long id) { | ||
String sql = "DELETE FROM time WHERE id = ?"; | ||
jdbcTemplate.update(sql, id); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package roomescape.domain; | ||
|
||
public class Reservation { | ||
private Long id; | ||
private String name; | ||
private String date; | ||
private Time time; | ||
|
||
public Reservation(Long id, String name, String date, Time time) { | ||
this.id = id; | ||
this.name = name; | ||
this.date = date; | ||
this.time = time; | ||
} | ||
|
||
public Long getId() { | ||
return id; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public String getDate() { | ||
return date; | ||
} | ||
|
||
public Time getTime() { | ||
return time; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package roomescape.domain; | ||
|
||
public class Time { | ||
private Long id; | ||
private String time; | ||
|
||
public Time(Long id, String time) { | ||
this.id = id; | ||
this.time = time; | ||
} | ||
|
||
public Long getId() { | ||
return id; | ||
} | ||
|
||
public String getTime() { | ||
return time; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package roomescape.dto; | ||
|
||
public class ReservationRequestDto { | ||
private String name; | ||
private String date; | ||
private String time; | ||
|
||
public ReservationRequestDto(String name, String date, String time) { | ||
this.name = name; | ||
this.date = date; | ||
this.time = time; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public String getDate() { | ||
return date; | ||
} | ||
|
||
public String getTime() { | ||
return time; | ||
} | ||
} |
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.
domain 객체(Time)를 Controller-Service-DAO 모든 계층에서 사용하고 있는 것 같아요. 만약 테이블 구조가 변경되어 domain 클래스가 변경된다면 무슨 문제가 생길 수 있을까요?
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.
모든 객체에서 동일한 도메인 객체(Time)를 사용하면, 테이블 구조가 변경될 때 해당 도메인 클래스도 변경되어야 하는데 이 변경은 모든 계층에 영향을 미치게 되어 각 계층의 코드도 함께 수정해야 합니다. 이렇게 되면 코드의 유연성을 저하시키고 도메인 객체의 변경이 빈번하게 발생하면 코드 수정 및 배포 과정이 복잡해질 수도 있습니다. 또한 테스트 코드도 함께 수정하게 되면서 테스트의 복잡성을 증가시키고 테스트 유지보수 비용도 높이게 됩니다.
즉, 모든 계층이 동일한 도메인 객체에 의존하기 때문에 여러 계층에 전파되어 시스템 결합도를 높이고 코드 유지보수가 어려워집니다. 또한 각 계층은 서로 다른 책임을 가지지만 동일한 도메인 객체를 공유하면 각 계층의 책임이 명확히 분리되지 않아 응집도를 낮추고 코드의 명확성을 떨어뜨립니다. 코드 개선해서 리팩토링하겠습니다!