Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions internal/policy/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,18 @@ func ValidatePolicy(policy *schema.UserPolicy) error {
}

// Validate rules
ruleNumbers := make(map[int]bool)
ruleIDs := make(map[string]bool)
for i, rule := range policy.Rules {
if rule.Say == "" {
return fmt.Errorf("rule %d: 'say' field is required", i+1)
}
if rule.No <= 0 {
return fmt.Errorf("rule %d: 'no' field must be positive", i+1)
if rule.ID == "" {
return fmt.Errorf("rule %d: 'id' field is required", i+1)
}
if ruleNumbers[rule.No] {
return fmt.Errorf("duplicate rule number: %d", rule.No)
if ruleIDs[rule.ID] {
return fmt.Errorf("duplicate rule id: %s", rule.ID)
}
ruleNumbers[rule.No] = true
ruleIDs[rule.ID] = true
}

// Validate RBAC roles
Expand Down
20 changes: 10 additions & 10 deletions internal/policy/templates/go-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,54 @@
},
"rules": [
{
"no": 1,
"id": "1",
"say": "패키지 이름은 소문자 한 단어로 작성합니다 (예: http, json, user)",
"category": "naming",
"example": "// ✅ 좋은 예:\npackage user\npackage database\npackage httputil\n\n// ❌ 나쁜 예:\npackage UserManagement\npackage data_base\npackage HTTP_Util"
},
{
"no": 2,
"id": "2",
"say": "변수와 함수 이름은 camelCase 또는 PascalCase를 사용합니다 (public은 대문자 시작)",
"category": "naming"
},
{
"no": 3,
"id": "3",
"say": "에러는 항상 처리하고, 무시하는 경우 명시적으로 _ 를 사용합니다",
"category": "error_handling",
"example": "// ✅ 좋은 예:\nfile, err := os.Open(\"config.json\")\nif err != nil {\n return fmt.Errorf(\"failed to open config: %w\", err)\n}\ndefer file.Close()\n\n// ❌ 나쁜 예:\nfile, _ := os.Open(\"config.json\")\ndefer file.Close()"
},
{
"no": 4,
"id": "4",
"say": "함수는 에러를 마지막 반환값으로 반환합니다",
"category": "error_handling"
},
{
"no": 5,
"id": "5",
"say": "인터페이스 이름은 -er 접미사를 사용합니다 (예: Reader, Writer, Handler)",
"category": "naming"
},
{
"no": 6,
"id": "6",
"say": "gofmt로 코드를 포맷팅합니다",
"category": "formatting"
},
{
"no": 7,
"id": "7",
"say": "공개(exported) 함수와 타입에는 주석을 작성합니다",
"category": "documentation"
},
{
"no": 8,
"id": "8",
"say": "context.Context를 함수의 첫 번째 파라미터로 전달합니다",
"category": "error_handling"
},
{
"no": 9,
"id": "9",
"say": "goroutine 사용 시 적절한 동기화(sync, channel)를 사용합니다",
"category": "error_handling"
},
{
"no": 10,
"id": "10",
"say": "defer를 사용하여 리소스 정리를 보장합니다",
"category": "error_handling"
}
Expand Down
20 changes: 10 additions & 10 deletions internal/policy/templates/node-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,55 +19,55 @@
},
"rules": [
{
"no": 1,
"id": "1",
"say": "환경 변수는 process.env가 아닌 설정 파일(config.js)을 통해 접근합니다",
"category": "security"
},
{
"no": 2,
"id": "2",
"say": "민감한 정보(API 키, 비밀번호)는 절대 하드코딩하지 않습니다",
"category": "security",
"example": "// ✅ 좋은 예:\nconst apiKey = process.env.API_KEY;\nconst dbPassword = config.get('database.password');\n\n// ❌ 나쁜 예:\nconst apiKey = 'sk-1234567890abcdef';\nconst dbPassword = 'mySecretPassword123';"
},
{
"no": 3,
"id": "3",
"say": "모든 비동기 함수는 try-catch 블록으로 에러를 처리합니다",
"category": "error_handling",
"example": "// ✅ 좋은 예:\nasync function fetchUser(id) {\n try {\n const user = await db.users.findById(id);\n return user;\n } catch (error) {\n logger.error('Failed to fetch user:', error);\n throw new ApiError('User not found', 404);\n }\n}\n\n// ❌ 나쁜 예:\nasync function fetchUser(id) {\n const user = await db.users.findById(id);\n return user;\n}"
},
{
"no": 4,
"id": "4",
"say": "API 라우트 핸들러는 항상 적절한 HTTP 상태 코드를 반환합니다",
"category": "error_handling"
},
{
"no": 5,
"id": "5",
"say": "데이터베이스 쿼리는 SQL injection을 방지하기 위해 parameterized query를 사용합니다",
"category": "security",
"example": "// ✅ 좋은 예:\nconst result = await db.query('SELECT * FROM users WHERE id = ?', [userId]);\n\n// ❌ 나쁜 예:\nconst result = await db.query(`SELECT * FROM users WHERE id = ${userId}`);"
},
{
"no": 6,
"id": "6",
"say": "파일 이름은 kebab-case를 사용합니다 (예: user-controller.js, auth-service.js)",
"category": "naming"
},
{
"no": 7,
"id": "7",
"say": "클래스 이름은 PascalCase, 함수/변수는 camelCase를 사용합니다",
"category": "naming"
},
{
"no": 8,
"id": "8",
"say": "미들웨어 함수는 next()를 명시적으로 호출하거나 응답을 보냅니다",
"category": "error_handling"
},
{
"no": 9,
"id": "9",
"say": "큰 의존성 패키지는 필요한 부분만 import합니다 (tree-shaking)",
"category": "performance"
},
{
"no": 10,
"id": "10",
"say": "로그 메시지는 적절한 레벨(debug, info, warn, error)을 사용합니다",
"category": "documentation"
}
Expand Down
20 changes: 10 additions & 10 deletions internal/policy/templates/python-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,55 +19,55 @@
},
"rules": [
{
"no": 1,
"id": "1",
"say": "PEP 8 스타일 가이드를 준수합니다 (들여쓰기 4칸, 최대 라인 길이 79자)",
"category": "formatting"
},
{
"no": 2,
"id": "2",
"say": "함수와 변수 이름은 snake_case를 사용합니다 (예: calculate_total, user_name)",
"category": "naming",
"example": "# ✅ 좋은 예:\ndef calculate_total_price(items):\n total_price = sum(item.price for item in items)\n return total_price\n\n# ❌ 나쁜 예:\ndef calculateTotalPrice(items):\n totalPrice = sum(item.price for item in items)\n return totalPrice"
},
{
"no": 3,
"id": "3",
"say": "클래스 이름은 PascalCase를 사용합니다 (예: UserModel, DataProcessor)",
"category": "naming",
"example": "# ✅ 좋은 예:\nclass UserProfile:\n def __init__(self, name):\n self.name = name\n\n# ❌ 나쁜 예:\nclass user_profile:\n def __init__(self, name):\n self.name = name"
},
{
"no": 4,
"id": "4",
"say": "상수는 대문자와 언더스코어를 사용합니다 (예: MAX_SIZE, API_KEY)",
"category": "naming"
},
{
"no": 5,
"id": "5",
"say": "모든 함수와 클래스는 docstring을 작성합니다",
"category": "documentation"
},
{
"no": 6,
"id": "6",
"say": "타입 힌트를 사용하여 함수 시그니처를 명확히 합니다",
"category": "documentation"
},
{
"no": 7,
"id": "7",
"say": "예외는 구체적인 타입으로 처리합니다 (Exception보다는 ValueError, TypeError 등)",
"category": "error_handling",
"example": "# ✅ 좋은 예:\ntry:\n result = int(user_input)\nexcept ValueError as e:\n logger.error(f\"Invalid input: {e}\")\n result = 0\n\n# ❌ 나쁜 예:\ntry:\n result = int(user_input)\nexcept:\n result = 0"
},
{
"no": 8,
"id": "8",
"say": "리소스(파일, 데이터베이스)는 with 문으로 관리합니다",
"category": "error_handling"
},
{
"no": 9,
"id": "9",
"say": "민감한 정보는 환경 변수나 설정 파일을 통해 관리합니다",
"category": "security"
},
{
"no": 10,
"id": "10",
"say": "리스트 컴프리헨션은 3줄 이내로 유지하고, 복잡한 경우 일반 for문을 사용합니다",
"category": "formatting"
}
Expand Down
20 changes: 10 additions & 10 deletions internal/policy/templates/react-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,60 +19,60 @@
},
"rules": [
{
"no": 1,
"id": "1",
"say": "컴포넌트 이름은 PascalCase를 사용합니다 (예: UserProfile, NavBar)",
"category": "naming",
"languages": ["javascript", "typescript", "jsx", "tsx"],
"example": "// ✅ 좋은 예:\nfunction UserProfile() { ... }\nfunction NavBar() { ... }\n\n// ❌ 나쁜 예:\nfunction userProfile() { ... }\nfunction nav_bar() { ... }"
},
{
"no": 2,
"id": "2",
"say": "React Hooks는 함수 컴포넌트 최상단에서만 호출합니다",
"category": "error_handling",
"languages": ["javascript", "typescript", "jsx", "tsx"],
"example": "// ✅ 좋은 예:\nfunction MyComponent() {\n const [count, setCount] = useState(0);\n const value = useMemo(() => count * 2, [count]);\n return <div>{value}</div>;\n}\n\n// ❌ 나쁜 예:\nfunction MyComponent() {\n if (condition) {\n const [count, setCount] = useState(0); // 조건부 호출 금지\n }\n}"
},
{
"no": 3,
"id": "3",
"say": "useEffect의 의존성 배열을 정확히 명시하여 무한 루프를 방지합니다",
"category": "error_handling",
"languages": ["javascript", "typescript"]
},
{
"no": 4,
"id": "4",
"say": "Props는 구조 분해 할당으로 받습니다 (예: function Button({ label, onClick }) {...})",
"category": "formatting",
"languages": ["javascript", "typescript", "jsx", "tsx"],
"example": "// ✅ 좋은 예:\nfunction Button({ label, onClick, disabled = false }) {\n return <button onClick={onClick} disabled={disabled}>{label}</button>;\n}\n\n// ❌ 나쁜 예:\nfunction Button(props) {\n return <button onClick={props.onClick}>{props.label}</button>;\n}"
},
{
"no": 5,
"id": "5",
"say": "컴포넌트 파일 하나당 하나의 컴포넌트만 export합니다",
"category": "formatting"
},
{
"no": 6,
"id": "6",
"say": "이벤트 핸들러 함수는 'handle' 접두사를 사용합니다 (예: handleClick, handleSubmit)",
"category": "naming"
},
{
"no": 7,
"id": "7",
"say": "boolean 타입의 Props는 'is', 'has', 'should' 접두사를 사용합니다 (예: isOpen, hasError)",
"category": "naming"
},
{
"no": 8,
"id": "8",
"say": "인라인 스타일 대신 CSS 모듈 또는 styled-components를 사용합니다",
"category": "formatting"
},
{
"no": 9,
"id": "9",
"say": "Key prop에 배열 인덱스를 사용하지 않습니다. 고유한 ID를 사용하세요",
"category": "performance",
"example": "// ✅ 좋은 예:\n{users.map(user => <UserCard key={user.id} user={user} />)}\n\n// ❌ 나쁜 예:\n{users.map((user, index) => <UserCard key={index} user={user} />)}"
},
{
"no": 10,
"id": "10",
"say": "큰 리스트는 React.memo 또는 useMemo로 최적화합니다",
"category": "performance"
}
Expand Down
20 changes: 10 additions & 10 deletions internal/policy/templates/typescript-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,54 @@
},
"rules": [
{
"no": 1,
"id": "1",
"say": "any 타입 사용을 최소화하고 구체적인 타입을 정의합니다",
"category": "error_handling",
"example": "// ✅ 좋은 예:\ninterface User {\n id: number;\n name: string;\n}\nfunction processUser(user: User): void { ... }\n\n// ❌ 나쁜 예:\nfunction processUser(user: any): void { ... }"
},
{
"no": 2,
"id": "2",
"say": "인터페이스와 타입 별칭의 이름은 PascalCase를 사용합니다",
"category": "naming"
},
{
"no": 3,
"id": "3",
"say": "타입 정의 파일(.d.ts)은 types/ 폴더에 위치시킵니다",
"category": "formatting"
},
{
"no": 4,
"id": "4",
"say": "제네릭 타입 파라미터는 의미 있는 이름을 사용합니다 (T보다는 TItem, TResponse)",
"category": "naming"
},
{
"no": 5,
"id": "5",
"say": "유틸리티 타입(Partial, Pick, Omit 등)을 적극 활용합니다",
"category": "performance"
},
{
"no": 6,
"id": "6",
"say": "strict 모드를 활성화하여 엄격한 타입 체크를 수행합니다",
"category": "error_handling"
},
{
"no": 7,
"id": "7",
"say": "열거형(enum) 대신 const assertion이나 union 타입을 고려합니다",
"category": "performance"
},
{
"no": 8,
"id": "8",
"say": "함수 시그니처에 반환 타입을 명시적으로 선언합니다",
"category": "documentation",
"example": "// ✅ 좋은 예:\nfunction calculateTotal(price: number, quantity: number): number {\n return price * quantity;\n}\n\n// ❌ 나쁜 예:\nfunction calculateTotal(price: number, quantity: number) {\n return price * quantity;\n}"
},
{
"no": 9,
"id": "9",
"say": "readonly를 사용하여 불변성을 보장합니다",
"category": "error_handling"
},
{
"no": 10,
"id": "10",
"say": "타입 가드를 사용하여 런타임 타입 안전성을 확보합니다",
"category": "error_handling"
}
Expand Down
Loading