NextJS도 백엔드 프레임워크 역할을 하므로 Express를 사용하지 않고 NextJS 하나로 소켓을 이용한 채팅 기능을 구현해보았다.
// pages/api/socket.ts
import { Server } from 'Socket.IO';
const SocketHandler = (req, res) => {
if (res.socket.server.io) {
console.log('이미 바인딩 되었습니다.');
} else {
console.log('서버-소켓 연결완료');
const io = new Server(res.socket.server);
res.socket.server.io = io;
}
res.end();
};
export default SocketHandler;
emit한 데이터를 on으로 받는다!
// pages/api/chat.ts
import { NextApiRequest } from "next";
export default (req: NextApiRequest, res: any) => {
if (req.method === 'POST') {
// 메시지 얻기
const message = req.body;
// on('message')가 메시지를 받음
res?.socket?.server?.io?.emit('message', message);
res.status(201).json(message);
}
};
import { chatAPI } from '../utils/api/chat';
import { Box, Button, HStack, Input, Text } from '@chakra-ui/react';
import React, { useState, useEffect } from 'react';
import io from 'socket.io-client';
export interface IMsg {
user: string;
msg: string;
}
let socket: SocketIOClient.Socket;
// 랜덤 유저
const user = 'User_' + String(new Date().getTime()).substr(-3);
// 내 메시지
const Me = ({ message }: { message: string }) => {
return (
<Box p="6px 10px">
<Text as="p">Me</Text>
<Box
bgColor="blue.400"
color="white"
maxW="320px"
borderRadius={8}
p="6px 8px"
display="inline-block"
>
<Text>{message}</Text>
</Box>
</Box>
);
};
// 상대 메시지
const Other = ({ user, message }: { user: string; message: string }) => {
return (
<Box p="6px 10px" textAlign="right">
<Text as="p" textAlign="right">
{user}
</Text>
<Box
bgColor="green.400"
color="white"
maxW="320px"
borderRadius={8}
p="6px 8px"
display="inline-block"
>
<Text>{message}</Text>
</Box>
</Box>
);
};
function index() {
// connected flag
const [connected, setConnected] = useState<boolean>(false);
// init chat and message
const [chat, setChat] = useState<IMsg[]>([]);
const [msg, setMsg] = useState<string>('');
// 소켓 연결
useEffect(() => {
const socketInitializer = async () => {
await fetch('/api/socketio');
socket = io();
socket.on('connect', () => {
console.log('connected', socket);
setConnected(true);
});
socket.on('error', (error: any) => {
console.log(error);
});
socket.on('message', (message: IMsg) => {
chat.push(message);
console.log(chat);
setChat([...chat]);
});
};
socketInitializer();
// 브라우저가 꺼지면 소켓 연결 종료
return () => {
if (socket) {
socket.disconnect();
}
};
}, []);
const sendMessage = async () => {
if (msg) {
const message: IMsg = {
user,
msg,
};
// send Message to Other user
const res = await chatAPI(message);
// reset field if OK
if (res.status === 201) {
setMsg('');
}
}
};
return (
<Box>
<Box
borderRadius={4}
w="480px"
h="640px"
m="0 auto"
mt="32px"
bgColor="gray.200"
overflowY="scroll"
>
{chat.map((chat, i) => (
<Box key={i}>
{chat.user === user ? (
<Me message={chat.msg} />
) : (
<Other user={chat.user} message={chat.msg} />
)}
</Box>
))}
</Box>
<HStack w="480px" h="75px" m="0 auto" mt="12px">
<Input
type="text"
w="75%"
h="100%"
value={msg}
placeholder={connected ? '메시지를 입력하세요' : '연결중입니다...'}
isDisabled={!connected}
onChange={(e) => {
setMsg(e.target.value);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
sendMessage();
}
}}
/>
<Button w="25%" h="100%" onClick={sendMessage} isLoading={!connected}>
보내기
</Button>
</HStack>
</Box>
);
}
export default index;
https://codesandbox.io/s/nextjs-socketio-chat-piffv?file=/src/pages/index.tsx
위 코드를 그대로 갖다 쓰면 클라이언트에서 소켓 연결이 안되었다. 왜 그러는지는 아직 잘 모르겠다..
소켓을 사용할 때 TS에서 타입을 어떻게 지정하는지도 공부가 필요할듯