From b0d4a6520fab3f00bf53127209e8e46cf1d06c07 Mon Sep 17 00:00:00 2001 From: JonghyeokNam Date: Tue, 19 Aug 2025 02:39:41 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[refactor]=20=EC=84=A4=EB=AC=B8=EC=A1=B0?= =?UTF-8?q?=EC=82=AC=20enum=ED=98=95=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20->=20=EC=9A=B0=EC=84=A0=20=EC=97=90?= =?UTF-8?q?=EB=84=88=EC=A7=80=20=EB=A0=88=EB=B2=A8=EB=A7=8C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../with_travel/domain/member/Member.java | 5 +- .../with_travel/domain/survey/Survey.java | 66 +++-- .../survey/controller/SurveyController.java | 19 +- .../survey/dto/request/SurveyRequestDto.java | 18 +- .../dto/response/SurveyResponseDto.java | 22 +- .../domain/survey/enums/EnergyLevel.java | 37 +++ .../domain/survey/enums/SurveyEnum.java | 7 + .../survey/repository/SurveyRepository.java | 6 +- .../domain/survey/service/SurveyService.java | 46 ++-- .../global/exception/error/ErrorCode.java | 1 + .../domain/survey/domain/SurveyTest.java | 134 ++++----- .../repository/SurveyRepositoryTest.java | 86 +++--- .../survey/service/SurveyServiceTest.java | 254 +++++++++--------- 13 files changed, 398 insertions(+), 303 deletions(-) create mode 100644 src/main/java/com/arom/with_travel/domain/survey/enums/EnergyLevel.java create mode 100644 src/main/java/com/arom/with_travel/domain/survey/enums/SurveyEnum.java diff --git a/src/main/java/com/arom/with_travel/domain/member/Member.java b/src/main/java/com/arom/with_travel/domain/member/Member.java index 5479c7b..76c05ec 100644 --- a/src/main/java/com/arom/with_travel/domain/member/Member.java +++ b/src/main/java/com/arom/with_travel/domain/member/Member.java @@ -117,8 +117,9 @@ public static Member create(String memberName, String email, String oauthId) { @OneToMany(mappedBy = "member") private List chatParts = new ArrayList<>(); - @OneToMany(mappedBy = "member") - private List surveys = new ArrayList<>(); + // 회원 생성 직후 아직 설문이 없을 수 있어서 optional = true로 처리 + @OneToOne(mappedBy = "member", optional = true) + private Survey survey; @OneToOne(mappedBy = "member") private Image image; diff --git a/src/main/java/com/arom/with_travel/domain/survey/Survey.java b/src/main/java/com/arom/with_travel/domain/survey/Survey.java index baa7ce7..b67d8d8 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/Survey.java +++ b/src/main/java/com/arom/with_travel/domain/survey/Survey.java @@ -1,9 +1,8 @@ package com.arom.with_travel.domain.survey; import com.arom.with_travel.domain.member.Member; +import com.arom.with_travel.domain.survey.enums.EnergyLevel; import com.arom.with_travel.global.entity.BaseEntity; -import com.arom.with_travel.global.exception.BaseException; -import com.arom.with_travel.global.exception.error.ErrorCode; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; @@ -12,9 +11,6 @@ import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLRestriction; -import java.util.ArrayList; -import java.util.List; - @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -26,28 +22,52 @@ public class Survey extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - // Survey 엔티티가 answers 문자열 리스트를 갖고 있고, 이 값들을 별도의 테이블에 저장하도록 리팩토링 - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "survey_answers", - joinColumns = @JoinColumn(name = "survey_id")) - @Column(name = "answer") - private List answers = new ArrayList<>(); - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") + @OneToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "member_id", nullable = false) private Member member; - public static Survey create(Member member,List answers) { - validateAnswers(answers); + @NotNull + @Enumerated(EnumType.STRING) + @Column(name = "energy_level", nullable = false, length = 50) + private EnergyLevel energyLevel; - Survey survey = new Survey(); - survey.member = member; - survey.answers = answers; - return survey; + public static Survey create( + Member member, + EnergyLevel energyLevel +// TravelGoal travelGoal, +// TravelPace travelPace, +// CommStyle commStyle, +// Personality personality, +// CompanionStyle companionStyle, +// SpendPattern spendPattern + ) { + Survey s = new Survey(); + s.member = member; + s.energyLevel = energyLevel; +// s.travelGoal = travelGoal; +// s.travelPace = travelPace; +// s.commStyle = commStyle; +// s.personality = personality; +// s.companionStyle = companionStyle; +// s.spendPattern = spendPattern; + return s; } - private static void validateAnswers(List answers){ - if(answers == null || answers.isEmpty()) - throw BaseException.from(ErrorCode.INVALID_SURVEY_ANSWER); + public void update( + EnergyLevel energyLevel +// TravelGoal travelGoal, +// TravelPace travelPace, +// CommStyle commStyle, +// Personality personality, +// CompanionStyle companionStyle, +// SpendPattern spendPattern + ) { + this.energyLevel = energyLevel; +// this.travelGoal = travelGoal; +// this.travelPace = travelPace; +// this.commStyle = commStyle; +// this.personality = personality; +// this.companionStyle = companionStyle; +// this.spendPattern = spendPattern; } } diff --git a/src/main/java/com/arom/with_travel/domain/survey/controller/SurveyController.java b/src/main/java/com/arom/with_travel/domain/survey/controller/SurveyController.java index c623644..e1775e3 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/controller/SurveyController.java +++ b/src/main/java/com/arom/with_travel/domain/survey/controller/SurveyController.java @@ -10,6 +10,7 @@ import com.arom.with_travel.global.security.domain.AuthenticatedMember; import com.arom.with_travel.global.security.domain.PrincipalDetails; import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; @@ -25,22 +26,18 @@ public class SurveyController { @PostNewSurvey @PostMapping("/surveys") - public void createSurvey(@AuthenticationPrincipal PrincipalDetails principal, - @RequestBody SurveyRequestDto dto) { - AuthenticatedMember member = principal.getAuthenticatedMember(); - surveyService.createSurvey(member.getEmail(), dto); - } + public void saveSurvey(@AuthenticationPrincipal PrincipalDetails principal, + @Valid @RequestBody SurveyRequestDto dto) { - @GetSingleSurvey - @GetMapping("/survey/{surveyId}") - public SurveyResponseDto getSurvey(@PathVariable Long surveyId) { - return surveyService.getSurvey(surveyId); + AuthenticatedMember member = principal.getAuthenticatedMember(); + surveyService.saveSurvey(member.getEmail(), dto); } @GetMySurveys @GetMapping("/surveys/my") - public List getMySurveys(@AuthenticationPrincipal PrincipalDetails principal) { + public SurveyResponseDto getSurvey(@AuthenticationPrincipal PrincipalDetails principal) { + AuthenticatedMember member = principal.getAuthenticatedMember(); - return surveyService.getSurveysByEmail(member.getEmail()); + return surveyService.getSurvey(member.getEmail()); } } diff --git a/src/main/java/com/arom/with_travel/domain/survey/dto/request/SurveyRequestDto.java b/src/main/java/com/arom/with_travel/domain/survey/dto/request/SurveyRequestDto.java index d6bc85a..1309a07 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/dto/request/SurveyRequestDto.java +++ b/src/main/java/com/arom/with_travel/domain/survey/dto/request/SurveyRequestDto.java @@ -1,20 +1,24 @@ package com.arom.with_travel.domain.survey.dto.request; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; +import com.arom.with_travel.domain.survey.enums.EnergyLevel; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import java.util.List; - @Getter @NoArgsConstructor -@AllArgsConstructor // <— 모든 필드를 초기화하는 생성자 추가 +@AllArgsConstructor @Builder public class SurveyRequestDto { - @NotEmpty(message = "설문 답변은 최소 1개 이상이어야 합니다.") - private List answers; + @NotNull(message = "energyLevel은 필수입니다.") + private EnergyLevel energyLevel; +// @NotNull TravelGoal travelGoal, +// @NotNull TravelPace travelPace, +// @NotNull CommStyle commStyle, +// @NotNull Personality personality, +// @NotNull CompanionStyle companionStyle, +// @NotNull SpendPattern spendPattern } diff --git a/src/main/java/com/arom/with_travel/domain/survey/dto/response/SurveyResponseDto.java b/src/main/java/com/arom/with_travel/domain/survey/dto/response/SurveyResponseDto.java index 80957bb..bb5a9bf 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/dto/response/SurveyResponseDto.java +++ b/src/main/java/com/arom/with_travel/domain/survey/dto/response/SurveyResponseDto.java @@ -1,6 +1,7 @@ package com.arom.with_travel.domain.survey.dto.response; import com.arom.with_travel.domain.survey.Survey; +import com.arom.with_travel.domain.survey.enums.EnergyLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -14,13 +15,24 @@ @Builder public class SurveyResponseDto { private Long surveyId; - private String question; - private List answers; + private Long memberId; - public static SurveyResponseDto from(Survey survey) { + private EnergyLevel energyLevel; + // 나중에 추가되면 주석 해제 + // private TravelGoal travelGoal; + // private TravelPace travelPace; + // private CommStyle commStyle; + // private Personality personality; + // private CompanionStyle companionStyle; + // private SpendPattern spendPattern; + + public static SurveyResponseDto from(Survey s) { return SurveyResponseDto.builder() - .surveyId(survey.getId()) - .answers(survey.getAnswers()) + .surveyId(s.getId()) + .memberId(s.getMember().getId()) + .energyLevel(s.getEnergyLevel()) + // .travelGoal(s.getTravelGoal()) + // ... .build(); } } diff --git a/src/main/java/com/arom/with_travel/domain/survey/enums/EnergyLevel.java b/src/main/java/com/arom/with_travel/domain/survey/enums/EnergyLevel.java new file mode 100644 index 0000000..54ae742 --- /dev/null +++ b/src/main/java/com/arom/with_travel/domain/survey/enums/EnergyLevel.java @@ -0,0 +1,37 @@ +package com.arom.with_travel.domain.survey.enums; + +import com.arom.with_travel.global.exception.BaseException; +import com.arom.with_travel.global.exception.error.ErrorCode; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +import java.util.Arrays; + +@AllArgsConstructor +public enum EnergyLevel implements SurveyEnum { + + MORNING_PERSON("MORNING_PERSON", "#아침형인간"), + NIGHT_OWL("NIGHT_OWL", "#밤올빼미"), + ENERGIZER("ENERGIZER", "#에너자이저"), + HEALING_MODE("HEALING_MODE", "#힐링모드"); + + private final String code; + private final String label; + + @Override public String getCode() { return code; } + @Override public String getLabel() { return label; } + + // 응답으로는 code를 내보내기 + @JsonValue + public String json() { return code; } + + // 요청으로는 name/code 둘 다 허용 + @JsonCreator + public static EnergyLevel from(String value) { + return Arrays.stream(values()) + .filter(v -> v.name().equalsIgnoreCase(value) || v.code.equalsIgnoreCase(value)) + .findFirst() + .orElseThrow(() -> BaseException.from(ErrorCode.INVALID_SURVEY_ENERGYLEVEL)); + } +} diff --git a/src/main/java/com/arom/with_travel/domain/survey/enums/SurveyEnum.java b/src/main/java/com/arom/with_travel/domain/survey/enums/SurveyEnum.java new file mode 100644 index 0000000..9d5ea2d --- /dev/null +++ b/src/main/java/com/arom/with_travel/domain/survey/enums/SurveyEnum.java @@ -0,0 +1,7 @@ +package com.arom.with_travel.domain.survey.enums; + +public interface SurveyEnum { + + String getCode(); + String getLabel(); +} diff --git a/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java b/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java index ba4fd25..a8efd4a 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java +++ b/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java @@ -4,9 +4,11 @@ import com.arom.with_travel.domain.survey.Survey; import org.springframework.data.jpa.repository.JpaRepository; -import java.util.List; +import java.util.Optional; public interface SurveyRepository extends JpaRepository { - List findByMember(Member member); + Optional findByMember(Member member); + Optional findByMemberIdAndIsDeletedFalse(Long memberId); + boolean existsByMemberIdAndIsDeletedFalse(Long memberId); } diff --git a/src/main/java/com/arom/with_travel/domain/survey/service/SurveyService.java b/src/main/java/com/arom/with_travel/domain/survey/service/SurveyService.java index 52946c0..d169671 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/service/SurveyService.java +++ b/src/main/java/com/arom/with_travel/domain/survey/service/SurveyService.java @@ -9,13 +9,10 @@ import com.arom.with_travel.domain.survey.repository.SurveyRepository; import com.arom.with_travel.global.exception.BaseException; import com.arom.with_travel.global.exception.error.ErrorCode; -import com.arom.with_travel.global.security.domain.AuthenticatedMember; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @Service @RequiredArgsConstructor public class SurveyService { @@ -24,28 +21,45 @@ public class SurveyService { private final MemberRepository memberRepository; @Transactional - public void createSurvey(String email, SurveyRequestDto dto) { + public void saveSurvey(String email, SurveyRequestDto dto) { Member member = memberRepository.findByEmail(email) .orElseThrow(() -> BaseException.from(ErrorCode.MEMBER_NOT_FOUND)); - Survey survey = Survey.create(member, dto.getAnswers()); - surveyRepository.save(survey); - } + Survey survey = surveyRepository.findByMemberIdAndIsDeletedFalse(member.getId()) + .map(existing -> { + existing.update( + dto.getEnergyLevel() +// dto.getTravelGoal(), +// dto.getTravelPace(), +// dto.getCommStyle(), +// dto.getPersonality(), +// dto.getCompanionStyle(), +// dto.getSpendPattern() + ); + return existing; + }) + .orElseGet(() -> Survey.create( + member, + dto.getEnergyLevel() +// dto.getTravelGoal(), +// dto.getTravelPace(), +// dto.getCommStyle(), +// dto.getPersonality(), +// dto.getCompanionStyle(), +// dto.getSpendPattern() + )); - @Transactional(readOnly = true) - public SurveyResponseDto getSurvey(Long surveyId) { - Survey survey = surveyRepository.findById(surveyId) - .orElseThrow(() -> BaseException.from(ErrorCode.SURVEY_NOT_FOUND)); - return SurveyResponseDto.from(survey); + surveyRepository.save(survey); } @Transactional(readOnly = true) - public List getSurveysByEmail(String email) { + public SurveyResponseDto getSurvey(String email) { Member member = memberRepository.findByEmail(email) .orElseThrow(() -> BaseException.from(ErrorCode.MEMBER_NOT_FOUND)); - return surveyRepository.findByMember(member).stream() - .map(SurveyResponseDto::from) - .toList(); + Survey survey = surveyRepository.findByMemberIdAndIsDeletedFalse(member.getId()) + .orElseThrow(() -> BaseException.from(ErrorCode.SURVEY_NOT_FOUND)); + + return SurveyResponseDto.from(survey); } } diff --git a/src/main/java/com/arom/with_travel/global/exception/error/ErrorCode.java b/src/main/java/com/arom/with_travel/global/exception/error/ErrorCode.java index 1a937cb..8510098 100644 --- a/src/main/java/com/arom/with_travel/global/exception/error/ErrorCode.java +++ b/src/main/java/com/arom/with_travel/global/exception/error/ErrorCode.java @@ -55,6 +55,7 @@ public enum ErrorCode { INVALID_SURVEY_ANSWER("SVY-0001", "설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), OVER_ANSWER_LIMIT("SVY-0002", "답변 개수가 초과되었습니다..", ErrorDisplayType.POPUP), INVALID_SURVEY_QUESTION("SVY-0003", "설문 질문이 비어있습니다.", ErrorDisplayType.POPUP), + INVALID_SURVEY_ENERGYLEVEL("SVY-0004", "에너지레벨 설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), // image INVALID_IMG_TYPE("IMG-0000", "지원하지 않는 이미지 형식입니다.", ErrorDisplayType.POPUP), diff --git a/src/test/java/com/arom/with_travel/domain/survey/domain/SurveyTest.java b/src/test/java/com/arom/with_travel/domain/survey/domain/SurveyTest.java index 6e57259..67ea8e5 100644 --- a/src/test/java/com/arom/with_travel/domain/survey/domain/SurveyTest.java +++ b/src/test/java/com/arom/with_travel/domain/survey/domain/SurveyTest.java @@ -1,67 +1,67 @@ -package com.arom.with_travel.domain.survey.domain; - -import com.arom.with_travel.domain.member.Member; -import com.arom.with_travel.domain.survey.Survey; -import com.arom.with_travel.global.exception.BaseException; -import com.arom.with_travel.global.exception.error.ErrorCode; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.*; - -@SuppressWarnings("NonAsciiCharacters") -public class SurveyTest { - - @Test - @DisplayName("정상 입력으로 Survey.create 호출 시 엔티티가 생성된다.") - void 설문_엔티티_생성() { - // given: 유효한 질문·답변 리스트와 더미 회원 - Member member = Member.create("user1", "user1@test.com", Member.Role.USER); - String question = "최애 여행지는?"; - List answers = List.of("제주도", "파리"); - - // when - Survey survey = Survey.create(member, question, answers); - - // then - assertThat(survey.getMember()).isEqualTo(member); - assertThat(survey.getQuestion()).isEqualTo(question); - assertThat(survey.getAnswers()).containsExactlyElementsOf(answers); - } - - @Test - @DisplayName("질문이 비었거나 Null이면 INVALID_SURVEY_QUESTION 예외") - void 질문_누락시_예외() { - Member member = Member.create("user1", "user1@test.com", Member.Role.USER); - List answers = List.of("제주도", "파리"); - - assertThatThrownBy(() -> Survey.create(member, null, answers)) - .isInstanceOf(BaseException.class) - .extracting("errorCode") - .isEqualTo(ErrorCode.INVALID_SURVEY_QUESTION); - - assertThatThrownBy(() -> Survey.create(member, " ", answers)) - .isInstanceOf(BaseException.class) - .extracting("errorCode") - .isEqualTo(ErrorCode.INVALID_SURVEY_QUESTION); - } - - @Test - @DisplayName("답변이 비었거나 Null이면 INVALID_SURVEY_ANSWER 예외") - void 답변_빈_리스트_예외() { - Member member = Member.create("user1", "user1@test.com", Member.Role.USER); - String question = "최애 여행지는?"; - - assertThatThrownBy(() -> Survey.create(member, question, null)) - .isInstanceOf(BaseException.class) - .extracting("errorCode") - .isEqualTo(ErrorCode.INVALID_SURVEY_ANSWER); - - assertThatThrownBy(() -> Survey.create(member, question, List.of())) - .isInstanceOf(BaseException.class) - .extracting("errorCode") - .isEqualTo(ErrorCode.INVALID_SURVEY_ANSWER); - } -} +//package com.arom.with_travel.domain.survey.domain; +// +//import com.arom.with_travel.domain.member.Member; +//import com.arom.with_travel.domain.survey.Survey; +//import com.arom.with_travel.global.exception.BaseException; +//import com.arom.with_travel.global.exception.error.ErrorCode; +//import org.junit.jupiter.api.DisplayName; +//import org.junit.jupiter.api.Test; +// +//import java.util.List; +// +//import static org.assertj.core.api.Assertions.*; +// +//@SuppressWarnings("NonAsciiCharacters") +//public class SurveyTest { +// +// @Test +// @DisplayName("정상 입력으로 Survey.create 호출 시 엔티티가 생성된다.") +// void 설문_엔티티_생성() { +// // given: 유효한 질문·답변 리스트와 더미 회원 +// Member member = Member.create("user1", "user1@test.com", Member.Role.USER); +// String question = "최애 여행지는?"; +// List answers = List.of("제주도", "파리"); +// +// // when +// Survey survey = Survey.create(member, question, answers); +// +// // then +// assertThat(survey.getMember()).isEqualTo(member); +// assertThat(survey.getQuestion()).isEqualTo(question); +// assertThat(survey.getAnswers()).containsExactlyElementsOf(answers); +// } +// +// @Test +// @DisplayName("질문이 비었거나 Null이면 INVALID_SURVEY_QUESTION 예외") +// void 질문_누락시_예외() { +// Member member = Member.create("user1", "user1@test.com", Member.Role.USER); +// List answers = List.of("제주도", "파리"); +// +// assertThatThrownBy(() -> Survey.create(member, null, answers)) +// .isInstanceOf(BaseException.class) +// .extracting("errorCode") +// .isEqualTo(ErrorCode.INVALID_SURVEY_QUESTION); +// +// assertThatThrownBy(() -> Survey.create(member, " ", answers)) +// .isInstanceOf(BaseException.class) +// .extracting("errorCode") +// .isEqualTo(ErrorCode.INVALID_SURVEY_QUESTION); +// } +// +// @Test +// @DisplayName("답변이 비었거나 Null이면 INVALID_SURVEY_ANSWER 예외") +// void 답변_빈_리스트_예외() { +// Member member = Member.create("user1", "user1@test.com", Member.Role.USER); +// String question = "최애 여행지는?"; +// +// assertThatThrownBy(() -> Survey.create(member, question, null)) +// .isInstanceOf(BaseException.class) +// .extracting("errorCode") +// .isEqualTo(ErrorCode.INVALID_SURVEY_ANSWER); +// +// assertThatThrownBy(() -> Survey.create(member, question, List.of())) +// .isInstanceOf(BaseException.class) +// .extracting("errorCode") +// .isEqualTo(ErrorCode.INVALID_SURVEY_ANSWER); +// } +//} diff --git a/src/test/java/com/arom/with_travel/domain/survey/repository/SurveyRepositoryTest.java b/src/test/java/com/arom/with_travel/domain/survey/repository/SurveyRepositoryTest.java index d27c203..132169f 100644 --- a/src/test/java/com/arom/with_travel/domain/survey/repository/SurveyRepositoryTest.java +++ b/src/test/java/com/arom/with_travel/domain/survey/repository/SurveyRepositoryTest.java @@ -1,43 +1,43 @@ -package com.arom.with_travel.domain.survey.repository; - -import com.arom.with_travel.domain.member.Member; -import com.arom.with_travel.domain.member.repository.MemberRepository; -import com.arom.with_travel.domain.survey.Survey; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; - -import java.util.List; - -import static org.assertj.core.api.Assertions.*; - -@DataJpaTest -@DisplayName("SurveyRepository 통합 테스트") -class SurveyRepositoryTest { - - @Autowired - SurveyRepository surveyRepository; - - @Autowired - MemberRepository memberRepository; - - @Test - @DisplayName("Survey와 answers 컬렉션이 DB에 저장·조회된다") - void 설문_저장_및_조회() { - // given: Member 저장 - Member member = Member.create("user1", "user1@test.com", Member.Role.USER); - memberRepository.save(member); - - // when: Survey 저장 - Survey saved = Survey.create(member, "최애 여행지는?", List.of("제주도", "파리")); - surveyRepository.save(saved); - - // then: 다시 조회해서 필드·answers 비교 - Survey loaded = surveyRepository.findById(saved.getId()).orElseThrow(); - assertThat(loaded.getQuestion()).isEqualTo("최애 여행지는?"); - assertThat(loaded.getAnswers()) - .hasSize(2) - .containsExactly("제주도", "파리"); - } -} +//package com.arom.with_travel.domain.survey.repository; +// +//import com.arom.with_travel.domain.member.Member; +//import com.arom.with_travel.domain.member.repository.MemberRepository; +//import com.arom.with_travel.domain.survey.Survey; +//import org.junit.jupiter.api.DisplayName; +//import org.junit.jupiter.api.Test; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +// +//import java.util.List; +// +//import static org.assertj.core.api.Assertions.*; +// +//@DataJpaTest +//@DisplayName("SurveyRepository 통합 테스트") +//class SurveyRepositoryTest { +// +// @Autowired +// SurveyRepository surveyRepository; +// +// @Autowired +// MemberRepository memberRepository; +// +// @Test +// @DisplayName("Survey와 answers 컬렉션이 DB에 저장·조회된다") +// void 설문_저장_및_조회() { +// // given: Member 저장 +// Member member = Member.create("user1", "user1@test.com", Member.Role.USER); +// memberRepository.save(member); +// +// // when: Survey 저장 +// Survey saved = Survey.create(member, "최애 여행지는?", List.of("제주도", "파리")); +// surveyRepository.save(saved); +// +// // then: 다시 조회해서 필드·answers 비교 +// Survey loaded = surveyRepository.findById(saved.getId()).orElseThrow(); +// assertThat(loaded.getQuestion()).isEqualTo("최애 여행지는?"); +// assertThat(loaded.getAnswers()) +// .hasSize(2) +// .containsExactly("제주도", "파리"); +// } +//} diff --git a/src/test/java/com/arom/with_travel/domain/survey/service/SurveyServiceTest.java b/src/test/java/com/arom/with_travel/domain/survey/service/SurveyServiceTest.java index 2a510ac..3cf6fdb 100644 --- a/src/test/java/com/arom/with_travel/domain/survey/service/SurveyServiceTest.java +++ b/src/test/java/com/arom/with_travel/domain/survey/service/SurveyServiceTest.java @@ -1,127 +1,127 @@ -package com.arom.with_travel.domain.survey.service; - -import com.arom.with_travel.domain.member.Member; -import com.arom.with_travel.domain.member.repository.MemberRepository; -import com.arom.with_travel.domain.survey.Survey; -import com.arom.with_travel.domain.survey.dto.request.SurveyRequestDto; -import com.arom.with_travel.domain.survey.dto.response.SurveyResponseDto; -import com.arom.with_travel.domain.survey.repository.SurveyRepository; -import com.arom.with_travel.global.exception.BaseException; -import com.arom.with_travel.global.exception.error.ErrorCode; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.mockito.*; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.BDDMockito.*; - -@DisplayName("SurveyService 단위 테스트") -class SurveyServiceTest { - - @Mock - SurveyRepository surveyRepository; - @Mock - MemberRepository memberRepository; - @InjectMocks - SurveyService surveyService; - - Member member; - SurveyRequestDto dto; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - // 테스트용 더미 데이터 - member = Member.create("user1", "user1@email.com", Member.Role.USER); - dto = SurveyRequestDto.builder() - .answers(List.of("A1","A2")) - .build(); - } - - @Test - @DisplayName("createSurvey: 정상적으로 설문조사 입력 시 DB에 저장") - void 설문_생성_성공() { - // given: memberRepository.findByEmail()가 member를 반환하도록 stub - given(memberRepository.findByEmail("user1@email.com")) - .willReturn(Optional.of(member)); - // save 리턴 값은 크게 상관없음(여기선 null을 지정) - given(surveyRepository.save(any())).willReturn(null); - - // when: 실제로 service 메서드 호출 - surveyService.createSurvey("user1@email.com", dto); - - // then: surveyRepository.save()가 정확히 한 번 호출됐는지 검증 - then(surveyRepository).should(times(1)).save(any()); - } - - @Test - @DisplayName("createSurvey: 멤버 없으면 MEMBER_NOT_FOUND 예외") - void 설문_생성_멤버_없음() { - // given: memberRepository는 빈 Optional 반환 - given(memberRepository.findByEmail(anyString())) - .willReturn(Optional.empty()); - - // when + then: 예외가 던져지는지 검증 - assertThatThrownBy(() -> surveyService.createSurvey("noone@email.com", dto)) - .isInstanceOf(BaseException.class) - .extracting("errorCode") - .isEqualTo(ErrorCode.MEMBER_NOT_FOUND); - } - - @Test - @DisplayName("getSurvey: 존재하는 surveyId면 DTO 반환") - void 설문_조회_성공() { - // given: Survey 엔티티와 ID 세팅 - Survey survey = Survey.create(member, "Q?", List.of("A")); - ReflectionTestUtils.setField(survey, "id", 100L); - given(surveyRepository.findById(100L)).willReturn(Optional.of(survey)); - - // when - SurveyResponseDto result = surveyService.getSurvey(100L); - - // then: DTO 필드가 엔티티와 일치하는지 검증 - assertThat(result.getSurveyId()).isEqualTo(100L); - assertThat(result.getQuestion()).isEqualTo("Q?"); - assertThat(result.getAnswers()).containsExactly("A"); - } - - @Test - @DisplayName("getSurvey: 없으면 SURVEY_NOT_FOUND 예외") - void 설문_조회_실패() { - // given: findById() 빈 Optional - given(surveyRepository.findById(anyLong())).willReturn(Optional.empty()); - - // when + then: SURVEY_NOT_FOUND 예외 발생 - assertThatThrownBy(() -> surveyService.getSurvey(1L)) - .isInstanceOf(BaseException.class) - .extracting("errorCode") - .isEqualTo(ErrorCode.SURVEY_NOT_FOUND); - } - - @Test - @DisplayName("getSurveysByEmail: 멤버별 설문 리스트 반환") - void 내_설문_목록_조회() { - // given: 회원 조회 stub - given(memberRepository.findByEmail("user1@email.com")) - .willReturn(Optional.of(member)); - - // given: findByMember() → 두 개의 Survey 리스트 - Survey s1 = Survey.create(member, "Q1", List.of("A")); - Survey s2 = Survey.create(member, "Q2", List.of("B","C")); - given(surveyRepository.findByMember(member)) - .willReturn(List.of(s1, s2)); - - // when - var list = surveyService.getSurveysByEmail("user1@email.com"); - - // then: DTO 리스트 크기·내용 검증 - assertThat(list).hasSize(2) - .extracting("question") - .containsExactly("Q1", "Q2"); - } -} +//package com.arom.with_travel.domain.survey.service; +// +//import com.arom.with_travel.domain.member.Member; +//import com.arom.with_travel.domain.member.repository.MemberRepository; +//import com.arom.with_travel.domain.survey.Survey; +//import com.arom.with_travel.domain.survey.dto.request.SurveyRequestDto; +//import com.arom.with_travel.domain.survey.dto.response.SurveyResponseDto; +//import com.arom.with_travel.domain.survey.repository.SurveyRepository; +//import com.arom.with_travel.global.exception.BaseException; +//import com.arom.with_travel.global.exception.error.ErrorCode; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.DisplayName; +//import org.junit.jupiter.api.Test; +//import org.mockito.*; +//import org.springframework.test.util.ReflectionTestUtils; +// +//import java.util.List; +//import java.util.Optional; +// +//import static org.assertj.core.api.Assertions.*; +//import static org.mockito.BDDMockito.*; +// +//@DisplayName("SurveyService 단위 테스트") +//class SurveyServiceTest { +// +// @Mock +// SurveyRepository surveyRepository; +// @Mock +// MemberRepository memberRepository; +// @InjectMocks +// SurveyService surveyService; +// +// Member member; +// SurveyRequestDto dto; +// +// @BeforeEach +// void setUp() { +// MockitoAnnotations.openMocks(this); +// // 테스트용 더미 데이터 +// member = Member.create("user1", "user1@email.com", Member.Role.USER); +// dto = SurveyRequestDto.builder() +// .answers(List.of("A1","A2")) +// .build(); +// } +// +// @Test +// @DisplayName("createSurvey: 정상적으로 설문조사 입력 시 DB에 저장") +// void 설문_생성_성공() { +// // given: memberRepository.findByEmail()가 member를 반환하도록 stub +// given(memberRepository.findByEmail("user1@email.com")) +// .willReturn(Optional.of(member)); +// // save 리턴 값은 크게 상관없음(여기선 null을 지정) +// given(surveyRepository.save(any())).willReturn(null); +// +// // when: 실제로 service 메서드 호출 +// surveyService.createSurvey("user1@email.com", dto); +// +// // then: surveyRepository.save()가 정확히 한 번 호출됐는지 검증 +// then(surveyRepository).should(times(1)).save(any()); +// } +// +// @Test +// @DisplayName("createSurvey: 멤버 없으면 MEMBER_NOT_FOUND 예외") +// void 설문_생성_멤버_없음() { +// // given: memberRepository는 빈 Optional 반환 +// given(memberRepository.findByEmail(anyString())) +// .willReturn(Optional.empty()); +// +// // when + then: 예외가 던져지는지 검증 +// assertThatThrownBy(() -> surveyService.createSurvey("noone@email.com", dto)) +// .isInstanceOf(BaseException.class) +// .extracting("errorCode") +// .isEqualTo(ErrorCode.MEMBER_NOT_FOUND); +// } +// +// @Test +// @DisplayName("getSurvey: 존재하는 surveyId면 DTO 반환") +// void 설문_조회_성공() { +// // given: Survey 엔티티와 ID 세팅 +// Survey survey = Survey.create(member, "Q?", List.of("A")); +// ReflectionTestUtils.setField(survey, "id", 100L); +// given(surveyRepository.findById(100L)).willReturn(Optional.of(survey)); +// +// // when +// SurveyResponseDto result = surveyService.getSurvey(100L); +// +// // then: DTO 필드가 엔티티와 일치하는지 검증 +// assertThat(result.getSurveyId()).isEqualTo(100L); +// assertThat(result.getQuestion()).isEqualTo("Q?"); +// assertThat(result.getAnswers()).containsExactly("A"); +// } +// +// @Test +// @DisplayName("getSurvey: 없으면 SURVEY_NOT_FOUND 예외") +// void 설문_조회_실패() { +// // given: findById() 빈 Optional +// given(surveyRepository.findById(anyLong())).willReturn(Optional.empty()); +// +// // when + then: SURVEY_NOT_FOUND 예외 발생 +// assertThatThrownBy(() -> surveyService.getSurvey(1L)) +// .isInstanceOf(BaseException.class) +// .extracting("errorCode") +// .isEqualTo(ErrorCode.SURVEY_NOT_FOUND); +// } +// +// @Test +// @DisplayName("getSurveysByEmail: 멤버별 설문 리스트 반환") +// void 내_설문_목록_조회() { +// // given: 회원 조회 stub +// given(memberRepository.findByEmail("user1@email.com")) +// .willReturn(Optional.of(member)); +// +// // given: findByMember() → 두 개의 Survey 리스트 +// Survey s1 = Survey.create(member, "Q1", List.of("A")); +// Survey s2 = Survey.create(member, "Q2", List.of("B","C")); +// given(surveyRepository.findByMember(member)) +// .willReturn(List.of(s1, s2)); +// +// // when +// var list = surveyService.getSurveysByEmail("user1@email.com"); +// +// // then: DTO 리스트 크기·내용 검증 +// assertThat(list).hasSize(2) +// .extracting("question") +// .containsExactly("Q1", "Q2"); +// } +//} From fb493fd62994956b2b9be14e4d8256543cf3f895 Mon Sep 17 00:00:00 2001 From: JonghyeokNam Date: Tue, 19 Aug 2025 13:08:26 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[refactor]=20MemberSignupService=20?= =?UTF-8?q?=EB=B0=94=EB=80=90=20=EC=84=A4=EB=AC=B8=EC=A1=B0=EC=82=AC=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/SignupWithSurveyRequestDto.java | 4 +-- .../member/service/MemberSignupService.java | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/arom/with_travel/domain/member/dto/request/SignupWithSurveyRequestDto.java b/src/main/java/com/arom/with_travel/domain/member/dto/request/SignupWithSurveyRequestDto.java index 6912483..68afe64 100644 --- a/src/main/java/com/arom/with_travel/domain/member/dto/request/SignupWithSurveyRequestDto.java +++ b/src/main/java/com/arom/with_travel/domain/member/dto/request/SignupWithSurveyRequestDto.java @@ -16,6 +16,6 @@ public class SignupWithSurveyRequestDto { @NotNull private MemberSignupRequestDto extraInfo; // 닉네임·성별·생년월일 - @NotEmpty - private List surveys; + @NotNull + private SurveyRequestDto survey; } diff --git a/src/main/java/com/arom/with_travel/domain/member/service/MemberSignupService.java b/src/main/java/com/arom/with_travel/domain/member/service/MemberSignupService.java index 44c4768..b02e01c 100644 --- a/src/main/java/com/arom/with_travel/domain/member/service/MemberSignupService.java +++ b/src/main/java/com/arom/with_travel/domain/member/service/MemberSignupService.java @@ -9,6 +9,7 @@ import com.arom.with_travel.domain.member.dto.request.SocialMemberVerificationRequest; import com.arom.with_travel.domain.member.repository.MemberRepository; import com.arom.with_travel.domain.survey.Survey; +import com.arom.with_travel.domain.survey.dto.request.SurveyRequestDto; import com.arom.with_travel.domain.survey.repository.SurveyRepository; import com.arom.with_travel.global.exception.BaseException; import com.arom.with_travel.global.exception.error.ErrorCode; @@ -54,10 +55,26 @@ public MemberSignupResponseDto registerWithSurvey(String email, MemberSignupRequestDto extra = req.getExtraInfo(); member.updateExtraInfo(extra.getNickname(), extra.getBirthdate(), extra.getGender(), extra.getIntroduction()); - req.getSurveys().forEach(sdto -> { - Survey survey = Survey.create(member, sdto.getAnswers()); - surveyRepository.save(survey); - }); +// req.getSurveys().forEach(sdto -> { +// Survey survey = Survey.create(member, sdto.getAnswers()); +// surveyRepository.save(survey); +// }); + + SurveyRequestDto s = req.getSurvey(); // 단일 설문 + Survey survey = surveyRepository.findByMemberIdAndIsDeletedFalse(member.getId()) + .map(existing -> { + existing.update( + s.getEnergyLevel() + // 이후 섹션 늘리면 여기에 추가 + ); + return existing; + }) + .orElseGet(() -> Survey.create( + member, + s.getEnergyLevel() + // 이후 섹션 늘리면 여기에 추가 + )); + surveyRepository.save(survey); member.markAdditionalDataChecked(); From 9c245fd1115aea8139fd1fff3b4e4da93ba42fb5 Mon Sep 17 00:00:00 2001 From: JonghyeokNam Date: Mon, 25 Aug 2025 12:00:42 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[refactor]=20=EC=84=A4=EB=AC=B8=EC=A1=B0?= =?UTF-8?q?=EC=82=AC=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=9E=90=EC=97=90=EC=84=9C=20=ED=95=84=EB=93=9C=EA=B0=92=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=EC=96=91=EB=B0=A9=ED=96=A5=20=EC=97=B0?= =?UTF-8?q?=EA=B4=80=EA=B4=80=EA=B3=84=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(1=EC=B0=A8=20=EC=88=98=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../with_travel/domain/member/Member.java | 4 ++ .../with_travel/domain/survey/Survey.java | 53 +++++++------------ .../survey/repository/SurveyRepository.java | 1 - 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/arom/with_travel/domain/member/Member.java b/src/main/java/com/arom/with_travel/domain/member/Member.java index 76c05ec..9a894c8 100644 --- a/src/main/java/com/arom/with_travel/domain/member/Member.java +++ b/src/main/java/com/arom/with_travel/domain/member/Member.java @@ -175,4 +175,8 @@ public void markAdditionalDataChecked() { public void uploadImage(Image image){ this.image = image; } + + public void setSurvey(Survey survey) { + this.survey = survey; + } } \ No newline at end of file diff --git a/src/main/java/com/arom/with_travel/domain/survey/Survey.java b/src/main/java/com/arom/with_travel/domain/survey/Survey.java index b67d8d8..8de1c3f 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/Survey.java +++ b/src/main/java/com/arom/with_travel/domain/survey/Survey.java @@ -31,43 +31,26 @@ public class Survey extends BaseEntity { @Column(name = "energy_level", nullable = false, length = 50) private EnergyLevel energyLevel; - public static Survey create( - Member member, - EnergyLevel energyLevel -// TravelGoal travelGoal, -// TravelPace travelPace, -// CommStyle commStyle, -// Personality personality, -// CompanionStyle companionStyle, -// SpendPattern spendPattern - ) { - Survey s = new Survey(); - s.member = member; - s.energyLevel = energyLevel; -// s.travelGoal = travelGoal; -// s.travelPace = travelPace; -// s.commStyle = commStyle; -// s.personality = personality; -// s.companionStyle = companionStyle; -// s.spendPattern = spendPattern; - return s; + private Survey(Member member, EnergyLevel energyLevel) { + this.energyLevel = energyLevel; + linkMember(member); + } + + public static Survey create(Member member, EnergyLevel energyLevel) { + return new Survey(member, energyLevel); + } + + private void linkMember(Member newMember) { + if (this.member != null && this.member.getSurvey() == this) { + this.member.setSurvey(null); + } + this.member = newMember; + if (newMember.getSurvey() != this) { + newMember.setSurvey(this); + } } - public void update( - EnergyLevel energyLevel -// TravelGoal travelGoal, -// TravelPace travelPace, -// CommStyle commStyle, -// Personality personality, -// CompanionStyle companionStyle, -// SpendPattern spendPattern - ) { + public void update(EnergyLevel energyLevel) { this.energyLevel = energyLevel; -// this.travelGoal = travelGoal; -// this.travelPace = travelPace; -// this.commStyle = commStyle; -// this.personality = personality; -// this.companionStyle = companionStyle; -// this.spendPattern = spendPattern; } } diff --git a/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java b/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java index a8efd4a..2561dc3 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java +++ b/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java @@ -8,7 +8,6 @@ public interface SurveyRepository extends JpaRepository { - Optional findByMember(Member member); Optional findByMemberIdAndIsDeletedFalse(Long memberId); boolean existsByMemberIdAndIsDeletedFalse(Long memberId); }