<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: wes5510</title>
    <description>The latest articles on Forem by wes5510 (@wes5510).</description>
    <link>https://forem.com/wes5510</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F263430%2F3cda1278-a2d3-46a5-af13-ec4f2f2303c9.jpeg</url>
      <title>Forem: wes5510</title>
      <link>https://forem.com/wes5510</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wes5510"/>
    <language>en</language>
    <item>
      <title>마크다운 파일로 7,600개 태스크를 관리하는 시스템</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Sat, 07 Feb 2026 03:58:47 +0000</pubDate>
      <link>https://forem.com/wes5510/makeudaun-pailro-7600gae-taeseukeureul-gwanrihaneun-siseutem-cja</link>
      <guid>https://forem.com/wes5510/makeudaun-pailro-7600gae-taeseukeureul-gwanrihaneun-siseutem-cja</guid>
      <description>&lt;h2&gt;
  
  
  개요
&lt;/h2&gt;

&lt;p&gt;태스크 하나가 폴더 하나입니다. 그 안에 &lt;code&gt;index.md&lt;/code&gt; 파일이 있고, YAML frontmatter에 메타데이터를 적습니다. 폴더 구조가 곧 카테고리입니다. DB 없이 파일 시스템이 데이터베이스 역할을 합니다.&lt;/p&gt;

&lt;p&gt;현재 7,655개 태스크, 94% 완료율로 운영 중입니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hq/
├── 회사A/
├── 회사B/
├── 개인/
└── inbox/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  태스크 구조
&lt;/h2&gt;

&lt;p&gt;태스크 하나는 이렇게 생겼습니다:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;배포-프로세스-개선/
└── index.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-02-07&lt;/span&gt;
&lt;span class="na"&gt;estimation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2h&lt;/span&gt;
&lt;span class="na"&gt;focus_time&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1h 30m&lt;/span&gt;
&lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;P1&lt;/span&gt;
&lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;recurrence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;weekly&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="s"&gt;본문에 상세 내용, 메모, 결과물을 자유롭게 적습니다.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;frontmatter 필드:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;필드&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;수행일&lt;/td&gt;
&lt;td&gt;2026-02-07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;estimation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;예상 소요 시간&lt;/td&gt;
&lt;td&gt;2h&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;focus_time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;실제 소요 시간 (자동 누적)&lt;/td&gt;
&lt;td&gt;1h 30m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;priority&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;P0(긴급) ~ P3(낮음)&lt;/td&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;done&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;완료 여부&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;recurrence&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;반복 주기&lt;/td&gt;
&lt;td&gt;weekly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;같은 이름의 태스크가 생기면 &lt;code&gt;___숫자&lt;/code&gt;를 붙입니다. &lt;code&gt;주간_회의&lt;/code&gt;, &lt;code&gt;주간_회의___2&lt;/code&gt;, &lt;code&gt;주간_회의___3&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  자동화: Claude Code Skills
&lt;/h2&gt;

&lt;p&gt;Claude Code의 Skills 시스템으로 자연어 명령을 태스크 조작으로 연결합니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  포커스 타임 트래커
&lt;/h3&gt;

&lt;p&gt;"시작"이라고 말하면 &lt;code&gt;~/.focus_session.json&lt;/code&gt;에 시작 시간을 기록합니다. "완료"라고 말하면 경과 시간을 계산하여 &lt;code&gt;focus_time&lt;/code&gt;에 누적합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🎯 포커스 시작!
   태스크: index.md
   시작: 2026-02-07T12:24:53

✅ 포커스 종료!
   태스크: index.md
   소요 시간: 56m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;동시에 하나의 세션만 허용합니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  반복 태스크
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;recurrence&lt;/code&gt; 필드가 있는 태스크를 완료하면 다음 날짜의 태스크를 자동 생성합니다. &lt;code&gt;done: false&lt;/code&gt;로 초기화하고 &lt;code&gt;focus_time&lt;/code&gt;은 비웁니다. weekly는 +7일, daily는 +1일입니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  오늘 할일 뷰
&lt;/h3&gt;

&lt;p&gt;"오늘 할일"이라고 말하면 Python 스크립트가 전체 파일을 스캔합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📅 오늘 할일 (2026-02-07)

📋 오늘 (2개)
  ⬜ [P1] 블로그_글쓰기 (2026-02-07) 🔄
  ⬜ [P1] 코드_리뷰 (2026-02-07)

⚡ 높은 우선순위 미배정 (1개)
  ⬜ [P1] 시스템_개선
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;기한 지난 미완료 태스크, 날짜 없는 P0/P1 태스크도 함께 표시합니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  외부 연동
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;연동 대상&lt;/th&gt;
&lt;th&gt;방식&lt;/th&gt;
&lt;th&gt;방향&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;외부 태스크 도구&lt;/td&gt;
&lt;td&gt;MCP 서버&lt;/td&gt;
&lt;td&gt;양방향 동기화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;캘린더&lt;/td&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;캘린더 → 태스크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git&lt;/td&gt;
&lt;td&gt;git CLI&lt;/td&gt;
&lt;td&gt;변경사항 자동 커밋/푸시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;소스 오브 트루스는 로컬 마크다운 파일입니다. 외부 도구의 이슈든 캘린더 일정이든 &lt;code&gt;index.md&lt;/code&gt;로 수렴합니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  구조 요약
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;사용자 ("시작", "완료", "오늘 할일")
  ↓
Claude Code Hook (키워드 감지)
  ↓
Skill (Python 스크립트 실행)
  ↓
마크다운 파일 (frontmatter 업데이트)
  ↓
Git (버전 관리, 푸시)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;태스크의 라이프사이클:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;생성 → 날짜/우선순위 배정 → 포커스 시작 → 완료 → (반복이면) 다음 태스크 자동 생성
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>productivity</category>
      <category>claudecode</category>
      <category>markdown</category>
      <category>taskmanagement</category>
    </item>
    <item>
      <title>Claude Code Custom Agent 설계 여정: Agent 중심에서 Task 중심으로</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Sun, 01 Feb 2026 03:05:51 +0000</pubDate>
      <link>https://forem.com/wes5510/claude-code-custom-agent-seolgye-yeojeong-agent-jungsimeseo-task-jungsimeuro-4954</link>
      <guid>https://forem.com/wes5510/claude-code-custom-agent-seolgye-yeojeong-agent-jungsimeseo-task-jungsimeuro-4954</guid>
      <description>&lt;h2&gt;
  
  
  들어가며
&lt;/h2&gt;

&lt;p&gt;Design Doc 작성을 자동화하고 싶었다.&lt;/p&gt;

&lt;p&gt;디자인 시안을 확인하고, 기획서를 읽고, 코드베이스를 파악해서 초안을 뽑아주는 것. 그게 &lt;code&gt;/design-doc&lt;/code&gt; Command의 목표였다.&lt;/p&gt;

&lt;p&gt;Command는 금방 만들었다. 문제는 그 다음이었다. Command가 호출할 Custom Agent를 어떻게 설계할 것인가.&lt;/p&gt;

&lt;p&gt;두 번의 설계 전환과 여러 실패 끝에 현재 구조에 도달했다. 이 글은 그 여정이다.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 첫 번째 시도: Agent 중심 설계
&lt;/h2&gt;

&lt;p&gt;처음엔 고정된 4개 Agent를 병렬로 호출하는 구조였다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/design-doc 실행
    ↓
Main Agent가 4개 Agent 병렬 호출
├── code-researcher (코드베이스 분석)
├── doc-researcher (내부 문서 분석)
├── external-researcher (외부 도구 전부)
└── app-researcher (실제 앱 동작 확인)
    ↓
결과 조합 → Design Doc 초안
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;구조가 단순하고 역할이 명확해 보였다.&lt;/p&gt;

&lt;h3&gt;
  
  
  문제 1: 하나가 느리면 전체가 느리다
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;code-researcher&lt;/code&gt;가 Opus 모델로 실행되면서 전체가 지연됐다. 다른 Agent들은 이미 끝났는데 하나를 기다리고 있었다.&lt;/p&gt;

&lt;h3&gt;
  
  
  문제 2: 불필요한 리서치도 실행된다
&lt;/h3&gt;

&lt;p&gt;입력에 포함되지 않은 소스도 조사하려 했다. 필요 없는 일을 하고 있었다.&lt;/p&gt;

&lt;h3&gt;
  
  
  문제 3: Agent 역할이 너무 넓다
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;external-researcher&lt;/code&gt; 하나가 디자인 도구, 문서 도구, 웹을 전부 처리해야 했다. 각 플랫폼의 특성이 다른데, 하나의 프롬프트에 다 담으려니 비대해졌다.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. 전환: Task 중심 설계
&lt;/h2&gt;

&lt;p&gt;Agent 단위가 아니라 Task 단위로 생각을 바꿨다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;입력: "A 기능 개선. 디자인: ..., 기획서: ..."
    ↓
Main Agent가 입력 분석 → 필요한 Task 도출
    ↓
Research Tasks (필요한 것만):
├── design-review
├── feature-spec
└── code-module-analysis
    ↓
Design Task → Write Task → 완료
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;핵심 변화&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Task가 작고 구체적&lt;/li&gt;
&lt;li&gt;필요한 Task만 선택적으로 실행&lt;/li&gt;
&lt;li&gt;Agent는 Task 수행을 위한 도구&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. external-researcher 분리
&lt;/h2&gt;

&lt;p&gt;Task 중심으로 바꾸면서, &lt;code&gt;external-researcher&lt;/code&gt;도 분리했다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;external-researcher (1개)
    ↓ 분리
design-researcher (디자인 도구)
docs-researcher (문서 도구)
web-researcher (웹)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  분리 이유
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;각 소스의 특성이 다르다&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;디자인 도구: 빠른 분석 vs 상세 분석 전략 필요&lt;/li&gt;
&lt;li&gt;문서 도구: 인증 방식, 페이지 구조 파악 방식 다름&lt;/li&gt;
&lt;li&gt;웹: MCP가 아닌 기본 도구 사용&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;실패 처리가 다르다&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;디자인 도구 권한 없음 → 공유 설정 확인 요청&lt;/li&gt;
&lt;li&gt;문서 도구 접근 불가 → 토큰 확인 요청&lt;/li&gt;
&lt;li&gt;웹 타임아웃 → 다른 URL 시도&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;URL 패턴으로 쉽게 결정 가능&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;디자인 도구 URL → design-researcher&lt;/li&gt;
&lt;li&gt;문서 도구 URL → docs-researcher&lt;/li&gt;
&lt;li&gt;그 외 → web-researcher&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. 상태 태그 패턴
&lt;/h2&gt;

&lt;p&gt;분리하고 나니 새로운 문제가 생겼다. Agent가 실패했을 때 어떻게 알릴 것인가.&lt;/p&gt;

&lt;h3&gt;
  
  
  문제 상황
&lt;/h3&gt;

&lt;p&gt;MCP 연결이 끊기거나 권한이 없을 때, Agent가 이렇게 반응했다:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"접근할 수 없습니다."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;그리고 끝. Command는 이게 재시도 가능한 건지, 포기해야 하는 건지 알 수 없었다. 모든 외부 연동에서 같은 문제가 있었다.&lt;/p&gt;

&lt;h3&gt;
  
  
  해결: 상태 태그
&lt;/h3&gt;

&lt;p&gt;Agent가 결과물 최상단에 상태를 명시하게 했다:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;[BLOCKED:mcp-disconnected]

&lt;span class="gu"&gt;## 분석 대상&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; URL: https://...

&lt;span class="gu"&gt;## 실패 사유&lt;/span&gt;
MCP 연결이 끊어졌습니다. 연결 확인 후 재시도해주세요.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상태&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;Command 후처리&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[SUCCESS]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;정상 완료&lt;/td&gt;
&lt;td&gt;다음 단계 진행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[BLOCKED:reason]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사용자 조건 필요&lt;/td&gt;
&lt;td&gt;사용자에게 전달, 대기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[PARTIAL]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;부분 완료&lt;/td&gt;
&lt;td&gt;결과 활용, 미완료 명시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[FAILED:reason]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;복구 불가&lt;/td&gt;
&lt;td&gt;사용자에게 알림&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Blocked 사유 코드&lt;/strong&gt;: &lt;code&gt;auth-error&lt;/code&gt;, &lt;code&gt;permission-denied&lt;/code&gt;, &lt;code&gt;invalid-input&lt;/code&gt;, &lt;code&gt;mcp-disconnected&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;핵심 규칙&lt;/strong&gt;: &lt;code&gt;[BLOCKED:*]&lt;/code&gt;는 Agent가 임의로 스킵하지 않는다. 반드시 호출자에게 전달.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. 책임 분리 원칙
&lt;/h2&gt;

&lt;p&gt;상태 태그를 설계하면서 정리한 원칙이다. Agent는 "누가 호출하는지" 몰라야 한다.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구성 요소&lt;/th&gt;
&lt;th&gt;책임&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Output 형식 정의, 실행 로직, Quality Gate, 상태 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Command&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;워크플로우 정의, Agent 호출 순서, 경로 지정, 결과 조합, 상태별 후처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Agent가 하지 않는 것&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;저장 경로 결정 (호출자가 지정)&lt;/li&gt;
&lt;li&gt;"Command"라는 개념 언급 (호출자 독립적)&lt;/li&gt;
&lt;li&gt;다음 단계 결정 (상태만 반환)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;이유&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent는 Command가 호출할 수도, 사용자가 직접 호출할 수도, 다른 Agent가 호출할 수도 있다&lt;/li&gt;
&lt;li&gt;호출자가 누구든 동일하게 동작해야 한다&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. 커뮤니티 리서치: 6-Section 구조
&lt;/h2&gt;

&lt;p&gt;여기까지 만들고 나서 다른 사람들은 어떻게 하는지 궁금해졌다.&lt;/p&gt;

&lt;p&gt;wshobson/agents(26k⭐), VoltAgent(6k⭐) 등을 조사했다. 공통된 구조가 있었다.&lt;/p&gt;

&lt;h3&gt;
  
  
  커뮤니티 6-Section
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;섹션&lt;/th&gt;
&lt;th&gt;목적&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Role Definition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;에이전트 정체성/전문성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expertise Areas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;기술 스택 나열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Workflow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;단계별 실행 절차&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Communication Protocol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;에이전트 간 통신 규약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output Format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;결과물 형식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quality Checklist&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;완료 전 검증 항목&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;정리된 구조가 있다는 게 좋았다. 하지만 내 상황과 맞지 않는 부분도 있었다.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. 6-Section에서 부족했던 것
&lt;/h2&gt;

&lt;h3&gt;
  
  
  입력 스펙이 없다
&lt;/h3&gt;

&lt;p&gt;"이 Agent가 뭘 받아야 하는지"가 없다. URL만? URL + 질문? 분석 깊이 옵션?&lt;/p&gt;

&lt;h3&gt;
  
  
  실패 케이스가 없다
&lt;/h3&gt;

&lt;p&gt;외부 서비스 연동 Agent에게 실패 처리는 필수다. MCP는 끊기고, API는 타임아웃 나고, 권한은 없다.&lt;/p&gt;

&lt;p&gt;Quality Checklist는 "잘 됐을 때" 검증이다. "안 됐을 때" 대응이 없다.&lt;/p&gt;

&lt;h3&gt;
  
  
  Communication Protocol은 필요했다
&lt;/h3&gt;

&lt;p&gt;반대로, 6-Section에 있는데 내 구조에 없는 것도 있었다.&lt;/p&gt;

&lt;p&gt;지금 내 상태 태그는 단방향이다. Agent → Command로 상태 전달만 한다.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. 최종 구조: 8-Section
&lt;/h2&gt;

&lt;p&gt;6-Section을 참고하고, 내 경험을 더해서 정리한 구조다.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;섹션&lt;/th&gt;
&lt;th&gt;목적&lt;/th&gt;
&lt;th&gt;비고&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;YAML Frontmatter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;name, description, tools, model&lt;/td&gt;
&lt;td&gt;description이 자동 위임 판단에 사용됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;역할 정의&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;한 줄로 핵심만&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;입력&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;필수/선택 파라미터&lt;/td&gt;
&lt;td&gt;6-Section에 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;실행&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;단계별 절차&lt;/td&gt;
&lt;td&gt;도구가 많으면 전략 포함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;상태 태그 + 결과 템플릿&lt;/td&gt;
&lt;td&gt;상태 태그 최상단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quality Gate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;자가 검증 체크리스트&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;실패 시&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;상황별 상태 코드와 대응&lt;/td&gt;
&lt;td&gt;6-Section에 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;주의 사항&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;자주 하는 실수 방지&lt;/td&gt;
&lt;td&gt;6-Section에 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  6-Section과 비교
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;6-Section&lt;/th&gt;
&lt;th&gt;8-Section&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Role Definition&lt;/td&gt;
&lt;td&gt;역할 정의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expertise Areas&lt;/td&gt;
&lt;td&gt;❌ (전문 Agent엔 불필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workflow&lt;/td&gt;
&lt;td&gt;실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Communication Protocol&lt;/td&gt;
&lt;td&gt;❌ (개선 예정)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output Format&lt;/td&gt;
&lt;td&gt;Output (상태 태그 추가)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quality Checklist&lt;/td&gt;
&lt;td&gt;Quality Gate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;입력&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;실패 시&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;주의 사항&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  9. 개선 포인트: Communication Protocol
&lt;/h2&gt;

&lt;p&gt;아직 구현 안 한 게 하나 있다. Communication Protocol이다.&lt;/p&gt;

&lt;p&gt;현재는 상태 태그로 단방향 반환만 한다:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Command → Agent → [SUCCESS] 결과
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;향후 구조화된 프로토콜을 추가하면, 더 유연한 통신이 가능해질 것이다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;상태 보고에 메타 정보 추가&lt;/li&gt;
&lt;li&gt;Agent 간 위임 요청&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;다음 개선 과제로 남겨둔다.&lt;/p&gt;




&lt;h2&gt;
  
  
  마무리
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;배운 것들&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agent 중심보다 Task 중심&lt;/strong&gt;: Agent 단위로 생각하면 역할이 비대해진다. Task 단위로 쪼개라.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;범용보다 전문화&lt;/strong&gt;: 플랫폼 특성이 다르면 분리하라.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;상태 태그로 소통하라&lt;/strong&gt;: Agent가 막히면 상태로 알려야 한다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;책임을 분리하라&lt;/strong&gt;: Agent는 상태만 반환, 후처리는 호출자가.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;입력과 실패를 설계하라&lt;/strong&gt;: 6-Section엔 없지만, 외부 연동 Agent엔 필수다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;커뮤니티를 참고하되 맹신하지 마라&lt;/strong&gt;: 내 상황에 맞게 조정하라.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>agentdesign</category>
      <category>devproductivity</category>
    </item>
    <item>
      <title>Electron MAS 빌드에서 HDR 비디오가 화면을 먹통으로 만드는 이유</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Sat, 24 Jan 2026 03:05:16 +0000</pubDate>
      <link>https://forem.com/wes5510/electron-mas-bildeueseo-hdr-bidioga-hwamyeoneul-meogtongeuro-mandeuneun-iyu-3o1a</link>
      <guid>https://forem.com/wes5510/electron-mas-bildeueseo-hdr-bidioga-hwamyeoneul-meogtongeuro-mandeuneun-iyu-3o1a</guid>
      <description>&lt;h2&gt;
  
  
  문제 상황
&lt;/h2&gt;

&lt;p&gt;Electron 앱의 Mac App Store(MAS) 빌드에서 특정 비디오를 재생하면 전체 창이 먹통이 된다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;증상:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; 태그로 HDR 비디오 재생 시 전체 윈도우 렌더링 실패&lt;/li&gt;
&lt;li&gt;비디오 영역만이 아니라 앱 전체가 하얗게 변함&lt;/li&gt;
&lt;li&gt;오디오는 정상 재생&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;재현 조건:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MAS 빌드 (&lt;code&gt;com.apple.security.app-sandbox&lt;/code&gt; entitlement 적용)&lt;/li&gt;
&lt;li&gt;하드웨어 가속 활성화&lt;/li&gt;
&lt;li&gt;10-bit HDR 콘텐츠 (BT.2020 색공간, HEVC/AV1 코덱)&lt;/li&gt;
&lt;li&gt;iPhone으로 촬영한 HDR 비디오가 대표적인 트리거&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;정상 동작하는 경우:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;로컬 개발 환경 (샌드박스 미적용)&lt;/li&gt;
&lt;li&gt;Windows 빌드&lt;/li&gt;
&lt;li&gt;SDR 비디오&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  원인: Electron #47947
&lt;/h2&gt;

&lt;p&gt;이 문제는 &lt;a href="https://github.com/electron/electron/issues/47947" rel="noopener noreferrer"&gt;Electron Issue #47947&lt;/a&gt;에 문서화된 regression이다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;버전 정보:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Electron 27.3.11: 정상 동작&lt;/li&gt;
&lt;li&gt;Electron 28.0.0 ~ 28.1.1: 앱 실행 자체가 크래시&lt;/li&gt;
&lt;li&gt;Electron 28.1.2 이후: 화면 먹통 (현재까지 미해결)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;왜 MAS 샌드박스에서만 발생하는가?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;중요한 점: 샌드박스가 HDR 렌더링 자체를 차단하는 게 아니다. 문제는 GPU 컨텍스트 손실 후 &lt;strong&gt;복구 과정&lt;/strong&gt;에서 발생한다.&lt;/p&gt;

&lt;p&gt;HDR 비디오는 SDR과 다르게 디코딩 후 추가 작업이 필요하다:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;색역 변환 (BT.2020 → 디스플레이 색공간)&lt;/li&gt;
&lt;li&gt;톤 매핑 (PQ/HLG 곡선 → SDR 밝기 범위)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;실제 실패 체인:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;HDR 비디오 디코딩 → GPU가 high-bitdepth 프레임 생성&lt;/li&gt;
&lt;li&gt;GPU 컴포지터가 텍스처 합성 (여기까지는 정상 동작)&lt;/li&gt;
&lt;li&gt;리소스 압박 또는 드라이버 버그로 &lt;strong&gt;GPU 컨텍스트 손실&lt;/strong&gt; 발생&lt;/li&gt;
&lt;li&gt;드라이버가 복구 시도 → 권한이 필요한 연산 요청&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;샌드박스가 복구 연산 차단&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;복구 실패 → 렌더링 중단&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chromium bug &lt;a href="https://crbug.com/893177" rel="noopener noreferrer"&gt;crbug.com/893177&lt;/a&gt;에 문서화된 내용: "일부 드라이버는 OUT_OF_MEM이나 컨텍스트 손실 후 복구하지 못한다." 이 경우 &lt;code&gt;exit_on_context_lost&lt;/code&gt; 옵션으로 GPU 프로세스를 재시작하는 워크어라운드가 있지만, MAS 환경에서는 이마저도 제대로 동작하지 않는 것으로 보인다.&lt;/p&gt;

&lt;p&gt;Apple의 네이티브 AVFoundation/EDR 프레임워크는 샌드박스 내에서 정상 동작한다. 문제는 Chromium의 GPU 복구 메커니즘이 MAS 샌드박스와 호환되지 않는다는 점이다.&lt;/p&gt;




&lt;h2&gt;
  
  
  시도한 해결책들
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;--disable-gpu-compositing&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendSwitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disable-gpu-compositing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;결과:&lt;/strong&gt; 동작함&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;문제:&lt;/strong&gt; GPU 컴포지팅을 완전히 끄고 CPU로 화면을 합성한다. 모든 레이어(UI, 비디오, 웹 콘텐츠)를 CPU가 처리하므로:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;스크롤, 애니메이션 성능 저하&lt;/li&gt;
&lt;li&gt;배터리 소모 증가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;화상 회의 기능에서 화면 끊김 제보 발생&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;하드웨어 가속을 쓰는 의미가 없어진다.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;--disable-features=UseHDRTransferFunction&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendSwitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disable-features&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UseHDRTransferFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HDR 전송 함수(PQ/HLG)를 비활성화하고 SDR처럼 처리한다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;결과:&lt;/strong&gt; 효과 없음&lt;/p&gt;

&lt;p&gt;추가로 발견한 문제: 이 플래그는 모든 플랫폼에 적용된다. Mac에서만 발생하는 버그인데 Windows/Linux HDR 기능까지 끄게 된다. 적용하려면 플랫폼 분기 필요:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;darwin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendSwitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disable-features&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UseHDRTransferFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;참고로 이 플래그에 대해 coderabbitai 봇이 "ChromeOS 전용이라 효과 없다"고 분석했는데, 이건 틀린 정보다. Linux Wayland 환경에서도 HDR 활성화에 사용되는 크로스 플랫폼 플래그다.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;--force-color-profile=srgb&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendSwitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;force-color-profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;srgb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;앱 전체 색공간을 sRGB로 강제 고정해서 색공간 전환 자체를 막는다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;결과:&lt;/strong&gt; 효과 없음&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;--disable-accelerated-video-decode&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendSwitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disable-accelerated-video-decode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GPU 비디오 디코딩만 끄고 CPU로 디코딩. 디코딩된 프레임은 여전히 GPU로 렌더링.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;결과:&lt;/strong&gt; 테스트 필요&lt;/p&gt;

&lt;p&gt;문제가 디코딩 단계인지, 디코딩 이후 렌더링 단계인지에 따라 효과가 달라질 것이다.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. GPU 백엔드 변경
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendSwitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use-angle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// 또는&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendSwitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use-gl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;angle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;결과:&lt;/strong&gt; 효과 없음&lt;/p&gt;




&lt;h2&gt;
  
  
  다른 옵션들
&lt;/h2&gt;

&lt;p&gt;아직 테스트하지 않은 방법들:&lt;/p&gt;

&lt;h3&gt;
  
  
  HDR 감지 후 Canvas 폴백
&lt;/h3&gt;

&lt;p&gt;HDR 콘텐츠를 감지해서 &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; 대신 Canvas나 WebGL로 렌더링하는 방식. 구현 복잡도가 높다.&lt;/p&gt;

&lt;h3&gt;
  
  
  FFmpeg 기반 우회
&lt;/h3&gt;

&lt;p&gt;LosslessCut이 사용하는 방식. Chromium의 비디오 스택을 완전히 우회하고 FFmpeg로 처리한다. 영상 편집 앱에는 적합하지만 범용 앱에는 과하다.&lt;/p&gt;

&lt;h3&gt;
  
  
  Electron 다운그레이드
&lt;/h3&gt;

&lt;p&gt;27.3.11로 내리면 해결되지만, 2년 전 버전이라 보안 패치와 새 기능을 포기해야 한다.&lt;/p&gt;




&lt;h2&gt;
  
  
  대형 앱들의 선택
&lt;/h2&gt;

&lt;p&gt;같은 Electron 기반이라도 MAS 배포 여부는 다르다:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;앱&lt;/th&gt;
&lt;th&gt;MAS 배포&lt;/th&gt;
&lt;th&gt;비디오 아키텍처&lt;/th&gt;
&lt;th&gt;전략&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Discord&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Electron (darwin 빌드)&lt;/td&gt;
&lt;td&gt;직접 배포&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Electron MAS&lt;/td&gt;
&lt;td&gt;화면 공유 버그 등 제한 수용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  현재 상태
&lt;/h2&gt;

&lt;p&gt;아직 완벽한 해결책을 찾지 못했다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;남은 선택지:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;disable-gpu-compositing&lt;/code&gt; 수용&lt;/strong&gt;: 화상 회의 품질 저하를 감수&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HDR 감지 + 사용자 알림&lt;/strong&gt;: HDR 비디오 감지 시 "이 비디오는 재생할 수 없습니다" 표시&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MAS 배포 포기&lt;/strong&gt;: Darwin 빌드로 직접 배포&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electron 이슈 해결 대기&lt;/strong&gt;: 언제 될지 모름&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;MAS 배포를 쉽게 포기하기 어렵고, 화상 회의 기능도 중요해서 GPU 컴포지팅을 끄기도 어렵다. 현재로서는 HDR 감지 후 graceful degradation 하는 방향을 검토 중이다.&lt;/p&gt;




&lt;h2&gt;
  
  
  참고 자료
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/electron/electron/issues/47947" rel="noopener noreferrer"&gt;Electron Issue #47947&lt;/a&gt; - MAS HDR 렌더링 버그&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/electron/electron/issues/41927" rel="noopener noreferrer"&gt;Electron Issue #41927&lt;/a&gt; - MAS GPU 프로세스 실패&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mifi/electron-mas-video-bug" rel="noopener noreferrer"&gt;mifi/electron-mas-video-bug&lt;/a&gt; - 최소 재현 레포&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://crbug.com/893177" rel="noopener noreferrer"&gt;Chromium Bug crbug.com/893177&lt;/a&gt; - GPU 컨텍스트 손실 후 복구 실패&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mifi/lossless-cut/issues/2392" rel="noopener noreferrer"&gt;LosslessCut Issue #2392&lt;/a&gt; - FFmpeg 우회 방식 논의&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;이 글은 2026년 1월 기준 Electron 37.x 버전에서의 경험을 바탕으로 작성되었습니다. 이후 버전에서 수정될 수 있습니다.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>electron</category>
      <category>macos</category>
      <category>hdr</category>
      <category>troubleshooting</category>
    </item>
    <item>
      <title>TypeScript --erasableSyntaxOnly 플래그, 왜 생겼고 언제 쓰나</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Sat, 10 Jan 2026 01:04:39 +0000</pubDate>
      <link>https://forem.com/wes5510/typescript-erasablesyntaxonly-peulraegeu-wae-saenggyeossgo-eonje-sseuna-24gm</link>
      <guid>https://forem.com/wes5510/typescript-erasablesyntaxonly-peulraegeu-wae-saenggyeossgo-eonje-sseuna-24gm</guid>
      <description>&lt;p&gt;TypeScript 5.8에 &lt;code&gt;--erasableSyntaxOnly&lt;/code&gt;라는 컴파일러 플래그가 추가됐다. 이름만 봐선 뭔가 싶은데, Node.js 네이티브 TS 지원, TC39 표준화 움직임과 맞물려 있는 플래그다.&lt;/p&gt;




&lt;h2&gt;
  
  
  새 플래그가 하는 일
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tsconfig.json&lt;/code&gt;에 이 옵션을 켜면, 특정 TypeScript 문법을 사용할 때 컴파일 에러가 난다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"erasableSyntaxOnly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;금지되는 것들:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;enum&lt;/code&gt; 선언&lt;/li&gt;
&lt;li&gt;런타임 코드가 있는 &lt;code&gt;namespace&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Parameter properties (&lt;code&gt;constructor(public x: number)&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;import =&lt;/code&gt; alias&lt;/li&gt;
&lt;li&gt;Old-style type assertion (&lt;code&gt;&amp;lt;Type&amp;gt;value&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;왜 이것들만 콕 집어서 막는 걸까?&lt;/p&gt;




&lt;h2&gt;
  
  
  "Erasable"이 뭔가
&lt;/h2&gt;

&lt;p&gt;Erasable은 "지울 수 있는"이라는 뜻이다. TypeScript 문법 중에서 &lt;strong&gt;그냥 지워버려도 JavaScript 실행에 아무 영향이 없는 것&lt;/strong&gt;을 erasable하다고 한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Erasable - 타입만 지우면 됨&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 타입 지운 결과&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;반면 enum은 다르다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Non-erasable - 지우면 안 됨&lt;/span&gt;
&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Inactive&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Active&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;enum을 그냥 지워버리면? &lt;code&gt;Status&lt;/code&gt;가 없어지니까 런타임 에러다. TypeScript 컴파일러가 이걸 JavaScript 객체로 변환해줘야 돌아간다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 컴파일 결과&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Inactive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Inactive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이게 "erasable하지 않다"의 의미다. 단순히 타입을 지우는 게 아니라 &lt;strong&gt;JavaScript 코드를 생성&lt;/strong&gt;해야 한다.&lt;/p&gt;




&lt;h2&gt;
  
  
  왜 이게 지금 나왔나
&lt;/h2&gt;

&lt;p&gt;두 가지 움직임 때문이다.&lt;/p&gt;

&lt;h3&gt;
  
  
  1️⃣ Node.js의 네이티브 TypeScript 지원
&lt;/h3&gt;

&lt;p&gt;Node.js 23.6부터 &lt;code&gt;--experimental-strip-types&lt;/code&gt; 플래그로 TypeScript 파일을 직접 실행할 수 있다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-strip-types&lt;/span&gt; app.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;근데 이름에서 알 수 있듯이 "strip types", 타입을 벗겨내기만 한다. enum이나 namespace처럼 JavaScript 변환이 필요한 건 지원 안 한다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Node.js has unflagged a mode called &lt;code&gt;--experimental-strip-types&lt;/code&gt; which requires that any TypeScript-specific syntax cannot have runtime semantics.&lt;br&gt;
— &lt;em&gt;TypeScript 공식 문서&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2️⃣ TC39 "Types as Comments" 제안
&lt;/h3&gt;

&lt;p&gt;더 큰 그림이 있다. TC39(JavaScript 표준을 만드는 곳)에서 "Types as Comments"라는 제안이 Stage 1로 진행 중이다. TypeScript PM인 Daniel Rosenwasser가 직접 champion으로 참여하고 있다.&lt;/p&gt;

&lt;p&gt;핵심 아이디어는 &lt;strong&gt;브라우저가 TypeScript 타입 문법을 주석처럼 무시&lt;/strong&gt;하게 만드는 것이다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 미래의 JavaScript?&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// 브라우저: "a: number? 몰라, 무시하고 실행할게"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이게 되면 빌드 스텝 없이 TypeScript를 바로 실행할 수 있다. 대신 조건이 있다. 타입은 "무시"할 수 있어야 한다. enum처럼 실제 JavaScript 코드를 만들어내는 문법은 scope 밖이다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Enums, namespaces, and parameter properties would be out of scope for this proposal since they have observable runtime behavior.&lt;br&gt;
— &lt;em&gt;Daniel Rosenwasser, 2022&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  그래서 enum은 deprecated인가?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;아니다.&lt;/strong&gt; TypeScript 팀은 enum을 deprecated로 선언한 적 없다.&lt;/p&gt;

&lt;p&gt;2020년에 "Deprecate const enum"이라는 GitHub 이슈(&lt;a href="https://github.com/microsoft/TypeScript/issues/41641" rel="noopener noreferrer"&gt;#41641&lt;/a&gt;)가 올라왔는데, TypeScript 팀이 &lt;strong&gt;Declined(거절)&lt;/strong&gt; 처리했다. 하위 호환성을 깨뜨릴 생각이 없다는 뜻이다.&lt;/p&gt;

&lt;p&gt;다만 공식 핸드북에서는 이런 뉘앙스를 준다:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In modern TypeScript, you may not need an enum when an object with &lt;code&gt;as const&lt;/code&gt; could suffice... The biggest argument in favour of this format over TypeScript's enum is that it keeps your codebase aligned with the state of JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;"쓰지 마"가 아니라 "JavaScript 방향에 맞추려면 대안 고려해봐"다.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  실무에서 어떻게 하면 되나
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;--erasableSyntaxOnly&lt;/code&gt;를 당장 켜야 하는 건 아니다. 기존 코드 다 고쳐야 하니까. 하지만 새 코드에서는 이런 대안을 쓰는 게 미래 지향적이다.&lt;/p&gt;

&lt;h3&gt;
  
  
  enum → as const
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Inactive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inactive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Inactive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inactive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  namespace → ES modules
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nx"&gt;Utils&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After (utils.ts)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  parameter properties → 명시적 할당
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  정리
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;질문&lt;/th&gt;
&lt;th&gt;답변&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;enum deprecated?&lt;/td&gt;
&lt;td&gt;❌ 아님&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;새 코드에서 권장?&lt;/td&gt;
&lt;td&gt;⚠️ 대안 고려 권장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;제거 계획?&lt;/td&gt;
&lt;td&gt;❌ 없음 (하위 호환성)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;TypeScript 팀은 기존 코드를 깨뜨리지 않으면서, opt-in 플래그로 점진적 전환을 지원하는 전략을 택했다. &lt;a href="https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals" rel="noopener noreferrer"&gt;TypeScript Design Goals&lt;/a&gt; 문서를 보면 이 방향성이 처음부터 있었다:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Align with current and future ECMAScript proposals"&lt;br&gt;
"Encourage programming patterns that do not require run-time metadata"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;enum과 namespace는 TypeScript 초창기(2012년)에 만들어졌다. 그때는 JavaScript에 이런 게 없었으니까. 지금은 &lt;code&gt;as const&lt;/code&gt;, ES modules 같은 표준 대안이 생겼고, TypeScript는 그쪽으로 무게중심을 옮기고 있다.&lt;/p&gt;

&lt;p&gt;"enum은 실수였다"는 커뮤니티 밈과 달리, 실제로는 &lt;strong&gt;실용적 공존&lt;/strong&gt;을 선택한 것으로 보인다. 다만 &lt;strong&gt;새로운 non-erasable 문법은 더 이상 추가하지 않겠다&lt;/strong&gt;는 건 분명해 보인다.&lt;/p&gt;




&lt;h2&gt;
  
  
  참고 자료
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html" rel="noopener noreferrer"&gt;TypeScript 5.8 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devblogs.microsoft.com/typescript/a-proposal-for-type-syntax-in-javascript/" rel="noopener noreferrer"&gt;A Proposal For Type Syntax in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tc39/proposal-type-annotations" rel="noopener noreferrer"&gt;TC39 proposal-type-annotations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/TypeScript/pull/61011" rel="noopener noreferrer"&gt;--erasableSyntaxOnly PR #61011&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>개발 조직에서 시니어의 진짜 역할: 성장 시스템 설계</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Sat, 03 Jan 2026 10:33:29 +0000</pubDate>
      <link>https://forem.com/wes5510/gaebal-jojigeseo-sinieoyi-jinjja-yeoghal-seongjang-siseutem-seolgye-3f7b</link>
      <guid>https://forem.com/wes5510/gaebal-jojigeseo-sinieoyi-jinjja-yeoghal-seongjang-siseutem-seolgye-3f7b</guid>
      <description>&lt;h2&gt;
  
  
  시니어가 있으면 성장이 보장될까?
&lt;/h2&gt;

&lt;p&gt;"우리 팀에 시니어가 있어요."&lt;/p&gt;

&lt;p&gt;채용 공고나 팀 소개에서 흔히 보는 문구입니다. 마치 시니어의 존재 자체가 팀의 기술력과 성장 가능성을 보장하는 것처럼요.&lt;/p&gt;

&lt;p&gt;팀을 리드하면서 깨달은 게 있습니다. &lt;strong&gt;시니어의 존재 자체보다 중요한 게 있습니다.&lt;/strong&gt; 그 시니어가 성장을 돕는 시스템을 만들고 운영하느냐입니다.&lt;/p&gt;

&lt;p&gt;이 글에서는 제가 팀을 리드하면서 고민하고 시도해온 것들을 공유합니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  팀을 리드하면서 했던 고민들
&lt;/h2&gt;

&lt;p&gt;팀을 리드하기 시작하면서 스스로에게 자주 질문했습니다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;나는 팀원과 충분히 대화하고 있는가?&lt;/li&gt;
&lt;li&gt;내 경험과 노하우를 체계적으로 전달하고 있는가?&lt;/li&gt;
&lt;li&gt;피드백이 구체적인가, 아니면 "잘하고 있어"로 끝나는가?&lt;/li&gt;
&lt;li&gt;팀원이 어디까지 성장해야 하는지 명확히 알고 있는가?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 질문들에 자신 있게 "예"라고 답하기 어려웠습니다. 시니어로서 어려운 문제를 푸는 건 자신 있었지만, 팀원의 성장을 돕는 건 별개의 역량이었습니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  시니어의 진짜 역할: 성장 시스템을 설계하고 운영하는 것
&lt;/h2&gt;

&lt;p&gt;시니어의 역할은 단순히 "어려운 문제를 푸는 사람"이 아닙니다. 물론 그것도 중요하지만, 더 중요한 건 &lt;strong&gt;팀원이 성장할 수 있는 구조를 만드는 것&lt;/strong&gt;입니다.&lt;/p&gt;

&lt;p&gt;제가 실제로 운영하고 있는 시스템의 뼈대를 공유합니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. 레벨별 기대 역량을 명확히 정의한다
&lt;/h3&gt;

&lt;p&gt;"시니어는 이 정도 해야 해", "주니어는 이 정도면 돼"라는 감각적인 기준만으로는 부족하다고 느꼈습니다. 사람마다 기준이 다르고, 평가가 주관적이 될 수 있기 때문입니다.&lt;/p&gt;

&lt;p&gt;그래서 저는 레벨별로 기대하는 역량을 명시적으로 정의해뒀습니다. 예를 들면:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;기술적 역량&lt;/strong&gt;: 어떤 범위의 문제를 독립적으로 해결할 수 있는가&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;영향력 범위&lt;/strong&gt;: 본인 업무만 vs 팀 전체 vs 조직 전체&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;커뮤니케이션&lt;/strong&gt;: 어떤 수준의 이해관계자와 소통할 수 있는가&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;자기주도성&lt;/strong&gt;: 업무를 할당받아서 하는가 vs 스스로 찾아서 하는가&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이게 있으면 팀원도 "내가 다음 레벨로 가려면 뭘 해야 하지?"를 스스로 파악할 수 있습니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. 1:1에서 구체적인 피드백을 주고받는다
&lt;/h3&gt;

&lt;p&gt;저는 정기적으로 1:1을 하고 있습니다. "요즘 어때?"로 시작해서 "힘내"로 끝나는 1:1은 아쉽다고 느꼈기 때문입니다.&lt;/p&gt;

&lt;p&gt;저는 1:1에서 두 가지를 다루려고 합니다:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;잘하고 있는 것&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;최근에 뭘 잘했는지 구체적 사례와 함께 이야기합니다&lt;/li&gt;
&lt;li&gt;왜 그게 좋았는지, 어떤 영향을 줬는지 설명합니다&lt;/li&gt;
&lt;li&gt;이걸 통해 강점을 인식하고 더 발전시킬 수 있습니다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;개선이 필요한 것&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;어떤 부분이 부족한지 솔직하게 이야기합니다&lt;/li&gt;
&lt;li&gt;단순히 "이게 부족해"가 아니라 "이렇게 해보면 어떨까"까지 제안합니다&lt;/li&gt;
&lt;li&gt;다음 1:1까지 해볼 수 있는 작은 액션 아이템을 함께 정합니다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;불편할 수 있습니다. 하지만 저는 모호한 칭찬보다 구체적인 피드백이 성장에 더 도움이 된다고 생각합니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. 시니어가 지속적으로 모니터링한다
&lt;/h3&gt;

&lt;p&gt;1:1 한 번 하고 끝이 아니라고 생각합니다. 저는 팀원의 성장을 지속적으로 관찰하려고 합니다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;코드 리뷰에서 같은 실수가 반복되는지&lt;/li&gt;
&lt;li&gt;이전에 피드백한 부분이 개선되고 있는지&lt;/li&gt;
&lt;li&gt;새로운 도전을 하고 있는지, 안전지대에만 머무는지&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이걸 바탕으로 다음 1:1에서 다시 피드백하고, 또 관찰하고, 또 피드백합니다. 이 사이클이 반복되면서 성장이 일어납니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  아직 고민 중인 것: 시간 투자 vs 문화
&lt;/h2&gt;

&lt;p&gt;여기까지 읽으면 "그거 다 좋은데, 시니어도 할 일이 많은데 언제 그걸 다 해?"라는 생각이 들 수 있습니다.&lt;/p&gt;

&lt;p&gt;솔직히 말하면, 시간이 많이 듭니다. &lt;/p&gt;

&lt;p&gt;시간 투자 자체는 의미가 있다고 생각합니다. 팀원이 성장하면 복잡한 업무를 나눌 수 있고, 코드 리뷰에서 같은 피드백을 반복하지 않아도 되니까요.&lt;/p&gt;

&lt;p&gt;하지만 시니어 개인의 시간 투자에만 의존하면 지속 가능하지 않다는 생각도 듭니다. 결국 &lt;strong&gt;문화로 녹여서 자연스럽게 돌아가게&lt;/strong&gt; 해야 하지 않을까요?&lt;/p&gt;

&lt;p&gt;예를 들면:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;피드백을 주고받는 게 당연한 분위기&lt;/li&gt;
&lt;li&gt;레벨별 기대치가 팀 전체에 공유되어 있는 상태&lt;/li&gt;
&lt;li&gt;시니어 한 명이 아니라 팀 전체가 서로의 성장을 신경 쓰는 구조&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;어떻게 문화로 만들 수 있을지는 아직 고민 중입니다. 이 부분은 더 시도해보고 나중에 다시 글로 정리해보려 합니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  마무리
&lt;/h2&gt;

&lt;p&gt;다시 처음으로 돌아가서.&lt;/p&gt;

&lt;p&gt;"우리 팀에 시니어가 있어요"라는 말의 진짜 의미는 뭘까요?&lt;/p&gt;

&lt;p&gt;저는 이렇게 생각합니다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;그 시니어가 팀원의 성장에 시간을 투자하고 있는지&lt;/li&gt;
&lt;li&gt;성장을 위한 구조(기대 역량 정의, 피드백 사이클, 모니터링)가 있는지&lt;/li&gt;
&lt;li&gt;그 구조가 실제로 돌아가고 있는지&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이게 갖춰진 팀에서는 주니어도 성장하고, 시니어도 함께 성장할 수 있다고 생각합니다.&lt;/p&gt;

&lt;p&gt;성장은 혼자 하는 게 아닙니다. 하지만 성장할 수 있는 환경은 누군가 의도적으로 만들어야 한다고 생각합니다.&lt;/p&gt;

&lt;p&gt;저는 그게 시니어의 진짜 역할이라고 생각하고, 그렇게 되려고 노력하고 있습니다.&lt;/p&gt;

</description>
      <category>career</category>
      <category>leadership</category>
      <category>teamwork</category>
      <category>growth</category>
    </item>
    <item>
      <title>리액트에서 props가 수정되면 컴포넌트가 리랜더링 될까? 과연?</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Sun, 14 Jan 2024 05:39:16 +0000</pubDate>
      <link>https://forem.com/wes5510/riaegteueseo-propsga-sujeongdoemyeon-keomponeonteuga-riraendeoring-doelgga-gwayeon-3ce0</link>
      <guid>https://forem.com/wes5510/riaegteueseo-propsga-sujeongdoemyeon-keomponeonteuga-riraendeoring-doelgga-gwayeon-3ce0</guid>
      <description>&lt;p&gt;예전에 문득 &lt;a href="https://react.dev/learn/render-and-commit#step-1-trigger-a-render"&gt;React 공식 문서&lt;/a&gt;를 읽다가 흥미로운 내용을 발견했습니다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 1: Trigger a render &lt;br&gt;
There are two reasons for a component to render:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It’s the component’s initial render.&lt;/li&gt;
&lt;li&gt;The component’s (or one of its ancestors’) state has been updated.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;자세히 읽어보니 랜더링 트리거 조건 중 "props가 변경된 경우"에 대한 언급이 없었습니다. 이전까지 "props가 변경되면 컴포넌트가 렌더링된다"라고 생각했었는데, 문서에는 그와 관련된 내용이 없어 궁금증이 생겼습니다. "실제로 prop가 수정되면 컴포넌트가 렌더링되는가?"에 대한 실험을 진행하기로 결정했습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  정의
&lt;/h2&gt;

&lt;p&gt;먼저 "props가 변경되면 컴포넌트가 렌더링된다"라는 가정에서 시작하여, props가 변하는 것과 컴포넌트가 렌더링되는 것 두 가지를 분리하여 고려해봅시다.&lt;/p&gt;

&lt;h2&gt;
  
  
  컴포넌트 렌더링
&lt;/h2&gt;

&lt;p&gt;React 공식 문서에 따르면 컴포넌트 렌더링은 해당 컴포넌트를 호출하는 것입니다. 아래의 예제를 살펴보겠습니다.&lt;/p&gt;

&lt;p&gt;Counter 컴포넌트는 onClick과 count라는 두 개의 props를 가지고 있습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;카운트 올려!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`count : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이 Counter 컴포넌트를 상태값인 count로 가지고 있는 StateCounter 컴포넌트를 살펴보겠습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stateCounterRenderCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;StateCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;stateCounterRenderCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`StateCounter 컴포넌트 렌더링 횟수: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stateCounterRenderCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Counter 컴포넌트의 버튼을 클릭하면 count 상태가 증가하고, StateCounter 컴포넌트의 렌더링 횟수도 증가합니다. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ipS6TDUo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qyyi80vn063g2p3mnq06.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ipS6TDUo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qyyi80vn063g2p3mnq06.gif" alt="run StateCounter" width="640" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;이때 렌더링 횟수가 2번씩 증가하는 이유는 &lt;a href="https://react.dev/reference/react/StrictMode#fixing-bugs-found-by-double-rendering-in-development"&gt;React 공식 문서&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Components breaking this rule behave unpredictably and cause bugs. To help you find accidentally impure code, Strict Mode calls some of your functions (only the ones that should be pure) twice in development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Strict Mode에서 순수 함수인지 디버깅하기 위해 의도적으로 함수를 두 번 호출합니다. 따라서 state를 수정할 때마다 stateCounterRenderCount가 2씩 증가합니다.&lt;/p&gt;
&lt;h2&gt;
  
  
  Props 수정
&lt;/h2&gt;

&lt;p&gt;이제 렌더링에 영향을 미치는 다른 요소들은 배제하고, Counter 컴포넌트를 가지고 있는 PropsCounter 컴포넌트를 살펴보겠습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;propsCounterRenderCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PropsCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;propsCounterRenderCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// count가 실제로 증가하는지 확인하기 위한 콘솔 로그&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`PropsCounter 컴포넌트 렌더링 횟수: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;propsCounterRenderCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Counter 컴포넌트의 버튼을 클릭하여 count를 증가시켜도 콘솔에는 count가 증가하지만 PropsCounter 컴포넌트의 렌더링 횟수는 증가하지 않습니다.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--27BJdz-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vg825rdqeakbec7dvfc9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--27BJdz-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vg825rdqeakbec7dvfc9.gif" alt="run PropsCounter" width="640" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;props가 수정되어도 렌더링이 자동으로 발생하지 않습니다. 렌더링이 트리거되는 순간은 리액트 공식 문서에서 설명한 대로 다음과 같습니다:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;컴포넌트의 초기 렌더링&lt;/li&gt;
&lt;li&gt;컴포넌트(또는 상위 컴포넌트 중 하나)의 상태가 변경되었을 때&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;즉, props가 변경되더라도 렌더링은 상태의 변화에 의해 주로 발생하며, props의 변경으로 인한 렌더링은 해당 props가 컴포넌트 내부에서 상태로 사용되고 상태가 변경될 때 발생합니다.&lt;/p&gt;

</description>
      <category>react</category>
      <category>rendering</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>P2P 기반 서비스 만들기 전 타 서비스 리서치</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Sun, 07 Jan 2024 11:17:55 +0000</pubDate>
      <link>https://forem.com/wes5510/p2p-giban-seobiseu-mandeulgi-jeon-ta-seobiseu-riseoci-235f</link>
      <guid>https://forem.com/wes5510/p2p-giban-seobiseu-mandeulgi-jeon-ta-seobiseu-riseoci-235f</guid>
      <description>&lt;h2&gt;
  
  
  목적
&lt;/h2&gt;

&lt;p&gt;다수의 사용자가 무료로 이용할 수 있는 SaaS를 개발하고자 하며, 이를 위해 SaaS에서 발생하는 주요 비용인 서버 및 저장소 비용을 최소화하고 중앙 저장소를 제거하고 P2P 기술을 적용하려는 목표를 가지고 있습니다. 현재까지는 P2P 기술에 대한 구체적인 이해가 부족하므로, 이를 보충하고자 타 서비스들의 코드를 분석해보려 합니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  SaaS에서의 비용 구성
&lt;/h3&gt;

&lt;p&gt;보통 서버와 저장소가 큰 비용 부담을 일으킵니다. 사용자 증가에 따라 서버와 저장소 비용이 증가하는 경향이 있어 이를 최소화하고자 합니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  중앙 저장소의 역할
&lt;/h3&gt;

&lt;p&gt;중앙 저장소는 다수의 기기 간에 데이터를 공유하기 위한 필수 요소입니다. P2P를 활용함으로써 중앙 저장소를 우회함으로써 효과적으로 비용을 절감하고자 합니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  분석을 위한 요구사항
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;제가 이해할 수 있는 언어(Java, TypeScript, JavaScript, Rust)를 기반으로 개발되었는가&lt;/li&gt;
&lt;li&gt;지속적인 유지보수가 이루어지고 있는가&lt;/li&gt;
&lt;li&gt;다양한 플랫폼에서 동작 가능한가&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  P2P 기반 오픈 소스 리스트
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AnyType&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2PvwD7aj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/758aj9k3pn5zbgemdie5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2PvwD7aj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/758aj9k3pn5zbgemdie5.png" alt="Image description" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/anyproto/anytype-ts"&gt;Github 링크&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;사용 언어: &lt;u&gt;TypeScript&lt;/u&gt;
&lt;/li&gt;
&lt;li&gt;지원 플랫폼: 데스크탑&lt;/li&gt;
&lt;li&gt;최근 업데이트: &lt;u&gt;1분 전&lt;/u&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Logseq&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hgXMd2hr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/firlodt2n2j0wxdgxua6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hgXMd2hr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/firlodt2n2j0wxdgxua6.png" alt="Image description" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/logseq/logseq"&gt;Github 링크&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;사용 언어: Clojure&lt;/li&gt;
&lt;li&gt;지원 플랫폼: &lt;u&gt;Android, Windows, Linux, iOS, Mac&lt;/u&gt;
&lt;/li&gt;
&lt;li&gt;최근 업데이트: &lt;u&gt;어제&lt;/u&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;EasyNotes&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dypvt2pi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2ohrmm5vxmg27mbi4z9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dypvt2pi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2ohrmm5vxmg27mbi4z9.png" alt="Image description" width="769" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/r57zone/EasyNotes"&gt;Github 링크&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;사용 언어: Pascal&lt;/li&gt;
&lt;li&gt;지원 플랫폼: 윈도우, iOS, Android&lt;/li&gt;
&lt;li&gt;최근 업데이트: 4달 전&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Athens&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EF4Tl6ao--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jezpffku6iiyabitsfg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EF4Tl6ao--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jezpffku6iiyabitsfg9.png" alt="Image description" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/athensresearch/athens"&gt;Github 링크&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;사용 언어: Clojure&lt;/li&gt;
&lt;li&gt;지원 플랫폼: 윈도우, Mac, Linux&lt;/li&gt;
&lt;li&gt;최근 업데이트: &lt;del&gt;2년 전&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Laverna&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ow4fk5zK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kvyedzky2hbnevoi29hy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ow4fk5zK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kvyedzky2hbnevoi29hy.png" alt="Image description" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Laverna/laverna"&gt;Github 링크&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;사용 언어: &lt;u&gt;JavaScript&lt;/u&gt;
&lt;/li&gt;
&lt;li&gt;지원 플랫폼: 웹&lt;/li&gt;
&lt;li&gt;최근 업데이트: &lt;del&gt;7년 전&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;Logseq가 플랫폼 다양성과 높은 사용자 활용도를 자랑하지만, AnyType은 TypeScript로 구성돼 있어 제가 분석하기에 가장 적합한 것으로 판단됩니다. AnyType에 대한 분석 결과가 만족스럽지 않을 경우, Logseq를 참고할 계획입니다. 다음에 AnyType 혹은 Logseq P2P 분석글에서 뵙겠습니다 :)&lt;br&gt;
&lt;strong&gt;혹시 더 좋은 서비스가 있다면 댓글에 남겨주세요!&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>모바일에서 invalid date</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Sat, 19 Feb 2022 08:28:50 +0000</pubDate>
      <link>https://forem.com/wes5510/mobaileseo-invalid-date-oi5</link>
      <guid>https://forem.com/wes5510/mobaileseo-invalid-date-oi5</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;date는 string X, number O&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  본론
&lt;/h2&gt;

&lt;p&gt;배경&lt;/p&gt;

&lt;p&gt;데스크탑 웹에서는 잘 동작하는 date가 모바일에서는 Invaild date 오류를 뿜음&lt;br&gt;
해결 과정&lt;/p&gt;

&lt;p&gt;구글링을 해보니 이런 게 있었음 RangeError: invalid date&lt;br&gt;
오늘의 연, 월, 일을 가져오는 함수에서 yyyy.mm.dd 형식의 string을 date로 만들어주는 게 문제가 됨.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getYearMonthDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFullYear&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;setHours(0, 0, 0, 0)으로 string을 안쓰고 해결함&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getYearMonthDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;new Date 인자값으로는 number를 써야하고 다른 언어와 연동되기 쉽게 DB에는 number(Unix Time Stamp)로 DB에 저장하는 게 좋다.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>date</category>
      <category>errors</category>
    </item>
    <item>
      <title>Yjs + typegoose = yTypegoose</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Sat, 19 Feb 2022 08:26:38 +0000</pubDate>
      <link>https://forem.com/wes5510/yjs-typegoose-ytypegoose-3b74</link>
      <guid>https://forem.com/wes5510/yjs-typegoose-ytypegoose-3b74</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;typegoose와 Yjs를 같이 사용할 수 있는 라이브러리를 만듬(유지보수 안함)&lt;/p&gt;

&lt;h2&gt;
  
  
  본론
&lt;/h2&gt;

&lt;p&gt;처음에 Notion, Roam Research, Workflowy같은 어플리케이션을 만들다가 이전에 긱뉴스에서 본 CRDT 관련 글이 인상깊어서 적용해보기로 했다.&lt;br&gt;
yjs는 그 자체로 CRDT 기능에 충실했고 군더더기 없이 심플했다. 그리고 websocket, indexeddb 등을 지원하기 때문에 레퍼런스가 훌륭하다고 생각했다.&lt;br&gt;
처음에는 y-leveldb와 mongodown 을 섞어서 사용하고 있었지만 오류가 많았다.(이 글을 쓰기 오래전에 만들어서 정확히 생각이 안나지만…^^;) 오류를 고치고 고치고 고치다가 결국 어떻게 돌아가는 지 대략 이해하게 되어서 yTypegoose를 만들어서 사용하게 되었다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;사용 방법
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yTypegoose.createModel('nodes');

const docId = 'node-1';
const doc = new Y.Doc();
await yTypegoose.storeUpdate(docId, Y.encodeStateAsUpdate(doc));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;code&gt;yTypegoose.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;CRDT를 구현하려면 Yjs를 사용하는 걸 추천한다. 사용하기 쉽고 레퍼런스도 많았다. 그리고 Typegoose와 Yjs를 사용하려면 yTypegoose.ts를 참고하는 것도 나쁘지 않다. 아 그리고 지금 유지보수를 안해서 참고만 하는 게 좋을 거 같다.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>yjs</category>
      <category>typegoose</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Jest Cheat Sheet In NodeJS</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Wed, 19 Aug 2020 15:16:00 +0000</pubDate>
      <link>https://forem.com/wes5510/jest-cheat-sheet-in-nodejs-2ppl</link>
      <guid>https://forem.com/wes5510/jest-cheat-sheet-in-nodejs-2ppl</guid>
      <description>&lt;p&gt;Thanks for visiting this post.&lt;br&gt;
All feedbacks on this post are always welcome.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Mocking a function in the module&lt;/p&gt;

&lt;p&gt;When testing the &lt;code&gt;doSomething1()&lt;/code&gt; function, If the &lt;code&gt;doSomething1()&lt;/code&gt; function in &lt;code&gt;ctrl.js&lt;/code&gt; module invokes &lt;code&gt;doSomething2()&lt;/code&gt; in the same module, you can do as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There is &lt;code&gt;jest.fn()&lt;/code&gt;, but if you use &lt;code&gt;jest.spyOn()&lt;/code&gt;, you can even check the existence of a function.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* 
         * Initialize to the original module 
         * so that the mock function created using spyOn 
         * does not affect other tests.
        */&lt;/span&gt;
        &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restoreAllMocks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doSomething2Mock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mockReturnedValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Mocking a variable in the module&lt;/p&gt;

&lt;p&gt;If you are mocking the &lt;code&gt;PREFIX&lt;/code&gt; variable in &lt;code&gt;module1.js&lt;/code&gt;, you can do it as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;module1.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pre&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;module1.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;oriPrefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;oriPrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;module1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oriPrefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;module1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Testing a async function&lt;/p&gt;

&lt;p&gt;When testing the &lt;code&gt;doSomething()&lt;/code&gt; function in &lt;code&gt;ctrl.js&lt;/code&gt; that includes the callback function, do as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Testing a callback function(&lt;code&gt;res.json()&lt;/code&gt;, ...)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="p"&gt;...&lt;/span&gt;
                    &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Testing &lt;code&gt;throw new Error()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When checking whether the &lt;code&gt;doSomething()&lt;/code&gt; function in &lt;code&gt;ctrl.js&lt;/code&gt; throws an error, do as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If &lt;code&gt;doSomething()&lt;/code&gt; function is asynchronous, you can check whether an error is thrown as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>jest</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>NodeJS에서의 Jest Cheat Sheet</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Mon, 27 Jul 2020 16:04:46 +0000</pubDate>
      <link>https://forem.com/wes5510/nodejs-jest-cheat-sheet-25c4</link>
      <guid>https://forem.com/wes5510/nodejs-jest-cheat-sheet-25c4</guid>
      <description>&lt;p&gt;이 포스트에 방문해주셔서 감사합니다.&lt;br&gt;
포스트에 대한 비판, 의견, 공감하는 피드백은 언제나 환영입니다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;모듈의 함수 mocking&lt;/p&gt;

&lt;p&gt;&lt;code&gt;doSomething1()&lt;/code&gt; 함수를 테스트해야할 때, &lt;code&gt;ctrl.js&lt;/code&gt;의 &lt;code&gt;doSomething1()&lt;/code&gt;함수가 같은 모듈의 &lt;code&gt;doSomething2()&lt;/code&gt;함수를 호출하고 있다면 아래와 같이하면 된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jest.fn()&lt;/code&gt;이 있지만 &lt;code&gt;jest.spyOn()&lt;/code&gt;을 사용하면 모듈이 유무까지 확인할 수 있다.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// spyOn를 이용해서 만든 mock 함수가 &lt;/span&gt;
        &lt;span class="c1"&gt;// 다른 test에 영향이 가지 않도록&lt;/span&gt;
        &lt;span class="c1"&gt;// 원래 모듈로 초기화&lt;/span&gt;
        &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restoreAllMocks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doSomething2Mock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mockReturnedValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;모듈의 변수 mocking&lt;/p&gt;

&lt;p&gt;&lt;code&gt;module1.js&lt;/code&gt;의 &lt;code&gt;PREFIX&lt;/code&gt;변수를 mocking하면 아래와 같이 할 수 있다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;module1.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pre&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;module1.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;oriPrefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;oriPrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;module1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oriPrefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;module1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;비동기(Async/Await) 함수 테스트&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;의 비동기 함수인 &lt;code&gt;doSomething()&lt;/code&gt;를 테스트할 때 아래와 같이 하면된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;콜백 함수(&lt;code&gt;res.json()&lt;/code&gt;, ...) 테스트&lt;/p&gt;

&lt;p&gt;콜백 함수가 포함된 &lt;code&gt;ctrl.js&lt;/code&gt;의 &lt;code&gt;doSomething()&lt;/code&gt;함수를 테스트할 때 아래와 같이 하면된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="p"&gt;...&lt;/span&gt;
                    &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;code&gt;throw new Error()&lt;/code&gt; 테스트&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;의 &lt;code&gt;doSomething()&lt;/code&gt;함수가 에러를 throw 하는 지 확인할 때 아래와 같이 하면된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;doSomething()&lt;/code&gt;함수가 비동기일 경우 에러를 throw하는 지 확인할 때는 아래와 같이 하면된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ctrl.test.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doSomething&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>jest</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Things that were inconvenient while using Redux</title>
      <dc:creator>wes5510</dc:creator>
      <pubDate>Mon, 13 Jul 2020 15:20:28 +0000</pubDate>
      <link>https://forem.com/wes5510/things-that-were-inconvenient-while-using-redux-2cl3</link>
      <guid>https://forem.com/wes5510/things-that-were-inconvenient-while-using-redux-2cl3</guid>
      <description>&lt;p&gt;Thanks for visiting this post.&lt;br&gt;
All feedbacks on this post are always welcome.&lt;br&gt;
This post isn't talking about a basic of Redux. Before reading this post, it is helpful to read "&lt;a href="https://dev.to/wes5510/how-far-will-props-go-down-2ab3"&gt;How far will props go down?&lt;/a&gt;".&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;There were some inconveniences when using Redux.

&lt;ol&gt;
&lt;li&gt;It was difficult to understand with non-intuitive logic&lt;/li&gt;
&lt;li&gt;Readability is poor when debugging because there are empty data that is not needed.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;However, I have improved the above discomfort and still use it. This is because using Redux creates patterns and it ensures high productivity and readability in application development.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Subject
&lt;/h2&gt;

&lt;p&gt;The following explains the inconveniences of using Redux.&lt;/p&gt;

&lt;h3&gt;
  
  
  Non-intuitive Logic
&lt;/h3&gt;

&lt;p&gt;The basic Redux code is shown below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;initState.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;actions.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reducers.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Comp1.jsx&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../reducers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Comp1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
 &lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;n&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapStateToProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mapStateToProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;Comp1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If anyone who doesn't know Redux sees the code above, can he/she knows exactly how n increases by 1? Honestly, I didn't understand at first. I didn't understand where the arguments (state, dispatch) of &lt;code&gt;mapStateToProps&lt;/code&gt;, &lt;code&gt;mapDispatchToProps&lt;/code&gt; are input and what is input. I thought I should just use it like that.&lt;/p&gt;

&lt;p&gt;I thought I lacked understanding, but when I told my co-workers that this was happening, there were quite a few people like me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Empty state data, not needed
&lt;/h3&gt;

&lt;p&gt;Suppose you implement a bulletin board with the following requirements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A page showing all posts (I'll call it the /posts page)&lt;/li&gt;
&lt;li&gt;A page showing detailed information (title, content, author) of the post (I will call it the /posts/:postID page)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use Redux, you can set InitState as below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;initState.js&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
 &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, &lt;code&gt;posts&lt;/code&gt; are useful only on the /posts page, and not required for the /posts/:postID page. Currently, there are only two pages, but if there are many pages like the admin application and there are few states used for one page, the empty status data will increase.&lt;/p&gt;

&lt;p&gt;There was a lot of empty state data that I didn't need, making it inconvenient to debug in &lt;a href="https://nextjs.org/"&gt;NEXT.js&lt;/a&gt; + &lt;a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en"&gt;Redux DevTools&lt;/a&gt;, and poor readability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Redux has its advantages, but also its disadvantages. However, the disadvantage of having a large learning curve can be covered by learning, and empty state data that are not needed can be avoided by good state design. And if you use Redux well, you can build your application more easily. For example, the pattern that implements business logic in the container and the part that communicates with the backend is implemented in the slice module, so that other developers can code predictably, thereby ensuring high readability and productivity.&lt;/p&gt;

</description>
      <category>react</category>
      <category>redux</category>
    </item>
  </channel>
</rss>
