--- id: wiki-2026-0508-사용자-제작-콘텐츠-ugc title: 사용자 제작 콘텐츠 (UGC) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [User-Generated Content, UGC, User Content] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [ugc, content, moderation, platform, architecture] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: nextjs --- # 사용자 제작 콘텐츠 (UGC) ## 매 한 줄 > **"매 user 의 platform 에서 create 하는 content"**. 매 Web 2.0 (2004) 의 core concept — Wikipedia, YouTube, Reddit 의 fuel. 2026 년 매 LLM-generated content + human content 의 mix 의 challenge — 매 provenance, moderation, copyright 의 critical. ## 매 핵심 ### 매 lifecycle 1. **Creation**: 매 upload, post, comment. 2. **Storage**: 매 object storage (S3, R2) + DB metadata. 3. **Moderation**: 매 automated (Perspective API, OpenAI Moderation) + human review. 4. **Distribution**: 매 CDN + recommendation algorithm. 5. **Lifecycle end**: 매 delete, archive, takedown (DMCA). ### 매 challenges (2026) - **AI-generated content**: 매 deepfake, slop — provenance (C2PA) 의 필요. - **Moderation scale**: 매 LLM moderator 의 cost-effective. - **Copyright**: 매 training data 의 lawsuit (NYT v OpenAI 등). - **Liability**: 매 Section 230 (US), DSA (EU) 의 compliance. ### 매 응용 1. Social media — 매 post, video, comment. 2. Reviews/ratings — 매 e-commerce. 3. Forums — 매 Reddit, Discord, HN. 4. Wiki — 매 collaborative knowledge. ## 💻 패턴 ### Upload + S3 presigned URL ```typescript // API: generate presigned URL import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; export async function POST(req: Request) { const { userId } = await authenticate(req); const { filename, contentType } = await req.json(); if (!ALLOWED_TYPES.includes(contentType)) { return Response.json({ error: 'invalid type' }, { status: 400 }); } const key = `uploads/${userId}/${crypto.randomUUID()}-${filename}`; const url = await getSignedUrl( s3, new PutObjectCommand({ Bucket: 'ugc', Key: key, ContentType: contentType }), { expiresIn: 300 } ); return Response.json({ url, key }); } ``` ### Moderation pipeline (text) ```typescript import OpenAI from 'openai'; const openai = new OpenAI(); async function moderateText(text: string) { const result = await openai.moderations.create({ model: 'omni-moderation-latest', input: text, }); const r = result.results[0]; if (r.flagged) { return { allowed: false, categories: r.categories }; } return { allowed: true }; } ``` ### Image moderation (vision) ```typescript // 매 NSFW + violence detection — Claude Opus 4.7 vision. import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic(); async function moderateImage(imageUrl: string) { const res = await client.messages.create({ model: 'claude-opus-4-7', max_tokens: 200, messages: [{ role: 'user', content: [ { type: 'image', source: { type: 'url', url: imageUrl } }, { type: 'text', text: 'Classify this image: SFW, NSFW, violence, hate, none. JSON only.' }, ], }], }); return JSON.parse(res.content[0].text); } ``` ### Provenance (C2PA) ```typescript // 매 image 의 origin metadata — AI 생성 여부 의 verify. import { read } from 'c2pa'; async function checkProvenance(imageUrl: string) { const c2pa = await read(imageUrl); if (!c2pa) return { verified: false }; const manifest = c2pa.manifestStore?.activeManifest; return { verified: c2pa.manifestStore?.validationStatus === 'valid', generator: manifest?.claimGenerator, aiGenerated: manifest?.assertions.some((a) => a.label.startsWith('c2pa.actions.ai_')), }; } ``` ### Vote + ranking ```typescript // 매 Reddit-style — Wilson score interval. function wilsonScore(up: number, down: number) { const n = up + down; if (n === 0) return 0; const z = 1.96; // 95% confidence const p = up / n; return (p + z*z/(2*n) - z * Math.sqrt((p*(1-p) + z*z/(4*n))/n)) / (1 + z*z/n); } const posts = await db.post.findMany(); posts.sort((a, b) => wilsonScore(b.up, b.down) - wilsonScore(a.up, a.down)); ``` ### Rate limit (anti-spam) ```typescript import { Ratelimit } from '@upstash/ratelimit'; import { Redis } from '@upstash/redis'; const ratelimit = new Ratelimit({ redis: Redis.fromEnv(), limiter: Ratelimit.slidingWindow(10, '1 h'), // 10 posts/hour }); const { success } = await ratelimit.limit(`post:${userId}`); if (!success) throw new Error('rate limit'); ``` ### DMCA takedown flow ```typescript async function handleDmcaTakedown(claim: DmcaClaim) { // 1. Hide content immediately await db.content.update({ where: { id: claim.contentId }, data: { status: 'TAKEDOWN_PENDING' }, }); // 2. Notify uploader (counter-notice option) await notifyUploader(claim); // 3. Log for audit await db.dmcaLog.create({ data: { ...claim, processedAt: new Date() } }); } ``` ## 매 결정 기준 | Content type | Moderation | |---|---| | Text comment | OpenAI Moderation API + human review | | Image | Claude vision + perceptual hash (NCMEC PhotoDNA) | | Video | Frame sampling + ML + community flag | | Long-form (article) | Pre-publish review + post-flag | | Realtime chat | Profanity filter + ML in pipeline | **기본값**: 매 LLM moderation + human escalation queue. ## 🔗 Graph - Adjacent: [[Content Moderation]] · [[C2PA]] ## 🤖 LLM 활용 **언제**: user-content platform 의 moderation, ranking, provenance 의 design 시. **언제 X**: closed editorial content (news site) — 매 UGC 무관. ## ❌ 안티패턴 - **No moderation**: 매 spam/abuse 의 platform 의 destroy. - **Heavy pre-moderation only**: 매 latency + cost — 매 hybrid 의 better. - **Naive ranking** (`order by votes desc`): 매 first-mover advantage — Wilson score 의 better. - **Ignore copyright**: 매 DMCA 의 ignore — legal liability. ## 🧪 검증 / 중복 - Verified (OpenAI Moderation docs; C2PA spec 2.0; OWASP UGC guidelines; DSA 2024). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — UGC lifecycle + moderation + C2PA |