Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"devDependencies": {
"@chromatic-com/storybook": "^3.2.4",
"@eslint/js": "^9.17.0",
"@mswjs/socket.io-binding": "^0.1.1",
"@storybook/addon-essentials": "^8.5.0",
"@storybook/addon-interactions": "^8.5.0",
"@storybook/addon-onboarding": "^8.5.0",
Expand Down
18 changes: 18 additions & 0 deletions src/frontend/src/__mock__/handlers/chatting.handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { WebSocketLink, ws } from 'msw'

import { CHAT_SERVER_URL } from '@/constants/env'
import { log } from '@/utils/log'

const chattingServer: WebSocketLink = ws.link(CHAT_SERVER_URL)

export const chattingHandlers = [
chattingServer.addEventListener('connection', ({ client }) => {
log('✅ WebSocket connection initiated')

// 클라이언트 메시지를 서버로 전달
client.addEventListener('message', (event) => {
// 연결 시도만 하고 실제 전송은 하지 않음
log('Message from client:', event.data)
})
})
]
64 changes: 64 additions & 0 deletions src/frontend/src/__mock__/handlers/signaling.handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { toSocketIo } from '@mswjs/socket.io-binding'
import { WebSocketLink, ws } from 'msw'

import { SIGNALING_NODE_SERVER_URL } from '@/constants/env'
import { log } from '@/utils/log'

const signalingServer: WebSocketLink = ws.link(
`${SIGNALING_NODE_SERVER_URL}/socket.io/?EIO=4&transport=websocket`
)

export const signalingHandlers = [
signalingServer.addEventListener('connection', (connection) => {
log('✅ WebSocket connection initiated')

const io = toSocketIo(connection)
const { client } = io
const mockSocketId = 'mock-' + Math.random().toString(36).substring(2, 9)
let roomName: string
let userId: string

// 클라이언트 메시지를 서버로 전달
connection.client.addEventListener('message', (event) => {
// 연결 시도만 하고 실제 전송은 하지 않음
log('Message from client:', event.data)
})

// 클라이언트 이벤트 처리
client.on('join_room', (_, data: unknown) => {
try {
const { roomName: room, userId: user } = data as { roomName: string; userId: string }
roomName = room
userId = user
log(`✅ User ${userId} joined room ${roomName}`)
client.emit('welcome', mockSocketId, userId)
} catch (e) {
log('Error in join_room:', e)
}
})

// 나머지 이벤트 핸들러들...
client.on('offer', (_, offer, remoteId) => {
log('Received offer for:', remoteId)
client.emit('offer', offer, mockSocketId, userId)
})

client.on('answer', (_, answer, remoteId) => {
log('Received answer for:', remoteId)
client.emit('answer', answer, mockSocketId, userId)
})

client.on('ice', (_, ice, remoteId) => {
log('Received ICE candidate for:', remoteId)
client.emit('ice', ice, mockSocketId, userId)
})

client.on('disconnect', () => {
log('❌ Client disconnected:', mockSocketId)
client.emit('user_left', mockSocketId)
})

// 초기 handshake 응답
client.emit('connect')
})
]
6 changes: 5 additions & 1 deletion src/frontend/src/__mock__/worker.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { setupWorker } from 'msw/browser'

import { authHandler } from './handlers/auth.handler'
import { chattingHandlers } from './handlers/chatting.handler'
import { searchHandler } from './handlers/search.handler'
import { serviceHandler } from './handlers/service.handler'
import { signalingHandlers } from './handlers/signaling.handler'
import { userHandler } from './handlers/user.handler'

export const worker = setupWorker(
...authHandler,
...serviceHandler,
...userHandler,
...searchHandler
...searchHandler,
...signalingHandlers,
...chattingHandlers
)
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { useEffect, useRef } from 'react'
import io, { Socket } from 'socket.io-client'

import { SIGNALING_NODE_SERVER_URL, TURN_SERVER_URL } from '@/constants/env'
import { TURN_SERVER_URL } from '@/constants/env'
import { useSignalingSocket } from '@/stores/use-signaling-socket'
import { useUserStatus } from '@/stores/use-user-status-store'

import useGetSelfUser from './queries/user/useGetSelfUser'

export function useSingalingWithMeshSocket(
channelId: number,
channelName: string,
serverName: string
) {
export function useSingaling() {
const { getCurrentChannelInfo, joinVoiceChannel, leaveVoiceChannel } = useUserStatus()
const { on, off, emit } = useSignalingSocket()
const selfUser = useGetSelfUser()

// 레퍼런스
Expand All @@ -20,13 +17,8 @@ export function useSingalingWithMeshSocket(
const myStreamRef = useRef<MediaStream | null>(null) // 내 로컬 미디어 스트림
const peersRef = useRef<Record<string, RTCPeerConnection>>({}) // socketId -> RTCPeerConnection
const userMapRef = useRef<Record<string, string>>({}) // socketId -> userId (상대방 표시용)
const socket = useRef<Socket>(
io(SIGNALING_NODE_SERVER_URL, {
// path: '/socket.io',
// transports: ['websocket'],
withCredentials: true
})
).current

const { channelId } = getCurrentChannelInfo() ?? {}

// -----------------------------
// (1) 미디어 스트림 획득
Expand Down Expand Up @@ -85,12 +77,12 @@ export function useSingalingWithMeshSocket(
}
}

socket.on('user_left', handleUserLeft)
on('user_left', handleUserLeft)

return () => {
socket.off('user_left', handleUserLeft)
off('user_left', handleUserLeft)
}
}, [socket])
}, [on, off])

// -----------------------------
// (4) 소켓 이벤트: welcome/offer/answer/ice
Expand Down Expand Up @@ -118,7 +110,7 @@ export function useSingalingWithMeshSocket(
// ICE candidate
pc.addEventListener('icecandidate', (event) => {
if (event.candidate) {
socket.emit('ice', event.candidate, socketId)
emit('ice', event.candidate, socketId)
}
})

Expand Down Expand Up @@ -180,7 +172,7 @@ export function useSingalingWithMeshSocket(
await peersRef.current[newSocketId].setLocalDescription(offer)

// 서버에 offer 전송
socket.emit('offer', offer, newSocketId)
emit('offer', offer, newSocketId)
}

// offer 수신 (내가 나중에 들어왔을 때)
Expand All @@ -203,7 +195,7 @@ export function useSingalingWithMeshSocket(
const answer = await peersRef.current[remoteId].createAnswer()
await peersRef.current[remoteId].setLocalDescription(answer)

socket.emit('answer', answer, remoteId)
emit('answer', answer, remoteId)
}

// answer 수신
Expand Down Expand Up @@ -232,18 +224,18 @@ export function useSingalingWithMeshSocket(
}
}

socket.on('welcome', handleWelcome)
socket.on('offer', handleOffer)
socket.on('answer', handleAnswer)
socket.on('ice', handleIce)
on('welcome', handleWelcome)
on('offer', handleOffer)
on('answer', handleAnswer)
on('ice', handleIce)

return () => {
socket.off('welcome', handleWelcome)
socket.off('offer', handleOffer)
socket.off('answer', handleAnswer)
socket.off('ice', handleIce)
off('welcome', handleWelcome)
off('offer', handleOffer)
off('answer', handleAnswer)
off('ice', handleIce)
}
}, [socket])
}, [on, off, emit])

// -----------------------------
// (5) 방 입장
Expand All @@ -256,9 +248,15 @@ export function useSingalingWithMeshSocket(
}
}, [channelId, getCurrentChannelInfo])

function joinChannel() {
socket.connect()

function joinChannel({
channelId,
channelName,
serverName
}: {
channelId: number
channelName: string
serverName: string
}) {
getMedia()

joinVoiceChannel({
Expand All @@ -267,7 +265,7 @@ export function useSingalingWithMeshSocket(
serverName
})

socket.emit('join_room', { roomName: channelId, userId: selfUser.id })
emit('join_room', { roomName: channelId, userId: selfUser.id })
}

function leaveChannel() {
Expand All @@ -276,8 +274,6 @@ export function useSingalingWithMeshSocket(
myStreamRef.current.getTracks().forEach((track) => track.stop())
myStreamRef.current = null
}
// 소켓 해제
socket.disconnect()

// 내 비디오 제거
if (myFaceRef.current) {
Expand All @@ -292,16 +288,7 @@ export function useSingalingWithMeshSocket(
leaveVoiceChannel()
}

// -----------------------------
// (10) 페이지 떠날 때 소켓 해제
// -----------------------------
useEffect(() => {
return function cleanup() {
socket.disconnect()
}
}, [socket])

const isInVoiceChannel = getCurrentChannelInfo()?.channelId === channelId
const isInVoiceChannel = getCurrentChannelInfo()

return {
callRef,
Expand Down
5 changes: 3 additions & 2 deletions src/frontend/src/layouts/main-layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import useGetSelfUser from '@/hooks/queries/user/useGetSelfUser'
import { useChattingStomp } from '@/hooks/use-chatting-stomp'
import { useMediaSettingsStore } from '@/stores/use-media-setting.store'
import { useServerUnreadStore } from '@/stores/use-server-unread-store'
import { useSignalingStomp } from '@/stores/use-signaling-stomp-store'
import { useSignalingSocket } from '@/stores/use-signaling-socket'

import ProfileCard from './components/profile-card'
import ProfileStatusButton from './components/profile-status-button'
import ServerCreateModal from './components/server-create-modal'
import { ServerList } from './components/server-list'
import { ServerListSkeleton } from './components/server-list/server-list-skeleton'
import SettingModal, { SettingModalTabsID } from './components/setting-modal'

const Inner = () => {
const {
connect: connectChatting,
Expand All @@ -29,7 +30,7 @@ const Inner = () => {
unsubscribe,
checkConnection
} = useChattingStomp()
const { connect: connectSignaling, disconnect: disconnectSignaling } = useSignalingStomp()
const { connect: connectSignaling, disconnect: disconnectSignaling } = useSignalingSocket()
const { serverId } = useParams<{ serverId: string }>()
const previousServerId = useRef<number | null>(null)

Expand Down
2 changes: 0 additions & 2 deletions src/frontend/src/layouts/root-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import { QueryClientProvider } from '@tanstack/react-query'
import { Toaster } from 'react-hot-toast'
import { Outlet } from 'react-router-dom'

import MSWProvider from '@/libs/msw'
import queryClient from '@/libs/query-client'
function RootLayout() {
return (
<div className='h-full min-h-screen max-w-full bg-blue-10'>
<title>Biscord</title>
<MSWProvider />
<QueryClientProvider client={queryClient}>
<Outlet />
<Toaster position='top-right' />
Expand Down
16 changes: 0 additions & 16 deletions src/frontend/src/libs/msw.tsx

This file was deleted.

22 changes: 19 additions & 3 deletions src/frontend/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import { startTransition } from 'react'
import { createRoot } from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'

import { MOCK_SERVICE_WORKER } from '@/constants/env'

import { router } from './routes'

createRoot(document.getElementById('root') as HTMLElement).render(
<RouterProvider router={router} />
)
async function enableMocking() {
if (MOCK_SERVICE_WORKER) {
const { worker } = await import('@/__mock__/worker')
worker.start({
onUnhandledRequest: 'bypass'
})
}
}

enableMocking().then(() => {
startTransition(() => {
createRoot(document.getElementById('root') as HTMLElement).render(
<RouterProvider router={router} />
)
})
})
14 changes: 7 additions & 7 deletions src/frontend/src/pages/channel/components/voice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import toast from 'react-hot-toast'

import ChatArea from '@/components/chat-area'
import CustomButton from '@/components/custom-button'
import { useSingalingWithMeshSocket } from '@/hooks/use-singaling-with-mesh'
import { useSingaling } from '@/hooks/use-singaling'
import { cn } from '@/libs/cn'
import { ChatUser } from '@/types/user'

Expand All @@ -26,14 +26,14 @@ function VideoComponent({
}: Props) {
const [sideBar, setSideBar] = useState(false)

const { joinChannel, leaveChannel, isInVoiceChannel, callRef, myFaceRef } =
useSingalingWithMeshSocket(channelId, channelName, serverName)

// const { joinChannel, leaveChannel, isInVoiceChannel, callRef, myFaceRef } =
// useSignalingWithSFU(channelId, channelName, serverName)
const { joinChannel, leaveChannel, isInVoiceChannel, callRef, myFaceRef } = useSingaling()

const handleJoinVoiceChannel = () => {
joinChannel()
joinChannel({
channelId,
channelName,
serverName
})
}

const handleLeaveVoiceChannel = () => {
Expand Down
Loading