LogoSEO Jing
  • All Posts
  • SEO Jing
  • okayJing
  • KD Team
  • CLAB Coreteam
  • Study

Contact Me

© 2026 SEOJing. All rights reserved.

ReactReact.ChildrenchildrenCompound ComponentContext트러블슈팅DevLog

Context로 퀴즈 컴포넌트를 만들다 막혀서 React.Children을 공부하게 된 이야기

2026년 3월 21일·20분 읽기

만들고 싶었던 것

이 블로그에 퀴즈 컴포넌트를 추가하고 싶었다. MDX 파일에서 이렇게 쓸 수 있으면 좋겠다고 생각했다.

mdx
<ArticleQuiz>
  <ArticleQuizItem
    mode="multiple"
    question="HTML에서 빈 태그란?"
    choices={["닫는 태그가 없는 태그", "내용이 비어있는 태그", "주석 태그"]}
    answer={0}
    explanation="<br />, <img /> 같은 태그를 빈 태그라고 합니다."
  />
  <ArticleQuizItem
    mode="description"
    question="React에서 조건부 렌더링에 쓰이는 연산자는?"
    answer="삼항 연산자"
    explanation="condition ? A : B 형태의 삼항 연산자가 가장 흔히 쓰입니다."
  />
</ArticleQuiz>

요구사항은 명확했다. 한 번에 하나의 문제만 보여주고, 정답 확인 후 다음 문제로 넘기고, 마지막에 점수를 보여준다. 부모인 ArticleQuiz가 현재 단계를 관리하면서, 자식 ArticleQuizItem에 콜백을 전달해야 한다.

첫 번째 시도: Context 기반 Compound Component

처음에는 "요즘은 Context로 Compound Component 만드는 게 정석이지"라는 생각으로 Context부터 잡았다.

tsx
const QuizContext = React.createContext<{
  currentStep: number;
  score: number;
  onResult isCorrect   

Post Q&A

오케이징에게 물어보기

Context로 퀴즈 컴포넌트를 만들다 막혀서 React.Children을 공부하게 된 이야기 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!

0/500

포스트 목록

/SEOJing
파일 15개, 폴더 1개
Cloudflare Workers에서 fs 모듈이 안 되는 이유와 해결법대표 이미지 자동화 실험 — 검색과 Codex 생성이 같은 경로로 붙었다본문 이미지를 나중에 넣는 게 아니라 — SEOJing 글쓰기 파이프라인에 시각 판단을 넣기모바일 웹에서 가로 모드를 강제하는 5가지 방법 — iOS Safari에서도 동작하는 코드 뷰어 만들기블로그 글을 PPT로 만들기 — DOM 클로닝 기반 프레젠테이션 모드100vh가 100%가 아닌 이유 — 모바일 뷰포트 단위 완전 정리Context로 퀴즈 컴포넌트를 만들다 막혀서 React.Children을 공부하게 된 이야기대표 이미지를 글마다 다시 붙이는 방식 — 사진 검색에서 리소그래프 배경까지글 위에 영상을 붙인다는 것 — SEOJing 요약 쇼츠 파이프라인localStorage 읽기에서 하이드레이션 에러가 터지는 이유 useSyncExternalStore로 해결useEffect cleanup과 의존성 배열 — 실전 버그 사례로 이해하는 생애주기vinext + GitHub Actions로 Cloudflare Workers 배포하기vinext 오픈소스 기여기: 한국어 slug가 RSC에서 이슈를 일으킨 이유RSC 환경에서 WebAssembly가 차단되는 이유 — Shiki에서 rehype-prism-plus로vinext는 왜 빠를까? — SSR, Vite, Edge, 그리고 Web Vitals까지

같은 섹션의 대표 이미지

39 posts · latest first
본문 이미지를 나중에 넣는 게 아니라 — SEOJing 글쓰기 파이프라인에 시각 판단을 넣기 글의 대표 이미지
SEO Jing26. 06. 22.

본문 이미지를 나중에 넣는 게 아니라 — SEOJing 글쓰기.

SEOJing에서 새 글을 쓸 때 대표 이미지와 본문 이미지를 빼먹지 않도록, 블로그 맵과 글쓰기 파이프라인 안에 시각 판단 단계를 넣은 과정을 정리했다.

26. 06. 22.SEOJing
:
(
:
boolean
)
=>
void
;
onNext: () => void;
} | null>(null);
function ArticleQuiz({ children }: { children: React.ReactNode }) {
const [currentStep, setCurrentStep] = useState(0);
const [score, setScore] = useState(0);
const handleResult = (isCorrect: boolean) => {
if (isCorrect) setScore((prev) => prev + 1);
};
const handleNext = () => {
setCurrentStep((prev) => prev + 1);
};
return (
<QuizContext.Provider
value={{ currentStep, score, onResult: handleResult, onNext: handleNext }}
>
<div className="my-8">{children}</div>
</QuizContext.Provider>
);
}

ArticleQuizItem은 Context에서 상태를 꺼내 쓰면 된다. 여기까지는 깔끔했다.

tsx
function ArticleQuizItem({ question, answer, ...props }: QuizItemProps) {
  const ctx = React.useContext(QuizContext);
  if (!ctx) throw new Error("ArticleQuizItem must be inside ArticleQuiz");

  const { currentStep, onResult, onNext } = ctx;
  // ... 문제 UI 렌더링
}

막힌 지점: "나는 몇 번째 문제인가?"

문제는 각 QuizItem이 자신의 index를 알 수 없다는 것이었다.

"한 번에 하나만 보여준다"는 건, 부모가 currentStep이 0이면 첫 번째 문제만 보여주고 나머지는 숨겨야 한다는 뜻이다. Context 패턴에서는 모든 QuizItem이 동시에 마운트되어 있으니, 각자 "내가 currentStep 번째인지" 판단해야 한다.

tsx
function ArticleQuizItem({ question, answer }: QuizItemProps) {
  const { currentStep } = React.useContext(QuizContext)!;

  // 문제: 내 index를 어떻게 알지?
  const myIndex = ???;

  if (myIndex !== currentStep) return null;
  // ...
}

Context에는 자식의 순서 정보가 없다. children이 몇 개이고, 내가 그 중 몇 번째인지는 Context가 알려줄 수 없는 정보다.

생각해본 우회법들

몇 가지 방법을 고민했다.
1. index prop을 수동으로 넘기기
mdx
<ArticleQuiz>
  <ArticleQuizItem index={0} question="..." />
  <ArticleQuizItem index={1} question="..." />
  <ArticleQuizItem index={2} question="..." />
</ArticleQuiz>

MDX 작성자가 직접 index를 매겨야 한다. 문제를 추가하거나 순서를 바꿀 때마다 index를 다시 계산해야 한다. 실수하기 딱 좋고, MDX 작성 경험을 망친다.

2. ref 기반 등록 패턴
tsx
function ArticleQuiz({ children }) {
  const registry = useRef<string[]>([]);

  const register = (id: string) => {
    if (!registry.current.includes(id)) registry.current.push(id);
    return registry.current.indexOf(id);
  };

  return 

렌더링 중에 ref를 mutate하는 건 React의 규칙에 어긋나고, Strict Mode에서 두 번 호출되면 index가 꼬인다. useEffect로 등록하면 첫 렌더에서 index를 알 수 없어서 깜빡임이 생긴다. 단순한 퀴즈 컴포넌트에 이 정도 복잡도는 과하다.

전환: React.Children이라는 해법

이 시점에서 발상을 바꿨다. "자식이 자기 index를 아는" 모델이 아니라, 부모가 자식을 골라서 보여주는 모델로 전환하면 어떨까?

찾아보니 React.Children이라는 유틸리티가 있었다. React 공식 문서에서는 레거시 API로 분류하고 있지만, 정확히 이 문제를 해결하기 위해 만들어진 도구였다.

tsx
function ArticleQuiz({ children }: ArticleQuizProps) {
  const [currentStep, setCurrentStep] = useState(0);

  const items = React.Children.toArray(children).filter(React.isValidElement);
  const totalSteps = items.length;

  return (
    <div>
      {React.cloneElement(items[currentStep] as  
이 코드가 해결해주는 것들을 보자.
  • index 문제 해소: 부모가 items[currentStep]으로 직접 골라서 보여준다. 자식이 자기 index를 알 필요가 없다
  • 한 번에 하나만 렌더: 현재 단계의 QuizItem만 마운트된다. 불필요한 렌더링이 없다
  • props 주입: cloneElement로 onResult, onNext를 전달한다. QuizItem 입장에서는 그냥 props로 받으면 된다

React.Children API 정리

이 경험을 계기로 React.Children API를 제대로 공부했다. 정리하면 다음과 같다.

API설명
React.Children.toArray(children)children을 평탄화된 배열로 변환. key를 자동 부여
React.Children.map(children, fn)children을 순회하며 변환. null/undefined는 자동 제거
React.Children.forEach(children, fn)map과 동일하되 반환값 없음
React.Children.count(children)유효한 자식 노드 개수 반환

toArray가 필요한 이유

React에서 children prop은 타입이 일정하지 않다. 자식이 하나면 ReactElement, 여러 개면 배열, 텍스트면 문자열, 없으면 undefined. React.Children.toArray는 이 불규칙한 타입을 항상 배열로 정규화해준다.

tsx
// children이 하나일 때
<Quiz><Item /></Quiz>          // children = ReactElement (배열 아님!)

// children이 여러 개일 때
<Quiz><Item /><Item /></Quiz>  // children = ReactElement[]

// toArray를 쓰면 항상 배열
const items = React.Children.toArray(children);
// 단일 원소 → [element], 배열 → 그대로, null → []

추가로 Fragment 안에 감싸진 중첩 구조도 평탄화하고, 각 원소에 안정적인 key를 자동 부여한다.

map — 변환이 필요할 때

React.Children.map은 toArray + Array.map과 비슷하지만, 콜백에서 null을 반환하면 해당 원소를 자동 제거한다는 차이가 있다.

tsx
function Tabs({ children }: { children: React.ReactNode }) {
  return (
    <div>
      {React.Children.map(children, (child, index) => {
        if (!React.isValidElement(child)) return null; // 자동 제거
        return React.cloneElement(child, { index });
      

count와 only

count는 렌더링 가능한 자식 수를 반환한다. ArticleQuiz에서 프로그레스 바의 전체 단계 수를 계산할 때 items.length 대신 쓸 수도 있다. only는 children이 정확히 하나의 React element인지 검증하고, 아니면 에러를 던진다. Tooltip 같은 wrapper에서 유용하다.

tsx
function Tooltip({
  children,
  text,
}: {
  children: React.ReactNode;
  text: string;
}) {
  const child = React.Children.only(children);
  return React.cloneElement(child as React.ReactElement, {
    onMouseEnter: showTooltip,
  });
}

React.Children의 한계

만능은 아니다. 알고 써야 할 한계점들이 있다.
  • 1단계 깊이만 탐색한다. children의 children(손자 노드)까지는 들어가지 않는다. 중간에 <div>로 감싸면 내부를 볼 수 없다
  • children 구조에 의존한다. 사용하는 쪽에서 children 구조를 바꾸면(감싸기, 조건부 렌더링 등) 깨질 수 있다
  • cloneElement의 암묵성. 어디서 prop이 주입되는지 코드만 봐서는 추적하기 어렵다
tsx
// 이렇게 감싸면 ArticleQuiz가 QuizItem을 인식 못함
<ArticleQuiz>
  <div>
    <ArticleQuizItem ... />  {/* toArray로 접근 불가 */}
  </div>
</ArticleQuiz>

그럼에도 이 선택이 맞는 이유

React 공식 문서가 React.Children을 레거시로 분류한 건 사실이다. 범용 라이브러리에서는 Context 기반 패턴이 더 안전하다는 것도 맞다. 하지만 기술 선택은 환경과 제약 조건에 따라 달라진다.

MDX라는 특수한 환경

ArticleQuiz는 일반 React 앱이 아니라 MDX 파일 안에서 사용 된다. MDX에서 컴포넌트를 쓸 때는 마크다운 작성자 입장에서의 작성 경험(DX)이 최우선이다.

mdx
{/* 데이터 배열 방식 — MDX에서 쓰면 이렇게 된다 */}

<ArticleQuiz
  items={[
    {
      mode: "multiple",
      question: "질문1",
      choices: ["A", "B", "C"],
      answer: 1,
      explanation: "해설",
    },
    {
      mode: "multiple",
      question: "질문2",
      choices: ["X", "Y", "Z"],
      answer: 0,
      explanation: "해설",
    },
  ]}
/>

MDX 안에서 거대한 JavaScript 객체 리터럴을 작성해야 한다. 중괄호와 대괄호의 중첩, 쉼표 누락, 문자열 이스케이프 — 이런 것들이 글 쓰는 흐름을 끊는다. 반면 children 패턴은 각 문제가 독립적인 태그로 분리되어 있어서 읽기 쉽고 수정하기 편하다.

children 구조가 깨질 위험이 없다

React.Children의 가장 큰 약점은 "사용하는 쪽에서 children 구조를 바꾸면 깨진다"는 것이다. 하지만 이 컴포넌트는 이 블로그 전용이다. 사용처가 MDX 파일뿐이고, 항상 <ArticleQuizItem>을 직접 나열하는 패턴으로만 쓴다. 아무도 ArticleQuiz 안에 <div>를 감싸거나 조건부 렌더링을 하지 않는다. 이 전제가 보장되는 한, React.Children의 한계는 실질적 문제가 되지 않는다.

Context는 이 문제에서 오버엔지니어링이었다

되돌아보면, Context를 도입하려 했을 때 늘어났을 보일러플레이트를 생각해보자. QuizContext 정의, Provider 래핑, useContext 훅, null 체크 에러 바운더리 — 그리고 그 모든 걸 해도 index 문제는 여전히 해결되지 않았다. 결국 React.Children.toArray를 써야 index를 알 수 있었을 것이고, 그러면 Context 레이어는 불필요한 중간 추상화가 된다.

"레거시니까 쓰지 말아야 한다"는 사고방식은 위험하다. 중요한 건

왜 이 API가 존재하는지, 어떤 한계가 있는지 이해한 상태에서 선택하는 것

이다. ArticleQuiz의 경우, MDX 환경 + 고정된 사용 패턴 + 부모 주도 렌더링이라는 세 가지 조건이 맞아떨어져서 React.Children이 최선의 선택이 되었다.

트러블슈팅: cloneElement가 DOM에 prop을 흘린다

구현을 끝내고 브라우저를 열었더니 콘솔에 경고가 떴다.
React does not recognize the `stepIndex` prop on a DOM element.
If you intentionally want it to appear in the DOM as a custom attribute,
spell it as lowercase `stepindex` instead.

원인은 cloneElement로 주입한 prop이 ArticleQuizItem 내부에서 {...props} 로 DOM까지 전달된 것이었다.

tsx
// ArticleQuiz — cloneElement로 내부 prop 주입
React.cloneElement(items[currentStep] as React.ReactElement, {
  stepIndex: currentStep, // ← 이 prop이 문제
  currentStep,
  onResult: handleResult,
  onNext: handleNext,
});

// ArticleQuizItem — ...props가 DOM div에 그대로 전달됨
function ArticleQuizItem({ mode, question, onResult, onNext, ...props }) {
  return <div {...props}>...</div>;
  //          ^^^^^^^^ stepIndex, currentStep이 DOM attribute로 들어감

cloneElement는 기존 props에 새 props를 merge한다. ArticleQuizItem에서 명시적으로 destructure하지 않은 prop은 ...props에 포함되고, 이것이 <div>까지 흘러간다. React는 DOM element에 stepIndex 같은 비표준 attribute가 들어오면 경고를 띄운다.

해결은 간단하다. 내부 전용 prop을 destructure에서 빼내면 된다.

tsx
function ArticleQuizItem({
  mode,
  question,
  choices,
  answer,
  explanation,
  onResult,
  onNext,
  className,
  stepIndex: _stepIndex, // destructure만 하고 사용하지 않음
  currentStep: _currentStep, // DOM으로 전달되는 것을 방지
  ...props
}: ArticleQuizItemProps) {
  return (
    <div className={cn("flex flex-col gap-4", className)} {...props}>
      ...
    </div>
  

이건 cloneElement 패턴에서 흔히 발생하는 문제다. 부모가 주입하는 prop과 DOM에 전달되는 prop의 경계가 암묵적이기 때문이다. 이 점이 앞서 언급한 "cloneElement의 암묵성" 한계와 직결된다. 주입되는 prop이 추가될 때마다 자식 컴포넌트의 destructure 목록도 함께 업데이트해야 한다는 점을 기억해둬야 한다.

참고 문헌

  • React 공식 문서: React.Children

  • React 공식 문서: cloneElement

  • React 공식 문서: Context로 데이터 깊숙이 전달하기

SEO Jing26. 06. 22.

글 위에 영상을 붙인다는 것 — SEOJing 요약.

SEOJing 글을 소셜용 영상으로 따로 소비시키는 게 아니라, 포스트 상단 요약과 블로그 유입 장치로 연결하기 위해 summaryVideo frontmatter와 Supertonic3 기반 요약 쇼츠 파이프라인을 붙인 과정을 정리했다.

26. 06. 22.SEOJing
대표 이미지 자동화 실험 — 검색과 Codex 생성이 같은 경로로 붙었다 글의 리소그래프 스타일 대표 이미지 배경
SEO Jing26. 06. 21.

대표 이미지 자동화 실험 — 검색과 Codex 생성이 같은.

SEOJing 블로그에 대표 이미지를 자동으로 붙이는 실험을 실제로 돌려봤다. 검색 기반 cover 삽입과 Codex CLI 기반 정적 SVG 생성이 같은 frontmatter 경로로 연결됐다.

26. 06. 21.SEOJing
대표 이미지를 글마다 다시 붙이는 방식 글의 리소그래프 스타일 대표 이미지 배경
SEO Jing26. 06. 21.

대표 이미지를 글마다 다시 붙이는 방식 — 사진 검색에서.

SEOJing 포스트 목록을 파일 탐색기처럼만 두지 않고, 최신 글부터 실제 사진 기반 리소그래프 배경을 붙이는 실험을 정리합니다. 이미지는 배경만 만들고, 제목과 아이콘은 블로그 UI가 맡는 쪽으로 방향을 바꿨습니다.

26. 06. 21.SEOJing
SEO Jing26. 04. 01.

Day 12 - 테스트 커버리지 개선, 모바일 프레젠테이션 버그 2건.

SEO Jing 개발 열두째 날. code-block 테스트 14개 추가로 커버리지 대폭 개선, 모바일 프레젠테이션에서 FullscreenView 방향 전환 문제와 스크롤 멈춤 버그 수정.

26. 04. 01.SEOJing
SEO Jing26. 04. 01.

useEffect cleanup과 의존성 배열 — 실전 버그.

useEffect 의존성 배열에 불필요한 값이 포함되면 cleanup과 재실행이 뒤엉켜 DOM 상태가 꼬일 수 있다. 프레젠테이션 모드에서 발생한 모바일 스크롤 고착 버그를 통해 원인과 해결 패턴을 정리한다.

26. 04. 01.SEOJing
SEO Jing26. 03. 25.

Day 11 - 프레젠테이션 확대 기능,.

SEO Jing 개발 열한째 날. PC 프레젠테이션 확대/축소 컨트롤 추가, 모바일 orientation 판단 로직 개선, FullscreenView를 독립 컴포넌트로 분리 및 PC 대응.

26. 03. 25.SEOJing
SEO Jing26. 03. 25.

vinext 오픈소스 기여기: 한국어 slug가 RSC에서.

한국어 MDX 블로그를 만들다 vinext 프레임워크의 ByteString 버그를 발견하고, 이슈를 작성하고, PR을 올리기까지의 과정

26. 03. 25.SEOJing
SEO Jing26. 03. 25.

vinext는 왜 빠를까? — SSR, Vite, Edge,.

vinext가 빠른 이유를 이해하기 위해, SSR부터 Hydration, 빌드 도구, Edge Runtime, Web Vitals, RSC, CDN 캐싱, ISR, PPR까지 웹 렌더링 성능의 전체 그림을 정리한다

26. 03. 25.SEOJing
SEO Jing26. 03. 24.

100vh가 100%가 아닌 이유 — 모바일 뷰포트 단위 완전 정리.

모바일 Safari에서 100vh가 화면을 넘치는 이유, vh/svh/lvh/dvh의 차이, JavaScript에서 실제 뷰포트를 구하는 방법, 그리고 전체화면 UI를 만들 때 알아야 할 CSS zoom과 모바일 판정 패턴까지 정리한다.

26. 03. 24.SEOJing
SEO Jing26. 03. 23.

Day 10 - 프레젠테이션 모드 안정화.

SEO Jing 개발 열째 날. 프레젠테이션 모드의 모바일 UX 문제들을 전면 수정. 퀴즈·코드블록·이미지·포스트목록 처리 개선, 롱프레스 UX 및 하단 바 레이아웃 안정화. 모바일 뷰포트·리스트 분할 문제 수정, 채움 비율 보수적으로 조정, 포스트 탐색기 자연 정렬 적용.

26. 03. 23.SEOJing
SEO Jing26. 03. 22.

Day 9 - 프레젠테이션 모드, 코드블럭 개선, 테스팅.

SEO Jing 개발 아홉째 날. 프레젠테이션 기능 추가, 퀴즈 구조 변경, 모바일 반응형, 코드블럭 사용성, 테스팅 도입.

26. 03. 22.SEOJing
SEO Jing26. 03. 22.

모바일 웹에서 가로 모드를 강제하는 5가지 방법 — iOS.

모바일 웹에서 코드 블록을 가로로 넓게 보여주고 싶었다. screen.orientation.lock()은 iOS에서 안 되고, PWA manifest는 브라우저에서 무시된다. 결국 CSS transform으로 가짜 회전을 만들었고, 그 과정에서 엄지 접근성까지 고민하게 됐다.

26. 03. 22.SEOJing
SEO Jing26. 03. 22.

블로그 글을 PPT로 만들기 — DOM 클로닝 기반.

MDX 파일을 수정하지 않고, 렌더된 DOM을 h2 기준으로 자르고 화면 높이에 맞춰 자동 페이지네이션하는 프레젠테이션 모드를 만들었다. 리스트 높이 측정이 왜 틀리는지 디버깅한 과정과, ul/ol을 li 단위로 분할하는 해결책을 정리한다.

26. 03. 22.SEOJing
SEO Jing26. 03. 21.

Day 8 - 아티클 퀴즈와 스터디 자료.

SEO Jing 개발 여덟째 날. 아티클 퀴즈 디자인시스템 구현과 스터디 대면 자료 작성.

26. 03. 21.SEOJing
SEO Jing26. 03. 21.

Context로 퀴즈 컴포넌트를 만들다 막혀서.

MDX 블로그에 퀴즈 컴포넌트를 만들면서, Context 기반 Compound Component로 시작했다가 index 문제에 막혀 React.Children API를 채택하게 된 과정을 정리한다.

26. 03. 21.SEOJing
SEO Jing26. 03. 20.

Day 7 - Front Matter CMS와 관련 게시물.

SEO Jing 개발 일곱째 날. Front Matter CMS 설치와 관련 게시물 이동 탐색기 구현.

26. 03. 20.SEOJing
SEO Jing26. 03. 18.

Day 6 - 스터디 자료 작성과 데스크탑 비율 수정.

SEO Jing 개발 여섯째 날. 데스크탑 비율 수정과 씨랩 스터디 사전 진단 자료 작성.

26. 03. 18.SEOJing
SEO Jing26. 03. 17.

Day 5 - shiki 제거, MDX 모듈화, 그리고.

SEO Jing 개발 다섯째 날. shiki를 rehype-prism-plus로 교체하고, gray-matter 직접 구현, MDX 모듈화, 페이지 내 검색, 테이블 디자인시스템까지.

26. 03. 17.SEOJing
SEO Jing26. 03. 16.

Day 4 - 배포와 CI/CD.

SEO Jing 개발 넷째 날. lint, codecov, Cloudflare 배포, fs 런타임 이슈.

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

Cloudflare Workers에서 fs 모듈이 안 되는 이유와.

배포 후 블로그 포스트가 404를 반환하던 문제부터, gray-matter eval 차단, next-mdx-remote eval 차단까지 — 세 겹으로 터진 이슈를 하나씩 해결한 기록

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

localStorage 읽기에서 하이드레이션 에러가 터지는 이유.

localStorage를 읽는 컴포넌트에서 하이드레이션 불일치가 발생하는 원인과, useState+useEffect가 아닌 useSyncExternalStore가 정답인 이유를 정리한다.

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

vinext + GitHub Actions로.

vinext 프로젝트를 GitHub Actions로 Cloudflare Workers에 자동 배포하는 방법과 실제 겪은 트러블슈팅 기록

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

RSC 환경에서 WebAssembly가 차단되는 이유 —.

코드 하이라이팅에 Shiki를 쓰면 왜 RSC에서 WebAssembly.instantiate() 에러가 터지는지, 그리고 빌드 타임 하이라이팅으로 어떻게 해결했는지 정리한다.

26. 03. 16.SEOJing
SEO Jing26. 03. 15.

엄청난 피드백.

CLI 코드 리뷰에서 받은 피드백과 전체 코드 수정 계획을 정리했다.

26. 03. 15.SEOJing
SEO Jing26. 03. 15.

생각보다 어려웠던 댓글, 완독 로컬스토리지.

localStorage만으로 글 읽기 추적, 스크롤 진행률, 댓글 감지를 구현한 과정을 정리했다.

26. 03. 15.SEOJing
SEO Jing26. 03. 15.

MDX 관련 이슈 노트.

블로그 디테일 페이지에서 MDX를 렌더링하기 위해 검토한 라이브러리들과 최종 선택 과정.

26. 03. 15.SEOJing
SEO Jing26. 03. 15.

Day 3 - MDX 이슈, 반응형, 다크모드.

SEO Jing 개발 셋째 날. MDX 라이브러리 이슈, 반응형, 코드블럭, 댓글, 다크모드, 코드 리뷰.

26. 03. 15.SEOJing
SEO Jing26. 03. 14.

디자인 시스템을 구축할 때 주의할 점.

디자인 시스템 구현 시 파일 구조, 디자인 토큰, 유의 사항을 정리했다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

폰트는 왜 메인 페이지에서만 적용이 안되고 있었을까?.

Tailwind v4 환경에서 폰트가 메인 페이지에서만 적용되지 않던 원인과 Hydration Mismatch 이슈를 정리했다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

MDX DOM 트리 파싱하기.

MDX 파일의 경로 탐색 로직과 콘텐츠 트리 생성 과정을 정리했다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

결국 Node.js 까지 와버렸다.

MDX 파일 구조를 JSON으로 변환하기 위해 Node.js의 fs 모듈을 배워봤다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

Day 2 - 블로그 스켈레톤과 MDX 파싱.

SEO Jing 개발 둘째 날. 디자인 시스템 확장, 블로그 스켈레톤, 폰트 이슈 해결.

26. 03. 14.SEOJing
SEO Jing26. 03. 13.

전체적인 플로우.

SEO Jing 프로젝트의 기술 스택 선정과 전체적인 개발 플로우 정리.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

Storybook으로 디자인 시스템 테스팅하기.

Storybook의 사용법과 디자인 시스템 개발에서의 장점을 정리했다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

MDX가 뭘까?.

MDX의 개념과 블로그에서 활용하는 이유를 정리했다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

Day 1 - 디자인 컨셉과 디자인 시스템.

SEO Jing 개발 첫째 날. 디자인 컨셉 설정과 디자인 시스템 구축을 시작했다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

기술 블로그를 직접 제작하게 된 이유.

SEO Jing을 개발하게 된 이유입니다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

왜 자꾸 프로젝트가 중단되는지.

프로젝트가 중단되는 이유에 대한 자기 회고입니다.

26. 03. 13.SEOJing
(
<QuizContext.Provider value={{ register, ... }}>
{children}
</QuizContext.Provider>
);
}
function ArticleQuizItem({ question }) {
const { register, currentStep } = useContext(QuizContext)!;
const id = useRef(crypto.randomUUID()).current;
const myIndex = register(id);
// ...
}
React
.
ReactElement
,
{
onResult: handleResult,
onNext: handleNext,
})}
</div>
);
}
React.Children.only(children)
children이 정확히 하나인지 검증. 아니면 에러
}
)
}
</div>
);
}
}
)
;
// 이제 stepIndex, currentStep은 ...props에 포함되지 않음
}