[Next.js] per-page layout을 이용해서 백엔드 socket.io 연결하기
Next.js 를 사용하면서 별도의 백엔드에서 돌아가고 있는 socket.io 웹소켓에 연결을 할 필요가 있었다.
const socket = io("https://server-domain.com");
그런데 각페이지에서 socket 연결을 만들면 페이지 이동 시 socket의 연결이 끊어졌다 다시 재연결하는 문제가 생긴다
이 문제를 해결하기위해 useContext를 이용해보자고 생각했다.
lib/socketContext.tsx
import { Manager, Socket, io } from "socket.io-client";
interface SocketContextValue {
chatSocket: Socket | null;
}
const SocketContext = createContext<SocketContextValue>({
chatSocket: null,
});
interface SocketProviderProps {
children: ReactNode;
}
먼저 SocketContext 생성에 필요한 타입을 선언하고 Context를 생성해준다. chatSocket 변수에 socket.io가 제공하는 Socket 객체를 담을 수 있도록 한다.
const SocketProvider = ({ children }: SocketProviderProps) => {
const [chatSocket, setChatSocket] = useState<Socket | null>(null);
useEffect(() => {
const manager = new Manager(`${SERVER_ADDRESS}`, {
extraHeaders: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
}
});
const newChatSocket = manager.socket("/chat-room", {
});
setChatSocket(newChatSocket);
return () => {
newChatSocket.close();
};
}, []);
return (
<UsersProvider>
<SocketContext.Provider
value={{ chatSocket }}
>
{children}
</SocketContext.Provider>
</UsersProvider>
);
};
다음으로 자식 컴포넌트를 감싸줄 Provider를 생성한다.
extraHeaders에 토큰을 넣어준 이유는 현재 진행중인 프로젝트는 로그인을 한 유저만 소켓연결이 가능하기 때문이다.
이때문에 return에서 SocketContext.Provider를 유저정보를 가져오는 UserProvider로 한번 더 감싸주었다.
pages/page.tsx
Page.getLayout = function getLayout(page: ReactElement) {
return (
<SocketProvider>
<Layout>{page}</Layout>
</SocketProvider>
);
};
이제 각 Page의 getLayout에서 page를 Provider로 감싸주면 해당 구조를 가진 페이지로 이동할 경우 Socket 연결이 끊어지지않고 이동된다.
※ 참고
Routing: Pages and Layouts | Next.js (nextjs.org)
Routing: Pages and Layouts | Next.js
Using Pages Router Features available in /pages
nextjs.org
import { SocketContext } from "@/lib/socketContext";
const { chatSocket: socket } = useContext(SocketContext);
useEffect(() => {
if (socket) {
socket.on("friendList", (data) => {
setuserData(data);
});
}
return () => {
if (socket) {
socket.off("friendList");
}
}
}, [socket]);
이런식으로 page 컴포넌트들에서 자유롭게 소켓 사용이 가능하다.
다만 useEffect 내에서 사용할 경우 return에 socket.off를 통해 등록한 이벤트를 해제하지 않으면 다음 해당 페이지에 들어올때 이벤트가 여러번 발생하니 주의해야한다.