프로젝트를 진행하면서 겪었던 문제에 대해 적어보려 합니다.
여담이지만 글을 쓴다는 것은 참 어려운 것 같아요.
저는 제가 개발중인 서비스이고 고민도 많이 해봤기에 어떤 내용인지 자세히 알지만
글을 읽는 독자분들은 제가 겪은 상황을 자세히 모르시기에 이해가 안되는 부분이 있을거라 생각해요.
그렇기에 나름의 상황설명을 덧붙여 글을 잘 써보려해요. (노션에 정리해보고 다듬어 글을 작성합니다.)
저는 현재 개인카페를 대상으로한 웹서비스 기반 스탬프적립 서비스를 개발중입니다.
이해가 잘 안되시거나 궁금한게 있으시면 댓글로 남겨주시면 감사하겠습니다.
요구 사항
임시회원으로 적립한 스탬프와 리워드 적립내역을 회원가입과 동시에 이전한다.
저희 서비스는 전화번호만으로 쉽게 스탬프 적립을 할 수 있도록 개발중입니다.
저희 서비스에대한 사용자의 허들을 낮추고 편의성을 높이기 위함인데요 !
하지만 스탬프 적립으로 인한 리워드를 사용하기 위해서는 회원가입을 진행해야 합니다.
그렇기에 전화번호를 통해서만 적립한 스탬프와 리워드를 모두 이전해야하는 상황입니다.
사전 지식
1. 임시회원 생성
카페에서 스탬프를 적립할 때 전화번호만 입력해서 적립을 한다.
즉, 전화번호로만 임시회원을 만들어 데이터베이스에 저장한다.
2. 가입회원 생성
스탬프를 모두 적립하면 리워드로 전환되고 전환된 리워드를 사용하려면 우리 서비스에 가입해야 한다.
정식 회원 가입을 거치면, 이전에 임시회원에서 적립했던 스탬프와 리워드를 이전해야한다.
문제 상황
먼저 회원 테이블 구조를 살펴보겠습니다.
현재 customer 테이블과 register_custer 테이블과 temporary_customer 테이블의 구조입니다.
조인 테이블 전략을 써서 구현을 했는데요. 현재 dtype으로 임시회원과 가입회원을 구분하고 있습니다.
만약 회원가입을 진행하지 않고 스탬프를 적립하면 자동으로 임시회원이 만들어집니다.
이제부터 customer 테이블의 PK인 customer_id를 기준으로 부르겠습니다.
PK가 1인 회원은 [회원 1]이라고 칭하겠습니다.
현재 [회원 1]과 [회원 2]는 정식 회원가입을 거치지 않은 채 스탬프를 적립하여 임시회원 상태입니다.
여기서 만약 [회원 2]가 정식 회원가입을 진행할 시 다음과 같은 상태가 됩니다.
[회원 2]와 [회원 3]는 동일한 유저입니다. 단지 [회원 2]는 회원 가입을 하기 전의 유저이고, [회원 3]는 회원 가입을 한 뒤의 유저입니다.
즉, 동일한 유저에 대해 임시 회원 상태와 가입 회원 상태가 공존합니다.
여기서 우리가 해야할 일은 [회원 2]의 적립 스탬프와 리워드를 [회원 3]에게 이전하는 것입니다.
다른 관점은 [회원 3]의 정보를 [회원 2]에 업데이트 시키고 [회원 3]를 지워버리는 것입니다.
여기서 고민해본 방법은 다음과 같습니다.
후보 해결 방법
첫 번째 방법
새로 추가된 가입 회원[회원 3]을 유지시키고 기존 임시 회원의 데이터를 이전한 후 임시 회원[회원 2]을 삭제한다.
이 경우는 애초에 큰 고려를 하지 않았습니다. 매우 비효율적인 작업이라고 생각했기 때문인데요.
현재 스탬프와 리워드는 customer 테이블의 PK인 customer_id를 FK 즉, 외래키로 가지고 있습니다.
스탬프와 리워드에 대한 FK를 모두 변경해줘야 하는 작업이라 비효율적이라고 생각했습니다.
위의 상황을 예로 들어보면 임시 회원인 [회원 2]가 적립한 스탬프와 리워드는 외래키가 모두 2입니다.
만약 이 유저가 가입을 해 customer_id가 3인 가입 회원 레코드가 추가되면,
[회원 2] 상태에서 적립한 모든 스탬프와 리워드의 외래키를 3으로 바꿔줘야하는 작업이 필요합니다.
또한 customer_id를 외래키로 갖는 새로운 테이블이 추가되면 그 외래키 또한 바꿔줘야하는
작업이 들어가기 때문에 확장성이 낮다고 판단하였습니다.
물론 객체적인 관점에선 스탬프 또는 리워드의 소유주(Customer)를 변경한다. 이기 때문에 전혀 이상할게 없어보이기도 합니다.
하지만 더 나은 방법을 찾기 위해 고민을 더 해봐야 했습니다.
두 번째 방법
새로 추가된 가입 회원[회원 3]의 정보로 기존 임시 회원[회원 2] 정보를 업데이트하고 가입 회원[회원 3]을 삭제한다.
이 방법의 경우 임시 회원 [회원 2]의 PK(customer_id = 2)를 외래키로 갖는 다른 테이블의 레코드들을
건들 필요가 없으며 회원 테이블만 수정하면 된다는 장점이 있습니다.
그렇다면 어떻게 임시 가입 회원 [회원 3]의 정보로 임시 회원 [회원 2]의 정보를 업데이트 해야할까요 ?
업데이트를 하기 전의 상태는 다음과 같습니다.
업데이트를 한 뒤의 상태는 다음과 같습니다.
그 다음에 해야할 작업은 다음과 같습니다.
1. temporary_customer 테이블에서 customer_id = 2인 레코드를 삭제한다.
2. register_customer 테이블에서 [회원 3]의 정보를 복사하여 [회원 2]를 저장한다.
3. customer 테이블과 register_customer 테이블에서 [회원 3]의 레코드를 삭제한다.
이 작업을 거치면 다음과 같은 상태가 됩니다.
이렇게 [회원 2]를 임시회원에서 가입회원으로 정보를 업데이트해서 기존 스탬프와 리워드 테이블은 건들지 않아도 됩니다.
하지만 이 방법 또한 결국 하지 않기로 했습니다.
저희는 JPA 기술을 쓰고 있기 때문에 dtype을 변경한다는 것은 결국 엔티티의 종류를 바꾸겠다는 것이고,
이 과정 또한 어렵고 객체지향적이지 않다고 판단했습니다.
jdbctemplate 등을 이용하여 조작할 수 있긴 하지만, DataSource를 직접 사용하여 DB에 접근하기 때문에
영속성 컨텍스트의 캐시에 의해 관리되는 객체들과의 정합성에 문제가 된다고 판단하였습니다.
앞서 말한 두 가지 방법을 쓰지 않으면 어떤 방법을 쓰겠다는거지 ?
앞서 고민해본 두 가지 방법은 모두 기존 테이블의 스키마를 유지하여 데이터를 이전한다는 공통점이 있습니다.
현재의 테이블 구조에서는 데이터 이전이 쉽지 않을 것이라 판단하여 과감히 테이블 구조를 변경하기로 마음먹었습니다.
유연한 구조로 설계해야 한다는 것을 배운 좋은 경험이었습니다.
테이블 구조를 변경하며 데이터를 이전시키는 과정에 대해서는 다음 글에 작성하도록 하겠습니다.
긴 글 읽어주셔서 감사합니다.