Files
invyone/CLAUDE.md
T
johngreen 895cb48ee0 docs(claude): UI/구조 변경 제안 시 ASCII Before/After 그림 의무화
기존 컨벤션에 § 💬 추가. 글로만 설명하면 사용자가 이해 못 함 — 변경 전/후 두 그림을 무조건
그려서 보여줘야 함. 코드 인용 (file:line, CSS class) 최소화하고 영문/SQL/전문용어 풀어쓰기.
3줄 패턴 (무슨 일 / 사용자 영향 / 어떻게 고치는지) 권장.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:21:13 +09:00

20 KiB
Raw Blame History

절대 규칙: 검증 없는 주장 금지

내가 출력하는 모든 발언은 근거가 있어야 한다. 근거가 없으면 그 말을 하지 않는다. 위로·추정·일반론·"보통 그렇다"로 채우지 않는다.

위반 사례 (절대 하지 말 것)

  • "100명 중 5명도 안 된다" 같은 통계를 출처 없이 만들어내기
  • "통과 확률 70~80%" 같은 수치를 추정으로 제시하기
  • "보통", "일반적으로", "대부분" 으로 시작하는 일반론
  • 본인이 검증 안 한 SDK/API 동작을 단정적으로 설명하기
  • 위로·격려를 위해 사실이 아닌 것을 끼워넣기

발화 전 자기 검증

한 문장이라도 출력하기 전에 다음을 확인:

  1. 출처가 있는가? — 코드(파일:라인), 명령 결과, 공식 문서, 사용자가 준 정보, 도구 호출 결과 중 하나
  2. 출처가 없다면 추정인가? — 추정이면 명시적으로 "추정이지만…" 또는 "확인 안 됐지만…" 으로 시작
  3. 추정도 근거가 없으면? — 말하지 않는다. "모릅니다" 또는 "확인이 필요합니다" 라고 한다

모를 때의 정답

  • 검색·문서 조회·코드 읽기로 확인 가능하면 확인부터 한다
  • 확인이 불가능하면 "모릅니다" 가 정답. 그럴듯한 답을 만들지 않는다
  • 사용자 의사결정에 영향을 주는 사실일수록 더 엄격하게 적용

어겼을 때

사용자가 "그 근거 뭐야" 라고 묻거나 잘못된 사실을 지적하면:

  • 즉시 인정. "맞습니다. 그 수치 제가 지어냈습니다." 같이 명시적으로 시인
  • 변명·재포장 금지
  • 무엇이 검증된 사실이고 무엇이 추정/날조였는지 다시 분리해서 제시

💬 사용자에게 설명할 때 — 그림으로 (★ 중요)

UI 변경 제안, 디자인 토론, 코드 구조 설명 등을 할 때는 반드시 변경 전/후를 ASCII 표나 도식으로 그려서 보여준다. 글로만 설명하면 사용자가 이해 못 한다.

원칙

  1. 변경 제안은 무조건 Before / After 두 그림
  2. 코드 인용 (file:line, 변수명, CSS class) 최소화 — 결론과 시각적 영향 위주
  3. 평어, 한국어, 짧은 문장
  4. 영문/SQL/전문용어 풀어쓰기 — "grid template" 대신 "표 컬럼 배치", "stopPropagation" 대신 "클릭이 위로 새는 거 막기"
  5. 3줄 패턴 권장 — 무슨 일 / 사용자한테 보이는 영향 / 어떻게 고치는지

나쁜 예시

"ColumnGrid.tsx:93-103 의 grid-cols-[4px_140px_1fr_100px_160px_40px] 를 5컬럼으로 축소하고, 라벨 셀에 sub-line 을 추가하면 entity/code/numbering 의 메타가 inline 으로..."

(사용자: "뭐라는지 모르겠어")

좋은 예시

지금 모양:

라벨·컬럼명  │ 참조/설정              │ 타입
거래처명     │ —                     │ 텍스트  ← 빈 칸
거래처ID     │ customer_mng → ...    │ 테이블참조

바꿔서:

라벨·컬럼명            │ 타입
거래처명               │ 텍스트
거래처ID               │ 테이블참조
  → customer_mng.id    ← 정보 있을 때만 작게 밑에

옵션 제시할 땐 표로

| 옵션 | 핵심                  | 단점              |
| A안  | 이름만 바꾸기          | 가장 가벼움       |
| B안  | 그룹을 잘게 쪼개기      | 그룹 수 늘어남    |

우선 순위

  • 첫 시도에 글만 쓰지 말 것. 그림부터 그리고 글은 짧게 보충.
  • 사용자가 "무슨 말인지 모르겠어" 하면 → 더 분해해서 다시 그림 그리기. 글 길어지면 더 헷갈림.

INVYONE — Claude 작업 컨벤션

이 파일은 git 에 올라가는 프로젝트 공용 Claude 가이드입니다. 모든 머신/팀원의 Claude Code 인스턴스가 이 컨벤션을 따라야 합니다.

(개인용 셋업은 CLAUDE.local.md — git 추적 제외, syncthing 동기화)


📝 분석 / 리포트 / 메모 MD 파일 저장 규칙 (★필수)

Claude 가 코드 분석, 보안 감사, 리팩토링 검토, 설계 문서, 회의록 등 새 MD 파일을 작성할 때는 다음 위치에 저장합니다.

저장 경로

notes/{git-user-name}/{YYYY-MM-DD}-{slug}.md
  • notes/ — 프로젝트 루트의 메모/리포트 모음 폴더 (이 폴더로 통일)
  • {git-user-name}git config user.name 으로 자동 결정 (예: gbpark, park)
  • {YYYY-MM-DD}-{slug}.md — 날짜 prefix + 짧은 제목 slug (kebab-case)

예시:

notes/gbpark/2026-04-08-auth-security-audit.md
notes/gbpark/2026-04-12-component-v2-migration-plan.md
notes/park/2026-04-15-docker-port-conflict-resolution.md

규칙

  1. 사용자 폴더가 이미 있으면 그 안에 넣는다 — 없으면 mkdir -p notes/{git-user} 로 생성
  2. 파일명은 항상 날짜 + slug 조합 — 시간순 정렬되어 추적 용이
  3. README 나 docs/ 와는 분리README.md, docs/ 는 사용자/개발자용 공식 문서. notes/ 는 작업 기록·분석·메모용
  4. MD 외 다른 산출물 (스크립트, JSON 등) 도 같이 둘 수 있음 — 필요하면 notes/{git-user}/{slug}/ 식 하위 폴더 사용
  5. 새 폴더/파일 작성 후엔 git add 권장 — syncthing 도 자동 동기화 (notes/.stignore-shared 에 없음)

어디에 안 넣는가

  • _local/, _backup/, _pipeline/ — syncthing ignore. 머신 로컬용
  • docs/ — 공식 개발 문서. 작업 기록 아님
  • 프로젝트 루트 직접 (./STATUS.md, ./PLAN.MD 등) — 이미 기존에 있는 것 외에 새로 만들지 말 것

컨벤션이 적용되는 시나리오

사용자 요청 저장 위치
"이 코드 분석해서 md 로 정리해줘" notes/{git-user}/{date}-{topic}.md
"보안 감사 리포트 만들어줘" notes/{git-user}/{date}-security-audit.md
"리팩토링 플랜 md 로 뽑아줘" notes/{git-user}/{date}-refactor-plan.md
"회의 노트 정리해줘" notes/{git-user}/{date}-meeting-notes.md
"마이그레이션 가이드 작성" notes/{git-user}/{date}-migration-guide.md

Claude 사용 시 추가 주의사항

  • 이 컨벤션은 사용자 명시 요청 없이도 자동 적용 — 사용자가 "md 만들어줘" 라고만 해도 위 경로에 저장
  • 현재 git user 확인이 필요하면 git config user.name 실행
  • 사용자 폴더가 처음이면 만들면서 .gitkeep 정도만 두지 말고 바로 첫 노트 작성

🎨 공통 디자인 시스템 / CSS 참조 규칙 (★★★ 무조건 적용)

UI 작업(컴포넌트 작성, HTML 목업, 새 페이지/화면, 디자인 리빌딩, 스타일 수정 등)을 할 때는 반드시 아래 공통 CSS 파일들을 먼저 읽고 그 안의 토큰/클래스 컨벤션을 100% 따라야 합니다. 절대 새 색상/간격/라운드/그림자 값을 즉흥으로 만들지 말 것.

"v5" 의 정체 (★ 헷갈리지 말 것)

INVION v5 = 디자인 시안 5번째 = 최종 채택본. 현재 컨셉은 "Solid + Glow" (2026-04-21 개정)

  • 디자이너가 v1~v5 까지 5번 시안을 만들고 그 중 v5 가 확정되어 React 로 포팅됨
  • 시안 원본 HTML (참고용, 여기엔 아직 glassmorphism 이 남아있음):
    • frontend/invion-layout-v5.html (973줄, 풀 레이아웃 셸)
    • frontend/invion-preview-v5.html (1049줄, 미리보기/모션 데모)
  • 폐기된 시안: frontend/invion-preview-v1~v4.html (참고만, 적용 금지)
  • 현재 적용 컨셉 (2026-04-21 개정):
    • 로그인 페이지: 우주(별/성운/별똥별/입자) 배경 + 글래스 카드 유지
    • 메인 화면 이후 전부: 반투명/blur/cosmic 배경 폐기. 불투명 솔리드 카드 + primary-color 글로우 + 보라(#6c5ce7)/시안(#00cec9)/핑크(#fd79a8) 액센트
  • v5 토큰이 옮겨진 곳: frontend/styles/v5-layout.css, frontend/app/(auth)/login/login.css

⚠️ POP 디자이너의 "v5 그리드 시스템" (PopRenderer.tsx, pop-layout.ts 등) 은 별개 의미 — POP 화면 데이터 포맷의 5번째 버전. UI 디자인 v5 와 무관. 혼동 금지.

항상 먼저 읽어야 하는 파일

파일 역할
frontend/invion-layout-v5.html v5 디자인 원본 (시안) — 모든 v5 토큰/클래스의 진실의 원천. 포팅된 css 와 다르면 이게 정답
frontend/styles/v5-layout.css INVION v5 React 포팅 메인--v5-* CSS 변수, 헤더/사이드바/탭/모달 등 모든 v5- 컴포넌트 클래스 정의. UI 작업 전 무조건 먼저 읽기
frontend/app/globals.css shadcn/Tailwind 토큰 (--background, --primary, --foreground 등 HSL), 다크모드 변수, 전역 reset
frontend/app/(auth)/login/login.css 로그인 전용 코스믹 배경(별/성운/입자) + 글래스 카드. 이 컨셉은 로그인에만 적용 — 메인 화면에 옮기지 말 것
frontend/components/layout/AppLayout.tsx v5 클래스가 실제로 어떻게 조립되는지 — 헤더/사이드바/탭/플라이아웃 사용 예

필수 준수 사항

  1. 디자인 토큰은 무조건 변수 사용--v5-primary, --v5-cyan, --v5-surface-solid, --v5-glow-sm/md/lg, hsl(var(--primary)) 등. 즉흥 hex/rgb 금지
  2. 클래스명은 v5- 접두사 컨벤션 따르기 — 새 컴포넌트도 .v5-card, .v5-btn, .v5-bdg 처럼 같은 네이밍. shadcn 컴포넌트 사용 시 그대로 사용
  3. 반투명/블러 금지 (★2026-04-21 신규) — 메인 화면 이후 전 영역에서 backdrop-filter: blur(...), var(--v5-glass), var(--v5-glass-strong) 사용 금지. 카드/모달/사이드바/헤더 배경은 var(--v5-surface-solid) (라이트 #ffffff / 다크 #11102a) 를 쓰고, 테두리는 border-border 또는 var(--v5-border). 예외: frontend/app/(auth)/login/frontend/styles/builder-ide.css 는 별도 스코프라 기존 유지
  4. 글로우는 유지 — 그림자는 검은 drop-shadow 대신 var(--v5-glow-sm/md/lg) (primary-color glow) 사용. 모달/강조 카드에 liberal 하게 사용 가능
  5. 다크/라이트 모드는 둘 다 동작.dark 변형 잊지 말 것. 다크에서 --v5-surface-solid#11102a, 라이트는 #ffffff. 별/입자/별똥별/성운은 로그인에만 존재, 메인은 평범한 단색 배경
  6. 컴팩트 폰트 사이즈 유지 — v5 는 0.55~0.85rem 의 컴팩트 UI. 새로 만들 때도 같은 스케일 따를 것
  7. 새 UI 패턴은 v5-layout.css 에 합치는 것을 기본 방향으로 — 일회성 inline <style> 보다 공통화 우선

작업 순서 (UI 작업 시 반드시)

1) frontend/styles/v5-layout.css 읽기
2) 필요하면 globals.css, login.css 도 함께 읽기
3) 기존 v5- 클래스 중 재사용 가능한 것 찾기
4) 모자라는 부분만 같은 토큰/네이밍으로 추가
5) 작업 결과를 사용자에게 보여줄 때 "어떤 v5 토큰/클래스를 따랐는지" 명시

예외

  • 단발성 디버그/실험 페이지(debug-*, test-*)는 임시 스타일 허용
  • notes/ 안의 1회성 HTML 목업은 v5 토큰을 inline 으로 가져와 standalone 이어도 됨 (단 토큰 값은 반드시 v5-layout.css 와 동일해야 함)

이 규칙은 사용자가 명시 요청하지 않아도 모든 UI 작업에 자동 적용됩니다.


🔧 백엔드 코딩 규칙 — 스타일 (★★★ 절대 규칙)

아키텍처: 3레이어 (Mapper Interface 금지)

Controller (@RestController)
    ↓
Service (extends BaseService) — sqlSession 직접 사용
    ↓
XML (resources/mapper/[module].xml) — 소문자 파일명
  • @Mapper 인터페이스 생성 금지sqlSession.selectList("namespace.queryId", params) 직접 호출
  • Service는 반드시 BaseService 상속, @Slf4j, @Autowired CommonService

데이터: Map<String, Object> — DTO/엔티티 클래스 금지

로우코드 ERP: 테이블/컬럼이 런타임에 결정됨. DTO 클래스 사전 생성 불가.

  • 모든 파라미터: Map<String, Object>
  • 모든 응답: Map<String, Object> 또는 List<Map<String, Object>>
  • ApiResponse만 유일한 DTO

네이밍 컨벤션 (절대 규칙)

위치 컨벤션 예시
Java 코드 camelCase getOrderList(), String companyCode
Map 키 (params.put, row.get) snake_case params.put("company_code", ...), row.get("table_name")
#{파라미터} snake_case #{company_code}, #{table_name}
SQL (키워드/테이블/컬럼) UPPER_SNAKE SELECT COMPANY_CODE FROM TEMPLATES
SELECT 쉼표 앞에 , COLUMN_NAME
XML 파일명 소문자, Mapper 안 붙임 meta.xml, template.xml
XML namespace 파일명과 동일 namespace="meta"
OGNL test 바깥 작은따옴표 test='company_code != "*"'

메서드명 패턴

조작 패턴 예시
목록 get[Module]List getTemplateList
카운트 get[Module]ListCnt getTemplateListCnt
단건 get[Module]Info getTemplateInfo
등록 insert[Module] insertTemplate
수정 update[Module] updateTemplate
삭제 delete[Module] deleteTemplate

List API는 반드시 Count 쿼리 동반 (getXxxList + getXxxListCnt = 한 세트)

common 레이어 필수

<include refid="common.companyCodeFilter"/>
<include refid="common.dynamicOrderBy"/>
<include refid="common.pagination"/>

🌐 프론트엔드 데이터 타입 규칙 (★★★ 절대 규칙)

Record<string, any> — 별도 인터페이스 정의 금지

백엔드가 Map<String, Object>이므로 프론트도 Record<string, any>.

// ❌ 금지 — 불필요한 인터페이스 정의
interface TableInfo {
  table_name: string;
  column_count: number;
}

// ✅ 올바름
const tables: Record<string, any>[] = await getTableList();

유일한 예외: invyone-component.ts 규격 타입

frontend/types/invyone-component.ts에 확정된 타입만 예외:

  • FieldConfig, FieldType, FieldRef
  • Component, ComponentType, Position
  • DataPort, Connection
  • Template, ViewConfig
  • ComponentTypeConfig (TableConfig, FormConfig 등)

이것들은 시스템의 핵심 계약이므로 타입 유지. 그 외 전부 Record<string, any>.


📐 INVYONE 로우코드 핵심 구조 (★ 모든 세션이 알아야 할 것)

VEX → INVYONE 관계

INVYONE은 이미 운영 중인 로우코드 플랫폼 VEX(Node.js)의 2세대 리뉴얼(Java/Spring). 핵심 원칙: 더 단순한 구조로 재설계하되, VEX 운영 기능은 전부 계승. 단순화 ≠ 기능 삭제.

FieldConfig 단일 규격

VEX의 ColumnConfig(354줄) + FilterConfig + FormField → FieldConfig 하나(~30줄)로 통합. 테이블/폼/검색 전부 같은 FieldConfig를 공유하며, 렌더러가 type을 보고 각자 다르게 렌더.

대시보드 = 메뉴

사이드바 메뉴 항목 = 대시보드. 별도 대시보드 UI가 아님. 대시보드 생성("수주관리") → 사이드바 메뉴에 자동 등록 → 템플릿 카드 배치 → 화면 완성

구현 순서

  1. DB 메타 읽기 → FieldConfig 변환
  2. 규격 기반 컴포넌트 (FcTable/FcForm/FcSearch)
  3. 개발자 빌더 (수동 템플릿 구성)
  4. 대시보드(=메뉴) 시스템
  5. 제어 모드 (비즈니스 룰)
  6. 자동생성/프리셋 (맨 마지막)

설계 문서 위치

문서 위치
컴포넌트 규격 v1.0 notes/gbpark/2026-04-08-invyone-component-spec.md
아키텍처 결정 notes/gbpark/2026-04-09-invyone-architecture.md
로우코드 플랫폼 SPEC notes/gbpark/2026-04-08-lowcode-platform-spec.md
Phase 1~5 구현 설계 notes/gbpark/2026-04-10-phase{1~5}-*.md
mockup (시각적 진실의 원천) notes/gbpark/2026-04-08-invyone-mockup/
FieldConfig TS 타입 frontend/types/invyone-component.ts

🏢 멀티테넌시 · 서브도메인 라우팅 (★★★ 아키텍처 핵심)

INVYONE 은 DB-per-tenant 구조. 회사 하나 = 전용 PostgreSQL DB 하나 = 전용 서브도메인 하나. qnc.invyone.com 접속 → SubdomainResolverFilter 가 Host 헤더 파싱 → TenantRoutingDataSourceqnc_invyone DB 로 자동 라우팅.

전체 설명·플로우·배포 가이드는 반드시 읽을 것:

  • docs/MULTI_TENANCY_ARCHITECTURE.md ← 이거 하나만 읽으면 끝 (환경별 도메인 전략, CORS 패턴, 배포 체크리스트 포함)

절대 건드리면 안 되는 3가지

  1. 테넌트 도메인에서 NEXT_PUBLIC_API_URL=/api 같은 Next rewrite 쓰지 말 것. Rewrite 가 Host 헤더를 변조해 *.invyone.com 서브도메인 파싱이 실패함. → frontend/lib/api/client.ts 의 ".invyone.com 이면 직접 :8083/api" 분기가 NEXT_PUBLIC_API_URL 체크보다 앞에 와야 함.

  2. CORS 는 setAllowedOriginPatterns (setAllowedOrigins 아님). 와일드카드 매칭 필요. YAML 의 [*] 는 반드시 따옴표로 감쌀 것 (sequence 로 해석됨). CORS_ALLOWED_ORIGINS.env 에 있고 .gitignore 대상 → 서버마다 수동 세팅.

  3. 회사별 Hikari 풀은 반드시 minIdle=0. 회사 N개 × minIdle=2 하면 Postgres max_connections 금방 초과. TenantDataSourceFactory.createTenant(...) 만 사용할 것.

회사 생성 = API 호출 한 번

POST /api/admin/provisioning/companies
  → 6단계 자동 실행 (DB 생성 → pg_dump 스키마 복제 → 템플릿 데이터 → 관리자 → 메타 등록)
  → 13~15초 내 완료, 바로 서브도메인 접속 가능

필수 필드 4개만: company_code, company_name, subdomain, db_prefix. 나머지(사업자번호·대표자·이메일 등) 전부 optional.

환경별 도메인 — 요약

환경 접속 추가 설정
운영 *.invyone.com (+ 메인 solution.invyone.com) 세팅 완료 (2026-04-24) — Porkbun 와일드카드 DNS + Traefik v2.11 + Let's Encrypt DNS-01 와일드카드 TLS
로컬 (직접 docker up) *.localhost 자동 0 (RFC 6761, Chrome/Firefox 기본 지원)
원격 공유 개발서버 nip.io or hosts 편집 경우 따라

새 환경 붙일 때 체크리스트

  • .envCORS_ALLOWED_ORIGINS 에 해당 도메인 패턴 포함 (http://*.invyone.com:[*] 등)
  • DB 마이그레이션 RUN_079/080/081.md 운영 DB 에서 1회 실행
  • docker compose build --no-cache backend-spring (postgresql16-client 포함)
  • 프로덕션은 tenant.provisioning.require-super-admin=true

관련 코드 맵

backend-spring/src/main/java/com/erp/
├── tenant/               # 라우팅 (Filter / Holder / CompanyResolver / RoutingDataSource / Factory / DataSourceConfig / TenantController)
└── provisioning/         # 회사 생성 (Service / Registry / DatabaseCreator / SchemaCopier / DataCopier / AdminAccountCreator / Controller / StatsService)

backend-spring/src/main/resources/mapper/
├── tenant.xml            # resolveDbNameBySubdomain
└── provisioning.xml      # exists / insertCompanyWithTenant / listCompaniesForUi / updateDbStatus

frontend/
├── lib/api/provisioning.ts
├── lib/tenant/subdomain.ts                 # 호스트 파싱 + 예약어 제외
├── components/TenantGuard.tsx              # 미등록 서브도메인 차단 (layout wrap)
├── components/admin/provisioning/          # 메인 화면 (accordion / stats strip / status dot / sparkline)
│   └── wizard/                             # 4-step 마법사 (force_password_change 옵션 포함)
├── app/tenant-not-found/page.tsx           # v5 solid+glow 에러 페이지
└── app/(main)/admin/sysMng/subdomainList/  # 회사 프로비저닝 페이지 엔트리

db/migrations/RUN_079/080/081_MIGRATION.md
notes/gbpark/2026-04-24-traefik-wildcard/   # 운영 Traefik 설정 산출물 (2026-04-24)