Skip to content

Commit 25db4f2

Browse files
authored
Merge pull request #297 from manNomi/chore/migrate-css-to-tailwind
Chore : css 테일윈드로 마이그레이션 , 크리티컬 css 적용
2 parents ed6e050 + 3cd8140 commit 25db4f2

17 files changed

+146
-472
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
},
88
"scripts": {
99
"dev": "next dev",
10-
"build": "next build",
10+
"build": "next build && node scripts/critical-css.js",
1111
"start": "next start",
1212
"lint": "next lint"
1313
},
@@ -28,7 +28,7 @@
2828
"axios": "^1.6.7",
2929
"class-variance-authority": "^0.7.1",
3030
"clsx": "^2.1.1",
31-
"critters": "^0.0.23",
31+
"beasties": "^0.2.0",
3232
"firebase": "^10.7.2",
3333
"firebase-admin": "^12.0.0",
3434
"js-cookie": "^3.0.5",

scripts/critical-css.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const Beasties = require("beasties");
2+
const fs = require("fs");
3+
const path = require("path");
4+
5+
const NEXT_BUILD_DIR = path.join(process.cwd(), ".next");
6+
const SERVER_APP_DIR = path.join(NEXT_BUILD_DIR, "server", "app");
7+
8+
async function processCriticalCSS() {
9+
console.log("Starting Critical CSS extraction with Beasties...");
10+
11+
const beasties = new Beasties({
12+
path: NEXT_BUILD_DIR,
13+
publicPath: "/_next/",
14+
preload: "swap",
15+
noscriptFallback: true,
16+
inlineFonts: true,
17+
pruneSource: false,
18+
reduceInlineStyles: true,
19+
mergeStylesheets: true,
20+
additionalStylesheets: [],
21+
fonts: true,
22+
});
23+
24+
const htmlFiles = findHtmlFiles(SERVER_APP_DIR);
25+
console.log(`Found ${htmlFiles.length} HTML files to process`);
26+
27+
for (const htmlFile of htmlFiles) {
28+
try {
29+
const html = fs.readFileSync(htmlFile, "utf-8");
30+
const processedHtml = await beasties.process(html);
31+
fs.writeFileSync(htmlFile, processedHtml);
32+
console.log(`Processed: ${path.relative(process.cwd(), htmlFile)}`);
33+
} catch (error) {
34+
console.error(`Error processing ${htmlFile}:`, error.message);
35+
}
36+
}
37+
38+
console.log("Critical CSS extraction complete!");
39+
}
40+
41+
function findHtmlFiles(dir) {
42+
const files = [];
43+
44+
if (!fs.existsSync(dir)) {
45+
console.warn(`Directory not found: ${dir}`);
46+
return files;
47+
}
48+
49+
const items = fs.readdirSync(dir);
50+
51+
for (const item of items) {
52+
const fullPath = path.join(dir, item);
53+
const stat = fs.statSync(fullPath);
54+
55+
if (stat.isDirectory()) {
56+
files.push(...findHtmlFiles(fullPath));
57+
} else if (item.endsWith(".html")) {
58+
files.push(fullPath);
59+
}
60+
}
61+
62+
return files;
63+
}
64+
65+
processCriticalCSS().catch((error) => {
66+
console.error("Fatal error:", error);
67+
process.exit(1);
68+
});

src/app/layout.tsx

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable @next/next/no-css-tags */
21
import type { Metadata, Viewport } from "next";
32
import dynamic from "next/dynamic";
43
import localFont from "next/font/local";
@@ -18,14 +17,12 @@ export const metadata: Metadata = {
1817
description: "솔리드 커넥션. 교환학생의 첫 걸음",
1918
};
2019

21-
// 🎯 폰트 최적화: 하나의 폰트만 사용
2220
const pretendard = localFont({
2321
src: "../../public/fonts/PretendardVariable.woff2",
24-
display: "swap", // optional → swap으로 변경 (preload와 호환)
22+
display: "swap",
2523
weight: "45 920",
2624
variable: "--font-pretendard",
2725
preload: true,
28-
// 폰트 로딩 실패 시 fallback 폰트 체인
2926
fallback: [
3027
"system-ui",
3128
"-apple-system",
@@ -44,8 +41,7 @@ const AppleScriptLoader = dynamic(() => import("@/lib/ScriptLoader/AppleScriptLo
4441

4542
declare global {
4643
interface Window {
47-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
48-
Kakao: any;
44+
Kakao: unknown;
4945
}
5046
}
5147

@@ -60,48 +56,13 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => (
6056
<AlertProvider>
6157
<html lang="ko" className={pretendard.variable}>
6258
<head>
63-
{/* 폰트 preload - CSS 블로킹 방지 */}
6459
<link
6560
rel="preload"
6661
href="/fonts/PretendardVariable.woff2"
6762
as="font"
6863
type="font/woff2"
6964
crossOrigin="anonymous"
7065
/>
71-
72-
{/* 최소한의 Critical CSS - 폰트 최적화만 */}
73-
<style
74-
dangerouslySetInnerHTML={{
75-
__html: `
76-
/* 폰트 즉시 렌더링 - swap과 호환 */
77-
html {
78-
font-family: var(--font-pretendard), system-ui, -apple-system, sans-serif;
79-
font-synthesis: none;
80-
text-rendering: optimizeLegibility;
81-
-webkit-font-smoothing: antialiased;
82-
-moz-osx-font-smoothing: grayscale;
83-
}
84-
85-
body {
86-
margin: 0;
87-
background: white;
88-
font-family: system-ui, -apple-system, sans-serif; /* 폰트 로딩 전 즉시 렌더링 */
89-
}
90-
91-
/* 폰트 로딩 시 깜빡임 최소화 */
92-
@font-face {
93-
font-family: 'Pretendard Variable';
94-
font-display: swap;
95-
}
96-
97-
/* LCP 이미지만 최적화 */
98-
.w-\\[153px\\] { width: 153px; }
99-
.h-\\[120px\\] { height: 120px; }
100-
.rounded-lg { border-radius: 0.5rem; }
101-
.object-cover { object-fit: cover; }
102-
`,
103-
}}
104-
/>
10566
</head>
10667
<body className={pretendard.className}>
10768
<AppleScriptLoader />

src/app/university/application/ScoreSearchBar.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
import styles from "./score-search-bar.module.css";
2-
31
import { IconSearchFilled } from "@/public/svgs";
42

53
type ScoreSearchBarProps = {
64
onClick: () => void;
75
textRef: React.RefObject<HTMLInputElement>;
8-
searchHandler: (e: React.FormEvent) => void;
6+
searchHandler: (_e: React.FormEvent) => void;
97
};
108

119
const ScoreSearchBar = ({ onClick, textRef, searchHandler }: ScoreSearchBarProps) => (
12-
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
13-
<form onClick={onClick} className={styles.searchBar} onSubmit={searchHandler}>
14-
<input className={styles.searchInput} placeholder="해외 파견 학교를 검색하세요." ref={textRef} />
15-
<button className={styles.searchButton} type="submit" aria-label="검색">
10+
<form
11+
onClick={onClick}
12+
onKeyDown={(e) => e.key === "Enter" && onClick()}
13+
className="flex h-[53px] flex-row items-center border-b border-[#d7d7d7]"
14+
onSubmit={searchHandler}
15+
role="search"
16+
>
17+
<input
18+
className="w-full border-0 pl-6 text-base font-normal leading-6 text-[#606060] outline-none"
19+
placeholder="해외 파견 학교를 검색하세요."
20+
ref={textRef}
21+
/>
22+
<button className="cursor-pointer border-0 bg-white pr-[11px]" type="submit" aria-label="검색">
1623
<IconSearchFilled />
1724
</button>
1825
</form>

src/app/university/application/ScoreSearchField.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
import styles from "./score-search-field.module.css";
2-
31
type ScoreSearchFieldProps = {
42
keyWords: string[];
5-
setKeyWord: (keyWord: string) => void;
3+
setKeyWord: (_keyWord: string) => void;
64
};
75

86
const ScoreSearchField = ({ keyWords, setKeyWord }: ScoreSearchFieldProps) => (
97
<div>
10-
<div className={styles.title}>인기 검색</div>
11-
<div className={styles.container}>
8+
<div className="ml-5 mt-[18px] text-base font-semibold text-black">인기 검색</div>
9+
<div className="ml-5 mt-2.5 flex flex-wrap gap-2">
1210
{keyWords.map((keyWord) => (
13-
<button key={keyWord} className={styles.item} onClick={() => setKeyWord(keyWord)} type="button">
11+
<button
12+
key={keyWord}
13+
className="flex items-center justify-center gap-2.5 rounded-full bg-[#fafafa] px-3 py-[5px] text-sm font-medium leading-[160%] text-black"
14+
onClick={() => setKeyWord(keyWord)}
15+
type="button"
16+
>
1417
{keyWord}
1518
</button>
1619
))}

src/app/university/application/score-search-bar.module.css

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/app/university/application/score-search-field.module.css

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/app/university/score/score-search-bar.module.css

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/app/university/score/score-search-field.module.css

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)