🚀 웹 앱 번들 사이즈 최적화 (198kB → 160kB)#416
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. Walkthrough
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@apps/admin/src/components/features/scores/GpaScoreTable.tsx`:
- Around line 214-223: Fix the inconsistent indentation in the pagination block
inside GpaScoreTable: align the Array.from({ length: totalPages }, ...)
expression to the same indentation level as the surrounding <Button> siblings
and make the props on the Button (key, onClick, variant) vertically aligned
(ensure onClick is at the same indent level as key and variant). Locate the
pagination map using Array.from and the Button elements that call
handlePageChange(idx + 1) and reference page to set the variant, then reformat
those lines so indentation is consistent and symmetric.
In `@apps/admin/src/components/features/scores/LanguageScoreTable.tsx`:
- Around line 236-245: Fix the indentation in LanguageScoreTable's pagination
block to match GpaScoreTable: align the {Array.from({ length: totalPages }, (_,
idx) => ( ... ))} block with the surrounding <Button> container and ensure the
attributes inside each Button (key={idx + 1}, onClick={() =>
handlePageChange(idx + 1)}, variant={page === idx + 1 ? "default" :
"secondary"}) have consistent indentation levels (make onClick aligned with key
and variant). Locate the pagination code in LanguageScoreTable (references:
totalPages, page, handlePageChange) and reformat it so its JSX indentation
mirrors the GpaScoreTable pagination section.
In `@apps/admin/src/routes/auth/login.tsx`:
- Around line 56-86: The JSX indentation inside the login form is inconsistent:
ensure each <div className="space-y-1"> is indented as a child of the
surrounding <form>, align all props of the Input components (id, type,
placeholder, value, onChange, disabled, required, className) at the same
indentation level, and align the closing `/>` for each <Input> and the closing
`</div>` tags to match their opening tags; after fixing the whitespace, run
"biome format --write" to normalize formatting across the <Label>, <Input>, and
surrounding form markup (refer to the Input and Label usage in this login
component).
In `@apps/web/next.config.mjs`:
- Around line 97-98: Remove the unsupported transpileClientSDK option from
next.config.mjs (delete the transpileClientSDK: false line and its IE11-related
comment); reference the `@sentry/nextjs` change (v8+ removed this option) and, if
IE11/older-browser support is required, instruct to implement explicit
transpilation and polyfills via Next.js webpack/babel config instead of using
transpileClientSDK.
In `@apps/web/package.json`:
- Line 50: The dependency `@next/bundle-analyzer` in apps/web package.json is
pinned to ^16.1.6 which mismatches the project's Next.js version (^14.2.35);
update the "@next/bundle-analyzer" entry to a 14.x compatible version (e.g.
^14.0.0 or the specific v14.x that matches Next 14) so the `@next/`* major
versions align, then reinstall deps (npm/yarn/pnpm install) and run the
app/build to verify compatibility; locate the "@next/bundle-analyzer" entry in
package.json to make this change.
🧹 Nitpick comments (7)
apps/web/src/components/ui/FloatingUpBtn/hooks/useFloatingUpHandler.ts (1)
10-15:handleClick도useCallback으로 감싸는 것을 고려해볼 수 있습니다.
handleScroll은 메모이제이션했지만,handleClick은 매 렌더마다 새로 생성됩니다. 이 함수가 자식 컴포넌트에 prop으로 전달된다면 불필요한 리렌더를 유발할 수 있습니다. 다만 현재 사용 패턴에서 문제가 없다면 무시하셔도 괜찮습니다.♻️ 선택적 개선안
- const handleClick = () => { - window.scrollTo({ - top: 0, - behavior: "smooth", - }); - }; + const handleClick = useCallback(() => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }, []);apps/web/src/app/university/score/page.tsx (1)
6-6:loading폴백을 추가하면 UX가 더 좋아질 수 있어요.
ssr: false로 동적 임포트하면 JS 청크가 로드될 때까지 해당 영역이 비어 보입니다. 간단한 스켈레톤이나 스피너를loading옵션에 넣어주면 사용자 체감이 자연스러워집니다.💡 예시
-const ScoreScreen = dynamic(() => import("./ScoreScreen"), { ssr: false }); +const ScoreScreen = dynamic(() => import("./ScoreScreen"), { + ssr: false, + loading: () => <div className="flex h-40 items-center justify-center">로딩 중...</div>, +});apps/web/src/app/university/search/page.tsx (1)
6-7: 동적 임포트에loading폴백이 없어요.
SearchBar와SchoolSearchForm모두ssr: false로 설정되어 있어서, JS가 로드되기 전까지 해당 영역이 비어 보일 수 있습니다.dynamic의loading옵션으로 간단한 스켈레톤이나 스피너를 넣어주면 사용자 체감 로딩 속도가 개선됩니다.next/dynamic loading option usage💡 예시
-const SearchBar = dynamic(() => import("./SearchBar"), { ssr: false }); -const SchoolSearchForm = dynamic(() => import("./PageContent"), { ssr: false }); +const SearchBar = dynamic(() => import("./SearchBar"), { + ssr: false, + loading: () => <div className="h-10 animate-pulse rounded-lg bg-gray-100" />, +}); +const SchoolSearchForm = dynamic(() => import("./PageContent"), { + ssr: false, + loading: () => <div className="h-40 animate-pulse rounded-lg bg-gray-100" />, +});apps/admin/src/lib/api/scores.ts (1)
30-44: 어학 성적 조회 및 수정 API 로직 확인 완료포맷팅 정리가 잘 되었습니다. 다만 향후 타입 안전성 개선을 고려해볼 수 있는 부분이 있습니다.
Line 38의
as LanguageTestType타입 단언은 서버에서 예상치 못한 값이 올 경우 런타임 오류를 발생시킬 수 있습니다. 필요시 타입 가드를 통한 검증을 추가하는 것을 고려해보세요.apps/web/src/lib/react-query/useMutationWithFailure.ts (2)
77-99:mutate/mutateAsync가 매 렌더마다 새 참조를 생성합니다.
mutate와mutateAsync가useCallback없이 매 렌더시 재생성됩니다.- React Query 자체의
mutation.mutate도 안정적 참조가 아니므로 실질적 문제는 아니지만, 이 함수들을 자식 컴포넌트의 prop으로 전달하는 경우 불필요한 리렌더가 발생할 수 있습니다.- 현재 규모에서는 큰 영향이 없으므로, 필요해지면
useCallback으로 감싸는 것을 고려해 주세요.
43-62:normalizeMutationOptions/normalizeMutateOptions— 타입 캐스팅에 대한 메모.
- Line 49, 60에서
as캐스팅을 사용하는데, 이는mergeOnError가TData를unknown으로 처리하기 때문에 필요합니다.- 안전성 측면에서 문제는 없지만,
mergeOnError의 제네릭에TData를 추가하면 캐스팅 없이 해결할 수 있습니다.♻️ 캐스팅 제거를 위한 제안
-const mergeOnError = <TError, TVariables, TContext>( - onError?: UseMutationOptions<unknown, TError, TVariables, TContext>["onError"], +const mergeOnError = <TData, TError, TVariables, TContext>( + onError?: UseMutationOptions<TData, TError, TVariables, TContext>["onError"], onFailure?: (error: TError, variables: TVariables, context: TContext | undefined) => void, -): UseMutationOptions<unknown, TError, TVariables, TContext>["onError"] => { +): UseMutationOptions<TData, TError, TVariables, TContext>["onError"] => { if (!onError && !onFailure) return undefined; - return (...args: Parameters<NonNullable<UseMutationOptions<unknown, TError, TVariables, TContext>["onError"]>>) => { + return (...args: Parameters<NonNullable<UseMutationOptions<TData, TError, TVariables, TContext>["onError"]>>) => { onError?.(...args); onFailure?.(args[0], args[1], args[2]); }; };이렇게 하면
normalizeMutationOptions와normalizeMutateOptions내부의as캐스팅을 제거할 수 있습니다.apps/web/sentry.client.config.ts (1)
35-44:lazyLoadIntegrationPromise에 에러 처리가 없습니다.네트워크 실패 등으로 lazy load가 실패할 경우 unhandled promise rejection이 발생할 수 있어요. Session Replay는 비필수 기능이니 조용히 실패해도 괜찮지만, 명시적으로
.catch()를 추가해 주는 것이 안전합니다.🛡️ 에러 처리 추가 제안
Sentry.lazyLoadIntegration("replayIntegration").then((replay) => { Sentry.addIntegration( replay({ maskAllText: true, blockAllMedia: true, }), ); - }); + }).catch(() => { + // Session Replay lazy load 실패 시 무시 (비필수 기능) + });
| // IE11 지원 불필요 - 번들 사이즈 최적화를 위해 비활성화 | ||
| transpileClientSDK: false, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
@sentry/nextjs v10 transpileClientSDK option documentation
💡 Result:
transpileClientSDK is not available in @sentry/nextjs v10 (or any v8+). It was removed in @sentry/nextjs 8.0.0 when Sentry dropped support for IE11/older browsers; the release notes explicitly say the option was removed and that you must down-compile the SDK yourself via your Next.js/Webpack setup if you still need that compatibility. [1]
Historically (pre-v8), transpileClientSDK: true told the Next.js SDK’s Webpack config to transpile Sentry’s client bundle to match your app’s transpilation level (to support older browsers). [2]
If your goal is “support older browsers,” note that current Sentry JavaScript SDKs target modern syntax (ES2020) and Sentry’s docs recommend transpiling + polyfills on your side if you need earlier browser versions. [3]
Sources: [1] [2] [3]
🌐 Web query:
Sentry SDK transpileClientSDK option removed deprecated v8 v9 v10
💡 Result:
transpileClientSDKwas added to the Sentry Next.js SDK in the v7 line (to force Webpack to transpile Sentry’s client bundle for older browsers like IE11). (newreleases.io)- It was removed in
@sentry/nextjs8.0.0 (Sentry JS SDK v8) because v8 dropped support for IE11 / older browsers, so the SDK no longer provides this built-in transpilation switch. (newreleases.io) - Therefore, the option is not available in v8, v9, or v10 of
@sentry/nextjs. If you still need older-browser support, you must configure Next.js/Webpack to down-compile/transpile the Sentry packages yourself. (newreleases.io)
Citations:
- 1: https://newreleases.io/project/npm/%40sentry/react/release/7.8.0?utm_source=openai
- 2: https://newreleases.io/project/npm/%40sentry/nextjs/release/8.0.0?utm_source=openai
- 3: https://newreleases.io/project/npm/%40sentry/nextjs/release/8.0.0?utm_source=openai
🌐 Web query:
@sentry/nextjs changelog transpileClientSDK
💡 Result:
transpileClientSDK was added to @sentry/nextjs in sentry-javascript v7.8.0 (“fix(nextjs): Add transpileClientSDK option”, PR #5472) as a Next.js Webpack config flag. It makes Next/Webpack transpile the Sentry client SDK code using the same transpilation rules as your app, which helps when targeting older browsers (e.g., missing ES6/ES6+ features like object spread). [1]
In the @sentry/nextjs type definitions (v7.x), it’s documented as:
transpileClientSDK?: boolean;— “apply the same transpilation rules to the SDK code as apply to user code…” [2]
Sources: [1] [2]
transpileClientSDK 옵션은 @sentry/nextjs v10에서 지원되지 않으므로 제거가 필요합니다.
다음과 같은 변경 사항이 필요합니다:
-
옵션 제거
@sentry/nextjs8.0.0부터transpileClientSDK옵션이 완전히 제거되었으므로, 이 라인은 삭제하세요. 현재 설정은 v10에서 인식되지 않아 자동으로 무시됩니다. -
IE11 지원 재평가
Sentry JavaScript SDK v8+ 이후 IE11 지원이 공식적으로 중단되었습니다. 만약 이전 브라우저 지원이 필수라면, Next.js의 webpack 설정을 통해 직접 transpilation + polyfill을 구성해야 합니다. 현재 주석의 의도(번들 최적화)라면 주석도 함께 제거하세요.
🤖 Prompt for AI Agents
In `@apps/web/next.config.mjs` around lines 97 - 98, Remove the unsupported
transpileClientSDK option from next.config.mjs (delete the transpileClientSDK:
false line and its IE11-related comment); reference the `@sentry/nextjs` change
(v8+ removed this option) and, if IE11/older-browser support is required,
instruct to implement explicit transpilation and polyfills via Next.js
webpack/babel config instead of using transpileClientSDK.
| "zustand": "^5.0.7" | ||
| }, | ||
| "devDependencies": { | ||
| "@next/bundle-analyzer": "^16.1.6", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat apps/web/package.json | grep -A 100 '"dependencies"' | head -30Repository: solid-connection/solid-connect-web
Length of output: 1085
🏁 Script executed:
cat apps/web/package.json | grep -E '"next"|"@next/bundle-analyzer"'Repository: solid-connection/solid-connect-web
Length of output: 141
🌐 Web query:
@next/bundle-analyzer Next.js 14 compatibility requirements version
💡 Result:
For Next.js 14, use @next/bundle-analyzer v14.x (same major as Next). In practice, keep it in lockstep with your next version (e.g., next@14.2.30 → @next/bundle-analyzer@14.2.30). The npm registry shows published 14.x releases (e.g., 14.2.30) for @next/bundle-analyzer. [1]
Requirements to run it with Next 14:
- Next.js 14 requires Node.js >= 18.17. [2]
- It’s a Webpack-based analyzer; if you run
next dev --turbo(Turbopack), you may see warnings because Webpack config/plugins (like bundle analyzer) aren’t supported the same way. [3]
Install example (pin to your Next 14 version):
npm i -D `@next/bundle-analyzer`@14.2.30Sources: [1] [2] [3]
@next/bundle-analyzer 버전이 Next.js 메이저 버전과 맞지 않습니다.
현재 프로젝트에서 next는 ^14.2.35이지만, @next/bundle-analyzer는 ^16.1.6으로 설정되어 있어요. @next/* 패키지들은 Next.js의 메이저 버전과 동일하게 유지해야 호환성이 보장됩니다. 공식 문서에서도 Next.js 14에는 @next/bundle-analyzer v14.x를 사용하도록 권장하고 있습니다.
다음과 같이 수정을 제안드립니다:
@next/bundle-analyzer버전을 Next.js 14과 맞춰서 업그레이드하기
🔧 버전 수정 방안
- "@next/bundle-analyzer": "^16.1.6",
+ "@next/bundle-analyzer": "^14.2.35",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "@next/bundle-analyzer": "^16.1.6", | |
| "@next/bundle-analyzer": "^14.2.35", |
🤖 Prompt for AI Agents
In `@apps/web/package.json` at line 50, The dependency `@next/bundle-analyzer` in
apps/web package.json is pinned to ^16.1.6 which mismatches the project's
Next.js version (^14.2.35); update the "@next/bundle-analyzer" entry to a 14.x
compatible version (e.g. ^14.0.0 or the specific v14.x that matches Next 14) so
the `@next/`* major versions align, then reinstall deps (npm/yarn/pnpm install)
and run the app/build to verify compatibility; locate the
"@next/bundle-analyzer" entry in package.json to make this change.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @.npmrc:
- Around line 3-5: The .npmrc contains conflicting hoisting settings: remove the
redundant public-hoist-pattern[]='*' entries (and the negations '!@types/react'
/ '!@types/react-dom' which are ineffective when shamefully-hoist=true) or else
remove shamefully-hoist=true so public-hoist-patterns take effect; then move
bundle-size optimizations into Next.js config by adjusting next.config.js (use
transpilePackages and optimizePackageImports) rather than relying on broad pnpm
hoisting.
| public-hoist-pattern[]='*' | ||
| public-hoist-pattern[]='!@types/react' | ||
| public-hoist-pattern[]='!@types/react-dom' |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
pnpm public-hoist-pattern shamefully-hoist interaction configuration
💡 Result:
shamefully-hoist and public-hoist-pattern both control public hoisting (putting selected packages into the root node_modules so “phantom” deps become resolvable by app/tooling).
Interaction / precedence
shamefully-hoist=trueis effectively the same aspublic-hoist-pattern[]=*(public-hoist everything). It also overrides anypublic-hoist-patternyou set. [1][2]shamefully-hoist=falsemeans pnpm usespublic-hoist-patternnormally (only patterns you specify are publicly hoisted). [2]
Recommended configuration approach
Prefer targeted public hoisting via public-hoist-pattern (only for broken tooling that expects flat/root deps), instead of shamefully-hoist=true. [1]
Example .npmrc
# Targeted public hoisting (recommended)
public-hoist-pattern[]=*plugin*
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*pnpm also supports excluding matches with ! (so you can broadly hoist, then carve out exceptions). [1][3]
public-hoist-pattern[]=*types*
public-hoist-pattern[]=!@types/react“Hoist everything” (legacy/last resort)
shamefully-hoist=true
# equivalent to: public-hoist-pattern[]=*[1]
Rule of thumb: if you set shamefully-hoist=true, don’t bother setting public-hoist-pattern—it will be treated as * anyway. [1][2]
Sources: pnpm .npmrc settings docs (public-hoist-pattern + shamefully-hoist) [1], pnpm 5.2.0 release notes describing override behavior [2], pnpm .npmrc docs noting exclusion patterns with ! [3].
🌐 Web query:
pnpm monorepo hoisting best practices Next.js bundle optimization
💡 Result:
pnpm monorepo hoisting: best practices (so you don’t break Next.js or hide missing deps)
- Prefer pnpm’s default “isolated” linker (
node-linker=isolated) to keep dependency boundaries correct and avoid “phantom dependency” bugs (packages importing deps they didn’t declare). Only switch tonode-linker=hoistedif you must support tooling/filesystems that can’t handle symlinks. [1] - Avoid blanket “public” hoisting (
publicHoistPattern=['*']/shamefullyHoist=true) unless you’re working around a specific broken tool, because it intentionally makes phantom deps available to app code. [2] - If you need hoisting, make it targeted:
- Use
hoistPatternto hoist only the problematic toolchain deps (recommended by pnpm) instead of everything. [2] - Use
publicHoistPatternsparingly for tools that incorrectly resolve plugins/deps and require root-level visibility. [2]
- Use
Typical .npmrc / pnpm config approach
# default / recommended
node-linker=isolated
# if some tool breaks, prefer targeted hoisting (examples)
# hoist-pattern[]=*eslint*
# hoist-pattern[]=*babel*
# public-hoist-pattern[]=*plugin*(Exact config file can be .npmrc or workspace config depending on how you manage pnpm settings; the knobs are nodeLinker, hoistPattern, publicHoistPattern.) [1][2]
Next.js in a pnpm monorepo: bundle optimization checklist
- Bundle/transpile your internal workspace packages (the usual monorepo fix) using
transpilePackagesso Next can compile code coming from workspace packages /node_modules. [3][4]
// next.config.js
module.exports = {
transpilePackages: ['@acme/ui', '@acme/shared'],
}- Measure first with the official bundle analyzer so you can see what’s actually inflating the client/server bundles. [5]
ANALYZE=true pnpm build- Fix “barrel export” bloat with
experimental.optimizePackageImports(or rely on defaults for popular libs). This can reduce work during builds and avoid pulling in huge module graphs for big “named export” packages. [5][6][7]
module.exports = {
experimental: {
optimizePackageImports: ['some-big-barrel-lib'],
},
}- Keep server-only deps out of bundling when appropriate using
serverExternalPackages(useful for native/binary deps or packages that shouldn’t be bundled). [5]
How this ties together (pnpm hoisting ↔ Next bundling)
- Don’t use broad hoisting to “make Next find things.” If Next can’t compile/import a workspace package cleanly, the right fix is usually
transpilePackages(and proper dependency declarations), not making everything visible via public hoisting. [3][4][2] - Use hoisting only as a compatibility escape hatch, and keep it pattern-based. [2][1]
Sources: pnpm settings for nodeLinker/hoisting knobs [1][2]; Next.js docs for bundle analyzer, transpilePackages, and optimizePackageImports [5][3][4][6]; Vercel guidance on optimizePackageImports and barrel-export impact [7].
공존할 수 없는 설정: 중복을 제거하고 번들 최적화 방식을 재검토하세요
현재 설정에 두 가지 문제가 있습니다:
-
설정 중복으로 인한 비효율
- 2번 라인의
shamefully-hoist=true가 이미 모든 의존성을 공개 호이스팅합니다 - 3~5번 라인의
public-hoist-pattern[]='*'는 위 설정과 정확히 동일한 동작을 하므로 불필요합니다 - pnpm 공식 문서에 따르면,
shamefully-hoist=true를 설정하면 다른public-hoist-pattern설정은 무시됩니다
- 2번 라인의
-
의도한 효과를 내지 못할 가능성
- 4~5번 라인의 React 타입 제외 설정(
!@types/react등)은shamefully-hoist=true앞에서 작동하지 않습니다 - React 타입도 결국 공개 호이스팅되므로, 의존성 충돌 방지 효과가 없습니다
- 4~5번 라인의 React 타입 제외 설정(
-
번들 최적화 목표 재검토
- 광범위한 호이스팅만으로는 Next.js 번들 크기를 줄일 수 없습니다
- 실제 최적화는
next.config.js의transpilePackages와optimizePackageImports설정으로 이루어집니다
권장 조치:
- 불필요한
public-hoist-pattern[]='*'를 제거하거나 shamefully-hoist=true로 통일하되, 번들 최적화는 Next.js 설정으로 진행하세요
🤖 Prompt for AI Agents
In @.npmrc around lines 3 - 5, The .npmrc contains conflicting hoisting
settings: remove the redundant public-hoist-pattern[]='*' entries (and the
negations '!@types/react' / '!@types/react-dom' which are ineffective when
shamefully-hoist=true) or else remove shamefully-hoist=true so
public-hoist-patterns take effect; then move bundle-size optimizations into
Next.js config by adjusting next.config.js (use transpilePackages and
optimizePackageImports) rather than relying on broad pnpm hoisting.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
d7cac40 to
294670f
Compare
Summary
웹 앱의 First Load JS shared by all을 198 kB → 160 kB (-38 kB, -19.2%)로 감소시킵니다.
변경 사항
1. Sentry transpileClientSDK 비활성화
2. Sentry replayIntegration lazy load
lazyLoadIntegration으로 비동기 로딩하여 초기 로딩 성능 개선3. @next/bundle-analyzer 추가
ANALYZE=true pnpm --filter @solid-connect/web run analyze)4. optimizePackageImports 설정
lucide-react,@radix-ui/*,zod,react-hook-form등 tree-shaking 최적화5. score/search 페이지 dynamic import
ScoreScreen,SearchBar,SchoolSearchForm컴포넌트를next/dynamic으로 변경빌드 결과 비교
/university/score/university/score/submit/gpa/university/score/submit/language-test/university/search참고
useMutationWithFailure.ts의 기존 타입 에러가 main에도 존재합니다 (이 PR과 무관)