이 글의 배경이 되는 이야기는 18화: 만든 걸 세상에 내놓기에서 읽을 수 있습니다.
이 글을 읽으면 알 수 있는 것
- 사용자의 알레르기·기피성분 데이터를 서버에 보내지 않는 이유
- localStorage 기반 개인화의 구조와 한계
- “서버는 모르고, 브라우저만 아는” 아키텍처의 설계 의도
문제: 알레르기 정보는 민감정보입니다
이 서비스는 사용자의 기피성분을 기반으로 제품을 분석합니다. “이 제품에 내가 피하는 성분이 있는가?” — 이것이 핵심 기능입니다.
이 기능을 구현하려면 두 가지 데이터가 필요합니다.
- 제품의 원재료 정보 — 서버에 있습니다 (104만 건)
- 사용자가 피하는 성분 목록 — 어디에 저장할 것인가?
당연한 접근은 서버에 저장하는 것입니다. 사용자 계정을 만들고, 로그인하면 기피성분 설정을 불러오고, 서버에서 매칭해서 결과를 돌려주는 구조.
하지만 이 접근에는 문제가 있습니다.
알레르기 정보는 건강 관련 민감정보입니다. 한국 개인정보보호법에서 건강정보는 민감정보로 분류되며, 수집·이용 시 별도의 동의가 필요합니다. “이 사용자는 땅콩 알레르기가 있다”는 정보를 서버에 저장하는 순간, 이 서비스는 민감정보 처리자가 됩니다.
스타트업 단계에서 민감정보 보호 의무를 완벽하게 이행하는 것은 현실적으로 어렵습니다. DB 암호화, 접근 통제, 유출 시 통지 의무 등 준수해야 할 것이 많아집니다.
갈림길: 서버 저장 vs 클라이언트 저장
| 방식 | 장점 | 단점 |
|---|---|---|
| 서버 저장 (DB) | 기기 간 동기화, 서버에서 개인화 가능 | 민감정보 처리자 의무, 보안 리스크 |
| 클라이언트 저장 (localStorage) | 서버에 민감정보 없음, 구현 단순 | 기기 간 동기화 불가, 브라우저 삭제 시 소실 |
두 번째를 선택했습니다.
원칙은 단순합니다. 서버는 사용자가 누구인지, 어떤 알레르기가 있는지 모릅니다. 서버가 제공하는 것은 제품 데이터와 기피성분 규칙 목록뿐입니다. “이 사용자에게 맞춤화된 결과”는 브라우저에서 계산합니다.
구조: 서버는 규칙을, 브라우저는 판단을
전체 흐름은 이렇습니다.
[서버] [브라우저]
제품 데이터 → API 제공 사용자가 알레르기 설정
기피성분 규칙 → API 제공 ────→ 규칙 캐시
제품 조회 시:
제품 원재료 + 사용자 설정 + 규칙
↓
브라우저에서 매칭
↓
"이 제품에 주의 성분이 있습니다"
중요한 점은, 제품 조회 API에 사용자 식별 정보가 포함되지 않는다는 것입니다. “새우깡의 원재료를 알려줘”라는 요청에 “이 사람은 밀 알레르기가 있다”는 정보가 붙지 않습니다. 모든 사용자에게 동일한 응답이 돌아갑니다.
개인화는 그 응답을 받은 후, 브라우저에서 일어납니다.
브라우저에 저장되는 것들
| 데이터 | 저장 위치 | 서버 전송 여부 |
|---|---|---|
| 알레르겐 선택 (22종) | localStorage | 전송 안 함 |
| 기피성분 선택 (인지 기반, 기호 기반) | localStorage | 전송 안 함 |
| 특수 건강군 (임산부, 영유아 등) | localStorage | 전송 안 함 |
| 즐겨찾기 제품 목록 | localStorage | 전송 안 함 |
| 최근 본 제품 | localStorage | 전송 안 함 |
| 개인정보 동의 여부 | localStorage | 전송 안 함 |
모든 사용자 개인 데이터는 브라우저의 localStorage에만 존재합니다. 서버 DB에는 사용자 테이블 자체가 없습니다.
기피성분 매칭이 브라우저에서 일어나는 과정
- 서비스 첫 접속 시, 서버에서 기피성분 규칙 목록을 받아옵니다 (42종의 규칙 정의)
- 이 규칙을 브라우저에 캐시합니다
- 사용자가 프로필에서 “나는 땅콩 알레르기가 있다”고 설정합니다 → localStorage에 저장
- 제품을 조회하면, 서버에서 원재료 목록을 받아옵니다
- 브라우저에서 원재료 목록을 사용자의 설정과 대조합니다
- 매칭 결과를 화면에 표시합니다
4번에서 5번으로 넘어갈 때, 서버에는 아무것도 보내지 않습니다. 매칭은 전적으로 브라우저의 JavaScript에서 실행됩니다.
이 설계가 포기하는 것
| 기능 | 서버 저장 시 | localStorage 시 |
|---|---|---|
| 기기 간 동기화 | 가능 | 불가능 |
| 서버 측 추천 | 가능 | 불가능 |
| 사용자 행동 분석 | 가능 | 불가능 |
| 데이터 백업 | 자동 | 사용자 책임 |
기기 간 동기화가 안 됩니다. 휴대폰에서 설정한 알레르기 정보가 PC에서는 보이지 않습니다. 각 기기에서 다시 설정해야 합니다.
서버 측 추천이 불가능합니다. “이 성분이 없는 대체 제품”을 서버에서 추천하려면 사용자의 기피성분을 알아야 합니다. 현재 구조에서는 이 추천을 브라우저에서 계산해야 합니다.
사용자 행동 분석을 할 수 없습니다. “사용자의 몇 %가 MSG를 기피하는가” 같은 통계를 낼 수 없습니다. 서버가 이 정보를 모르니까요.
이 모든 것을 포기하고 얻는 것은 하나입니다. 민감정보 유출 리스크가 0입니다. 서버에 없는 데이터는 유출될 수 없습니다.
결과
| 항목 | 내용 |
|---|---|
| 사용자 개인 데이터 저장 | 전량 localStorage |
| 서버 전송 | 없음 |
| 사용자 계정 시스템 | 없음 |
| 매칭 실행 위치 | 브라우저 (JavaScript) |
한계
브라우저 캐시가 삭제되면 모든 설정이 사라집니다. 사용자가 브라우저 데이터를 정리하면 알레르기 설정을 다시 해야 합니다. 이를 완화하기 위해 설정 내보내기/가져오기 기능을 고려하고 있지만, 아직 구현하지 않았습니다.
향후 서버 저장이 필요해질 수 있습니다. 사용자가 늘어나고 기기 간 동기화 요구가 강해지면, 서버 저장으로 전환해야 할 수 있습니다. 그때는 민감정보 처리에 대한 별도 동의와 암호화가 필수입니다. 현재 구조에서 전환할 수 있도록, 데이터 접근 함수를 한 곳에 모아두었습니다.
한 줄 교훈
서버에 없는 데이터는 유출될 수 없습니다. 민감정보를 다루는 서비스에서 “저장하지 않는 것”이 가장 강력한 보호입니다.