Skip to content

[Feature] #42 웹소켓 전역에서 사용하도록 추가 #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Dec 4, 2024
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
117 changes: 116 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"build-and-test": "npm run build && lhci autorun"
},
"dependencies": {
"@stomp/stompjs": "^7.0.0",
"@emotion/react": "^11.13.5",
"@emotion/styled": "^11.13.5",
"@mui/material": "^6.1.9",
Expand All @@ -28,6 +29,7 @@
"react-icons": "^5.3.0",
"react-router-dom": "^6.28.0",
"react-textarea-autosize": "^8.5.5",
"sockjs-client": "^1.6.1",
"styled-components": "^6.1.13",
"styled-reset": "^4.5.2",
"zustand": "^5.0.1"
Expand All @@ -39,6 +41,7 @@
"@types/ol": "^7.0.0",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@types/sockjs-client": "^1.5.4",
"@types/react-textarea-autosize": "^8.0.0",
"@types/styled-components": "^5.1.34",
"@typescript-eslint/eslint-plugin": "^7.8.0",
Expand Down
10 changes: 7 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { router } from '~/router'
import GlobalStyle from '~/styles/globalStyle'
import { darkTheme, lightTheme } from '~/styles/theme'
import Loader from '~components/Loader'
import { WebSocketProvider } from '~/WebSocketContext'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

const queryClient = new QueryClient()
Expand All @@ -15,8 +16,10 @@ function App() {
//* 다크모드 확장성 고려
const [theme, setTheme] = useState(lightTheme)
const toggleTheme = () => setTheme(prev => (prev === lightTheme ? darkTheme : lightTheme))

return (
<>
<WebSocketProvider>
<HelmetProvider>
<ThemeProvider theme={theme}>
<QueryClientProvider client={queryClient}>
Expand All @@ -34,9 +37,10 @@ function App() {
</Suspense>
</MobileContainer>
<PWABadge />
</QueryClientProvider>
</ThemeProvider>
</HelmetProvider>
</QueryClientProvider>
</ThemeProvider>
</HelmetProvider>
</WebSocketProvider>
</>
)
}
Expand Down
82 changes: 82 additions & 0 deletions src/WebSocketContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { createContext, useContext, useEffect, useState } from 'react'
import { Client } from '@stomp/stompjs'
import SockJS from 'sockjs-client'

interface WebSocketContextType {
client: Client | null
isConnected: boolean
subscribe: (destination: string, callback: (message: any) => void) => void

Check warning on line 8 in src/WebSocketContext.tsx

View workflow job for this annotation

GitHub Actions / lighthouse

Unexpected any. Specify a different type
publish: (destination: string, body: any) => void

Check warning on line 9 in src/WebSocketContext.tsx

View workflow job for this annotation

GitHub Actions / lighthouse

Unexpected any. Specify a different type
}

const WebSocketContext = createContext<WebSocketContextType | null>(null)

export const WebSocketProvider = ({ children }: { children: React.ReactNode }) => {
const [client, setClient] = useState<Client | null>(null)
const [isConnected, setIsConnected] = useState(false)

useEffect(() => {
const token = localStorage.getItem('token')
const SERVER_URL = 'https://ddang.shop/ws'

const socket = new SockJS(SERVER_URL)
const stompClient = new Client({
webSocketFactory: () => socket,
connectHeaders: {
Authorization: `Bearer ${token}`,
},
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
onConnect: () => {
console.log('WebSocket 연결 성공')
setIsConnected(true)
},
onStompError: frame => {
console.error('Stomp 오류:', frame)
},
onWebSocketClose: () => {
console.log('WebSocket 연결 종료')
setIsConnected(false)
},
})

stompClient.activate()
setClient(stompClient)

return () => {
stompClient.deactivate()
}
}, [])

const subscribe = (destination: string, callback: (message: any) => void) => {

Check warning on line 52 in src/WebSocketContext.tsx

View workflow job for this annotation

GitHub Actions / lighthouse

Unexpected any. Specify a different type
if (client && isConnected) {
client.subscribe(destination, message => {
callback(message)
})
}
}

const publish = (destination: string, body: any) => {

Check warning on line 60 in src/WebSocketContext.tsx

View workflow job for this annotation

GitHub Actions / lighthouse

Unexpected any. Specify a different type
if (client && isConnected) {
client.publish({
destination,
body: JSON.stringify(body),
})
}
}

return (
<WebSocketContext.Provider value={{ client, isConnected, subscribe, publish }}>
{children}
</WebSocketContext.Provider>
)
}

export const useWebSocket = () => {

Check warning on line 76 in src/WebSocketContext.tsx

View workflow job for this annotation

GitHub Actions / lighthouse

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
const context = useContext(WebSocketContext)
if (!context) {
throw new Error('useWebSocket must be used within a WebSocketProvider')
}
return context
}
Loading
Loading