23년 3월 7일 TIL & 개발노트 (흑우집합소 계정 탈퇴 관련)

thumbnail

계정 탈퇴 기능을 개발하다가…

흑우집합소 내 계정 탈퇴 기능을 개발하고 있었다.
다른 사이트 보면 계정 탈퇴를 아주 꼭꼭 숨겨두는데, 개인적으로 좀 이해 안되는 행동이다.

사이트에 들어오는건 쉬운데 나가는건 어렵게 한다?

img01

자기네 사이트에 회원을 묶으려는 수작질이다.
한때 유행했던 무슨 UX 패턴이었는데…

나갈 사용자는 어떻게 해서든 찾아서 나간다.
그렇다면 그냥 탈퇴도 쉽게 해주는게 좋지 않을까?

정말 사용작에게 필요한 서비스고 유용하다면 탈퇴해도 다시 돌아올 것이다.
그만큼 서비스에 자신이 없는걸까?

무튼 개인적으로 너무 악질이었던 패턴 같다.
특히 대기업 서비스에서도 이런걸 하는게 이해하기 어려웠다.
대기업이면 나름 서비스에도 자신이 있을텐데 말이다.

서론이 길었다.
무튼 탈퇴 로직을 개발하면서 몇 가지 이슈를 찾게 되었다.

어떤 이슈인가?

내가 흑우집합소에서 계정의 경우 닉네임은 고유값(Unique)이다.

초기에 이걸 고려 안해서 탈퇴 후 신규 회원이 해당 닉네임을 사용하려 했더니 불가능했다.
그래서 사용자가 탈퇴를 하면 닉네임과 사용자의 상태 값이 변경되게 처리했다.

nickName: getUuid().split("-")[0] + "-" + account.nickName,

근데 탈퇴한 사용자가 다시 회원에 가입을 시도할 자꾸 신규 가입 처리가 되었다.
원래 정책상 6개월 뒤에 가입이 가능하게끔 했는데 말이다.

그래서 회원 가입쪽 로직을 보니 아래의 로직을 사용하고 있었다.

export class AccountFindEmailJoinTypeRepository
  implements FindAccountEmainJoinTypeOutboundPort {
  constructor(
    @InjectModel(Account.name) private readonly accountModel: Model<Account>
  ) {}

  async excute(params: FindAccountEmainJoinTypeInputDto): Promise<Account> {
    return await this.accountModel.findOne({
      email: params.email,
      joinType: params.joinType,
    })
  }
}

그렇다…
상태 검사 없이 그냥 이메일이랑 조인 타입만 구분해서 처리하고 있었다.

그래서 다른 포트를 구현했다.
이메일과 조인타입 그리고 상태 값을 같이 조회하는 로직으로 구현했다.

이렇게 하니 내가 생각한 로직의 방향으로 잘 구현이 되었다.
그리고 저렇게 포트 앤 어뎁터 형식을 사용하니 기존 로직에서 변화가 없이 그냥 포트 구현하고, 그 포트를 구현한 구현체 서비스를 주입하는 형태로 쓰니,
기존 로직에는 변화가 없이 해당 부분만 수정할 수 있었다.

const isResignAccount = await this.isResignAcountInbound.excute({
  email,
  joinType: ACCOUNT_JOIN_TYPE.SNS_GOOGLE,
})
if (isResignAccount.isResign) {
  throw new AccountException(AccountErrorCode.AccessResignAccount())
}

원래 이런식으로 6개월 탈퇴를 체크한다.
그리고 저 isResignAcountInbound메서드에선 실제 해당 계정이 탈퇴를 한 사용자인지 체크를 하는 서비스로직이다.

로직은 아래와 같이 구현되어 있다.

export class IsResignAccountService implements IsResignAccountInboundPort {
  constructor(
    @Inject(FIND_ACCOUNT_EMAIL_JOINTYPE_STATUS_OUTBOUND_PORT)
    private readonly findAccountEmailJoinTypeStatusOutboundPort: FindAccountEmainJoinTypeStatusOutboundPort
  ) {}

  async excute(
    params: IsResignAccountInputDto
  ): Promise<IsResignAccountOutputDto> {
    //처리 내용
  }
}

excute내용은 실무 코드라서 공개는 힘들지만 대략 해당 계정을 조회해서 탈퇴 이력을 찾는 부분이다.

원래는 생성자에서 FindAccountEmainJoinTypeOutboundPort라는 포트를 받았지만,
이제는 상태 값을 받는 포트를 주입받음으로써 계정의 조회 영역은 별다른 수정 없이 바로 사용할 수 있었다.

좀 더 정확히 설명하자면 기존 로직에서는 다중 계정의 정보가 리턴되어 탈퇴 이력을 정확하게 판단하지 못했다.
하지만 이번 로직에서는 바로 상태 값까지 조회하기에 단 하나의 계정만 조회가 된다.

이게 언뜻 보면 너무 당연한 이야기고 간단한 내용이다.
하지만 설계를 제대로 안하고, 로직을 복잡하게 하나의 코드에서 다 구현을 했다면…

아마 쉽게 수정하기 어려웠을 것이다.
이번 이슈에서 배운 내용은 두 가지이다.

  1. 각 기능에 대한 설계를 잘 해두자.
  2. 로직 아키텍쳐는 심플하게… 하나의 기능만 수행하는 코드를 작성하자.

정리

과거의 기록을 찾아서 다시 블로그에 정리를 하니…
회상도 되고 흑우집합소를 개발하면서 저런 일도 있었구나… 라는게 좋았다.

이렇게 TIL을 잘 모아서 작성하다보면 내가 어떻게 만들고,
어떤 이슈를 어떻게 해결했는지 도움이 되는 것 같다.


Written by@MHLab
로또는 흑우집합소 🎲
와인관리, 시음노트, 셀러관리는 마와셀 🥂

🫥 My Service|  📜 Contact|  💻 GitHub