From 356d87e645c6acb7d92b4fd16eaabe4068986ae5 Mon Sep 17 00:00:00 2001 From: kim nan seul <154604350+seulnan@users.noreply.github.com> Date: Fri, 25 Apr 2025 19:20:08 +0900 Subject: [PATCH 01/25] =?UTF-8?q?feat:=204=EB=8B=A8=EA=B3=84=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Member, Time, Reservation 도메인에 대해 JpaRepository 기반 전환 - Time 및 Theme Entity에 deleted 필드 추가 후 findByDeletedFalse() 적용 - schema.sql 충돌 방지를 위해 spring.sql.init.mode=never 설정 - 기존 서비스 로직을 Repository 기반으로 리팩토링하면서 API 기능 유지 --- build.gradle | 2 +- .../com/yourssu/roomescape/member/Member.java | 30 ++-- .../roomescape/member/MemberRepository.java | 14 ++ .../roomescape/member/MemberService.java | 12 +- .../roomescape/reservation/Reservation.java | 9 ++ .../reservation/ReservationDao.java | 128 ------------------ .../reservation/ReservationRepository.java | 14 ++ .../reservation/ReservationResponse.java | 2 +- .../reservation/ReservationService.java | 45 ++++-- .../com/yourssu/roomescape/theme/Theme.java | 21 ++- .../roomescape/theme/ThemeController.java | 16 ++- .../yourssu/roomescape/theme/ThemeDao.java | 42 ------ .../roomescape/theme/ThemeRepository.java | 9 ++ .../com/yourssu/roomescape/time/Time.java | 35 +++-- .../roomescape/time/TimeController.java | 2 +- .../com/yourssu/roomescape/time/TimeDao.java | 42 ------ .../roomescape/time/TimeRepository.java | 10 ++ .../yourssu/roomescape/time/TimeService.java | 24 ++-- src/main/resources/application.properties | 9 +- src/main/resources/schema.sql | 60 -------- .../yourssu/roomescape/Week2MissionTest.java | 31 +++++ 21 files changed, 216 insertions(+), 341 deletions(-) create mode 100644 src/main/java/com/yourssu/roomescape/member/MemberRepository.java delete mode 100644 src/main/java/com/yourssu/roomescape/reservation/ReservationDao.java create mode 100644 src/main/java/com/yourssu/roomescape/reservation/ReservationRepository.java delete mode 100644 src/main/java/com/yourssu/roomescape/theme/ThemeDao.java create mode 100644 src/main/java/com/yourssu/roomescape/theme/ThemeRepository.java delete mode 100644 src/main/java/com/yourssu/roomescape/time/TimeDao.java create mode 100644 src/main/java/com/yourssu/roomescape/time/TimeRepository.java delete mode 100644 src/main/resources/schema.sql create mode 100644 src/test/java/com/yourssu/roomescape/Week2MissionTest.java diff --git a/build.gradle b/build.gradle index 6b61c3f..ad7b228 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter:3.4.4' implementation 'org.springframework.boot:spring-boot-starter-web:3.4.4' - implementation 'org.springframework.boot:spring-boot-starter-jdbc:3.4.4' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.4.4' implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.5.1' diff --git a/src/main/java/com/yourssu/roomescape/member/Member.java b/src/main/java/com/yourssu/roomescape/member/Member.java index b3b7a37..098a3ac 100644 --- a/src/main/java/com/yourssu/roomescape/member/Member.java +++ b/src/main/java/com/yourssu/roomescape/member/Member.java @@ -1,25 +1,35 @@ package com.yourssu.roomescape.member; +import jakarta.persistence.*; import lombok.Getter; @Getter +@Entity +@Table(name = "member") public class Member { - private final Long id; - private final String name; - private final String email; - private final String password; - private final String role; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - public Member(Long id, String name, String email, String password, String role) { - this.id = id; + private String name; + private String email; + private String password; + private String role; + + protected Member() {} + + public Member(String name, String email, String password, String role) { this.name = name; this.email = email; this.password = password; this.role = role; } - public Member(String name, String email, String password, String role) { - this(null, name, email, password, role); + public Member(Long id, String name, String email, String password, String role) { + this.id = id; + this.name = name; + this.email = email; + this.password = password; + this.role = role; } - } diff --git a/src/main/java/com/yourssu/roomescape/member/MemberRepository.java b/src/main/java/com/yourssu/roomescape/member/MemberRepository.java new file mode 100644 index 0000000..7b58e4e --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/member/MemberRepository.java @@ -0,0 +1,14 @@ +package com.yourssu.roomescape.member; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface MemberRepository extends JpaRepository { + + Optional findByEmail(String email); + + Optional findByName(String name); + + Optional findByEmailAndPassword(String email, String password); +} diff --git a/src/main/java/com/yourssu/roomescape/member/MemberService.java b/src/main/java/com/yourssu/roomescape/member/MemberService.java index a36472c..fc56b1d 100644 --- a/src/main/java/com/yourssu/roomescape/member/MemberService.java +++ b/src/main/java/com/yourssu/roomescape/member/MemberService.java @@ -5,25 +5,25 @@ @Service public class MemberService { - private final MemberDao memberDao; + private final MemberRepository memberRepository; - public MemberService(MemberDao memberDao) { - this.memberDao = memberDao; + public MemberService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; } public MemberResponse createMember(MemberRequest request) { Member member = new Member(request.getName(), request.getEmail(), request.getPassword(), "USER"); - Member saved = memberDao.save(member); + Member saved = memberRepository.save(member); return new MemberResponse(saved.getId(), saved.getName(), saved.getEmail()); } public Member findByEmail(String email) { - return memberDao.findByEmail(email) + return memberRepository.findByEmail(email) .orElseThrow(() -> new MemberNotFoundException("이메일이 일치하는 회원이 없습니다.")); } public Member findByName(String name) { - return memberDao.findByName(name) + return memberRepository.findByName(name) .orElseThrow(() -> new MemberNotFoundException("이름이 일치하는 회원이 없습니다.")); } } diff --git a/src/main/java/com/yourssu/roomescape/reservation/Reservation.java b/src/main/java/com/yourssu/roomescape/reservation/Reservation.java index 84f7f5d..c935c67 100644 --- a/src/main/java/com/yourssu/roomescape/reservation/Reservation.java +++ b/src/main/java/com/yourssu/roomescape/reservation/Reservation.java @@ -2,14 +2,23 @@ import com.yourssu.roomescape.theme.Theme; import com.yourssu.roomescape.time.Time; +import jakarta.persistence.*; import lombok.Getter; +@Entity @Getter public class Reservation { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private String name; private String date; + + @ManyToOne(fetch = FetchType.LAZY) private Time time; + + @ManyToOne(fetch = FetchType.LAZY) private Theme theme; public Reservation(Long id, String name, String date, Time time, Theme theme) { diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationDao.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationDao.java deleted file mode 100644 index 733bb51..0000000 --- a/src/main/java/com/yourssu/roomescape/reservation/ReservationDao.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.yourssu.roomescape.reservation; - -import com.yourssu.roomescape.theme.Theme; -import com.yourssu.roomescape.time.Time; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -import java.sql.PreparedStatement; -import java.util.List; -import java.util.Objects; - -@Repository -public class ReservationDao { - - private final JdbcTemplate jdbcTemplate; - - public ReservationDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findAll() { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id", - - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public Reservation save(ReservationRequest reservationRequest) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - PreparedStatement ps = connection.prepareStatement("INSERT INTO reservation(date, name, theme_id, time_id) VALUES (?, ?, ?, ?)", new String[]{"id"}); - ps.setString(1, reservationRequest.getDate()); - ps.setString(2, reservationRequest.getName()); - ps.setLong(3, reservationRequest.getTheme()); - ps.setLong(4, reservationRequest.getTime()); - return ps; - }, keyHolder); - - Time time = jdbcTemplate.queryForObject("SELECT * FROM time WHERE id = ?", - (rs, rowNum) -> new Time(rs.getLong("id"), rs.getString("time_value")), - reservationRequest.getTime()); - - Theme theme = jdbcTemplate.queryForObject("SELECT * FROM theme WHERE id = ?", - (rs, rowNum) -> new Theme(rs.getLong("id"), rs.getString("name"), rs.getString("description")), - reservationRequest.getTheme()); - - return new Reservation( - Objects.requireNonNull(keyHolder.getKey()).longValue(), - reservationRequest.getName(), - reservationRequest.getDate(), - time, - theme - ); - } - - public void deleteById(Long id) { - jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); - } - - public List findReservationsByDateAndTheme(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id" + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public List findByDateAndThemeId(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id " + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } -} diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationRepository.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationRepository.java new file mode 100644 index 0000000..b91cfde --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/reservation/ReservationRepository.java @@ -0,0 +1,14 @@ +package com.yourssu.roomescape.reservation; + +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ReservationRepository extends JpaRepository { + + List findByDateAndThemeId(String date, Long themeId); + + @EntityGraph(attributePaths = {"theme", "time"}) + List findAll(); +} diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationResponse.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationResponse.java index 42521f8..12dc0c0 100644 --- a/src/main/java/com/yourssu/roomescape/reservation/ReservationResponse.java +++ b/src/main/java/com/yourssu/roomescape/reservation/ReservationResponse.java @@ -17,7 +17,7 @@ public static ReservationResponse of(Reservation reservation) { reservation.getId(), reservation.getName(), reservation.getDate(), - reservation.getTime().getValue(), + reservation.getTime().getTime_value(), reservation.getTheme().getName() ); } diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationService.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationService.java index 2151126..1e16ddc 100644 --- a/src/main/java/com/yourssu/roomescape/reservation/ReservationService.java +++ b/src/main/java/com/yourssu/roomescape/reservation/ReservationService.java @@ -3,6 +3,10 @@ import com.yourssu.roomescape.auth.LoginMember; import com.yourssu.roomescape.member.Member; import com.yourssu.roomescape.member.MemberService; +import com.yourssu.roomescape.theme.Theme; +import com.yourssu.roomescape.theme.ThemeRepository; +import com.yourssu.roomescape.time.Time; +import com.yourssu.roomescape.time.TimeRepository; import org.springframework.stereotype.Service; import java.util.List; @@ -10,15 +14,21 @@ @Service public class ReservationService { - private final ReservationDao reservationDao; + private final ReservationRepository reservationRepository; private final MemberService memberService; + private final ThemeRepository themeRepository; + private final TimeRepository timeRepository; public ReservationService( - ReservationDao reservationDao, - MemberService memberService + ReservationRepository reservationRepository, + MemberService memberService, + ThemeRepository themeRepository, + TimeRepository timeRepository ) { - this.reservationDao = reservationDao; + this.reservationRepository = reservationRepository; this.memberService = memberService; + this.themeRepository = themeRepository; + this.timeRepository = timeRepository; } public ReservationResponse save(ReservationRequest request, LoginMember loginMember) { @@ -27,18 +37,35 @@ public ReservationResponse save(ReservationRequest request, LoginMember loginMem : memberService.findByEmail(loginMember.getEmail()); ReservationRequest filledRequest = request.withName(member.getName()); - Reservation reservation = reservationDao.save(filledRequest); - return ReservationResponse.of(reservation); + Theme theme = themeRepository.findById(filledRequest.getTheme()) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 테마입니다.")); + Time time = timeRepository.findById(filledRequest.getTime()) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 시간입니다.")); + + Reservation reservation = new Reservation( + filledRequest.getName(), + filledRequest.getDate(), + time, + theme + ); + + return ReservationResponse.of(reservationRepository.save(reservation)); } public void deleteById(Long id) { - reservationDao.deleteById(id); + reservationRepository.deleteById(id); } public List findAll() { - return reservationDao.findAll().stream() - .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getValue())) + return reservationRepository.findAll().stream() + .map(it -> new ReservationResponse( + it.getId(), + it.getName(), + it.getTheme().getName(), + it.getDate(), + it.getTime().getTime_value() + )) .toList(); } } diff --git a/src/main/java/com/yourssu/roomescape/theme/Theme.java b/src/main/java/com/yourssu/roomescape/theme/Theme.java index 3103408..2bcf5fc 100644 --- a/src/main/java/com/yourssu/roomescape/theme/Theme.java +++ b/src/main/java/com/yourssu/roomescape/theme/Theme.java @@ -1,25 +1,36 @@ package com.yourssu.roomescape.theme; +import jakarta.persistence.*; import lombok.Getter; @Getter +@Entity +@Table(name = "theme") public class Theme { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private String name; private String description; - public Theme() { - } + @Column(nullable = false) + private boolean deleted = false; - public Theme(Long id, String name, String description) { - this.id = id; + protected Theme() {} + + public Theme(String name, String description) { this.name = name; this.description = description; } - public Theme(String name, String description) { + public Theme(Long id, String name, String description) { + this.id = id; this.name = name; this.description = description; } + public void markDeleted() { + this.deleted = true; + } } diff --git a/src/main/java/com/yourssu/roomescape/theme/ThemeController.java b/src/main/java/com/yourssu/roomescape/theme/ThemeController.java index d55b8c4..b9c9ef7 100644 --- a/src/main/java/com/yourssu/roomescape/theme/ThemeController.java +++ b/src/main/java/com/yourssu/roomescape/theme/ThemeController.java @@ -8,26 +8,30 @@ @RestController public class ThemeController { - private final ThemeDao themeDao; + private final ThemeRepository themeRepository; - public ThemeController(ThemeDao themeDao) { - this.themeDao = themeDao; + public ThemeController(ThemeRepository themeRepository) { + this.themeRepository = themeRepository; } @PostMapping("/themes") public ResponseEntity createTheme(@RequestBody Theme theme) { - Theme newTheme = themeDao.save(theme); + Theme newTheme = themeRepository.save(theme); return ResponseEntity.created(URI.create("/themes/" + newTheme.getId())).body(newTheme); } @GetMapping("/themes") public ResponseEntity> list() { - return ResponseEntity.ok(themeDao.findAll()); + return ResponseEntity.ok(themeRepository.findByDeletedFalse()); } @DeleteMapping("/themes/{id}") public ResponseEntity deleteTheme(@PathVariable Long id) { - themeDao.deleteById(id); + themeRepository.findById(id) + .ifPresent(theme -> { + theme.markDeleted(); + themeRepository.save(theme); + }); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/com/yourssu/roomescape/theme/ThemeDao.java b/src/main/java/com/yourssu/roomescape/theme/ThemeDao.java deleted file mode 100644 index b1afa9b..0000000 --- a/src/main/java/com/yourssu/roomescape/theme/ThemeDao.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.yourssu.roomescape.theme; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Objects; - -@Repository -public class ThemeDao { - private final JdbcTemplate jdbcTemplate; - - public ThemeDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findAll() { - return jdbcTemplate.query("SELECT * FROM theme where deleted = false", (rs, rowNum) -> new Theme( - rs.getLong("id"), - rs.getString("name"), - rs.getString("description") - )); - } - - public Theme save(Theme theme) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - var ps = connection.prepareStatement("INSERT INTO theme(name, description) VALUES (?, ?)", new String[]{"id"}); - ps.setString(1, theme.getName()); - ps.setString(2, theme.getDescription()); - return ps; - }, keyHolder); - - return new Theme(Objects.requireNonNull(keyHolder.getKey()).longValue(), theme.getName(), theme.getDescription()); - } - - public void deleteById(Long id) { - jdbcTemplate.update("UPDATE theme SET deleted = true WHERE id = ?", id); - } -} diff --git a/src/main/java/com/yourssu/roomescape/theme/ThemeRepository.java b/src/main/java/com/yourssu/roomescape/theme/ThemeRepository.java new file mode 100644 index 0000000..0c9764f --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/theme/ThemeRepository.java @@ -0,0 +1,9 @@ +package com.yourssu.roomescape.theme; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ThemeRepository extends JpaRepository { + List findByDeletedFalse(); +} diff --git a/src/main/java/com/yourssu/roomescape/time/Time.java b/src/main/java/com/yourssu/roomescape/time/Time.java index 4b298e2..3867e59 100644 --- a/src/main/java/com/yourssu/roomescape/time/Time.java +++ b/src/main/java/com/yourssu/roomescape/time/Time.java @@ -1,27 +1,34 @@ package com.yourssu.roomescape.time; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Entity +@Table(name = "times") public class Time { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String value; - - public Time(Long id, String value) { - this.id = id; - this.value = value; - } - public Time(String value) { - this.value = value; - } + @Setter + @Column(name = "time_value", nullable = false) + private String time_value; - public Time() { + @Setter + @Column(nullable = false) + private boolean deleted = false; + protected Time() { } - public Long getId() { - return id; + public Time(String time_value) { + this.time_value = time_value; } - public String getValue() { - return value; + public Time(Long id, String time_value) { + this.id = id; + this.time_value = time_value; } } diff --git a/src/main/java/com/yourssu/roomescape/time/TimeController.java b/src/main/java/com/yourssu/roomescape/time/TimeController.java index 228b805..f1c8114 100644 --- a/src/main/java/com/yourssu/roomescape/time/TimeController.java +++ b/src/main/java/com/yourssu/roomescape/time/TimeController.java @@ -21,7 +21,7 @@ public List