기존 컨벤션에 § 💬 추가. 글로만 설명하면 사용자가 이해 못 함 — 변경 전/후 두 그림을 무조건 그려서 보여줘야 함. 코드 인용 (file:line, CSS class) 최소화하고 영문/SQL/전문용어 풀어쓰기. 3줄 패턴 (무슨 일 / 사용자 영향 / 어떻게 고치는지) 권장. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
20 KiB
절대 규칙: 검증 없는 주장 금지
내가 출력하는 모든 발언은 근거가 있어야 한다. 근거가 없으면 그 말을 하지 않는다. 위로·추정·일반론·"보통 그렇다"로 채우지 않는다.
위반 사례 (절대 하지 말 것)
- "100명 중 5명도 안 된다" 같은 통계를 출처 없이 만들어내기
- "통과 확률 70~80%" 같은 수치를 추정으로 제시하기
- "보통", "일반적으로", "대부분" 으로 시작하는 일반론
- 본인이 검증 안 한 SDK/API 동작을 단정적으로 설명하기
- 위로·격려를 위해 사실이 아닌 것을 끼워넣기
발화 전 자기 검증
한 문장이라도 출력하기 전에 다음을 확인:
- 출처가 있는가? — 코드(파일:라인), 명령 결과, 공식 문서, 사용자가 준 정보, 도구 호출 결과 중 하나
- 출처가 없다면 추정인가? — 추정이면 명시적으로 "추정이지만…" 또는 "확인 안 됐지만…" 으로 시작
- 추정도 근거가 없으면? — 말하지 않는다. "모릅니다" 또는 "확인이 필요합니다" 라고 한다
모를 때의 정답
- 검색·문서 조회·코드 읽기로 확인 가능하면 확인부터 한다
- 확인이 불가능하면 "모릅니다" 가 정답. 그럴듯한 답을 만들지 않는다
- 사용자 의사결정에 영향을 주는 사실일수록 더 엄격하게 적용
어겼을 때
사용자가 "그 근거 뭐야" 라고 묻거나 잘못된 사실을 지적하면:
- 즉시 인정. "맞습니다. 그 수치 제가 지어냈습니다." 같이 명시적으로 시인
- 변명·재포장 금지
- 무엇이 검증된 사실이고 무엇이 추정/날조였는지 다시 분리해서 제시
💬 사용자에게 설명할 때 — 그림으로 (★ 중요)
UI 변경 제안, 디자인 토론, 코드 구조 설명 등을 할 때는 반드시 변경 전/후를 ASCII 표나 도식으로 그려서 보여준다. 글로만 설명하면 사용자가 이해 못 한다.
원칙
- 변경 제안은 무조건 Before / After 두 그림
- 코드 인용 (file:line, 변수명, CSS class) 최소화 — 결론과 시각적 영향 위주
- 평어, 한국어, 짧은 문장
- 영문/SQL/전문용어 풀어쓰기 — "grid template" 대신 "표 컬럼 배치", "stopPropagation" 대신 "클릭이 위로 새는 거 막기"
- 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
규칙
- 사용자 폴더가 이미 있으면 그 안에 넣는다 — 없으면
mkdir -p notes/{git-user}로 생성 - 파일명은 항상 날짜 + slug 조합 — 시간순 정렬되어 추적 용이
- README 나 docs/ 와는 분리 —
README.md,docs/는 사용자/개발자용 공식 문서.notes/는 작업 기록·분석·메모용 - MD 외 다른 산출물 (스크립트, JSON 등) 도 같이 둘 수 있음 — 필요하면
notes/{git-user}/{slug}/식 하위 폴더 사용 - 새 폴더/파일 작성 후엔 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 클래스가 실제로 어떻게 조립되는지 — 헤더/사이드바/탭/플라이아웃 사용 예 |
필수 준수 사항
- 디자인 토큰은 무조건 변수 사용 —
--v5-primary,--v5-cyan,--v5-surface-solid,--v5-glow-sm/md/lg,hsl(var(--primary))등. 즉흥 hex/rgb 금지 - 클래스명은 v5- 접두사 컨벤션 따르기 — 새 컴포넌트도
.v5-card,.v5-btn,.v5-bdg처럼 같은 네이밍. shadcn 컴포넌트 사용 시 그대로 사용 - 반투명/블러 금지 (★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는 별도 스코프라 기존 유지 - 글로우는 유지 — 그림자는 검은 drop-shadow 대신
var(--v5-glow-sm/md/lg)(primary-color glow) 사용. 모달/강조 카드에 liberal 하게 사용 가능 - 다크/라이트 모드는 둘 다 동작 —
.dark변형 잊지 말 것. 다크에서--v5-surface-solid는#11102a, 라이트는#ffffff. 별/입자/별똥별/성운은 로그인에만 존재, 메인은 평범한 단색 배경 - 컴팩트 폰트 사이즈 유지 — v5 는 0.55~0.85rem 의 컴팩트 UI. 새로 만들 때도 같은 스케일 따를 것
- 새 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,FieldRefComponent,ComponentType,PositionDataPort,ConnectionTemplate,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가 아님.
대시보드 생성("수주관리") → 사이드바 메뉴에 자동 등록 → 템플릿 카드 배치 → 화면 완성
구현 순서
- DB 메타 읽기 → FieldConfig 변환
- 규격 기반 컴포넌트 (FcTable/FcForm/FcSearch)
- 개발자 빌더 (수동 템플릿 구성)
- 대시보드(=메뉴) 시스템
- 제어 모드 (비즈니스 룰)
- 자동생성/프리셋 (맨 마지막)
설계 문서 위치
| 문서 | 위치 |
|---|---|
| 컴포넌트 규격 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 헤더 파싱 → TenantRoutingDataSource 가 qnc_invyone DB 로 자동 라우팅.
전체 설명·플로우·배포 가이드는 반드시 읽을 것:
docs/MULTI_TENANCY_ARCHITECTURE.md← 이거 하나만 읽으면 끝 (환경별 도메인 전략, CORS 패턴, 배포 체크리스트 포함)
절대 건드리면 안 되는 3가지
-
테넌트 도메인에서
NEXT_PUBLIC_API_URL=/api같은 Next rewrite 쓰지 말 것. Rewrite 가 Host 헤더를 변조해*.invyone.com서브도메인 파싱이 실패함. →frontend/lib/api/client.ts의 ".invyone.com이면 직접:8083/api" 분기가 NEXT_PUBLIC_API_URL 체크보다 앞에 와야 함. -
CORS 는
setAllowedOriginPatterns(setAllowedOrigins아님). 와일드카드 매칭 필요. YAML 의[*]는 반드시 따옴표로 감쌀 것 (sequence 로 해석됨).CORS_ALLOWED_ORIGINS는.env에 있고.gitignore대상 → 서버마다 수동 세팅. -
회사별 Hikari 풀은 반드시
minIdle=0. 회사 N개 × minIdle=2 하면 Postgresmax_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 편집 |
경우 따라 |
새 환경 붙일 때 체크리스트
.env의CORS_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)