From 8e5d8fdedcd7c2b2a46c70b157cd770ace01f9e8 Mon Sep 17 00:00:00 2001 From: hyn Date: Thu, 23 Jan 2025 23:10:26 +0900 Subject: [PATCH 01/34] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20argument=20resolver=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/application/service/AuthService.java | 6 +++ .../UnAuthenticatedMemberException.java | 12 +++++ .../AuthenticationArgumentResolver.java | 50 +++++++++++++++++++ .../udong/common/annotation/LoginMember.java | 11 ++++ .../hyun/udong/common/config/WebConfig.java | 21 ++++++++ .../udong/common/exception/ErrorCode.java | 3 +- 6 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hyun/udong/auth/exception/UnAuthenticatedMemberException.java create mode 100644 src/main/java/com/hyun/udong/auth/presentation/resolver/AuthenticationArgumentResolver.java create mode 100644 src/main/java/com/hyun/udong/common/annotation/LoginMember.java create mode 100644 src/main/java/com/hyun/udong/common/config/WebConfig.java diff --git a/src/main/java/com/hyun/udong/auth/application/service/AuthService.java b/src/main/java/com/hyun/udong/auth/application/service/AuthService.java index cd30d1e..e94d892 100644 --- a/src/main/java/com/hyun/udong/auth/application/service/AuthService.java +++ b/src/main/java/com/hyun/udong/auth/application/service/AuthService.java @@ -84,4 +84,10 @@ private Member updateRefreshToken(Long memberId, String refreshToken) { member.updateRefreshToken(refreshToken); return member; } + + public Member getMemberFromToken(String token) { + Long memberId = Long.parseLong(jwtTokenProvider.getSubjectFromToken(token)); + return memberRepository.findById(memberId) + .orElseThrow(() -> InvalidTokenException.EXCEPTION); + } } diff --git a/src/main/java/com/hyun/udong/auth/exception/UnAuthenticatedMemberException.java b/src/main/java/com/hyun/udong/auth/exception/UnAuthenticatedMemberException.java new file mode 100644 index 0000000..9361387 --- /dev/null +++ b/src/main/java/com/hyun/udong/auth/exception/UnAuthenticatedMemberException.java @@ -0,0 +1,12 @@ +package com.hyun.udong.auth.exception; + +import com.hyun.udong.common.exception.ErrorCode; +import com.hyun.udong.common.exception.UdongException; + +public class UnAuthenticatedMemberException extends UdongException { + public static final UdongException EXCEPTION = new UnAuthenticatedMemberException(); + + private UnAuthenticatedMemberException() { + super(ErrorCode.UNAUTHENTICATED_MEMBER); + } +} diff --git a/src/main/java/com/hyun/udong/auth/presentation/resolver/AuthenticationArgumentResolver.java b/src/main/java/com/hyun/udong/auth/presentation/resolver/AuthenticationArgumentResolver.java new file mode 100644 index 0000000..7959a29 --- /dev/null +++ b/src/main/java/com/hyun/udong/auth/presentation/resolver/AuthenticationArgumentResolver.java @@ -0,0 +1,50 @@ +package com.hyun.udong.auth.presentation.resolver; + +import com.hyun.udong.auth.application.service.AuthService; +import com.hyun.udong.auth.exception.UnAuthenticatedMemberException; +import com.hyun.udong.common.annotation.LoginMember; +import com.hyun.udong.member.domain.Member; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +public class AuthenticationArgumentResolver implements HandlerMethodArgumentResolver { + + private static final String PREFIX = "Bearer "; + + private final AuthService authService; + + public AuthenticationArgumentResolver(AuthService authService) { + this.authService = authService; + } + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(LoginMember.class) + && parameter.getParameterType().equals(Member.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + String token = request.getHeader("Authorization"); + + isValidToken(token); + + return authService.getMemberFromToken(token.substring(PREFIX.length())); + } + + private void isValidToken(String token) { + if (token == null || !token.startsWith(PREFIX)) { + throw UnAuthenticatedMemberException.EXCEPTION; + } + } +} diff --git a/src/main/java/com/hyun/udong/common/annotation/LoginMember.java b/src/main/java/com/hyun/udong/common/annotation/LoginMember.java new file mode 100644 index 0000000..3fb4d72 --- /dev/null +++ b/src/main/java/com/hyun/udong/common/annotation/LoginMember.java @@ -0,0 +1,11 @@ +package com.hyun.udong.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface LoginMember { +} diff --git a/src/main/java/com/hyun/udong/common/config/WebConfig.java b/src/main/java/com/hyun/udong/common/config/WebConfig.java new file mode 100644 index 0000000..d86f7a3 --- /dev/null +++ b/src/main/java/com/hyun/udong/common/config/WebConfig.java @@ -0,0 +1,21 @@ +package com.hyun.udong.common.config; + +import com.hyun.udong.auth.presentation.resolver.AuthenticationArgumentResolver; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +@RequiredArgsConstructor +public class WebConfig implements WebMvcConfigurer { + + private final AuthenticationArgumentResolver authenticationArgumentResolver; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(authenticationArgumentResolver); + } +} diff --git a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java index 479169c..d77af2e 100644 --- a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java +++ b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java @@ -10,7 +10,8 @@ public enum ErrorCode { MEMBER_NOT_FOUND(BAD_REQUEST, "해당하는 회원이 없습니다."), INVALID_TOKEN(UNAUTHORIZED, "유효하지 않은 토큰입니다."), - TOKEN_EXPIRED(UNAUTHORIZED, "만료된 토큰입니다."); + TOKEN_EXPIRED(UNAUTHORIZED, "만료된 토큰입니다."), + UNAUTHENTICATED_MEMBER(UNAUTHORIZED, "인증되지 않은 회원입니다."); private final HttpStatus status; private final String message; From 5e9d7fcf7c8911fb18a63d8363a55751c55fe261 Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 04:17:17 +0900 Subject: [PATCH 02/34] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=97=AC=ED=96=89=20=EC=9D=BC=EC=A0=95=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../udong/common/entity/BaseTimeEntity.java | 23 +++++++++++ .../com/hyun/udong/member/domain/Member.java | 16 ++++---- .../com/hyun/udong/travel/domain/City.java | 25 ++++++++++++ .../com/hyun/udong/travel/domain/Country.java | 22 +++++++++++ .../travel/domain/MemberTravelSchedule.java | 38 +++++++++++++++++++ .../travel/domain/TravelScheduleCity.java | 32 ++++++++++++++++ 6 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/hyun/udong/common/entity/BaseTimeEntity.java create mode 100644 src/main/java/com/hyun/udong/travel/domain/City.java create mode 100644 src/main/java/com/hyun/udong/travel/domain/Country.java create mode 100644 src/main/java/com/hyun/udong/travel/domain/MemberTravelSchedule.java create mode 100644 src/main/java/com/hyun/udong/travel/domain/TravelScheduleCity.java diff --git a/src/main/java/com/hyun/udong/common/entity/BaseTimeEntity.java b/src/main/java/com/hyun/udong/common/entity/BaseTimeEntity.java new file mode 100644 index 0000000..2e639d3 --- /dev/null +++ b/src/main/java/com/hyun/udong/common/entity/BaseTimeEntity.java @@ -0,0 +1,23 @@ +package com.hyun.udong.common.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseTimeEntity { + + @Column(updatable = false) + @CreatedDate + private LocalDateTime createdAt; + + @Column + @LastModifiedDate + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/hyun/udong/member/domain/Member.java b/src/main/java/com/hyun/udong/member/domain/Member.java index 98fff63..5b02cb2 100644 --- a/src/main/java/com/hyun/udong/member/domain/Member.java +++ b/src/main/java/com/hyun/udong/member/domain/Member.java @@ -1,5 +1,7 @@ package com.hyun.udong.member.domain; +import com.hyun.udong.common.entity.BaseTimeEntity; +import com.hyun.udong.travel.domain.MemberTravelSchedule; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @@ -9,10 +11,11 @@ @Entity @Getter @NoArgsConstructor(access = PROTECTED) -public class Member { +public class Member extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "member_id") private Long id; private Long socialId; @@ -30,15 +33,10 @@ public class Member { private String refreshToken; - public Member(Long socialId, SocialType socialType, String nickname, String profileImageUrl) { - this.socialId = socialId; - this.socialType = socialType; - this.nickname = nickname; - this.profileImageUrl = profileImageUrl; - } + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private MemberTravelSchedule travelSchedule; - public Member(Long id, Long socialId, SocialType socialType, String nickname, String profileImageUrl) { - this.id = id; + public Member(Long socialId, SocialType socialType, String nickname, String profileImageUrl) { this.socialId = socialId; this.socialType = socialType; this.nickname = nickname; diff --git a/src/main/java/com/hyun/udong/travel/domain/City.java b/src/main/java/com/hyun/udong/travel/domain/City.java new file mode 100644 index 0000000..6ee39d4 --- /dev/null +++ b/src/main/java/com/hyun/udong/travel/domain/City.java @@ -0,0 +1,25 @@ +package com.hyun.udong.travel.domain; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static lombok.AccessLevel.PROTECTED; + +@Entity +@Getter +@NoArgsConstructor(access = PROTECTED) +public class City { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "city_id") + private Long id; + + private String name; + + @ManyToOne + @JoinColumn(name = "country_id", nullable = false) + private Country country; + +} diff --git a/src/main/java/com/hyun/udong/travel/domain/Country.java b/src/main/java/com/hyun/udong/travel/domain/Country.java new file mode 100644 index 0000000..ab60364 --- /dev/null +++ b/src/main/java/com/hyun/udong/travel/domain/Country.java @@ -0,0 +1,22 @@ +package com.hyun.udong.travel.domain; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static lombok.AccessLevel.PROTECTED; + +@Entity +@Getter +@NoArgsConstructor(access = PROTECTED) +public class Country { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "country_id") + private Long id; + + private String name; + + private String code; +} diff --git a/src/main/java/com/hyun/udong/travel/domain/MemberTravelSchedule.java b/src/main/java/com/hyun/udong/travel/domain/MemberTravelSchedule.java new file mode 100644 index 0000000..bf1478c --- /dev/null +++ b/src/main/java/com/hyun/udong/travel/domain/MemberTravelSchedule.java @@ -0,0 +1,38 @@ +package com.hyun.udong.travel.domain; + +import com.hyun.udong.common.entity.BaseTimeEntity; +import jakarta.persistence.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +import static lombok.AccessLevel.PROTECTED; + +@Entity +@Getter +@NoArgsConstructor(access = PROTECTED) +public class MemberTravelSchedule extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "travel_schedule_id") + private Long id; + + private LocalDate startDate; + + private LocalDate endDate; + + @OneToMany(mappedBy = "travelSchedule", cascade = CascadeType.ALL, orphanRemoval = true) + private List travelScheduleCities = new ArrayList<>(); + + @Builder + public MemberTravelSchedule(LocalDate startDate, LocalDate endDate, List travelScheduleCities) { + this.startDate = startDate; + this.endDate = endDate; + this.travelScheduleCities = travelScheduleCities; + } +} diff --git a/src/main/java/com/hyun/udong/travel/domain/TravelScheduleCity.java b/src/main/java/com/hyun/udong/travel/domain/TravelScheduleCity.java new file mode 100644 index 0000000..cb9231c --- /dev/null +++ b/src/main/java/com/hyun/udong/travel/domain/TravelScheduleCity.java @@ -0,0 +1,32 @@ +package com.hyun.udong.travel.domain; + +import jakarta.persistence.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static lombok.AccessLevel.PROTECTED; + +@Entity +@Getter +@NoArgsConstructor(access = PROTECTED) +public class TravelScheduleCity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "travel_schedule_id", nullable = false) + private MemberTravelSchedule travelSchedule; + + @ManyToOne + @JoinColumn(name = "city_id", nullable = false) + private City city; + + @Builder + public TravelScheduleCity(MemberTravelSchedule travelSchedule, City city) { + this.travelSchedule = travelSchedule; + this.city = city; + } +} From 6a910f6ff840ea32986ddab26e7baed07bd44a5a Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 04:37:11 +0900 Subject: [PATCH 03/34] =?UTF-8?q?chore:=20starter-validation=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 05c72da..b55971b 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-starter-validation' compileOnly 'org.projectlombok:lombok' runtimeOnly 'org.postgresql:postgresql' annotationProcessor 'org.projectlombok:lombok' From 844bbb08355c401e1b8de0ae7b94b48ced6879b1 Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 05:03:22 +0900 Subject: [PATCH 04/34] =?UTF-8?q?style:=20travel->travelschedule=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/hyun/udong/member/domain/Member.java | 2 +- .../com/hyun/udong/{travel => travelschedule}/domain/City.java | 2 +- .../hyun/udong/{travel => travelschedule}/domain/Country.java | 2 +- .../{travel => travelschedule}/domain/MemberTravelSchedule.java | 2 +- .../{travel => travelschedule}/domain/TravelScheduleCity.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/com/hyun/udong/{travel => travelschedule}/domain/City.java (90%) rename src/main/java/com/hyun/udong/{travel => travelschedule}/domain/Country.java (89%) rename src/main/java/com/hyun/udong/{travel => travelschedule}/domain/MemberTravelSchedule.java (95%) rename src/main/java/com/hyun/udong/{travel => travelschedule}/domain/TravelScheduleCity.java (94%) diff --git a/src/main/java/com/hyun/udong/member/domain/Member.java b/src/main/java/com/hyun/udong/member/domain/Member.java index 5b02cb2..2f48321 100644 --- a/src/main/java/com/hyun/udong/member/domain/Member.java +++ b/src/main/java/com/hyun/udong/member/domain/Member.java @@ -1,7 +1,7 @@ package com.hyun.udong.member.domain; import com.hyun.udong.common.entity.BaseTimeEntity; -import com.hyun.udong.travel.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/src/main/java/com/hyun/udong/travel/domain/City.java b/src/main/java/com/hyun/udong/travelschedule/domain/City.java similarity index 90% rename from src/main/java/com/hyun/udong/travel/domain/City.java rename to src/main/java/com/hyun/udong/travelschedule/domain/City.java index 6ee39d4..9fefe62 100644 --- a/src/main/java/com/hyun/udong/travel/domain/City.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/City.java @@ -1,4 +1,4 @@ -package com.hyun.udong.travel.domain; +package com.hyun.udong.travelschedule.domain; import jakarta.persistence.*; import lombok.Getter; diff --git a/src/main/java/com/hyun/udong/travel/domain/Country.java b/src/main/java/com/hyun/udong/travelschedule/domain/Country.java similarity index 89% rename from src/main/java/com/hyun/udong/travel/domain/Country.java rename to src/main/java/com/hyun/udong/travelschedule/domain/Country.java index ab60364..7891fc1 100644 --- a/src/main/java/com/hyun/udong/travel/domain/Country.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/Country.java @@ -1,4 +1,4 @@ -package com.hyun.udong.travel.domain; +package com.hyun.udong.travelschedule.domain; import jakarta.persistence.*; import lombok.Getter; diff --git a/src/main/java/com/hyun/udong/travel/domain/MemberTravelSchedule.java b/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java similarity index 95% rename from src/main/java/com/hyun/udong/travel/domain/MemberTravelSchedule.java rename to src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java index bf1478c..ef380f4 100644 --- a/src/main/java/com/hyun/udong/travel/domain/MemberTravelSchedule.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java @@ -1,4 +1,4 @@ -package com.hyun.udong.travel.domain; +package com.hyun.udong.travelschedule.domain; import com.hyun.udong.common.entity.BaseTimeEntity; import jakarta.persistence.*; diff --git a/src/main/java/com/hyun/udong/travel/domain/TravelScheduleCity.java b/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java similarity index 94% rename from src/main/java/com/hyun/udong/travel/domain/TravelScheduleCity.java rename to src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java index cb9231c..6e1fd15 100644 --- a/src/main/java/com/hyun/udong/travel/domain/TravelScheduleCity.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java @@ -1,4 +1,4 @@ -package com.hyun.udong.travel.domain; +package com.hyun.udong.travelschedule.domain; import jakarta.persistence.*; import lombok.Builder; From 4cfa1d9a1b48043e1928606a6fe44d61d0caea4b Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 07:56:13 +0900 Subject: [PATCH 05/34] =?UTF-8?q?test:=20city,=20country=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?db=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 4 ++ src/main/resources/data.sql | 81 ++++++++++++++++++++++++++++ src/test/resources/application.yml | 13 +++-- src/test/resources/data.sql | 84 ++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 src/main/resources/data.sql create mode 100644 src/test/resources/data.sql diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 265b529..7bcf19e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -10,12 +10,16 @@ spring: hibernate: ddl-auto: create show-sql: true + defer-datasource-initialization: true # Hibernate 초기화 후 데이터 삽입 실행 application: name: udong mvc: view: prefix: /templates/ suffix: .html + sql: + init: + mode: always social: kakao: diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..0f87194 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,81 @@ +-- 나라 데이터 삽입 +INSERT INTO country (country_id, name, code) +VALUES (1, 'South Korea', 'KR'), + (2, 'United States', 'US'), + (3, 'United Kingdom', 'GB'), + (4, 'France', 'FR'), + (5, 'Germany', 'DE'), + (6, 'Japan', 'JP'), + (7, 'China', 'CN'), + (8, 'India', 'IN'), + (9, 'Australia', 'AU'), + (10, 'Canada', 'CA'), + (11, 'Italy', 'IT'), + (12, 'Spain', 'ES'), + (13, 'Brazil', 'BR'), + (14, 'Mexico', 'MX'), + (15, 'Russia', 'RU'), + (16, 'South Africa', 'ZA'), + (17, 'Argentina', 'AR'), + (18, 'New Zealand', 'NZ'), + (19, 'Indonesia', 'ID'), + (20, 'Thailand', 'TH'), + (21, 'Turkey', 'TR'), + (22, 'Egypt', 'EG'), + (23, 'Saudi Arabia', 'SA'), + (24, 'Vietnam', 'VN'), + (25, 'Malaysia', 'MY'); + +-- 도시 데이터 삽입 +INSERT INTO city (city_id, name, country_id) +VALUES (1, 'Seoul', 1), + (2, 'Busan', 1), + (3, 'New York', 2), + (4, 'Los Angeles', 2), + (5, 'Chicago', 2), + (6, 'London', 3), + (7, 'Manchester', 3), + (8, 'Paris', 4), + (9, 'Lyon', 4), + (10, 'Berlin', 5), + (11, 'Munich', 5), + (12, 'Tokyo', 6), + (13, 'Osaka', 6), + (14, 'Beijing', 7), + (15, 'Shanghai', 7), + (16, 'Mumbai', 8), + (17, 'Delhi', 8), + (18, 'Sydney', 9), + (19, 'Melbourne', 9), + (20, 'Toronto', 10), + (21, 'Vancouver', 10), + (22, 'Rome', 11), + (23, 'Milan', 11), + (24, 'Madrid', 12), + (25, 'Barcelona', 12), + (26, 'Rio de Janeiro', 13), + (27, 'São Paulo', 13), + (28, 'Mexico City', 14), + (29, 'Guadalajara', 14), + (30, 'Moscow', 15), + (31, 'Saint Petersburg', 15), + (32, 'Cape Town', 16), + (33, 'Johannesburg', 16), + (34, 'Buenos Aires', 17), + (35, 'Córdoba', 17), + (36, 'Auckland', 18), + (37, 'Wellington', 18), + (38, 'Jakarta', 19), + (39, 'Bali', 19), + (40, 'Bangkok', 20), + (41, 'Chiang Mai', 20), + (42, 'Istanbul', 21), + (43, 'Ankara', 21), + (44, 'Cairo', 22), + (45, 'Alexandria', 22), + (46, 'Riyadh', 23), + (47, 'Jeddah', 23), + (48, 'Hanoi', 24), + (49, 'Ho Chi Minh City', 24), + (50, 'Kuala Lumpur', 25), + (51, 'Penang', 25); diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index be22647..2e3a50c 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -2,13 +2,18 @@ spring: config: import: optional:file:.env[.properties] datasource: - url: jdbc:h2:mem:testdb - username: sa - password: + url: ${DATASOURCE_URL}_test + username: ${DATASOURCE_USERNAME} + password: ${DATASOURCE_PASSWORD} jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect hibernate: - ddl-auto: create-drop + ddl-auto: create show-sql: true + defer-datasource-initialization: true + sql: + init: + mode: always social: kakao: diff --git a/src/test/resources/data.sql b/src/test/resources/data.sql new file mode 100644 index 0000000..ff3d45f --- /dev/null +++ b/src/test/resources/data.sql @@ -0,0 +1,84 @@ +TRUNCATE TABLE city CASCADE; +TRUNCATE TABLE country CASCADE; + +-- 나라 데이터 삽입 +INSERT INTO country (country_id, name, code) +VALUES (1, 'South Korea', 'KR'), + (2, 'United States', 'US'), + (3, 'United Kingdom', 'GB'), + (4, 'France', 'FR'), + (5, 'Germany', 'DE'), + (6, 'Japan', 'JP'), + (7, 'China', 'CN'), + (8, 'India', 'IN'), + (9, 'Australia', 'AU'), + (10, 'Canada', 'CA'), + (11, 'Italy', 'IT'), + (12, 'Spain', 'ES'), + (13, 'Brazil', 'BR'), + (14, 'Mexico', 'MX'), + (15, 'Russia', 'RU'), + (16, 'South Africa', 'ZA'), + (17, 'Argentina', 'AR'), + (18, 'New Zealand', 'NZ'), + (19, 'Indonesia', 'ID'), + (20, 'Thailand', 'TH'), + (21, 'Turkey', 'TR'), + (22, 'Egypt', 'EG'), + (23, 'Saudi Arabia', 'SA'), + (24, 'Vietnam', 'VN'), + (25, 'Malaysia', 'MY'); + +-- 도시 데이터 삽입 +INSERT INTO city (city_id, name, country_id) +VALUES (1, 'Seoul', 1), + (2, 'Busan', 1), + (3, 'New York', 2), + (4, 'Los Angeles', 2), + (5, 'Chicago', 2), + (6, 'London', 3), + (7, 'Manchester', 3), + (8, 'Paris', 4), + (9, 'Lyon', 4), + (10, 'Berlin', 5), + (11, 'Munich', 5), + (12, 'Tokyo', 6), + (13, 'Osaka', 6), + (14, 'Beijing', 7), + (15, 'Shanghai', 7), + (16, 'Mumbai', 8), + (17, 'Delhi', 8), + (18, 'Sydney', 9), + (19, 'Melbourne', 9), + (20, 'Toronto', 10), + (21, 'Vancouver', 10), + (22, 'Rome', 11), + (23, 'Milan', 11), + (24, 'Madrid', 12), + (25, 'Barcelona', 12), + (26, 'Rio de Janeiro', 13), + (27, 'São Paulo', 13), + (28, 'Mexico City', 14), + (29, 'Guadalajara', 14), + (30, 'Moscow', 15), + (31, 'Saint Petersburg', 15), + (32, 'Cape Town', 16), + (33, 'Johannesburg', 16), + (34, 'Buenos Aires', 17), + (35, 'Córdoba', 17), + (36, 'Auckland', 18), + (37, 'Wellington', 18), + (38, 'Jakarta', 19), + (39, 'Bali', 19), + (40, 'Bangkok', 20), + (41, 'Chiang Mai', 20), + (42, 'Istanbul', 21), + (43, 'Ankara', 21), + (44, 'Cairo', 22), + (45, 'Alexandria', 22), + (46, 'Riyadh', 23), + (47, 'Jeddah', 23), + (48, 'Hanoi', 24), + (49, 'Ho Chi Minh City', 24), + (50, 'Kuala Lumpur', 25), + (51, 'Penang', 25); From 753f179523ea6dcf3a26dc8acc6d5cf45477180a Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 07:57:01 +0900 Subject: [PATCH 06/34] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=98=20=EC=97=AC?= =?UTF-8?q?=ED=96=89=20=EC=9D=BC=EC=A0=95=20=EC=B6=94=EA=B0=80=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../udong/common/exception/ErrorCode.java | 3 +- .../com/hyun/udong/member/domain/Member.java | 4 ++ .../service/TravelScheduleService.java | 52 +++++++++++++++++++ .../domain/MemberTravelSchedule.java | 5 +- .../exception/CityNotFoundException.java | 12 +++++ .../repository/CityRepository.java | 7 +++ .../TravelScheduleCityRepository.java | 7 +++ .../repository/TravelScheduleRepository.java | 7 +++ .../controller/TravelScheduleController.java | 31 +++++++++++ .../dto/TravelScheduleRequest.java | 23 ++++++++ .../dto/TravelScheduleResponse.java | 38 ++++++++++++++ 11 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java create mode 100644 src/main/java/com/hyun/udong/travelschedule/exception/CityNotFoundException.java create mode 100644 src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/CityRepository.java create mode 100644 src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleCityRepository.java create mode 100644 src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleRepository.java create mode 100644 src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java create mode 100644 src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleRequest.java create mode 100644 src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java diff --git a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java index d77af2e..4f46701 100644 --- a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java +++ b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java @@ -11,7 +11,8 @@ public enum ErrorCode { MEMBER_NOT_FOUND(BAD_REQUEST, "해당하는 회원이 없습니다."), INVALID_TOKEN(UNAUTHORIZED, "유효하지 않은 토큰입니다."), TOKEN_EXPIRED(UNAUTHORIZED, "만료된 토큰입니다."), - UNAUTHENTICATED_MEMBER(UNAUTHORIZED, "인증되지 않은 회원입니다."); + UNAUTHENTICATED_MEMBER(UNAUTHORIZED, "인증되지 않은 회원입니다."), + CITY_NOT_FOUND(BAD_REQUEST, "해당하는 도시가 없습니다."); private final HttpStatus status; private final String message; diff --git a/src/main/java/com/hyun/udong/member/domain/Member.java b/src/main/java/com/hyun/udong/member/domain/Member.java index 2f48321..0c6bbf7 100644 --- a/src/main/java/com/hyun/udong/member/domain/Member.java +++ b/src/main/java/com/hyun/udong/member/domain/Member.java @@ -46,4 +46,8 @@ public Member(Long socialId, SocialType socialType, String nickname, String prof public void updateRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } + + public void registerTravelSchedule(MemberTravelSchedule travelSchedule) { + this.travelSchedule = travelSchedule; + } } diff --git a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java new file mode 100644 index 0000000..b910e1f --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java @@ -0,0 +1,52 @@ +package com.hyun.udong.travelschedule.application.service; + +import com.hyun.udong.member.domain.Member; +import com.hyun.udong.member.exception.MemberNotFoundException; +import com.hyun.udong.member.infrastructure.repository.MemberRepository; +import com.hyun.udong.travelschedule.domain.City; +import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.domain.TravelScheduleCity; +import com.hyun.udong.travelschedule.exception.CityNotFoundException; +import com.hyun.udong.travelschedule.infrastructure.repository.CityRepository; +import com.hyun.udong.travelschedule.infrastructure.repository.TravelScheduleRepository; +import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class TravelScheduleService { + + private final MemberRepository memberRepository; + private final TravelScheduleRepository travelScheduleRepository; + private final CityRepository cityRepository; + + @Transactional + public MemberTravelSchedule registerTravelSchedule(Long memberId, TravelScheduleRequest request) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> MemberNotFoundException.EXCEPTION); + + MemberTravelSchedule travelSchedule = MemberTravelSchedule.builder() + .startDate(request.getStartDate()) + .endDate(request.getEndDate()) + .build(); + + List travelScheduleCities = request.getCityIds().stream() + .map(cityId -> { + City city = cityRepository.findById(cityId) + .orElseThrow(() -> CityNotFoundException.EXCEPTION); + return new TravelScheduleCity(travelSchedule, city); + }) + .toList(); + travelSchedule.addTravelScheduleCity(travelScheduleCities); + travelScheduleRepository.save(travelSchedule); + + member.registerTravelSchedule(travelSchedule); + + return travelSchedule; + } +} diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java b/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java index ef380f4..f4ae0b7 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java @@ -30,9 +30,12 @@ public class MemberTravelSchedule extends BaseTimeEntity { private List travelScheduleCities = new ArrayList<>(); @Builder - public MemberTravelSchedule(LocalDate startDate, LocalDate endDate, List travelScheduleCities) { + public MemberTravelSchedule(LocalDate startDate, LocalDate endDate) { this.startDate = startDate; this.endDate = endDate; + } + + public void addTravelScheduleCity(List travelScheduleCities) { this.travelScheduleCities = travelScheduleCities; } } diff --git a/src/main/java/com/hyun/udong/travelschedule/exception/CityNotFoundException.java b/src/main/java/com/hyun/udong/travelschedule/exception/CityNotFoundException.java new file mode 100644 index 0000000..43d12c5 --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/exception/CityNotFoundException.java @@ -0,0 +1,12 @@ +package com.hyun.udong.travelschedule.exception; + +import com.hyun.udong.common.exception.ErrorCode; +import com.hyun.udong.common.exception.UdongException; + +public class CityNotFoundException extends UdongException { + public static final CityNotFoundException EXCEPTION = new CityNotFoundException(); + + private CityNotFoundException() { + super(ErrorCode.CITY_NOT_FOUND); + } +} diff --git a/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/CityRepository.java b/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/CityRepository.java new file mode 100644 index 0000000..24912a5 --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/CityRepository.java @@ -0,0 +1,7 @@ +package com.hyun.udong.travelschedule.infrastructure.repository; + +import com.hyun.udong.travelschedule.domain.City; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CityRepository extends JpaRepository { +} diff --git a/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleCityRepository.java b/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleCityRepository.java new file mode 100644 index 0000000..539dd16 --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleCityRepository.java @@ -0,0 +1,7 @@ +package com.hyun.udong.travelschedule.infrastructure.repository; + +import com.hyun.udong.travelschedule.domain.TravelScheduleCity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TravelScheduleCityRepository extends JpaRepository { +} diff --git a/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleRepository.java b/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleRepository.java new file mode 100644 index 0000000..e6b374b --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleRepository.java @@ -0,0 +1,7 @@ +package com.hyun.udong.travelschedule.infrastructure.repository; + +import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TravelScheduleRepository extends JpaRepository { +} diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java b/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java new file mode 100644 index 0000000..7826148 --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java @@ -0,0 +1,31 @@ +package com.hyun.udong.travelschedule.presentation.controller; + +import com.hyun.udong.common.annotation.LoginMember; +import com.hyun.udong.member.domain.Member; +import com.hyun.udong.travelschedule.application.service.TravelScheduleService; +import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; +import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/travel") +public class TravelScheduleController { + + private final TravelScheduleService travelScheduleService; + + @PostMapping("/schedule") + public ResponseEntity registerTravelSchedule( + @LoginMember Member member, + @RequestBody TravelScheduleRequest travelScheduleRequest) { + MemberTravelSchedule travelSchedule = travelScheduleService.registerTravelSchedule(member.getId(), travelScheduleRequest); + TravelScheduleResponse response = TravelScheduleResponse.from(travelSchedule); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleRequest.java b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleRequest.java new file mode 100644 index 0000000..ebb19b0 --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleRequest.java @@ -0,0 +1,23 @@ +package com.hyun.udong.travelschedule.presentation.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.time.LocalDate; +import java.util.List; + +@Getter +@AllArgsConstructor +public class TravelScheduleRequest { + + @NotNull + private LocalDate startDate; + + @NotNull + private LocalDate endDate; + + @NotNull + private List cityIds; + +} diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java new file mode 100644 index 0000000..bf1d425 --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java @@ -0,0 +1,38 @@ +package com.hyun.udong.travelschedule.presentation.dto; + +import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@Builder +public class TravelScheduleResponse { + + private LocalDate startDate; + private LocalDate endDate; + private List travelScheduleCities; + + @Getter + @Builder + public static class TravelScheduleCityResponse { + private Long cityId; + private String cityName; + } + + public static TravelScheduleResponse from(MemberTravelSchedule travelSchedule) { + return TravelScheduleResponse.builder() + .startDate(travelSchedule.getStartDate()) + .endDate(travelSchedule.getEndDate()) + .travelScheduleCities(travelSchedule.getTravelScheduleCities().stream() + .map(city -> TravelScheduleCityResponse.builder() + .cityId(city.getCity().getId()) + .cityName(city.getCity().getName()) + .build()) + .collect(Collectors.toList())) + .build(); + } +} From f1a551aacb799548a3bb5d3812d4a1670c4a4705 Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 07:57:28 +0900 Subject: [PATCH 07/34] =?UTF-8?q?test:=20=EB=82=98=EC=9D=98=20=EC=97=AC?= =?UTF-8?q?=ED=96=89=20=EC=9D=BC=EC=A0=95=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/TravelScheduleServiceTest.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java new file mode 100644 index 0000000..6222895 --- /dev/null +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -0,0 +1,84 @@ +package com.hyun.udong.travelschedule.application.service; + +import com.hyun.udong.member.domain.Member; +import com.hyun.udong.member.domain.SocialType; +import com.hyun.udong.member.exception.MemberNotFoundException; +import com.hyun.udong.member.infrastructure.repository.MemberRepository; +import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.exception.CityNotFoundException; +import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.time.LocalDate; +import java.util.List; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.api.BDDAssertions.thenThrownBy; + +@SpringBootTest +class TravelScheduleServiceTest { + + public static final long FIRST_MEMBER_ID = 1L; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private TravelScheduleService travelScheduleService; + + @BeforeEach + void setUp() { + Member member = new Member(1L, SocialType.KAKAO, "hyun", "profile_image"); + memberRepository.save(member); + } + + private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { + return new TravelScheduleRequest(LocalDate.now(), LocalDate.now().plusDays(5), List.of(cityIds)); + } + + @DisplayName("유효한 회원 ID와 유효한 요청으로 여행 일정을 등록한다.") + @Test + void registerTravelSchedule_validMemberAndRequest() { + // given + TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); + + // when + MemberTravelSchedule travelSchedule = travelScheduleService.registerTravelSchedule(FIRST_MEMBER_ID, request); + + // then + then(travelSchedule).isNotNull(); + then(travelSchedule.getTravelScheduleCities()).hasSize(2); + then(travelSchedule.getTravelScheduleCities().get(0).getCity().getName()).isEqualTo("Seoul"); + then(travelSchedule.getTravelScheduleCities().get(1).getCity().getName()).isEqualTo("Busan"); + } + + @DisplayName("존재하지 않는 회원 ID로 여행 일정을 등록할 때 예외가 발생한다.") + @Test + void registerTravelSchedule_invalidMemberId() { + // given + Long memberId = 999L; + TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); + + // when & then + thenThrownBy(() -> travelScheduleService.registerTravelSchedule(memberId, request)) + .isInstanceOf(MemberNotFoundException.class); + } + + @DisplayName("존재하지 않는 도시 ID로 여행 일정을 등록할 때 예외가 발생한다.") + @Test + void registerTravelSchedule_invalidCityId() { + // given + Long memberId = 1L; + TravelScheduleRequest request = createTravelScheduleRequest(1L, 999L); + Member member = new Member(memberId, SocialType.KAKAO, "hyun", "profile_image"); + memberRepository.save(member); + + // when & then + thenThrownBy(() -> travelScheduleService.registerTravelSchedule(memberId, request)) + .isInstanceOf(CityNotFoundException.class); + } +} From 8891965d9321d1bdbddc1ad073b1a5a075514fb9 Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 07:59:17 +0900 Subject: [PATCH 08/34] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20repository=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/TravelScheduleCityRepository.java | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleCityRepository.java diff --git a/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleCityRepository.java b/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleCityRepository.java deleted file mode 100644 index 539dd16..0000000 --- a/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleCityRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.hyun.udong.travelschedule.infrastructure.repository; - -import com.hyun.udong.travelschedule.domain.TravelScheduleCity; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface TravelScheduleCityRepository extends JpaRepository { -} From d35d3eebd4117b63660d5c4b02d64ffc8fa1ba4a Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 09:05:31 +0900 Subject: [PATCH 09/34] =?UTF-8?q?feat:=20=EC=97=AC=ED=96=89=20=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hyun/udong/common/exception/ErrorCode.java | 3 ++- .../application/service/TravelScheduleService.java | 12 ++++++++++++ .../travelschedule/domain/MemberTravelSchedule.java | 2 +- .../MemberTravelScheduleNotFoundException.java | 12 ++++++++++++ .../controller/TravelScheduleController.java | 12 ++++++++---- 5 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/hyun/udong/travelschedule/exception/MemberTravelScheduleNotFoundException.java diff --git a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java index 4f46701..8905a7d 100644 --- a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java +++ b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java @@ -12,7 +12,8 @@ public enum ErrorCode { INVALID_TOKEN(UNAUTHORIZED, "유효하지 않은 토큰입니다."), TOKEN_EXPIRED(UNAUTHORIZED, "만료된 토큰입니다."), UNAUTHENTICATED_MEMBER(UNAUTHORIZED, "인증되지 않은 회원입니다."), - CITY_NOT_FOUND(BAD_REQUEST, "해당하는 도시가 없습니다."); + CITY_NOT_FOUND(BAD_REQUEST, "해당하는 도시가 없습니다."), + MEMBER_TRAVEL_SCHEDULE_NOT_FOUND(BAD_REQUEST, "해당 회원의 여행 일정이 존재하지 않습니다."); private final HttpStatus status; private final String message; diff --git a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java index b910e1f..32f2d40 100644 --- a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java +++ b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java @@ -7,6 +7,7 @@ import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; import com.hyun.udong.travelschedule.domain.TravelScheduleCity; import com.hyun.udong.travelschedule.exception.CityNotFoundException; +import com.hyun.udong.travelschedule.exception.MemberTravelScheduleNotFoundException; import com.hyun.udong.travelschedule.infrastructure.repository.CityRepository; import com.hyun.udong.travelschedule.infrastructure.repository.TravelScheduleRepository; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; @@ -49,4 +50,15 @@ public MemberTravelSchedule registerTravelSchedule(Long memberId, TravelSchedule return travelSchedule; } + + public MemberTravelSchedule findTravelSchedule(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> MemberNotFoundException.EXCEPTION); + + MemberTravelSchedule travelSchedule = member.getTravelSchedule(); + if (travelSchedule == null) { + throw MemberTravelScheduleNotFoundException.EXCEPTION; + } + return travelSchedule; + } } diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java b/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java index f4ae0b7..080b2cc 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java @@ -26,7 +26,7 @@ public class MemberTravelSchedule extends BaseTimeEntity { private LocalDate endDate; - @OneToMany(mappedBy = "travelSchedule", cascade = CascadeType.ALL, orphanRemoval = true) + @OneToMany(mappedBy = "travelSchedule", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) private List travelScheduleCities = new ArrayList<>(); @Builder diff --git a/src/main/java/com/hyun/udong/travelschedule/exception/MemberTravelScheduleNotFoundException.java b/src/main/java/com/hyun/udong/travelschedule/exception/MemberTravelScheduleNotFoundException.java new file mode 100644 index 0000000..a8fbe51 --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/exception/MemberTravelScheduleNotFoundException.java @@ -0,0 +1,12 @@ +package com.hyun.udong.travelschedule.exception; + +import com.hyun.udong.common.exception.ErrorCode; +import com.hyun.udong.common.exception.UdongException; + +public class MemberTravelScheduleNotFoundException extends UdongException { + public static final MemberTravelScheduleNotFoundException EXCEPTION = new MemberTravelScheduleNotFoundException(); + + private MemberTravelScheduleNotFoundException() { + super(ErrorCode.MEMBER_TRAVEL_SCHEDULE_NOT_FOUND); + } +} diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java b/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java index 7826148..4763f0b 100644 --- a/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java @@ -8,10 +8,7 @@ import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleResponse; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @@ -28,4 +25,11 @@ public ResponseEntity registerTravelSchedule( TravelScheduleResponse response = TravelScheduleResponse.from(travelSchedule); return ResponseEntity.ok(response); } + + @GetMapping("/schedule") + public ResponseEntity getTravelSchedule(@LoginMember Member member) { + MemberTravelSchedule travelSchedule = travelScheduleService.findTravelSchedule(member.getId()); + TravelScheduleResponse response = TravelScheduleResponse.from(travelSchedule); + return ResponseEntity.ok(response); + } } From ee4048ef060cc245502ba1b3609f6d3be56abc2b Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 09:05:52 +0900 Subject: [PATCH 10/34] =?UTF-8?q?test:=20=EC=97=AC=ED=96=89=20=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EA=B8=B0=EC=A1=B4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/TravelScheduleServiceTest.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index 6222895..02ad675 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -6,12 +6,15 @@ import com.hyun.udong.member.infrastructure.repository.MemberRepository; import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; import com.hyun.udong.travelschedule.exception.CityNotFoundException; +import com.hyun.udong.travelschedule.exception.MemberTravelScheduleNotFoundException; +import com.hyun.udong.travelschedule.infrastructure.repository.TravelScheduleRepository; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.List; @@ -23,6 +26,7 @@ class TravelScheduleServiceTest { public static final long FIRST_MEMBER_ID = 1L; + public static final long NOT_EXIST_MEMBER_ID = 2L; @Autowired private MemberRepository memberRepository; @@ -30,6 +34,9 @@ class TravelScheduleServiceTest { @Autowired private TravelScheduleService travelScheduleService; + @Autowired + TravelScheduleRepository travelScheduleRepository; + @BeforeEach void setUp() { Member member = new Member(1L, SocialType.KAKAO, "hyun", "profile_image"); @@ -81,4 +88,48 @@ void registerTravelSchedule_invalidCityId() { thenThrownBy(() -> travelScheduleService.registerTravelSchedule(memberId, request)) .isInstanceOf(CityNotFoundException.class); } + + @DisplayName("유효한 회원 ID로 여행 일정을 조회한다.") + @Test + @Transactional + void findTravelSchedule_ok() { + // given + Long memberId = 1L; + TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); + travelScheduleService.registerTravelSchedule(FIRST_MEMBER_ID, request); + + // when + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> MemberNotFoundException.EXCEPTION); + MemberTravelSchedule travelSchedule = member.getTravelSchedule(); + + // then + then(travelSchedule).isNotNull(); + then(travelSchedule.getStartDate()).isEqualTo(request.getStartDate()); + then(travelSchedule.getEndDate()).isEqualTo(request.getEndDate()); + then(travelSchedule.getTravelScheduleCities()).hasSize(request.getCityIds().size()); + } + + @DisplayName("존재하지 않는 회원 ID로 여행 일정을 조회할 때 예외가 발생한다.") + @Test + void findTravelSchedule_noMember_throw() { + // given + Long memberId = 999L; + + // when & then + thenThrownBy(() -> travelScheduleService.findTravelSchedule(memberId)) + .isInstanceOf(MemberNotFoundException.class); + } + + @DisplayName("회원의 여행 일정이 없을 때 예외가 발생한다.") + @Test + void findTravelSchedule_noTravelSchedule_throw() { + // given + Member member = new Member(2L, SocialType.KAKAO, "hyun", "profile_image"); + memberRepository.save(member); + + // when & then + thenThrownBy(() -> travelScheduleService.findTravelSchedule(NOT_EXIST_MEMBER_ID)) + .isInstanceOf(MemberTravelScheduleNotFoundException.class); + } } From 1706182c26a070cf08e473a9fba121815797a0ba Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 24 Jan 2025 09:06:51 +0900 Subject: [PATCH 11/34] =?UTF-8?q?test:=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/TravelScheduleServiceTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index 02ad675..859299f 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -7,7 +7,6 @@ import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; import com.hyun.udong.travelschedule.exception.CityNotFoundException; import com.hyun.udong.travelschedule.exception.MemberTravelScheduleNotFoundException; -import com.hyun.udong.travelschedule.infrastructure.repository.TravelScheduleRepository; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -34,9 +33,6 @@ class TravelScheduleServiceTest { @Autowired private TravelScheduleService travelScheduleService; - @Autowired - TravelScheduleRepository travelScheduleRepository; - @BeforeEach void setUp() { Member member = new Member(1L, SocialType.KAKAO, "hyun", "profile_image"); From cc689d66b9398ef5297f6c3d9ea0d023736b85ff Mon Sep 17 00:00:00 2001 From: hyn Date: Sat, 25 Jan 2025 06:40:04 +0900 Subject: [PATCH 12/34] =?UTF-8?q?test:=20=EC=97=AC=ED=96=89=20=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hyun/udong/member/domain/Member.java | 7 ++ .../auth/oauth/MockArgumentResolver.java | 23 ++++++ .../TravelScheduleControllerTest.java | 77 +++++++++++++++++++ src/test/resources/member.sql | 2 + 4 files changed, 109 insertions(+) create mode 100644 src/test/java/com/hyun/udong/auth/oauth/MockArgumentResolver.java create mode 100644 src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java create mode 100644 src/test/resources/member.sql diff --git a/src/main/java/com/hyun/udong/member/domain/Member.java b/src/main/java/com/hyun/udong/member/domain/Member.java index 0c6bbf7..a6cf6a2 100644 --- a/src/main/java/com/hyun/udong/member/domain/Member.java +++ b/src/main/java/com/hyun/udong/member/domain/Member.java @@ -43,6 +43,13 @@ public Member(Long socialId, SocialType socialType, String nickname, String prof this.profileImageUrl = profileImageUrl; } + public static Member createForTest(Long id, String nickname) { + Member member = new Member(); + member.id = id; + member.nickname = nickname; + return member; + } + public void updateRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } diff --git a/src/test/java/com/hyun/udong/auth/oauth/MockArgumentResolver.java b/src/test/java/com/hyun/udong/auth/oauth/MockArgumentResolver.java new file mode 100644 index 0000000..44e791f --- /dev/null +++ b/src/test/java/com/hyun/udong/auth/oauth/MockArgumentResolver.java @@ -0,0 +1,23 @@ +package com.hyun.udong.auth.oauth; + +import com.hyun.udong.auth.application.service.AuthService; +import com.hyun.udong.auth.presentation.resolver.AuthenticationArgumentResolver; +import com.hyun.udong.member.domain.Member; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.ModelAndViewContainer; + +public class MockArgumentResolver extends AuthenticationArgumentResolver { + public MockArgumentResolver(AuthService authService) { + super(authService); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + return Member.createForTest(1L, "hyun"); + } +} diff --git a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java new file mode 100644 index 0000000..1b06626 --- /dev/null +++ b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java @@ -0,0 +1,77 @@ +package com.hyun.udong.travelschedule.presentation.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hyun.udong.auth.oauth.MockArgumentResolver; +import com.hyun.udong.travelschedule.application.service.TravelScheduleService; +import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.time.LocalDate; +import java.util.List; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@Sql("/member.sql") +class TravelScheduleControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + private TravelScheduleRequest request; + + private String accessToken; + + @Autowired + private TravelScheduleService travelScheduleService; + + @BeforeEach + void setUp() { + accessToken = "Bearer mockToken"; + request = new TravelScheduleRequest( + LocalDate.of(2025, 1, 25), + LocalDate.of(2025, 2, 10), + List.of(1L, 2L)); + + this.mockMvc = MockMvcBuilders.standaloneSetup(new TravelScheduleController(travelScheduleService)) + .setCustomArgumentResolvers(new MockArgumentResolver(null)) + .setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper)) + .build(); + } + + @Test + void registerTravelSchedule_ReturnsOk() throws Exception { + mockMvc.perform(post("/travel/schedule") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .header("Authorization", accessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.startDate").value(String.valueOf(request.getStartDate()))) + .andExpect(jsonPath("$.endDate").value(String.valueOf(request.getEndDate()))) + .andExpect(jsonPath("$.travelScheduleCities[0].cityId").value(1L)) + .andExpect(jsonPath("$.travelScheduleCities[1].cityId").value(2L)); + } + + @Test + void registerTravelSchedule_WithNullRequest_ReturnsBadRequest() throws Exception { + mockMvc.perform(post("/travel/schedule") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", accessToken)) + .andExpect(status().isBadRequest()); + } +} diff --git a/src/test/resources/member.sql b/src/test/resources/member.sql new file mode 100644 index 0000000..9ce33cc --- /dev/null +++ b/src/test/resources/member.sql @@ -0,0 +1,2 @@ +INSERT INTO member (member_id, social_id, social_type, nickname, age, profile_image_url) +VALUES (1, 1, 'KAKAO', 'hyun', 25, 'profile_image'); From e92d5a5b96c6bd73a959c1b44a232234566c82a6 Mon Sep 17 00:00:00 2001 From: hyn Date: Sat, 25 Jan 2025 06:43:10 +0900 Subject: [PATCH 13/34] =?UTF-8?q?refactor:=20request,=20responsea=20?= =?UTF-8?q?=EC=A7=81=EB=A0=AC=ED=99=94/=EC=97=AD=EC=A7=81=EB=A0=AC?= =?UTF-8?q?=ED=99=94=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hyun/udong/common/annotation/DateFormat.java | 11 +++++++++++ .../presentation/dto/TravelScheduleRequest.java | 5 +++++ .../presentation/dto/TravelScheduleResponse.java | 5 +++++ 3 files changed, 21 insertions(+) create mode 100644 src/main/java/com/hyun/udong/common/annotation/DateFormat.java diff --git a/src/main/java/com/hyun/udong/common/annotation/DateFormat.java b/src/main/java/com/hyun/udong/common/annotation/DateFormat.java new file mode 100644 index 0000000..ef87e5f --- /dev/null +++ b/src/main/java/com/hyun/udong/common/annotation/DateFormat.java @@ -0,0 +1,11 @@ +package com.hyun.udong.common.annotation; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.springframework.format.annotation.DateTimeFormat; + +@JacksonAnnotationsInside +@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") +@DateTimeFormat(pattern = "yyyy-MM-dd") +public @interface DateFormat { +} diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleRequest.java b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleRequest.java index ebb19b0..ffd1482 100644 --- a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleRequest.java +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleRequest.java @@ -1,20 +1,25 @@ package com.hyun.udong.travelschedule.presentation.dto; +import com.hyun.udong.common.annotation.DateFormat; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import java.time.LocalDate; import java.util.List; @Getter +@NoArgsConstructor @AllArgsConstructor public class TravelScheduleRequest { @NotNull + @DateFormat private LocalDate startDate; @NotNull + @DateFormat private LocalDate endDate; @NotNull diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java index bf1d425..7a44838 100644 --- a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java @@ -1,5 +1,6 @@ package com.hyun.udong.travelschedule.presentation.dto; +import com.hyun.udong.common.annotation.DateFormat; import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; import lombok.Builder; import lombok.Getter; @@ -12,8 +13,12 @@ @Builder public class TravelScheduleResponse { + @DateFormat private LocalDate startDate; + + @DateFormat private LocalDate endDate; + private List travelScheduleCities; @Getter From 6d6449e84f0ffd23158007e45447f179a4598534 Mon Sep 17 00:00:00 2001 From: hyn Date: Sun, 26 Jan 2025 05:35:40 +0900 Subject: [PATCH 14/34] =?UTF-8?q?refactor:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=84=A4=EC=A0=95=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/hyun/udong/member/domain/Member.java | 2 +- .../application/service/TravelScheduleService.java | 4 ++-- .../controller/TravelScheduleController.java | 4 ++-- .../service/TravelScheduleServiceTest.java | 14 +++++++------- .../controller/TravelScheduleControllerTest.java | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/hyun/udong/member/domain/Member.java b/src/main/java/com/hyun/udong/member/domain/Member.java index a6cf6a2..6c73b64 100644 --- a/src/main/java/com/hyun/udong/member/domain/Member.java +++ b/src/main/java/com/hyun/udong/member/domain/Member.java @@ -54,7 +54,7 @@ public void updateRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } - public void registerTravelSchedule(MemberTravelSchedule travelSchedule) { + public void updateTravelSchedule(MemberTravelSchedule travelSchedule) { this.travelSchedule = travelSchedule; } } diff --git a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java index 32f2d40..370bcf7 100644 --- a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java +++ b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java @@ -27,7 +27,7 @@ public class TravelScheduleService { private final CityRepository cityRepository; @Transactional - public MemberTravelSchedule registerTravelSchedule(Long memberId, TravelScheduleRequest request) { + public MemberTravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRequest request) { Member member = memberRepository.findById(memberId) .orElseThrow(() -> MemberNotFoundException.EXCEPTION); @@ -46,7 +46,7 @@ public MemberTravelSchedule registerTravelSchedule(Long memberId, TravelSchedule travelSchedule.addTravelScheduleCity(travelScheduleCities); travelScheduleRepository.save(travelSchedule); - member.registerTravelSchedule(travelSchedule); + member.updateTravelSchedule(travelSchedule); return travelSchedule; } diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java b/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java index 4763f0b..be506b3 100644 --- a/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java @@ -18,10 +18,10 @@ public class TravelScheduleController { private final TravelScheduleService travelScheduleService; @PostMapping("/schedule") - public ResponseEntity registerTravelSchedule( + public ResponseEntity updateTravelSchedule( @LoginMember Member member, @RequestBody TravelScheduleRequest travelScheduleRequest) { - MemberTravelSchedule travelSchedule = travelScheduleService.registerTravelSchedule(member.getId(), travelScheduleRequest); + MemberTravelSchedule travelSchedule = travelScheduleService.updateTravelSchedule(member.getId(), travelScheduleRequest); TravelScheduleResponse response = TravelScheduleResponse.from(travelSchedule); return ResponseEntity.ok(response); } diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index 859299f..c7ddbda 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -45,12 +45,12 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { @DisplayName("유효한 회원 ID와 유효한 요청으로 여행 일정을 등록한다.") @Test - void registerTravelSchedule_validMemberAndRequest() { + void updateTravelSchedule_validMemberAndRequest() { // given TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); // when - MemberTravelSchedule travelSchedule = travelScheduleService.registerTravelSchedule(FIRST_MEMBER_ID, request); + MemberTravelSchedule travelSchedule = travelScheduleService.updateTravelSchedule(FIRST_MEMBER_ID, request); // then then(travelSchedule).isNotNull(); @@ -61,19 +61,19 @@ void registerTravelSchedule_validMemberAndRequest() { @DisplayName("존재하지 않는 회원 ID로 여행 일정을 등록할 때 예외가 발생한다.") @Test - void registerTravelSchedule_invalidMemberId() { + void updateTravelSchedule_invalidMemberId() { // given Long memberId = 999L; TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); // when & then - thenThrownBy(() -> travelScheduleService.registerTravelSchedule(memberId, request)) + thenThrownBy(() -> travelScheduleService.updateTravelSchedule(memberId, request)) .isInstanceOf(MemberNotFoundException.class); } @DisplayName("존재하지 않는 도시 ID로 여행 일정을 등록할 때 예외가 발생한다.") @Test - void registerTravelSchedule_invalidCityId() { + void updateTravelSchedule_invalidCityId() { // given Long memberId = 1L; TravelScheduleRequest request = createTravelScheduleRequest(1L, 999L); @@ -81,7 +81,7 @@ void registerTravelSchedule_invalidCityId() { memberRepository.save(member); // when & then - thenThrownBy(() -> travelScheduleService.registerTravelSchedule(memberId, request)) + thenThrownBy(() -> travelScheduleService.updateTravelSchedule(memberId, request)) .isInstanceOf(CityNotFoundException.class); } @@ -92,7 +92,7 @@ void findTravelSchedule_ok() { // given Long memberId = 1L; TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); - travelScheduleService.registerTravelSchedule(FIRST_MEMBER_ID, request); + travelScheduleService.updateTravelSchedule(FIRST_MEMBER_ID, request); // when Member member = memberRepository.findById(memberId) diff --git a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java index 1b06626..84b47f6 100644 --- a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java @@ -55,7 +55,7 @@ void setUp() { } @Test - void registerTravelSchedule_ReturnsOk() throws Exception { + void updateTravelSchedule_ReturnsOk() throws Exception { mockMvc.perform(post("/travel/schedule") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) @@ -68,7 +68,7 @@ void registerTravelSchedule_ReturnsOk() throws Exception { } @Test - void registerTravelSchedule_WithNullRequest_ReturnsBadRequest() throws Exception { + void updateTravelSchedule_WithNullRequest_ReturnsBadRequest() throws Exception { mockMvc.perform(post("/travel/schedule") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", accessToken)) From 2f86ff1211118cf7aeec8793b93d380eba734401 Mon Sep 17 00:00:00 2001 From: hyn Date: Wed, 29 Jan 2025 01:58:22 +0900 Subject: [PATCH 15/34] =?UTF-8?q?refactor:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=84=A4=EC=A0=95=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD,?= =?UTF-8?q?=20city=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EB=B0=98=EB=B3=B5=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/TravelScheduleService.java | 12 ++++-------- .../travelschedule/domain/MemberTravelSchedule.java | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java index 370bcf7..8c0a01b 100644 --- a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java +++ b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java @@ -6,7 +6,6 @@ import com.hyun.udong.travelschedule.domain.City; import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; import com.hyun.udong.travelschedule.domain.TravelScheduleCity; -import com.hyun.udong.travelschedule.exception.CityNotFoundException; import com.hyun.udong.travelschedule.exception.MemberTravelScheduleNotFoundException; import com.hyun.udong.travelschedule.infrastructure.repository.CityRepository; import com.hyun.udong.travelschedule.infrastructure.repository.TravelScheduleRepository; @@ -36,14 +35,11 @@ public MemberTravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRe .endDate(request.getEndDate()) .build(); - List travelScheduleCities = request.getCityIds().stream() - .map(cityId -> { - City city = cityRepository.findById(cityId) - .orElseThrow(() -> CityNotFoundException.EXCEPTION); - return new TravelScheduleCity(travelSchedule, city); - }) + List cities = cityRepository.findAllById(request.getCityIds()); + List travelScheduleCities = cities.stream() + .map(city -> new TravelScheduleCity(travelSchedule, city)) .toList(); - travelSchedule.addTravelScheduleCity(travelScheduleCities); + travelSchedule.addTravelScheduleCities(travelScheduleCities); travelScheduleRepository.save(travelSchedule); member.updateTravelSchedule(travelSchedule); diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java b/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java index 080b2cc..f40d2b2 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java @@ -35,7 +35,7 @@ public MemberTravelSchedule(LocalDate startDate, LocalDate endDate) { this.endDate = endDate; } - public void addTravelScheduleCity(List travelScheduleCities) { + public void addTravelScheduleCities(List travelScheduleCities) { this.travelScheduleCities = travelScheduleCities; } } From 912d976edc2c7cd753b2ce53209572f015d2d181 Mon Sep 17 00:00:00 2001 From: hyn Date: Wed, 29 Jan 2025 02:36:38 +0900 Subject: [PATCH 16/34] =?UTF-8?q?test:=20=EC=A1=B4=EC=9E=AC=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=8F=84=EC=8B=9C=20id?= =?UTF-8?q?=EB=A1=9C=20=EC=97=AC=ED=96=89=20=EC=9D=BC=EC=A0=95=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=8B=9C=20=EC=98=88=EC=99=B8=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/TravelScheduleService.java | 4 ++++ .../application/service/TravelScheduleServiceTest.java | 7 +------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java index 8c0a01b..7a0b5bb 100644 --- a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java +++ b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java @@ -6,6 +6,7 @@ import com.hyun.udong.travelschedule.domain.City; import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; import com.hyun.udong.travelschedule.domain.TravelScheduleCity; +import com.hyun.udong.travelschedule.exception.CityNotFoundException; import com.hyun.udong.travelschedule.exception.MemberTravelScheduleNotFoundException; import com.hyun.udong.travelschedule.infrastructure.repository.CityRepository; import com.hyun.udong.travelschedule.infrastructure.repository.TravelScheduleRepository; @@ -36,6 +37,9 @@ public MemberTravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRe .build(); List cities = cityRepository.findAllById(request.getCityIds()); + if (cities.size() != request.getCityIds().size()) { + throw CityNotFoundException.EXCEPTION; + } List travelScheduleCities = cities.stream() .map(city -> new TravelScheduleCity(travelSchedule, city)) .toList(); diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index c7ddbda..db87697 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -25,7 +25,6 @@ class TravelScheduleServiceTest { public static final long FIRST_MEMBER_ID = 1L; - public static final long NOT_EXIST_MEMBER_ID = 2L; @Autowired private MemberRepository memberRepository; @@ -120,12 +119,8 @@ void findTravelSchedule_noMember_throw() { @DisplayName("회원의 여행 일정이 없을 때 예외가 발생한다.") @Test void findTravelSchedule_noTravelSchedule_throw() { - // given - Member member = new Member(2L, SocialType.KAKAO, "hyun", "profile_image"); - memberRepository.save(member); - // when & then - thenThrownBy(() -> travelScheduleService.findTravelSchedule(NOT_EXIST_MEMBER_ID)) + thenThrownBy(() -> travelScheduleService.findTravelSchedule(FIRST_MEMBER_ID)) .isInstanceOf(MemberTravelScheduleNotFoundException.class); } } From 1e7610b3d3b1b0720939d94927d8518fa7af4537 Mon Sep 17 00:00:00 2001 From: hyn Date: Wed, 29 Jan 2025 02:53:16 +0900 Subject: [PATCH 17/34] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=AA=BD=20=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=20=EB=B0=8F?= =?UTF-8?q?=20=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../udong/auth/util/JwtTokenProvider.java | 4 +- src/test/resources/application.yml | 23 ++--- src/test/resources/data.sql | 84 ------------------- 3 files changed, 14 insertions(+), 97 deletions(-) delete mode 100644 src/test/resources/data.sql diff --git a/src/main/java/com/hyun/udong/auth/util/JwtTokenProvider.java b/src/main/java/com/hyun/udong/auth/util/JwtTokenProvider.java index 3b1d211..c444ac9 100644 --- a/src/main/java/com/hyun/udong/auth/util/JwtTokenProvider.java +++ b/src/main/java/com/hyun/udong/auth/util/JwtTokenProvider.java @@ -23,10 +23,10 @@ @Slf4j public class JwtTokenProvider { - @Value("${ACCESS_TOKEN_EXPIRE_TIME}") + @Value("${jwt.access-token-expire-time}") private long accessTokenExpireTime; - @Value("${REFRESH_TOKEN_EXPIRE_TIME}") + @Value("${jwt.refresh-token-expire-time}") private long refreshTokenExpireTime; @Value("${jwt.secret}") diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 2e3a50c..e32fea3 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -2,9 +2,9 @@ spring: config: import: optional:file:.env[.properties] datasource: - url: ${DATASOURCE_URL}_test - username: ${DATASOURCE_USERNAME} - password: ${DATASOURCE_PASSWORD} + url: jdbc:postgresql://localhost:5432/hyn_test + username: myuser + password: 1234 jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect hibernate: @@ -14,18 +14,19 @@ spring: sql: init: mode: always + scripts: classpath:/data.sql social: kakao: - client-id: ${KAKAO_CLIENT_ID} - redirect-uri: ${KAKAO_REDIRECT_URI} + client-id: kakao-client-id + redirect-uri: kakao-redirect-uri google: - client-id: ${GOOGLE_CLIENT_ID} - client-secret: ${GOOGLE_CLIENT_SECRET} - redirect-uri: ${GOOGLE_REDIRECT_URI} + client-id: google-client-id + client-secret: google-client-secret + redirect-uri: google-redirect-uri jwt: - secret: ${JWT_SECRET} - access-token-expire-time: ${ACCESS_TOKEN_EXPIRE_TIME} - refresh-token-expire-time: ${REFRESH_TOKEN_EXPIRE_TIME} + secret: udongudongudongudongudongudongudong123 + access-token-expire-time: 3600000 + refresh-token-expire-time: 1209600000 diff --git a/src/test/resources/data.sql b/src/test/resources/data.sql deleted file mode 100644 index ff3d45f..0000000 --- a/src/test/resources/data.sql +++ /dev/null @@ -1,84 +0,0 @@ -TRUNCATE TABLE city CASCADE; -TRUNCATE TABLE country CASCADE; - --- 나라 데이터 삽입 -INSERT INTO country (country_id, name, code) -VALUES (1, 'South Korea', 'KR'), - (2, 'United States', 'US'), - (3, 'United Kingdom', 'GB'), - (4, 'France', 'FR'), - (5, 'Germany', 'DE'), - (6, 'Japan', 'JP'), - (7, 'China', 'CN'), - (8, 'India', 'IN'), - (9, 'Australia', 'AU'), - (10, 'Canada', 'CA'), - (11, 'Italy', 'IT'), - (12, 'Spain', 'ES'), - (13, 'Brazil', 'BR'), - (14, 'Mexico', 'MX'), - (15, 'Russia', 'RU'), - (16, 'South Africa', 'ZA'), - (17, 'Argentina', 'AR'), - (18, 'New Zealand', 'NZ'), - (19, 'Indonesia', 'ID'), - (20, 'Thailand', 'TH'), - (21, 'Turkey', 'TR'), - (22, 'Egypt', 'EG'), - (23, 'Saudi Arabia', 'SA'), - (24, 'Vietnam', 'VN'), - (25, 'Malaysia', 'MY'); - --- 도시 데이터 삽입 -INSERT INTO city (city_id, name, country_id) -VALUES (1, 'Seoul', 1), - (2, 'Busan', 1), - (3, 'New York', 2), - (4, 'Los Angeles', 2), - (5, 'Chicago', 2), - (6, 'London', 3), - (7, 'Manchester', 3), - (8, 'Paris', 4), - (9, 'Lyon', 4), - (10, 'Berlin', 5), - (11, 'Munich', 5), - (12, 'Tokyo', 6), - (13, 'Osaka', 6), - (14, 'Beijing', 7), - (15, 'Shanghai', 7), - (16, 'Mumbai', 8), - (17, 'Delhi', 8), - (18, 'Sydney', 9), - (19, 'Melbourne', 9), - (20, 'Toronto', 10), - (21, 'Vancouver', 10), - (22, 'Rome', 11), - (23, 'Milan', 11), - (24, 'Madrid', 12), - (25, 'Barcelona', 12), - (26, 'Rio de Janeiro', 13), - (27, 'São Paulo', 13), - (28, 'Mexico City', 14), - (29, 'Guadalajara', 14), - (30, 'Moscow', 15), - (31, 'Saint Petersburg', 15), - (32, 'Cape Town', 16), - (33, 'Johannesburg', 16), - (34, 'Buenos Aires', 17), - (35, 'Córdoba', 17), - (36, 'Auckland', 18), - (37, 'Wellington', 18), - (38, 'Jakarta', 19), - (39, 'Bali', 19), - (40, 'Bangkok', 20), - (41, 'Chiang Mai', 20), - (42, 'Istanbul', 21), - (43, 'Ankara', 21), - (44, 'Cairo', 22), - (45, 'Alexandria', 22), - (46, 'Riyadh', 23), - (47, 'Jeddah', 23), - (48, 'Hanoi', 24), - (49, 'Ho Chi Minh City', 24), - (50, 'Kuala Lumpur', 25), - (51, 'Penang', 25); From 117d0fdb5d403d4bdfd217cb6d3b5f0077c5b022 Mon Sep 17 00:00:00 2001 From: hyn Date: Wed, 29 Jan 2025 03:23:21 +0900 Subject: [PATCH 18/34] =?UTF-8?q?test:=20argument=20resolve=20=EB=AA=A8?= =?UTF-8?q?=ED=82=B9=20=EC=A0=9C=EA=B1=B0,=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20REST-Assured=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../auth/oauth/MockArgumentResolver.java | 23 ----- .../com/hyun/udong/auth/oauth/TestOauth.java | 20 +++++ .../TravelScheduleControllerTest.java | 87 +++++++++---------- 4 files changed, 61 insertions(+), 71 deletions(-) delete mode 100644 src/test/java/com/hyun/udong/auth/oauth/MockArgumentResolver.java create mode 100644 src/test/java/com/hyun/udong/auth/oauth/TestOauth.java diff --git a/build.gradle b/build.gradle index b55971b..97c2d59 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,8 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + testImplementation("io.rest-assured:rest-assured:5.5.0") } tasks.named('test') { diff --git a/src/test/java/com/hyun/udong/auth/oauth/MockArgumentResolver.java b/src/test/java/com/hyun/udong/auth/oauth/MockArgumentResolver.java deleted file mode 100644 index 44e791f..0000000 --- a/src/test/java/com/hyun/udong/auth/oauth/MockArgumentResolver.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.hyun.udong.auth.oauth; - -import com.hyun.udong.auth.application.service.AuthService; -import com.hyun.udong.auth.presentation.resolver.AuthenticationArgumentResolver; -import com.hyun.udong.member.domain.Member; -import org.springframework.core.MethodParameter; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.ModelAndViewContainer; - -public class MockArgumentResolver extends AuthenticationArgumentResolver { - public MockArgumentResolver(AuthService authService) { - super(authService); - } - - @Override - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) { - return Member.createForTest(1L, "hyun"); - } -} diff --git a/src/test/java/com/hyun/udong/auth/oauth/TestOauth.java b/src/test/java/com/hyun/udong/auth/oauth/TestOauth.java new file mode 100644 index 0000000..370b217 --- /dev/null +++ b/src/test/java/com/hyun/udong/auth/oauth/TestOauth.java @@ -0,0 +1,20 @@ +package com.hyun.udong.auth.oauth; + +import com.hyun.udong.auth.util.JwtTokenProvider; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class TestOauth { + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + public static String ACCESS_TOKEN_1L; + + @PostConstruct + public void init() { + ACCESS_TOKEN_1L = "Bearer " + jwtTokenProvider.generateAccessToken(1L); + } +} diff --git a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java index 84b47f6..5bd519a 100644 --- a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java @@ -1,77 +1,68 @@ package com.hyun.udong.travelschedule.presentation.controller; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.hyun.udong.auth.oauth.MockArgumentResolver; -import com.hyun.udong.travelschedule.application.service.TravelScheduleService; +import com.hyun.udong.auth.oauth.TestOauth; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.time.LocalDate; import java.util.List; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.hamcrest.Matchers.equalTo; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@AutoConfigureMockMvc @Sql("/member.sql") class TravelScheduleControllerTest { - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - private TravelScheduleRequest request; - - private String accessToken; - - @Autowired - private TravelScheduleService travelScheduleService; + @LocalServerPort + private int port; @BeforeEach void setUp() { - accessToken = "Bearer mockToken"; - request = new TravelScheduleRequest( + RestAssured.port = port; + } + + @Test + void updateTravelSchedule_ReturnsOk() { + TravelScheduleRequest request = new TravelScheduleRequest( LocalDate.of(2025, 1, 25), LocalDate.of(2025, 2, 10), List.of(1L, 2L)); - this.mockMvc = MockMvcBuilders.standaloneSetup(new TravelScheduleController(travelScheduleService)) - .setCustomArgumentResolvers(new MockArgumentResolver(null)) - .setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper)) - .build(); - } + RestAssured + .given().log().all() + .contentType(ContentType.JSON) + .header("Authorization", TestOauth.ACCESS_TOKEN_1L) + .body(request) - @Test - void updateTravelSchedule_ReturnsOk() throws Exception { - mockMvc.perform(post("/travel/schedule") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request)) - .header("Authorization", accessToken)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.startDate").value(String.valueOf(request.getStartDate()))) - .andExpect(jsonPath("$.endDate").value(String.valueOf(request.getEndDate()))) - .andExpect(jsonPath("$.travelScheduleCities[0].cityId").value(1L)) - .andExpect(jsonPath("$.travelScheduleCities[1].cityId").value(2L)); + .when() + .post("/travel/schedule") + + .then().log().all() + .statusCode(200) + .body("startDate", equalTo(String.valueOf(request.getStartDate()))) + .body("endDate", equalTo(String.valueOf(request.getEndDate()))) + .body("travelScheduleCities[0].cityId", equalTo(1)) + .body("travelScheduleCities[1].cityId", equalTo(2)); } @Test - void updateTravelSchedule_WithNullRequest_ReturnsBadRequest() throws Exception { - mockMvc.perform(post("/travel/schedule") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", accessToken)) - .andExpect(status().isBadRequest()); + void updateTravelSchedule_WithNullRequest_ReturnsBadRequest() { + RestAssured + .given().log().all() + .contentType(ContentType.JSON) + .header("Authorization", TestOauth.ACCESS_TOKEN_1L) + + .when() + .post("/travel/schedule") + + .then().log().all() + .statusCode(HttpStatus.BAD_REQUEST.value()); } } From 354957e477d56b20320349ab492838b0db2bcfab Mon Sep 17 00:00:00 2001 From: hyn Date: Wed, 29 Jan 2025 03:23:35 +0900 Subject: [PATCH 19/34] =?UTF-8?q?refactor:=20member.sql=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=82=BD=EC=9E=85=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/member.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/resources/member.sql b/src/test/resources/member.sql index 9ce33cc..8ccc028 100644 --- a/src/test/resources/member.sql +++ b/src/test/resources/member.sql @@ -1,2 +1,3 @@ INSERT INTO member (member_id, social_id, social_type, nickname, age, profile_image_url) -VALUES (1, 1, 'KAKAO', 'hyun', 25, 'profile_image'); +VALUES (1, 1, 'KAKAO', 'hyun', 25, 'profile_image') +ON CONFLICT (member_id) DO NOTHING; From 9200d686a5e7519150e16aa45efde3515b0b9590 Mon Sep 17 00:00:00 2001 From: hyn Date: Wed, 29 Jan 2025 03:24:36 +0900 Subject: [PATCH 20/34] =?UTF-8?q?refactor:=20member=20=EB=82=B4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9=20=EC=A0=95=EC=A0=81=20?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/hyun/udong/member/domain/Member.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/com/hyun/udong/member/domain/Member.java b/src/main/java/com/hyun/udong/member/domain/Member.java index 6c73b64..19bb775 100644 --- a/src/main/java/com/hyun/udong/member/domain/Member.java +++ b/src/main/java/com/hyun/udong/member/domain/Member.java @@ -43,13 +43,6 @@ public Member(Long socialId, SocialType socialType, String nickname, String prof this.profileImageUrl = profileImageUrl; } - public static Member createForTest(Long id, String nickname) { - Member member = new Member(); - member.id = id; - member.nickname = nickname; - return member; - } - public void updateRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } From 5e9c116df1704e1b8ad0632d078e180ad67c9683 Mon Sep 17 00:00:00 2001 From: hyn Date: Wed, 29 Jan 2025 03:38:29 +0900 Subject: [PATCH 21/34] =?UTF-8?q?refactor:=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=20MemberTravelSchedule=20->=20TravelSchedule=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/hyun/udong/member/domain/Member.java | 6 +++--- .../application/service/TravelScheduleService.java | 14 +++++++------- ...mberTravelSchedule.java => TravelSchedule.java} | 4 ++-- .../travelschedule/domain/TravelScheduleCity.java | 4 ++-- .../MemberTravelScheduleNotFoundException.java | 12 ------------ .../exception/TravelScheduleNotFoundException.java | 12 ++++++++++++ .../repository/TravelScheduleRepository.java | 4 ++-- .../controller/TravelScheduleController.java | 6 +++--- .../presentation/dto/TravelScheduleResponse.java | 4 ++-- .../service/TravelScheduleServiceTest.java | 10 +++++----- 10 files changed, 38 insertions(+), 38 deletions(-) rename src/main/java/com/hyun/udong/travelschedule/domain/{MemberTravelSchedule.java => TravelSchedule.java} (88%) delete mode 100644 src/main/java/com/hyun/udong/travelschedule/exception/MemberTravelScheduleNotFoundException.java create mode 100644 src/main/java/com/hyun/udong/travelschedule/exception/TravelScheduleNotFoundException.java diff --git a/src/main/java/com/hyun/udong/member/domain/Member.java b/src/main/java/com/hyun/udong/member/domain/Member.java index 19bb775..d451b39 100644 --- a/src/main/java/com/hyun/udong/member/domain/Member.java +++ b/src/main/java/com/hyun/udong/member/domain/Member.java @@ -1,7 +1,7 @@ package com.hyun.udong.member.domain; import com.hyun.udong.common.entity.BaseTimeEntity; -import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.domain.TravelSchedule; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @@ -34,7 +34,7 @@ public class Member extends BaseTimeEntity { private String refreshToken; @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) - private MemberTravelSchedule travelSchedule; + private TravelSchedule travelSchedule; public Member(Long socialId, SocialType socialType, String nickname, String profileImageUrl) { this.socialId = socialId; @@ -47,7 +47,7 @@ public void updateRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } - public void updateTravelSchedule(MemberTravelSchedule travelSchedule) { + public void updateTravelSchedule(TravelSchedule travelSchedule) { this.travelSchedule = travelSchedule; } } diff --git a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java index 7a0b5bb..2a1f34d 100644 --- a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java +++ b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java @@ -4,10 +4,10 @@ import com.hyun.udong.member.exception.MemberNotFoundException; import com.hyun.udong.member.infrastructure.repository.MemberRepository; import com.hyun.udong.travelschedule.domain.City; -import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.domain.TravelSchedule; import com.hyun.udong.travelschedule.domain.TravelScheduleCity; import com.hyun.udong.travelschedule.exception.CityNotFoundException; -import com.hyun.udong.travelschedule.exception.MemberTravelScheduleNotFoundException; +import com.hyun.udong.travelschedule.exception.TravelScheduleNotFoundException; import com.hyun.udong.travelschedule.infrastructure.repository.CityRepository; import com.hyun.udong.travelschedule.infrastructure.repository.TravelScheduleRepository; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; @@ -27,11 +27,11 @@ public class TravelScheduleService { private final CityRepository cityRepository; @Transactional - public MemberTravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRequest request) { + public TravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRequest request) { Member member = memberRepository.findById(memberId) .orElseThrow(() -> MemberNotFoundException.EXCEPTION); - MemberTravelSchedule travelSchedule = MemberTravelSchedule.builder() + TravelSchedule travelSchedule = TravelSchedule.builder() .startDate(request.getStartDate()) .endDate(request.getEndDate()) .build(); @@ -51,13 +51,13 @@ public MemberTravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRe return travelSchedule; } - public MemberTravelSchedule findTravelSchedule(Long memberId) { + public TravelSchedule findTravelSchedule(Long memberId) { Member member = memberRepository.findById(memberId) .orElseThrow(() -> MemberNotFoundException.EXCEPTION); - MemberTravelSchedule travelSchedule = member.getTravelSchedule(); + TravelSchedule travelSchedule = member.getTravelSchedule(); if (travelSchedule == null) { - throw MemberTravelScheduleNotFoundException.EXCEPTION; + throw TravelScheduleNotFoundException.EXCEPTION; } return travelSchedule; } diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java b/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java similarity index 88% rename from src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java rename to src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java index f40d2b2..c7ca21e 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/MemberTravelSchedule.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java @@ -15,7 +15,7 @@ @Entity @Getter @NoArgsConstructor(access = PROTECTED) -public class MemberTravelSchedule extends BaseTimeEntity { +public class TravelSchedule extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -30,7 +30,7 @@ public class MemberTravelSchedule extends BaseTimeEntity { private List travelScheduleCities = new ArrayList<>(); @Builder - public MemberTravelSchedule(LocalDate startDate, LocalDate endDate) { + public TravelSchedule(LocalDate startDate, LocalDate endDate) { this.startDate = startDate; this.endDate = endDate; } diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java b/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java index 6e1fd15..2d2dcda 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java @@ -18,14 +18,14 @@ public class TravelScheduleCity { @ManyToOne @JoinColumn(name = "travel_schedule_id", nullable = false) - private MemberTravelSchedule travelSchedule; + private TravelSchedule travelSchedule; @ManyToOne @JoinColumn(name = "city_id", nullable = false) private City city; @Builder - public TravelScheduleCity(MemberTravelSchedule travelSchedule, City city) { + public TravelScheduleCity(TravelSchedule travelSchedule, City city) { this.travelSchedule = travelSchedule; this.city = city; } diff --git a/src/main/java/com/hyun/udong/travelschedule/exception/MemberTravelScheduleNotFoundException.java b/src/main/java/com/hyun/udong/travelschedule/exception/MemberTravelScheduleNotFoundException.java deleted file mode 100644 index a8fbe51..0000000 --- a/src/main/java/com/hyun/udong/travelschedule/exception/MemberTravelScheduleNotFoundException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.hyun.udong.travelschedule.exception; - -import com.hyun.udong.common.exception.ErrorCode; -import com.hyun.udong.common.exception.UdongException; - -public class MemberTravelScheduleNotFoundException extends UdongException { - public static final MemberTravelScheduleNotFoundException EXCEPTION = new MemberTravelScheduleNotFoundException(); - - private MemberTravelScheduleNotFoundException() { - super(ErrorCode.MEMBER_TRAVEL_SCHEDULE_NOT_FOUND); - } -} diff --git a/src/main/java/com/hyun/udong/travelschedule/exception/TravelScheduleNotFoundException.java b/src/main/java/com/hyun/udong/travelschedule/exception/TravelScheduleNotFoundException.java new file mode 100644 index 0000000..1c15832 --- /dev/null +++ b/src/main/java/com/hyun/udong/travelschedule/exception/TravelScheduleNotFoundException.java @@ -0,0 +1,12 @@ +package com.hyun.udong.travelschedule.exception; + +import com.hyun.udong.common.exception.ErrorCode; +import com.hyun.udong.common.exception.UdongException; + +public class TravelScheduleNotFoundException extends UdongException { + public static final TravelScheduleNotFoundException EXCEPTION = new TravelScheduleNotFoundException(); + + private TravelScheduleNotFoundException() { + super(ErrorCode.MEMBER_TRAVEL_SCHEDULE_NOT_FOUND); + } +} diff --git a/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleRepository.java b/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleRepository.java index e6b374b..9aa92b3 100644 --- a/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleRepository.java +++ b/src/main/java/com/hyun/udong/travelschedule/infrastructure/repository/TravelScheduleRepository.java @@ -1,7 +1,7 @@ package com.hyun.udong.travelschedule.infrastructure.repository; -import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.domain.TravelSchedule; import org.springframework.data.jpa.repository.JpaRepository; -public interface TravelScheduleRepository extends JpaRepository { +public interface TravelScheduleRepository extends JpaRepository { } diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java b/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java index be506b3..6237331 100644 --- a/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleController.java @@ -3,7 +3,7 @@ import com.hyun.udong.common.annotation.LoginMember; import com.hyun.udong.member.domain.Member; import com.hyun.udong.travelschedule.application.service.TravelScheduleService; -import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.domain.TravelSchedule; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleResponse; import lombok.RequiredArgsConstructor; @@ -21,14 +21,14 @@ public class TravelScheduleController { public ResponseEntity updateTravelSchedule( @LoginMember Member member, @RequestBody TravelScheduleRequest travelScheduleRequest) { - MemberTravelSchedule travelSchedule = travelScheduleService.updateTravelSchedule(member.getId(), travelScheduleRequest); + TravelSchedule travelSchedule = travelScheduleService.updateTravelSchedule(member.getId(), travelScheduleRequest); TravelScheduleResponse response = TravelScheduleResponse.from(travelSchedule); return ResponseEntity.ok(response); } @GetMapping("/schedule") public ResponseEntity getTravelSchedule(@LoginMember Member member) { - MemberTravelSchedule travelSchedule = travelScheduleService.findTravelSchedule(member.getId()); + TravelSchedule travelSchedule = travelScheduleService.findTravelSchedule(member.getId()); TravelScheduleResponse response = TravelScheduleResponse.from(travelSchedule); return ResponseEntity.ok(response); } diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java index 7a44838..273e84e 100644 --- a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java @@ -1,7 +1,7 @@ package com.hyun.udong.travelschedule.presentation.dto; import com.hyun.udong.common.annotation.DateFormat; -import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.domain.TravelSchedule; import lombok.Builder; import lombok.Getter; @@ -28,7 +28,7 @@ public static class TravelScheduleCityResponse { private String cityName; } - public static TravelScheduleResponse from(MemberTravelSchedule travelSchedule) { + public static TravelScheduleResponse from(TravelSchedule travelSchedule) { return TravelScheduleResponse.builder() .startDate(travelSchedule.getStartDate()) .endDate(travelSchedule.getEndDate()) diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index db87697..796f21d 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -4,9 +4,9 @@ import com.hyun.udong.member.domain.SocialType; import com.hyun.udong.member.exception.MemberNotFoundException; import com.hyun.udong.member.infrastructure.repository.MemberRepository; -import com.hyun.udong.travelschedule.domain.MemberTravelSchedule; +import com.hyun.udong.travelschedule.domain.TravelSchedule; import com.hyun.udong.travelschedule.exception.CityNotFoundException; -import com.hyun.udong.travelschedule.exception.MemberTravelScheduleNotFoundException; +import com.hyun.udong.travelschedule.exception.TravelScheduleNotFoundException; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -49,7 +49,7 @@ void updateTravelSchedule_validMemberAndRequest() { TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); // when - MemberTravelSchedule travelSchedule = travelScheduleService.updateTravelSchedule(FIRST_MEMBER_ID, request); + TravelSchedule travelSchedule = travelScheduleService.updateTravelSchedule(FIRST_MEMBER_ID, request); // then then(travelSchedule).isNotNull(); @@ -96,7 +96,7 @@ void findTravelSchedule_ok() { // when Member member = memberRepository.findById(memberId) .orElseThrow(() -> MemberNotFoundException.EXCEPTION); - MemberTravelSchedule travelSchedule = member.getTravelSchedule(); + TravelSchedule travelSchedule = member.getTravelSchedule(); // then then(travelSchedule).isNotNull(); @@ -121,6 +121,6 @@ void findTravelSchedule_noMember_throw() { void findTravelSchedule_noTravelSchedule_throw() { // when & then thenThrownBy(() -> travelScheduleService.findTravelSchedule(FIRST_MEMBER_ID)) - .isInstanceOf(MemberTravelScheduleNotFoundException.class); + .isInstanceOf(TravelScheduleNotFoundException.class); } } From 6f9420f4019610fed0323f12d05bb064df10f9ec Mon Sep 17 00:00:00 2001 From: hyn Date: Wed, 29 Jan 2025 03:40:18 +0900 Subject: [PATCH 22/34] =?UTF-8?q?refactor:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EA=B4=80=EB=A0=A8=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=A7=80=EC=97=B0=20=EB=A1=9C=EB=94=A9=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/hyun/udong/travelschedule/domain/City.java | 2 +- .../com/hyun/udong/travelschedule/domain/TravelSchedule.java | 2 +- .../hyun/udong/travelschedule/domain/TravelScheduleCity.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/City.java b/src/main/java/com/hyun/udong/travelschedule/domain/City.java index 9fefe62..bb81191 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/City.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/City.java @@ -18,7 +18,7 @@ public class City { private String name; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "country_id", nullable = false) private Country country; diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java b/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java index c7ca21e..38410cc 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java @@ -26,7 +26,7 @@ public class TravelSchedule extends BaseTimeEntity { private LocalDate endDate; - @OneToMany(mappedBy = "travelSchedule", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + @OneToMany(mappedBy = "travelSchedule", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) private List travelScheduleCities = new ArrayList<>(); @Builder diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java b/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java index 2d2dcda..1bfbf4c 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java @@ -16,11 +16,11 @@ public class TravelScheduleCity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "travel_schedule_id", nullable = false) private TravelSchedule travelSchedule; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "city_id", nullable = false) private City city; From edf28903e43f13f5bf2a3e5540fcc0fc966fb34b Mon Sep 17 00:00:00 2001 From: hyn Date: Wed, 29 Jan 2025 04:41:15 +0900 Subject: [PATCH 23/34] =?UTF-8?q?test:=20@DisplayName=20->=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=AA=85=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD,=20RestAssured=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/AuthServiceTest.java | 10 +-- .../controller/AuthControllerTest.java | 73 +++++++++++-------- .../udong/auth/util/JwtTokenProviderTest.java | 38 ++++------ .../service/MemberServiceTest.java | 12 +-- .../service/TravelScheduleServiceTest.java | 19 ++--- .../TravelScheduleControllerTest.java | 4 +- 6 files changed, 72 insertions(+), 84 deletions(-) diff --git a/src/test/java/com/hyun/udong/auth/application/service/AuthServiceTest.java b/src/test/java/com/hyun/udong/auth/application/service/AuthServiceTest.java index ce62422..3c67a9b 100644 --- a/src/test/java/com/hyun/udong/auth/application/service/AuthServiceTest.java +++ b/src/test/java/com/hyun/udong/auth/application/service/AuthServiceTest.java @@ -35,8 +35,7 @@ class AuthServiceTest { private AuthService authService; @Test - @DisplayName("카카오 로그인 시 사용자 정보와 refresh_token이 저장된다.") - void kakaoLogin_save() { + void 카카오_로그인_시_사용자_정보와_refresh_token이_저장된다() { // given String code = "authCode"; KakaoTokenResponse kakaoTokenResponse = new KakaoTokenResponse("accessToken"); @@ -59,8 +58,8 @@ void kakaoLogin_save() { } @Test - @DisplayName("refreshToken 재발급 시 새로운 토큰을 저장한다.") - void refreshTokens_ok() { + @DisplayName(" 재발급 시 새로운 토큰을 저장한다.") + void refreshToken_재발급_시_새로운_토큰을_저장한다() { // given Member member = memberService.save(new Member(100L, SocialType.KAKAO, "hyun", "profile_image")); String initialRefreshToken = "initialRefreshToken"; @@ -80,9 +79,8 @@ void refreshTokens_ok() { then(updatedMember.getRefreshToken()).isEqualTo(newRefreshToken); } - @DisplayName("토큰 재발급 시 유효한 코드가 아니면 예외가 발생한다.") @Test - void refreshTokens_throw() { + void 토큰_재발급_시_유효한_코드가_아니면_예외가_발생한다() { String otherRefreshToken = jwtTokenProvider.generateRefreshToken(200L); // when & then diff --git a/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java b/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java index 08bff2f..c75e3e2 100644 --- a/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java +++ b/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java @@ -7,28 +7,24 @@ import com.hyun.udong.member.application.service.MemberService; import com.hyun.udong.member.domain.Member; import com.hyun.udong.member.domain.SocialType; -import org.junit.jupiter.api.DisplayName; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.springframework.test.web.servlet.MockMvc; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@AutoConfigureMockMvc class AuthControllerTest { - public static final long FIRST_SAVED_MEMBER_ID = 1L; - - @Autowired - protected MockMvc mockMvc; + public static final int FIRST_SAVED_MEMBER_ID = 1; @Autowired private JwtTokenProvider jwtTokenProvider; @@ -39,9 +35,16 @@ class AuthControllerTest { @Autowired private MemberService memberService; - @DisplayName("카카오 로그인 성공 시 loginResponse가 반환되는지 확인한다.") + @LocalServerPort + private int port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + @Test - void kakaoLogin() throws Exception { + void 카카오_로그인_성공_시_loginResponse가_반환되는지_확인한다() { // given KakaoTokenResponse kakaoTokenResponse = new KakaoTokenResponse("accessToken"); KakaoProfileResponse kakaoProfileResponse = new KakaoProfileResponse(100L, "hyun", "profile_image"); @@ -49,18 +52,24 @@ void kakaoLogin() throws Exception { given(kakaoOAuthClient.getUserProfile(anyString())).willReturn(kakaoProfileResponse); // when & then - mockMvc.perform(get("/auth/oauth/kakao") - .param("code", "authCode")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id").value(FIRST_SAVED_MEMBER_ID)) - .andExpect(jsonPath("$.nickname").value(kakaoProfileResponse.getKakaoAccount().getNickname())) - .andExpect(jsonPath("$.token.accessToken").exists()) - .andExpect(jsonPath("$.token.refreshToken").exists()); + RestAssured + .given().log().all() + .contentType(ContentType.JSON) + .param("code", "authCode") + + .when() + .get("/auth/oauth/kakao") + + .then().log().all() + .statusCode(200) + .body("id", equalTo(FIRST_SAVED_MEMBER_ID)) + .body("nickname", equalTo(kakaoProfileResponse.getKakaoAccount().getNickname())) + .body("token.accessToken", notNullValue()) + .body("token.refreshToken", notNullValue()); } - @DisplayName("토큰 갱신 성공 시 loginResponse가 반환되는지 확인한다.") @Test - void refreshTokens() throws Exception { + void 토큰_갱신_성공_시_loginResponse가_반환되는지_확인한다() { // given Member member = memberService.save(new Member(100L, SocialType.KAKAO, "hyun", "profile_image")); String refreshToken = jwtTokenProvider.generateRefreshToken(member.getId()); @@ -69,13 +78,19 @@ void refreshTokens() throws Exception { memberService.save(member); // when & then - mockMvc.perform(get("/auth/token/refresh") - .param("refreshToken", refreshToken)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.id").value(member.getId())) - .andExpect(jsonPath("$.nickname").value(member.getNickname())) - .andExpect(jsonPath("$.token.accessToken").exists()) - .andExpect(jsonPath("$.token.refreshToken").exists()); + RestAssured + .given().log().all() + .contentType(ContentType.JSON) + .param("refreshToken", refreshToken) + + .when() + .get("/auth/token/refresh") + .then().log().all() + .statusCode(200) + .body("id", equalTo(member.getId().intValue())) + .body("nickname", equalTo(member.getNickname())) + .body("token.accessToken", notNullValue()) + .body("token.refreshToken", notNullValue()); } } diff --git a/src/test/java/com/hyun/udong/auth/util/JwtTokenProviderTest.java b/src/test/java/com/hyun/udong/auth/util/JwtTokenProviderTest.java index e03bf95..c9e3b03 100644 --- a/src/test/java/com/hyun/udong/auth/util/JwtTokenProviderTest.java +++ b/src/test/java/com/hyun/udong/auth/util/JwtTokenProviderTest.java @@ -3,7 +3,6 @@ import com.hyun.udong.auth.exception.ExpiredTokenException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -25,40 +24,31 @@ class JwtTokenProviderTest { private JwtTokenProvider jwtTokenProvider; @Test - @DisplayName("유효한 ID로 액세스 토큰을 생성하면 토큰을 반환한다.") - void generateAccessToken_validId_returnsToken() { - Long id = 1L; - String token = jwtTokenProvider.generateAccessToken(id); + void 유효한_ID로_액세스_토큰을_생성하면_토큰을_반환한다() { + String token = jwtTokenProvider.generateAccessToken(1L); assertNotNull(token); } @Test - @DisplayName("유효한 ID로 리프레시 토큰을 생성하면 토큰을 반환한다.") - void generateRefreshToken_validId_returnsToken() { - Long id = 1L; - String token = jwtTokenProvider.generateRefreshToken(id); + void 유효한_ID로_리프레시_토큰을_생성하면_토큰을_반환한다() { + String token = jwtTokenProvider.generateRefreshToken(1L); assertNotNull(token); } @Test - @DisplayName("유효한 토큰에서 회원 ID를 추출하면 ID를 반환한다.") - void getSubject() { - Long id = 1L; - String token = jwtTokenProvider.generateAccessToken(id); + void 유효한_토큰에서_회원_ID를_추출하면_ID를_반환한다() { + String token = jwtTokenProvider.generateAccessToken(1L); String memberId = jwtTokenProvider.getSubjectFromToken(token); - assertEquals(id.toString(), memberId); + assertEquals(((Long) 1L).toString(), memberId); } @Test - @DisplayName("유효하지 않은 토큰에서 회원 ID를 추출하면 예외를 던진다.") - void getSubjectFromToken_invalidToken_throwsException() { - String token = "invalid.token"; - assertThrows(Exception.class, () -> jwtTokenProvider.getSubjectFromToken(token)); + void 유효하지_않은_토큰에서_회원_ID를_추출하면_예외를_던진다() { + assertThrows(Exception.class, () -> jwtTokenProvider.getSubjectFromToken("invalid.token")); } @Test - @DisplayName("만료된 토큰을 검증하면 ExpiredTokenException을 던진다.") - void parseToken_expiredToken_throwsExpiredTokenException() { + void 만료된_토큰을_검증하면_ExpiredTokenException을_던진다() { String expiredToken = Jwts.builder() .setSubject("1") .setIssuedAt(new Date(System.currentTimeMillis() - 2000)) @@ -70,12 +60,10 @@ void parseToken_expiredToken_throwsExpiredTokenException() { } @Test - @DisplayName("액세스 토큰과 리프레시 토큰의 만료 시간(1시간 뒤, 2주 뒤)을 검증한다.") - void validateTokenExpiryTimes() { + void 액세스_토큰과_리프레시_토큰의_만료_시간을_검증한다() { // given - Long memberId = 1L; - String accessToken = jwtTokenProvider.generateAccessToken(memberId); - String refreshToken = jwtTokenProvider.generateRefreshToken(memberId); + String accessToken = jwtTokenProvider.generateAccessToken(1L); + String refreshToken = jwtTokenProvider.generateRefreshToken(1L); // when LocalDateTime accessTokenExpireTime = jwtTokenProvider.getTokenExpireTime(accessToken); diff --git a/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java b/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java index 1284e49..9fc1321 100644 --- a/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java +++ b/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java @@ -4,18 +4,15 @@ import com.hyun.udong.member.domain.SocialType; import com.hyun.udong.member.exception.MemberNotFoundException; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertThrows; @SpringBootTest -@Transactional class MemberServiceTest { @Autowired @@ -28,8 +25,7 @@ void setUp() { } @Test - @DisplayName("신규 사용자일 경우 사용자 정보를 저장한다.") - void saveMember() { + void 신규_사용자일_경우_사용자_정보를_저장한다() { Member member = new Member(3L, SocialType.KAKAO, "신영만", "https://user3.com"); Member savedMember = memberService.save(member); @@ -42,8 +38,7 @@ void saveMember() { } @Test - @DisplayName("기존 사용자일 경우 사용자 정보를 반환한다.") - void updateMember() { + void 기존_사용자일_경우_사용자_정보를_반환한다() { Member member = new Member(2L, SocialType.KAKAO, "짱아", "https://user2.com"); Member savedMember = memberService.save(member); @@ -56,8 +51,7 @@ void updateMember() { } @Test - @DisplayName("id로 사용자 정보를 조회했을 때 없는 사용자일 경우 예외를 발생시킨다.") - void getMemberByIdWithNotExists() { + void id로_사용자_정보를_조회했을_때_없는_사용자일_경우_예외를_발생시킨다() { assertThrows(MemberNotFoundException.class, () -> memberService.findById(100L)); } } diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index 796f21d..295a6f8 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -9,7 +9,6 @@ import com.hyun.udong.travelschedule.exception.TravelScheduleNotFoundException; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -42,9 +41,8 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { return new TravelScheduleRequest(LocalDate.now(), LocalDate.now().plusDays(5), List.of(cityIds)); } - @DisplayName("유효한 회원 ID와 유효한 요청으로 여행 일정을 등록한다.") @Test - void updateTravelSchedule_validMemberAndRequest() { + void 유효한_회원_ID와_유효한_요청으로_여행_일정을_등록한다() { // given TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); @@ -58,9 +56,8 @@ void updateTravelSchedule_validMemberAndRequest() { then(travelSchedule.getTravelScheduleCities().get(1).getCity().getName()).isEqualTo("Busan"); } - @DisplayName("존재하지 않는 회원 ID로 여행 일정을 등록할 때 예외가 발생한다.") @Test - void updateTravelSchedule_invalidMemberId() { + void 존재하지_않는_회원_ID로_여행_일정을_등록할_때_예외가_발생한다() { // given Long memberId = 999L; TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); @@ -70,9 +67,8 @@ void updateTravelSchedule_invalidMemberId() { .isInstanceOf(MemberNotFoundException.class); } - @DisplayName("존재하지 않는 도시 ID로 여행 일정을 등록할 때 예외가 발생한다.") @Test - void updateTravelSchedule_invalidCityId() { + void 존재하지_않는_도시_ID로_여행_일정을_등록할_때_예외가_발생한다() { // given Long memberId = 1L; TravelScheduleRequest request = createTravelScheduleRequest(1L, 999L); @@ -84,10 +80,9 @@ void updateTravelSchedule_invalidCityId() { .isInstanceOf(CityNotFoundException.class); } - @DisplayName("유효한 회원 ID로 여행 일정을 조회한다.") @Test @Transactional - void findTravelSchedule_ok() { + void 유효한_회원_ID로_여행_일정을_조회한다() { // given Long memberId = 1L; TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); @@ -105,9 +100,8 @@ void findTravelSchedule_ok() { then(travelSchedule.getTravelScheduleCities()).hasSize(request.getCityIds().size()); } - @DisplayName("존재하지 않는 회원 ID로 여행 일정을 조회할 때 예외가 발생한다.") @Test - void findTravelSchedule_noMember_throw() { + void 존재하지_않는_회원_ID로_여행_일정을_조회할_때_예외가_발생한다() { // given Long memberId = 999L; @@ -116,9 +110,8 @@ void findTravelSchedule_noMember_throw() { .isInstanceOf(MemberNotFoundException.class); } - @DisplayName("회원의 여행 일정이 없을 때 예외가 발생한다.") @Test - void findTravelSchedule_noTravelSchedule_throw() { + void 회원의_여행_일정이_없을_때_예외가_발생한다() { // when & then thenThrownBy(() -> travelScheduleService.findTravelSchedule(FIRST_MEMBER_ID)) .isInstanceOf(TravelScheduleNotFoundException.class); diff --git a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java index 5bd519a..4a4e8d3 100644 --- a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java @@ -29,7 +29,7 @@ void setUp() { } @Test - void updateTravelSchedule_ReturnsOk() { + void 여행_일정_업데이트_성공시_OK_반환한다() { TravelScheduleRequest request = new TravelScheduleRequest( LocalDate.of(2025, 1, 25), LocalDate.of(2025, 2, 10), @@ -53,7 +53,7 @@ void updateTravelSchedule_ReturnsOk() { } @Test - void updateTravelSchedule_WithNullRequest_ReturnsBadRequest() { + void null_요청으로_여행_일정_업데이트시_BAD_REQUEST_반환한다() { RestAssured .given().log().all() .contentType(ContentType.JSON) From 33616087e9f1f216a9126d09aa748004841571d0 Mon Sep 17 00:00:00 2001 From: hyn Date: Thu, 30 Jan 2025 00:04:50 +0900 Subject: [PATCH 24/34] =?UTF-8?q?chore:=20=ED=86=A0=ED=81=B0=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EA=B4=80=EB=A0=A8=20=EC=9E=84=EC=9D=98=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EB=B3=80=EC=88=98=EB=A1=9C=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../udong/auth/util/JwtTokenProviderTest.java | 18 ------------------ src/test/resources/application.yml | 4 ++-- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/test/java/com/hyun/udong/auth/util/JwtTokenProviderTest.java b/src/test/java/com/hyun/udong/auth/util/JwtTokenProviderTest.java index c9e3b03..a97f875 100644 --- a/src/test/java/com/hyun/udong/auth/util/JwtTokenProviderTest.java +++ b/src/test/java/com/hyun/udong/auth/util/JwtTokenProviderTest.java @@ -9,7 +9,6 @@ import org.springframework.boot.test.context.SpringBootTest; import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; import java.util.Date; import static org.junit.jupiter.api.Assertions.*; @@ -58,21 +57,4 @@ class JwtTokenProviderTest { assertThrows(ExpiredTokenException.class, () -> jwtTokenProvider.getSubjectFromToken(expiredToken)); } - - @Test - void 액세스_토큰과_리프레시_토큰의_만료_시간을_검증한다() { - // given - String accessToken = jwtTokenProvider.generateAccessToken(1L); - String refreshToken = jwtTokenProvider.generateRefreshToken(1L); - - // when - LocalDateTime accessTokenExpireTime = jwtTokenProvider.getTokenExpireTime(accessToken); - LocalDateTime refreshTokenExpireTime = jwtTokenProvider.getTokenExpireTime(refreshToken); - - // then - LocalDateTime now = LocalDateTime.now(); - assertEquals(now.plusHours(1).withNano(0), accessTokenExpireTime.withNano(0)); - assertEquals(now.plusDays(14).withNano(0), refreshTokenExpireTime.withNano(0)); - } - } diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index e32fea3..e0c43a5 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -27,6 +27,6 @@ social: jwt: secret: udongudongudongudongudongudongudong123 - access-token-expire-time: 3600000 - refresh-token-expire-time: 1209600000 + access-token-expire-time: 123123 + refresh-token-expire-time: 123123 From 4a02c5f471c4b9474ce617d68f5f3adbd1a79417 Mon Sep 17 00:00:00 2001 From: hyn Date: Thu, 30 Jan 2025 01:01:31 +0900 Subject: [PATCH 25/34] =?UTF-8?q?refactor:=20~~NotFoundException=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/application/service/AuthService.java | 4 +-- .../udong/common/exception/ErrorCode.java | 7 ++++- .../common/exception/NotFoundException.java | 7 +++++ .../common/exception/UdongException.java | 5 ++++ .../application/service/MemberService.java | 4 +-- .../exception/MemberNotFoundException.java | 12 --------- .../service/TravelScheduleService.java | 12 ++++----- .../exception/CityNotFoundException.java | 12 --------- .../TravelScheduleNotFoundException.java | 12 --------- .../service/MemberServiceTest.java | 8 +++--- .../service/TravelScheduleServiceTest.java | 26 ++++++++++++------- 11 files changed, 48 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/hyun/udong/common/exception/NotFoundException.java delete mode 100644 src/main/java/com/hyun/udong/member/exception/MemberNotFoundException.java delete mode 100644 src/main/java/com/hyun/udong/travelschedule/exception/CityNotFoundException.java delete mode 100644 src/main/java/com/hyun/udong/travelschedule/exception/TravelScheduleNotFoundException.java diff --git a/src/main/java/com/hyun/udong/auth/application/service/AuthService.java b/src/main/java/com/hyun/udong/auth/application/service/AuthService.java index e94d892..8a87bd6 100644 --- a/src/main/java/com/hyun/udong/auth/application/service/AuthService.java +++ b/src/main/java/com/hyun/udong/auth/application/service/AuthService.java @@ -7,10 +7,10 @@ import com.hyun.udong.auth.presentation.dto.KakaoTokenResponse; import com.hyun.udong.auth.presentation.dto.LoginResponse; import com.hyun.udong.auth.util.JwtTokenProvider; +import com.hyun.udong.common.exception.NotFoundException; import com.hyun.udong.member.application.service.MemberService; import com.hyun.udong.member.domain.Member; import com.hyun.udong.member.domain.SocialType; -import com.hyun.udong.member.exception.MemberNotFoundException; import com.hyun.udong.member.infrastructure.repository.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -80,7 +80,7 @@ private void validateIsTokenOwner(String refreshToken, Long memberId) { private Member updateRefreshToken(Long memberId, String refreshToken) { Member member = memberRepository.findById(memberId) - .orElseThrow(() -> MemberNotFoundException.EXCEPTION); + .orElseThrow(() -> new NotFoundException("해당하는 회원이 존재하지 않습니다.")); member.updateRefreshToken(refreshToken); return member; } diff --git a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java index 8905a7d..3f9872c 100644 --- a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java +++ b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java @@ -13,11 +13,16 @@ public enum ErrorCode { TOKEN_EXPIRED(UNAUTHORIZED, "만료된 토큰입니다."), UNAUTHENTICATED_MEMBER(UNAUTHORIZED, "인증되지 않은 회원입니다."), CITY_NOT_FOUND(BAD_REQUEST, "해당하는 도시가 없습니다."), - MEMBER_TRAVEL_SCHEDULE_NOT_FOUND(BAD_REQUEST, "해당 회원의 여행 일정이 존재하지 않습니다."); + MEMBER_TRAVEL_SCHEDULE_NOT_FOUND(BAD_REQUEST, "해당 회원의 여행 일정이 존재하지 않습니다."), + NOT_FOUND(BAD_REQUEST); private final HttpStatus status; private final String message; + ErrorCode(HttpStatus status) { + this(status, null); + } + ErrorCode(HttpStatus status, String message) { this.status = status; this.message = message; diff --git a/src/main/java/com/hyun/udong/common/exception/NotFoundException.java b/src/main/java/com/hyun/udong/common/exception/NotFoundException.java new file mode 100644 index 0000000..ea32a2d --- /dev/null +++ b/src/main/java/com/hyun/udong/common/exception/NotFoundException.java @@ -0,0 +1,7 @@ +package com.hyun.udong.common.exception; + +public class NotFoundException extends UdongException { + public NotFoundException(String message) { + super(ErrorCode.MEMBER_TRAVEL_SCHEDULE_NOT_FOUND, message); + } +} diff --git a/src/main/java/com/hyun/udong/common/exception/UdongException.java b/src/main/java/com/hyun/udong/common/exception/UdongException.java index 73da0ac..70dbee3 100644 --- a/src/main/java/com/hyun/udong/common/exception/UdongException.java +++ b/src/main/java/com/hyun/udong/common/exception/UdongException.java @@ -10,6 +10,11 @@ public UdongException(ErrorCode errorCode) { this.errorCode = errorCode; } + public UdongException(ErrorCode errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + public ErrorCode getErrorCode() { return errorCode; } diff --git a/src/main/java/com/hyun/udong/member/application/service/MemberService.java b/src/main/java/com/hyun/udong/member/application/service/MemberService.java index 8d429d2..3410fa4 100644 --- a/src/main/java/com/hyun/udong/member/application/service/MemberService.java +++ b/src/main/java/com/hyun/udong/member/application/service/MemberService.java @@ -1,8 +1,8 @@ package com.hyun.udong.member.application.service; +import com.hyun.udong.common.exception.NotFoundException; import com.hyun.udong.member.domain.Member; import com.hyun.udong.member.domain.SocialType; -import com.hyun.udong.member.exception.MemberNotFoundException; import com.hyun.udong.member.infrastructure.repository.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -28,7 +28,7 @@ public Optional findBySocialIdAndSocialType(Long socialId, SocialType so public Member findById(Long id) { return memberRepository.findById(id) - .orElseThrow(() -> MemberNotFoundException.EXCEPTION); + .orElseThrow(() -> new NotFoundException("해당 회원이 존재하지 않습니다.")); } } diff --git a/src/main/java/com/hyun/udong/member/exception/MemberNotFoundException.java b/src/main/java/com/hyun/udong/member/exception/MemberNotFoundException.java deleted file mode 100644 index 9267886..0000000 --- a/src/main/java/com/hyun/udong/member/exception/MemberNotFoundException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.hyun.udong.member.exception; - -import com.hyun.udong.common.exception.ErrorCode; -import com.hyun.udong.common.exception.UdongException; - -public class MemberNotFoundException extends UdongException { - public static final UdongException EXCEPTION = new MemberNotFoundException(); - - private MemberNotFoundException() { - super(ErrorCode.MEMBER_NOT_FOUND); - } -} diff --git a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java index 2a1f34d..1c0b7c1 100644 --- a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java +++ b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java @@ -1,13 +1,11 @@ package com.hyun.udong.travelschedule.application.service; +import com.hyun.udong.common.exception.NotFoundException; import com.hyun.udong.member.domain.Member; -import com.hyun.udong.member.exception.MemberNotFoundException; import com.hyun.udong.member.infrastructure.repository.MemberRepository; import com.hyun.udong.travelschedule.domain.City; import com.hyun.udong.travelschedule.domain.TravelSchedule; import com.hyun.udong.travelschedule.domain.TravelScheduleCity; -import com.hyun.udong.travelschedule.exception.CityNotFoundException; -import com.hyun.udong.travelschedule.exception.TravelScheduleNotFoundException; import com.hyun.udong.travelschedule.infrastructure.repository.CityRepository; import com.hyun.udong.travelschedule.infrastructure.repository.TravelScheduleRepository; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; @@ -29,7 +27,7 @@ public class TravelScheduleService { @Transactional public TravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRequest request) { Member member = memberRepository.findById(memberId) - .orElseThrow(() -> MemberNotFoundException.EXCEPTION); + .orElseThrow(() -> new NotFoundException("해당 회원이 존재하지 않습니다.")); TravelSchedule travelSchedule = TravelSchedule.builder() .startDate(request.getStartDate()) @@ -38,7 +36,7 @@ public TravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRequest List cities = cityRepository.findAllById(request.getCityIds()); if (cities.size() != request.getCityIds().size()) { - throw CityNotFoundException.EXCEPTION; + throw new NotFoundException("해당 도시가 존재하지 않습니다."); } List travelScheduleCities = cities.stream() .map(city -> new TravelScheduleCity(travelSchedule, city)) @@ -53,11 +51,11 @@ public TravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRequest public TravelSchedule findTravelSchedule(Long memberId) { Member member = memberRepository.findById(memberId) - .orElseThrow(() -> MemberNotFoundException.EXCEPTION); + .orElseThrow(() -> new NotFoundException("해당 회원이 존재하지 않습니다.")); TravelSchedule travelSchedule = member.getTravelSchedule(); if (travelSchedule == null) { - throw TravelScheduleNotFoundException.EXCEPTION; + throw new NotFoundException("여행 일정이 존재하지 않습니다."); } return travelSchedule; } diff --git a/src/main/java/com/hyun/udong/travelschedule/exception/CityNotFoundException.java b/src/main/java/com/hyun/udong/travelschedule/exception/CityNotFoundException.java deleted file mode 100644 index 43d12c5..0000000 --- a/src/main/java/com/hyun/udong/travelschedule/exception/CityNotFoundException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.hyun.udong.travelschedule.exception; - -import com.hyun.udong.common.exception.ErrorCode; -import com.hyun.udong.common.exception.UdongException; - -public class CityNotFoundException extends UdongException { - public static final CityNotFoundException EXCEPTION = new CityNotFoundException(); - - private CityNotFoundException() { - super(ErrorCode.CITY_NOT_FOUND); - } -} diff --git a/src/main/java/com/hyun/udong/travelschedule/exception/TravelScheduleNotFoundException.java b/src/main/java/com/hyun/udong/travelschedule/exception/TravelScheduleNotFoundException.java deleted file mode 100644 index 1c15832..0000000 --- a/src/main/java/com/hyun/udong/travelschedule/exception/TravelScheduleNotFoundException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.hyun.udong.travelschedule.exception; - -import com.hyun.udong.common.exception.ErrorCode; -import com.hyun.udong.common.exception.UdongException; - -public class TravelScheduleNotFoundException extends UdongException { - public static final TravelScheduleNotFoundException EXCEPTION = new TravelScheduleNotFoundException(); - - private TravelScheduleNotFoundException() { - super(ErrorCode.MEMBER_TRAVEL_SCHEDULE_NOT_FOUND); - } -} diff --git a/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java b/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java index 9fc1321..7a2f181 100644 --- a/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java +++ b/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java @@ -1,16 +1,16 @@ package com.hyun.udong.member.application.service; +import com.hyun.udong.common.exception.NotFoundException; import com.hyun.udong.member.domain.Member; import com.hyun.udong.member.domain.SocialType; -import com.hyun.udong.member.exception.MemberNotFoundException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertThrows; @SpringBootTest class MemberServiceTest { @@ -52,6 +52,8 @@ void setUp() { @Test void id로_사용자_정보를_조회했을_때_없는_사용자일_경우_예외를_발생시킨다() { - assertThrows(MemberNotFoundException.class, () -> memberService.findById(100L)); + assertThatThrownBy(() -> memberService.findById(100L)) + .isInstanceOf(NotFoundException.class) + .hasMessage("해당 회원이 존재하지 않습니다."); } } diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index 295a6f8..1705da2 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -1,12 +1,10 @@ package com.hyun.udong.travelschedule.application.service; +import com.hyun.udong.common.exception.NotFoundException; import com.hyun.udong.member.domain.Member; import com.hyun.udong.member.domain.SocialType; -import com.hyun.udong.member.exception.MemberNotFoundException; import com.hyun.udong.member.infrastructure.repository.MemberRepository; import com.hyun.udong.travelschedule.domain.TravelSchedule; -import com.hyun.udong.travelschedule.exception.CityNotFoundException; -import com.hyun.udong.travelschedule.exception.TravelScheduleNotFoundException; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -64,7 +62,8 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { // when & then thenThrownBy(() -> travelScheduleService.updateTravelSchedule(memberId, request)) - .isInstanceOf(MemberNotFoundException.class); + .isInstanceOf(NotFoundException.class) + .hasMessage("해당 회원이 존재하지 않습니다."); } @Test @@ -76,8 +75,9 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { memberRepository.save(member); // when & then - thenThrownBy(() -> travelScheduleService.updateTravelSchedule(memberId, request)) - .isInstanceOf(CityNotFoundException.class); + thenThrownBy(() -> travelScheduleService.updateTravelSchedule(FIRST_MEMBER_ID, request)) + .isInstanceOf(NotFoundException.class) + .hasMessage("해당 도시가 존재하지 않습니다."); } @Test @@ -90,7 +90,7 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { // when Member member = memberRepository.findById(memberId) - .orElseThrow(() -> MemberNotFoundException.EXCEPTION); + .orElseThrow(() -> new NotFoundException("해당 회원이 존재하지 않습니다.")); TravelSchedule travelSchedule = member.getTravelSchedule(); // then @@ -107,13 +107,19 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { // when & then thenThrownBy(() -> travelScheduleService.findTravelSchedule(memberId)) - .isInstanceOf(MemberNotFoundException.class); + .isInstanceOf(NotFoundException.class) + .hasMessage("해당 회원이 존재하지 않습니다."); } @Test void 회원의_여행_일정이_없을_때_예외가_발생한다() { + // given + Member member = new Member(2L, SocialType.KAKAO, "dong", "profile_image"); + memberRepository.save(member); + // when & then - thenThrownBy(() -> travelScheduleService.findTravelSchedule(FIRST_MEMBER_ID)) - .isInstanceOf(TravelScheduleNotFoundException.class); + thenThrownBy(() -> travelScheduleService.findTravelSchedule(member.getId())) + .isInstanceOf(NotFoundException.class) + .hasMessage("여행 일정이 존재하지 않습니다."); } } From fa4a5a59f41a8ecd8e359a63d124d5aa09bf8a18 Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 31 Jan 2025 01:19:25 +0900 Subject: [PATCH 26/34] =?UTF-8?q?refactor:=20=ED=86=A0=ED=81=B0=EC=97=90?= =?UTF-8?q?=EC=84=9C=20member=EC=B6=94=EC=B6=9C=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hyun/udong/auth/application/service/AuthService.java | 2 +- .../presentation/resolver/AuthenticationArgumentResolver.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hyun/udong/auth/application/service/AuthService.java b/src/main/java/com/hyun/udong/auth/application/service/AuthService.java index 8a87bd6..13aed34 100644 --- a/src/main/java/com/hyun/udong/auth/application/service/AuthService.java +++ b/src/main/java/com/hyun/udong/auth/application/service/AuthService.java @@ -85,7 +85,7 @@ private Member updateRefreshToken(Long memberId, String refreshToken) { return member; } - public Member getMemberFromToken(String token) { + public Member findMemberFromToken(String token) { Long memberId = Long.parseLong(jwtTokenProvider.getSubjectFromToken(token)); return memberRepository.findById(memberId) .orElseThrow(() -> InvalidTokenException.EXCEPTION); diff --git a/src/main/java/com/hyun/udong/auth/presentation/resolver/AuthenticationArgumentResolver.java b/src/main/java/com/hyun/udong/auth/presentation/resolver/AuthenticationArgumentResolver.java index 7959a29..c07870d 100644 --- a/src/main/java/com/hyun/udong/auth/presentation/resolver/AuthenticationArgumentResolver.java +++ b/src/main/java/com/hyun/udong/auth/presentation/resolver/AuthenticationArgumentResolver.java @@ -39,7 +39,7 @@ public Object resolveArgument(MethodParameter parameter, isValidToken(token); - return authService.getMemberFromToken(token.substring(PREFIX.length())); + return authService.findMemberFromToken(token.substring(PREFIX.length())); } private void isValidToken(String token) { From 4fa5360dc5de6900cb4e3fb4fa221da0e0eb6c0f Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 31 Jan 2025 01:22:02 +0900 Subject: [PATCH 27/34] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=97=90=EB=9F=AC=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/hyun/udong/common/exception/ErrorCode.java | 3 --- .../com/hyun/udong/common/exception/NotFoundException.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java index 3f9872c..8620681 100644 --- a/src/main/java/com/hyun/udong/common/exception/ErrorCode.java +++ b/src/main/java/com/hyun/udong/common/exception/ErrorCode.java @@ -8,12 +8,9 @@ @Getter public enum ErrorCode { - MEMBER_NOT_FOUND(BAD_REQUEST, "해당하는 회원이 없습니다."), INVALID_TOKEN(UNAUTHORIZED, "유효하지 않은 토큰입니다."), TOKEN_EXPIRED(UNAUTHORIZED, "만료된 토큰입니다."), UNAUTHENTICATED_MEMBER(UNAUTHORIZED, "인증되지 않은 회원입니다."), - CITY_NOT_FOUND(BAD_REQUEST, "해당하는 도시가 없습니다."), - MEMBER_TRAVEL_SCHEDULE_NOT_FOUND(BAD_REQUEST, "해당 회원의 여행 일정이 존재하지 않습니다."), NOT_FOUND(BAD_REQUEST); private final HttpStatus status; diff --git a/src/main/java/com/hyun/udong/common/exception/NotFoundException.java b/src/main/java/com/hyun/udong/common/exception/NotFoundException.java index ea32a2d..bdd3c2c 100644 --- a/src/main/java/com/hyun/udong/common/exception/NotFoundException.java +++ b/src/main/java/com/hyun/udong/common/exception/NotFoundException.java @@ -2,6 +2,6 @@ public class NotFoundException extends UdongException { public NotFoundException(String message) { - super(ErrorCode.MEMBER_TRAVEL_SCHEDULE_NOT_FOUND, message); + super(ErrorCode.NOT_FOUND, message); } } From 105b7a3fec7e1174ef68ce3af201ec6337a8950b Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 31 Jan 2025 01:43:08 +0900 Subject: [PATCH 28/34] =?UTF-8?q?refactor:=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EB=8F=84=EC=8B=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=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 --- .../application/service/TravelScheduleService.java | 2 +- .../com/hyun/udong/travelschedule/domain/TravelSchedule.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java index 1c0b7c1..c6ca8a0 100644 --- a/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java +++ b/src/main/java/com/hyun/udong/travelschedule/application/service/TravelScheduleService.java @@ -41,7 +41,7 @@ public TravelSchedule updateTravelSchedule(Long memberId, TravelScheduleRequest List travelScheduleCities = cities.stream() .map(city -> new TravelScheduleCity(travelSchedule, city)) .toList(); - travelSchedule.addTravelScheduleCities(travelScheduleCities); + travelSchedule.updateTravelScheduleCities(travelScheduleCities); travelScheduleRepository.save(travelSchedule); member.updateTravelSchedule(travelSchedule); diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java b/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java index 38410cc..1d41de4 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/TravelSchedule.java @@ -35,7 +35,7 @@ public TravelSchedule(LocalDate startDate, LocalDate endDate) { this.endDate = endDate; } - public void addTravelScheduleCities(List travelScheduleCities) { + public void updateTravelScheduleCities(List travelScheduleCities) { this.travelScheduleCities = travelScheduleCities; } } From 5686450c980548ef14ceebe0d1516a669ae009c5 Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 31 Jan 2025 02:14:32 +0900 Subject: [PATCH 29/34] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=ED=99=98=EA=B2=BD=20h2DB=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/application.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index e0c43a5..e3b7b2a 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -1,12 +1,11 @@ spring: - config: - import: optional:file:.env[.properties] datasource: - url: jdbc:postgresql://localhost:5432/hyn_test - username: myuser - password: 1234 + url: jdbc:h2:mem:testdb;MODE=PostgreSQL; + driver-class-name: org.h2.Driver + username: sa + password: jpa: - database-platform: org.hibernate.dialect.PostgreSQLDialect + database-platform: org.hibernate.dialect.H2Dialect hibernate: ddl-auto: create show-sql: true From ccafaccb02eed1bacb78b9313c542d8492d6c03f Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 31 Jan 2025 04:57:18 +0900 Subject: [PATCH 30/34] =?UTF-8?q?refactor:=20Member=20TestFixture=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9,=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=B4=88=EA=B8=B0=ED=99=94=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/TravelScheduleCity.java | 1 + .../application/service/AuthServiceTest.java | 3 ++ .../controller/AuthControllerTest.java | 3 ++ .../udong/common/fixture/TestFixture.java | 8 +++ .../hyun/udong/common/util/DataCleaner.java | 49 +++++++++++++++++++ .../common/util/DataCleanerExtension.java | 18 +++++++ .../service/MemberServiceTest.java | 3 ++ .../service/TravelScheduleServiceTest.java | 3 ++ .../TravelScheduleControllerTest.java | 12 ++++- src/test/resources/member.sql | 3 -- 10 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/hyun/udong/common/fixture/TestFixture.java create mode 100644 src/test/java/com/hyun/udong/common/util/DataCleaner.java create mode 100644 src/test/java/com/hyun/udong/common/util/DataCleanerExtension.java delete mode 100644 src/test/resources/member.sql diff --git a/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java b/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java index 1bfbf4c..8ba167a 100644 --- a/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java +++ b/src/main/java/com/hyun/udong/travelschedule/domain/TravelScheduleCity.java @@ -14,6 +14,7 @@ public class TravelScheduleCity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "travel_schedule_city_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) diff --git a/src/test/java/com/hyun/udong/auth/application/service/AuthServiceTest.java b/src/test/java/com/hyun/udong/auth/application/service/AuthServiceTest.java index 3c67a9b..a4df2ac 100644 --- a/src/test/java/com/hyun/udong/auth/application/service/AuthServiceTest.java +++ b/src/test/java/com/hyun/udong/auth/application/service/AuthServiceTest.java @@ -6,11 +6,13 @@ import com.hyun.udong.auth.presentation.dto.KakaoTokenResponse; import com.hyun.udong.auth.presentation.dto.LoginResponse; import com.hyun.udong.auth.util.JwtTokenProvider; +import com.hyun.udong.common.util.DataCleanerExtension; import com.hyun.udong.member.application.service.MemberService; import com.hyun.udong.member.domain.Member; import com.hyun.udong.member.domain.SocialType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.bean.override.mockito.MockitoBean; @@ -19,6 +21,7 @@ import static org.assertj.core.api.BDDAssertions.thenThrownBy; import static org.mockito.BDDMockito.given; +@ExtendWith(DataCleanerExtension.class) @SpringBootTest class AuthServiceTest { diff --git a/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java b/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java index c75e3e2..8f25f4e 100644 --- a/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java +++ b/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java @@ -4,6 +4,7 @@ import com.hyun.udong.auth.presentation.dto.KakaoProfileResponse; import com.hyun.udong.auth.presentation.dto.KakaoTokenResponse; import com.hyun.udong.auth.util.JwtTokenProvider; +import com.hyun.udong.common.util.DataCleanerExtension; import com.hyun.udong.member.application.service.MemberService; import com.hyun.udong.member.domain.Member; import com.hyun.udong.member.domain.SocialType; @@ -11,6 +12,7 @@ import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; @@ -21,6 +23,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; +@ExtendWith(DataCleanerExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class AuthControllerTest { diff --git a/src/test/java/com/hyun/udong/common/fixture/TestFixture.java b/src/test/java/com/hyun/udong/common/fixture/TestFixture.java new file mode 100644 index 0000000..4317311 --- /dev/null +++ b/src/test/java/com/hyun/udong/common/fixture/TestFixture.java @@ -0,0 +1,8 @@ +package com.hyun.udong.common.fixture; + +import com.hyun.udong.member.domain.Member; +import com.hyun.udong.member.domain.SocialType; + +public class TestFixture { + public static final Member HYUN = new Member(1L, SocialType.KAKAO, "hyun", "profile_image"); +} diff --git a/src/test/java/com/hyun/udong/common/util/DataCleaner.java b/src/test/java/com/hyun/udong/common/util/DataCleaner.java new file mode 100644 index 0000000..68161e5 --- /dev/null +++ b/src/test/java/com/hyun/udong/common/util/DataCleaner.java @@ -0,0 +1,49 @@ +package com.hyun.udong.common.util; + +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class DataCleaner { + + private static final String FIND_TABLES_QUERY = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'PUBLIC' AND table_name NOT IN ('CITY', 'COUNTRY')"; + + private final List tableNames = new ArrayList<>(); + + @PersistenceContext + private EntityManager entityManager; + + @SuppressWarnings("unchecked") + @PostConstruct + public void findDatabaseTableNames() { + List tableInfos = entityManager.createNativeQuery(FIND_TABLES_QUERY).getResultList(); + for (Object tableInfo : tableInfos) { + tableNames.add((String) tableInfo); + } + } + + @Transactional + public void clear() { + entityManager.clear(); + truncate(); + } + + public void truncate() { + entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY FALSE").executeUpdate(); + for (String tableName : tableNames) { + entityManager.createNativeQuery("TRUNCATE TABLE " + tableName).executeUpdate(); + entityManager.createNativeQuery("ALTER TABLE " + tableName + " ALTER COLUMN " + getIdColumnName(tableName) + " RESTART WITH 1").executeUpdate(); + } + entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY TRUE").executeUpdate(); + } + + private String getIdColumnName(String tableName) { + return tableName.toUpperCase() + "_ID"; + } +} diff --git a/src/test/java/com/hyun/udong/common/util/DataCleanerExtension.java b/src/test/java/com/hyun/udong/common/util/DataCleanerExtension.java new file mode 100644 index 0000000..c3ed61f --- /dev/null +++ b/src/test/java/com/hyun/udong/common/util/DataCleanerExtension.java @@ -0,0 +1,18 @@ +package com.hyun.udong.common.util; + +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +public class DataCleanerExtension implements BeforeEachCallback { + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + DataCleaner dataCleaner = getDataCleaner(context); + dataCleaner.clear(); + } + + private DataCleaner getDataCleaner(ExtensionContext context) { + return SpringExtension.getApplicationContext(context).getBean(DataCleaner.class); + } +} diff --git a/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java b/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java index 7a2f181..84b466c 100644 --- a/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java +++ b/src/test/java/com/hyun/udong/member/application/service/MemberServiceTest.java @@ -1,10 +1,12 @@ package com.hyun.udong.member.application.service; import com.hyun.udong.common.exception.NotFoundException; +import com.hyun.udong.common.util.DataCleanerExtension; import com.hyun.udong.member.domain.Member; import com.hyun.udong.member.domain.SocialType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -12,6 +14,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; +@ExtendWith(DataCleanerExtension.class) @SpringBootTest class MemberServiceTest { diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index 1705da2..e3a2666 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -1,6 +1,7 @@ package com.hyun.udong.travelschedule.application.service; import com.hyun.udong.common.exception.NotFoundException; +import com.hyun.udong.common.util.DataCleanerExtension; import com.hyun.udong.member.domain.Member; import com.hyun.udong.member.domain.SocialType; import com.hyun.udong.member.infrastructure.repository.MemberRepository; @@ -8,6 +9,7 @@ import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; @@ -18,6 +20,7 @@ import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.api.BDDAssertions.thenThrownBy; +@ExtendWith(DataCleanerExtension.class) @SpringBootTest class TravelScheduleServiceTest { diff --git a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java index 4a4e8d3..7ff2c4f 100644 --- a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java @@ -1,31 +1,39 @@ package com.hyun.udong.travelschedule.presentation.controller; import com.hyun.udong.auth.oauth.TestOauth; +import com.hyun.udong.common.fixture.TestFixture; +import com.hyun.udong.common.util.DataCleanerExtension; +import com.hyun.udong.member.infrastructure.repository.MemberRepository; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpStatus; -import org.springframework.test.context.jdbc.Sql; import java.time.LocalDate; import java.util.List; import static org.hamcrest.Matchers.equalTo; +@ExtendWith(DataCleanerExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@Sql("/member.sql") class TravelScheduleControllerTest { @LocalServerPort private int port; + @Autowired + private MemberRepository memberRepository; + @BeforeEach void setUp() { RestAssured.port = port; + memberRepository.save(TestFixture.HYUN); } @Test diff --git a/src/test/resources/member.sql b/src/test/resources/member.sql deleted file mode 100644 index 8ccc028..0000000 --- a/src/test/resources/member.sql +++ /dev/null @@ -1,3 +0,0 @@ -INSERT INTO member (member_id, social_id, social_type, nickname, age, profile_image_url) -VALUES (1, 1, 'KAKAO', 'hyun', 25, 'profile_image') -ON CONFLICT (member_id) DO NOTHING; From 0865b99851b467d9e7e2106814532a54a92750d0 Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 31 Jan 2025 04:58:18 +0900 Subject: [PATCH 31/34] =?UTF-8?q?chore:=20TestOauth=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hyun/udong/{auth/oauth => common/fixture}/TestOauth.java | 2 +- .../presentation/controller/TravelScheduleControllerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/test/java/com/hyun/udong/{auth/oauth => common/fixture}/TestOauth.java (92%) diff --git a/src/test/java/com/hyun/udong/auth/oauth/TestOauth.java b/src/test/java/com/hyun/udong/common/fixture/TestOauth.java similarity index 92% rename from src/test/java/com/hyun/udong/auth/oauth/TestOauth.java rename to src/test/java/com/hyun/udong/common/fixture/TestOauth.java index 370b217..c4120b1 100644 --- a/src/test/java/com/hyun/udong/auth/oauth/TestOauth.java +++ b/src/test/java/com/hyun/udong/common/fixture/TestOauth.java @@ -1,4 +1,4 @@ -package com.hyun.udong.auth.oauth; +package com.hyun.udong.common.fixture; import com.hyun.udong.auth.util.JwtTokenProvider; import jakarta.annotation.PostConstruct; diff --git a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java index 7ff2c4f..07a43ed 100644 --- a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java @@ -1,7 +1,7 @@ package com.hyun.udong.travelschedule.presentation.controller; -import com.hyun.udong.auth.oauth.TestOauth; import com.hyun.udong.common.fixture.TestFixture; +import com.hyun.udong.common.fixture.TestOauth; import com.hyun.udong.common.util.DataCleanerExtension; import com.hyun.udong.member.infrastructure.repository.MemberRepository; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; From 73255cd5afc5dc21c27a869a4111ab524f2ac45c Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 31 Jan 2025 05:10:11 +0900 Subject: [PATCH 32/34] =?UTF-8?q?refactor:=20=EB=A9=A4=EB=B2=84=20id=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EC=82=AC=EC=9A=A9=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthControllerTest.java | 2 -- .../service/TravelScheduleServiceTest.java | 25 +++++++------------ 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java b/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java index 8f25f4e..f273681 100644 --- a/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java +++ b/src/test/java/com/hyun/udong/auth/presentation/controller/AuthControllerTest.java @@ -27,7 +27,6 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class AuthControllerTest { - public static final int FIRST_SAVED_MEMBER_ID = 1; @Autowired private JwtTokenProvider jwtTokenProvider; @@ -65,7 +64,6 @@ void setUp() { .then().log().all() .statusCode(200) - .body("id", equalTo(FIRST_SAVED_MEMBER_ID)) .body("nickname", equalTo(kakaoProfileResponse.getKakaoAccount().getNickname())) .body("token.accessToken", notNullValue()) .body("token.refreshToken", notNullValue()); diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index e3a2666..5383c99 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -1,9 +1,9 @@ package com.hyun.udong.travelschedule.application.service; import com.hyun.udong.common.exception.NotFoundException; +import com.hyun.udong.common.fixture.TestFixture; import com.hyun.udong.common.util.DataCleanerExtension; import com.hyun.udong.member.domain.Member; -import com.hyun.udong.member.domain.SocialType; import com.hyun.udong.member.infrastructure.repository.MemberRepository; import com.hyun.udong.travelschedule.domain.TravelSchedule; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; @@ -24,7 +24,6 @@ @SpringBootTest class TravelScheduleServiceTest { - public static final long FIRST_MEMBER_ID = 1L; @Autowired private MemberRepository memberRepository; @@ -32,10 +31,11 @@ class TravelScheduleServiceTest { @Autowired private TravelScheduleService travelScheduleService; + private Member savedMember; + @BeforeEach void setUp() { - Member member = new Member(1L, SocialType.KAKAO, "hyun", "profile_image"); - memberRepository.save(member); + savedMember = memberRepository.save(TestFixture.HYUN); } private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { @@ -48,7 +48,7 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); // when - TravelSchedule travelSchedule = travelScheduleService.updateTravelSchedule(FIRST_MEMBER_ID, request); + TravelSchedule travelSchedule = travelScheduleService.updateTravelSchedule(savedMember.getId(), request); // then then(travelSchedule).isNotNull(); @@ -72,13 +72,10 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { @Test void 존재하지_않는_도시_ID로_여행_일정을_등록할_때_예외가_발생한다() { // given - Long memberId = 1L; TravelScheduleRequest request = createTravelScheduleRequest(1L, 999L); - Member member = new Member(memberId, SocialType.KAKAO, "hyun", "profile_image"); - memberRepository.save(member); // when & then - thenThrownBy(() -> travelScheduleService.updateTravelSchedule(FIRST_MEMBER_ID, request)) + thenThrownBy(() -> travelScheduleService.updateTravelSchedule(savedMember.getId(), request)) .isInstanceOf(NotFoundException.class) .hasMessage("해당 도시가 존재하지 않습니다."); } @@ -87,9 +84,9 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { @Transactional void 유효한_회원_ID로_여행_일정을_조회한다() { // given - Long memberId = 1L; + Long memberId = savedMember.getId(); TravelScheduleRequest request = createTravelScheduleRequest(1L, 2L); - travelScheduleService.updateTravelSchedule(FIRST_MEMBER_ID, request); + travelScheduleService.updateTravelSchedule(savedMember.getId(), request); // when Member member = memberRepository.findById(memberId) @@ -116,12 +113,8 @@ private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { @Test void 회원의_여행_일정이_없을_때_예외가_발생한다() { - // given - Member member = new Member(2L, SocialType.KAKAO, "dong", "profile_image"); - memberRepository.save(member); - // when & then - thenThrownBy(() -> travelScheduleService.findTravelSchedule(member.getId())) + thenThrownBy(() -> travelScheduleService.findTravelSchedule(savedMember.getId())) .isInstanceOf(NotFoundException.class) .hasMessage("여행 일정이 존재하지 않습니다."); } From 41d769a43ffc17bb75e76dd9e8c4fd89a194e3c6 Mon Sep 17 00:00:00 2001 From: hyn Date: Fri, 31 Jan 2025 05:44:28 +0900 Subject: [PATCH 33/34] =?UTF-8?q?refactor:=20TravelScheduleResponse=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=20=EC=B1=85=EC=9E=84=20=EC=9D=B4=EB=8F=99,?= =?UTF-8?q?=20=EA=B5=AD=EA=B0=80=20=EC=9D=B4=EB=A6=84=EB=8F=84=20=EB=B0=98?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/TravelScheduleResponse.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java index 273e84e..ba0014b 100644 --- a/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java +++ b/src/main/java/com/hyun/udong/travelschedule/presentation/dto/TravelScheduleResponse.java @@ -2,12 +2,12 @@ import com.hyun.udong.common.annotation.DateFormat; import com.hyun.udong.travelschedule.domain.TravelSchedule; +import com.hyun.udong.travelschedule.domain.TravelScheduleCity; import lombok.Builder; import lombok.Getter; import java.time.LocalDate; import java.util.List; -import java.util.stream.Collectors; @Getter @Builder @@ -26,6 +26,15 @@ public class TravelScheduleResponse { public static class TravelScheduleCityResponse { private Long cityId; private String cityName; + private String countryName; + + public static TravelScheduleCityResponse from(TravelScheduleCity travelScheduleCity) { + return TravelScheduleCityResponse.builder() + .cityId(travelScheduleCity.getCity().getId()) + .cityName(travelScheduleCity.getCity().getName()) + .countryName(travelScheduleCity.getCity().getCountry().getName()) + .build(); + } } public static TravelScheduleResponse from(TravelSchedule travelSchedule) { @@ -33,11 +42,8 @@ public static TravelScheduleResponse from(TravelSchedule travelSchedule) { .startDate(travelSchedule.getStartDate()) .endDate(travelSchedule.getEndDate()) .travelScheduleCities(travelSchedule.getTravelScheduleCities().stream() - .map(city -> TravelScheduleCityResponse.builder() - .cityId(city.getCity().getId()) - .cityName(city.getCity().getName()) - .build()) - .collect(Collectors.toList())) + .map(TravelScheduleCityResponse::from) + .toList()) .build(); } } From ef6d45fdd1f9b147bac6fb48a1159d2f6d33ce22 Mon Sep 17 00:00:00 2001 From: hyn Date: Sat, 1 Feb 2025 02:34:37 +0900 Subject: [PATCH 34/34] =?UTF-8?q?refactor:=20staleObjectStateException,=20?= =?UTF-8?q?ObjectOptimisticLockingFailureException=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EB=B0=A9=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/TravelScheduleServiceTest.java | 5 ++--- .../controller/TravelScheduleControllerTest.java | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java index 5383c99..0348b34 100644 --- a/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/application/service/TravelScheduleServiceTest.java @@ -1,9 +1,9 @@ package com.hyun.udong.travelschedule.application.service; import com.hyun.udong.common.exception.NotFoundException; -import com.hyun.udong.common.fixture.TestFixture; import com.hyun.udong.common.util.DataCleanerExtension; import com.hyun.udong.member.domain.Member; +import com.hyun.udong.member.domain.SocialType; import com.hyun.udong.member.infrastructure.repository.MemberRepository; import com.hyun.udong.travelschedule.domain.TravelSchedule; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; @@ -24,7 +24,6 @@ @SpringBootTest class TravelScheduleServiceTest { - @Autowired private MemberRepository memberRepository; @@ -35,7 +34,7 @@ class TravelScheduleServiceTest { @BeforeEach void setUp() { - savedMember = memberRepository.save(TestFixture.HYUN); + savedMember = memberRepository.save(new Member(1L, SocialType.KAKAO, "짱구", "https://user1.com")); } private TravelScheduleRequest createTravelScheduleRequest(Long... cityIds) { diff --git a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java index 07a43ed..7d196c8 100644 --- a/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java +++ b/src/test/java/com/hyun/udong/travelschedule/presentation/controller/TravelScheduleControllerTest.java @@ -1,8 +1,9 @@ package com.hyun.udong.travelschedule.presentation.controller; -import com.hyun.udong.common.fixture.TestFixture; import com.hyun.udong.common.fixture.TestOauth; import com.hyun.udong.common.util.DataCleanerExtension; +import com.hyun.udong.member.domain.Member; +import com.hyun.udong.member.domain.SocialType; import com.hyun.udong.member.infrastructure.repository.MemberRepository; import com.hyun.udong.travelschedule.presentation.dto.TravelScheduleRequest; import io.restassured.RestAssured; @@ -33,7 +34,7 @@ class TravelScheduleControllerTest { @BeforeEach void setUp() { RestAssured.port = port; - memberRepository.save(TestFixture.HYUN); + memberRepository.save(new Member(1L, SocialType.KAKAO, "짱구", "https://user1.com")); } @Test