diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cb3c3d3d0..bc19e0e79 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -81,6 +81,7 @@ "typescript": "^5.5.3", "undici": "^6.19.5", "webpack": "^5.92.1", + "webpack-bundle-analyzer": "^4.10.2", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" } @@ -4862,6 +4863,12 @@ "node": ">= 8" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -10653,6 +10660,12 @@ "integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==", "dev": true }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true + }, "node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", @@ -11061,6 +11074,12 @@ "url": "https://dotenvx.com" } }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, "node_modules/duplexify": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", @@ -13412,6 +13431,21 @@ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -17514,6 +17548,15 @@ "node": ">=0.4.0" } }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -18215,6 +18258,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -20171,6 +20223,20 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -21696,6 +21762,15 @@ "node": ">=0.6" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", @@ -22667,6 +22742,86 @@ } } }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "html-escaper": "^2.0.2", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/webpack-cli": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 74568bd5d..6f400ecc1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -83,6 +83,7 @@ "typescript": "^5.5.3", "undici": "^6.19.5", "webpack": "^5.92.1", + "webpack-bundle-analyzer": "^4.10.2", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" }, diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a307c65f3..fabcf9b31 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -15,9 +15,10 @@ export default function App() { useEffect(() => { window.addEventListener('beforeunload', removeInviteCode); - - return window.removeEventListener('beforeunload', removeInviteCode); - }); + return () => { + window.removeEventListener('beforeunload', removeInviteCode); + }; + }, []); return ( diff --git a/frontend/src/common/assets/default_profile.png b/frontend/src/common/assets/default_profile.png deleted file mode 100644 index 3143fc761..000000000 Binary files a/frontend/src/common/assets/default_profile.png and /dev/null differ diff --git a/frontend/src/common/assets/fonts/woff2/PretendardVariable.woff2 b/frontend/src/common/assets/fonts/woff2/PretendardVariable.woff2 deleted file mode 100644 index 49c54b515..000000000 Binary files a/frontend/src/common/assets/fonts/woff2/PretendardVariable.woff2 and /dev/null differ diff --git a/frontend/src/common/assets/regret_cat.png b/frontend/src/common/assets/regret_cat.png deleted file mode 100644 index c63876d37..000000000 Binary files a/frontend/src/common/assets/regret_cat.png and /dev/null differ diff --git a/frontend/src/common/assets/regret_cat.webp b/frontend/src/common/assets/regret_cat.webp new file mode 100644 index 000000000..afcdd236f Binary files /dev/null and b/frontend/src/common/assets/regret_cat.webp differ diff --git a/frontend/src/common/font.style.ts b/frontend/src/common/font.style.ts index 666f43481..da9e13498 100644 --- a/frontend/src/common/font.style.ts +++ b/frontend/src/common/font.style.ts @@ -8,7 +8,6 @@ import pretendardMediumWoff2 from './assets/fonts/woff2-subset/Pretendard-Medium import pretendardRegularWoff2 from './assets/fonts/woff2-subset/Pretendard-Regular.subset.woff2'; import pretendardSemiboldWoff2 from './assets/fonts/woff2-subset/Pretendard-SemiBold.subset.woff2'; import pretendardThinWoff2 from './assets/fonts/woff2-subset/Pretendard-Thin.subset.woff2'; -import pretendardVariableWoff2 from './assets/fonts/woff2/PretendardVariable.woff2'; const fonts = css` @font-face { @@ -76,14 +75,6 @@ const fonts = css` font-display: swap; src: url(${pretendardThinWoff2}) format('woff2'); } - - @font-face { - font-family: 'Pretendard Variable'; - font-weight: 45 920; - font-style: normal; - font-display: swap; - src: url(${pretendardVariableWoff2}) format('woff2-variations'); - } `; export default fonts; diff --git a/frontend/src/components/MissingFallback/MissingFallback.tsx b/frontend/src/components/MissingFallback/MissingFallback.tsx index 9f2bf83a8..defd2f720 100644 --- a/frontend/src/components/MissingFallback/MissingFallback.tsx +++ b/frontend/src/components/MissingFallback/MissingFallback.tsx @@ -2,7 +2,7 @@ import * as S from './MissingFallback.style'; import { SerializedStyles, useTheme } from '@emotion/react'; -import regretCat from '@_common/assets/regret_cat.png'; +import regretCat from '@_common/assets/regret_cat.webp'; interface MissingFallbackProps { text: string; diff --git a/frontend/src/components/UserPreview/UserPreview.style.ts b/frontend/src/components/UserPreview/UserPreview.style.ts index 2635d1f48..4e0e673e8 100644 --- a/frontend/src/components/UserPreview/UserPreview.style.ts +++ b/frontend/src/components/UserPreview/UserPreview.style.ts @@ -1,5 +1,5 @@ import { css } from '@emotion/react'; -import defaultProfile from '@_common/assets/default_profile.png'; +import defaultProfile from '@_common/assets/empty_profile.svg?url'; export const preview = ({ imageUrl, diff --git a/frontend/src/custom.d.ts b/frontend/src/custom.d.ts index 82ca1de9a..58e503d61 100644 --- a/frontend/src/custom.d.ts +++ b/frontend/src/custom.d.ts @@ -2,6 +2,7 @@ declare module '*.png'; declare module '*.jpg'; declare module '*.jpeg'; declare module '*.gif'; +declare module '*.webp'; declare module '*.woff2' { const src: string; export default src; diff --git a/frontend/src/index.html b/frontend/src/index.html index ded8d1869..8239db169 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -15,6 +15,10 @@ + diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index a4536a218..9654f27c2 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -27,22 +27,28 @@ if (process.env.NODE_ENV === 'production' && process.env.SENTRY_DSN) { }); } -async function enableMocking() { - if (process.env.MSW !== 'true') { - return; +const enableMocking = async () => { + if (process.env.NODE_ENV === 'development' && process.env.MSW === 'true') { + const { worker } = await import('./mocks/browser'); + await worker.start(); } +}; - const { worker } = await import('./mocks/browser'); - - return worker.start(); -} - -enableMocking().then(() => { +const renderApp = () => { const rootElement = document.getElementById('root') as HTMLElement; const root = createRoot(rootElement); + root.render( , ); -}); +}; + +// MSW가 개발 환경에서만 동작하도록 설정하고, 앱을 렌더링 +const init = async () => { + await enableMocking(); + renderApp(); +}; + +init(); diff --git a/frontend/src/layouts/ChattingRoomLayout/ChattingRoomLayout.tsx b/frontend/src/layouts/ChattingRoomLayout/ChattingRoomLayout.tsx index 08d6a585a..635bf8bb1 100644 --- a/frontend/src/layouts/ChattingRoomLayout/ChattingRoomLayout.tsx +++ b/frontend/src/layouts/ChattingRoomLayout/ChattingRoomLayout.tsx @@ -3,7 +3,6 @@ import * as S from './ChattingRoomLayout.style'; import ChattingRoomFooter from './ChattingRoomFooter/ChattingRoomFooter'; import { PropsWithChildren } from 'react'; import StickyTriSectionHeader from '@_layouts/components/StickyTriSectionHeader/StickyTriSectionHeader'; -import { useTheme } from '@emotion/react'; function ChattingRoomLayout(props: PropsWithChildren) { const { children } = props; @@ -19,15 +18,7 @@ function Header(props: PropsWithChildren) { Header.Left = StickyTriSectionHeader.Left; Header.Right = StickyTriSectionHeader.Right; -Header.Center = function Center(props: PropsWithChildren) { - const { children } = props; - const theme = useTheme(); - return ( - -
{children}
-
- ); -}; +Header.Center = StickyTriSectionHeader.Center; ChattingRoomLayout.Header = Header; ChattingRoomLayout.Footer = ChattingRoomFooter; diff --git a/frontend/src/pages/Chatting/ChatPage/components/ChatMenuItem/ChatMenuItem.style.ts b/frontend/src/pages/Chatting/ChatPage/components/ChatMenuItem/ChatMenuItem.style.ts index a98ebfbdb..ccdea9fde 100644 --- a/frontend/src/pages/Chatting/ChatPage/components/ChatMenuItem/ChatMenuItem.style.ts +++ b/frontend/src/pages/Chatting/ChatPage/components/ChatMenuItem/ChatMenuItem.style.ts @@ -4,7 +4,9 @@ export const item = css` display: flex; flex-direction: column; align-items: center; + width: 7rem; + height: 100%; `; export const button = ({ theme }: { theme: Theme }) => { diff --git a/frontend/src/pages/Chatting/ChatPage/components/ChattingFooter/ChattingFooter.style.ts b/frontend/src/pages/Chatting/ChatPage/components/ChattingFooter/ChattingFooter.style.ts index d22f5a862..33ab6f988 100644 --- a/frontend/src/pages/Chatting/ChatPage/components/ChattingFooter/ChattingFooter.style.ts +++ b/frontend/src/pages/Chatting/ChatPage/components/ChattingFooter/ChattingFooter.style.ts @@ -6,6 +6,10 @@ export const footer = ({ theme }: { theme: Theme }) => css` justify-content: space-around; padding: 1.3rem 0; + /* stylelint-disable */ + padding-bottom: constant(safe-area-inset-bottom); + /* stylelint-enable */ + padding-bottom: env(safe-area-inset-bottom); background-color: ${theme.colorPalette.white[100]}; box-shadow: 0 -10px 15px rgb(0 0 0 / 20%); diff --git a/frontend/src/pages/Chatting/ChattingRoomPage/ChattingRoomPage.tsx b/frontend/src/pages/Chatting/ChattingRoomPage/ChattingRoomPage.tsx index a20174079..f5f2c7a2e 100644 --- a/frontend/src/pages/Chatting/ChattingRoomPage/ChattingRoomPage.tsx +++ b/frontend/src/pages/Chatting/ChattingRoomPage/ChattingRoomPage.tsx @@ -1,25 +1,26 @@ import { useMemo, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import Back from '@_common/assets/back.svg'; import CalenderClock from '@_components/Icons/CalenderClock'; +import ChatBottomMenu from '../ChatPage/components/ChatBottomMenu/ChatBottomMenu'; +import ChatList from '../ChatPage/components/ChatList/ChatList'; +import ChatMenuItem from '../ChatPage/components/ChatMenuItem/ChatMenuItem'; +import ChattingFooter from '../ChatPage/components/ChattingFooter/ChattingFooter'; +import ChattingRoomLayout from '@_layouts/ChattingRoomLayout/ChattingRoomLayout'; +import DarakbangNameWrapper from '@_components/DarakbangNameWrapper/DarakbangNameWrapper'; +import DateTimeModalContent from '../ChatPage/components/DateTimeModalContent/DateTimeModalContent'; import MissingFallback from '@_components/MissingFallback/MissingFallback'; import Modal from '@_components/Modal/Modal'; import Picker from '@_components/Icons/Picker'; +import PlaceModalContent from './components/PlaceModalContent/PlaceModalContent'; +import SolidArrow from '@_components/Icons/SolidArrow'; import useChamyoMine from '@_hooks/queries/useChamyoMine'; import useChats from '@_hooks/queries/useChats'; import useConfirmDateTime from '@_hooks/mutaions/useConfirmDatetime'; import useConfirmPlace from '@_hooks/mutaions/useConfirmPlace'; -import useMoims from '@_hooks/queries/useMoims'; +import useMoim from '@_hooks/queries/useMoim'; import useSendMessage from '@_hooks/mutaions/useSendMessage'; import { useTheme } from '@emotion/react'; -import PlaceModalContent from './components/PlaceModalContent/PlaceModalContent'; -import DateTimeModalContent from '../ChatPage/components/DateTimeModalContent/DateTimeModalContent'; -import ChatBottomMenu from '../ChatPage/components/ChatBottomMenu/ChatBottomMenu'; -import ChattingRoomLayout from '@_layouts/ChattingRoomLayout/ChattingRoomLayout'; -import ChatMenuItem from '../ChatPage/components/ChatMenuItem/ChatMenuItem'; -import ChatList from '../ChatPage/components/ChatList/ChatList'; -import ChattingFooter from '../ChatPage/components/ChattingFooter/ChattingFooter'; type ModalContent = 'place' | 'datetime'; @@ -30,9 +31,9 @@ export default function ChattingRoomPage() { const moimId = +(params.moimId || '0'); - const { moims } = useMoims(); const { chats } = useChats(moimId); const { role } = useChamyoMine(moimId); + const { moim } = useMoim(moimId); const { mutate: confirmDateTime, isPending: isPendingConfirmDateTime } = useConfirmDateTime(); @@ -44,11 +45,6 @@ export default function ChattingRoomPage() { const [isModalOpen, setIsModalOpen] = useState(false); const [nowModalContent, setNowModalContent] = useState('place'); - const moimTitle = useMemo( - () => moims?.find((moim) => moim.moimId === moimId)?.title, - [moims, moimId], - ); - const modal = useMemo(() => { if (nowModalContent === 'datetime') return ( @@ -121,12 +117,12 @@ export default function ChattingRoomPage() { -
navigate(-1)}> - -
+ navigate(-1)} />
-

{moimTitle}

+ + {moim?.title} +
diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js index 600efaacc..d53e0ded4 100644 --- a/frontend/webpack.common.js +++ b/frontend/webpack.common.js @@ -10,15 +10,18 @@ module.exports = { output: { filename: '[name].[contenthash].js', // 캐시를 위한 해시 추가 + chunkFilename: '[name].[contenthash].js', path: path.resolve(__dirname, 'dist'), clean: true, publicPath: '/', }, + devServer: { historyApiFallback: true, open: true, port:8081 }, + plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, './src/index.html'), diff --git a/frontend/webpack.prod.js b/frontend/webpack.prod.js index 674118ab4..6104c6f55 100644 --- a/frontend/webpack.prod.js +++ b/frontend/webpack.prod.js @@ -3,10 +3,11 @@ const common = require('./webpack.common.js'); const { sentryWebpackPlugin } = require('@sentry/webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); +const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports = merge(common, { mode: 'production', - devtool: 'source-map', + devtool: 'hidden-source-map', module: { rules: [ { @@ -17,13 +18,12 @@ module.exports = merge(common, { ], }, performance: { - hints: false, maxEntrypointSize: 512000, maxAssetSize: 512000, }, optimization: { minimize: true, - minimizer: [ + minimizer: ['...', new TerserPlugin({ parallel: true, // 병렬 처리 활성화 terserOptions: { @@ -34,13 +34,10 @@ module.exports = merge(common, { }), ], splitChunks: { - chunks: 'all', // 모든 타입의 청크를 분할 - maxInitialRequests: 3, - maxAsyncRequests: 5, - minSize: 30000, - }, - runtimeChunk: 'single', // 런타임 청크를 분리 + chunks: 'all', + } }, + plugins: [ // Put the Sentry Webpack plugin after all other plugins sentryWebpackPlugin({ @@ -50,8 +47,9 @@ module.exports = merge(common, { }), new CopyWebpackPlugin({ patterns: [ - { from: 'public', to: '' }, // public 폴더의 모든 파일을 dist 폴더의 루트로 복사 + { from: 'public', to: '', globOptions: { ignore: ['**/mockServiceWorker.js'] } }, ], }), + new BundleAnalyzerPlugin() ], });