inblog logo
|
찬찬잉
    CSFront-end Developer

    하네스 엔지니어링과 1분기 Ai 사용 후기

    이젠 Ai를 순응하고 대비할 때
    찬찬잉's avatar
    찬찬잉
    Apr 10, 2026
    하네스 엔지니어링과 1분기 Ai 사용 후기
    Contents
    기존 개발 방식: 프롬프트 엔지니어링한계: AI는 결국 실수한다그래서 등장한 개념: 하네스 엔지니어링쉽게 말하면?실제 적용: AI 파이프라인 구축문제 발생: Codex UTF-8 버그해결 방법: Skill 기반 제약Claude 호출 구조 핵심Codex 구조에서 달라진 점실제 결과 (핵심 포인트)중요한 인사이트공급망 공격 대응 (Axios 사례 기준)총정리
    현재 IT 회사들의 변화 속도는 상상을 초월할 정도로 빠르게 진행되고 있습니다.
    특히 AI를 활용한 개발 방식은 단순한 도구 수준을 넘어, 개발 프로세스 자체를 바꾸는 방향으로 진화하고 있습니다.
    제가 참고한 영상은 실밸개발자 채널인데,
    실밸개발자
    AI 시대, 나만 뒤쳐질까 봐 불안하신가요? 8년 차 Meta 엔지니어가 현업에서 쓰는 AI 코딩 기술과 인사이트를 가감 없이 전달하여 누구나 실전에 바로 적용할 수 있게 됩니다. AI를 활용해 대체 불가능한 리더로 성장하는 길, 저와 함께하세요. 💼 Profile • Meta Software Engineer • 실리콘밸리 거주 및 근무 • 8년 차 개발자 👉 비즈니스 문의 - svdev.biz@gmail.com
    실밸개발자
    https://www.youtube.com/@sv.developer
    실밸개발자
    요즘 실리콘밸리에서 실제로 어떻게 AI를 활용하는지 흐름을 이해하는 데 도움이 많이 되었습니다.

    기존 개발 방식: 프롬프트 엔지니어링

    이전에는 대부분 다음과 같은 방식으로 AI를 활용했습니다.
    • 프롬프트를 잘 작성해서 결과를 얻는다
    • 결과를 사람이 검수하고 수정한다
    • 다시 질문해서 개선한다
    이 방식은 결국 아래 개념과 연결됩니다.
    GIGO(Garbage In, Garbage Out) → 잘못된 입력을 넣으면 결과도 쓰레기가 나온다
    저 역시 기획 + 개발 관점에서 OpenAI, Claude 같은 AI를 활용해 방향을 잡고 설계하는 방식으로 많이 사용해왔습니다.

    한계: AI는 결국 실수한다

    AI를 사용하면서 느낀 가장 큰 문제는 단 하나였습니다.
    AI는 생각보다 자주 틀린다
    • 타입을 잘못 추론
    • edge case 놓침
    • 구조적으로 위험한 코드 생성
    그래서 “프롬프트를 잘 쓰는 것”만으로는 부족하다는 결론에 도달했습니다.

    그래서 등장한 개념: 하네스 엔지니어링

    하네스 엔지니어링(Harness Engineering)은 다음과 같이 정의할 수 있습니다.
    AI가 안전하게 동작하도록 제약, 도구, 피드백 구조를 설계하는 방식
    notion image
    핵심은 3가지입니다.

    1. AI 제어 (Harnessing)

    • AI를 자유롭게 두는 것이 아니라
    • 규칙과 제한을 걸어 원하는 방향으로 유도

    2. 스캐폴딩 (Scaffolding)

    • AI 주변에 안전장치 구성
    • 파일 접근, 실행 범위, 환경 제어

    3. 피드백 루프

    • AI의 실수를 기록하고
    • 다시 같은 실수를 하지 않도록 구조 개선

    쉽게 말하면?

    AI를 그냥 쓰는 것이 아니라
    “AI를 통제하는 시스템을 만든다”
    이게 핵심입니다.
    notion image

    실제 적용: AI 파이프라인 구축

    저는 모노레포 기반 프로젝트에서 다음 구조로 적용했습니다.
    • packages 기반 구조
    • ai-pipeline 생성
    • git 변경사항 기반 자동 검수

    사용한 AI 역할 분리

    1. Claude → 코드 생성 + 1차 검수
    1. Codex → 더 엄격한 2차 검수

    문제 발생: Codex UTF-8 버그

    여기서 가장 크게 막힌 부분이 하나 있습니다.
    Codex가 한글을 깨뜨리는 문제
    (확실한 원인: 인코딩 처리 문제 / UTF-8 미명시)
    C:\Users\ducks\.codex\skills\utf8-korean-guard 경로에 --- name: utf8-korean-guard description: Enforce strict UTF-8 handling and preserve Korean text without corruption. Prevent mojibake in all read/write/edit operations. --- # UTF-8 Korean Guard Use this skill when working with Korean text, UTF-8 encoding, file I/O, or when any encoding-related risk exists. ## Hard Constraints (MUST) - All text files MUST be treated as UTF-8. - All file read/write operations MUST explicitly specify UTF-8. - Korean text MUST be preserved exactly as-is. - Korean text MUST remain human-readable Hangul. ## Forbidden (NEVER) - NEVER save files as CP949, EUC-KR, UTF-16, ANSI. - NEVER omit encoding in file I/O. - NEVER convert Korean text into unicode escape sequences (e.g. \uXXXX). - NEVER rewrite entire files when partial patching is possible. - NEVER introduce broken characters like �. ## Editing Workflow 1. Detect whether the file contains Korean text or encoding risks. 2. Prefer patch-based edits (apply_patch) instead of full rewrites. 3. When reading files, explicitly use UTF-8. 4. When writing files, explicitly use UTF-8. 5. After editing, verify Korean text integrity. ## Node.js Rules - Always use explicit encoding: ```js fs.readFileSync(path, 'utf8') fs.writeFileSync(path, data, 'utf8') ``` - Do not use default encoding behavior. ## PowerShell UTF-8 I/O - Read: - Get-Content -Encoding utf8 <path> - Get-Content -Raw -Encoding utf8 <path> - Write: - Set-Content -Encoding utf8 <path> <value> - Out-File -Encoding utf8 <path> ## Output Rules - Korean text MUST be output as readable Hangul. - Do not escape Korean characters. - Do not alter meaning or structure of Korean sentences. ## Verification Checklist - No replacement glyph (�) exists. - No mojibake (깨진 한글) exists. - Korean text remains unchanged in meaning. - Encoding is confirmed as UTF-8. If any corruption is detected: - STOP immediately. - Re-open the file explicitly as UTF-8. - Re-apply changes safely.

    해결 방법: Skill 기반 제약

    Codex에 강제로 규칙을 학습시키는 방식으로 해결했습니다.

    핵심 전략

    • 글로벌 Skill 생성
    • 실행 전에 항상 읽도록 설정
    utf8-korean-guard → UTF-8 강제 + 한글 보호

    핵심 포인트

    • 모든 파일 UTF-8 강제
    • 한글 깨짐 금지
    • 부분 수정만 허용 (전체 rewrite 금지)
    이 방식으로 실제 문제를 해결했습니다.
    import { spawnSync } from 'child_process'; // 외부 프로세스(터미널 명령어)를 동기적으로 실행하기 위한 모듈 /** * Claude AI가 응답할 데이터의 규격(타입) 정의 */ export type AgentResponse = { pass: boolean; // 코드 검수 통과 여부 (true/false) feedback: string; // 통과하지 못했을 때의 이유나 개선 사항 }; /** * 대화의 한 단위를 정의 (나와 상대방의 대화 기록 저장용) */ type ConversationTurn = { role: 'claude' | 'codex'; response: AgentResponse; }; /** * Claude가 출력한 문자열(텍스트)에서 JSON 부분만 추출하여 객체로 변환하는 함수 */ function parseAgentResponse(raw: string): AgentResponse { // 정규표현식을 사용해 문자열 내에서 { ... } 형태의 JSON 문법을 찾음 const jsonMatch = raw.match(/\{[\s\S]*\}/); if (!jsonMatch) { throw new Error('JSON을 찾을 수 없습니다.'); } // 찾은 내용을 JSON 객체로 파싱 const parsed: unknown = JSON.parse(jsonMatch[0]); if (!parsed || typeof parsed !== 'object') { throw new Error('응답 JSON 형식이 올바르지 않습니다.'); } const candidate = parsed as Record<string, unknown>; // 최종적으로 AgentResponse 타입에 맞춰 데이터 가공 및 반환 return { pass: candidate.pass === true, // 명확히 true일 때만 true로 인정 feedback: typeof candidate.feedback === 'string' ? candidate.feedback : '피드백 없음', }; } /** * 검수할 코드와 이전 대화 기록을 합쳐서 AI에게 보낼 최종 질문(Prompt)을 만드는 함수 */ function buildPrompt(code: string, history: ConversationTurn[]): string { // 1. 기본 검수 가이드라인 설정 const baseInstruction = ` 다음 TypeScript/TSX 코드를 검수하세요. 검수 기준: - any 사용 금지 - as any 사용 금지 - undefined / null 안전성 확인 - 타입 누락 여부 확인 - strict TypeScript 기준으로 문제 여부 판단 검수할 코드: \`\`\` ${code} \`\`\` `.trim(); // 2. 이전 대화가 있다면 문자열로 변환하여 추가 (문맥 유지 목적) const historySection = history.length > 0 ? '\n\n---\n이전 대화 내용:\n' + history .map( turn => `[${turn.role === 'claude' ? 'Claude(나)' : 'Codex(상대방)'}]: pass=${turn.response.pass}, feedback="${turn.response.feedback}"`, ) .join('\n') : ''; // 3. AI가 반드시 지켜야 할 응답 형식(JSON) 지정 const responseInstruction = ` ${historySection} 반드시 아래 JSON 형식으로만 응답하세요: {"pass": true, "feedback": "문제 없음"} 또는 {"pass": false, "feedback": "문제 설명"}`; return baseInstruction + responseInstruction; } /** * 시스템 환경에 따라 Claude 실행 명령어(Binary) 경로를 결정하는 함수 */ function resolveClaudeCommand(): string { // 1. 환경 변수(CLAUDE_BIN)가 설정되어 있으면 그것을 사용 // 2. 없다면 윈도우는 'claude.cmd', 그 외(Mac/Linux)는 'claude' 명령어를 기본값으로 사용 return process.env.CLAUDE_BIN?.trim() ?? (process.platform === 'win32' ? 'claude.cmd' : 'claude'); } /** * 실제로 Claude를 실행하고 결과를 받아오는 핵심 함수 */ export function callClaude(code: string, history: ConversationTurn[]): AgentResponse { const prompt = buildPrompt(code, history); // 보낼 질문 생성 const claudeCommand = resolveClaudeCommand(); // 실행할 명령어 확인 try { // 터미널에서 'claude -p --output-format json' 명령어를 실행하는 것과 같음 const result = spawnSync(claudeCommand, ['-p', '--output-format', 'json'], { input: prompt, // AI에게 보낼 질문을 표준 입력(stdin)으로 전달 encoding: 'utf-8', // 글자 인코딩 설정 timeout: 180000, // 3분(180초) 안에 응답 없으면 강제 종료 shell: process.platform === 'win32', // 윈도우 환경 대응 }); // 실행 과정 자체에서 오류가 발생한 경우 (명령어 못 찾음 등) if (result.error) { throw result.error; } // Claude 프로세스가 비정상 종료된 경우 (Exit Code 0이 아님) if (result.status !== 0) { const stderr = result.stderr?.toString().trim(); const stdout = result.stdout?.toString().trim(); throw new Error(stderr || stdout || `Claude 종료 코드 ${result.status}`); } // 실행 결과를 텍스트로 가져옴 const output = result.stdout?.toString() ?? ''; if (!output) { throw new Error('Claude 응답이 비어 있습니다.'); } // --output-format json 옵션 사용 시, 응답이 { "result": "내용" } 형태로 올 수 있으므로 처리 const envelope = JSON.parse(output) as { result?: string }; const resultText = envelope.result ?? output; // 텍스트에서 최종적으로 pass/feedback 데이터를 파싱하여 반환 return parseAgentResponse(resultText); } catch (error) { // 모든 에러 발생 시 '실패' 상태와 에러 메시지를 반환하여 프로그램 중단을 방지 return { pass: false, feedback: `Claude 호출 실패: ${error instanceof Error ? error.message : '알 수 없는 오류'}`, }; } }

    Claude 호출 구조 핵심

    코드 구조를 간단히 요약하면 다음 흐름입니다.

    1. buildPrompt

    • 검수 기준 + 코드 + 히스토리 합침

    2. spawnSync

    • 로컬 Claude CLI 실행

    3. parseAgentResponse

    • JSON만 추출해서 결과 처리

    4. Error Handling

    • 실패 시 안전하게 fallback
    import fs from 'fs'; import os from 'os'; import path from 'path'; import { execSync, spawnSync } from 'child_process'; import type { AgentResponse } from './claudeAgent.js'; // 대화 내역 저장 타입 (이전과 동일) type ConversationTurn = { role: 'claude' | 'codex'; response: AgentResponse; }; /** * AI의 답변 텍스트에서 JSON만 뽑아내는 함수 (이전과 동일) */ function parseAgentResponse(raw: string): AgentResponse { const jsonMatch = raw.match(/\{[\s\S]*\}/); if (!jsonMatch) { throw new Error('JSON을 찾을 수 없습니다.'); } const parsed: unknown = JSON.parse(jsonMatch[0]); if (!parsed || typeof parsed !== 'object') { throw new Error('응답 JSON 형식이 올바르지 않습니다.'); } const candidate = parsed as Record<string, unknown>; return { pass: candidate.pass === true, feedback: typeof candidate.feedback === 'string' ? candidate.feedback : '피드백 없음', }; } /** * AI에게 전달할 질문지 구성 (이전과 거의 동일하나 역할 이름이 Codex 위주로 변경) */ function buildPrompt(code: string, history: ConversationTurn[]): string { const baseInstruction = ` 다음 TypeScript/TSX 코드를 검수하세요. 검수 기준: any 금지, null 안전성 등... \`\`\` ${code} \`\`\` `.trim(); const historySection = history.length > 0 ? '\n\n---\n이전 대화 내용:\n' + history .map( turn => // 역할에 따라 누가 말했는지 표시 (Codex 기준) `[${turn.role === 'codex' ? 'Codex(나)' : 'Claude(상대방)'}]: pass=${turn.response.pass}, feedback="${turn.response.feedback}"`, ) .join('\n') : ''; const responseInstruction = ` ${historySection} 반드시 JSON 형식 {"pass": boolean, "feedback": string}으로 응답하세요.`; return baseInstruction + responseInstruction; } /** * 현재 프로젝트(Git 저장소)의 최상위 루트 경로를 알아내는 함수 */ function getRepoRoot(): string { return execSync('git rev-parse --show-toplevel', { encoding: 'utf-8', }).trim(); } /** * Codex 실행 파일의 경로를 찾는 함수 (윈도우/맥/리눅스 대응) */ function resolveCodexCommand(): string { const configured = process.env.CODEX_BIN?.trim(); if (configured) return configured; if (process.platform === 'win32') { const localAppData = process.env.LOCALAPPDATA; if (localAppData) { // pnpm으로 설치된 codex 명령어 경로 확인 const pnpmCodexCmd = path.join(localAppData, 'pnpm', 'codex.CMD'); if (fs.existsSync(pnpmCodexCmd)) return pnpmCodexCmd; } return 'codex.CMD'; } return 'codex'; } /** * Codex를 실행하여 코드 검수를 수행하는 메인 함수 */ export function callCodex(code: string, history: ConversationTurn[]): AgentResponse { const prompt = buildPrompt(code, history); // 1. 보안과 충돌 방지를 위해 임시 폴더 생성 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ai-pipeline-codex-')); const outputPath = path.join(tempDir, 'last-message.txt'); // 결과가 저장될 파일 const schemaPath = path.join(tempDir, 'response.schema.json'); // 응답 규격 파일 try { const repoRoot = getRepoRoot(); const codexCommand = resolveCodexCommand(); // 2. AI가 지켜야 할 JSON 규격(Schema) 파일을 임시로 작성 // AI에게 "너는 반드시 pass(불리언)와 feedback(문자열)을 포함해야 해"라고 강제하는 용도 fs.writeFileSync( schemaPath, JSON.stringify({ type: 'object', additionalProperties: false, required: ['pass', 'feedback'], properties: { pass: { type: 'boolean' }, feedback: { type: 'string' }, }, }, null, 2), 'utf-8', ); // 3. Codex 명령어 실행 // --output-schema: 미리 정의한 규격대로 답변하게 함 // -o: 답변을 파일(outputPath)로 저장하게 함 const result = spawnSync(codexCommand, ['exec', '--color', 'never', '-C', repoRoot, '--output-schema', schemaPath, '-o', outputPath, '-'], { input: prompt, // 질문 전달 encoding: 'utf-8', timeout: 180000, shell: process.platform === 'win32', } ); // 에러 처리 if (result.error) throw result.error; if (result.status === null || result.status !== 0) { throw new Error(`Codex 종료 코드 ${result.status}`); } // 4. 저장된 파일이 있으면 읽고, 없으면 터미널 출력값에서 답변을 가져옴 const output = fs.existsSync(outputPath) ? fs.readFileSync(outputPath, 'utf-8') : (result.stdout?.toString() ?? ''); return parseAgentResponse(output); } catch (error) { return { pass: false, feedback: `Codex 호출 실패: ${error instanceof Error ? error.message : '알 수 없는 오류'}`, }; } finally { // 5. 사용이 끝난 임시 폴더와 파일들을 깨끗하게 삭제 (정리 정돈) fs.rmSync(tempDir, { recursive: true, force: true }); } }

    Codex 구조에서 달라진 점

    Codex는 훨씬 더 “강제적”입니다.

    주요 차이

    1. JSON Schema 강제
    1. 임시 파일 기반 응답 처리
    1. Git Root 기준 실행
    1. finally로 정리

    한 줄 요약

    Claude = 유연
    Codex = 엄격
    notion image
    AI끼리 터미널에서 대화하며 코딩하게 만드는 법 (Tmux 활용)
    AI 에이전트, API 연동 없이 터미널끼리 대화하게 만들 수 있을까요? 매번 AI가 짜준 코드를 복사해서 다른 터미널에 붙여넣는 'Ctrl+C, Ctrl+V' 지옥에서 벗어나고 싶었습니다. 이번 영상은 와탭랩스의 노성현 개발자가 Tmux의 'send-keys' 기능을 활용해 Dev 에이전트가 QA 에이전트에게 직접 작업물을 넘겨주는 가장 기초적인 멀티 에이전트 통신 방법을 시연한 영상입니다. 시연은 요즘IT 클코나잇 - 딥다이브 세미나에서 노성현 개발자가 진행한 "AI 오케스트레이션"의 일부로 공유되었습니다. 📌발표 영상 보기 https://youtu.be/dh6tkvH6psQ 노성현 개발자는 세미나에서 AI 오케스트레이션을 통해 99% 개발 자동화를 달성하기까지 겪은 자신의 경험을 네 단계(stage)로 나누어 들려줬습니다. 이번 영상은 그중 stage1에 해당하는 시연입니다. ➡️'AI 에이전트 오케스트레이션 4 단계 실전 가이드' 전문 보러가기(12월 15일 공개) https://yozm.wishket.com/magazine/detail/3503/ 지금 요즘IT 가입하고 '요즘 세미나' 계정을 '최신 알림 설정'하면 발행 즉시 📧이메일로 발송됩니다. 🍕 핵심 포인트 - Tmux 세션을 활용한 백그라운드 에이전트 관리 - send-keys로 키보드 입력 자동화하기 - Dev Agent → QA Agent 업무 이관 시연
    AI끼리 터미널에서 대화하며 코딩하게 만드는 법 (Tmux 활용)
    https://www.youtube.com/watch?v=zVJK1HDIbRU&time_continue=1&source_ve_path=NzY3NTg&embeds_referring_euri=https%3A%2F%2Fyozm.wishket.com%2F
    AI끼리 터미널에서 대화하며 코딩하게 만드는 법 (Tmux 활용)

    실제 결과 (핵심 포인트)

    검수 결과를 보면 재미있는 패턴이 있습니다.
    • Claude → 대부분 통과
    • Codex → 엄격하게 실패 판정
    즉,
    AI 두 개를 붙이면 검수 품질이 올라간다

    중요한 인사이트

    여기서 핵심은 단순합니다.
    AI를 하나 쓰는 게 아니라
    AI끼리 검수하게 만든다
    # 오후 02:44 실행 결과 - 통과: 8개 - 실패: 0개 - 사용자 판정 필요: 4개 ## `packages/3d-editor/src/components/three/SceneInstance.tsx` **최종 판정:** 사용자 판정 필요 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음. any/as any 사용 없음. null/undefined 안전성은 optional chaining(?.), nullish coalescing(??), 조건 검사(if (!groupRef.current), if (!obj), if (!nodes) 등)로 적절히 처리됨. 타입 누락 없음. (old.material as THREE.Material) 캐스트는 THREE.js의 Material | Material[] 유니온 타입을 좁히는 정당한 캐스트로 as any 해당 없음. outline.raycast = () => {} 는 TypeScript에서 함수 파라미터 축약 할당이 허용되어 문제 없음. | | Codex | ❌ | 명시적 `any`/`as any`는 없지만 strict TypeScript 기준에서는 문제 있습니다. `child.getObjectByName(SENSOR_OUTLINE) as THREE.LineSegments | undefined`는 실제 반환 타입인 `Object3D | undefined`를 단정하고 있고, `(old.material as THREE.Material).dispose()`는 `THREE.Material | THREE.Material[]` 유니온을 강제로 덮어 배열 케이스를 누락합니다. 이 부분은 `instanceof THREE.LineSegments`로 좁히고, `Array.isArray(old.material)` 분기 후 각각 `dispose()`해야 타입/런타임 안전성이 맞습니다. | ## `packages/ai-pipeline/src/claude/inspection.ts` **최종 판정:** 사용자 판정 필요 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ❌ | strict TypeScript 기준 통과 불가: `let response;`는 응답 타입이 빠진 암묵적 any라서 any 금지 기준에 걸립니다. `JSON.parse(content.text) as Partial<InspectionResult>`는 JSON.parse의 any 반환값과 타입 단언으로 타입 검증을 우회하며, 파싱 결과가 `null`이거나 객체가 아니어도 `parsed.pass`/`parsed.feedback`에 접근하게 되어 null 안전성도 충분하지 않습니다. 응답 타입을 명시하고, parse 결과는 `unknown`으로 받은 뒤 타입 가드로 검사해야 합니다. | ## `packages/ai-pipeline/src/dialogue/claudeAgent.ts` **최종 판정:** 사용자 판정 필요 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | any 및 as any 사용 없음. unknown → Record<string, unknown> 단계적 타입 좁힘 적절. optional chaining으로 null/undefined 안전하게 처리. 단, 두 가지 사소한 주의 사항: (1) SpawnSyncReturns.status는 number | null이므로 result.status !== 0 조건이 null일 때도 true가 되어 에러 메시지가 'Claude 종료 코드 null'로 출력될 수 있음 — result.status != null && result.status !== 0 으로 보완 권장. (2) JSON.parse(output) as { result?: string }은 런타임 검증 없는 단순 단언이므로 output이 비객체 JSON(예: 숫자, 배열)일 경우 envelope.result 접근이 undefined를 반환하는 수준에서 안전하게 처리되긴 하나, 엄밀한 방어 코딩이 필요하다면 unknown으로 파싱 후 타입 가드를 추가하는 것이 더 안전함. 전반적으로 strict TypeScript 기준에 부합. | | Codex | ❌ | `callClaude`의 `const envelope = JSON.parse(output) as { result?: string };`는 파싱 결과를 검증하지 않은 채 단언하고 있습니다. `output`이 `null`이거나 객체가 아니거나, `result`가 문자열이 아닌 경우에도 타입상 통과되어 `envelope.result` 접근 또는 `parseAgentResponse(resultText)` 호출에서 런타임 오류가 날 수 있습니다. strict TypeScript 기준에서 null/undefined 안전성과 타입 검증이 부족하므로, `JSON.parse` 결과를 `unknown`으로 받은 뒤 객체 여부와 `result`의 문자열 여부를 좁혀야 합니다. | ## `packages/ai-pipeline/src/dialogue/codexAgent.ts` **최종 판정:** 통과 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ✅ | strict TypeScript 기준에서 any/as any 사용은 없고, null/undefined 처리와 타입 선언도 큰 문제 없습니다. | ## `packages/ai-pipeline/src/dialogue/consensus.ts` **최종 판정:** 통과 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ✅ | 문제 없음 | ## `packages/ai-pipeline/src/dialogue/orchestrator.ts` **최종 판정:** 통과 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ✅ | 제시된 코드 범위에서는 any/as any 사용이 없고, `lastRound`에 대한 undefined 체크도 있어 strict TypeScript 기준으로 눈에 띄는 타입 안전성 문제는 없습니다. | ## `packages/ai-pipeline/src/files/readCodeFile.ts` **최종 판정:** 통과 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ✅ | 문제 없음 | ## `packages/ai-pipeline/src/generate.ts` **최종 판정:** 통과 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ✅ | 문제 없음 | ## `packages/ai-pipeline/src/git/getChangedFiles.ts` **최종 판정:** 통과 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ✅ | 문제 없음 | ## `packages/ai-pipeline/src/index.ts` **최종 판정:** 통과 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ✅ | 문제 없음 | ## `packages/ai-pipeline/src/report/writeReport.ts` **최종 판정:** 사용자 판정 필요 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ❌ | `any`/`as any`는 없고 `round` 인덱싱도 가드되어 null/undefined 안전성은 대체로 괜찮습니다. 다만 strict TypeScript 기준에서는 `import fs from 'fs'`, `import path from 'path'`가 `esModuleInterop` 또는 `allowSyntheticDefaultImports` 설정에 의존하므로, 해당 설정이 없으면 `Module has no default export` 타입 오류가 발생할 수 있습니다. | ## `packages/ai-pipeline/src/runInspection.ts` **최종 판정:** 통과 #### Round 1 | | pass | feedback | |---|---|---| | Claude | ✅ | 문제 없음 | | Codex | ✅ | 문제 없음. 다만 strict TypeScript 기준에서는 getChangedFiles가 string[], readCodeFile이 string, inspect가 Promise<InspectionResult>를 반환하도록 정확히 타입 선언되어 있어야 합니다. |
    위와 같은 방식으로 검수를 진행하면, 통과하지 못한 부분은 결국 마지막에 제가 직접 확인하게 됩니다. 이 구조를 보면서 감을 잡으신 분들도 분명 있을 거라고 생각합니다.
    결국 핵심은 여기서부터입니다. 어떤 기능을 추가하고, 어떻게 확장하느냐에 따라 결과물의 수준은 완전히 달라지게 됩니다. 단순히 검수에서 끝나는 것이 아니라, 설계·개발·피드백까지 이어지는 구조로 확장할 수 있기 때문입니다.
    실제로 Meta에서 공개한 오픈소스 사례를 살펴보면, 단순한 코드 생성이나 검수를 넘어 설계부터 개발, 검수까지 전체 흐름을 하나의 오케스트라처럼 지휘하는 구조를 사용하고 있습니다.
    이러한 구조에서는 각 역할을 맡은 AI들이 유기적으로 협력하며 하나의 시스템처럼 움직이게 됩니다. 그리고 그 결과를 기반으로 성과를 측정하고, 실제 조직 운영에도 영향을 준다는 점이 인상적이었습니다.
    물론 “성과 기반으로 인력 구조까지 변화한다”는 부분은 다소 극단적으로 느껴질 수도 있습니다. (이 부분은 확실하지 않음) 하지만 한 가지는 분명하게 느낄 수 있었습니다.
    하네스 엔지니어링을 적용하면 개발 속도뿐 아니라, 품질과 관리 측면에서도 확실한 이점을 가져갈 수밖에 없는 구조가 만들어진다는 점입니다.

    그럼 이런 부분이 생긴다 보안? 보안 어쩔건데?

    공급망 공격 대응 (Axios 사례 기준)

    최근 이슈를 보면 공통점이 있습니다.
    npm install 단계에서 공격 발생

    대응 전략

    • 자동 업데이트는 허용
    • 자동 배포는 금지

    권장 흐름

    1. 업데이트 PR 생성
    1. 보안 스캔
    1. 테스트
    1. 승인
    1. 배포
    특히 주의해야 할 부분
    • postinstall 스크립트
    • 의존성 내부 실행 코드
    --- name: no-env-access description: Prevent any access to .env and environment secret files. Protect sensitive credentials from exposure. --- # No .env Access Use this skill whenever there is any possibility of interacting with environment or secret files. ## Hard Constraints (MUST) - NEVER read any `.env` file. - NEVER print or expose contents of `.env`. - NEVER edit or overwrite `.env` files. - NEVER create or duplicate `.env` files. - NEVER log environment variables. - NEVER access process.env or similar runtime secrets for output. ## Scope This applies to all sensitive config patterns: - `.env` - `.env.*` (e.g. `.env.local`, `.env.production`) - `.envrc` - any file containing API keys, tokens, secrets - runtime environment variables (`process.env`, `ENV`, `os.environ`) ## Required Behavior When a task involves `.env`: 1. Refuse access immediately 2. Do NOT attempt partial exposure (even masked) 3. Suggest using placeholders or mock values instead 4. Continue only if secrets are not required ## Forbidden Actions - Printing API keys, DB passwords, tokens - Logging environment variables - Using `.env` values in output - Inferring secrets from code context - Reconstructing hidden values ## Allowed Alternatives - Use placeholders: `API_KEY=***` - Use mock/test credentials - Ask user to manually input secrets outside the system ## Response Rule If user requests `.env` access: "보안 정책상 .env 파일 및 환경 변수 값은 접근하거나 노출할 수 없습니다. 필요한 경우 더미 값이나 구조 기준으로 안내드릴 수 있습니다." ## Escalation Rule If the task cannot be completed without secrets: - Stop immediately - Do not attempt workaround

    총정리

    하네스 엔지니어링은 단순히 AI를 잘 쓰는 방법이 아니라, AI를 안전하게 통제하고 시스템 안에서 동작하도록 만드는 구조 설계에 가깝습니다. 실제로 적용해보니 AI를 하나만 사용하는 것보다 역할을 나누고 서로 검수하게 만드는 것이 훨씬 안정적이었고, 특히 Codex처럼 엄격한 모델을 검수에 활용하면 품질이 확연히 올라가는 것을 확인할 수 있었습니다. 또한 Skill 기반 제약을 통해 보안 문제까지 함께 해결할 수 있다는 점에서, 앞으로 AI 개발의 기본 구조는 단순 프롬프트가 아니라 이러한 하네스 구조로 넘어갈 가능성이 높다고 느꼈습니다.
    Science Adam
    Unrivaled Science x AI Channel This channel is hosted by the creator of the iTunes podcast, "The Man Who Reads Science Books." Over the past 10 years, I have narrated over 2,000 science books and donated 3,000 copies to orphanages. Based on this deep background in knowledge curation, I provide high-quality insights into Science and AI. [What is S.A.D.A.M?] • Science • AI • Details • And • Minds (Insights from Great Minds) 🚀 Global Business & Collaboration We welcome partnerships and sponsorship inquiries from AI companies & Tech startups worldwide. ⓒ Science Bookcase Company 📧 Contact: ScienceAdam2025@gmail.com
    Science Adam
    https://www.youtube.com/@ScienceADAM
    Science Adam
    마지막으로 IT, 유명 대학의 강의를 한글화 해놓은 유튜브를 공유드리며 글을 마무리한다.
    GitHub - VoltAgent/awesome-design-md: Collection of DESIGN.md files that capture design systems from popular websites. Drop one into your project and let coding agents build matching UI.
    Collection of DESIGN.md files that capture design systems from popular websites. Drop one into your project and let coding agents build matching UI. - VoltAgent/awesome-design-md
    GitHub - VoltAgent/awesome-design-md: Collection of DESIGN.md files that capture design systems from popular websites. Drop one into your project and let coding agents build matching UI.
    https://github.com/VoltAgent/awesome-design-md
    GitHub - VoltAgent/awesome-design-md: Collection of DESIGN.md files that capture design systems from popular websites. Drop one into your project and let coding agents build matching UI.
    다음글은 디자인 Ai 자동화 글을 다루려한다. 미친 기술이다… Paper의 대단한… 기술
     
    Share article

    찬찬잉

    RSS·Powered by Inblog