[UniLetter] 회원 탈퇴 API (hard delete)

Seohyun-kim·2022년 11월 13일
1
post-thumbnail

1. 기존에는..

사실 기존에 구글로그인만 있을 때는 회원 탈퇴 기능을 따로 만들어 두지 않았다.

user table의 deleted_at에 탈퇴 시각이 들어가 있으면, 탈퇴한 것으로 치는
soft delete 형태로 잡고 있었다.

그리고 사실 안드로이드에는 탈퇴기능은 넣지도 않았음 ㅎㅋ
아 들어올땐 자유지만~ 나가는건 없어

mysql> DESC user;
+-------------------+--------------+------+-----+----------------------+----------------+
| Field             | Type         | Null | Key | Default              | Extra          |
+-------------------+--------------+------+-----+----------------------+----------------+
| id                | int(11)      | NO   | PRI | NULL                 | auto_increment |
| email             | varchar(255) | NO   |     | NULL                 |                |
| nickname          | varchar(255) | NO   |     | NULL                 |                |
| image_uuid        | varchar(255) | YES  |     | NULL                 |                |
| oauth_provider    | varchar(255) | NO   |     | NULL                 |                |
| oauth_id          | varchar(255) | NO   |     | NULL                 |                |
| remember_me_token | varchar(255) | NO   |     | NULL                 |                |
| fcm_token         | varchar(255) | YES  |     | NULL                 |                |
| subscribing       | tinyint(4)   | NO   |     | NULL                 |                |
| subscribing_on    | varchar(255) | YES  |     | NULL                 |                |
| created_at        | datetime(6)  | NO   |     | CURRENT_TIMESTAMP(6) |                |
| deleted_at        | datetime     | YES  |     | NULL                 |                |
+-------------------+--------------+------+-----+----------------------+----------------+
12 rows in set (0.00 sec)


2. Apple 지침

앱에서 계정 삭제 기능 제공하기

2022년 6월 30일부터 App Store에 제출한 앱 중에서 계정 생성을 지원하는 앱에는 사용자가 앱 내에서 계정을 삭제할 수 있도록 해야 합니다. 계정을 삭제하면 개발자의 기록에서 계정이 삭제되며, 해당 계정과 관련하여 개발자가 법적으로 보관할 의무가 없는 모든 데이터도 함께 삭제됩니다.

완전히 지워야 한다! (hard delete)

근데 개발 입장에서는 그게 더 편한듯!

deleted_at 에 값을 주는 형태로 soft delete를 하면,

  1. 탈퇴 전 작성한 게시글/댓글은 어떻게 되는가?

    • 게시글 자체는 DB상에 남겨두되, 사용자에게 내려주지는 않을 것인지
    • 게시글 자체를 그대로 내려주되 작성자를 (알수없음)표시할 것인지
    • 게시글은 완전히 삭제를 할 것인지..
  1. 탈퇴한 사용자가 재가입하면 어떻게 되는가?

    • 기존에 작성한 게시글/댓글 다시 복원?
    • 그냥 원래처럼 (알수없음이면 알수없음, 안내려줬으면 안내려주는 상태)

크게 이 두 가지 관점에서 여러모로 복잡해 진다.



3. 회원 탈퇴 시나리오

데이터베이스 table이 foreign key로 다 묶여있기 때문에
연관된 값을 모두 삭제 해 주어야 user 삭제가 가능하다.

  1. 사용자의 저장(like) 모두 삭제

  2. 사용자가 작성한 댓글 모두 삭제

  3. 사용자가 설정한 알림 모두 삭제

  4. 작성한 이벤트 모두 삭제
    4.1 이벤트에 딸린 댓글 모두 삭제
    4.2 이벤트에 딸린 저장(like) 모두 삭제
    4.3 이벤트에 딸린 알림 모두 삭제
    4.4 이벤트 삭제

  5. 사용자가 설정한 유저차단목록 삭제

  6. 유저 삭제



4. 회원 탈퇴 라우터 정의

자기 자신만 삭제 가능하다.

src/server/routes/users/deleteUser.ts

export default defineRoute('delete', '/delete-me', schema, authorizer(), async (req, res) => {
  const userId = req.requireUserId();
  await UserService.deleteUserHardly(userId);

  return res.send();
});
  • req에서 사용자 본인의 id를 빼 온다. (userId)
  • 해당 유저의 정보를 UserService.deleteUserHardly로 넘겨 완전히 삭제한다.


4. 유저 정보 완전 삭제 서비스 구현

4.1 UserService.deleteUserHardly

src/service/UserService.ts

  async deleteUserHardly(userId:number): Promise<void> {
    const user = await User.findOneOrFail(userId);
    const event = await Event.find({where:{user}});

    log(`${user.toString()}의 모든 like 정보를 삭제합니다.`);
    await EventLike.delete({user});

    log(`회원 탈퇴하기에 앞서, ${user.toString()}가 작성한 댓글 모두 삭제!`);
    await CommentService.deleteMyAllComment(userId)

    log(`회원 탈퇴하기에 앞서, ${user.toString()}가 설정한 알림 모두 삭제!`);
    await EventNotification.delete({user});

    log(`${user.toString()}가  작성한 이벤트 모두 삭제!`);
    await event.forEach(event => {
      EventService.deleteEvent(event.id);
    })

    log(`${user.toString()}가 설정한 차단 모두 삭제!`);
    await Block.delete({
      blockingUser: userId
    });

    await User.delete({
      id: userId
    });
    log(`${user.toString()} 탈퇴 성공!`);
  }
}
  • 3의 시나리오 대로 하나씩 삭제 해 준다.

  • 댓글과 이벤트는 각 service에서 따로 처리를 해 주었다.

  • 유저가 작성한 이벤트가 여러개이기 때문에 Event.find({where:{user}});로부터 찾아온
    event의 값이 복수개로 좌라라락 나오는데 이 부분을 어떻게 처리해야 할 지 고민하고 있었는데
    같이 하는 server 팀원이 forEach 문으로 야무지에 구현해주었다!

    await event.forEach(event => {
          EventService.deleteEvent(event.id);
        })
  • 각각에 연관된 테이블의 row를 삭제해주고 나서 마지막에
    user테이블에서 해당 유저 삭제.


4.2 CommentService.deleteMyAllComment(userId)

유저가 작성한 댓글을 모두 삭제한다.

src/service/CommentService.ts

  async deleteMyAllComment(userId: number): Promise<void> {
    const user = await User.findOneOrFail(userId);
    log(`${userId}가 작성한 댓글 모두 삭제 삭제!`);
    await Comment.delete({
      user: user
    });
  }

4.3 EventService.deleteEvent(event.id)

각 이벤트 별 이벤트에 딸린 댓글, 좋아요, 알림 삭제

src/service/EventService.ts

  async deleteEvent(eventId: number): Promise<void> {
    const event = await Event.findOneOrFail(eventId);

    log(`${event}를 삭제하기에 앞서, 이벤트에 딸린 댓글을 모두 지웁니다.`);
    await Comment.delete({event});

    log(`${event}를 삭제하기에 앞서, 이벤트에 딸린 좋아요를 모두 지웁니다.`);
    await EventLike.delete({event});

    log(`${event}를 삭제하기에 앞서, 이벤트에 딸린 알림을 모두 지웁니다.`);
    await EventNotification.delete({event});

    log(`이제 ${event}를 삭제합니다.`);
    await Event.delete({id: eventId});
  }

끝!

0개의 댓글