오늘은 필자가 경험한 인증 서비스 Refactoring 다뤄보도록 하겠다.
현업에서 다룬 만큼 어떻게 그리고 왜 Refactoring 했는지와 연관되지 않는 부분은 제거하고 작성하겠다.
앞서 해당 글에서 작성하면서 사용한 도메인의 설정을 정의하고 가겠다.
- 상품: 유저가 구매하는 상품이며 보통 1년에 1~4개를 구매한다.
상품 유형은 A, B, C, D 상품이 존재한다. - 인증: 유저가 상품을 구매한 구매자인지 인증하기 위한 도메인이며, 현재 인증기간일 경우 할인이 적용된다.
우선 필자가 만난 인증 서비스는 아래와 같았다.
간단하게 유저가 상품을 구매하면 인증서버에 해당 유저가 상품을 구매했다고 상품에 명시되어 있는 인증기간 동안 구매자 인증을 해주는 시스템이다.
해당 시스템은 매우 간단하지만 도메인전문가의 니즈는 조금 복잡했다.
- 고객의 인증기간은 관리자가 조절할 수 있다.
- 상품을 구매하지 않더라도 관리자가 임의로 인증기간을 할당할 수 있다.
- 구매자가 현재 얼마만큼의 인증기간을 가지고 있는지 확인해야 한다.
- 상품마다 인증기간이 다르며, 인증기간 사이에 비인증 기간이 존재할 수 있다.
또한 해당 서버에는 고질적인 문제가 있었다.
왜 어째서인지 고객이 상품을 구매하였지만 인증기간이 존재하지 않는 데이터들도 존재하는 것이었다.
왜 그럴까?
우선 인증서버에서 인증 데이터를 어떤 것들을 저장하는지 확인해 보자.
A시작일 | A종료일 | B상품 | B시작일 | B종료일 | C상품 | C시작일 | C종료일 | D상품 | D시작일 | D종료일 |
위 칼럼을 확인 후 필자는 이와 같이 생각했다.
- 인증은 A, B, C, D상품 인증일 중 하나라도 현재 인증 기간 범위 내에 존재한다면 구매자로 인증된다.
- 구매한 상품의 타입이 같다면 인증기간을 기존 인증기간이 존재한다면 두 데이터를 비교하여 더 빠른 인증시작일과 더 늦게 종료되는 인증 종료일을 저장한다.
- 스케줄링을 통해 인증 종료된 인증 데이터에 대해서는 빈칸으로 수정해 준다.( Mysql을 사용하였고, 해당 칼럼 타입 VARCHAR이었다. )
그리고 데이터에도 문제가 있다는 것을 확인하였다.
- 상품을 구매하였지만 인증데이터가 생성되지 않는 유저가 존재한다.
- 어째서인지 들어갈 수 없는 날짜가 인증 종료기간에 들어가 있다.
- 인증기간은 존재하지만 상품 아이디가 없다.
그럼 상황을 정리해 보자.
- 상품을 구매할 때마다 인증 데이터의 신뢰도가 떨어진다.
- 현재 인증일인지 확인하기 복잡하다.
- 테스트한 데이터가 운영 데이터베이스에 있다.
- 인증 데이터에 상품 아이디가 존재하지 않는 이유는 관리자가 임의로 생성한 데이터였기 때문이다.
- 이상한 날짜가 들어간 이유는 인증 종료일을 강제로 31일로 넣었기 때문이다.
필자는 위 상황을 정리하며 해당 시스템에 어떤 고객 니즈가 존재하는지, 기존 시스템을 파악하며 어떤 문제가 존재하는지를 파악하였다.
이제 이를 해결하기 위해 어떤 고민이 있었고, 어떻게 해결하였는지 알아보자.
Chapter 1. 상품을 구매할 때마다 인증 데이터의 신뢰도가 떨어진다.
필자가 맨 처음 고민한 부분이다.
위 인증 데이터는 상품을 구매할 경우 인증기간을 상품과 상관없이 인증기간을 인증 데이터에 저장하고 있었다.
이렇게 하였을 경우 문제점은 이와 같았다.
- 정상적으로 인증 데이터가 생성되지 않을 수 있다.
- 상품의 실제 인증기간과 별개의 인증기간이 만들어질 수 있다.
- 현재 인증기간이 어떤 상품을 구매해서 생긴 것인지 정확히 알기 어렵다.
위와 같은 문제가 발생한다면 이는 또다시 이런 일이 발생하지 않도록 하는 게 좋다 생각했다.
그럼 어떻게 풀어낼 수 있을까?
우선 간단하게 생각했다.
상품을 구매할 때마다 인증기간을 생성한다면 인증기간을 신뢰도가 높고, 효율적으로 관리할 수 있을 것이라 생각했다.
그리하여 인증과 인증기간을 분리하기로 하였다.
인증 테이블
인증 아이디 | 고객 아이디 |
인증 기간 테이블
인증 기간 아이디 | 인증 아이디 | 상품 아이디 | 인증 시작일 | 인증 종료일 |
위와 같이 정규화를 하여 관리한다면 구매한 상품에 대한 인증기간을 정확하게 관리할 수 있다.
또한 상품 타입이 늘어나거나 줄어들 때마다 테이블 칼럼을 수정하지 않아도 된다.
Chapter 2. 현재 인증된 고객인지 빠르고 정확하게 확인하고 싶다.
기존 인증 데이터의 경우 A, B, C, D 상품 인증 시작일과 종료일을 하나의 Record로 관리하고 있었다.
따라서 sql을 작성한다고 가정하면 이렇다.
SELECT *
FROM 인증
WHERE 유저아이디 = :유저아이디
AND (
(A시작일 <= :현재날짜 AND A종료일 >= :현재날짜) OR
(B시작일 <= :현재날짜 AND B종료일 >= :현재날짜) OR
(C시작일 <= :현재날짜 AND C종료일 >= :현재날짜) OR
(D시작일 <= :현재날짜 AND D종료일 >= :현재날짜)
);
하지만 Chapter 1에서 정규화된 테이블을 사용한다면 이와 같이 작성될 것이다.
SELECT 인증아이디
FROM 인증
WHERE 유저아이디 = :유저아이디;
SELECT *
FROM 인증기간
WHERE 인증아이디 = :인증아이디
AND 시작일 <= :현재날짜
AND 종료일 >= :현재날짜;
비정규화 쿼리와 정규화 쿼리를 비교해 보자.
비정규화 | 정규화 | |
인덱스 활용도 | 낮음 | 높음 |
유지보수 | 어려움 (A~D 컬럼 늘어나면 쿼리 수정 필요) | 쉬움 |
쿼리 단순성 | 조건문 복잡 | 명확하고 간결 |
확장성 | 나쁨 (인증 수 늘면 테이블 변경 필요) | 좋음 (행으로 관리) |
성능 | 느림 (OR 조건 병렬평가) | 빠름 (범위 인덱스 활용 가능) |
이와 같이 정규화를 적절하게 적용한다면 매우 많은 이점을 가질 수 있게 된다.
물론 서버에서 조회한 다음 로직으로 현재 인증일인지 판단한다 하면 성능자체는 비정규화 방식이 좋을 수도 있다 생각한다.
왜냐면 정규화를 한다면 최소 4배 이상의 데이터가 생길 것이기 때문이다.
하지만 유지보수성과 관리의 측면에서 바라본다면 좀 더 효율적으로 관리할 수 있을 것이다.
Chapter 3. 서버가 다운되더라도 인증 기간은 정상적으로 생성하고 싶다.
정규화를 통해 데이터를 잘 관리할 수 있게 되었다면 우리는 다음 문제를 해결해야 한다.
상품을 구매하였는데 인증 기간이 생기지 않는 상황이다.
이는 몇 가지 고민을 해볼 수 있다.
1. 하나의 트랜잭션으로 묶는다.
만약 MA라면 가장 간단할 거다.
하지만 여기서도 고민해 볼 것은 존재한다 생각한다.
인증 기간이 생성되지 않는다고 상품 구매를 원복 시킨다?
뭔가 배보다 배꼽이 커진 거 같다.
???: "상품을 구매하였기 때문에 인증 기간이 생성되어야 합니다."
???: "인증 기간이 생성되지 않았기 때문에 상품 구매는 취소됩니다."
논리적으로 앞뒤가 바뀐 거 같지 않은가?
필자는 이에 주도권은 상품에게 존재한다 생각하였다.
그렇기 때문에 하나의 트랜잭션으로 묶는 것은 적절하지 않다 생각한다.
2. 스케줄링을 돌려 인증 기간을 생성한다.
이 또한 간단하게 처리할 수 있는 방법이라 생각한다.
다만 구매한 상품들 중 인증 안된 상품을 어떻게 찾을 것인가?
만약 한다면 n초 전 구매한 상품들은 전부 인증기간을 생성해야 하는 가?
이러한 부분을 원활하게 처리하기 위해서는 스케줄링은 적합하지 않다 판단했다.
3. Event를 사용하여 비동기로 생성한다.
상품을 구매하면 상품정보와 고객 정보로 Message를 만들어 발행하는 것이다.
이렇게 진행한다면 인증 서비스에서는 해당 Topic으로 발행된 Message를 컨슈밍 하여 인증 기간을 생성하는 것으로 깔끔한 과정이 완성된다.
물론 과정만 깔끔하다 해서 해당 방법을 채택한 것은 아니었다.
- 고가용성 보장
인증 기간 생성 중 서버 장애가 발생해도, 메시지를 재처리하여 복구 가능하다. - 비동기 처리로 API 성능 향상
인증 생성은 후속 작업이므로, 클라이언트 응답 속도에 영향을 주지 않는다. - 프로세스 간 결합도 감소
상품 구매와 인증 생성 로직이 분리되어 유지보수 용이하다.
이러한 장점을 적용시키기 위해 Event를 발행하는 방법으로 개발을 진행하였다.
이렇게 리펙토링 및 정규화 과정을 거치면서 도메인전문가의 니즈를 충족시키며 기존 발생했던 문제들 혹은 불편한 점들을 개선시킬 수 있었다.
- 고객이 구매한 상품 인증기간을 관리자가 조절할 수 있다.
- 상품을 구매하지 않더라도 관리자가 해당 유저의 인증기간을 새로 생성 혹은 기존 인증 기간을 수정할 수 있다.
- 생성된 인증기간들을 통해 관리자는 유저가 보유한 인증기간을 쉽게 파악할 수 있다.
- 상품마다 인증기간이 따로 관리되기 때문에 비인증 기간을 파악하기 편하다.
그럼 이렇게만 처리하면 모든 게 다 베스트인가?
완벽하게 잘 처리한 것인가?
필자는 주어진 자원 내에 최적의 방법을 선택하여 과제를 해결했다 생각한다.
다만 해당 처리에도 이슈들은 분명 존재한다.
- Kafka관련 이슈들
- 기존 데이터 마이그레이션
하지만 이와 관련된 처리는 다른 글에서 다루도록 하고, 여기선 우선 리팩터링 하는 과정에서 했던 고민들과 어떻게 해결하였는지만 다루도록 하겠다.
또한, 개발자인 필자가 위와 같이 판단하였다고 맘대로 진행하면 안 된다.
도메인전문가와 기획자와의 소통은 필수 요소이며, 소통을 얼마나 잘하느냐에 따라 결과물이 달라지기 때문이다.
똑같은 일 두 번 하기 싫으면 소통을 자주 하는 것을 권장한다.
더 좋은 방법 혹은 이야기해보고 싶은 내용이 있다면 댓글 부탁드립니다.
'Server' 카테고리의 다른 글
파티션과 샤딩에 관하여 (2) | 2025.06.30 |
---|---|
[Monorepo] 모노레포 그것이 답인가? (2) | 2025.05.22 |
[EDA] EDA는 왜 적용하게 된걸까? (0) | 2025.04.03 |
[MSA] 왜 MSA로 가야하나요? (0) | 2025.04.03 |
[Architecture] Clean Architecture VS Hexagonal Architecture (0) | 2025.01.14 |