Skip to content

Commit

Permalink
Feat/chat jwt : 채팅에 JWT 입히기 & 넘치는 채팅 조절 (#152)
Browse files Browse the repository at this point in the history
* feat/chat-jwt-1: refreshToken으로 Auth 변경

- 기존 accessToken으로만 가져왔던 부분 -> refreshToken으로 변경
(AppConfig에 적용)

* feat/chat-jwt-2: 채팅에 JWT 적용

- SecureStore 이용해 refreshToken 가져온 후 가져온 token을 CONNECT/SUB/CHAT 시에 사용
- 기존 채팅에서 가져왔던 memberId 삭제 및 SecurityContext 적용

* feat/chat-jwt-3 : 장문 채팅 깨짐 문제 해결

- 말풍선 보낸이에 따라 채팅 상단/하단에 위치할 지 설정 -> underlay, trailContainer
- 겹칠 때에는 ChattingBubble이 우선시 되도록 zIndex 부여 (1이 우선)

- 채팅 좌우 패딩 줄이기

* feat/chat-jwt-4: run prettier

* feat/chat-jwt-5: lint test

* feat/chat-jwt-6: SecureStore로 들어오는 refreshToken 가져오기 함수화
  • Loading branch information
KooSuYeon committed Oct 17, 2024
1 parent 94d1329 commit 68387b3
Show file tree
Hide file tree
Showing 11 changed files with 1,662 additions and 1,496 deletions.
4 changes: 3 additions & 1 deletion App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,10 @@ function AppContent() {
const memberId = await SecureStore.getItemAsync("memberId");
const accessToken =
await SecureStore.getItemAsync("accessToken");
const refreshToken =
await SecureStore.getItemAsync("refreshToken");

if (memberId && accessToken) {
if (memberId && (accessToken || refreshToken)) {
try {
const profileResponse = await getMyProfile();
if (profileResponse.data.isVerified) {
Expand Down
2,853 changes: 1,440 additions & 1,413 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"axios": "^1.6.8",
"deprecated-react-native-prop-types": "^5.0.0",
"dotenv-cli": "^7.4.2",
"expo": "~51.0.35",
"expo": "^51.0.37",
"expo-checkbox": "~3.0.0",
"expo-clipboard": "~6.0.3",
"expo-dev-client": "~4.0.27",
Expand Down Expand Up @@ -93,8 +93,8 @@
"@babel/core": "^7.24.0",
"@eslint/js": "^9.7.0",
"@stylistic/eslint-plugin-ts": "^2.3.0",
"@types/eslint__js": "^8.42.3",
"@types/eslint-config-prettier": "^6.11.3",
"@types/eslint__js": "^8.42.3",
"@types/react-native-dotenv": "^0.2.2",
"@typescript-eslint/eslint-plugin": "^7.16.1",
"@typescript-eslint/parser": "^7.16.1",
Expand Down
21 changes: 17 additions & 4 deletions src/components/chat/ChatInputSend.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import {
View,
TextInput,
Expand All @@ -12,13 +12,24 @@ import { CustomTheme } from "@styles/CustomTheme";

import IconChatSend from "@components/chat/IconChatSend";
import { useWebSocket } from "context/WebSocketContext";
import { getRefreshToken } from "util/secureStoreUtils";

const { fontBody14 } = CustomTheme;

const ChatInputSend = ({ chatroomId, memberId }) => {
const ChatInputSend = ({ chatroomId }) => {
const [chatInput, setChatInput] = useState("");
const [plusClick, setPlusClick] = useState(false);
const { publishMessage } = useWebSocket();
const [token, setToken] = useState(null);

useEffect(() => {
const fetchToken = async () => {
const token = await getRefreshToken();
setToken(token);
};

fetchToken();
}, []);

const handleInputFocus = () => {
if (plusClick) {
Expand All @@ -28,14 +39,16 @@ const ChatInputSend = ({ chatroomId, memberId }) => {

const handleSend = () => {
const trimmedChatInput = chatInput.trim();
if (trimmedChatInput) {
if (trimmedChatInput && token) {
publishMessage({
chatType: "CHAT",
chatroomId,
memberId,
message: trimmedChatInput,
token,
});
setChatInput("");
} else {
console.log("Token is missing or input is empty");
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/config/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const api = axios.create({
});

api.interceptors.request.use(async (config) => {
const token = await SecureStore.getItemAsync("accessToken");
const token = await SecureStore.getItemAsync("refreshToken");
if (token) {
config.headers["Authorization"] = `Bearer ${token}`;
}
Expand Down
71 changes: 49 additions & 22 deletions src/config/websocket.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,53 @@
import { Client } from "@stomp/stompjs";
import { WS_URL } from "@env";
import { getRefreshToken } from "util/secureStoreUtils";

export const connectWebSocket = (onMessage) => {
let client = new Client({
brokerURL: WS_URL,
debug: (str) => {
console.log(str);
},
reconnectDelay: 0,
onConnect: () => {
onMessage("Connected to WebSocket");
},
onStompError: (frame) => {
console.log("Broker reported error: " + frame.headers["message"]);
console.log("Additional details: " + frame.body);
},
onWebSocketError: (error) => {
console.log("Websocket error: " + error);
},
onWebSocketClose: () => {
console.log("Websocket connection closed");
},
});
client.activate();
export const connectWebSocket = async (onMessage) => {
try {
const token = await getRefreshToken();
if (!token) {
console.error("Failed to retrieve token");
return;
}

let client = new Client({
brokerURL: WS_URL,
debug: (str) => {
console.log(str);
},
reconnectDelay: 0,
connectHeaders: {
authorization: `Bearer ${token}`,
},
onConnect: () => {
onMessage("Connected to WebSocket");

client.publish({
destination: "/pub/chatroom/chat",
headers: {
"content-type": "application/json",
authorization: `Bearer ${token}`,
},
body: JSON.stringify({
message: "Hello, this is a test message.",
}),
});
},
onStompError: (frame) => {
console.log(
"Broker reported error: " + frame.headers["message"],
);
console.log("Additional details: " + frame.body);
},
onWebSocketError: (error) => {
console.log("Websocket error: " + error);
},
onWebSocketClose: () => {
console.log("Websocket connection closed");
},
});
client.activate();
} catch (error) {
console.error("Failed to retrieve the token:", error);
}
};
101 changes: 66 additions & 35 deletions src/context/WebSocketContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Client } from "@stomp/stompjs";
import { getChatroomsByType, getChatsByChatroomId } from "../config/api";
import Loading from "@components/common/loading/Loading";
import { sortByIds } from "util/util";
import { getRefreshToken } from "util/secureStoreUtils";

const WebSocketContext = createContext(null);

Expand All @@ -17,42 +18,53 @@ export const WebSocketProvider = ({ children }) => {
const [chatrooms, setChatrooms] = useState([]);
const [messages, setMessages] = useState({});
const [isConnected, setIsConnected] = useState(false);
const [token, setToken] = useState(null);
const WS_URL = process.env.EXPO_PUBLIC_WS_URL;

useEffect(() => {
ws.current = new Client({
brokerURL: WS_URL,
debug: (str) => {
console.log(str);
},
reconnectDelay: 0,
onConnect: async () => {
const { allChatrooms } = await updateChatroomsAndMessages();
subscribeToChatrooms(allChatrooms);
setIsConnected(true);
},
onStompError: (frame) => {
console.log(
"Broker reported error: " + frame.headers["message"],
);
console.log("Additional details: " + frame.body);
},
onWebSocketError: (error) => {
console.log("WebSocket error: " + error);
},
onWebSocketClose: () => {
console.log("WebSocket connection closed");
},
});
const connectWebSocket = async () => {
const token = await getRefreshToken();
setToken(token);

ws.current.activate();
ws.current = new Client({
brokerURL: WS_URL,
debug: (str) => {
console.log(str);
},
reconnectDelay: 0,
connectHeaders: {
authorization: `Bearer ${token}`,
},
onConnect: async () => {
const { allChatrooms } = await updateChatroomsAndMessages();
subscribeToChatrooms(allChatrooms, token);
setIsConnected(true);
},
onStompError: (frame) => {
console.log(
"Broker reported error: " + frame.headers["message"],
);
console.log("Additional details: " + frame.body);
},
onWebSocketError: (error) => {
console.log("WebSocket error: " + error);
},
onWebSocketClose: () => {
console.log("WebSocket connection closed");
},
});

ws.current.activate();
};

connectWebSocket();

return () => {
if (ws.current) {
disconnectWebSocket();
}
};
}, []);
}, [token]);

const updateChatroomsAndMessages = async () => {
const { allChatrooms, initialMessages } =
Expand All @@ -62,18 +74,30 @@ export const WebSocketProvider = ({ children }) => {
return { allChatrooms, initialMessages };
};

const subscribeToChatrooms = (chatrooms) => {
const subscribeToChatrooms = (chatrooms, token) => {
chatrooms.forEach(({ id }) => {
ws.current.subscribe(`/sub/chatroom/${id}`, (message) => {
handleIncomingMessage(id, message.body);
});
ws.current.subscribe(
`/sub/chatroom/${id}`,
(message) => {
handleIncomingMessage(id, message.body);
},
{
authorization: `Bearer ${token}`,
},
);
});
};

const subscribeToNewChatroom = (chatroomId) => {
ws.current.subscribe(`/sub/chatroom/${chatroomId}`, (message) => {
handleIncomingMessage(chatroomId, message.body);
});
const subscribeToNewChatroom = (chatroomId, token) => {
ws.current.subscribe(
`/sub/chatroom/${chatroomId}`,
(message) => {
handleIncomingMessage(chatroomId, message.body);
},
{
authorization: `Bearer ${token}`,
},
);
};

const handleIncomingMessage = (chatroomId, message) => {
Expand Down Expand Up @@ -109,10 +133,17 @@ export const WebSocketProvider = ({ children }) => {

const publishMessage = (message) => {
if (ws.current && ws.current.connected) {
const { token, ...messageWithoutToken } = message;

ws.current.publish({
destination: `/pub/chatroom/chat`,
body: JSON.stringify(message),
headers: {
"content-type": "application/json",
authorization: `Bearer ${token}`,
},
body: JSON.stringify(messageWithoutToken),
});
console.log(messageWithoutToken);
} else {
console.log("Client is not connected");
}
Expand Down
Loading

0 comments on commit 68387b3

Please sign in to comment.