Supabase Realtime 을 통한 채팅 구현 - 3
Supabase 에서 Realtime 을 통해, DB에 변화가 생기면 이를 감지해 원하는 동작을 수행하도록 만드는 방법을 알았다.
따라서, 몇 가지 상태를 추가해보고 기능을 구현해보았다.
내가 직접 구현한 부분에서 문제가 있던 부분만 살펴보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
type MessageRow = Tables<"Messages">;
type MessageInsert = TablesInsert<"Messages">;
const Chat = () => {
const [messages, setMessages] = useState<MessageRow[]>([]);
const [newMessages, setNewMessages] = useState<any>(); // NOTE: type 수정하기
const [inputValue, setInputValue] = useState<string>("");
const [deletedMessageId, setDeletedMessageId] = useState<string>("");
const supabase = createClient();
const getAllMessages = async () => {
const { data, error } = await supabase
.from("Messages")
.select("*")
.order("sent_at", { ascending: true });
if (error) {
console.error(error);
return;
}
setMessages(data as MessageRow[]);
};
// customHook 으로 따로 빼서 코드 정리
useEffect(() => {
// 채팅 내역 불러오기
getAllMessages();
// INSERT 이벤트 감지
const openTalkSubscription = supabase
.channel("openTalk") // realtime 이라는 명칭만 아니면 아무 문자열이나 가능함
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "Messages"
},
// (payload) => console.log(payload), // 여기 콜백에 setState 함수 사용
() => {
setMessages([...messages, newMessages]);
}
)
.on(
"postgres_changes",
{
event: "DELETE",
schema: "public",
table: "Messages"
},
() => {
setMessages((prevMessages) => {
return prevMessages.filter(
(prevMessage) => prevMessage.message_id !== deletedMessageId
);
});
}
)
.subscribe();
}, [message]);
};
전체 코드 중에서 전반적으로 이 부분에 문제가 있었다.
데이터가 잘 보내지지만, 정확히는 INSERT
이벤트를 핸들러가 동작하지 않았던 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const openTalkSubscription = supabase
.channel("openTalk") // realtime 이라는 명칭만 아니면 아무 문자열이나 가능함
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "Messages"
},
// (payload) => console.log(payload), // 여기 콜백에 setState 함수 사용
() => {
setMessages([...messages, newMessages]);
}
);
const handleSubmit = async (evt: FormEvent<HTMLFormElement>) => {
evt.preventDefault();
const { data, error } = await supabase
.from("Messages")
.insert({
// channel_id 하드코딩 할 필요 없도록 해야함
channel_id: "214322ba-1cbd-424c-9ef1-e4b281f71675",
user_id: "2e47ab8c-da7a-4590-b114-b0512b1b22cd",
content: `${inputValue}`
})
.select("*");
if (error) {
console.error("에러: ", error);
return;
}
setNewMessages(data);
setInputValue("");
};
newMessages
는 submit 이벤트가 발생할 때, handleSubmit
함수 에서 input 태그의 값을 갖는다.
현재 내 코드가 정상적으로 동작하려면, insert()
가 일어난 다음엔 이벤트를 감지하는 것 보다 앞서 setNewMessages()
가 실행 되어야 하는 것이다. 그런데, 실제로 이 코드는 내 의도대로 동작하지 않았다.
INSERT
이벤트 핸들러가 setNewMessages()
보다 먼저 동작하기 때문으로 이해했다.
그래서, 이 부분을 아래처럼 변경했다.
이전에 작성했던 코드를 발전해 이렇게 구현했었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const openTalkSubscription = supabase
.channel("openTalk") // realtime 이라는 명칭만 아니면 아무 문자열이나 가능함
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "Messages"
},
(payload) => {
setMessages((prevMessages) => {
return [...prevMessages, payload.new as MessageRow];
});
// setState 자체가 비동기적으로 동작해서 handleSubmit 함수 내부에서 동작하는 setNewMessages() 가 제대로 실행될 거라고 보장할 수 없다.
// 따라서, 함수형으로 작성하고 데이터가 존재하는 것이 확실히 보장된 payload 객체를 이용하자!
}
);
const handleSubmit = async (evt: FormEvent<HTMLFormElement>) => {
evt.preventDefault();
const { data, error } = await supabase
.from("Messages")
.insert({
// channel_id 하드코딩 할 필요 없도록 해야함
channel_id: "214322ba-1cbd-424c-9ef1-e4b281f71675",
user_id: "2e47ab8c-da7a-4590-b114-b0512b1b22cd",
content: `${inputValue}`
})
.select("*");
if (error) {
console.error("에러: ", error);
return;
}
// setNewMessages(data); // 얘가 원인?, 실질적으로 필요하지 않을 수 있음.
setInputValue("");
};
postgres_changes
에서 이벤트 핸들러를 정의할 때 payload
객체를 통해, 실제로 DB 에 전송한 값을 가져올 수 있는 것으로 확인했다.
이를 통해 간편하게 처리해줄 수 있었다.
This post is licensed under CC BY 4.0 by the author.