AI ์ฝ๋, ์ ์๊พธ ํ๋ก๋์ ์์ ํฐ์ง๊น? ๋๋ฒ๊น ์์ ์ ๋ณต
AI ์ฝ๋, ์ ์๊พธ ํ๋ก๋์ ์์ ํฐ์ง๊น? ๋๋ฒ๊น ์์ ์ ๋ณต
๋ค๋ค ํ ๋ฒ์ฏค ๊ฒช์ด๋ณด์ จ์ฃ ? AI๊ฐ ์๋ฒฝํด ๋ณด์ด๋ ์ฝ๋๋ฅผ ๋ฝ์๋ ๋๋ค. ๋ฌธ๋ฒ๋ ๊น๋ํ๊ณ , ๊ตฌ์กฐ๋ ๋ ผ๋ฆฌ์ ์ด๊ณ , ์ฌ์ง์ด ์ฃผ์๊น์ง ๋ฌ์์ค์. ๋ณต๋ถํด์ ๋ก์ปฌ์์ ํ ์คํธํ๋ฉด ์ ๋์๊ฐ๋๋ค. ๊ทผ๋ฐ ํ๋ก๋์ ์ ์ฌ๋ฆฌ๊ณ ๋๋ฉด? ๋ช ์๊ฐ ๋ง์ ๋ชจ๋ํฐ๋ง ๋์๋ณด๋๊ฐ ๋นจ๊ฐ๋ถ ์ฒ์ง๊ฐ ๋์ฃ .
์ฌ๋ฌ๋ถ๋ง ๊ทธ๋ฐ ๊ฑฐ ์๋๋๋ค. ์ต๊ทผ ์กฐ์ฌ์ ๋ฐ๋ฅด๋ฉด ๊ฐ๋ฐ์ 84%๊ฐ AI ์ฝ๋ฉ ๋๊ตฌ๋ฅผ ์ฐ๊ณ ์๋๋ฐ, ๊ทธ ์ค 46%๋ AI๊ฐ ๋ฝ์๋ธ ์ฝ๋๋ฅผ ์จ์ ํ ์ ๋ขฐํ์ง ๋ชปํ๋ค๊ณ ํด์. ๊ฐ์ฅ ๋ง์ ๋ถ๋ง์ด ๋ญ์ง ์์ธ์? "๊ฑฐ์ ๋ง๋๋ฐ... ๋ญ๊ฐ ์ข ์๋์ผ"โ์ด ์ ๋งคํ ์ํ๊ฐ ์คํ๋ ค ์ฒ์๋ถํฐ ์ง๋ ๊ฒ๋ณด๋ค ๋๋ฒ๊น ์ ๋ ํ๋ค๊ฒ ๋ง๋ ๋ค๋ ๊ฑฐ์์.
AI ์ฝ๋ฉ ๋๊ตฌ๋ฅผ ๊น์๋ด๋ฆฌ๋ ค๋ ๊ฒ ์๋๋๋ค. ์ง์ง ํ์ ์ ์ธ ๋๊ตฌ ๋ง์์. ๊ทผ๋ฐ ํ ๊ฐ์ง ์ค์ํ ์ง์์ด ๋น์ด์์ด์: AI ์ฝ๋๊ฐ ํ๋ก๋์ ์์ ์ ํฐ์ง๋์ง, ๊ทธ๋ฆฌ๊ณ ํฐ์ง๊ธฐ ์ ์ ์ด๋ป๊ฒ ์ก์๋ผ ์ ์๋์ง ๋ง์ด์์. ์ด ๊ธ์์ ๊ทธ ๋นํ์ ์ฑ์๋๋ฆด๊ฒ์.
AI ์ฝ๋๋ ์ ํฐ์ง๋: ๊ทผ๋ณธ ์์ธ ํํค์น๊ธฐ
๋๋ฒ๊น ๊ธฐ๋ฒ์ผ๋ก ๋์ด๊ฐ๊ธฐ ์ ์, ๋จผ์ ์ AI ์ฝ๋๊ฐ ๊ฐ๋ฐ ํ๊ฒฝ์์ ๋ฉ์ฉกํ๋ฐ ํ๋ก๋์ ์์ ๋ง์ฝ์ธ์ง ์ดํดํด์ผ ํด์. ๋ฌด์์๋ก ๋ฒ๊ทธ๊ฐ ํฐ์ง๋ ๊ฒ ์๋๋๋คโLLM์ด ๋์ํ๋ ์๋ฆฌ์์ ๋์ค๋ ์์ธก ๊ฐ๋ฅํ ํจํด์ด ์์ด์.
1. ์ปจํ ์คํธ ์๋์ฐ์ ํ๊ณ
AI ๋ชจ๋ธ์ ์ปจํ ์คํธ ์๋์ฐ๊ฐ ์ ํด์ ธ ์์ด์. ์ฝ๋๋ฅผ ์์ฑํ ๋ ์ฌ๋ฌ๋ถ์ ์ ์ฒด ์ฝ๋๋ฒ ์ด์ค๋ฅผ ๋ค ๋ณด๋ ๊ฒ ์๋๋ผ, ์ผ๋ถ๋ง ๋ณผ ์ ์๋ค๋ ๊ฑฐ์ฃ . ๊ทธ๋์ ์ด๋ฐ ๋ฌธ์ ๊ฐ ์๊ฒจ์:
์๋ import๋ฅผ ์ด๋ค: AI๊ฐ ํ์ต ๋ฐ์ดํฐ์์ ๋ณธ ํจํด์ ๋ฐํ์ผ๋ก "์์ ๊ฑฐ์ผ"๋ผ๊ณ ์ถ์ธกํ ํจ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ๋ค ์ฐ๋๋ฐ, ์ ์ ์ฌ๋ฌ๋ถ ํ๋ก์ ํธ์ ์๋ ๊ฒฝ์ฐ๊ฐ ์์ด์.
// "๋ง์ ๋ณด์ด๋" AI ์ฝ๋ import { validateUserInput } from '@/utils/validation'; import { sanitizeHTML } from '@/lib/security'; async function processUserData(data) { const validated = validateUserInput(data); const safe = sanitizeHTML(validated.content); // ... }
๋ฌธ์ ๊ฐ ๋ญ๋๋ฉด์? ์ฌ๋ฌ๋ถ ํ๋ก์ ํธ๋ @/utils/validation์ด ์๋๋ผ @/helpers/validation์ ์ธ ์๋ ์๊ณ , sanitizeHTML ํจ์ ์์ฒด๊ฐ ์์ ์๋ ์์ด์. ์ด๋ฐ ์๋ฌ๋ ๋ฐํ์ ๋์์ผ ํฐ์ง๋๋ค.
๋ค์ด๋ฐ ์ปจ๋ฒค์ ์ด ๋ค์ฃฝ๋ฐ์ฃฝ: AI๊ฐ ํ์ตํ ์ฌ๋ฌ ์ฝ๋๋ฒ ์ด์ค์ ์คํ์ผ์ ์์ด ์ฐ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์์:
# ์ปจ๋ฒค์ ์ด ์์ธ AI ์ฝ๋ def getUserData(user_id): # camelCase ํจ์๋ช user_info = fetch_user_info(user_id) # snake_case ํธ์ถ return user_info.getData() # ๋ camelCase # ์ฌ๋ฌ๋ถ ์ฝ๋๋ฒ ์ด์ค๋ snake_case ํต์ผ def get_user_data(user_id): user_info = fetch_user_info(user_id) return user_info.get_data()
2. ํ์ต ๋ฐ์ดํฐ๊ฐ ๊ณผ๊ฑฐ์ ๋ฉ์ถฐ์๋ค
์ด๊ฒ ์ ์ผ ๊ณจ์น ์ํ ๋ฌธ์ ์์. AI๋ ํน์ ์์ ์ ์ฝ๋๋ก ํ์ต๋๋๋ฐ, API๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ฒ ์คํธ ํ๋ํฐ์ค๋ ๊ณ์ ๋ฐ๋์์์.
deprecated API๋ฅผ ์ด๋ค: AI๊ฐ ํ์ต ๊ธฐ์ค์ผ ์ดํ๋ก deprecated๋ API๋ฅผ ํ์ฐํ๊ฒ ์จ๋ฒ๋ฆฌ๋ ๊ฒฝ์ฐ๊ฐ ์์ด์:
// deprecated ํจํด ์ฐ๋ AI React ์ฝ๋ class UserProfile extends React.Component { componentWillMount() { // React 16.3๋ถํฐ deprecated this.fetchUserData(); } componentWillReceiveProps(nextProps) { // ์ด๊ฒ๋ deprecated if (nextProps.userId !== this.props.userId) { this.fetchUserData(nextProps.userId); } } } // ์์ฆ ์คํ์ผ function UserProfile({ userId }) { useEffect(() => { fetchUserData(userId); }, [userId]); }
์๋ ๋ณด์ ํจํด: ์ฌ๊ธฐ์ ์ง์ง ์ํํด์ ธ์. ๋ณด์ ๋ฒ ์คํธ ํ๋ํฐ์ค๋ ๋น ๋ฅด๊ฒ ๋ฐ๋๋๋ฐ, AI๊ฐ ์ด์ ๋ ๋ซ๋ฆฌ๋ ๊ฑธ๋ก ์๋ ค์ง ํจํด์ ์ธ ์ ์๊ฑฐ๋ ์:
# ๋ณด์ ์ทจ์ฝํ AI ์ฝ๋ import hashlib def hash_password(password): return hashlib.md5(password.encode()).hexdigest() # ์ ๋ ์ฐ๋ฉด ์ ๋จ # ์ ๋๋ก ๋ ๋ฐฉ๋ฒ import bcrypt def hash_password(password): return bcrypt.hashpw(password.encode(), bcrypt.gensalt())
3. ํดํผ ํจ์ค๋ง ์๋ค
AI๋ ์ฃผ๋ก ์์ ์ฝ๋๋ ํํ ๋ฆฌ์ผ๋ก ํ์ตํ๋๋ฐ, ์ด๋ฐ ๊ฑด ๊ฑฐ์ ๋ค "์ ๋๋ ๊ฒฝ์ฐ"๋ง ๋ณด์ฌ์ค์. ๊ทผ๋ฐ ํ๋ก๋์ ์ ์ ๋๋ ๊ฒฝ์ฐ๋ฅผ ๋ค ์ปค๋ฒํด์ผ ํ์์์: ๋คํธ์ํฌ ๋๊น, ์ด์ํ ๋ฐ์ดํฐ, ๋์ ์์ฒญ, ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ, ๋ณ๋ณ ์ฃ์ง์ผ์ด์ค๋ค.
์๋ฌ ํธ๋ค๋ง์ด ์๋ค:
// AI ์ฝ๋: ํดํผ ํจ์ค์์ ์๋ฒฝ async function fetchAndProcessData(url: string) { const response = await fetch(url); const data = await response.json(); return data.items.map(item => item.name.toUpperCase()); } // ํ๋ก๋์ ํ์ค: ๋ค ํฐ์ง ์ ์์ async function fetchAndProcessData(url: string) { let response; try { response = await fetch(url, { timeout: 5000, signal: AbortSignal.timeout(5000) }); } catch (error) { if (error.name === 'TimeoutError') { throw new DataFetchError('ํ์์์', { url, cause: error }); } throw new DataFetchError('๋คํธ์ํฌ ์๋ฌ', { url, cause: error }); } if (!response.ok) { throw new DataFetchError(`HTTP ${response.status}`, { url, status: response.status }); } let data; try { data = await response.json(); } catch (error) { throw new DataFetchError('JSON ํ์ฑ ์คํจ', { url, cause: error }); } if (!data?.items || !Array.isArray(data.items)) { throw new DataFetchError('์๋ต ๊ตฌ์กฐ๊ฐ ์ด์ํจ', { url, data }); } return data.items .filter(item => item?.name != null) .map(item => String(item.name).toUpperCase()); }
null ์ฒดํฌ๋ฅผ ์ ํ๋ค:
// AI ์ฝ๋: ๋ฐ์ดํฐ๊ฐ ํญ์ ์์ ํ๋ค๊ณ ๊ฐ์ function getUserDisplayName(user) { return `${user.firstName} ${user.lastName}`; } // ํ๋ก๋์ : ๋ถ์์ ํ ๋ฐ์ดํฐ๋ ์ฒ๋ฆฌ function getUserDisplayName(user) { if (!user) return 'Unknown User'; const parts = [user.firstName, user.lastName].filter(Boolean); return parts.length > 0 ? parts.join(' ') : user.email || 'Unknown User'; }
4. ๋์์ฑ? ๊ทธ๊ฒ ๋ญ๋ฐ?
AI๊ฐ ๋ฐฐ์ฐ๋ ์ฝ๋ ๋๋ถ๋ถ์ด ์ฑ๊ธ ์ค๋ ๋ ๋๊ธฐ ์ฝ๋์์. ๊ทธ๋์ AI ์ฝ๋์๋ ํ๋ก๋์ ํธ๋ํฝ ๋ฐ์ผ๋ฉด ํฐ์ง๋ ๋ ์ด์ค ์ปจ๋์ ์ด ์จ์ด์๋ ๊ฒฝ์ฐ๊ฐ ๋ง์์.
# AI ์ฝ๋: ๊ด์ฐฎ์ ๋ณด์ด์ง๋ง ๋ ์ด์ค ์ปจ๋์ ์์ class Counter: def __init__(self): self.count = 0 def increment(self): self.count += 1 # ์์์ ์ฐ์ฐ ์๋! return self.count # ๋์์ ์ ๊ทผํ๋ฉด ํฐ์ง # ์ค๋ ๋ ๋ ๊ฐ๊ฐ ๋์์ count=5 ์ฝ๊ณ , ๋ ๋ค 6์ ์ # ์ ๋๋ก ๋ ๋ฒ์ import threading class Counter: def __init__(self): self.count = 0 self._lock = threading.Lock() def increment(self): with self._lock: self.count += 1 return self.count
JavaScript async ๋ ์ด์ค ์ปจ๋์ :
// AI ์ฝ๋: ๋ฏธ๋ฌํ ๋ ์ด์ค ์ปจ๋์ let cachedUser = null; async function getUser(id) { if (!cachedUser || cachedUser.id !== id) { cachedUser = await fetchUser(id); } return cachedUser; } // ๋ค๋ฅธ id๋ก ์ฐ๋ฌ์ ํธ์ถํ๋ฉด: // ํธ์ถ 1: id=1, fetch ์์ // ํธ์ถ 2: id=2, fetch ์์ (cachedUser ์์ง null) // ํธ์ถ 2๊ฐ ๋จผ์ ๋๋จ, cachedUser = user2 // ํธ์ถ 1์ด ๋๋จ, user1์ผ๋ก ๋ฎ์ด์ // ํธ์ถ 2 ํ ์ชฝ์ด user1์ ๋ฐ์๋ฒ๋ฆผ! // ๊ณ ์น ๋ฒ์ const pendingRequests = new Map(); async function getUser(id) { if (cachedUser?.id === id) { return cachedUser; } if (pendingRequests.has(id)) { return pendingRequests.get(id); } const promise = fetchUser(id).then(user => { cachedUser = user; pendingRequests.delete(id); return user; }); pendingRequests.set(id, promise); return promise; }
AI ์ฝ๋ ๋๋ฒ๊น , ์ด๋ ๊ฒ ํ์ธ์
์ด์ ์ ํฐ์ง๋์ง ์์์ผ๋, ์ด๋ป๊ฒ ์ก์์ง ์์๋ณผ๊ฒ์.
์ ๋ต 1: ๋จธ์ง ์ ์ฒดํฌ๋ฆฌ์คํธ
AI ์ฝ๋๊ฐ ๋ฉ์ธ ๋ธ๋์น์ ๋ค์ด๊ฐ๊ธฐ ์ ์ ์ด๊ฒ๋ค ํ์ธํ์ธ์:
import ๊ฒ์ฆ:
# JS/TS ํ๋ก์ ํธ npx tsc --noEmit 2>&1 | grep "Cannot find module" # Python ํ๋ก์ ํธ python -c "import ast; ast.parse(open('file.py').read())" python -m py_compile file.py
deprecated API ์ฒดํฌ:
// package-audit.js const fs = require('fs'); const content = fs.readFileSync(process.argv[2], 'utf8'); const deprecatedPatterns = [ { pattern: /componentWillMount/g, message: 'React deprecated ๋ผ์ดํ์ฌ์ดํด' }, { pattern: /componentWillReceiveProps/g, message: 'React deprecated ๋ผ์ดํ์ฌ์ดํด' }, { pattern: /findDOMNode/g, message: 'React deprecated API' }, { pattern: /substr\(/g, message: 'deprecated, substring() ์ฐ์ธ์' }, ]; deprecatedPatterns.forEach(({ pattern, message }) => { const matches = content.match(pattern); if (matches) { console.warn(`โ ๏ธ ${message}: ${matches.length}๊ฑด`); } });
์๋ฌ ํธ๋ค๋ง ์ฒดํฌ:
# bare except ์ฐพ๊ธฐ import ast import sys class ErrorHandlingChecker(ast.NodeVisitor): def __init__(self): self.issues = [] def visit_ExceptHandler(self, node): if node.type is None: self.issues.append(f"{node.lineno}๋ฒ ์ค: bare except") elif isinstance(node.type, ast.Name) and node.type.id == 'Exception': if not any(isinstance(n, ast.Raise) for n in ast.walk(node)): self.issues.append(f"{node.lineno}๋ฒ ์ค: Exception ์ก๊ณ ๋ค์ ์ ๋์ง") self.generic_visit(node) tree = ast.parse(open(sys.argv[1]).read()) checker = ErrorHandlingChecker() checker.visit(tree) for issue in checker.issues: print(issue)
์ ๋ต 2: ํ๋ก๋์ ์ํฉ ์๋ฎฌ๋ ์ด์
AI๊ฐ ๊ฑฐ์ ์ ๋ค๋ฃจ๋ ํ๋ก๋์ ์ํฉ์ ํ ์คํธํ์ธ์:
// stress-test.js class ProductionSimulator { // ๋คํธ์ํฌ ์๋ฌ ์๋ฎฌ async withNetworkFailure(fn, failureRate = 0.3) { const original = global.fetch; global.fetch = async (...args) => { if (Math.random() < failureRate) { throw new TypeError('Failed to fetch'); } return original(...args); }; try { return await fn(); } finally { global.fetch = original; } } // ๋๋ฆฐ ์๋ต ์๋ฎฌ async withLatency(fn, minMs = 100, maxMs = 5000) { const original = global.fetch; global.fetch = async (...args) => { const delay = minMs + Math.random() * (maxMs - minMs); await new Promise(resolve => setTimeout(resolve, delay)); return original(...args); }; try { return await fn(); } finally { global.fetch = original; } } // ์ด์ํ ๋ฐ์ดํฐ ์๋ฎฌ async withMalformedData(fn) { const original = global.fetch; global.fetch = async (...args) => { const response = await original(...args); return { ...response, json: async () => { const data = await response.json(); return this.corruptData(data); } }; }; try { return await fn(); } finally { global.fetch = original; } } corruptData(data) { if (Array.isArray(data)) { return data.map((item, i) => i % 3 === 0 ? null : this.corruptData(item) ); } if (typeof data === 'object' && data !== null) { const keys = Object.keys(data); const corrupted = { ...data }; keys.forEach(key => { if (Math.random() < 0.2) delete corrupted[key]; }); return corrupted; } return data; } // ๋์ ์์ฒญ ์๋ฎฌ async withConcurrency(fn, concurrencyLevel = 100) { const promises = Array(concurrencyLevel) .fill(null) .map(() => fn()); const results = await Promise.allSettled(promises); const failures = results.filter(r => r.status === 'rejected'); if (failures.length > 0) { console.error(`${failures.length}/${concurrencyLevel}๊ฐ ์คํจ`); failures.forEach(f => console.error(f.reason)); } return results; } }
์ ๋ต 3: ๊ธฐ์กด ์ฝ๋๋ ๋น๊ต ํ ์คํธ
AI๊ฐ ๊ธฐ์กด ๊ธฐ๋ฅ์ ๋์ฒดํ๋ ์ฝ๋๋ฅผ ์งฐ๋ค๋ฉด, ๋์์ด ๋๊ฐ์์ง ๋น๊ตํ์ธ์:
# differential_test.py import json import random from typing import Any, Callable def differential_test( original_fn: Callable, ai_generated_fn: Callable, input_generator: Callable, num_tests: int = 1000 ) -> list[dict]: """AI ์ฝ๋๊ฐ ๋ค๋ฅด๊ฒ ๋์ํ๋ ์ ๋ ฅ ์ฐพ๊ธฐ""" differences = [] for i in range(num_tests): test_input = input_generator() try: original_result = original_fn(test_input) original_error = None except Exception as e: original_result = None original_error = type(e).__name__ try: ai_result = ai_generated_fn(test_input) ai_error = None except Exception as e: ai_result = None ai_error = type(e).__name__ if original_result != ai_result or original_error != ai_error: differences.append({ 'input': test_input, 'original': {'result': original_result, 'error': original_error}, 'ai_generated': {'result': ai_result, 'error': ai_error} }) return differences # ์ฌ์ฉ๋ฒ def generate_random_user_input(): """์ฃ์ง์ผ์ด์ค ํฌํจ ๋๋ค ์ ๋ ฅ""" edge_cases = [ None, {}, {'name': None}, {'name': ''}, {'name': 'a' * 10000}, {'name': '<script>alert("xss")</script>'}, {'name': '๐จโ๐ฉโ๐งโ๐ฆ'}, {'name': 'O\'Brien'}, {'id': float('nan')}, {'id': float('inf')}, ] if random.random() < 0.2: return random.choice(edge_cases) return { 'name': ''.join(random.choices('abcdefghijklmnop', k=random.randint(1, 50))), 'id': random.randint(-1000, 1000) } differences = differential_test( original_process_user, ai_generated_process_user, generate_random_user_input ) if differences: print(f"{len(differences)}๊ฐ ์ฐจ์ด ๋ฐ๊ฒฌ!") print(json.dumps(differences[:5], indent=2))
์ ๋ต 4: ๋ก๊น ๋ถํฐ ์ ๋๋ก
ํ๋ก๋์ ์์ ํฐ์ก์ ๋ ๋ก์ปฌ์์ ์ฌํํ๋ ค๊ณ ํ๋ฉด ๋๋ถ๋ถ ์คํจํด์. ๊ทธ ์ํฉ์ ์กฐ๊ฑด์ ๋๊ฐ์ด ๋ง๋ค ์๊ฐ ์๊ฑฐ๋ ์. ๊ทธ๋์ ์ฒ์๋ถํฐ ๋ก๊น ์ ์ ํด๋์ผ ํด์:
// observability.ts interface CodeExecutionContext { functionName: string; aiGenerated: boolean; inputs: Record<string, any>; startTime: number; } class ObservableWrapper { private context: CodeExecutionContext; constructor(functionName: string, aiGenerated: boolean = true) { this.context = { functionName, aiGenerated, inputs: {}, startTime: Date.now() }; } recordInput(name: string, value: any) { this.context.inputs[name] = this.sanitize(structuredClone(value)); } recordCheckpoint(name: string, data?: any) { console.log(JSON.stringify({ type: 'checkpoint', ...this.context, checkpoint: name, data: this.sanitize(data), elapsed: Date.now() - this.context.startTime })); } recordSuccess(result: any) { console.log(JSON.stringify({ type: 'success', ...this.context, result: this.sanitize(result), duration: Date.now() - this.context.startTime })); } recordError(error: Error, additionalContext?: any) { console.error(JSON.stringify({ type: 'error', ...this.context, error: { message: error.message, name: error.name, stack: error.stack }, additionalContext, duration: Date.now() - this.context.startTime })); } private sanitize(obj: any): any { if (obj === null || obj === undefined) return obj; if (typeof obj !== 'object') return obj; const sensitiveKeys = ['password', 'token', 'secret', 'apiKey', 'authorization']; const result: any = Array.isArray(obj) ? [] : {}; for (const [key, value] of Object.entries(obj)) { if (sensitiveKeys.some(k => key.toLowerCase().includes(k))) { result[key] = '[REDACTED]'; } else if (typeof value === 'object') { result[key] = this.sanitize(value); } else { result[key] = value; } } return result; } } // ์ฌ์ฉ๋ฒ async function aiGeneratedProcessOrder(order: Order) { const obs = new ObservableWrapper('processOrder', true); obs.recordInput('order', order); try { obs.recordCheckpoint('validation_start'); const validated = validateOrder(order); obs.recordCheckpoint('validation_complete', { isValid: true }); obs.recordCheckpoint('payment_start'); const payment = await processPayment(validated); obs.recordCheckpoint('payment_complete', { paymentId: payment.id }); obs.recordCheckpoint('fulfillment_start'); const result = await fulfillOrder(validated, payment); obs.recordCheckpoint('fulfillment_complete'); obs.recordSuccess(result); return result; } catch (error) { obs.recordError(error as Error, { orderState: order.status, retryable: isRetryableError(error) }); throw error; } }
์๋ฐฉ์ด ์ต์ : AI์ ๊ฐํ ๊ฐ๋ฐ ํ์ดํ๋ผ์ธ
๋๋ฒ๊น ์ ํด๋ ๋๋ ๊ฒ ์ต๊ณ ์์์. ์ ์ด์ ๋ฌธ์ ๋ฅผ ๋ง๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊ฒ์.
1. AIํํ ์ ๋๋ก ์ํค๊ธฐ
## ํ๋ก๋์ ์ฉ AI ํ๋กฌํํธ ํ ํ๋ฆฟ [ํจ์ ์ค๋ช ]์ ๋ค์ ์๊ตฌ์ฌํญ๋๋ก ์ง์ค: **์ํฉ:** - ์ด ์ฝ๋๋ [์์ ํธ๋ํฝ]์ ๋ฐ๋ ํ๋ก๋์ ์์ ๋๋ค - [๊ธฐ์กด ์์คํ ]์ด๋ ์ฐ๋ํด์ผ ํด - ์ฐ๋ฆฌ ์ฝ๋๋ฒ ์ด์ค๋ [๋ค์ด๋ฐ ์ปจ๋ฒค์ ]์ด๋ [์ฝ๋ ์คํ์ผ] ์ **ํ์:** 1. ์๋ฌ ํธ๋ค๋ง ๊ผญ ๋ฃ์ด: - ๋คํธ์ํฌ ์๋ฌ, ํ์์์ - ์๋ชป๋ ์ ๋ ฅ ๋ฐ์ดํฐ - null/undefined - ๋์ ์์ฒญ 2. ์ ๋ ฅ ๊ฒ์ฆ ๋ฃ์ด 3. ์ฃผ์ ์ง์ ์ ๋ก๊น ๋ฃ์ด 4. ์ฃ์ง์ผ์ด์ค ๋ค ์ฒ๋ฆฌํด 5. ์ด ์์กด์ฑ๋ง ์จ (์๋ ๊ฑฐ ๊ฐ์ ธ๋ค ์ฐ์ง ๋ง): [์ฌ์ฉ ๊ฐ๋ฅํ ์์กด์ฑ ๋ชฉ๋ก] **ํ์ง ๋ง:** - deprecated API ์ฐ์ง ๋ง - ์์ธ ์ผํค์ง ๋ง - ์ธ๋ถ ์๋น์ค ํญ์ ๋๋ค๊ณ ๊ฐ์ ํ์ง ๋ง - ๋ฐ์ดํฐ ํญ์ ์์ ํ๋ค๊ณ ๊ฐ์ ํ์ง ๋ง **์คํ์ผ:** - [snake_case/camelCase] ์จ - async๋ฉด ํ์์์ ์ฒ๋ฆฌ ํ์ - ํจ์ 50์ค ๋๊ธฐ์ง ๋ง
2. CI์์ ์๋ ์ฒดํฌ
# .github/workflows/ai-code-review.yml name: AI ์ฝ๋ ๋ฆฌ๋ทฐ on: pull_request: paths: - '**.js' - '**.ts' - '**.py' jobs: ai-code-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: AI ์ฝ๋ ํจํด ๊ฐ์ง run: | # async์ try/catch ์๋์ง grep -rn "async.*{$" --include="*.ts" --include="*.js" | \ xargs -I {} sh -c 'file="{}"; grep -L "try\|catch" "$file" && echo "$file์ try/catch ์์"' # Python bare except grep -rn "except:$" --include="*.py" && echo "bare except ๋ฐ๊ฒฌ" # deprecated React grep -rn "componentWillMount\|componentWillReceiveProps" --include="*.tsx" --include="*.jsx" && \ echo "deprecated React ๋ผ์ดํ์ฌ์ดํด ๋ฐ๊ฒฌ" - name: ๋ณต์ก๋ ์ฒดํฌ run: | npx complexity-report --format json src/ | \ jq '.functions[] | select(.complexity > 15) | {name, complexity}' - name: ๋ณด์ ํจํด ์ฒดํฌ run: | grep -rn "md5\|sha1" --include="*.py" --include="*.js" | grep -i password && \ echo "์ทจ์ฝํ ํด์ ํจ์ ๋ฐ๊ฒฌ"
3. AI ์ฝ๋ ๊ฒฉ๋ฆฌ ํจํด
AI ์ฝ๋๋ฅผ ๋ฏฟ์ ์ ์๋ ์ ๋ ฅ์ฒ๋ผ ์ทจ๊ธํ์ธ์. ๊ฒฉ๋ฆฌํ๊ณ , ๊ฒ์ฆํ๊ณ , ์ฒ์ฒํ ์ ๋ขฐ๋ฅผ ์์๊ฐ์ธ์:
// ai-code-quarantine.ts interface QuarantinedFunction<TInput, TOutput> { implementation: (input: TInput) => TOutput | Promise<TOutput>; validator: (input: TInput) => boolean; sanitizer: (input: TInput) => TInput; fallback: (input: TInput, error: Error) => TOutput; } function createQuarantinedFunction<TInput, TOutput>( config: QuarantinedFunction<TInput, TOutput> ) { return async function quarantined(input: TInput): Promise<TOutput> { if (!config.validator(input)) { throw new Error('์ ๋ ฅ ๊ฒ์ฆ ์คํจ'); } const sanitizedInput = config.sanitizer(input); try { const result = await Promise.race([ config.implementation(sanitizedInput), new Promise<never>((_, reject) => setTimeout(() => reject(new Error('ํ์์์')), 5000) ) ]); return result; } catch (error) { console.error('๊ฒฉ๋ฆฌ ํจ์ ์คํจ:', error); return config.fallback(sanitizedInput, error as Error); } }; } // ์ฌ์ฉ๋ฒ const processUserData = createQuarantinedFunction({ implementation: aiGeneratedProcessUserData, validator: (input) => input != null && typeof input.id === 'number', sanitizer: (input) => ({ ...input, name: String(input.name || '').slice(0, 100) }), fallback: (input, error) => { return originalProcessUserData(input); } });
์ฌ๋์ด๋ AI๋ ์ ํ์ ํ๊ธฐ
๋ชฉํ๋ AI๋ฅผ ์ ์ฐ๋ ๊ฒ ์๋์์. AI๊ฐ ์๋๋ฅผ ๋ด์ฃผ๊ณ , ์ฌ๋์ด ์์ ์ฑ์ ์ฑ๊ธฐ๋ ํ์ ๋ชจ๋ธ์ ๋ง๋๋ ๊ฑฐ์์.
๋ฆฌ๋ทฐ ์ฒดํฌ๋ฆฌ์คํธ
AI ์ฝ๋ ๋จธ์ง ์ ์ ํ์ธํ ๊ฒ๋ค:
## AI ์ฝ๋ ๋ฆฌ๋ทฐ ์ฒดํฌ๋ฆฌ์คํธ ### ํ์ (๋ค ํต๊ณผํด์ผ ํจ) - [ ] import ๋ค ์์ - [ ] deprecated API ์ ์ - [ ] ์๋ฌ ํธ๋ค๋ง ์์ (๋คํธ์ํฌ, ํ์์์, null) - [ ] ์ ๋ ฅ ๊ฒ์ฆ ์์ - [ ] ๋ฏผ๊ฐ ์ ๋ณด ๋ก๊น ์ ํจ - [ ] ํ๋์ฝ๋ฉ๋ ์ํฌ๋ฆฟ ์์ ### ํ๋ก๋์ ์ค๋น - [ ] ๋์์ฑ ์ฒ๋ฆฌ ๋จ - [ ] ์ฌ์๋ ๋ก์ง ์์ - [ ] ์ํท๋ธ๋ ์ด์ปค ์์ - [ ] ๋๋ฒ๊น ์ฉ ๋ก๊ทธ ์์ - [ ] ๋ฆฌ์์ค ์ ๋ฆฌ ๋ณด์ฅ๋จ ### ์คํ์ผ - [ ] ๋ค์ด๋ฐ ์ปจ๋ฒค์ ๋ง์ - [ ] ๋ณต์ก๋ ์ ๋นํจ - [ ] ์ฃ์ง์ผ์ด์ค ํ ์คํธ ์์
์ ๋ขฐ ๋ ๋ฒจ ์์คํ
AI ์ฝ๋์ ๋ํ ์ ๋ขฐ๋ฅผ ๋จ๊ณ์ ์ผ๋ก ์์๊ฐ์ธ์:
๋ ๋ฒจ 1 - ๊ฒฉ๋ฆฌ (0-10ํ ์ฌ์ฉ): ํด๋ฐฑ ํ์, ๋ก๊ทธ ๋ง์ด, ์๋์ฐ ํ
์คํธ
๋ ๋ฒจ 2 - ๊ด์ฐฐ (10-100ํ): ํด๋ฐฑ ์ค๋น, ๋ก๊ทธ ๊ฐํ
๋ ๋ฒจ 3 - ์ ๋ขฐ (100ํ ์ด์ ๋ฌด์ฌ๊ณ ): ์ผ๋ฐ ๋ก๊ทธ, ํด๋ฐฑ ๋ถํ์
์ ๋ฆฌ
AI ์ฝ๋๊ฐ ํ๋ก๋์ ์์ ํฐ์ง๋ ๊ฑด ์์ธก ๊ฐ๋ฅํด์: ์ปจํ ์คํธ ํ๊ณ, ์๋ ํ์ต ๋ฐ์ดํฐ, ํดํผ ํจ์ค๋ง ์, ๋์์ฑ ๋ชฐ๋ผ์. ์ด ํจํด๋ค์ ์๋ฉด ๋ฐฐํฌ ์ ์ ์ก์ ์ ์๊ณ , ํฐ์ก์ ๋๋ ๋น ๋ฅด๊ฒ ๊ณ ์น ์ ์์ด์.
ํต์ฌ ์ ๋ฆฌ:
-
AI๋ ์ฌ๋ฌ๋ถ ์ฝ๋๋ฅผ ๋ชจ๋ฅธ๋ค โ ํจํด ๊ธฐ๋ฐ์ผ๋ก ์ถ์ธก๋ง ํจ. import, ๋ค์ด๋ฐ, ์์กด์ฑ ๊ผญ ํ์ธ.
-
AI๋ ์์ ๋ก ๋ฐฐ์ ์ง, ํ๋ก๋์ ์ผ๋ก ์ ๋ฐฐ์ ๋ค โ ์๋ฌ ํธ๋ค๋ง, ์ฃ์ง์ผ์ด์ค, ๋์์ฑ ์ง์ ํ ์คํธํด์ผ ํจ.
-
AI ํ์ต ๋ฐ์ดํฐ๋ ๊ณผ๊ฑฐ๋ค โ deprecated API, ์๋ ๋ณด์ ํจํด ์ฒดํฌ ํ์.
-
๋ก๊น ์ฒ์๋ถํฐ ์ ํด๋๋ผ โ AI ์ฝ๋ ํ์ํด๋๋ฉด ๋๋ฒ๊น ๋นจ๋ผ์ง.
-
๋ฏฟ๋ ๊ฒ์ฆํ๋ผ โ ๊ฒฉ๋ฆฌ ํจํด์ผ๋ก ์์ ํ๊ฒ ํตํฉํ๋ฉด์ ํ๋ก๋์ ์์ ์ฑ ์ ์ง.
2026๋ ์ ์ ๋๊ฐ๋ ๊ฐ๋ฐ์๋ AI ์ ์ฐ๋ ์ฌ๋๋ ์๋๊ณ , AI ์ฝ๋ ๋ฌด์กฐ๊ฑด ๋ฏฟ๋ ์ฌ๋๋ ์๋ ๊ฑฐ์์. ํฐ์ง๋ ํจํด์ ์ดํดํ๊ณ , ๊ฒ์ฆ ํ์ดํ๋ผ์ธ ๋ง๋ค๊ณ , ์ฌ๋-AI ํ์ ์ ์ ํ๋ ์ฌ๋๋ค์ด์์.
AI ์ฝ๋๋ ์ฌ๋ผ์ง์ง ์์์. ์ ํฐ์ง๋์ง, ์ด๋ป๊ฒ ๊ณ ์น๋์ง ์๋ ๊ฒ ์ด์ ํ์ ์คํฌ์ ๋๋ค.