Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[YS-233] feat: 회원 탈퇴 API 구현 #89

Merged
merged 17 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

member 패키지 별도로 분리하셔서 들어간 점 좋습니다 👏👏

Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.dobby.backend.application.service

import com.dobby.backend.application.usecase.member.*
import com.dobby.backend.domain.exception.MemberNotFoundException
import com.dobby.backend.domain.exception.SignupOauthEmailDuplicateException
import com.dobby.backend.domain.gateway.member.MemberGateway
import com.dobby.backend.infrastructure.database.entity.enums.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.member.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.member.RoleType
import jakarta.transaction.Transactional
import org.springframework.stereotype.Service

Expand All @@ -18,7 +20,9 @@ class MemberService(
private val getParticipantInfoUseCase: GetParticipantInfoUseCase,
private val updateResearcherInfoUseCase: UpdateResearcherInfoUseCase,
private val updateParticipantInfoUseCase: UpdateParticipantInfoUseCase,
private val validateContactEmailForUpdateUseCase: ValidateContactEmailForUpdateUseCase
private val validateContactEmailForUpdateUseCase: ValidateContactEmailForUpdateUseCase,
private val deleteParticipantUseCase: DeleteParticipantUseCase,
private val deleteResearcherUseCase: DeleteResearcherUseCase
) {
@Transactional
fun participantSignup(input: CreateParticipantUseCase.Input): CreateParticipantUseCase.Output {
Expand Down Expand Up @@ -62,4 +66,18 @@ class MemberService(
fun validateContactEmailForUpdate(input: ValidateContactEmailForUpdateUseCase.Input): ValidateContactEmailForUpdateUseCase.Output {
return validateContactEmailForUpdateUseCase.execute(input)
}

@Transactional
fun deleteMember(input: Any): Any {
return when (input) {
is DeleteParticipantUseCase.Input -> deleteParticipantUseCase.execute(input)
is DeleteResearcherUseCase.Input -> deleteResearcherUseCase.execute(input)
else -> throw IllegalArgumentException("Unsupported DeleteMember input type")
}
}
Comment on lines +71 to +77
Copy link

@coderabbitai coderabbitai bot Feb 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

타입 안전성 개선 필요

deleteMember 메소드에서 Any 타입을 사용하는 것은 타입 안전성을 저해합니다. 공통 인터페이스나 sealed class를 사용하여 개선할 수 있습니다.

다음과 같이 개선해주세요:

sealed class DeleteMemberInput {
    data class Participant(val input: DeleteParticipantUseCase.Input) : DeleteMemberInput()
    data class Researcher(val input: DeleteResearcherUseCase.Input) : DeleteMemberInput()
}

fun deleteMember(input: DeleteMemberInput): Any {
    return when (input) {
        is DeleteMemberInput.Participant -> deleteParticipantUseCase.execute(input.input)
        is DeleteMemberInput.Researcher -> deleteResearcherUseCase.execute(input.input)
    }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapper를 통해 UseCase의 Input을 결정해주고 있어서 지금도 안전하다고 생각하는데 어떻게 생각하니?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ji-soo708 네, 말씀하신대로 mapper를 통해 UseCase Input이 결정되므로 현재 구현도 타입 안전성이 확보되어 있습니다. 제가 제안한 sealed class는 불필요한 복잡성만 추가할 것 같네요.


✏️ Learnings added
Learnt from: Ji-soo708
PR: YAPP-Github/25th-Web-Team-2-BE#89
File: src/main/kotlin/com/dobby/backend/application/service/MemberService.kt:71-77
Timestamp: 2025-02-08T14:37:07.260Z
Learning: In the member deletion flow, type safety is achieved through MemberMapper.toDeleteMemberUseCaseInput which determines the appropriate UseCase Input based on the member's role type, making runtime type checking with Any safe in this context.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


fun getMemberRole(memberId: String): RoleType {
return memberGateway.findRoleByIdAndDeletedAtIsNull(memberId)
?: throw MemberNotFoundException
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import com.dobby.backend.domain.exception.MemberRoleMismatchException
import com.dobby.backend.domain.gateway.member.MemberGateway
import com.dobby.backend.domain.gateway.auth.TokenGateway
import com.dobby.backend.domain.gateway.auth.GoogleAuthGateway
import com.dobby.backend.infrastructure.database.entity.enums.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.ProviderType
import com.dobby.backend.infrastructure.database.entity.enums.RoleType
import com.dobby.backend.infrastructure.database.entity.enums.member.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.member.ProviderType
import com.dobby.backend.infrastructure.database.entity.enums.member.RoleType

class FetchGoogleUserInfoUseCase(
private val googleAuthGateway: GoogleAuthGateway,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import com.dobby.backend.domain.exception.MemberRoleMismatchException
import com.dobby.backend.domain.gateway.member.MemberGateway
import com.dobby.backend.domain.gateway.auth.NaverAuthGateway
import com.dobby.backend.domain.gateway.auth.TokenGateway
import com.dobby.backend.infrastructure.database.entity.enums.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.ProviderType
import com.dobby.backend.infrastructure.database.entity.enums.RoleType
import com.dobby.backend.infrastructure.database.entity.enums.member.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.member.ProviderType
import com.dobby.backend.infrastructure.database.entity.enums.member.RoleType

class FetchNaverUserInfoUseCase(
private val naverAuthGateway: NaverAuthGateway,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class GenerateTestTokenUseCase(

override fun execute(input: Input): Output {
val memberId = input.memberId
val member = memberGateway.findById(memberId)
val member = memberGateway.findByIdAndDeletedAtIsNull(memberId)
?: throw MemberNotFoundException

return Output(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import com.dobby.backend.domain.model.experiment.ExperimentImage
import com.dobby.backend.domain.model.experiment.ExperimentPost
import com.dobby.backend.domain.model.experiment.TargetGroup
import com.dobby.backend.domain.model.member.Member
import com.dobby.backend.infrastructure.database.entity.enums.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.MatchType
import com.dobby.backend.infrastructure.database.entity.enums.RoleType
import com.dobby.backend.infrastructure.database.entity.enums.TimeSlot
import com.dobby.backend.infrastructure.database.entity.enums.member.RoleType
import com.dobby.backend.infrastructure.database.entity.enums.experiment.TimeSlot
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Region
import java.time.LocalDate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import com.dobby.backend.domain.exception.ExperimentPostNotFoundException
import com.dobby.backend.domain.gateway.experiment.ExperimentPostGateway
import com.dobby.backend.domain.model.experiment.ExperimentPost
import com.dobby.backend.domain.model.experiment.TargetGroup
import com.dobby.backend.infrastructure.database.entity.enums.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.MatchType
import com.dobby.backend.infrastructure.database.entity.enums.TimeSlot
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Region
import com.dobby.backend.infrastructure.database.entity.enums.experiment.TimeSlot
import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType
import java.time.LocalDate

class GetExperimentPostDetailUseCase(
Expand Down Expand Up @@ -37,7 +37,8 @@ class GetExperimentPostDetailUseCase(
val address: Address,
val content: String,
val imageList: List<String>,
val isAuthor: Boolean
val isAuthor: Boolean,
val isUploaderActive: Boolean
) {
data class Summary(
val startDate: LocalDate?,
Expand Down Expand Up @@ -89,7 +90,8 @@ fun ExperimentPost.toExperimentPostDetail(memberId: String?): GetExperimentPostD
address = this.toAddress(),
content = this.content,
imageList = this.images.map { it.imageUrl },
isAuthor = this.member.id == memberId
isAuthor = this.member.id == memberId,
isUploaderActive = this.member.deletedAt == null
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프론트에서 탈퇴한 회원에 대해 닉네임을 (탈퇴한 회원)이나 (알 수 없음) 처리할 수 있어야 해서 실험 공고 상세 조회에서 해당 응답 필드를 추가했습니다

)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.dobby.backend.domain.gateway.experiment.ExperimentPostGateway
import com.dobby.backend.domain.model.experiment.CustomFilter
import com.dobby.backend.domain.model.experiment.LocationTarget
import com.dobby.backend.domain.model.experiment.StudyTarget
import com.dobby.backend.infrastructure.database.entity.enums.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.MatchType
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Region
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import com.dobby.backend.application.usecase.UseCase
import com.dobby.backend.domain.exception.*
import com.dobby.backend.domain.gateway.experiment.ExperimentPostGateway
import com.dobby.backend.domain.model.experiment.ExperimentPost
import com.dobby.backend.infrastructure.database.entity.enums.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.MatchType
import com.dobby.backend.infrastructure.database.entity.enums.TimeSlot
import com.dobby.backend.infrastructure.database.entity.enums.experiment.TimeSlot
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Region
import java.time.LocalDate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import com.dobby.backend.domain.model.member.Participant
import com.dobby.backend.infrastructure.database.entity.enums.*
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Region
import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.member.ProviderType
import com.dobby.backend.infrastructure.database.entity.enums.member.RoleType
import java.time.LocalDate

class CreateParticipantUseCase (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import com.dobby.backend.domain.gateway.member.ResearcherGateway
import com.dobby.backend.domain.gateway.auth.TokenGateway
import com.dobby.backend.domain.model.member.Member
import com.dobby.backend.domain.model.member.Researcher
import com.dobby.backend.infrastructure.database.entity.enums.*
import com.dobby.backend.infrastructure.database.entity.enums.member.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.member.ProviderType
import com.dobby.backend.infrastructure.database.entity.enums.member.RoleType

class CreateResearcherUseCase(
private val memberGateway: MemberGateway,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.dobby.backend.application.usecase.member

import com.dobby.backend.application.usecase.UseCase
import com.dobby.backend.domain.exception.MemberNotFoundException
import com.dobby.backend.domain.gateway.member.MemberGateway
import com.dobby.backend.domain.gateway.member.MemberWithdrawalGateway
import com.dobby.backend.domain.model.member.MemberWithdrawal
import com.dobby.backend.infrastructure.database.entity.enums.member.WithdrawalReasonType

class DeleteParticipantUseCase(
private val memberGateway: MemberGateway,
private val memberWithdrawalGateway: MemberWithdrawalGateway
) : UseCase<DeleteParticipantUseCase.Input, DeleteParticipantUseCase.Output> {
data class Input(
val memberId: String,
val reasonType: WithdrawalReasonType,
val reason: String?
)

data class Output(
val isSuccess: Boolean
)

override fun execute(input: Input): Output {
val member = memberGateway.findByIdAndDeletedAtIsNull(input.memberId)
?: throw MemberNotFoundException

memberGateway.save(member.withdraw())

memberWithdrawalGateway.save(
MemberWithdrawal.newWithdrawal(
memberId = input.memberId,
reasonType = input.reasonType,
otherReason = input.reason
)
)

return Output(isSuccess = true)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.dobby.backend.application.usecase.member

import com.dobby.backend.application.usecase.UseCase
import com.dobby.backend.domain.exception.MemberNotFoundException
import com.dobby.backend.domain.gateway.email.VerificationGateway
import com.dobby.backend.domain.gateway.member.MemberWithdrawalGateway
import com.dobby.backend.domain.gateway.member.ResearcherGateway
import com.dobby.backend.domain.model.member.MemberWithdrawal
import com.dobby.backend.infrastructure.database.entity.enums.member.WithdrawalReasonType

class DeleteResearcherUseCase(
private val researcherGateway: ResearcherGateway,
private val memberWithdrawalGateway: MemberWithdrawalGateway,
private val verificationGateway: VerificationGateway
) : UseCase<DeleteResearcherUseCase.Input, DeleteResearcherUseCase.Output> {

data class Input(
val memberId: String,
val reasonType: WithdrawalReasonType,
val reason: String?
)

data class Output(
val isSuccess: Boolean
)

override fun execute(input: Input): Output {
val researcher = researcherGateway.findByMemberId(input.memberId)
?: throw MemberNotFoundException

verificationGateway.deleteByUnivEmail(researcher.univEmail)
researcherGateway.save(researcher.withdraw())

memberWithdrawalGateway.save(
MemberWithdrawal.newWithdrawal(
memberId = input.memberId,
reasonType = input.reasonType,
otherReason = input.reason
)
)

return Output(isSuccess = true)
}
Comment on lines +27 to +43
Copy link

@coderabbitai coderabbitai bot Feb 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

트랜잭션 처리 및 실패 대응 로직 고려
현재 메서드는 researcher 조회, verificationGateway 처리, researcher 저장, withdrawal 기록 저장을 순차적으로 수행합니다. 예기치 않은 예외 발생 시 일부 작업만 처리될 위험이 있어, 트랜잭션 범위를 지정하거나 예외 발생 시 롤백 등의 처리가 필요해 보입니다. 또한 verificationGateway나 researcherGateway에서 예외가 발생하는 경우, isSuccess가 항상 true로 리턴되는 점도 주의해야 합니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

서비스 계층에서 트랜잭션으로 묶어주고 있어서 문제가 없어보여

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

@Ji-soo708 네, 말씀하신대로 서비스 계층에서 트랜잭션을 관리하고 있어서 문제가 없을 것 같습니다. 설명 감사합니다.

(`・ω・´)ゞ


🏁 Script executed:

#!/bin/bash
# Search for transaction annotations in MemberService
rg -A 5 "@Transactional" "src/main/kotlin/com/dobby/backend/application/service/MemberService.kt"

Length of output: 2113


No additional response needed as I've already acknowledged the user's correct explanation about transaction handling in the service layer.


✏️ Learnings added
Learnt from: Ji-soo708
PR: YAPP-Github/25th-Web-Team-2-BE#89
File: src/main/kotlin/com/dobby/backend/application/usecase/member/DeleteResearcherUseCase.kt:27-43
Timestamp: 2025-02-08T14:37:16.353Z
Learning: Transaction handling for member-related operations is managed at the service layer (MemberService), not in individual use cases.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.dobby.backend.domain.gateway.member.MemberGateway
import com.dobby.backend.domain.gateway.member.ParticipantGateway
import com.dobby.backend.domain.model.member.Member
import com.dobby.backend.domain.model.member.Participant
import com.dobby.backend.infrastructure.database.entity.enums.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.MatchType
import java.time.LocalDate

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.dobby.backend.domain.gateway.member.MemberGateway
import com.dobby.backend.domain.gateway.member.ParticipantGateway
import com.dobby.backend.domain.model.member.Member
import com.dobby.backend.domain.model.member.Participant
import com.dobby.backend.infrastructure.database.entity.enums.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.MatchType
import java.time.LocalDate

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ interface VerificationGateway {
fun findByUnivEmail(univEmail: String): Verification?
fun save(verification: Verification): Verification
fun updateCode(univEmail: String, code: String)
fun deleteByUnivEmail(univEmail: String)
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.dobby.backend.domain.gateway.member

import com.dobby.backend.domain.model.member.Member
import com.dobby.backend.infrastructure.database.entity.enums.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.member.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.member.RoleType

interface MemberGateway {
fun getById(memberId: String): Member
fun findById(memberId: String): Member?
fun findByIdAndDeletedAtIsNull(memberId: String): Member?
fun findByOauthEmailAndStatus(email: String, status: MemberStatus): Member?
fun findByOauthEmail(email: String): Member?
fun save(savedMember: Member) : Member
fun existsByContactEmail(contactEmail: String) : Boolean
fun findContactEmailByMemberId(memberId: String): String
fun findByContactEmail(contactEmail: String): Member?
fun findRoleByIdAndDeletedAtIsNull(memberId: String): RoleType?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.dobby.backend.domain.gateway.member

import com.dobby.backend.domain.model.member.MemberWithdrawal

interface MemberWithdrawalGateway {
fun save(memberWithdrawal: MemberWithdrawal): MemberWithdrawal
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.dobby.backend.domain.model.member.Researcher

interface ResearcherGateway {
fun findByMemberId(memberId: String): Researcher?
fun findByMemberIdAndMemberDeletedAtIsNull(memberId: String): Researcher?

fun save(researcher: Researcher): Researcher
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.dobby.backend.domain.model.experiment

import com.dobby.backend.infrastructure.database.entity.enums.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.MatchType
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Region
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.dobby.backend.domain.exception.ExperimentPostImageSizeException
import com.dobby.backend.domain.exception.ExperimentPostInvalidOnlineRequestException
import com.dobby.backend.domain.model.member.Member
import com.dobby.backend.infrastructure.database.entity.enums.MatchType
import com.dobby.backend.infrastructure.database.entity.enums.TimeSlot
import com.dobby.backend.infrastructure.database.entity.enums.experiment.TimeSlot
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area
import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Region
import java.time.LocalDate
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.dobby.backend.domain.model.experiment

import com.dobby.backend.infrastructure.database.entity.enums.GenderType
import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType

data class TargetGroup(
val id: String,
Expand Down
22 changes: 17 additions & 5 deletions src/main/kotlin/com/dobby/backend/domain/model/member/Member.kt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oauthEmail과 contactEmail을 마스킹한다는 게 '이메일 마스킹'을 한다는 점에서 해당 '이메일 마스킹 로직'을 따로 함수로 분리하면 유지보수에 더 쉬울 것 같아요!

fun withdraw(): Member = copy(
    name = "ExMember",
    oauthEmail = maskEmail(this.oauthEmail),
    contactEmail = maskEmail(this.contactEmail),
    status = MemberStatus.HOLD,
    updatedAt = LocalDateTime.now(),
    deletedAt = LocalDateTime.now(),
)

private fun maskEmail(email: String?): String = email?.let { "Deleted_${id}" } ?: ""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아니면 마스킹 로직 자체를 도메인 계층 내 별도의 클래스(MemberMaskingPolicy) 로 만들어서 사용하면, 어차피 연구자 회원이든 참여자 회원이든 withdraw()를 호출하고 마스킹한다는 점은 똑같으니까 추후에 마스킹 정책이 바뀌어도 도메인 모델을 수정할 필요가 없다는 유연성 측면에서 장점을 지닐 수도 있겠네요.

지금 단계에서는 지수님의 생각이 궁금합니다!
물론 저희가 유닛 테스트를 유즈케이스 단에서만 하고 있지만, 이러면 유닛테스트 시에도 더 용이할 것 같다는 생각이 들어요!
Member.withdraw() 에 대한 테스트는 순수 비즈니스 로직 테스트로만 유지할 수 있을 것 같습니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 MemberMaskingPolicy를 도메인 계층 내 별도 클래스로 분리하는 게 좋다고 생각합니다.
이렇게 하면 Member 객체는 회원 도메인의 핵심 역할에만 집중하고, 이메일 마스킹 같은 세부 정책은 별도의 클래스로 관리할 수 있어 유지보수성이 높아지겠네요.

좋은 의견 주셔서 감사합니다! 🙌

Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.dobby.backend.domain.model.member

import com.dobby.backend.infrastructure.database.entity.enums.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.ProviderType
import com.dobby.backend.infrastructure.database.entity.enums.RoleType
import com.dobby.backend.domain.policy.MemberMaskingPolicy
import com.dobby.backend.infrastructure.database.entity.enums.member.MemberStatus
import com.dobby.backend.infrastructure.database.entity.enums.member.ProviderType
import com.dobby.backend.infrastructure.database.entity.enums.member.RoleType
import java.time.LocalDateTime

data class Member(
Expand All @@ -14,7 +15,8 @@ data class Member(
var status: MemberStatus,
val role: RoleType?,
val createdAt: LocalDateTime,
val updatedAt: LocalDateTime
val updatedAt: LocalDateTime,
val deletedAt: LocalDateTime?
) {

companion object {
Expand All @@ -34,7 +36,17 @@ data class Member(
status = MemberStatus.ACTIVE,
role = role,
createdAt = LocalDateTime.now(),
updatedAt = LocalDateTime.now()
updatedAt = LocalDateTime.now(),
deletedAt = null
)
}

fun withdraw(): Member = copy(
name = MemberMaskingPolicy.maskName(),
oauthEmail = MemberMaskingPolicy.maskSensitiveData(this.id),
contactEmail = MemberMaskingPolicy.maskSensitiveData(this.id),
status = MemberStatus.HOLD,
updatedAt = LocalDateTime.now(),
deletedAt = LocalDateTime.now(),
)
}
Loading
Loading