diff --git a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/model/MonthlyStats.kt b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/model/MonthlyStats.kt new file mode 100644 index 00000000..e826e316 --- /dev/null +++ b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/model/MonthlyStats.kt @@ -0,0 +1,29 @@ +package com.side.tiggle.domain.scheduler.model + +import com.fasterxml.jackson.annotation.JsonIgnore +import com.side.tiggle.domain.category.model.Category +import com.side.tiggle.domain.member.model.Member +import java.time.LocalDate +import javax.persistence.* + +@Entity +@Table(name = "monthly_stats") +class MonthlyStats( + @JsonIgnore + @JoinColumn(name = "member_id", nullable = false) + @ManyToOne(fetch = FetchType.LAZY) + val member: Member, + val monthlyStart: LocalDate, + val monthlyEnd: LocalDate, + val totalAmount: Int, + val highestAmount: Int, + val lowestAmount: Int, + @JsonIgnore + @JoinColumn(name = "most_frequent_category_id", nullable = false) + @ManyToOne(fetch = FetchType.LAZY) + val category: Category +){ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null +} \ No newline at end of file diff --git a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/model/WeeklyStats.kt b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/model/WeeklyStats.kt new file mode 100644 index 00000000..6211d852 --- /dev/null +++ b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/model/WeeklyStats.kt @@ -0,0 +1,26 @@ +package com.side.tiggle.domain.scheduler.model + +import com.side.tiggle.domain.category.model.Category +import com.side.tiggle.domain.member.model.Member +import java.time.LocalDate +import javax.persistence.* + +@Entity +@Table(name = "weekly_stats") +class WeeklyStats( + @JoinColumn(name = "member_id", nullable = false) + @ManyToOne(fetch = FetchType.LAZY) + val member: Member, + val weeklyStart: LocalDate, + val weeklyEnd: LocalDate, + val totalAmount: Int, + val highestAmount: Int, + val lowestAmount: Int, + @JoinColumn(name = "most_frequent_category_id", nullable = false) + @ManyToOne(fetch = FetchType.LAZY) + val category: Category +){ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null +} \ No newline at end of file diff --git a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/repository/MonthlyStatsRepository.kt b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/repository/MonthlyStatsRepository.kt new file mode 100644 index 00000000..c4d1d134 --- /dev/null +++ b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/repository/MonthlyStatsRepository.kt @@ -0,0 +1,7 @@ +package com.side.tiggle.domain.scheduler.repository + +import com.side.tiggle.domain.scheduler.model.MonthlyStats +import org.springframework.data.jpa.repository.JpaRepository + +interface MonthlyStatsRepository: JpaRepository { +} \ No newline at end of file diff --git a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/repository/WeeklyStatsRepository.kt b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/repository/WeeklyStatsRepository.kt new file mode 100644 index 00000000..2f1fa88a --- /dev/null +++ b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/repository/WeeklyStatsRepository.kt @@ -0,0 +1,7 @@ +package com.side.tiggle.domain.scheduler.repository + +import com.side.tiggle.domain.scheduler.model.WeeklyStats +import org.springframework.data.jpa.repository.JpaRepository + +interface WeeklyStatsRepository: JpaRepository { +} \ No newline at end of file diff --git a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/service/JobService.kt b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/service/JobService.kt new file mode 100644 index 00000000..6fb949bc --- /dev/null +++ b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/service/JobService.kt @@ -0,0 +1,51 @@ +package com.side.tiggle.domain.scheduler.service + +import com.side.tiggle.domain.transaction.repository.TransactionRepository +import org.springframework.stereotype.Service +import java.time.DayOfWeek +import java.time.LocalDate +import java.time.YearMonth + +@Service +class JobService( + private val txRepository: TransactionRepository, + private val statsService: StatsService +) { + + fun runNowJob() { + // 현재 작업 로직 + val (weeklyStart, weeklyEnd) = getDate("weekly", LocalDate.now()) + val weeklyMemberTxsMap = txRepository.findByDateBetweenOrderByDateAsc(weeklyStart, weeklyEnd).groupBy { it.member.id } + println("시작일 : ${weeklyStart}, 종료일 : ${weeklyEnd}") + statsService.calculateAndSaveStats(weeklyMemberTxsMap, weeklyStart, weeklyEnd) + } + + fun generateWeeklySummary() { + val (weeklyStart, weeklyEnd) = getDate("weekly", LocalDate.now()) + val weeklyMemberTxsMap = txRepository.findByDateBetweenOrderByDateAsc(weeklyStart, weeklyEnd).groupBy { it.member.id } + } + + fun generateMonthlySummary() { + + println("Montly summary is running...") + + val (monthlyStart, monthlyEnd) = getDate("monthly", LocalDate.now()) + val monthlyMemberTxsMap = txRepository.findByDateBetweenOrderByDateAsc(monthlyStart, monthlyEnd).groupBy { it.member.id } + } + + fun getDate(type: String, today: LocalDate): Pair { + return when (type) { + "weekly" -> { + val lastMonday = today.minusWeeks(1).with(DayOfWeek.MONDAY) + val lastSunday = today.minusWeeks(1).with(DayOfWeek.SUNDAY) + Pair(lastMonday, lastSunday) + } + "monthly" -> { + val firstDayOfLastMonth = today.minusMonths(1).withDayOfMonth(1) + val lastDayOfLastMonth = YearMonth.from(today.minusMonths(1)).atEndOfMonth() + Pair(firstDayOfLastMonth, lastDayOfLastMonth) + } + else -> throw IllegalStateException("Unknown type $type") + } + } +} diff --git a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/service/StatsService.kt b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/service/StatsService.kt new file mode 100644 index 00000000..baf0faa9 --- /dev/null +++ b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/scheduler/service/StatsService.kt @@ -0,0 +1,70 @@ +package com.side.tiggle.domain.scheduler.service + +import com.side.tiggle.domain.category.repository.CategoryRepository +import com.side.tiggle.domain.member.repository.MemberRepository +import com.side.tiggle.domain.scheduler.model.MonthlyStats +import com.side.tiggle.domain.scheduler.model.WeeklyStats +import com.side.tiggle.domain.scheduler.repository.MonthlyStatsRepository +import com.side.tiggle.domain.scheduler.repository.WeeklyStatsRepository +import com.side.tiggle.domain.transaction.model.Transaction +import com.side.tiggle.global.exception.NotFoundException +import org.springframework.stereotype.Service +import java.time.LocalDate +import java.time.temporal.ChronoUnit + +@Service +class StatsService( + private val weeklyStatsRepository: WeeklyStatsRepository, + private val monthlyStatsRepository: MonthlyStatsRepository, + private val memberRepository: MemberRepository, + private val categoryRepository: CategoryRepository +) { + + fun calculateAndSaveStats(memberTxs: Map>, periodStart: LocalDate, periodEnd: LocalDate) { + memberTxs.mapValues { (memberId, txs) -> + val totalAmount = txs.sumOf { it.amount } + val highestAmount = txs.maxOf { it.amount } + val lowestAmount = txs.minOf { it.amount } + val mostFrequentCategory = txs + .groupBy { it.category.id } + .mapValues { entry -> + entry.value.size to entry.value.sumOf { it.amount } + } + .entries + .sortedWith( + compareByDescending>> { it.value.first } + .thenByDescending { it.value.second } + ) + .firstOrNull()?.key ?: -1 + + val member = memberRepository.findById(memberId).orElseThrow { NotFoundException() } + val category = categoryRepository.findById(mostFrequentCategory).orElseThrow { NotFoundException() } + + val daysBetween = ChronoUnit.DAYS.between(periodStart, periodEnd) + println("total : $totalAmount \n highest : $highestAmount \n lowest : $lowestAmount") + if (daysBetween <= 7) { + val weeklyStats = WeeklyStats( + member = member, + weeklyStart = periodStart, + weeklyEnd = periodEnd, + totalAmount = totalAmount, + highestAmount = highestAmount, + lowestAmount = lowestAmount, + category = category + ) + weeklyStatsRepository.save(weeklyStats) + } else { + val monthlyStats = MonthlyStats( + member = member, + monthlyStart = periodStart, + monthlyEnd = periodEnd, + totalAmount = totalAmount, + highestAmount = highestAmount, + lowestAmount = lowestAmount, + category = category + ) + monthlyStatsRepository.save(monthlyStats) + } + } + } +} \ No newline at end of file diff --git a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/transaction/repository/TransactionRepository.kt b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/transaction/repository/TransactionRepository.kt index 46e41b9e..5ec61f56 100644 --- a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/transaction/repository/TransactionRepository.kt +++ b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/domain/transaction/repository/TransactionRepository.kt @@ -4,8 +4,10 @@ import com.side.tiggle.domain.transaction.model.Transaction import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.JpaRepository +import java.time.LocalDate interface TransactionRepository: JpaRepository { fun findByMemberId(memberId: Long, pageable: Pageable): Page -} \ No newline at end of file + fun findByDateBetweenOrderByDateAsc(startDate: LocalDate, endDate: LocalDate): List +} diff --git a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/global/config/SchedulerConfiguration.kt b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/global/config/SchedulerConfiguration.kt new file mode 100644 index 00000000..f3c7adc7 --- /dev/null +++ b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/global/config/SchedulerConfiguration.kt @@ -0,0 +1,48 @@ +package com.side.tiggle.global.config + +import com.side.tiggle.domain.scheduler.service.JobService +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Configuration +import org.springframework.scheduling.annotation.EnableScheduling +import org.springframework.scheduling.annotation.SchedulingConfigurer +import org.springframework.scheduling.config.ScheduledTaskRegistrar + +@Configuration +@EnableScheduling +class SchedulerConfig( + private val jobService: JobService, + @Value("\${week-schedule.cron}") private val weekCron: String, + @Value("\${week-schedule.use}") private val useWeekSchedule: Boolean, + @Value("\${monthly-schedule.cron}") private val monthlyCron: String, + @Value("\${monthly-schedule.use}") private val useMonthlySchedule: Boolean, + @Value("\${now-schedule.cron}") private val nowCron: String, + @Value("\${now-schedule.use}") private val useNowSchedule: Boolean +) : SchedulingConfigurer { + + override fun configureTasks(taskRegistrar: ScheduledTaskRegistrar) { + if (useWeekSchedule) { + taskRegistrar.addCronTask({ runJob("weekly") }, weekCron) + } + if (useMonthlySchedule) { + taskRegistrar.addCronTask({ runJob("monthly") }, monthlyCron) + } + if (useNowSchedule) { + taskRegistrar.addCronTask({ runJob("now") }, nowCron) + } + } + + private fun runJob(jobType: String) { + try { + when (jobType) { + "weekly" -> jobService.generateWeeklySummary() + "monthly" -> jobService.generateMonthlySummary() + "now" -> jobService.runNowJob() + else -> println("Unknown job type: $jobType") + } + } catch (e: InterruptedException) { + println("* Thread가 강제 종료되었습니다. Message: ${e.message}") + } catch (e: Exception) { + println("* Batch 시스템이 예기치 않게 종료되었습니다. Message: ${e.message}") + } + } +} diff --git a/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/global/scheduler/JobService.kt b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/global/scheduler/JobService.kt new file mode 100644 index 00000000..fd70c40a --- /dev/null +++ b/backend/tiggle-root/tiggle/src/main/kotlin/com/side/tiggle/global/scheduler/JobService.kt @@ -0,0 +1,83 @@ +package com.side.tiggle.global.scheduler + +import com.side.tiggle.domain.transaction.model.Transaction +import com.side.tiggle.domain.transaction.repository.TransactionRepository +import org.springframework.stereotype.Service +import java.time.DayOfWeek +import java.time.LocalDate +import java.time.YearMonth + +@Service +class JobService( + private val txRepository: TransactionRepository +) { + + fun runNowJob() { + // 현재 작업 로직 + val (weeklyStart, weeklyEnd) = getDate("weekly", LocalDate.now()) + val weeklyMemberTxsMap = txRepository.findByDateBetweenOrderByDateAsc(weeklyStart, weeklyEnd).groupBy { it.member.id } + + println(calculateMemberStats(weeklyMemberTxsMap)) + } + + fun generateWeeklySummary() { + val (weeklyStart, weeklyEnd) = getDate("weekly", LocalDate.now()) + val weeklyMemberTxsMap = txRepository.findByDateBetweenOrderByDateAsc(weeklyStart, weeklyEnd).groupBy { it.member.id } + } + + fun generateMonthlySummary() { + + println("Montly summary is running...") + + val (monthlyStart, monthlyEnd) = getDate("monthly", LocalDate.now()) + val monthlyMemberTxsMap = txRepository.findByDateBetweenOrderByDateAsc(monthlyStart, monthlyEnd).groupBy { it.member.id } + } + + fun getDate(type: String, today: LocalDate): Pair { + return when (type) { + "weekly" -> { + val lastMonday = today.minusWeeks(1).with(DayOfWeek.MONDAY) + val lastSunday = today.minusDays(1).with(DayOfWeek.SUNDAY) + Pair(lastMonday, lastSunday) + } + "monthly" -> { + val firstDayOfLastMonth = today.minusMonths(1).withDayOfMonth(1) + val lastDayOfLastMonth = YearMonth.from(today.minusMonths(1)).atEndOfMonth() + Pair(firstDayOfLastMonth, lastDayOfLastMonth) + } + else -> throw IllegalStateException("Unknown type $type") + } + } + + fun calculateMemberStats(memberTxs: Map>) : Map { + return memberTxs.mapValues { entry -> + val txs = entry.value + val totalAmount = txs.sumOf { it.amount } + val highestAmount = txs.maxOf { it.amount } + val lowestAmount = txs.minOf { it.amount } + val mostFrequentCategory = txs + .groupingBy { it.category.id } + .eachCount() + .maxByOrNull { it.value }?.key ?: -1 + + MemberStats( + totalAmount = totalAmount, + highestAmount = highestAmount, + lowestAmount = lowestAmount, + mostFrequentCategory = mostFrequentCategory + ) + } + } +} + +// 총 금액 +// 최고 금액 +// 최저 금액 +// 최다 카테고리 항목 +// 이전 주(달)과 차액 +data class MemberStats( + val totalAmount: Int, + val highestAmount: Int, + val lowestAmount: Int, + val mostFrequentCategory: Long +) diff --git a/backend/tiggle-root/tiggle/src/main/resources/application-scheduler.yml b/backend/tiggle-root/tiggle/src/main/resources/application-scheduler.yml new file mode 100644 index 00000000..3f5f2dfd --- /dev/null +++ b/backend/tiggle-root/tiggle/src/main/resources/application-scheduler.yml @@ -0,0 +1,11 @@ +week-schedule: + cron: "0 0 0 * * MON" + use: true + +monthly-schedule: + cron: "0 0 0 1 * *" + use: true + +now-schedule: + cron: "0 0/1 * * * ?" # 매분 동작 + use: true diff --git a/backend/tiggle-root/tiggle/src/main/resources/application.yml.example b/backend/tiggle-root/tiggle/src/main/resources/application.yml.example index 8f4b7081..44f78e35 100644 --- a/backend/tiggle-root/tiggle/src/main/resources/application.yml.example +++ b/backend/tiggle-root/tiggle/src/main/resources/application.yml.example @@ -61,8 +61,7 @@ part: path: upload/image/ app: - gateway-path: http://localhost:8081 - client-redirect-uri: http://localhost:3000/login/success + client-redirect-uri: https://tiggle.duckdns.org/login/success --- spring: diff --git a/backend/tiggle-root/tiggle/src/test/java/com/side/tiggle/TiggleApplicationTests.java b/backend/tiggle-root/tiggle/src/test/java/com/side/tiggle/TiggleApplicationTests.java deleted file mode 100644 index eae5edc4..00000000 --- a/backend/tiggle-root/tiggle/src/test/java/com/side/tiggle/TiggleApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.side.tiggle; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class TiggleApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/backend/tiggle-root/tiggle/src/test/java/com/side/tiggle/domain/category/service/CategoryServiceTest.java b/backend/tiggle-root/tiggle/src/test/java/com/side/tiggle/domain/category/service/CategoryServiceTest.java deleted file mode 100644 index fa8b6357..00000000 --- a/backend/tiggle-root/tiggle/src/test/java/com/side/tiggle/domain/category/service/CategoryServiceTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.side.tiggle.domain.category.service; - -import com.side.tiggle.domain.category.model.Category; -import com.side.tiggle.domain.category.model.CategoryType; -import com.side.tiggle.domain.category.repository.CategoryRepository; -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.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.tuple; - -@ActiveProfiles("test") -@SpringBootTest -@Transactional -class CategoryServiceTest { - - @Autowired - private CategoryRepository categoryRepository; - - @DisplayName("카테고리 생성할 수 있다.") - @Test - void 카테고리_생성() { - // given - Category category1 = createCategory("카테고리 1", CategoryType.INCOME, false); - Category category2 = createCategory("카테고리 2", CategoryType.OUTCOME, true); - Category category3 = createCategory("카테고리 3", CategoryType.INCOME, true); - - categoryRepository.saveAll(List.of(category1, category2, category3)); - - // when - List categories = categoryRepository.findAll(); - - // then - assertThat(categories) - .extracting("name", "type", "defaults") - .containsExactlyInAnyOrder( - tuple("카테고리 1", CategoryType.INCOME, false), - tuple("카테고리 2", CategoryType.OUTCOME, true), - tuple("카테고리 3", CategoryType.INCOME, true) - ); - } - - @DisplayName("카테고리 항목을 조회할 때 수입 타입으로 조회할 수 있다.") - @Test - void 카테고리_수입_조회() { - // given - Category category1 = createCategory("카테고리 1", CategoryType.INCOME, false); - Category category2 = createCategory("카테고리 2", CategoryType.OUTCOME, true); - Category category3 = createCategory("카테고리 3", CategoryType.INCOME, true); - - categoryRepository.saveAll(List.of(category1, category2, category3)); - // when - List categories = categoryRepository.findByType(CategoryType.INCOME); - - // then - assertThat(categories) - .extracting("name", "type", "defaults") - .contains( - tuple("카테고리 1", CategoryType.INCOME, false), - tuple("카테고리 3", CategoryType.INCOME, true) - ); - } - @DisplayName("카테고리 항목을 조회할 때 지출 타입으로 조회할 수 있다.") - @Test - void 카테고리_지출_조회() { - // given - Category category1 = createCategory("카테고리 1", CategoryType.INCOME, false); - Category category2 = createCategory("카테고리 2", CategoryType.OUTCOME, true); - Category category3 = createCategory("카테고리 3", CategoryType.INCOME, true); - - categoryRepository.saveAll(List.of(category1, category2, category3)); - // when - List categories = categoryRepository.findByType(CategoryType.OUTCOME); - - // then - assertThat(categories) - .extracting("name", "type", "defaults") - .contains( - tuple("카테고리 2", CategoryType.OUTCOME, true) - ); - } - - - private static Category createCategory(String name, CategoryType type, boolean defaults) { - return Category.builder() - .name(name) - .type(type) - .defaults(defaults) - .build(); - } -} \ No newline at end of file diff --git a/backend/tiggle-root/tiggle/src/test/kotlin/com/side/tiggle/domain/member/service/MemberServiceTest.kt b/backend/tiggle-root/tiggle/src/test/kotlin/com/side/tiggle/domain/member/service/MemberServiceTest.kt deleted file mode 100644 index ad7b7230..00000000 --- a/backend/tiggle-root/tiggle/src/test/kotlin/com/side/tiggle/domain/member/service/MemberServiceTest.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.side.tiggle.domain.member.service - -import com.side.tiggle.domain.member.dto.service.MemberDto -import com.side.tiggle.domain.member.model.Member -import io.kotest.core.spec.style.BehaviorSpec -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.test.context.ActiveProfiles -import java.time.LocalDate - -@SpringBootTest -@ActiveProfiles("test") -class MemberServiceTest( - private val memberService: MemberService -) : BehaviorSpec({ - - given("profilUrl이 null인 기본 Member가 주어지면") { - val member = Member("email", null, "nickname", LocalDate.of(2020, 1, 20)) - - `when`("회원가입할 때") { - val createMember: Member = memberService.createMember(MemberDto(null, member.email, member.profileUrl, member.nickname, member.birth)) - - then("memberResponseDto로 Member의 정보를 확인할 수 있어야 한다.") { - createMember.email shouldBe member.email - createMember.nickname shouldBe member.nickname - createMember.birth shouldBe member.birth - } - - `when`("단일 조회할 때") { - val findMember: Member = memberService.getMember(createMember.id) - - then("memberResponseDto로 Member의 정보를 확인할 수 있어야 한다.") { - createMember.email shouldBe findMember.email - createMember.nickname shouldBe findMember.nickname - createMember.birth shouldBe findMember .birth - } - } - - `when`("전체 조회할 때") { - val findMemberList: List = memberService.getAllMember() - - then("전체 Member의 각 email, nickname, birth은 null이 아니어야 한다.") { - for (i in findMemberList) { - i.email shouldNotBe null - i.nickname shouldNotBe null - i.birth shouldNotBe null - } - } - } - - `when`("MemberId 1번 Member의 profileUrl은 수정하지 않고, email, nickname 변경할 때") { - val chgMemberDto = MemberDto(createMember.id, "chg@email.com", null, "chgNickname", member.birth) - val updateMember: Member = memberService.updateMember(createMember.id, chgMemberDto, null) - val findUpdateMember: Member = memberService.getMember(createMember.id) - - then("변경한 Member id 조회 시 조회 정보와 변경 정보가 동일해야 한다.") { - findUpdateMember.id shouldBe updateMember.id - findUpdateMember.email shouldBe updateMember.email - findUpdateMember.profileUrl shouldBe updateMember.profileUrl - findUpdateMember.nickname shouldBe updateMember.nickname - findUpdateMember.birth shouldBe updateMember.birth - - } - } - } - } -})