1) public.users ‒ 회원(=auth.users)의 확장 테이블로 사용 ‒ 집계형 필드만 둡니다. id (PK, auth.users FK) membership_level TEXT → FK public.membership_levels.level my_zzim INT -- 내가 받은 찜 총합 zzim_mile INT -- 누적 마일리지 available_zzim_mile INT -- 사용가능(미소모) 마일리지 today_zzim_count INT today_zzim_date DATE created_at, updated_at 2) public.membership_levels level PK ('basic','premium'…) api_call_limit INT daily_zzim_limit INT max_zzim_mileage INT mileage_per_zzim INT 3) public.user_markets id PK user_id FK → public.users.id my_markets JSONB -- [{ market_url , … , zzim_received_count , … }] created_at, updated_at 4) 뷰(View) 하나 추가하면 코드가 훨씬 단순합니다. CREATE OR REPLACE VIEW public.v_user_market_stats AS SELECT u.id AS user_id, u.membership_level, u.available_zzim_mile, u.my_zzim, u.zzim_mile, m.my_markets FROM public.users u LEFT JOIN public.user_markets m ON m.user_id = u.id; -- 한번만 실행 WITH s AS ( SELECT user_id, COALESCE(SUM((m->>'zzim_received_count')::INT),0) AS total_zzim FROM public.user_markets, LATERAL jsonb_array_elements(my_markets) m GROUP BY user_id ) UPDATE public.users u SET my_zzim = s.total_zzim FROM s WHERE u.id = s.user_id;