엑셀 파일 부르기 완료, 입력창 UI 설정 완료

This commit is contained in:
sheetEasy AI Team
2025-06-24 17:48:11 +09:00
parent 105265a384
commit 5712c40ec9
14 changed files with 456 additions and 5241 deletions

View File

@@ -0,0 +1,122 @@
---
description:
globs:
alwaysApply: false
---
# Tailwind CSS v4 + Shadcn UI 호환성 규칙
## **CSS 설정 (src/index.css)**
- **@theme 레이어 사용**
```css
@theme {
--radius: 0.5rem;
}
```
- **CSS 변수 정의**
- `:root`에 라이트 모드 색상 변수 정의
- `.dark`에 다크 모드 색상 변수 정의
- `hsl(var(--foreground))` 형태로 색상 사용
## **cn 함수 (src/lib/utils.ts)**
- **에러 핸들링 필수**
```typescript
// ✅ DO: fallback 로직 포함
export function cn(...inputs: ClassValue[]) {
try {
return twMerge(clsx(inputs));
} catch (error) {
console.warn("tailwind-merge fallback:", error);
return clsx(inputs);
}
}
// ❌ DON'T: 에러 핸들링 없이 사용
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
```
## **컴포넌트 스타일링**
- **CSS 변수 활용**
```typescript
// ✅ DO: CSS 변수 기반 스타일링
className="bg-background text-foreground border-border"
// ✅ DO: cn 함수로 조건부 스타일링
className={cn(
"base-styles",
condition && "conditional-styles"
)}
```
- **색상 시스템 준수**
- `background`, `foreground`, `primary`, `secondary` 등 정의된 변수 사용
- 직접 색상 값 대신 변수 사용
## **패키지 관리**
- **필수 패키지**
```json
{
"@tailwindcss/cli": "^4.1.10",
"@tailwindcss/vite": "^4.1.10",
"tailwind-merge": "latest",
"clsx": "^2.1.1",
"class-variance-authority": "^0.7.1"
}
```
- **제거해야 할 파일**
- `tailwind.config.js` (v4는 CSS-first 방식)
- `postcss.config.js` (v4는 PostCSS 불필요)
## **Vite 설정**
- **플러그인 설정**
```typescript
// vite.config.ts
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [react(), tailwindcss()],
});
```
## **문제 해결**
- **tailwind-merge 오류 시**
- 최신 버전으로 업데이트
- cn 함수에 fallback 로직 구현
- **스타일이 적용되지 않을 때**
- CSS 변수가 올바르게 정의되었는지 확인
- @theme 레이어가 포함되었는지 확인
- **빌드 오류 시**
- node_modules 캐시 삭제 후 재설치
- package-lock.json 삭제 후 재설치
## **모범 사례**
- **컴포넌트 개발 시**
- 항상 CSS 변수 사용
- cn 함수로 클래스 조합
- 조건부 스타일링에 적절한 패턴 적용
- **테마 관리**
- 라이트/다크 모드 변수 동시 정의
- 일관된 색상 시스템 유지
- **성능 최적화**
- 불필요한 클래스 중복 방지
- cn 함수 사용으로 클래스 충돌 해결
## **참고 자료**
- [Tailwind CSS v4 공식 문서](https://tailwindcss.com/docs/v4-beta)
- [Shadcn UI + Tailwind v4 가이드](https://www.luisball.com/blog/shadcn-ui-with-tailwind-v4)
- [Shadcn UI 공식 설치 가이드](https://ui.shadcn.com/docs/installation/manual)

View File

@@ -3,3 +3,193 @@ description:
globs:
alwaysApply: false
---
# xlsx-js-style 스타일 보존 규칙
## **핵심 원칙**
- xlsx-js-style의 공식 API 구조를 직접 활용하여 스타일 변환
- 복잡한 색상 변환 로직 대신 공식 COLOR_STYLE 형식 지원
- 배경색과 테두리 색상 누락 방지를 위한 완전한 스타일 매핑
## **공식 xlsx-js-style API 활용**
### **색상 처리 (COLOR_STYLE)**
```typescript
// ✅ DO: 공식 COLOR_STYLE 형식 모두 지원
function convertXlsxColorToLuckysheet(colorObj: any): string {
// RGB 형태: {rgb: "FFCC00"}
if (colorObj.rgb) { /* RGB 처리 */ }
// Theme 색상: {theme: 4} 또는 {theme: 1, tint: 0.4}
if (typeof colorObj.theme === 'number') { /* Theme 처리 */ }
// Indexed 색상: Excel 기본 색상표
if (typeof colorObj.indexed === 'number') { /* Indexed 처리 */ }
}
// ❌ DON'T: 특정 색상 형식만 처리
function badColorConvert(colorObj: any): string {
return colorObj.rgb || "rgb(0,0,0)"; // rgb만 처리하고 theme, indexed 무시
}
```
### **스타일 객체 변환**
```typescript
// ✅ DO: 공식 스타일 속성 완전 매핑
function convertXlsxStyleToLuckysheet(xlsxStyle: any): any {
const luckyStyle: any = {};
// 폰트: {name: "Courier", sz: 24, bold: true, color: {rgb: "FF0000"}}
if (xlsxStyle.font) {
if (xlsxStyle.font.name) luckyStyle.ff = xlsxStyle.font.name;
if (xlsxStyle.font.sz) luckyStyle.fs = xlsxStyle.font.sz;
if (xlsxStyle.font.bold) luckyStyle.bl = 1;
if (xlsxStyle.font.color) {
luckyStyle.fc = convertXlsxColorToLuckysheet(xlsxStyle.font.color);
}
}
// 배경: {fgColor: {rgb: "E9E9E9"}}
if (xlsxStyle.fill?.fgColor) {
luckyStyle.bg = convertXlsxColorToLuckysheet(xlsxStyle.fill.fgColor);
}
// 테두리: {top: {style: "thin", color: {rgb: "000000"}}}
if (xlsxStyle.border) {
luckyStyle.bd = {};
if (xlsxStyle.border.top) {
luckyStyle.bd.t = {
style: convertBorderStyleToLuckysheet(xlsxStyle.border.top.style),
color: convertXlsxColorToLuckysheet(xlsxStyle.border.top.color)
};
}
}
return luckyStyle;
}
// ❌ DON'T: 수동으로 스타일 속성 하나씩 처리
luckyCell.v.s = {
ff: cell.s.font?.name || "Arial",
bg: cell.s.fill?.fgColor?.rgb || "rgb(255,255,255)" // 직접 rgb만 처리
};
```
## **배경색과 테두리 색상 누락 방지**
### **배경색 처리**
```typescript
// ✅ DO: fgColor와 bgColor 모두 확인
if (xlsxStyle.fill) {
if (xlsxStyle.fill.fgColor) {
luckyStyle.bg = convertXlsxColorToLuckysheet(xlsxStyle.fill.fgColor);
} else if (xlsxStyle.fill.bgColor) {
luckyStyle.bg = convertXlsxColorToLuckysheet(xlsxStyle.fill.bgColor);
}
}
// ❌ DON'T: fgColor만 확인
if (xlsxStyle.fill?.fgColor) {
luckyStyle.bg = xlsxStyle.fill.fgColor.rgb; // 다른 색상 형식 무시
}
```
### **테두리 색상 처리**
```typescript
// ✅ DO: 모든 테두리 방향과 색상 형식 지원
if (xlsxStyle.border) {
['top', 'bottom', 'left', 'right'].forEach(side => {
if (xlsxStyle.border[side]) {
luckyStyle.bd[side[0]] = {
style: convertBorderStyleToLuckysheet(xlsxStyle.border[side].style),
color: convertXlsxColorToLuckysheet(xlsxStyle.border[side].color)
};
}
});
}
// ❌ DON'T: 하드코딩된 색상 사용
luckyStyle.bd.t = {
style: 1,
color: "rgb(0,0,0)" // 실제 색상 무시
};
```
## **Excel Tint 처리**
```typescript
// ✅ DO: Excel tint 공식 적용
function applyTintToRgbColor(rgbColor: string, tint: number): string {
const applyTint = (color: number, tint: number): number => {
if (tint < 0) {
return Math.round(color * (1 + tint));
} else {
return Math.round(color * (1 - tint) + (255 - 255 * (1 - tint)));
}
};
// RGB 각 채널에 tint 적용
}
// ❌ DON'T: tint 무시
if (colorObj.theme) {
return themeColors[colorObj.theme]; // tint 무시
}
```
## **오류 방지 패턴**
### **안전한 스타일 읽기**
```typescript
// ✅ DO: 옵셔널 체이닝과 타입 검사
workbook = XLSX.read(arrayBuffer, {
cellStyles: true // 스타일 정보 보존
});
// 스타일 정보 확인
if (cell.s) {
console.log(`🎨 셀 ${cellAddress}에 스타일 정보:`, cell.s);
luckyCell.v.s = convertXlsxStyleToLuckysheet(cell.s);
}
// ❌ DON'T: 스타일 옵션 누락
workbook = XLSX.read(arrayBuffer); // cellStyles 옵션 없음
```
### **스타일 쓰기 보존**
```typescript
// ✅ DO: 쓰기 시에도 스타일 보존
const xlsxData = XLSX.write(workbook, {
type: "array",
bookType: "xlsx",
cellStyles: true // 스타일 정보 보존
});
// ❌ DON'T: 쓰기 시 스타일 누락
const xlsxData = XLSX.write(workbook, {
type: "array",
bookType: "xlsx"
// cellStyles 옵션 없음
});
```
## **디버깅 및 검증**
### **스타일 정보 로깅**
```typescript
// ✅ DO: 개발 모드에서 스타일 정보 상세 분석
if (import.meta.env.DEV && cell.s) {
console.log(`🎨 셀 ${cellAddress} 스타일:`, {
font: cell.s.font,
fill: cell.s.fill,
border: cell.s.border,
alignment: cell.s.alignment
});
}
// ❌ DON'T: 스타일 정보 무시
// 스타일 관련 로그 없음
```
## **참고 사항**
- [xlsx-js-style GitHub](https://github.com/gitbrent/xlsx-js-style) 공식 문서 참조
- 공식 COLOR_STYLE 형식: `{rgb: "FFCC00"}`, `{theme: 4}`, `{theme: 1, tint: 0.4}`
- 공식 BORDER_STYLE 값: `thin`, `medium`, `thick`, `dotted`, `dashed` 등
- Excel 테마 색상과 tint 처리는 공식 Excel 색상 공식 사용