훈돌라
8. 7. 소셜 로그인 auth 정보를 public.users 정보로 옮기는 sql ( 닉네임 변경 가능 ) 본문
create or replace function public.handle_user_update()
returns trigger as $$
declare
new_nickname text;
begin
-- 닉네임 값을 결정
new_nickname := coalesce(nullif(new.raw_user_meta_data->>'display_name', ''), new.raw_user_meta_data->>'full_name');
-- 새로운 데이터 삽입 또는 업데이트
insert into public.users (id, nickname, email)
values (new.id, new_nickname, new.email)
on conflict (id) do update set
nickname = excluded.nickname,
email = excluded.email;
return new;
end;
$$ language plpgsql security definer;
-- 트리거 생성
create trigger on_auth_user_insert_or_update
after insert or update on auth.users
for each row execute procedure public.handle_user_update();
기존 sql
-- 함수 생성 또는 교체
create or replace function public.handle_user_update()
returns trigger as $$
declare
new_nickname text;
begin
-- 닉네임 값을 결정
new_nickname := coalesce(nullif(new.raw_user_meta_data->>'display_name', ''), new.raw_user_meta_data->>'full_name');
-- 새로운 데이터 삽입 또는 업데이트
insert into public.users (id, nickname, email)
values (new.id, new_nickname, new.email)
on conflict (id) do update set
nickname = excluded.nickname,
email = excluded.email;
-- auth.users의 display_name이 변경된 경우 public.users의 nickname을 업데이트
if new.raw_user_meta_data->>'display_name' is not null then
update public.users
set nickname = new.raw_user_meta_data->>'display_name'
where id = new.id;
end if;
return new;
end;
$$ language plpgsql security definer;
-- 트리거 생성
create trigger on_auth_user_insert_or_update
after insert or update on auth.users
for each row execute procedure public.handle_user_update();
수정된 sql
import useMyModalStore from '@/stores/my.modal.store';
import React, { useEffect, useRef, useState } from 'react';
import { supabase } from '@/supabase/client';
import useUserStore from '@/stores/user.store'; // 유저 상태 관리 스토어 추가
const NicknameModal: React.FC = () => {
const { isNicknameModalOpen, toggleNicknameModal } = useMyModalStore((state) => state);
const { nickname, setNickname } = useUserStore((state) => state); // 유저 상태 관리 스토어에서 닉네임 가져오기
const nicknameRef = useRef<HTMLInputElement>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchNickname = async () => {
try {
const {
data: { user },
error: userError
} = await supabase.auth.getUser();
if (userError) throw userError;
if (user) {
const { data, error } = await supabase.from('users').select('nickname').eq('id', user.id).single();
if (error) {
console.error('닉네임 페치 실패:', error);
} else {
console.log('닉네임 페치 성공:', data.nickname);
if (data.nickname !== null) {
setNickname(data.nickname); // 전역 상태에 닉네임 설정
}
}
}
} catch (error) {
console.error('유저 가져오기 실패:', error);
}
};
fetchNickname();
}, [setNickname]);
const handleBackGroundClick = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
if (e.target === e.currentTarget) {
toggleNicknameModal();
}
};
const handleNicknameSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (nicknameRef.current) {
const newNickname = nicknameRef.current.value.trim();
if (newNickname.length < 2 || newNickname.length > 8) {
setError('닉네임은 최소 2글자, 최대 8글자입니다.');
return;
}
try {
const {
data: { user },
error: userError
} = await supabase.auth.getUser();
if (userError) throw userError;
if (user) {
const { error } = await supabase.auth.updateUser({ data: { display_name: newNickname } });
if (error) {
console.error('닉네임 업데이트 실패:', error);
} else {
console.log('닉네임 업데이트 성공:', newNickname);
setNickname(newNickname); // 전역 상태에 닉네임 업데이트
toggleNicknameModal();
}
}
} catch (error) {
console.error('닉네임 업데이트 중 오류 발생:', error);
}
}
};
return (
<>
{isNicknameModalOpen && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"
onClick={handleBackGroundClick}
>
<div className="bg-white p-9 rounded-[10px]" style={{ width: '20em' }}>
<div onClick={(e) => e.stopPropagation()}>
<div className="flex justify-between items-center mb-4">
<h1 className="text-xl font-bold text-left text-black">닉네임 변경</h1>
<button className="text-black" onClick={toggleNicknameModal} type="button">
✕
</button>
</div>
<form className="flex flex-col gap-2" onSubmit={handleNicknameSubmit}>
<input
type="text"
placeholder="새 닉네임 입력"
className="mb-4 p-2 border rounded w-full text-black"
ref={nicknameRef}
defaultValue={nickname ?? ''}
/>
{error && <p className="text-red-500">{error}</p>}
<div className="flex flex-col gap-2">
<button
type="submit"
className="px-4 py-2 w-full text-white rounded"
style={{ backgroundColor: '#9E9E9E' }}
onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = '#008A02')}
onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = '#9E9E9E')}
>
변경하기
</button>
</div>
</form>
</div>
</div>
</div>
)}
</>
);
};
export default NicknameModal;
await supabase.auth.updateUser({ data: { display_name: newNickname} });
public.users 의 닉네임 변경이 안 되는 문제 ->
닉네임 업데이트 하는 대상을 public.users 의 nickname 이 아닌 auth.users 의 display name 으로 변경
( 소셜 로그인에 성공하면 auth.users 의 display name 에 raw.user.meta.data (nickname) 이 담김 )
( 일반 유저만 수정이 가능한게 아닌 소셜 로그인을 이용한 유저도 수정 가능하게 하기 위함. )
sql 문에 의하면 auth.users -> public.users 데이터 전달 과정 중에 display name 이 있다면 그 데이터를 public.users nickname 으로사용한다고 되어있어서 이럴거면 차라리 supabase sign up 시에도 nickname 값을 auth.users display name 에다 넣고 수정 시도도 auth users display name 으로 하면 되지 않을까? 하는 생각에서 해결한 케이스.