본문 바로가기

Dev Log

사용자 추천 기능은 어떻게 테이블을 설계해야 할까

728x90

— 데이터보다 중요한 건 ‘생성 당시의 상태’였다

 

서비스를 만들다 보면 한 번쯤은 추천 기능을 구현하게 된다.

관심사 기반 추천이든, 행동 기반 추천이든, 혹은 특정 데이터를 바탕으로 한 자동 추천이든 형태만 다를 뿐 구조는 비슷하다.

 

최근에 헬스케어 서비스에서 추천 기능을 설계하면서, 단순히 테이블을 만드는 문제라고 생각했다가 예상보다 깊은 고민을 하게 됐다. 결론부터 말하면, 이 문제의 핵심은 추천 로직이 아니라 사용자 입력 데이터와 그 입력이 발생한 시점의 상태를 어떻게 함께 저장할 것인가였다.

 

 

1. 처음에는 단순한 구조처럼 보였다

 

기능 요구사항은 직관적이었다.

 

  • 사용자가 건강 카테고리를 직접 선택할 수 있음
  • 건강검진 결과가 있으면, 수치 기반으로 자동 추천 카테고리가 생성됨
  • 이후 영양성분 페이지에서 이 카테고리들을 기준으로 추천을 보여줌

 

그래서 테이블도 단순하게 생각했다.

 

  • 사용자 선택 카테고리 저장
  • 자동 추천 카테고리 저장
  • 구분은 checkup_based_yn 같은 플래그로 처리

 

예를 들면:

 

  • Y → 검진 기반 자동 추천
  • N → 사용자 직접 선택

 

여기까지는 전형적인 CRUD 설계다. 크게 문제될 게 없어 보였다.

 

 

2. 그런데 UI 요구사항이 들어오면서 꼬이기 시작했다

 

UI에서는 단순히 카테고리 목록만 보여주는 게 아니라,

추천의 “근거”도 함께 노출해야 했다.

 

예를 들면:

 

  • “건강검진 결과를 기반으로 추천해 드려요”
  • “사용자가 선택한 관심사 기반 추천입니다”

 

여기서부터 질문이 생겼다.

이런 상황은 어떻게 구분하지?

케이스1

  • 과거: 검진 데이터 없음
  • 사용자가 카테고리 선택
  • → 전부 사용자 선택 기반

케이스 2

  • 나중에: 검진 데이터가 새로 들어옴
  • 그런데 기존에 선택한 카테고리는 그대로 존재함

 

이 상태에서 서버는 이렇게 판단하게 된다:

검진 데이터 있음 → checkupResultYn = Y

 

하지만 실제로 카테고리는 “검진이 없을 때 사용자가 고른 것”이다.

 

UI 입장에서는:

  • 검진 있음 → 검진 기반 추천으로 보이는데
  • 실제로는 전혀 아님

 

이 순간부터 데이터의 “의미”가 틀어지기 시작한다.

 

케이스 3

또 놓쳤던 상황은 아래와 같은 상황이다.

  • 검진 데이터는 있음
  • 그런데 모든 수치가 정상
  • → 자동 추천 카테고리 0개

 

이 경우 DB에는 이렇게 남는다:

  • 전부 사용자 선택 카테고리만 존재
  • checkup_based_yn = Y 인 row는 하나도 없음

그런데 실제 맥락은 이거다:

  • 검진은 분명히 했음
  • 단지 추천할 게 없었을 뿐

 

하지만 DB만 보면: “그냥 사용자가 선택한 카테고리만 있네?”

 

이렇게밖에 해석이 안 된다.

여기서 깨달았다.

 

지금 구분하려는 건 ‘카테고리 데이터’가 아니라 ‘추천이 만들어진 당시의 상태’라는 걸.

처음 만든 checkup_based_yn 플래그에 너무 많은 역할을 넣으려다 보니 이  결과에 도달하기 까지에도 꽤 시간이 걸렸다.

 

4. 문제의 본질은 데이터가 아니라 ‘시점’이었다

 

처음엔 이렇게 생각했다.

  • 플래그를 하나 더 둘까? 
  • 검진 있었을 때 선택한 건지 표시해야 하나?
  • 상태값을 추가해야 하나?

그런데 계속 고민하다 보니 근본 원인이 보였다.

지금까지는: “무슨 데이터를 저장했냐” 만 생각하고 있었다.

 

하지만 실제로 필요한 건: “그 데이터가 어떤 상태에서 생성됐냐였다.

 

정리하면 이거다:

우리가 저장한 것

사용자 선택 저장됨
자동 추천 결과 저장됨
검진 데이터 존재 여부 별도 테이블에 있음

 

하지만 빠진 게 하나 있었다. “이 추천이 만들어질 당시, 검진이 있었는가?” 이 정보가 없으니까, 계속 돌았던 것이다. 

 

5. ‘없는 데이터’를 해석하려고 해서 더 꼬였다

 

특히 이런 상황에서 많이 헤맸다.

  • 검진 있음
  • 전부 정상
  • 자동 추천 카테고리 없음

이건 사실 의미가 있는 상태다.

 

하지만 DB에는:

  • 아무 row도 생성되지 않음

 

그런데 UI는 알고 싶어 한다. “검진을 기반으로 분석된 결과인가?”

 

즉,

  • 데이터는 없지만
  • 상태는 존재함

 

이걸 카테고리 row 존재 여부로 추론하려고 하니까 설계가 계속 꼬였던 거다.

 

 

6. 그래서 얻은 결론

 

추천 기능 설계에서 중요한 건

카테고리 테이블 구조 자체가 아니었다.

 

진짜 중요한 건 이거였다: 사용자 입력 데이터만 저장하면 충분한 줄 알았는데

실제로는 ‘입력 당시의 상태(context)’를 같이 저장해야 했다.

 

즉, 추천 시스템은 항상 두 가지를 함께 가진다.

 

1. 결과 데이터

  • 어떤 카테고리가 추천됐는지
  • 사용자가 무엇을 선택했는지

2. 생성 컨텍스트

  • 검진이 있었는지
  • 자동 추천이 동작한 상황인지
  • 사용자 선택만 있었는지

 

이 두 개가 분리되지 않으면:

  • UI 분기가 어려워지고
  • 의미 해석이 꼬이고
  • 나중에 정책 변경에도 취약해진다.

 

마무리

처음에는 단순히 “추천 카테고리를 어떻게 저장할까?”라는 데이터 구조의 문제처럼 보였지만, 실제로는 훨씬 더 본질적인 고민이었다. 결국 핵심은 하나의 값을 저장하는 것이 아니라, 그 값이 만들어진 순간의 상태(state), 그 변화를 만든 이벤트(event), 그리고 생성 당시의 맥락(context)을 함께 어떻게 모델링할 것인가에 있었다.

 

현재 추천 카테고리를 저장하는 테이블은 추천 컨텐츠나 추천 챌린지 뭐 이런 값들의 상태값을 저장할 수 있는 공통 테이블로 우선 설계는 해뒀다. 근데 이런 구조가 확장성이 있는지, 공통을 잘 쓰일 수 있는 구조인지는 조금 더 고민이 필요해보인다. 

 

728x90