diff --git a/package-lock.json b/package-lock.json index 60875e21..5a0cde2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,14 @@ "dependencies": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", + "@stomp/stompjs": "^7.0.0", "@tanstack/react-query": "^4.36.1", + "@types/stompjs": "^2.3.7", "axios": "^1.5.1", "d3": "^7.8.5", "emotion-reset": "^3.0.1", "framer-motion": "^10.16.4", + "http-proxy-middleware": "^2.0.6", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.47.0", @@ -1692,6 +1695,11 @@ "integrity": "sha512-2yn4qTkXZTByQffL3ymS6viYuyZk3YnJT49bopGBlm9Thtyfa7iuFUV6tt+09YIRO1sjmSWILf4dPj6+Dr5YVA==", "dev": true }, + "node_modules/@stomp/stompjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.0.0.tgz", + "integrity": "sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw==" + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -2483,6 +2491,14 @@ "integrity": "sha512-uK2z1ZHJyC0nQRbuovXFt4mzXDwf27vQeUWNhfKGwRcWW429GOhP8HxUHlM6TLH4bzmlv/HlEjpvJh3JfmGsAA==", "dev": true }, + "node_modules/@types/http-proxy": { + "version": "1.17.13", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.13.tgz", + "integrity": "sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/js-levenshtein": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.2.tgz", @@ -2506,7 +2522,6 @@ "version": "20.8.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", - "dev": true, "dependencies": { "undici-types": "~5.25.1" } @@ -2560,6 +2575,14 @@ "integrity": "sha512-NwCYScf83RIwCyi5/9cXocrJB//xrqMh5PMw3mYTSFGaI3DuVjBLfO/PCk7QVAC3Da8b9NjxNmTO9Aj9T3rl/Q==", "dev": true }, + "node_modules/@types/stompjs": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@types/stompjs/-/stompjs-2.3.7.tgz", + "integrity": "sha512-E9Gw+P2ifAIdo6DFXVvR8sDqmCadVVJc5JyEh3hAGIBz24dMidxfrrMahfQT+zDbDYnvi4rDpvrpSI+3UkiSPw==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz", @@ -3355,7 +3378,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -5214,7 +5236,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5702,6 +5723,47 @@ "react-is": "^16.7.0" } }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -6110,7 +6172,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6158,7 +6219,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -6224,7 +6284,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -6253,6 +6312,17 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -6793,7 +6863,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -7435,7 +7504,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -7749,6 +7817,11 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -8575,7 +8648,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -8780,8 +8852,7 @@ "node_modules/undici-types": { "version": "5.25.3", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", - "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", - "dev": true + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==" }, "node_modules/untildify": { "version": "4.0.0", diff --git a/package.json b/package.json index 5c48947f..2b425564 100644 --- a/package.json +++ b/package.json @@ -13,20 +13,23 @@ "dependencies": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", + "@stomp/stompjs": "^7.0.0", "@tanstack/react-query": "^4.36.1", + "@types/stompjs": "^2.3.7", "axios": "^1.5.1", "d3": "^7.8.5", "emotion-reset": "^3.0.1", "framer-motion": "^10.16.4", + "http-proxy-middleware": "^2.0.6", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.47.0", "react-icons": "^4.11.0", "react-router-dom": "^6.16.0", + "react-spinners": "^0.13.8", "react-toastify": "^9.1.3", "zustand": "^4.4.4", - "zustand-persist": "^0.4.0", - "react-spinners": "^0.13.8" + "zustand-persist": "^0.4.0" }, "devDependencies": { "@rushstack/eslint-config": "^3.4.1", diff --git a/setupProxy.ts b/setupProxy.ts new file mode 100644 index 00000000..b7c3139b --- /dev/null +++ b/setupProxy.ts @@ -0,0 +1,10 @@ +import { createProxyMiddleware } from 'http-proxy-middleware' + +module.exports = (app) => { + app.use( + createProxyMiddleware('/api/chat-stomp', { + target: process.env.VITE_BASE_URL, + ws: true, + }), + ) +} diff --git a/src/components/common/ChattingText/index.tsx b/src/components/common/ChattingText/index.tsx new file mode 100644 index 00000000..24032bd9 --- /dev/null +++ b/src/components/common/ChattingText/index.tsx @@ -0,0 +1,42 @@ +import styled from '@emotion/styled' +import { type HTMLAttributes, type ReactNode } from 'react' + +import { type TextType, theme } from '@/styles/theme' +/** + * @param as Text 컴포넌트의 종류 : 'span' | 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div'; + * @param typo Typo theme 선택 + * @param color Palette theme 선택 + */ + +export interface TextProps extends HTMLAttributes { + as?: 'span' | 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div' + typo: TextType['typo'] + color?: TextType['color'] + children: ReactNode +} + +export type TextPropsKey = 'typo' | 'color' + +export const ChattingText = ({ + typo = 'Body_16', + as = 'h1', + color, + children, + ...props +}: TextProps) => { + return ( + + {children} + + ) +} + +const StyledText = styled.span<{ + typoKey: TextType['typo'] + colorKey?: TextType['color'] +}>` + white-space: pre-wrap; + color: ${({ colorKey }) => { + return colorKey && theme.palette[colorKey] + }}; +` diff --git a/src/components/common/chattingBubble/index.tsx b/src/components/common/chattingBubble/index.tsx new file mode 100644 index 00000000..a5b4fcd2 --- /dev/null +++ b/src/components/common/chattingBubble/index.tsx @@ -0,0 +1,83 @@ +import styled from '@emotion/styled' +import { ComponentProps } from 'react' + +import { ChattingText } from '@/components/common/ChattingText' +import { FlexBox } from '@/components/common/Flexbox' +import { palette } from '@/styles/palette' +import { type KeyOfPalette, type KeyOfTypo } from '@/styles/theme' + +interface ChattingBubbleProps extends ComponentProps<'div'> { + isMyChat?: boolean + message: string + time: string + messageTypo?: KeyOfTypo + messageColor?: KeyOfPalette + timeTypo?: KeyOfTypo + timeColor?: KeyOfPalette + color?: KeyOfPalette +} + +/** + * @param isMychat : 내 채팅인지, 상대방의 채팅인지 여부 / 기본 : false + * @param message : 메시지 내용 + * @param time : 메시지 작성 시간 + * @param messageType : message에 적용할 typo + * @param messageColor: message에 적용시킬 color + * @param timeType : time에 적용할 typo + * @param timeColor : time에 적용할 color + */ + +const ChattingBubble = ({ + isMyChat = false, + message, + time, + messageTypo = 'Body_12', + messageColor = 'BLACK', + timeTypo = 'Caption_11', + timeColor = 'GRAY500', + ...props +}: ChattingBubbleProps) => { + return ( + + + + {message} + + + + {time} + + + ) +} + +const BubbleContainer = styled(FlexBox)<{ isMyChat: boolean }>` + justify-content: ${(props) => props.isMyChat && 'flex-end'}; +` + +const StyledText = styled.div<{ + isMyChat: boolean +}>` + border-radius: 10px; + background-color: ${palette.WHITE}; + padding: 7px 12px; + word-wrap: break-word; + order: ${(props) => (props.isMyChat ? '2' : '1')}; +` + +const TimeText = styled(ChattingText)` + order: 1; + line-height: 150%; +` + +const MessageText = styled(ChattingText)` + line-height: 150%; +` + +export default ChattingBubble diff --git a/tsconfig.json b/tsconfig.json index 027d4a68..0f1ca04b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ // "typeRoots":["./node_modules/@types","./types"], "module": "ESNext", "skipLibCheck": true, + "types": ["node","vite/client"], /* Bundler mode */ "moduleResolution": "bundler", diff --git a/vite.config.ts b/vite.config.ts index 8b2b1405..649ef791 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,4 +8,13 @@ export default defineConfig({ resolve: { alias: { '@/': `${process.cwd()}/src/` }, }, + server: { + port: 3000, + https: true, + hmr: { + host: process.env.VITE_BASE_URL, + port: 3001, + protocol: 'wss', + }, + }, })