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 폴더 제거.
7.5 KiB
7.5 KiB
id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
| id | title | category | status | canonical_id | aliases | duplicate_of | source_trust_level | confidence_score | verification_status | tags | raw_sources | last_reinforced | github_commit | tech_stack | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| wiki-2026-0508-style-registry | Style Registry | 10_Wiki/Topics | verified | self |
|
none | A | 0.9 | applied |
|
2026-05-10 | pending |
|
Style Registry
매 한 줄
"매 SSR streaming 시 CSS-in-JS 의 styles 를 HTML 에 inject 하는 mechanism". Next.js 13 App Router 의
useServerInsertedHTMLhook 도입 — 매 streaming RSC render 도중 styled-components / emotion / @mui 가 generated CSS 를<head>의 inject. 2026 zero-runtime CSS (Vanilla Extract / Panda) 의 등장 으로 registry 의 less common, but legacy SC/emotion app 의 still required.
매 핵심
매 Why needed
- CSS-in-JS = runtime styles: rules generated when component renders.
- SSR: server renders HTML; client hydrates. Without registry → FOUC (flash of unstyled content) + hydration mismatch.
- Streaming SSR: HTML chunks sent progressively. Styles must inject as components render, not at end.
- App Router:
useServerInsertedHTMLprovides hook into Suspense boundary stream.
매 Mechanism
- Server: collect styles into sheet during render (per request).
- Server: insert
<style>tags into HTML stream viauseServerInsertedHTML. - Client: hydrate — runtime takes over, no re-render needed.
- Concurrency: per-request sheet (no global state pollution).
매 응용
- styled-components in Next.js App Router.
- Emotion in Next.js App Router.
- @mui v5+ in Next.js.
💻 패턴
styled-components Registry
// app/lib/registry.tsx
'use client';
import React, { useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
export default function StyledComponentsRegistry({
children,
}: {
children: React.ReactNode;
}) {
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement();
styledComponentsStyleSheet.instance.clearTag();
return <>{styles}</>;
});
if (typeof window !== 'undefined') return <>{children}</>;
return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
{children}
</StyleSheetManager>
);
}
Apply in root layout
// app/layout.tsx
import StyledComponentsRegistry from './lib/registry';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
);
}
Emotion Registry
// app/lib/emotion-registry.tsx
'use client';
import createCache from '@emotion/cache';
import { CacheProvider } from '@emotion/react';
import { useServerInsertedHTML } from 'next/navigation';
import { useState } from 'react';
export default function EmotionRegistry({
children,
}: {
children: React.ReactNode;
}) {
const [{ cache, flush }] = useState(() => {
const cache = createCache({ key: 'css' });
cache.compat = true;
const prevInsert = cache.insert;
let inserted: string[] = [];
cache.insert = (...args) => {
const serialized = args[1];
if (cache.inserted[serialized.name] === undefined) {
inserted.push(serialized.name);
}
return prevInsert(...args);
};
const flush = () => {
const prev = inserted;
inserted = [];
return prev;
};
return { cache, flush };
});
useServerInsertedHTML(() => {
const names = flush();
if (names.length === 0) return null;
let styles = '';
for (const name of names) {
styles += cache.inserted[name];
}
return (
<style
data-emotion={`${cache.key} ${names.join(' ')}`}
dangerouslySetInnerHTML={{ __html: styles }}
/>
);
});
return <CacheProvider value={cache}>{children}</CacheProvider>;
}
@mui Registry
// app/lib/mui-registry.tsx
'use client';
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
import { ThemeProvider } from '@mui/material/styles';
import { theme } from './theme';
export default function MuiRegistry({
children,
}: {
children: React.ReactNode;
}) {
return (
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</AppRouterCacheProvider>
);
}
Pages Router (legacy) — _document.tsx
// pages/_document.tsx — Pages Router uses different mechanism
import Document, { DocumentContext } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: [initialProps.styles, sheet.getStyleElement()],
};
} finally {
sheet.seal();
}
}
}
Verifying SSR works
// 1. View source (Cmd+U) — should see <style> tags with rules
// 2. Disable JS in DevTools — page should still be styled
// 3. Check Network tab — no FOUC during page transition
// 4. React DevTools — no hydration mismatch warnings
매 결정 기준
| 상황 | Approach |
|---|---|
| Greenfield 2026 Next.js | Tailwind / Vanilla Extract — no registry needed |
| Existing styled-components migration | Add StyledComponentsRegistry |
| Emotion-based codebase | EmotionRegistry pattern |
| @mui v5+ | AppRouterCacheProvider (built-in) |
| Pages Router legacy | _document.tsx + sheet collection |
기본값: greenfield → zero-runtime CSS (no registry). Existing CSS-in-JS → use library-recommended registry pattern.
🔗 Graph
- 부모: CSS in JS · SSR
- 변형: useServerInsertedHTML
- 응용: Styled Components v6
- Adjacent: Streaming SSR · React Server Components — 경계 의식 · Hydration
🤖 LLM 활용
언제: SSR setup for CSS-in-JS, Next.js App Router migration from Pages, debugging FOUC / hydration mismatch. 언제 X: Tailwind / CSS Modules / Vanilla Extract — these are zero-runtime, no registry needed.
❌ 안티패턴
- No registry → FOUC: styled-components SSR without registry shows unstyled HTML on first paint.
- Global sheet (not per-request): cross-request style pollution / memory leak.
'use client'on registry but rendered at top level: marks entire tree as client — kills RSC benefit. Wrap deeply.- Forgetting
clearTag(): duplicate styles inserted on each chunk. - Mixing registries: emotion + styled-components → two style systems, double bundle, conflicts.
🧪 검증 / 중복
- Verified (Next.js docs
app/building-your-application/styling, styled-components Next.js example, Emotion + Next.js guide). - 신뢰도 A.
🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — full canonical (registry mechanism + SC/Emotion/MUI patterns + Pages Router fallback) |