From 4fa868dd8a63f4819913ae03754cd3e6f5a9817c Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Sat, 28 May 2022 21:03:36 +0900 Subject: [PATCH 01/35] =?UTF-8?q?feat:=20Entity=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Airbnb의 Entity생성 및 매핑을 함 --- .../com/team11/airbnb/domain/Address.java | 26 ++++++++++++ .../java/com/team11/airbnb/domain/Host.java | 26 ++++++++++++ .../com/team11/airbnb/domain/Reservation.java | 34 +++++++++++++++ .../airbnb/domain/ReservationSchedule.java | 19 +++++++++ .../java/com/team11/airbnb/domain/Review.java | 32 +++++++++++++++ .../java/com/team11/airbnb/domain/Room.java | 41 +++++++++++++++++++ .../com/team11/airbnb/domain/RoomImage.java | 23 +++++++++++ .../com/team11/airbnb/domain/RoomInfo.java | 27 ++++++++++++ .../java/com/team11/airbnb/domain/User.java | 26 ++++++++++++ .../com/team11/airbnb/domain/WishList.java | 27 ++++++++++++ 10 files changed, 281 insertions(+) create mode 100644 BE/src/main/java/com/team11/airbnb/domain/Address.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/Host.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/Reservation.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/ReservationSchedule.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/Review.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/Room.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/RoomImage.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/User.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/WishList.java diff --git a/BE/src/main/java/com/team11/airbnb/domain/Address.java b/BE/src/main/java/com/team11/airbnb/domain/Address.java new file mode 100644 index 000000000..b0b677c9e --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/Address.java @@ -0,0 +1,26 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Embeddable; +import lombok.Getter; + +@Embeddable +@Getter +public class Address { + + private String street; + private String city; + private String state; + private String zipcode; + private String country; + + protected Address() { + } + + public Address(String street, String city, String state, String zipcode, String country) { + this.street = street; + this.city = city; + this.state = state; + this.zipcode = zipcode; + this.country = country; + } +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/Host.java b/BE/src/main/java/com/team11/airbnb/domain/Host.java new file mode 100644 index 000000000..61db41c21 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/Host.java @@ -0,0 +1,26 @@ +package com.team11.airbnb.domain; + +import java.util.ArrayList; +import java.util.List; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +public class Host { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name ="host_id") + private Long id; + + private String name; + + @OneToMany(mappedBy = "host") + private List rooms = new ArrayList<>(); + +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/Reservation.java b/BE/src/main/java/com/team11/airbnb/domain/Reservation.java new file mode 100644 index 000000000..6d13fa419 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/Reservation.java @@ -0,0 +1,34 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +public class Reservation { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "room_id") + private Room room; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + @Embedded + private ReservationSchedule reservationSchedule; + + private int fixedPrice; + +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/ReservationSchedule.java b/BE/src/main/java/com/team11/airbnb/domain/ReservationSchedule.java new file mode 100644 index 000000000..199a26d12 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/ReservationSchedule.java @@ -0,0 +1,19 @@ +package com.team11.airbnb.domain; + +import java.time.LocalDateTime; +import javax.persistence.Embeddable; + +@Embeddable +public class ReservationSchedule { + + private LocalDateTime checkInDate; + private LocalDateTime checkOutDate; + + public ReservationSchedule() { + } + + public ReservationSchedule(LocalDateTime checkInDate, LocalDateTime checkOutDate) { + this.checkInDate = checkInDate; + this.checkOutDate = checkOutDate; + } +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/Review.java b/BE/src/main/java/com/team11/airbnb/domain/Review.java new file mode 100644 index 000000000..18a7cbaa2 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/Review.java @@ -0,0 +1,32 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +public class Review { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "review_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "room_id") + private Room room; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + private int grade; + private String content; + +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/Room.java b/BE/src/main/java/com/team11/airbnb/domain/Room.java new file mode 100644 index 000000000..4eefc7355 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/Room.java @@ -0,0 +1,41 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +public class Room { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "room_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "host_id") + private Host host; + + @Embedded + private Address address; + + @Column(name = "average_grade") + private Double averageGrade; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "review_id") + private Review review; + + @Embedded + private RoomInfo roomInfo; + + private String description; + private int price; +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java b/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java new file mode 100644 index 000000000..3e0d6b77b --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java @@ -0,0 +1,23 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +public class RoomImage { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "room_id") + private Room room; +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java b/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java new file mode 100644 index 000000000..cc0f59182 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java @@ -0,0 +1,27 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Embeddable; +import lombok.Getter; + +@Embeddable +@Getter +public class RoomInfo { + + private String roomType; + private String numberOfBedroom; + private String numberOfBed; + private String checkInTime; + private String checkOutTime; + + protected RoomInfo() { + } + + public RoomInfo(String roomType, String numberOfBedroom, String numberOfBed, String checkInTime, + String checkOutTime) { + this.roomType = roomType; + this.numberOfBedroom = numberOfBedroom; + this.numberOfBed = numberOfBed; + this.checkInTime = checkInTime; + this.checkOutTime = checkOutTime; + } +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/User.java b/BE/src/main/java/com/team11/airbnb/domain/User.java new file mode 100644 index 000000000..51ee759bf --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/User.java @@ -0,0 +1,26 @@ +package com.team11.airbnb.domain; + +import java.util.ArrayList; +import java.util.List; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_id") + private Long id; + + private String name; + + @OneToMany(mappedBy = "user") + private List wishLists = new ArrayList<>(); +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/WishList.java b/BE/src/main/java/com/team11/airbnb/domain/WishList.java new file mode 100644 index 000000000..4251f2c86 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/WishList.java @@ -0,0 +1,27 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +public class WishList { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "room_id") + private Room room; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; +} From eb8b8ae5c7f357b2e3c66e9dcc87769ae7d2b9b8 Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Tue, 31 May 2022 16:20:22 +0900 Subject: [PATCH 02/35] =?UTF-8?q?chore:=20=EC=B4=88=EA=B8=B0=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=9E=85=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 엔티티 설계 및 테스트 DB(RDS)에 더미 데이터 추가 --- BE/sql/data.sql | 23 +++++++++++++++++++ .../java/com/team11/airbnb/domain/Host.java | 1 - .../com/team11/airbnb/domain/Reservation.java | 3 +-- .../airbnb/domain/ReservationSchedule.java | 2 +- .../java/com/team11/airbnb/domain/Review.java | 1 - .../java/com/team11/airbnb/domain/Room.java | 18 ++++++++++----- .../com/team11/airbnb/domain/RoomImage.java | 9 ++++++-- .../com/team11/airbnb/domain/RoomInfo.java | 16 ++++++++----- .../com/team11/airbnb/domain/RoomType.java | 12 ++++++++++ .../java/com/team11/airbnb/domain/User.java | 3 +-- .../com/team11/airbnb/domain/WishList.java | 3 +-- BE/src/main/resources/application.properties | 1 - 12 files changed, 68 insertions(+), 24 deletions(-) create mode 100644 BE/sql/data.sql create mode 100644 BE/src/main/java/com/team11/airbnb/domain/RoomType.java delete mode 100644 BE/src/main/resources/application.properties diff --git a/BE/sql/data.sql b/BE/sql/data.sql new file mode 100644 index 000000000..3ccf8001a --- /dev/null +++ b/BE/sql/data.sql @@ -0,0 +1,23 @@ + +insert into room (city, country, state, street, zipcode, average_grade, description, name, price, check_in_time, + check_out_time, number_of_bathroom, number_of_bed, number_of_bedroom, room_type, host_id) +VALUES ('종로구', '한국', '서울시', '짱짱로', 1234, 3, '짱짱이에요', '[독채한옥] 도심속 고요한 휴식의 공간, 서울의하루 가회동', 196714, 14, 10, 2, 2, 2, + 'RESIDENCE', 1), + ('광주시', '한국', '경기도', '선을로', 1234, 4, '칭찬해요', '오포숲-숲뷰 맛집/넷플릭스/무료주차공간/매일 소독/서현10분', 100714, 15, 11, 2, 2, 2, 'SHARE_HOUSE', 1); + +insert into room_image (image_path, room_id) +values ('https://a0.muscache.com/im/pictures/miso/Hosting-22724133/original/8514626f-60c8-4118-b207-b25285305915.jpeg?im_w=960', + 1), + ('https://a0.muscache.com/im/pictures/miso/Hosting-22724133/original/a98f70e9-a56e-41cd-9363-2656dfccb353.jpeg?im_w=720', + 1), + ('https://a0.muscache.com/im/pictures/miso/Hosting-22724133/original/551e045b-8e9f-4c78-aa18-b32d6388ab59.jpeg?im_w=720', + 1), + ('https://a0.muscache.com/im/pictures/5a6d01a9-3ba3-457c-a504-de665e65057d.jpg?im_w=960', 2), + ('https://a0.muscache.com/im/pictures/ca4d3ad4-5bec-4643-83f5-785a7e8afc1e.jpg?im_w=720', 2), + ('https://a0.muscache.com/im/pictures/d29bb793-e264-422a-9a2b-96f5377f4153.jpg?im_w=720', 2); + +insert into host (name) +values ('meenzino'), + ('phil'); + + diff --git a/BE/src/main/java/com/team11/airbnb/domain/Host.java b/BE/src/main/java/com/team11/airbnb/domain/Host.java index 61db41c21..d69682716 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Host.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Host.java @@ -15,7 +15,6 @@ public class Host { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name ="host_id") private Long id; private String name; diff --git a/BE/src/main/java/com/team11/airbnb/domain/Reservation.java b/BE/src/main/java/com/team11/airbnb/domain/Reservation.java index 6d13fa419..de7ba35bb 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Reservation.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Reservation.java @@ -14,8 +14,7 @@ @NoArgsConstructor public class Reservation { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) diff --git a/BE/src/main/java/com/team11/airbnb/domain/ReservationSchedule.java b/BE/src/main/java/com/team11/airbnb/domain/ReservationSchedule.java index 199a26d12..98cdcf1b0 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/ReservationSchedule.java +++ b/BE/src/main/java/com/team11/airbnb/domain/ReservationSchedule.java @@ -9,7 +9,7 @@ public class ReservationSchedule { private LocalDateTime checkInDate; private LocalDateTime checkOutDate; - public ReservationSchedule() { + protected ReservationSchedule() { } public ReservationSchedule(LocalDateTime checkInDate, LocalDateTime checkOutDate) { diff --git a/BE/src/main/java/com/team11/airbnb/domain/Review.java b/BE/src/main/java/com/team11/airbnb/domain/Review.java index 18a7cbaa2..64987fc2d 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Review.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Review.java @@ -15,7 +15,6 @@ public class Review { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "review_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) diff --git a/BE/src/main/java/com/team11/airbnb/domain/Room.java b/BE/src/main/java/com/team11/airbnb/domain/Room.java index 4eefc7355..b893199f7 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Room.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Room.java @@ -1,5 +1,8 @@ package com.team11.airbnb.domain; +import java.util.ArrayList; +import java.util.List; + import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.Entity; @@ -9,6 +12,10 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import com.fasterxml.jackson.annotation.JsonManagedReference; + import lombok.NoArgsConstructor; @Entity @@ -16,7 +23,6 @@ public class Room { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "room_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @@ -26,16 +32,16 @@ public class Room { @Embedded private Address address; - @Column(name = "average_grade") private Double averageGrade; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "review_id") - private Review review; + + @OneToMany(mappedBy = "room") + @JsonManagedReference + private List roomImages = new ArrayList<>(); @Embedded private RoomInfo roomInfo; - + private String name; private String description; private int price; } diff --git a/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java b/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java index 3e0d6b77b..b1ca53149 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java +++ b/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java @@ -7,17 +7,22 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; + +import com.fasterxml.jackson.annotation.JsonBackReference; + import lombok.NoArgsConstructor; @Entity @NoArgsConstructor public class RoomImage { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) + @JsonBackReference @JoinColumn(name = "room_id") private Room room; + + private String imagePath; } diff --git a/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java b/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java index cc0f59182..437853f62 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java +++ b/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java @@ -1,5 +1,7 @@ package com.team11.airbnb.domain; +import java.time.LocalTime; + import javax.persistence.Embeddable; import lombok.Getter; @@ -8,19 +10,21 @@ public class RoomInfo { private String roomType; - private String numberOfBedroom; - private String numberOfBed; - private String checkInTime; - private String checkOutTime; + private int numberOfBedroom; + private int numberOfBed; + private int numberOfBathroom; + private LocalTime checkInTime; + private LocalTime checkOutTime; protected RoomInfo() { } - public RoomInfo(String roomType, String numberOfBedroom, String numberOfBed, String checkInTime, - String checkOutTime) { + public RoomInfo(String roomType, int numberOfBedroom, int numberOfBed, int numberOfBathroom, + LocalTime checkInTime, LocalTime checkOutTime) { this.roomType = roomType; this.numberOfBedroom = numberOfBedroom; this.numberOfBed = numberOfBed; + this.numberOfBathroom = numberOfBathroom; this.checkInTime = checkInTime; this.checkOutTime = checkOutTime; } diff --git a/BE/src/main/java/com/team11/airbnb/domain/RoomType.java b/BE/src/main/java/com/team11/airbnb/domain/RoomType.java new file mode 100644 index 000000000..43eb9f1b2 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/RoomType.java @@ -0,0 +1,12 @@ +package com.team11.airbnb.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum RoomType { + APARTMENT("아파트"), PRIVATE_ROOM("개인실"), RESIDENCE("레지던스 전체"), SHARE_HOUSE("공동주택 전체"); + + private final String type; +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/User.java b/BE/src/main/java/com/team11/airbnb/domain/User.java index 51ee759bf..a377a7a58 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/User.java +++ b/BE/src/main/java/com/team11/airbnb/domain/User.java @@ -14,8 +14,7 @@ @NoArgsConstructor public class User { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_id") private Long id; diff --git a/BE/src/main/java/com/team11/airbnb/domain/WishList.java b/BE/src/main/java/com/team11/airbnb/domain/WishList.java index 4251f2c86..acce4304a 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/WishList.java +++ b/BE/src/main/java/com/team11/airbnb/domain/WishList.java @@ -13,8 +13,7 @@ @NoArgsConstructor public class WishList { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne diff --git a/BE/src/main/resources/application.properties b/BE/src/main/resources/application.properties deleted file mode 100644 index 8b1378917..000000000 --- a/BE/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - From 5857eff0d819106918dd1aeea385d710b33dcc6f Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Thu, 2 Jun 2022 11:11:36 +0900 Subject: [PATCH 03/35] =?UTF-8?q?Feat:=20Room=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Room의 ID를 입력받으면 해당 Room의 상세정보를 반환한다. --- .../java/com/team11/airbnb/domain/Host.java | 5 +++- .../com/team11/airbnb/domain/Reservation.java | 2 ++ .../java/com/team11/airbnb/domain/Room.java | 12 ++++++---- .../com/team11/airbnb/domain/RoomImage.java | 2 ++ .../airbnb/web/controller/RoomController.java | 23 +++++++++++++++++++ .../airbnb/web/controller/RoomRepository.java | 10 ++++++++ .../airbnb/web/controller/RoomService.java | 18 +++++++++++++++ 7 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/controller/RoomRepository.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/controller/RoomService.java diff --git a/BE/src/main/java/com/team11/airbnb/domain/Host.java b/BE/src/main/java/com/team11/airbnb/domain/Host.java index d69682716..b94ca8659 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Host.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Host.java @@ -1,16 +1,18 @@ package com.team11.airbnb.domain; +import com.fasterxml.jackson.annotation.JsonManagedReference; import java.util.ArrayList; import java.util.List; -import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity +@Getter @NoArgsConstructor public class Host { @@ -20,6 +22,7 @@ public class Host { private String name; @OneToMany(mappedBy = "host") + @JsonManagedReference private List rooms = new ArrayList<>(); } diff --git a/BE/src/main/java/com/team11/airbnb/domain/Reservation.java b/BE/src/main/java/com/team11/airbnb/domain/Reservation.java index de7ba35bb..8559c6bf8 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Reservation.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Reservation.java @@ -8,10 +8,12 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity @NoArgsConstructor +@Getter public class Reservation { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/BE/src/main/java/com/team11/airbnb/domain/Room.java b/BE/src/main/java/com/team11/airbnb/domain/Room.java index b893199f7..8b19039dd 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Room.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Room.java @@ -1,9 +1,10 @@ package com.team11.airbnb.domain; +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonManagedReference; import java.util.ArrayList; import java.util.List; - -import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.FetchType; @@ -13,19 +14,20 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; - -import com.fasterxml.jackson.annotation.JsonManagedReference; - +import lombok.Getter; import lombok.NoArgsConstructor; @Entity @NoArgsConstructor +@Getter public class Room { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @JsonIgnore private Long id; @ManyToOne(fetch = FetchType.LAZY) + @JsonBackReference @JoinColumn(name = "host_id") private Host host; diff --git a/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java b/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java index b1ca53149..5e4767597 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java +++ b/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java @@ -10,10 +10,12 @@ import com.fasterxml.jackson.annotation.JsonBackReference; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity @NoArgsConstructor +@Getter public class RoomImage { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java new file mode 100644 index 000000000..d228ac136 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java @@ -0,0 +1,23 @@ +package com.team11.airbnb.web.controller; + +import com.team11.airbnb.domain.Room; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping +public class RoomController { + + private final RoomService roomService; + + public RoomController(RoomService roomService){ + this.roomService = roomService; + } + + @GetMapping("{id}") + public Room findByRoomDetail(@PathVariable("id") Long id) throws Exception { + return roomService.findOne(id); + } +} diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/RoomRepository.java b/BE/src/main/java/com/team11/airbnb/web/controller/RoomRepository.java new file mode 100644 index 000000000..0d92a9c76 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/controller/RoomRepository.java @@ -0,0 +1,10 @@ +package com.team11.airbnb.web.controller; + +import com.team11.airbnb.domain.Room; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RoomRepository extends JpaRepository { + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/RoomService.java b/BE/src/main/java/com/team11/airbnb/web/controller/RoomService.java new file mode 100644 index 000000000..ea28b70a8 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/controller/RoomService.java @@ -0,0 +1,18 @@ +package com.team11.airbnb.web.controller; + +import com.team11.airbnb.domain.Room; +import org.springframework.stereotype.Service; + +@Service +public class RoomService { + + private final RoomRepository repository; + + public RoomService(RoomRepository repository) { + this.repository = repository; + } + + public Room findOne(Long roomId) throws Exception { + return repository.findById(roomId).orElseThrow(() -> new Exception()); + } +} From af97364cacac1a5ce51356fc48f1444b165bcd66 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Thu, 2 Jun 2022 11:28:41 +0900 Subject: [PATCH 04/35] =?UTF-8?q?Chore:=20GitHub=20Actions=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 96 +++++++++++++++++++ BE/Dockerfile | 18 ++++ BE/appspec.yml | 18 ++++ BE/build.gradle | 14 ++- BE/scripts/deploy.sh | 14 +++ .../web/controller/HealthController.java | 12 +++ BE/src/main/resources/application.yml | 18 ++++ 7 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/gradle.yml create mode 100644 BE/Dockerfile create mode 100644 BE/appspec.yml create mode 100644 BE/scripts/deploy.sh create mode 100644 BE/src/main/java/com/team11/airbnb/web/controller/HealthController.java create mode 100644 BE/src/main/resources/application.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 000000000..191d85ebc --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,96 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle + +name: Java CI with Gradle + +on: + push: + branches: [ BE-deploy ] + pull_request: + branches: [ BE-deploy ] + +jobs: + build: + + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./BE + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'temurin' + + - name: Insert data source information into application.yml + run: | + sed -i "s|datasource_url|$DATASOURCE_URL|g" ./src/main/resources/application.yml + sed -i "s|datasource_username|$DATASOURCE_USERNAME|g" ./src/main/resources/application.yml + sed -i "s|datasource_password|$DATASOURCE_PASSWORD|g" ./src/main/resources/application.yml + env: + DATASOURCE_URL: ${{ secrets.DATASOURCE_URL }} + DATASOURCE_USERNAME: ${{ secrets.DATASOURCE_USERNAME }} + DATASOURCE_PASSWORD: ${{ secrets.DATASOURCE_PASSWORD }} + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew clean bootJar + + # 도커 빌드(도커 이미지 생성) + - name: Docker build + run: | + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker build -t meenzino/airbnb:1.0 . + docker push ${{ secrets.DOCKER_USERNAME }}/airbnb:1.0 + docker rmi ${{ secrets.DOCKER_USERNAME }}/airbnb:1.0 + + # 디렉토리 생성 + - name: Make Directory + run: mkdir -p deploy + + # appspec.yml 파일 복사 + - name: Copy appspec.yml + run: cp appspec.yml ./deploy + + # script files 복사 + - name: Copy script + run: cp ./scripts/*.sh ./deploy + + # 파일 압축 + - name: Make zip file + run: zip -r ./airbnb.zip ./deploy + + # 빌드 성공시 슬랙 알람 + - name: action-slack + uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} + author_name: Github Action Test # default: 8398a7@action-slack + fields: repo,message,commit,author,action,eventName,ref,workflow,job,took + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required + if: always() # Pick up events even if the job fails or is canceled. + + # S3와 CodeDeploy를 통한 배포 + - name: Deploy + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + + - name: Upload to S3 + run: aws s3 cp --region ap-northeast-2 --acl private ./airbnb.zip s3://airbnb-deploy-s3/ + + - name: Start CodeDeploy + run: aws deploy create-deployment --application-name airbnb-deploy-group --deployment-group-name airbnb-deploy-auto-scaling-group --s3-location bucket=airbnb-deploy-s3,key=airbnb.zip,bundleType=zip diff --git a/BE/Dockerfile b/BE/Dockerfile new file mode 100644 index 000000000..063884149 --- /dev/null +++ b/BE/Dockerfile @@ -0,0 +1,18 @@ +# JDK11 이미지 사용 +FROM openjdk:11-jdk + +#마운트에 /tmp를 사용하는 이유 +#문서는 아래와 같이 작성되어 있습니다. +#spring boot의 Tomcat의 default 저장소가 /tmp인데 +#위와 같이 볼륨 마운트를 해주면 호스트의 /var/lib/docker에 임시파일을 만들고 +#컨테이너 안의 /tmp 와 연결할 수 있다는 뜻입니다. +VOLUME /tmp + +# JAR_FILE 변수에 값을 저장 +ARG JAR_FILE=./build/libs/*.jar + +# 변수에 저장된 것을 컨데이너 실행시 이름을 app.jar파일로 변경하여 컨테이너에 저장 +COPY ${JAR_FILE} app.jar + +# 빌드된 이미지가 run 될 때 실행할 명령어 +ENTRYPOINT ["java","-jar","/app.jar"] diff --git a/BE/appspec.yml b/BE/appspec.yml new file mode 100644 index 000000000..ba526e52d --- /dev/null +++ b/BE/appspec.yml @@ -0,0 +1,18 @@ +version: 0.0 +os: linux +files: + - source: / + destination: /home/ubuntu/app/ + overwrite: yes + +permissions: + - object: / + pattern: "**" + owner: ubuntu + group: ubuntu + +hooks: + ApplicationStart: + - location: deploy.sh + timeout: 60 + runas: ubuntu diff --git a/BE/build.gradle b/BE/build.gradle index 3f413ed58..1c2da385c 100644 --- a/BE/build.gradle +++ b/BE/build.gradle @@ -4,10 +4,6 @@ plugins { id 'java' } -group = 'com.team11' -version = '0.0.1-SNAPSHOT' -sourceCompatibility = '11' - configurations { compileOnly { extendsFrom annotationProcessor @@ -30,3 +26,13 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +jar { + enabled = false +} + +bootJar { + archivesBaseName = 'app' + archiveFileName = 'app.jar' + archiveVersion = "0.0.0" +} diff --git a/BE/scripts/deploy.sh b/BE/scripts/deploy.sh new file mode 100644 index 000000000..21d7d5628 --- /dev/null +++ b/BE/scripts/deploy.sh @@ -0,0 +1,14 @@ +# 가동중인 jpa-shop 도커 중단 및 삭제 +sudo docker ps -a -q --filter "name=jpa-shop" | grep -q . && docker stop jpa-shop && docker rm jpa-shop | true + +# 기존 이미지 삭제 +sudo docker rmi meenzino/jpa-shop:1.0 + +# 도커허브 이미지 pull +sudo docker pull meenzino/jpa-shop:1.0 + +# 도커 run +sudo docker run -d -p 80:8080 --name jpa-shop meenzino/jpa-shop:1.0 + +# 사용하지 않는 불필요한 이미지 삭제 -> 현재 컨테이너가 물고 있는 이미지는 삭제되지 않습니다. +sudo docker rmi -f $(docker images -f "dangling=true" -q) || true diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/HealthController.java b/BE/src/main/java/com/team11/airbnb/web/controller/HealthController.java new file mode 100644 index 000000000..93687e1e2 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/controller/HealthController.java @@ -0,0 +1,12 @@ +package com.team11.airbnb.web.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HealthController { + @GetMapping("/health") + public String healthCheck(){ + return "health ok"; + } +} diff --git a/BE/src/main/resources/application.yml b/BE/src/main/resources/application.yml new file mode 100644 index 000000000..5a5c50513 --- /dev/null +++ b/BE/src/main/resources/application.yml @@ -0,0 +1,18 @@ +spring: + datasource: + url: datasource_url + username: datasource_username + password: datasource_password + driver-class-name: com.mysql.cj.jdbc.Driver + sql: + init: + mode: always + jpa: + defer-datasource-initialization: true + hibernate: + ddl-auto: create + properties: + hibernate: + show_sql: true + format_sql: true + generate-ddl: true From 5bf53313f75a3b2613cdb8196706ea21cae539fb Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Thu, 2 Jun 2022 11:46:24 +0900 Subject: [PATCH 05/35] =?UTF-8?q?chore:=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit repository와 service 패키지 생성 --- .../com/team11/airbnb/web/controller/RoomController.java | 1 + .../web/{controller => repository}/RoomRepository.java | 2 +- .../airbnb/web/{controller => service}/RoomService.java | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) rename BE/src/main/java/com/team11/airbnb/web/{controller => repository}/RoomRepository.java (84%) rename BE/src/main/java/com/team11/airbnb/web/{controller => service}/RoomService.java (66%) diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java index d228ac136..890d4e932 100644 --- a/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java +++ b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java @@ -1,6 +1,7 @@ package com.team11.airbnb.web.controller; import com.team11.airbnb.domain.Room; +import com.team11.airbnb.web.service.RoomService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/RoomRepository.java b/BE/src/main/java/com/team11/airbnb/web/repository/RoomRepository.java similarity index 84% rename from BE/src/main/java/com/team11/airbnb/web/controller/RoomRepository.java rename to BE/src/main/java/com/team11/airbnb/web/repository/RoomRepository.java index 0d92a9c76..7ccf118a0 100644 --- a/BE/src/main/java/com/team11/airbnb/web/controller/RoomRepository.java +++ b/BE/src/main/java/com/team11/airbnb/web/repository/RoomRepository.java @@ -1,4 +1,4 @@ -package com.team11.airbnb.web.controller; +package com.team11.airbnb.web.repository; import com.team11.airbnb.domain.Room; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/RoomService.java b/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java similarity index 66% rename from BE/src/main/java/com/team11/airbnb/web/controller/RoomService.java rename to BE/src/main/java/com/team11/airbnb/web/service/RoomService.java index ea28b70a8..f1d897b08 100644 --- a/BE/src/main/java/com/team11/airbnb/web/controller/RoomService.java +++ b/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java @@ -1,6 +1,7 @@ -package com.team11.airbnb.web.controller; +package com.team11.airbnb.web.service; import com.team11.airbnb.domain.Room; +import com.team11.airbnb.web.repository.RoomRepository; import org.springframework.stereotype.Service; @Service @@ -13,6 +14,6 @@ public RoomService(RoomRepository repository) { } public Room findOne(Long roomId) throws Exception { - return repository.findById(roomId).orElseThrow(() -> new Exception()); + return repository.findById(roomId).orElseThrow(Exception::new); } } From 2258819e2df474ce036238617dabf11af6274e5a Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Thu, 2 Jun 2022 17:22:24 +0900 Subject: [PATCH 06/35] =?UTF-8?q?refactor:=20=EC=83=81=EC=84=B8=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EC=8B=9C=20DTO=EB=A5=BC=20=EB=B0=98=ED=99=98=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Address는 ios와 회의 결과 주소가 아닌 위도와 경도로 받아서 의미에 부합하도록 클래스 이름을 Location으로 변경 - Host와 Room의 DTO 생성 - Image 리스트의 경우 copyOf를 써서 불변객체를 반환 - 불필요한 import 삭제 --- .../com/team11/airbnb/domain/Address.java | 26 ------------------- .../java/com/team11/airbnb/domain/Host.java | 11 ++------ .../com/team11/airbnb/domain/Location.java | 20 ++++++++++++++ .../java/com/team11/airbnb/domain/Review.java | 4 ++- .../java/com/team11/airbnb/domain/Room.java | 6 ++--- .../com/team11/airbnb/domain/RoomImage.java | 6 ++--- .../com/team11/airbnb/domain/RoomInfo.java | 5 +++- .../airbnb/web/controller/RoomController.java | 4 +-- .../com/team11/airbnb/web/dto/HostDto.java | 16 ++++++++++++ .../team11/airbnb/web/dto/RoomDetailDto.java | 25 ++++++++++++++++++ .../airbnb/web/service/RoomService.java | 23 +++++++++++++--- 11 files changed, 96 insertions(+), 50 deletions(-) delete mode 100644 BE/src/main/java/com/team11/airbnb/domain/Address.java create mode 100644 BE/src/main/java/com/team11/airbnb/domain/Location.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/dto/HostDto.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/dto/RoomDetailDto.java diff --git a/BE/src/main/java/com/team11/airbnb/domain/Address.java b/BE/src/main/java/com/team11/airbnb/domain/Address.java deleted file mode 100644 index b0b677c9e..000000000 --- a/BE/src/main/java/com/team11/airbnb/domain/Address.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.team11.airbnb.domain; - -import javax.persistence.Embeddable; -import lombok.Getter; - -@Embeddable -@Getter -public class Address { - - private String street; - private String city; - private String state; - private String zipcode; - private String country; - - protected Address() { - } - - public Address(String street, String city, String state, String zipcode, String country) { - this.street = street; - this.city = city; - this.state = state; - this.zipcode = zipcode; - this.country = country; - } -} diff --git a/BE/src/main/java/com/team11/airbnb/domain/Host.java b/BE/src/main/java/com/team11/airbnb/domain/Host.java index b94ca8659..1344f84d1 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Host.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Host.java @@ -1,13 +1,9 @@ package com.team11.airbnb.domain; -import com.fasterxml.jackson.annotation.JsonManagedReference; -import java.util.ArrayList; -import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; -import javax.persistence.OneToMany; import lombok.Getter; import lombok.NoArgsConstructor; @@ -18,11 +14,8 @@ public class Host { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String name; - - @OneToMany(mappedBy = "host") - @JsonManagedReference - private List rooms = new ArrayList<>(); + private boolean isSuperHost; + private String profileImagePath; } diff --git a/BE/src/main/java/com/team11/airbnb/domain/Location.java b/BE/src/main/java/com/team11/airbnb/domain/Location.java new file mode 100644 index 000000000..63865b471 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/Location.java @@ -0,0 +1,20 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Embeddable; +import lombok.Getter; + +@Embeddable +@Getter +public class Location { + + private String latitude; + private String longitude; + + protected Location() { + } + + public Location(String latitude, String longitude) { + this.latitude = latitude; + this.longitude = longitude; + } +} diff --git a/BE/src/main/java/com/team11/airbnb/domain/Review.java b/BE/src/main/java/com/team11/airbnb/domain/Review.java index 64987fc2d..4b63dd047 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Review.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Review.java @@ -1,6 +1,6 @@ package com.team11.airbnb.domain; -import javax.persistence.Column; +import com.fasterxml.jackson.annotation.JsonBackReference; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; @@ -8,9 +8,11 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity +@Getter @NoArgsConstructor public class Review { diff --git a/BE/src/main/java/com/team11/airbnb/domain/Room.java b/BE/src/main/java/com/team11/airbnb/domain/Room.java index 8b19039dd..2d15579a7 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Room.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Room.java @@ -1,6 +1,5 @@ package com.team11.airbnb.domain; -import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonManagedReference; import java.util.ArrayList; @@ -27,15 +26,16 @@ public class Room { private Long id; @ManyToOne(fetch = FetchType.LAZY) - @JsonBackReference @JoinColumn(name = "host_id") private Host host; @Embedded - private Address address; + private Location location; private Double averageGrade; + @OneToMany(mappedBy = "room") + private List reviews = new ArrayList<>(); @OneToMany(mappedBy = "room") @JsonManagedReference diff --git a/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java b/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java index 5e4767597..7333a1948 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java +++ b/BE/src/main/java/com/team11/airbnb/domain/RoomImage.java @@ -1,5 +1,6 @@ package com.team11.airbnb.domain; +import com.fasterxml.jackson.annotation.JsonBackReference; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; @@ -7,9 +8,6 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; - -import com.fasterxml.jackson.annotation.JsonBackReference; - import lombok.Getter; import lombok.NoArgsConstructor; @@ -22,8 +20,8 @@ public class RoomImage { private Long id; @ManyToOne(fetch = FetchType.LAZY) - @JsonBackReference @JoinColumn(name = "room_id") + @JsonBackReference private Room room; private String imagePath; diff --git a/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java b/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java index 437853f62..b738c0f2c 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java +++ b/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java @@ -13,6 +13,7 @@ public class RoomInfo { private int numberOfBedroom; private int numberOfBed; private int numberOfBathroom; + private int capacity; private LocalTime checkInTime; private LocalTime checkOutTime; @@ -20,12 +21,14 @@ protected RoomInfo() { } public RoomInfo(String roomType, int numberOfBedroom, int numberOfBed, int numberOfBathroom, - LocalTime checkInTime, LocalTime checkOutTime) { + int capacity, LocalTime checkInTime, LocalTime checkOutTime) { this.roomType = roomType; this.numberOfBedroom = numberOfBedroom; this.numberOfBed = numberOfBed; this.numberOfBathroom = numberOfBathroom; + this.capacity = capacity; this.checkInTime = checkInTime; this.checkOutTime = checkOutTime; } + } diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java index 890d4e932..b3e37edce 100644 --- a/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java +++ b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java @@ -1,6 +1,6 @@ package com.team11.airbnb.web.controller; -import com.team11.airbnb.domain.Room; +import com.team11.airbnb.web.dto.RoomDetailDto; import com.team11.airbnb.web.service.RoomService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -18,7 +18,7 @@ public RoomController(RoomService roomService){ } @GetMapping("{id}") - public Room findByRoomDetail(@PathVariable("id") Long id) throws Exception { + public RoomDetailDto findByRoomDetail(@PathVariable("id") Long id) throws Exception { return roomService.findOne(id); } } diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/HostDto.java b/BE/src/main/java/com/team11/airbnb/web/dto/HostDto.java new file mode 100644 index 000000000..d122966a5 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/dto/HostDto.java @@ -0,0 +1,16 @@ +package com.team11.airbnb.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class HostDto { + private Long id; + private String name; + private boolean isSuperHost; + private String profileImagePath; + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/RoomDetailDto.java b/BE/src/main/java/com/team11/airbnb/web/dto/RoomDetailDto.java new file mode 100644 index 000000000..22a005610 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/dto/RoomDetailDto.java @@ -0,0 +1,25 @@ +package com.team11.airbnb.web.dto; + +import com.team11.airbnb.domain.Location; +import com.team11.airbnb.domain.RoomImage; +import com.team11.airbnb.domain.RoomInfo; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Builder @Getter +public class RoomDetailDto { + private String name; + private Location location; + private double averageGrade; + private int numberOfReviews; + private List roomImages; + private RoomInfo roomInfo; + private String description; + private int price; + private HostDto host; +} diff --git a/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java b/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java index f1d897b08..f76a5f4e2 100644 --- a/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java +++ b/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java @@ -1,19 +1,34 @@ package com.team11.airbnb.web.service; +import com.team11.airbnb.domain.Host; import com.team11.airbnb.domain.Room; +import com.team11.airbnb.web.dto.HostDto; +import com.team11.airbnb.web.dto.RoomDetailDto; import com.team11.airbnb.web.repository.RoomRepository; +import java.util.List; import org.springframework.stereotype.Service; @Service public class RoomService { - + private final RoomRepository repository; public RoomService(RoomRepository repository) { this.repository = repository; } - - public Room findOne(Long roomId) throws Exception { - return repository.findById(roomId).orElseThrow(Exception::new); + + public RoomDetailDto findOne(Long roomId) throws Exception { + Room room = repository.findById(roomId).orElseThrow(Exception::new); + Host host = room.getHost(); + return RoomDetailDto.builder(). + name(room.getName()).location(room.getLocation()). + averageGrade(room.getAverageGrade()). + numberOfReviews(room.getReviews().size()). + roomImages(List.copyOf(room.getRoomImages())). + roomInfo(room.getRoomInfo()). + description(room.getDescription()). + price(room.getPrice()). + host(new HostDto(host.getId(), host.getName(), host.isSuperHost(), + host.getProfileImagePath())).build(); } } From 6c166ab66a4eb5eb7546977a359afbe73e07bfdc Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Thu, 2 Jun 2022 17:24:07 +0900 Subject: [PATCH 07/35] =?UTF-8?q?refactor:=20WishList=20FetchType=20LAZY?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20=EC=BB=AC=EB=9F=BC=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/main/java/com/team11/airbnb/domain/WishList.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/BE/src/main/java/com/team11/airbnb/domain/WishList.java b/BE/src/main/java/com/team11/airbnb/domain/WishList.java index acce4304a..ac8deb277 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/WishList.java +++ b/BE/src/main/java/com/team11/airbnb/domain/WishList.java @@ -1,7 +1,7 @@ package com.team11.airbnb.domain; -import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -16,11 +16,10 @@ public class WishList { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne - @JoinColumn(name = "room_id") + @ManyToOne(fetch = FetchType.LAZY) private Room room; - @ManyToOne - @JoinColumn(name = "user_id") + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name="user_id") private User user; } From a96c81a8235c84b95f7d984289aa024f7a101690 Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Sat, 4 Jun 2022 20:20:08 +0900 Subject: [PATCH 08/35] =?UTF-8?q?chore:=20github=20actions=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gradle.yml 작성 - application-develop.yml의 datasource를 secret key로 관리하도록 수정 --- .github/workflows/gradle.yml | 34 +++++++++---------- BE/sql/data.sql | 2 +- BE/src/main/resources/application-develop.yml | 21 ++++++++++++ 3 files changed, 39 insertions(+), 18 deletions(-) create mode 100644 BE/src/main/resources/application-develop.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 191d85ebc..a389595d4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -9,9 +9,9 @@ name: Java CI with Gradle on: push: - branches: [ BE-deploy ] + branches: [ BE-develop ] pull_request: - branches: [ BE-deploy ] + branches: [ BE-develop ] jobs: build: @@ -43,13 +43,13 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew clean bootJar + run: ./gradlew bootJar # 도커 빌드(도커 이미지 생성) - name: Docker build run: | docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker build -t meenzino/airbnb:1.0 . + docker build -t philsogood/airbnb:1.0 . docker push ${{ secrets.DOCKER_USERNAME }}/airbnb:1.0 docker rmi ${{ secrets.DOCKER_USERNAME }}/airbnb:1.0 @@ -69,17 +69,6 @@ jobs: - name: Make zip file run: zip -r ./airbnb.zip ./deploy - # 빌드 성공시 슬랙 알람 - - name: action-slack - uses: 8398a7/action-slack@v3 - with: - status: ${{ job.status }} - author_name: Github Action Test # default: 8398a7@action-slack - fields: repo,message,commit,author,action,eventName,ref,workflow,job,took - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required - if: always() # Pick up events even if the job fails or is canceled. - # S3와 CodeDeploy를 통한 배포 - name: Deploy uses: aws-actions/configure-aws-credentials@v1 @@ -90,7 +79,18 @@ jobs: - name: Upload to S3 - run: aws s3 cp --region ap-northeast-2 --acl private ./airbnb.zip s3://airbnb-deploy-s3/ + run: aws s3 cp --region ap-northeast-2 --acl private ./airbnb.zip s3://airbnb-phil-deploy-dockerimage/ - name: Start CodeDeploy - run: aws deploy create-deployment --application-name airbnb-deploy-group --deployment-group-name airbnb-deploy-auto-scaling-group --s3-location bucket=airbnb-deploy-s3,key=airbnb.zip,bundleType=zip + run: aws deploy create-deployment --application-name Airbnb-Codedeploy-Application --deployment-group-name Airbnb-Codedeploy-Target-Group --s3-location bucket=airbnb-phil-deploy-dockerimage,key=airbnb.zip,bundleType=zip + + # 배경 성공시 슬랙 알람 + - name: action-slack + uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} + author_name: Github Action Test # default: 8398a7@action-slack + fields: repo,message,commit,author,action,eventName,ref,workflow,job,took + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required + if: always() # Pick up events even if the job fails or is canceled. diff --git a/BE/sql/data.sql b/BE/sql/data.sql index 3ccf8001a..fae11621b 100644 --- a/BE/sql/data.sql +++ b/BE/sql/data.sql @@ -6,7 +6,7 @@ VALUES ('종로구', '한국', '서울시', '짱짱로', 1234, 3, '짱짱이에 ('광주시', '한국', '경기도', '선을로', 1234, 4, '칭찬해요', '오포숲-숲뷰 맛집/넷플릭스/무료주차공간/매일 소독/서현10분', 100714, 15, 11, 2, 2, 2, 'SHARE_HOUSE', 1); insert into room_image (image_path, room_id) -values ('https://a0.muscache.com/im/pictures/miso/Hosting-22724133/original/8514626f-60c8-4118-b207-b25285305915.jpeg?im_w=960', +values ('https://a0.muscache.com/im/pictures/miso/Hostinvg-22724133/original/8514626f-60c8-4118-b207-b25285305915.jpeg?im_w=960', 1), ('https://a0.muscache.com/im/pictures/miso/Hosting-22724133/original/a98f70e9-a56e-41cd-9363-2656dfccb353.jpeg?im_w=720', 1), diff --git a/BE/src/main/resources/application-develop.yml b/BE/src/main/resources/application-develop.yml new file mode 100644 index 000000000..2af17fd67 --- /dev/null +++ b/BE/src/main/resources/application-develop.yml @@ -0,0 +1,21 @@ +spring: + datasource: + url: datasource_url + username: datasource_username + password: datasource_password + driver-class-name: com.mysql.cj.jdbc.Driver + sql: + init: + mode: always + jpa: + defer-datasource-initialization: true + hibernate: + ddl-auto: validate + properties: + hibernate: + show_sql: true + format_sql: true + generate-ddl: true +logging: + level: + com.team11: debug From 19c051f9de9265bef93edda5501d3c0ff39662c4 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Sat, 4 Jun 2022 20:25:14 +0900 Subject: [PATCH 09/35] =?UTF-8?q?fix:=20=EB=B0=B0=ED=8F=AC=EC=9A=A9=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - deploy.sh에서 도커허브 레포지토리 변경 --- BE/scripts/deploy.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BE/scripts/deploy.sh b/BE/scripts/deploy.sh index 21d7d5628..765eaecf5 100644 --- a/BE/scripts/deploy.sh +++ b/BE/scripts/deploy.sh @@ -1,14 +1,14 @@ # 가동중인 jpa-shop 도커 중단 및 삭제 -sudo docker ps -a -q --filter "name=jpa-shop" | grep -q . && docker stop jpa-shop && docker rm jpa-shop | true +sudo docker ps -a -q --filter "name=airbnb" | grep -q . && docker stop airbnb && docker rm airbnb | true # 기존 이미지 삭제 -sudo docker rmi meenzino/jpa-shop:1.0 +sudo docker rmi philsogood/airbnb:1.0 # 도커허브 이미지 pull -sudo docker pull meenzino/jpa-shop:1.0 +sudo docker pull philsogood/airbnb:1.0 # 도커 run -sudo docker run -d -p 80:8080 --name jpa-shop meenzino/jpa-shop:1.0 +sudo docker run -d -p 80:8080 --name airbnb philsogood/airbnb:1.0 # 사용하지 않는 불필요한 이미지 삭제 -> 현재 컨테이너가 물고 있는 이미지는 삭제되지 않습니다. sudo docker rmi -f $(docker images -f "dangling=true" -q) || true From 38845b7a5c86c72ac7ae4619f75475f1fd4b6bf4 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Sat, 4 Jun 2022 20:26:42 +0900 Subject: [PATCH 10/35] =?UTF-8?q?fix:=20GitHUb=20Actions=20=ED=8A=B8?= =?UTF-8?q?=EB=A6=AC=EC=BB=A4=20=EB=B8=8C=EB=9E=9C=EC=B9=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 개발 메인 브랜치인 BE-develop에서 배포용 브랜치 BE-deploy로 수정 --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index a389595d4..fc3357b36 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -9,9 +9,9 @@ name: Java CI with Gradle on: push: - branches: [ BE-develop ] + branches: [ BE-deploy ] pull_request: - branches: [ BE-develop ] + branches: [ BE-deploy ] jobs: build: From 2e70228fa6c7d405be8c8eac64d68855383556c2 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Sat, 4 Jun 2022 20:49:39 +0900 Subject: [PATCH 11/35] =?UTF-8?q?fix:=20application.yml=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 잘못 올린 파일 삭제 --- BE/src/main/resources/application-develop.yml | 21 ------------------- BE/src/main/resources/application.yml | 5 ++++- 2 files changed, 4 insertions(+), 22 deletions(-) delete mode 100644 BE/src/main/resources/application-develop.yml diff --git a/BE/src/main/resources/application-develop.yml b/BE/src/main/resources/application-develop.yml deleted file mode 100644 index 2af17fd67..000000000 --- a/BE/src/main/resources/application-develop.yml +++ /dev/null @@ -1,21 +0,0 @@ -spring: - datasource: - url: datasource_url - username: datasource_username - password: datasource_password - driver-class-name: com.mysql.cj.jdbc.Driver - sql: - init: - mode: always - jpa: - defer-datasource-initialization: true - hibernate: - ddl-auto: validate - properties: - hibernate: - show_sql: true - format_sql: true - generate-ddl: true -logging: - level: - com.team11: debug diff --git a/BE/src/main/resources/application.yml b/BE/src/main/resources/application.yml index 5a5c50513..2af17fd67 100644 --- a/BE/src/main/resources/application.yml +++ b/BE/src/main/resources/application.yml @@ -10,9 +10,12 @@ spring: jpa: defer-datasource-initialization: true hibernate: - ddl-auto: create + ddl-auto: validate properties: hibernate: show_sql: true format_sql: true generate-ddl: true +logging: + level: + com.team11: debug From 059b7b7448cd49876b663342c4117954e5be2fbb Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Mon, 6 Jun 2022 16:17:07 +0900 Subject: [PATCH 12/35] =?UTF-8?q?feat:=20MainEvent(=ED=9E=88=EC=96=B4?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=98=81=EC=97=AD)?= =?UTF-8?q?=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - IOS 기획서에 첫번째 페이지에서 히어로 이미지 영역의 내용 및 이미지 주소를 담당하는 클래스 --- .../com/team11/airbnb/domain/MainEvent.java | 26 +++++++++++++++++++ .../team11/airbnb/web/dto/MainEventDto.java | 16 ++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 BE/src/main/java/com/team11/airbnb/domain/MainEvent.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/dto/MainEventDto.java diff --git a/BE/src/main/java/com/team11/airbnb/domain/MainEvent.java b/BE/src/main/java/com/team11/airbnb/domain/MainEvent.java new file mode 100644 index 000000000..cdf869fdd --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/MainEvent.java @@ -0,0 +1,26 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class MainEvent { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + private String label; + private String imagePath; + private String buttonText; + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/MainEventDto.java b/BE/src/main/java/com/team11/airbnb/web/dto/MainEventDto.java new file mode 100644 index 000000000..88ac0f18e --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/dto/MainEventDto.java @@ -0,0 +1,16 @@ +package com.team11.airbnb.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class MainEventDto { + + private String title; + private String label; + private String imagePath; + private String buttonText; + + +} From 27cd4a64f309de49e706c9067976f3b83919c8d0 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Mon, 6 Jun 2022 16:18:28 +0900 Subject: [PATCH 13/35] =?UTF-8?q?feat:=20ArroungSpotDto=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 홈화면에서 가까운 여행지 둘러보기를 담당하는 DTO --- .../com/team11/airbnb/web/dto/ArroundSpotDto.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 BE/src/main/java/com/team11/airbnb/web/dto/ArroundSpotDto.java diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/ArroundSpotDto.java b/BE/src/main/java/com/team11/airbnb/web/dto/ArroundSpotDto.java new file mode 100644 index 000000000..6bf402019 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/dto/ArroundSpotDto.java @@ -0,0 +1,14 @@ +package com.team11.airbnb.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class ArroundSpotDto { + + private String title; + private String label; + private int distance; + private String imagePath; +} From a51fe1407b9aa479914d6345e947d52f618af4f5 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Mon, 6 Jun 2022 16:20:13 +0900 Subject: [PATCH 14/35] =?UTF-8?q?feat:=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=ED=85=8C=EB=A7=88=20=EC=97=AC=ED=96=89=EC=A7=80(ThemeSpot)=20?= =?UTF-8?q?=EB=8B=B4=EB=8B=B9=20=EC=97=94=ED=8B=B0=ED=8B=B0=EC=99=80=20DTO?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/team11/airbnb/domain/ThemeSpot.java | 23 +++++++++++++++++++ .../team11/airbnb/web/dto/ThemeSpotDto.java | 13 +++++++++++ 2 files changed, 36 insertions(+) create mode 100644 BE/src/main/java/com/team11/airbnb/domain/ThemeSpot.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/dto/ThemeSpotDto.java diff --git a/BE/src/main/java/com/team11/airbnb/domain/ThemeSpot.java b/BE/src/main/java/com/team11/airbnb/domain/ThemeSpot.java new file mode 100644 index 000000000..6e6c00437 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/ThemeSpot.java @@ -0,0 +1,23 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class ThemeSpot { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + private String imagePath; + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/ThemeSpotDto.java b/BE/src/main/java/com/team11/airbnb/web/dto/ThemeSpotDto.java new file mode 100644 index 000000000..17a90d091 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/dto/ThemeSpotDto.java @@ -0,0 +1,13 @@ +package com.team11.airbnb.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class ThemeSpotDto { + + private String imagePath; + private String title; + +} From 115554ce365b4def84604aa667a822f52be2c22e Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Mon, 6 Jun 2022 16:25:24 +0900 Subject: [PATCH 15/35] =?UTF-8?q?fix:=20=EB=B3=80=EC=88=98=20RoomType?= =?UTF-8?q?=EC=9D=84=20Enum=20=ED=83=80=EC=9E=85=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java b/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java index b738c0f2c..5edd7ff49 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java +++ b/BE/src/main/java/com/team11/airbnb/domain/RoomInfo.java @@ -3,13 +3,16 @@ import java.time.LocalTime; import javax.persistence.Embeddable; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import lombok.Getter; @Embeddable @Getter public class RoomInfo { - private String roomType; + @Enumerated(EnumType.STRING) + private RoomType roomType; private int numberOfBedroom; private int numberOfBed; private int numberOfBathroom; @@ -20,7 +23,7 @@ public class RoomInfo { protected RoomInfo() { } - public RoomInfo(String roomType, int numberOfBedroom, int numberOfBed, int numberOfBathroom, + public RoomInfo(RoomType roomType, int numberOfBedroom, int numberOfBed, int numberOfBathroom, int capacity, LocalTime checkInTime, LocalTime checkOutTime) { this.roomType = roomType; this.numberOfBedroom = numberOfBedroom; From e5e2e5d60aedf3e5477dc8de5063b02b003d66bb Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Tue, 7 Jun 2022 10:44:47 +0900 Subject: [PATCH 16/35] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=B3=80=EC=88=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../airbnb/web/dto/{ArroundSpotDto.java => AroundSpotDto.java} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename BE/src/main/java/com/team11/airbnb/web/dto/{ArroundSpotDto.java => AroundSpotDto.java} (78%) diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/ArroundSpotDto.java b/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java similarity index 78% rename from BE/src/main/java/com/team11/airbnb/web/dto/ArroundSpotDto.java rename to BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java index 6bf402019..e3cdafad9 100644 --- a/BE/src/main/java/com/team11/airbnb/web/dto/ArroundSpotDto.java +++ b/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java @@ -5,10 +5,9 @@ @AllArgsConstructor @Getter -public class ArroundSpotDto { +public class AroundSpotDto { private String title; - private String label; private int distance; private String imagePath; } From 7cdcd50236e74e66c9f4966dfd1aa8a9f10a90bb Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Tue, 7 Jun 2022 10:46:46 +0900 Subject: [PATCH 17/35] =?UTF-8?q?feat:=20=ED=96=89=EC=A0=95=EA=B5=AC?= =?UTF-8?q?=EC=97=AD=20=EC=9D=B8=EA=B8=B0=EC=97=AC=ED=96=89=EC=A7=80?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=9B=EC=95=84=EC=98=A4=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Location은 각 행정지의 중심좌표를 의미 --- .../com/team11/airbnb/domain/District.java | 27 +++++++++++++++++++ .../web/repository/DistrictRepository.java | 10 +++++++ 2 files changed, 37 insertions(+) create mode 100644 BE/src/main/java/com/team11/airbnb/domain/District.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/repository/DistrictRepository.java diff --git a/BE/src/main/java/com/team11/airbnb/domain/District.java b/BE/src/main/java/com/team11/airbnb/domain/District.java new file mode 100644 index 000000000..690553cf2 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/domain/District.java @@ -0,0 +1,27 @@ +package com.team11.airbnb.domain; + +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@AllArgsConstructor +@NoArgsConstructor +public class District { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private String imagePath; + + @Embedded + private Location location; + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/repository/DistrictRepository.java b/BE/src/main/java/com/team11/airbnb/web/repository/DistrictRepository.java new file mode 100644 index 000000000..e1278ff59 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/repository/DistrictRepository.java @@ -0,0 +1,10 @@ +package com.team11.airbnb.web.repository; + +import com.team11.airbnb.domain.District; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface DistrictRepository extends JpaRepository { + +} From f7020ffee895a46a0892ada2e8919f7e80b10e02 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Tue, 7 Jun 2022 10:57:41 +0900 Subject: [PATCH 18/35] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20Controller,=20Service,=20Dto=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HomeData는 MainEventDto, AroundSpotDto List, ThemeSpotDto List를 포함하고 있음 --- .../airbnb/web/controller/HomeController.java | 22 +++++++++ .../com/team11/airbnb/web/dto/HomeData.java | 16 +++++++ .../airbnb/web/service/HomeService.java | 46 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/dto/HomeData.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/service/HomeService.java diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java b/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java new file mode 100644 index 000000000..f10033dd9 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java @@ -0,0 +1,22 @@ +package com.team11.airbnb.web.controller; + +import com.team11.airbnb.web.dto.HomeData; +import com.team11.airbnb.web.service.HomeService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HomeController { + + private final HomeService homeService; + + public HomeController(HomeService homeService){ + this.homeService = homeService; + } + + @GetMapping("/home") + public HomeData home() throws Exception { + return homeService.initApplication(); + } + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/HomeData.java b/BE/src/main/java/com/team11/airbnb/web/dto/HomeData.java new file mode 100644 index 000000000..cc02735fa --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/dto/HomeData.java @@ -0,0 +1,16 @@ +package com.team11.airbnb.web.dto; + +import java.util.ArrayList; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class HomeData { + + private MainEventDto mainEventDto; + private List aroundSpotDto = new ArrayList<>(); + private List themeSpotDto = new ArrayList<>(); + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java b/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java new file mode 100644 index 000000000..21a12ca7c --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java @@ -0,0 +1,46 @@ +package com.team11.airbnb.web.service; + +import com.team11.airbnb.domain.District; +import com.team11.airbnb.domain.MainEvent; +import com.team11.airbnb.domain.ThemeSpot; +import com.team11.airbnb.web.dto.AroundSpotDto; +import com.team11.airbnb.web.dto.HomeData; +import com.team11.airbnb.web.dto.MainEventDto; +import com.team11.airbnb.web.dto.ThemeSpotDto; +import com.team11.airbnb.web.repository.DistrictRepository; +import com.team11.airbnb.web.repository.MainEventRepository; +import com.team11.airbnb.web.repository.ThemeRepository; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class HomeService { + + private final DistrictRepository districtRepository; + private final MainEventRepository mainEventRepository; + private final ThemeRepository themeRepository; + + public HomeData initApplication() throws Exception { + + MainEvent mainEvent = mainEventRepository.findById(1L).orElseThrow(Exception::new); + List districtList = districtRepository.findAll(); + List themeSpotList = themeRepository.findAll(); + MainEventDto mainEventDto = new MainEventDto(mainEvent.getTitle(), mainEvent.getLabel(), + mainEvent.getImagePath(), mainEvent.getButtonText()); + List aroundSpotDtoList = new ArrayList<>(); + List themeSpotDtoList = new ArrayList<>(); + for (District district : districtList) { + AroundSpotDto aroundSpotDto = new AroundSpotDto(district.getName(), 10, + district.getImagePath()); + aroundSpotDtoList.add(aroundSpotDto); + } + for (ThemeSpot themeSpot : themeSpotList) { + ThemeSpotDto themeSpotDto = new ThemeSpotDto(themeSpot.getImagePath(), themeSpot.getTitle()); + themeSpotDtoList.add(themeSpotDto); + } + return new HomeData(mainEventDto, aroundSpotDtoList, themeSpotDtoList); + } +} From ab00bfb30ea96fe70a4f8a5503eff6bd17b7a97a Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Tue, 7 Jun 2022 10:58:45 +0900 Subject: [PATCH 19/35] =?UTF-8?q?fix:=20Location=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=9E=90=EB=A5=BC=20=EB=A1=AC=EB=B3=B5=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/team11/airbnb/domain/Location.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/BE/src/main/java/com/team11/airbnb/domain/Location.java b/BE/src/main/java/com/team11/airbnb/domain/Location.java index 63865b471..ad6982263 100644 --- a/BE/src/main/java/com/team11/airbnb/domain/Location.java +++ b/BE/src/main/java/com/team11/airbnb/domain/Location.java @@ -1,20 +1,17 @@ package com.team11.airbnb.domain; import javax.persistence.Embeddable; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; @Embeddable +@AllArgsConstructor +@NoArgsConstructor @Getter public class Location { private String latitude; private String longitude; - protected Location() { - } - - public Location(String latitude, String longitude) { - this.latitude = latitude; - this.longitude = longitude; - } } From a30843794070c8873478372561fcefcae2d8826d Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Tue, 7 Jun 2022 10:59:40 +0900 Subject: [PATCH 20/35] =?UTF-8?q?feat:=20ThemeRepository,=20MainEventRepos?= =?UTF-8?q?itory=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../airbnb/web/repository/MainEventRepository.java | 10 ++++++++++ .../team11/airbnb/web/repository/ThemeRepository.java | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 BE/src/main/java/com/team11/airbnb/web/repository/MainEventRepository.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/repository/ThemeRepository.java diff --git a/BE/src/main/java/com/team11/airbnb/web/repository/MainEventRepository.java b/BE/src/main/java/com/team11/airbnb/web/repository/MainEventRepository.java new file mode 100644 index 000000000..725604c63 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/repository/MainEventRepository.java @@ -0,0 +1,10 @@ +package com.team11.airbnb.web.repository; + +import com.team11.airbnb.domain.MainEvent; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MainEventRepository extends JpaRepository { + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/repository/ThemeRepository.java b/BE/src/main/java/com/team11/airbnb/web/repository/ThemeRepository.java new file mode 100644 index 000000000..7b77364ff --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/repository/ThemeRepository.java @@ -0,0 +1,10 @@ +package com.team11.airbnb.web.repository; + +import com.team11.airbnb.domain.ThemeSpot; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ThemeRepository extends JpaRepository { + +} From 50ee00fc4cf12b06ec9e2a7a737bc6c087b2a66d Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Tue, 7 Jun 2022 11:01:43 +0900 Subject: [PATCH 21/35] =?UTF-8?q?fix:=20RoomController=EC=9D=98=20RequestM?= =?UTF-8?q?apping=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - api 구분을 주기위해서 Mapping에 room을 넣어줌 --- .../java/com/team11/airbnb/web/controller/RoomController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java index b3e37edce..a48ce03a4 100644 --- a/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java +++ b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java @@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping +@RequestMapping("room") public class RoomController { private final RoomService roomService; From efd722fea849a747cd317fb915788f38597ad565 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Tue, 7 Jun 2022 11:20:11 +0900 Subject: [PATCH 22/35] =?UTF-8?q?fix:=20=ED=96=89=EC=A0=95=EA=B5=AC?= =?UTF-8?q?=EC=97=AD=EC=9D=98=20=EA=B5=AC=EB=B6=84=EC=9D=84=20=EC=A3=BC?= =?UTF-8?q?=EA=B8=B0=EC=9C=84=ED=95=B4=20Dto=EC=97=90=20id=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/team11/airbnb/web/dto/AroundSpotDto.java | 1 + .../java/com/team11/airbnb/web/service/HomeService.java | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java b/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java index e3cdafad9..626d349cd 100644 --- a/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java +++ b/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java @@ -7,6 +7,7 @@ @Getter public class AroundSpotDto { + private Long id; private String title; private int distance; private String imagePath; diff --git a/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java b/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java index 21a12ca7c..54ee5fdbd 100644 --- a/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java +++ b/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java @@ -33,12 +33,14 @@ public HomeData initApplication() throws Exception { List aroundSpotDtoList = new ArrayList<>(); List themeSpotDtoList = new ArrayList<>(); for (District district : districtList) { - AroundSpotDto aroundSpotDto = new AroundSpotDto(district.getName(), 10, + AroundSpotDto aroundSpotDto = new AroundSpotDto(district.getId(), district.getName(), + 10, district.getImagePath()); aroundSpotDtoList.add(aroundSpotDto); } for (ThemeSpot themeSpot : themeSpotList) { - ThemeSpotDto themeSpotDto = new ThemeSpotDto(themeSpot.getImagePath(), themeSpot.getTitle()); + ThemeSpotDto themeSpotDto = new ThemeSpotDto(themeSpot.getImagePath(), + themeSpot.getTitle()); themeSpotDtoList.add(themeSpotDto); } return new HomeData(mainEventDto, aroundSpotDtoList, themeSpotDtoList); From f2529fd148bf5b4102abf1e987253573da7d7a7f Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Wed, 8 Jun 2022 14:32:01 +0900 Subject: [PATCH 23/35] =?UTF-8?q?feat:=20Open=20API=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=EC=84=9C=20=ED=98=84=EC=9E=AC=EC=9C=84?= =?UTF-8?q?=EC=B9=98=EB=A5=BC=20=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=97=AC=ED=96=89=EC=A7=80=EC=99=80=EC=9D=98=20=EA=B1=B0?= =?UTF-8?q?=EB=A6=AC=EB=A5=BC=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 카카오 네비게이션 API를 사용 --- .../team11/airbnb/openapi/DistanceInfo.java | 26 ++++++ .../airbnb/openapi/DistanceInfoResponse.java | 13 +++ .../com/team11/airbnb/openapi/District.java | 34 ++++++++ .../airbnb/openapi/DistrictSearchService.java | 85 +++++++++++++++++++ .../com/team11/airbnb/openapi/Position.java | 13 +++ .../controller/DistanceSearchController.java | 37 ++++++++ 6 files changed, 208 insertions(+) create mode 100644 BE/src/main/java/com/team11/airbnb/openapi/DistanceInfo.java create mode 100644 BE/src/main/java/com/team11/airbnb/openapi/DistanceInfoResponse.java create mode 100644 BE/src/main/java/com/team11/airbnb/openapi/District.java create mode 100644 BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java create mode 100644 BE/src/main/java/com/team11/airbnb/openapi/Position.java create mode 100644 BE/src/main/java/com/team11/airbnb/web/controller/DistanceSearchController.java diff --git a/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfo.java b/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfo.java new file mode 100644 index 000000000..0dfed34ce --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfo.java @@ -0,0 +1,26 @@ +package com.team11.airbnb.openapi; + +import lombok.Getter; + +@Getter +public class DistanceInfo { + + private static final int M_TO_KM = 1000; + private static final int SEC_TO_MIN = 60; + + private final double distanceByKiloMeters; + private final double timeByMinutes; + + public DistanceInfo(double distanceByMeters, double timeBySeconds) { + this.distanceByKiloMeters = toKm(distanceByMeters); + this.timeByMinutes = toMin(timeBySeconds); + } + + private double toKm(double distanceByMeters) { + return distanceByMeters / M_TO_KM; + } + + private double toMin(double timeBySeconds) { + return timeBySeconds / SEC_TO_MIN; + } +} diff --git a/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfoResponse.java b/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfoResponse.java new file mode 100644 index 000000000..5b3a345c7 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfoResponse.java @@ -0,0 +1,13 @@ +package com.team11.airbnb.openapi; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class DistanceInfoResponse { + + private String name; + private String distance; + private String time; +} diff --git a/BE/src/main/java/com/team11/airbnb/openapi/District.java b/BE/src/main/java/com/team11/airbnb/openapi/District.java new file mode 100644 index 000000000..0b1f10015 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/openapi/District.java @@ -0,0 +1,34 @@ +package com.team11.airbnb.openapi; + +public enum District { + SEOUL("서울", 126.97857310672501, 37.56654037462486), + YONGIN("용인",127.17751600488093, 37.2409718113933), + SUWON("수원", 127.02869106496286, 37.263501338400935), + GANGRUENG("강릉", 128.8759059060531, 37.752098625346775), + BUSAN("부산", 129.075073717621, 35.17975315226952), + JEONJU("전주", 127.14807326809175, 35.82363477515946), + YEOSU("여수", 127.66230030492281, 34.76021870014728), + GYEONGJU("경주", 129.22480648691675, 35.8561148324664); + + private String name; + private double longtitude; //x + private double latitude; //y + + District(String name, double longtitude, double latitude) { + this.name = name; + this.longtitude = longtitude; + this.latitude = latitude; + } + + public String getName() { + return name; + } + + public double getLongtitude() { + return longtitude; + } + + public double getLatitude() { + return latitude; + } +} diff --git a/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java b/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java new file mode 100644 index 000000000..895b3786a --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java @@ -0,0 +1,85 @@ +package com.team11.airbnb.openapi; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Service +public class DistrictSearchService { + + private String kakaokey = "a1d0ecbd325633346f2938e35d6db4d4"; + + public List searchDistrictInfo(Position position) + throws JsonProcessingException { + District[] districts = District.values(); + List distanceInfoResponses = new ArrayList<>(); + for (District district : districts) { + DistanceInfo distanceInfo = calculateDistanceAndTime(position, district.getLongtitude(), + district.getLatitude()); + String distance = String.valueOf(distanceInfo.getDistanceByKiloMeters()); + String duration = String.valueOf(distanceInfo.getTimeByMinutes()); + distanceInfoResponses.add( + new DistanceInfoResponse(district.name(), distance, duration)); + } + + return distanceInfoResponses; + } + + private DistanceInfo calculateDistanceAndTime(Position position, double destinationX, + double destinationY) throws + JsonProcessingException { + RestTemplate restTemplate = new RestTemplate(); + String url = getUrl(position.getX(), position.getY(), destinationX, destinationY); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, + new HttpEntity<>(getHeaders()), String.class); + + ObjectMapper objectMapper = new ObjectMapper(); + + JsonNode body = objectMapper.readTree(response.getBody()); + JsonNode routes = body.get("routes"); + JsonNode route = routes.get(0); + int resultCode = route.get("result_code").asInt(); + if (resultCode != 0) { + return new DistanceInfo(-999, -999); + } + JsonNode summary = route.get("summary"); + double distanceByMeters = summary.get("distance").asDouble(); + double timeBySeconds = summary.get("duration").asDouble(); + + return new DistanceInfo(distanceByMeters, timeBySeconds); + } + + private MultiValueMap getHeaders() { + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add("Host", "apis-navi.kakaomobility.com"); + headers.add("Authorization", "KakaoAK " + kakaokey); + + return headers; + } + + private String getUrl(double originX, double originY, double destinationX, + double destinationY) { + String url = "https://apis-navi.kakaomobility.com/v1/directions"; + String origin = originX + "," + originY; + String destination = destinationX + "," + destinationY; + return UriComponentsBuilder.fromHttpUrl(url) + .queryParam("origin", origin) + .queryParam("destination", destination) + .queryParam("summary", true) + .encode() + .toUriString(); + } +} diff --git a/BE/src/main/java/com/team11/airbnb/openapi/Position.java b/BE/src/main/java/com/team11/airbnb/openapi/Position.java new file mode 100644 index 000000000..179534d09 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/openapi/Position.java @@ -0,0 +1,13 @@ +package com.team11.airbnb.openapi; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class Position { + + private double x; + private double y; + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/DistanceSearchController.java b/BE/src/main/java/com/team11/airbnb/web/controller/DistanceSearchController.java new file mode 100644 index 000000000..9f3ebe34f --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/controller/DistanceSearchController.java @@ -0,0 +1,37 @@ +package com.team11.airbnb.web.controller; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.team11.airbnb.openapi.DistanceInfoResponse; +import com.team11.airbnb.openapi.DistrictSearchService; +import com.team11.airbnb.openapi.Position; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping("/distance") +@Slf4j +public class DistanceSearchController { + + private final DistrictSearchService districtSearchService; + + public DistanceSearchController(DistrictSearchService districtSearchService) { + this.districtSearchService = districtSearchService; + } + + @GetMapping("/times") + public ResponseEntity> getDurations(Position position) + throws JsonProcessingException { + log.info("position={}, {}", position.getX(), position.getY()); + List distanceInfoResponses = districtSearchService.searchDistrictInfo(position); + + return ResponseEntity.ok(distanceInfoResponses); + + } + +} From 55d8840bbd719800cce79516018fcfdcab42ca39 Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Wed, 8 Jun 2022 16:40:45 +0900 Subject: [PATCH 24/35] =?UTF-8?q?fix:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=EB=A5=BC=20=ED=86=B5=ED=95=9C=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EB=B0=8F=20=EA=B1=B0=EB=A6=AC=20=EB=8B=A8=EC=9C=84?= =?UTF-8?q?=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 내장 라이브러리를 통해 시간 단위를 초에서 분으로 변경 - 외부 라이브러리를 통해 거리 단위를 미터에서 킬로미터로 변경 --- BE/build.gradle | 1 + .../airbnb/openapi/DistrictSearchService.java | 38 ++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/BE/build.gradle b/BE/build.gradle index 1c2da385c..7c882a2f5 100644 --- a/BE/build.gradle +++ b/BE/build.gradle @@ -21,6 +21,7 @@ dependencies { runtimeOnly 'mysql:mysql-connector-java' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation fileTree(dir: 'libs', include: '*.jar') } tasks.named('test') { diff --git a/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java b/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java index 895b3786a..018b70549 100644 --- a/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java +++ b/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java @@ -1,9 +1,11 @@ package com.team11.airbnb.openapi; +import java.time.Duration; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; @@ -13,11 +15,15 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import com.digidemic.unitof.UnitOf; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; + @Service +@Slf4j public class DistrictSearchService { private String kakaokey = "a1d0ecbd325633346f2938e35d6db4d4"; @@ -27,18 +33,18 @@ public List searchDistrictInfo(Position position) District[] districts = District.values(); List distanceInfoResponses = new ArrayList<>(); for (District district : districts) { - DistanceInfo distanceInfo = calculateDistanceAndTime(position, district.getLongtitude(), + Map result = calculateDistanceAndTime(position, district.getLongtitude(), district.getLatitude()); - String distance = String.valueOf(distanceInfo.getDistanceByKiloMeters()); - String duration = String.valueOf(distanceInfo.getTimeByMinutes()); + String distance = result.get("distance"); + String time = result.get("time"); distanceInfoResponses.add( - new DistanceInfoResponse(district.name(), distance, duration)); + new DistanceInfoResponse(district.name(), distance, time)); } return distanceInfoResponses; } - private DistanceInfo calculateDistanceAndTime(Position position, double destinationX, + private Map calculateDistanceAndTime(Position position, double destinationX, double destinationY) throws JsonProcessingException { RestTemplate restTemplate = new RestTemplate(); @@ -47,19 +53,32 @@ private DistanceInfo calculateDistanceAndTime(Position position, double destinat new HttpEntity<>(getHeaders()), String.class); ObjectMapper objectMapper = new ObjectMapper(); + Map result = new HashMap<>(); JsonNode body = objectMapper.readTree(response.getBody()); JsonNode routes = body.get("routes"); JsonNode route = routes.get(0); int resultCode = route.get("result_code").asInt(); + log.info("result code = {}", resultCode); if (resultCode != 0) { - return new DistanceInfo(-999, -999); + result.put("time", "-999"); + result.put("distance", "-999"); + return result; } JsonNode summary = route.get("summary"); double distanceByMeters = summary.get("distance").asDouble(); - double timeBySeconds = summary.get("duration").asDouble(); + Long timeBySeconds = summary.get("duration").asLong(); + log.warn("distance = {}, time = {}", distanceByMeters, timeBySeconds); + + + Duration duration = Duration.ofSeconds(timeBySeconds); + String timeByMinutes = String.valueOf(duration.toMinutes()); + UnitOf.Length distanceUnitConverter = new UnitOf.Length().fromMeters(distanceByMeters); + String distanceByKilometers = String.valueOf(distanceUnitConverter.toKilometers()); + result.put("time", timeByMinutes); + result.put("distance", distanceByKilometers); - return new DistanceInfo(distanceByMeters, timeBySeconds); + return result; } private MultiValueMap getHeaders() { @@ -75,6 +94,7 @@ private String getUrl(double originX, double originY, double destinationX, String url = "https://apis-navi.kakaomobility.com/v1/directions"; String origin = originX + "," + originY; String destination = destinationX + "," + destinationY; + log.info("origin = {}, destination = {}", origin, destination); return UriComponentsBuilder.fromHttpUrl(url) .queryParam("origin", origin) .queryParam("destination", destination) From 4c43731b5112213f99c288685357e5996cfa0a3b Mon Sep 17 00:00:00 2001 From: Sangpil Hwang Date: Thu, 9 Jun 2022 11:44:20 +0900 Subject: [PATCH 25/35] Update README.md --- README.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8ec09bd21..aa7f50b38 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,82 @@ -# airbnb -그룹 프로젝트 #4 +# Team11_ Airbnb clone App + +## 📑 Summarized +### 🤼‍♂️ Member (iOS / BackEnd) +| | 닉네임 | github ID | MBTI | +| --- | --- | --- | --- | +| **IOS** | 솔 | [Hansolkkim](https://github.com/Hansolkkim) | ISFJ | +| | 푸코 | [wnsxor1993](https://github.com/wnsxor1993) | INTP | +| **BE** | 필 | [PhilSoGooood](https://github.com/PhilSoGooood) | ENFP | +| | 민지노 | [Minzino](https://github.com/Minzino) | ESFJ | + +### 🏆 Purpose +#### 기존 Airbnb와 유사한 기능과 화면을 구현하는 클론 앱을 만들고자 합니다. + + +## ✍️ Description +
+ 🧑‍💻 iOS +
+ +### 🔥 iOS's Purpose +- **MVC 패턴을 기반으로 최대한 많은 화면과 기능을 구현하는 것과 전부 코드로 작성하는 것을 목표로 작업 진행** + +### 📋 Main Work +- AlamoFire를 이용한 네트워킹 +- MapKit을 활용하여 장소 자동 서칭 기능 구현 +- CLLocation을 통해 사용자 위치 정보 권한 요청 및 데이터 가공 기능 구현 +- Custom CalendarView 구현 +- CompositionalLayout 활용 및 dataSource 로직 분리 + +### 💾 DailyScrum +[노션 링크_ 일별 작업 진행 사항](https://sticky-pajama-dc0.notion.site/099e80f32cd741388309c0ad2b6c20b4?v=bcc2b7f8465d4159b0f8ebc9fe26018f) + +### 📝 Summary +1. HomeView + - CLLocation을 통해 사용자 위치 권한 요청 및 정보 데이터 관리 기능 구현 + - 위치 권한 denied 시, Alert를 띄워 설정창으로 이동할 수 있게 구현 + - 사용자 위치 정보 데이터 관리 객체 구현 + - 한 화면에 여러 CollectionView가 필요하여 `UICollectionViewCompositionalLayout`을 활용하여 화면 구성 + - UISearchBar를 button 형태로 활용 + +2. BrowseView + - UISearchController를 통해 searchBar 구현 + - 화면 전환 시, searchBar를 firstResponder로 지정하여 키보드가 바로 출력되도록 구현 + - Mapkit을 활용하여 장소에 대한 자동 서칭 기능 구현 + - 기존 화면은 자동으로 인기 장소를 보여주는 화면이나, 서칭 시에 자동 서칭 화면으로 전환되도록 구현 + - MKLocalSearchCompleter와 Completion 배열을 관리하는 manager 객체를 분리 + + +3. DecidingOptionsView + - Custom CalendarView를 구현하여 체크인/ 체크아웃 날짜를 선택할 수 있는 기능 구현 + - Delegate 패턴을 이용해 View와 Controller, UseCase 간 데이터를 주고 받도록 구현 + +4. AccomodationsView + - 이전 화면에서 선택한 Option들을 넘겨받아 화면을 구성하도록 구현 + +5. DetailPageView + - dataSource가 VC의 코드와 분리되어 있는 상태에서 Cell 내부의 프로퍼티의 액션을 연결하기 위해 delegate 활용 + - Custom ToolBar 구현 + - 단순 button이 아닌, customView를 UIBarButtonItem으로 할당 + +|HomeView|BrowseView1(검색전)|BrowseView2(검색후)| +|---|---|---| +|SS 2022-06-08 PM 03 33 54|SS 2022-06-08 PM 05 46 10|SS 2022-06-08 PM 05 47 14| + + + +|DecidingOptionsView(CalendarView)|Accomodations|DetailPageView| +|---|---|---| +|SS 2022-06-08 PM 05 49 25|SS 2022-06-08 PM 04 23 32|SS 2022-06-08 PM 04 20 19| + +
+
+ +
+ 🧑‍💻 Backend +
+BE분들 내용은 여기다가 작성하시면 됩니다! + +
+
+ From ca38301c6f2b3e93142904d41755a9d9cee8567b Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Thu, 9 Jun 2022 12:09:11 +0900 Subject: [PATCH 26/35] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 단위 변환을 내장, 외장 라이브러리를 사용하여 해당 역할을 담당하던 DistanceInfo 클래스를 삭제하였습니다. --- .../team11/airbnb/openapi/DistanceInfo.java | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 BE/src/main/java/com/team11/airbnb/openapi/DistanceInfo.java diff --git a/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfo.java b/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfo.java deleted file mode 100644 index 0dfed34ce..000000000 --- a/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfo.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.team11.airbnb.openapi; - -import lombok.Getter; - -@Getter -public class DistanceInfo { - - private static final int M_TO_KM = 1000; - private static final int SEC_TO_MIN = 60; - - private final double distanceByKiloMeters; - private final double timeByMinutes; - - public DistanceInfo(double distanceByMeters, double timeBySeconds) { - this.distanceByKiloMeters = toKm(distanceByMeters); - this.timeByMinutes = toMin(timeBySeconds); - } - - private double toKm(double distanceByMeters) { - return distanceByMeters / M_TO_KM; - } - - private double toMin(double timeBySeconds) { - return timeBySeconds / SEC_TO_MIN; - } -} From ecf0e0f68b4ce157e8c96bd65be62b643ab91229 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Thu, 9 Jun 2022 14:55:09 +0900 Subject: [PATCH 27/35] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20ENUM=ED=81=B4=EB=9E=98=EC=8A=A4=EC=99=80=20DTO?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - district를 enum이 아닌 엔티티로 관리하기위해 수정 - DistanceInfoResponse를 AroundSpotDto로 대체 --- .../airbnb/openapi/DistanceInfoResponse.java | 13 ----- .../com/team11/airbnb/openapi/District.java | 34 ------------ .../airbnb/openapi/DistrictSearchService.java | 53 ++++++++++--------- .../airbnb/web/controller/HomeController.java | 8 +-- .../team11/airbnb/web/dto/AroundSpotDto.java | 6 ++- 5 files changed, 35 insertions(+), 79 deletions(-) delete mode 100644 BE/src/main/java/com/team11/airbnb/openapi/DistanceInfoResponse.java delete mode 100644 BE/src/main/java/com/team11/airbnb/openapi/District.java diff --git a/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfoResponse.java b/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfoResponse.java deleted file mode 100644 index 5b3a345c7..000000000 --- a/BE/src/main/java/com/team11/airbnb/openapi/DistanceInfoResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.team11.airbnb.openapi; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public class DistanceInfoResponse { - - private String name; - private String distance; - private String time; -} diff --git a/BE/src/main/java/com/team11/airbnb/openapi/District.java b/BE/src/main/java/com/team11/airbnb/openapi/District.java deleted file mode 100644 index 0b1f10015..000000000 --- a/BE/src/main/java/com/team11/airbnb/openapi/District.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.team11.airbnb.openapi; - -public enum District { - SEOUL("서울", 126.97857310672501, 37.56654037462486), - YONGIN("용인",127.17751600488093, 37.2409718113933), - SUWON("수원", 127.02869106496286, 37.263501338400935), - GANGRUENG("강릉", 128.8759059060531, 37.752098625346775), - BUSAN("부산", 129.075073717621, 35.17975315226952), - JEONJU("전주", 127.14807326809175, 35.82363477515946), - YEOSU("여수", 127.66230030492281, 34.76021870014728), - GYEONGJU("경주", 129.22480648691675, 35.8561148324664); - - private String name; - private double longtitude; //x - private double latitude; //y - - District(String name, double longtitude, double latitude) { - this.name = name; - this.longtitude = longtitude; - this.latitude = latitude; - } - - public String getName() { - return name; - } - - public double getLongtitude() { - return longtitude; - } - - public double getLatitude() { - return latitude; - } -} diff --git a/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java b/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java index 018b70549..2eb183b5a 100644 --- a/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java +++ b/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java @@ -1,11 +1,20 @@ package com.team11.airbnb.openapi; +import com.digidemic.unitof.UnitOf; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.team11.airbnb.domain.District; +import com.team11.airbnb.domain.Location; +import com.team11.airbnb.web.dto.AroundSpotDto; +import com.team11.airbnb.web.repository.DistrictRepository; import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; @@ -15,39 +24,35 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; -import com.digidemic.unitof.UnitOf; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -import lombok.extern.slf4j.Slf4j; - @Service @Slf4j +@RequiredArgsConstructor public class DistrictSearchService { - private String kakaokey = "a1d0ecbd325633346f2938e35d6db4d4"; + private final String kakaokey = "a1d0ecbd325633346f2938e35d6db4d4"; + private final DistrictRepository districtRepository; - public List searchDistrictInfo(Position position) + public List searchDistrictInfo(Position position) throws JsonProcessingException { - District[] districts = District.values(); - List distanceInfoResponses = new ArrayList<>(); + List districts = districtRepository.findAll(); + List aroundSpotDtoList = new ArrayList<>(); for (District district : districts) { - Map result = calculateDistanceAndTime(position, district.getLongtitude(), - district.getLatitude()); + Map result = calculateDistanceAndTime(position, district.getLocation()); String distance = result.get("distance"); String time = result.get("time"); - distanceInfoResponses.add( - new DistanceInfoResponse(district.name(), distance, time)); + aroundSpotDtoList.add( + new AroundSpotDto(district.getId(), district.getName(), district.getImagePath(), + distance, time)); } - return distanceInfoResponses; + return aroundSpotDtoList; } - private Map calculateDistanceAndTime(Position position, double destinationX, - double destinationY) throws - JsonProcessingException { + private Map calculateDistanceAndTime(Position position, Location location) + throws JsonProcessingException { RestTemplate restTemplate = new RestTemplate(); + double destinationX = Double.parseDouble(location.getLongitude()); + double destinationY = Double.parseDouble(location.getLatitude()); String url = getUrl(position.getX(), position.getY(), destinationX, destinationY); ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(getHeaders()), String.class); @@ -70,7 +75,6 @@ private Map calculateDistanceAndTime(Position position, double d Long timeBySeconds = summary.get("duration").asLong(); log.warn("distance = {}, time = {}", distanceByMeters, timeBySeconds); - Duration duration = Duration.ofSeconds(timeBySeconds); String timeByMinutes = String.valueOf(duration.toMinutes()); UnitOf.Length distanceUnitConverter = new UnitOf.Length().fromMeters(distanceByMeters); @@ -95,11 +99,8 @@ private String getUrl(double originX, double originY, double destinationX, String origin = originX + "," + originY; String destination = destinationX + "," + destinationY; log.info("origin = {}, destination = {}", origin, destination); - return UriComponentsBuilder.fromHttpUrl(url) - .queryParam("origin", origin) - .queryParam("destination", destination) - .queryParam("summary", true) - .encode() + return UriComponentsBuilder.fromHttpUrl(url).queryParam("origin", origin) + .queryParam("destination", destination).queryParam("summary", true).encode() .toUriString(); } } diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java b/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java index f10033dd9..d1652ae04 100644 --- a/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java +++ b/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java @@ -1,18 +1,18 @@ package com.team11.airbnb.web.controller; +import com.team11.airbnb.openapi.DistrictSearchService; import com.team11.airbnb.web.dto.HomeData; import com.team11.airbnb.web.service.HomeService; +import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController +@RequiredArgsConstructor public class HomeController { private final HomeService homeService; - - public HomeController(HomeService homeService){ - this.homeService = homeService; - } + private final DistrictSearchService districtSearchService; @GetMapping("/home") public HomeData home() throws Exception { diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java b/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java index 626d349cd..3ad8f1754 100644 --- a/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java +++ b/BE/src/main/java/com/team11/airbnb/web/dto/AroundSpotDto.java @@ -8,7 +8,9 @@ public class AroundSpotDto { private Long id; - private String title; - private int distance; + private String title;; private String imagePath; + private String distance; + private String time; + } From 4921ea2dc00fac770545df47e232c9f3d17e0c84 Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Thu, 9 Jun 2022 15:44:05 +0900 Subject: [PATCH 28/35] =?UTF-8?q?refactor:=20=ED=99=88=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=98=84=EC=9E=AC=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=EC=99=80=20=EC=97=AC=ED=96=89=EC=A7=80=EC=99=80=EC=9D=98=20?= =?UTF-8?q?=EA=B1=B0=EB=A6=AC=20=EB=B0=8F=20=EC=9D=B4=EB=8F=99=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=EC=9D=84=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HomeController에서 HomeService, DistrictSearchService를 통해 각 Dto를 반환받아 HomeData를 생성하는것으로 수정하였습니다. --- .../controller/DistanceSearchController.java | 37 ------------------- .../airbnb/web/controller/HomeController.java | 15 ++++++-- .../service}/DistrictSearchService.java | 3 +- .../airbnb/web/service/HomeService.java | 35 +++++++----------- 4 files changed, 28 insertions(+), 62 deletions(-) delete mode 100644 BE/src/main/java/com/team11/airbnb/web/controller/DistanceSearchController.java rename BE/src/main/java/com/team11/airbnb/{openapi => web/service}/DistrictSearchService.java (98%) diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/DistanceSearchController.java b/BE/src/main/java/com/team11/airbnb/web/controller/DistanceSearchController.java deleted file mode 100644 index 9f3ebe34f..000000000 --- a/BE/src/main/java/com/team11/airbnb/web/controller/DistanceSearchController.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.team11.airbnb.web.controller; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.team11.airbnb.openapi.DistanceInfoResponse; -import com.team11.airbnb.openapi.DistrictSearchService; -import com.team11.airbnb.openapi.Position; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - - -@RestController -@RequestMapping("/distance") -@Slf4j -public class DistanceSearchController { - - private final DistrictSearchService districtSearchService; - - public DistanceSearchController(DistrictSearchService districtSearchService) { - this.districtSearchService = districtSearchService; - } - - @GetMapping("/times") - public ResponseEntity> getDurations(Position position) - throws JsonProcessingException { - log.info("position={}, {}", position.getX(), position.getY()); - List distanceInfoResponses = districtSearchService.searchDistrictInfo(position); - - return ResponseEntity.ok(distanceInfoResponses); - - } - -} diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java b/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java index d1652ae04..ab20c0570 100644 --- a/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java +++ b/BE/src/main/java/com/team11/airbnb/web/controller/HomeController.java @@ -1,6 +1,12 @@ package com.team11.airbnb.web.controller; -import com.team11.airbnb.openapi.DistrictSearchService; +import java.util.List; + +import com.team11.airbnb.openapi.Position; +import com.team11.airbnb.web.dto.AroundSpotDto; +import com.team11.airbnb.web.dto.MainEventDto; +import com.team11.airbnb.web.dto.ThemeSpotDto; +import com.team11.airbnb.web.service.DistrictSearchService; import com.team11.airbnb.web.dto.HomeData; import com.team11.airbnb.web.service.HomeService; import lombok.RequiredArgsConstructor; @@ -15,8 +21,11 @@ public class HomeController { private final DistrictSearchService districtSearchService; @GetMapping("/home") - public HomeData home() throws Exception { - return homeService.initApplication(); + public HomeData home(Position position) throws Exception { + MainEventDto mainEventDto = homeService.getMainEventDto(); + List themeSpotDtoList = homeService.getThemeSpotDtoList(); + List aroundSpotDtos = districtSearchService.searchDistrictInfo(position); + return new HomeData(mainEventDto, aroundSpotDtos, themeSpotDtoList); } } diff --git a/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java b/BE/src/main/java/com/team11/airbnb/web/service/DistrictSearchService.java similarity index 98% rename from BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java rename to BE/src/main/java/com/team11/airbnb/web/service/DistrictSearchService.java index 2eb183b5a..7f2ca921d 100644 --- a/BE/src/main/java/com/team11/airbnb/openapi/DistrictSearchService.java +++ b/BE/src/main/java/com/team11/airbnb/web/service/DistrictSearchService.java @@ -1,4 +1,4 @@ -package com.team11.airbnb.openapi; +package com.team11.airbnb.web.service; import com.digidemic.unitof.UnitOf; import com.fasterxml.jackson.core.JsonProcessingException; @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.team11.airbnb.domain.District; import com.team11.airbnb.domain.Location; +import com.team11.airbnb.openapi.Position; import com.team11.airbnb.web.dto.AroundSpotDto; import com.team11.airbnb.web.repository.DistrictRepository; import java.time.Duration; diff --git a/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java b/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java index 54ee5fdbd..062756fe5 100644 --- a/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java +++ b/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java @@ -1,48 +1,41 @@ package com.team11.airbnb.web.service; -import com.team11.airbnb.domain.District; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Service; + import com.team11.airbnb.domain.MainEvent; import com.team11.airbnb.domain.ThemeSpot; -import com.team11.airbnb.web.dto.AroundSpotDto; -import com.team11.airbnb.web.dto.HomeData; import com.team11.airbnb.web.dto.MainEventDto; import com.team11.airbnb.web.dto.ThemeSpotDto; import com.team11.airbnb.web.repository.DistrictRepository; import com.team11.airbnb.web.repository.MainEventRepository; import com.team11.airbnb.web.repository.ThemeRepository; -import java.util.ArrayList; -import java.util.List; + import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class HomeService { - private final DistrictRepository districtRepository; private final MainEventRepository mainEventRepository; private final ThemeRepository themeRepository; - public HomeData initApplication() throws Exception { - - MainEvent mainEvent = mainEventRepository.findById(1L).orElseThrow(Exception::new); - List districtList = districtRepository.findAll(); + public List getThemeSpotDtoList() { List themeSpotList = themeRepository.findAll(); - MainEventDto mainEventDto = new MainEventDto(mainEvent.getTitle(), mainEvent.getLabel(), - mainEvent.getImagePath(), mainEvent.getButtonText()); - List aroundSpotDtoList = new ArrayList<>(); List themeSpotDtoList = new ArrayList<>(); - for (District district : districtList) { - AroundSpotDto aroundSpotDto = new AroundSpotDto(district.getId(), district.getName(), - 10, - district.getImagePath()); - aroundSpotDtoList.add(aroundSpotDto); - } for (ThemeSpot themeSpot : themeSpotList) { ThemeSpotDto themeSpotDto = new ThemeSpotDto(themeSpot.getImagePath(), themeSpot.getTitle()); themeSpotDtoList.add(themeSpotDto); } - return new HomeData(mainEventDto, aroundSpotDtoList, themeSpotDtoList); + return themeSpotDtoList; + } + + public MainEventDto getMainEventDto() throws Exception { + MainEvent mainEvent = mainEventRepository.findById(1L).orElseThrow(Exception::new); + return new MainEventDto(mainEvent.getTitle(), mainEvent.getLabel(), + mainEvent.getImagePath(), mainEvent.getButtonText()); } } From fdd438a2571dfa8ae9a5610d162186e8a530c8a7 Mon Sep 17 00:00:00 2001 From: Meenzino <86910955+Minzino@users.noreply.github.com> Date: Thu, 9 Jun 2022 18:32:31 +0900 Subject: [PATCH 29/35] =?UTF-8?q?feat:=20=EB=B0=A9=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../airbnb/web/controller/RoomController.java | 9 ++++++++- .../airbnb/web/dto/RoomSearchResponse.java | 18 ++++++++++++++++++ .../team11/airbnb/web/service/RoomService.java | 13 +++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 BE/src/main/java/com/team11/airbnb/web/dto/RoomSearchResponse.java diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java index a48ce03a4..54cfd757a 100644 --- a/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java +++ b/BE/src/main/java/com/team11/airbnb/web/controller/RoomController.java @@ -1,14 +1,16 @@ package com.team11.airbnb.web.controller; import com.team11.airbnb.web.dto.RoomDetailDto; +import com.team11.airbnb.web.dto.RoomSearchResponse; import com.team11.airbnb.web.service.RoomService; +import java.util.List; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("room") +@RequestMapping("rooms") public class RoomController { private final RoomService roomService; @@ -17,6 +19,11 @@ public RoomController(RoomService roomService){ this.roomService = roomService; } + @GetMapping + public List findRooms(){ + return roomService.findAll(); + } + @GetMapping("{id}") public RoomDetailDto findByRoomDetail(@PathVariable("id") Long id) throws Exception { return roomService.findOne(id); diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/RoomSearchResponse.java b/BE/src/main/java/com/team11/airbnb/web/dto/RoomSearchResponse.java new file mode 100644 index 000000000..94d0bee82 --- /dev/null +++ b/BE/src/main/java/com/team11/airbnb/web/dto/RoomSearchResponse.java @@ -0,0 +1,18 @@ +package com.team11.airbnb.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class RoomSearchResponse { + + private Long id; + private double averageGrade; + private int numberOfReviews; + private String imagePath; + private String title; + private boolean isWishList; + private int price; + +} diff --git a/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java b/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java index f76a5f4e2..4b9a8be49 100644 --- a/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java +++ b/BE/src/main/java/com/team11/airbnb/web/service/RoomService.java @@ -4,7 +4,9 @@ import com.team11.airbnb.domain.Room; import com.team11.airbnb.web.dto.HostDto; import com.team11.airbnb.web.dto.RoomDetailDto; +import com.team11.airbnb.web.dto.RoomSearchResponse; import com.team11.airbnb.web.repository.RoomRepository; +import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Service; @@ -31,4 +33,15 @@ public RoomDetailDto findOne(Long roomId) throws Exception { host(new HostDto(host.getId(), host.getName(), host.isSuperHost(), host.getProfileImagePath())).build(); } + + public List findAll() { + List rooms = repository.findAll(); + List roomSearchResponses = new ArrayList<>(); + + for (Room room : rooms) { + roomSearchResponses.add(new RoomSearchResponse(room.getId(),room.getAverageGrade(),room.getReviews().size(), room.getRoomImages().get(0).getImagePath(), room.getName(), false, room.getPrice())); + } + + return roomSearchResponses; + } } From ce23eeba494e00141e5c22271ef69e3615541d1e Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Thu, 9 Jun 2022 18:36:47 +0900 Subject: [PATCH 30/35] =?UTF-8?q?test:=20=EB=B0=B0=ED=8F=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 100 +++++++++++++++++------------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index fc3357b36..a8aa4f52f 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -9,84 +9,84 @@ name: Java CI with Gradle on: push: - branches: [ BE-deploy ] + branches: [ deploy-test2 ] pull_request: - branches: [ BE-deploy ] + branches: [ deploy-test2 ] jobs: build: runs-on: ubuntu-latest - defaults: run: working-directory: ./BE steps: - uses: actions/checkout@v2 + + # JDK11로 gradle 빌드 - name: Set up JDK 11 uses: actions/setup-java@v2 with: java-version: '11' distribution: 'temurin' - - - name: Insert data source information into application.yml - run: | - sed -i "s|datasource_url|$DATASOURCE_URL|g" ./src/main/resources/application.yml - sed -i "s|datasource_username|$DATASOURCE_USERNAME|g" ./src/main/resources/application.yml - sed -i "s|datasource_password|$DATASOURCE_PASSWORD|g" ./src/main/resources/application.yml - env: - DATASOURCE_URL: ${{ secrets.DATASOURCE_URL }} - DATASOURCE_USERNAME: ${{ secrets.DATASOURCE_USERNAME }} - DATASOURCE_PASSWORD: ${{ secrets.DATASOURCE_PASSWORD }} - - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew bootJar + run: ./gradlew build # 도커 빌드(도커 이미지 생성) - name: Docker build run: | - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker build -t philsogood/airbnb:1.0 . - docker push ${{ secrets.DOCKER_USERNAME }}/airbnb:1.0 - docker rmi ${{ secrets.DOCKER_USERNAME }}/airbnb:1.0 - - # 디렉토리 생성 - - name: Make Directory - run: mkdir -p deploy - - # appspec.yml 파일 복사 - - name: Copy appspec.yml - run: cp appspec.yml ./deploy - - # script files 복사 - - name: Copy script - run: cp ./scripts/*.sh ./deploy - - # 파일 압축 - - name: Make zip file - run: zip -r ./airbnb.zip ./deploy - - # S3와 CodeDeploy를 통한 배포 + docker login -u zinophilsquad -p Password1234! + docker build -t zinophilsquad/airbnb-test-deploy:1.0 . + docker push zinophilsquad/airbnb-test-deploy:1.0 + docker rmi zinophilsquad/airbnb-test-deploy:1.0 + # 도커 이미지 배포 및 실행(EC2 ubuntu20.04로 배포) - name: Deploy - uses: aws-actions/configure-aws-credentials@v1 + uses: appleboy/ssh-action@master with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ap-northeast-2 - - - - name: Upload to S3 - run: aws s3 cp --region ap-northeast-2 --acl private ./airbnb.zip s3://airbnb-phil-deploy-dockerimage/ - - - name: Start CodeDeploy - run: aws deploy create-deployment --application-name Airbnb-Codedeploy-Application --deployment-group-name Airbnb-Codedeploy-Target-Group --s3-location bucket=airbnb-phil-deploy-dockerimage,key=airbnb.zip,bundleType=zip - - # 배경 성공시 슬랙 알람 + host: 144.24.86.236 + username: ubuntu + key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEogIBAAKCAQEAzR7erj4IQoFcQ5S1yBOARlMe1BIAPymPV7og8/ltqbGn36uf + QrQLrJs0HAQ1aFrXeyd+aIKm68XSlWAcHVIwIEC/Sz14vIMhQYZLS++frVJnkWjN + W75bc3/Wkh7u71l45OM+8LXztkKwIi7z+CfP/0lK7PKMPPdZbZCe2hZJjwnSNUVg + C6/SO/Kbp7K6e6d3f2XYRREazER3pxnQLJiykHIQPzA6siRFrnqbprGnp47mtQSp + CrLY3aRxsjXg0TkK9lEUYM5iUMNBFXMYH+RmR11I20o+AJehbt47jAFgIl93fcAd + yrEo7jTNHbr0r6TeOLTdJNoTcd1Wl8us3OBrMwIDAQABAoIBAApp/iWb4ft5FIrH + KruFEfGAGC6YLgmxkyTXFFk5WVymN78JYu/rXYmz5N9kmfmPCHr+NFLGCgd/vFii + eorv40r1PpZpVo9A8Rf3ONdwIQvsWnRtfQ+isy3tWE2EOIlPt5hCBANgmYZauGqO + i7XA/7RsWjDRglaOEFeZt2xHJBDuTWsmNWCCdc5o8rX73c1cUz6BcN2I7/av6LSU + gM40F8/z+eu8Z7vzAY/HtKHbrj/lqABsgObKnNa4VI0dlQYKhlHwXAM+hZ7e57mr + i9f42nmZUktkldqb3UNepKy2BJxL9b9KfaXNPBF81d525uAytTMqNCaAte8o386v + ziv9xyECgYEA8UoI3vzdOPjAp7GAKpqnIEVQCvj+FlqhnejMCp84lZ3tWrWlKRCO + 2kGNHdykLxIvIKtGMRxlP9Iv5X4j9w5Ag2mLgwuOgL71mXHNKB+fO8d8xijX5XWz + zvkrGfQFYbZG4IQiS1EFXmFDHLHlNx32fV2SBKaj9Rqx9e/yxObKuJsCgYEA2aBT + svYcvjqFHTtLjs9Y0yLzbuU/k+LZHQBTnXBGXPTk5FaImKgXlf0qs+KL2NcjW+h3 + vKpgz0LC0xSrNxaPjr8WqiM/6swvUiRYa8FVCECzVIfBSpoK987nf02bxTUD94N4 + 0ck1eEX9EwH1pNAVrRYQyst+dVeewx6RSYjwRUkCgYBJ1VO9mDbBdizNo50KbGRz + lYIWuXZWtMj7nv7AyVPxRHAa77ZoSG7ODnz2BEwhMzlEj+Rcpsm8q76IRP8QXEYV + cDiGkwS6FLTmjVIv0u1Qx4xAvsLBrjClRs2PlYIIElTB4uzziwlPhgeZQj5XjU2d + swI5dhx5pf+KuC/bXJvvvQKBgGrbCR3fMD/LVh8DRVdKwg9xotu5np0LjpVp2qR8 + Q2BTMqxXPNzP9DzGQRhwTUBcalUvcNNnSP+bhTPFe0giQLzTYNqLUlAEj9uiUvQd + ypWxxxvjSGpL9sS0iAB+59RN8rOujz1asXFr1BZoKOgS8AG7yuT4RBBzxFWEBcx4 + e5lZAoGAE7b8lrbOmqGnGt9a+ui9X5PLPJycOEVib9JPIADFoPaKobWj4Kor1QSn + jOW3jBPDLMNARWSmLKIod+My9JtQb3Y1XfIB0IQcdm8zPVKZ6Dvc71GUFSucqxVt + tjB2069+PZnVYyzCI0zkmmzRRxqYKLhVRVcrfdHgAxMzd5Is8ro= + -----END RSA PRIVATE KEY----- + envs: GITHUB_SHA + script: | + sudo docker ps -a -q --filter "name=airbnb-test-deploy" | grep -q . && docker stop airbnb-test-deploy && docker rm airbnb-test-deploy | true + sudo docker rmi zinophilsquad/airbnb-test-deploy:1.0 + sudo docker pull zinophilsquad/airbnb-test-deploy:1.0 + sudo docker run -d -p 80:8080 --name airbnb-test-deploy zinophilsquad/airbnb-test-deploy:1.0 + sudo docker rmi -f $(docker images -f "dangling=true" -q) || true + # 빌드 성공시 슬랙채널로 알람 - name: action-slack uses: 8398a7/action-slack@v3 + with: status: ${{ job.status }} author_name: Github Action Test # default: 8398a7@action-slack From 72d7c4567ed888b10abf4d18e14574bd235ef08c Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Thu, 9 Jun 2022 18:40:16 +0900 Subject: [PATCH 31/35] =?UTF-8?q?test:=20=EB=B0=B0=ED=8F=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/libs/UnitOf.jar | Bin 0 -> 74020 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 BE/libs/UnitOf.jar diff --git a/BE/libs/UnitOf.jar b/BE/libs/UnitOf.jar new file mode 100755 index 0000000000000000000000000000000000000000..ec4a8106f0388f112a14ce57cac7b4bc595df48c GIT binary patch literal 74020 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 literal 0 HcmV?d00001 From 625a35ebbae3a04b3c733f46dd44c16ee0b2a10e Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Thu, 9 Jun 2022 18:41:20 +0900 Subject: [PATCH 32/35] =?UTF-8?q?test:=20=EB=B0=B0=ED=8F=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/main/resources/application.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BE/src/main/resources/application.yml b/BE/src/main/resources/application.yml index 2af17fd67..6bb106335 100644 --- a/BE/src/main/resources/application.yml +++ b/BE/src/main/resources/application.yml @@ -1,8 +1,8 @@ spring: datasource: - url: datasource_url - username: datasource_username - password: datasource_password + url: jdbc:mysql://airbnb-db.cabi5bwgwykn.ap-northeast-2.rds.amazonaws.com:3306/airbnb + username: team11 + password: Password1! driver-class-name: com.mysql.cj.jdbc.Driver sql: init: From 58f5c583e55f88451435bcb4e0b99f4a3f642075 Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Fri, 10 Jun 2022 14:04:39 +0900 Subject: [PATCH 33/35] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81(=EB=B0=98=EB=B3=B5?= =?UTF-8?q?=EB=AC=B8=20->=20stream)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TheamSpot 엔티티를 Dto로 변환하는 과정을 Stream을 이용하여 간소화하였습니다. --- .../com/team11/airbnb/web/dto/ThemeSpotDto.java | 6 ++++++ .../com/team11/airbnb/web/service/HomeService.java | 13 ++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/BE/src/main/java/com/team11/airbnb/web/dto/ThemeSpotDto.java b/BE/src/main/java/com/team11/airbnb/web/dto/ThemeSpotDto.java index 17a90d091..76378c7c6 100644 --- a/BE/src/main/java/com/team11/airbnb/web/dto/ThemeSpotDto.java +++ b/BE/src/main/java/com/team11/airbnb/web/dto/ThemeSpotDto.java @@ -1,5 +1,7 @@ package com.team11.airbnb.web.dto; +import com.team11.airbnb.domain.ThemeSpot; + import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,4 +12,8 @@ public class ThemeSpotDto { private String imagePath; private String title; + public ThemeSpotDto(ThemeSpot themeSpot) { + this.imagePath = themeSpot.getImagePath(); + this.title = themeSpot.getTitle(); + } } diff --git a/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java b/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java index 062756fe5..bc24a067a 100644 --- a/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java +++ b/BE/src/main/java/com/team11/airbnb/web/service/HomeService.java @@ -1,7 +1,7 @@ package com.team11.airbnb.web.service; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.springframework.stereotype.Service; @@ -9,7 +9,6 @@ import com.team11.airbnb.domain.ThemeSpot; import com.team11.airbnb.web.dto.MainEventDto; import com.team11.airbnb.web.dto.ThemeSpotDto; -import com.team11.airbnb.web.repository.DistrictRepository; import com.team11.airbnb.web.repository.MainEventRepository; import com.team11.airbnb.web.repository.ThemeRepository; @@ -24,13 +23,9 @@ public class HomeService { public List getThemeSpotDtoList() { List themeSpotList = themeRepository.findAll(); - List themeSpotDtoList = new ArrayList<>(); - for (ThemeSpot themeSpot : themeSpotList) { - ThemeSpotDto themeSpotDto = new ThemeSpotDto(themeSpot.getImagePath(), - themeSpot.getTitle()); - themeSpotDtoList.add(themeSpotDto); - } - return themeSpotDtoList; + return themeSpotList.stream() + .map(ThemeSpotDto::new) + .collect(Collectors.toList()); } public MainEventDto getMainEventDto() throws Exception { From 39e2d957f6964b456e1c6d4a2ceec391464dd65c Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Fri, 10 Jun 2022 14:05:30 +0900 Subject: [PATCH 34/35] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81(actuator=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 HealthController를 통해 서버 상태를 확인하던 것을 Actuator로 바꾸었습니다. --- BE/build.gradle | 1 + .../airbnb/web/controller/HealthController.java | 12 ------------ BE/src/main/resources/application.yml | 13 +++++++++++++ 3 files changed, 14 insertions(+), 12 deletions(-) delete mode 100644 BE/src/main/java/com/team11/airbnb/web/controller/HealthController.java diff --git a/BE/build.gradle b/BE/build.gradle index 7c882a2f5..4e56e2f4f 100644 --- a/BE/build.gradle +++ b/BE/build.gradle @@ -22,6 +22,7 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation fileTree(dir: 'libs', include: '*.jar') + implementation 'org.springframework.boot:spring-boot-starter-actuator' } tasks.named('test') { diff --git a/BE/src/main/java/com/team11/airbnb/web/controller/HealthController.java b/BE/src/main/java/com/team11/airbnb/web/controller/HealthController.java deleted file mode 100644 index 93687e1e2..000000000 --- a/BE/src/main/java/com/team11/airbnb/web/controller/HealthController.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.team11.airbnb.web.controller; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class HealthController { - @GetMapping("/health") - public String healthCheck(){ - return "health ok"; - } -} diff --git a/BE/src/main/resources/application.yml b/BE/src/main/resources/application.yml index 6bb106335..b1aa35275 100644 --- a/BE/src/main/resources/application.yml +++ b/BE/src/main/resources/application.yml @@ -19,3 +19,16 @@ spring: logging: level: com.team11: debug + +management: + endpoints: + web: + exposure: + include: "*" + endpoint: + health: + enabled: + show-details: always + health: + db: + enabled: true From 71c9750cf9a77b6965ec72b6fe028d20338b57ff Mon Sep 17 00:00:00 2001 From: PhilSoGooood Date: Fri, 10 Jun 2022 14:10:26 +0900 Subject: [PATCH 35/35] =?UTF-8?q?chore:=20=ED=99=98=EA=B2=BD=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit workflow 수정 --- .github/workflows/gradle.yml | 100 +++++++++++++++++------------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index a8aa4f52f..fc3357b36 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -9,84 +9,84 @@ name: Java CI with Gradle on: push: - branches: [ deploy-test2 ] + branches: [ BE-deploy ] pull_request: - branches: [ deploy-test2 ] + branches: [ BE-deploy ] jobs: build: runs-on: ubuntu-latest + defaults: run: working-directory: ./BE steps: - uses: actions/checkout@v2 - - # JDK11로 gradle 빌드 - name: Set up JDK 11 uses: actions/setup-java@v2 with: java-version: '11' distribution: 'temurin' + + - name: Insert data source information into application.yml + run: | + sed -i "s|datasource_url|$DATASOURCE_URL|g" ./src/main/resources/application.yml + sed -i "s|datasource_username|$DATASOURCE_USERNAME|g" ./src/main/resources/application.yml + sed -i "s|datasource_password|$DATASOURCE_PASSWORD|g" ./src/main/resources/application.yml + env: + DATASOURCE_URL: ${{ secrets.DATASOURCE_URL }} + DATASOURCE_USERNAME: ${{ secrets.DATASOURCE_USERNAME }} + DATASOURCE_PASSWORD: ${{ secrets.DATASOURCE_PASSWORD }} + - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew build + run: ./gradlew bootJar # 도커 빌드(도커 이미지 생성) - name: Docker build run: | - docker login -u zinophilsquad -p Password1234! - docker build -t zinophilsquad/airbnb-test-deploy:1.0 . - docker push zinophilsquad/airbnb-test-deploy:1.0 - docker rmi zinophilsquad/airbnb-test-deploy:1.0 - # 도커 이미지 배포 및 실행(EC2 ubuntu20.04로 배포) + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker build -t philsogood/airbnb:1.0 . + docker push ${{ secrets.DOCKER_USERNAME }}/airbnb:1.0 + docker rmi ${{ secrets.DOCKER_USERNAME }}/airbnb:1.0 + + # 디렉토리 생성 + - name: Make Directory + run: mkdir -p deploy + + # appspec.yml 파일 복사 + - name: Copy appspec.yml + run: cp appspec.yml ./deploy + + # script files 복사 + - name: Copy script + run: cp ./scripts/*.sh ./deploy + + # 파일 압축 + - name: Make zip file + run: zip -r ./airbnb.zip ./deploy + + # S3와 CodeDeploy를 통한 배포 - name: Deploy - uses: appleboy/ssh-action@master + uses: aws-actions/configure-aws-credentials@v1 with: - host: 144.24.86.236 - username: ubuntu - key: | - -----BEGIN RSA PRIVATE KEY----- - MIIEogIBAAKCAQEAzR7erj4IQoFcQ5S1yBOARlMe1BIAPymPV7og8/ltqbGn36uf - QrQLrJs0HAQ1aFrXeyd+aIKm68XSlWAcHVIwIEC/Sz14vIMhQYZLS++frVJnkWjN - W75bc3/Wkh7u71l45OM+8LXztkKwIi7z+CfP/0lK7PKMPPdZbZCe2hZJjwnSNUVg - C6/SO/Kbp7K6e6d3f2XYRREazER3pxnQLJiykHIQPzA6siRFrnqbprGnp47mtQSp - CrLY3aRxsjXg0TkK9lEUYM5iUMNBFXMYH+RmR11I20o+AJehbt47jAFgIl93fcAd - yrEo7jTNHbr0r6TeOLTdJNoTcd1Wl8us3OBrMwIDAQABAoIBAApp/iWb4ft5FIrH - KruFEfGAGC6YLgmxkyTXFFk5WVymN78JYu/rXYmz5N9kmfmPCHr+NFLGCgd/vFii - eorv40r1PpZpVo9A8Rf3ONdwIQvsWnRtfQ+isy3tWE2EOIlPt5hCBANgmYZauGqO - i7XA/7RsWjDRglaOEFeZt2xHJBDuTWsmNWCCdc5o8rX73c1cUz6BcN2I7/av6LSU - gM40F8/z+eu8Z7vzAY/HtKHbrj/lqABsgObKnNa4VI0dlQYKhlHwXAM+hZ7e57mr - i9f42nmZUktkldqb3UNepKy2BJxL9b9KfaXNPBF81d525uAytTMqNCaAte8o386v - ziv9xyECgYEA8UoI3vzdOPjAp7GAKpqnIEVQCvj+FlqhnejMCp84lZ3tWrWlKRCO - 2kGNHdykLxIvIKtGMRxlP9Iv5X4j9w5Ag2mLgwuOgL71mXHNKB+fO8d8xijX5XWz - zvkrGfQFYbZG4IQiS1EFXmFDHLHlNx32fV2SBKaj9Rqx9e/yxObKuJsCgYEA2aBT - svYcvjqFHTtLjs9Y0yLzbuU/k+LZHQBTnXBGXPTk5FaImKgXlf0qs+KL2NcjW+h3 - vKpgz0LC0xSrNxaPjr8WqiM/6swvUiRYa8FVCECzVIfBSpoK987nf02bxTUD94N4 - 0ck1eEX9EwH1pNAVrRYQyst+dVeewx6RSYjwRUkCgYBJ1VO9mDbBdizNo50KbGRz - lYIWuXZWtMj7nv7AyVPxRHAa77ZoSG7ODnz2BEwhMzlEj+Rcpsm8q76IRP8QXEYV - cDiGkwS6FLTmjVIv0u1Qx4xAvsLBrjClRs2PlYIIElTB4uzziwlPhgeZQj5XjU2d - swI5dhx5pf+KuC/bXJvvvQKBgGrbCR3fMD/LVh8DRVdKwg9xotu5np0LjpVp2qR8 - Q2BTMqxXPNzP9DzGQRhwTUBcalUvcNNnSP+bhTPFe0giQLzTYNqLUlAEj9uiUvQd - ypWxxxvjSGpL9sS0iAB+59RN8rOujz1asXFr1BZoKOgS8AG7yuT4RBBzxFWEBcx4 - e5lZAoGAE7b8lrbOmqGnGt9a+ui9X5PLPJycOEVib9JPIADFoPaKobWj4Kor1QSn - jOW3jBPDLMNARWSmLKIod+My9JtQb3Y1XfIB0IQcdm8zPVKZ6Dvc71GUFSucqxVt - tjB2069+PZnVYyzCI0zkmmzRRxqYKLhVRVcrfdHgAxMzd5Is8ro= - -----END RSA PRIVATE KEY----- - envs: GITHUB_SHA - script: | - sudo docker ps -a -q --filter "name=airbnb-test-deploy" | grep -q . && docker stop airbnb-test-deploy && docker rm airbnb-test-deploy | true - sudo docker rmi zinophilsquad/airbnb-test-deploy:1.0 - sudo docker pull zinophilsquad/airbnb-test-deploy:1.0 - sudo docker run -d -p 80:8080 --name airbnb-test-deploy zinophilsquad/airbnb-test-deploy:1.0 - sudo docker rmi -f $(docker images -f "dangling=true" -q) || true - # 빌드 성공시 슬랙채널로 알람 + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + + - name: Upload to S3 + run: aws s3 cp --region ap-northeast-2 --acl private ./airbnb.zip s3://airbnb-phil-deploy-dockerimage/ + + - name: Start CodeDeploy + run: aws deploy create-deployment --application-name Airbnb-Codedeploy-Application --deployment-group-name Airbnb-Codedeploy-Target-Group --s3-location bucket=airbnb-phil-deploy-dockerimage,key=airbnb.zip,bundleType=zip + + # 배경 성공시 슬랙 알람 - name: action-slack uses: 8398a7/action-slack@v3 - with: status: ${{ job.status }} author_name: Github Action Test # default: 8398a7@action-slack