9148c358d0
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
5.3 KiB
5.3 KiB
id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, created_at, updated_at, last_reinforced, review_reason, merge_history, tags, raw_sources, tech_stack, applied_in
| id | title | category | status | canonical_id | aliases | duplicate_of | source_trust_level | confidence_score | verification_status | created_at | updated_at | last_reinforced | review_reason | merge_history | tags | raw_sources | tech_stack | applied_in | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| idempotent-operations | 멱등성 연산 실전 (Idempotent Operations) | Coding | draft | idempotent-operations |
|
null | B | 0.9 | conceptual | 2026-05-09 | 2026-05-09 | 2026-05-09 |
|
|
|
멱등성 연산 실전
같은 요청이 N번 와도 부작용은 1번분만. 분산 시스템에서 "exactly-once" 는 불가능에 가깝고, "at-least-once + idempotent" 가 현실적 답이다.
📖 핵심 개념
멱등성(idempotent): f(f(x)) == f(x). 연산을 여러 번 적용해도 첫 번째 적용과 결과가 같음.
분산 환경의 모든 네트워크 호출은 재시도 가능성이 있다 (ACK 유실 / 타임아웃 / 클라이언트 retry). 따라서 다음을 항상 가정:
- 클라이언트는 같은 요청을 여러 번 보낼 수 있다
- 메시지 큐는 같은 메시지를 여러 번 전달할 수 있다 (at-least-once delivery)
- DB 트랜잭션은 commit 후 응답을 못 받고 재시도될 수 있다
해결: 연산 자체를 멱등하게 만들거나, 외부 idempotency key로 중복 감지.
💻 코드 패턴
1. HTTP 엔드포인트 — Idempotency-Key 헤더
// Express
app.post('/api/payments', async (req, res) => {
const key = req.header('Idempotency-Key');
if (!key) return res.status(400).json({ error: 'Idempotency-Key required' });
// 1) 같은 키의 이전 응답을 캐시에서 조회
const cached = await redis.get(`idem:${key}`);
if (cached) return res.json(JSON.parse(cached));
// 2) 진짜 결제 수행 (DB 트랜잭션 안에서 키 unique 보장)
try {
const payment = await db.transaction(async (tx) => {
await tx.idempotencyKeys.insert({ key, status: 'processing' }); // UNIQUE
const result = await chargePayment(req.body);
await tx.idempotencyKeys.update(key, { status: 'done', response: result });
return result;
});
await redis.setex(`idem:${key}`, 86400, JSON.stringify(payment));
res.json(payment);
} catch (e) {
if (isUniqueViolation(e)) {
// 이전 호출이 진행 중 — 클라이언트가 잠시 후 재시도하도록
return res.status(409).json({ error: 'In progress, retry later' });
}
throw e;
}
});
2. SQL — UPSERT 로 멱등 INSERT
-- 같은 user_id 가 여러 번 들어와도 한 행
INSERT INTO user_settings (user_id, theme)
VALUES ($1, $2)
ON CONFLICT (user_id) DO UPDATE SET theme = EXCLUDED.theme;
3. Message Queue Consumer — processed_id 테이블
async function handleMessage(msg: { id: string; payload: any }) {
const inserted = await db.processedMessages.insertIfAbsent({ id: msg.id }); // UNIQUE
if (!inserted) {
logger.info('Duplicate message ignored', { id: msg.id });
return; // 이미 처리됨
}
await actuallyProcess(msg.payload);
}
🤔 의사결정 기준
| HTTP 메서드 | 멱등성 기본값 | 강제 필요 |
|---|---|---|
| GET / HEAD / PUT / DELETE | 멱등 (RFC 9110) | 일반적으로 자연스러움 |
| POST | 비멱등 | 반드시 Idempotency-Key 도입 |
| 작업 종류 | 멱등화 전략 |
|---|---|
| 결제 / 외부 API 호출 | Idempotency-Key + 응답 캐시 |
| DB INSERT | UPSERT or UNIQUE 제약 |
| 메시지 큐 consumer | processed_id 테이블 |
| 파일 업로드 | content hash 기반 dedup |
| 카운터 증가 | 이건 멱등화 어려움 — sequence number / event sourcing 고려 |
❌ 안티패턴
- 클라이언트가 키를 안 보냈다고 그냥 처리: "키 없으면 실패" 가 안전. 키를 매번 새로 만드는 클라이언트는 매번 새 결제로 처리되어도 클라이언트 책임.
- 응답 캐시 TTL 너무 짧음: 24시간 권장. 그보다 짧으면 늦은 retry가 새 처리를 만듦.
- 카운터 증가를 "그냥 두 번 올리면 되지" 식으로 처리: at-least-once + ++counter 는 정확한 카운트 보장 못 함. 이벤트 소싱 또는 외부 dedup 필요.
- 트랜잭션 밖에서 키 체크: 키 체크 → 실 처리 사이에 race. 키 INSERT는 같은 트랜잭션 안에서.
- 에러 응답도 캐싱: 일시 오류(5xx)를 캐시하면 후속 retry가 모두 실패. 4xx만 캐싱, 5xx는 캐시 안 함.
🤖 LLM 활용 힌트
- LLM에게 외부 호출 코드 작성을 시킬 때 "at-least-once delivery 가정 + Idempotency-Key 패턴 적용" 명시.
- DB INSERT 코드: "같은 키로 N번 호출되어도 한 행만 남도록" 라고 요청 → UPSERT or UNIQUE.
- 메시지 핸들러 작성: "이 핸들러는 같은 메시지가 N번 들어와도 안전해야 함" 명시.
🧪 검증 상태
- verification_status:
conceptual - Stripe / AWS / Square 의 실제 결제 API 가 모두 Idempotency-Key 사용 → B급 출처.
- 적용 사례 발견 시
applied_in추가.