<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>BAEK'space</title>
    <link>https://baekspace.tistory.com/</link>
    <description>백씨 개발자의 공간인 Baek's Space
</description>
    <language>ko</language>
    <pubDate>Wed, 27 May 2026 14:35:13 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>백씨네</managingEditor>
    <image>
      <title>BAEK'space</title>
      <url>https://tistory1.daumcdn.net/tistory/5651363/attach/b8fd9237cbd14e2094ac688005eab38b</url>
      <link>https://baekspace.tistory.com</link>
    </image>
    <item>
      <title>Claude Code for Front-end #5: 서브에이전트 실전 활용기</title>
      <link>https://baekspace.tistory.com/283</link>
      <description>&lt;h3 id=&quot;466f2ee4-4181-4688-a57e-c763f7b196b3&quot; data-pm-slice=&quot;1 1 []&quot; data-toc-id=&quot;466f2ee4-4181-4688-a57e-c763f7b196b3&quot; data-ke-size=&quot;size23&quot;&gt;한국어 키워드로 각 분야 전문가를 호출하는 나만의 워크플로우&lt;/h3&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;b33a5239-609a-4554-9cf0-1964cc41b437&quot; data-toc-id=&quot;b33a5239-609a-4554-9cf0-1964cc41b437&quot; data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 제가 Claude Code의 서브에이전트 시스템을 실제로 어떻게 활용하고 있는지, 제 경험을 중심으로 풀어서 보여드리려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 개념은 공식 문서를 참고하되, 단순 설명에 그치지 않고 제가 실제 프로젝트에서 어떻게 적용했는지를 위주로 정리했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이 방식이 정답은 아니고, 각자의 개발 환경에 맞게 가볍게 참고하시고 필요한 부분만 가져가시면 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;565c3896-a15f-4274-a1b9-3919ab794ac1&quot; data-toc-id=&quot;565c3896-a15f-4274-a1b9-3919ab794ac1&quot; data-ke-size=&quot;size26&quot;&gt;1️⃣ Claude Code 에이전트 구조 이해하기&lt;/h2&gt;
&lt;h3 id=&quot;83f5cf2b-befc-4109-9304-6465e751d887&quot; data-toc-id=&quot;83f5cf2b-befc-4109-9304-6465e751d887&quot; data-ke-size=&quot;size23&quot;&gt;메인 에이전트와 서브에이전트의 관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 사용하다 보면 &quot;메인 에이전트&quot;와 &quot;서브에이전트&quot;라는 용어를 마주하게 됩니다. 처음에는 이 둘의 차이가 명확하지 않아서 혼란스러울 수 있는데, 제 경험상 다음과 같이 이해하시면 도움이 됩니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메인 에이전트 (Main Agent)&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러분이 대화하는 &quot;바로 그 Claude&quot;&lt;/li&gt;
&lt;li&gt;전체 대화 흐름을 관리하고 조율하는 역할&lt;/li&gt;
&lt;li&gt;프로젝트 컨텍스트를 계속 유지하며 대화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서브에이전트 (Sub-Agent)&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 작업을 위해 &lt;b&gt;잠깐 호출되는&lt;/b&gt; 전문가&lt;/li&gt;
&lt;li&gt;독립적인 컨텍스트에서 작업 수행&lt;/li&gt;
&lt;li&gt;작업 완료 후 결과를 메인 에이전트에게 전달하고 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;ee5b5131-991a-4e3f-87f8-d8a5c08c0d29&quot; data-toc-id=&quot;ee5b5131-991a-4e3f-87f8-d8a5c08c0d29&quot; data-ke-size=&quot;size23&quot;&gt;실제로는 어떻게 작동할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 겪은 실제 상황으로 설명드리겠습니다:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;나: &quot;이 코드 성능 최적화해줘&quot;

[메인 에이전트가 판단]
&amp;rarr; 성능 최적화는 performance-engineer 에이전트의 전문 분야
&amp;rarr; Task 도구로 performance-engineer 호출
&amp;rarr; 작업 결과를 받아서 나에게 전달

나: &quot;고마워, 그럼 테스트도 작성해줘&quot;

[메인 에이전트가 다시 판단]
&amp;rarr; 테스트 작성은 quality-engineer 에이전트의 전문 분야
&amp;rarr; Task 도구로 quality-engineer 호출
&amp;rarr; 작업 결과를 받아서 나에게 전달&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 메인 에이전트는 &lt;b&gt;오케스트라 지휘자&lt;/b&gt;처럼 전체를 조율하고, 서브에이전트들은 &lt;b&gt;각 파트의 전문 연주자&lt;/b&gt;처럼 특정 작업을 수행합니다.&lt;/p&gt;
&lt;h3 id=&quot;f68a566d-454e-49bb-9c2c-278d7c1913ac&quot; data-toc-id=&quot;f68a566d-454e-49bb-9c2c-278d7c1913ac&quot; data-ke-size=&quot;size23&quot;&gt;왜 이런 구조가 필요한가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컨텍스트 관리&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 대화에 모든 작업 내용을 담으면 컨텍스트 창이 금방 찹니다&lt;/li&gt;
&lt;li&gt;서브에이전트가 자체 컨텍스트에서 작업하면 메인 대화가 깨끗하게 유지됩니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전문성 향상&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 에이전트는 특정 도메인에만 집중하므로 더 정확한 결과를 냅니다&lt;/li&gt;
&lt;li&gt;성능 최적화 전문가와 보안 검토 전문가는 다른 관점을 가집니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;재사용 가능&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 번 만든 에이전트는 모든 프로젝트에서 활용 가능&lt;/li&gt;
&lt;li&gt;팀원과 공유하여 일관된 품질 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;471d8aae-1294-4b10-932c-cd7b41a7ba52&quot; data-toc-id=&quot;471d8aae-1294-4b10-932c-cd7b41a7ba52&quot; data-ke-size=&quot;size26&quot;&gt;2️⃣ 서브에이전트의 종류 (공식 문서 기반)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 섹션은 Claude Code 공식 문서를 기반으로 작성되었습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;2d2399ec-0042-47f4-83cb-4da7d197ac1d&quot; data-toc-id=&quot;2d2399ec-0042-47f4-83cb-4da7d197ac1d&quot; data-ke-size=&quot;size23&quot;&gt;내장 에이전트 (Built-in Agents)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code에 기본으로 탑재된 에이전트는 다음 4개입니다:&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;/agents 출력:

Built-in agents (always available)
  general-purpose &amp;middot; sonnet    # 범용 작업 처리
  statusline-setup &amp;middot; sonnet   # 상태바 설정
  output-style-setup &amp;middot; sonnet # 출력 스타일 설정
  Explore &amp;middot; haiku             # 빠른 코드베이스 탐색
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;확인 방법&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/agents&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;0e6be2d1-955b-49ba-8bcf-c90f4da054ea&quot; data-toc-id=&quot;0e6be2d1-955b-49ba-8bcf-c90f4da054ea&quot; data-ke-size=&quot;size23&quot;&gt;서브에이전트 우선순위&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 이름의 에이전트가 여러 곳에 있을 때 적용되는 우선순위:&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;프로젝트 &amp;gt; CLI &amp;gt; 사용자 &amp;gt; 플러그인 &amp;gt; 내장
(.claude/agents/) &amp;gt; (--agents) &amp;gt; (~/.claude/agents/) &amp;gt; (플러그인) &amp;gt; (Built-in)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실무 팁&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트별 특화 에이전트는 .claude/agents/에 두세요&lt;/li&gt;
&lt;li&gt;범용적으로 쓰는 에이전트는 ~/.claude/agents/에 두세요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;3e2235b9-160a-449c-bb7a-a2c40b111484&quot; data-toc-id=&quot;3e2235b9-160a-449c-bb7a-a2c40b111484&quot; data-ke-size=&quot;size26&quot;&gt;3️⃣ 서브에이전트 생성 방법 (공식 문서)&lt;/h2&gt;
&lt;h3 id=&quot;1029fc65-9f8e-4177-b11b-fbabf852c140&quot; data-toc-id=&quot;1029fc65-9f8e-4177-b11b-fbabf852c140&quot; data-ke-size=&quot;size23&quot;&gt;방법 1: /agents 명령어 (권장)&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/agents&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;생성 단계&lt;/b&gt;:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&quot;Create New Agent&quot; 선택&lt;/li&gt;
&lt;li&gt;User-level 또는 Project-level 선택&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Claude가 초안 생성&lt;/b&gt; (권장)&lt;/li&gt;
&lt;li&gt;도구 권한 선택 (체크박스 UI)&lt;/li&gt;
&lt;li&gt;e 키로 편집기에서 프롬프트 수정&lt;/li&gt;
&lt;li&gt;저장&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제 경험&lt;/b&gt;: Claude가 생성한 초안을 기반으로 커스터마이징하는 것이 처음부터 작성하는 것보다 훨씬 빠르고 효과적입니다.&lt;/p&gt;
&lt;h3 id=&quot;7bf75314-4cc5-47c3-aff1-e88bdc50fb88&quot; data-toc-id=&quot;7bf75314-4cc5-47c3-aff1-e88bdc50fb88&quot; data-ke-size=&quot;size23&quot;&gt;방법 2: 수동 파일 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파일 형식&lt;/b&gt; (~/.claude/agents/agent-name.md):&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;---
name: agent-name
description: 에이전트가 언제 호출되어야 하는지 설명
tools: Read, Grep, Glob, Bash # 선택사항
model: sonnet # 또는 inherit, opus, haiku
---

시스템 프롬프트가 여기에 들어갑니다.

에이전트의 역할, 능력, 접근 방식을 명확히 정의하세요.

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;필드 설명&lt;/b&gt;:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드필수설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;name&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;✅&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;소문자와 하이픈만 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;description&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;✅&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;자연어로 목적 설명&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;tools&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;❌&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;생략 시 모든 도구 상속&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;model&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;❌&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;sonnet, opus, haiku, inherit&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자세한 내용&lt;/b&gt;은 &lt;a href=&quot;https://docs.anthropic.com/ko/docs/claude-code/sub-agents&quot;&gt;공식 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;a3db1d53-12b7-4fa8-a036-790698cd3ef8&quot; data-toc-id=&quot;a3db1d53-12b7-4fa8-a036-790698cd3ef8&quot; data-ke-size=&quot;size26&quot;&gt;4️⃣ 제가 사용하는 에이전트 (개인 경험)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 섹션부터는 제 개인적인 경험과 의견입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;ceb7f0ba-17e5-4985-a36d-97f3bca03441&quot; data-toc-id=&quot;ceb7f0ba-17e5-4985-a36d-97f3bca03441&quot; data-ke-size=&quot;size23&quot;&gt;현재 설치된 에이전트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 ~/.claude/agents/ 디렉토리에는 14개의 에이전트가 있습니다:&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;backend-architect.md        # 백엔드 아키텍처 설계
devops-architect.md         # 배포 및 인프라
frontend-architect.md       # 프론트엔드 아키텍처
learning-guide.md           # 개념 설명 및 학습
performance-engineer.md     # 성능 최적화
python-expert.md            # Python 전문
quality-engineer.md         # 테스트 작성
refactoring-expert.md       # 리팩토링
requirements-analyst.md     # 요구사항 분석
root-cause-analyst.md       # 근본 원인 분석
security-engineer.md        # 보안 검토
socratic-mentor.md          # 소크라테스식 멘토링
system-architect.md         # 시스템 아키텍처
technical-writer.md         # 기술 문서 작성&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;608d9395-e1cb-4504-b96f-61acc221feea&quot; data-toc-id=&quot;608d9395-e1cb-4504-b96f-61acc221feea&quot; data-ke-size=&quot;size23&quot;&gt;주로 사용하는 에이전트 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root-cause-analyst.md - 가장 자주 쓰는 에이전트:&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;---
name: root-cause-analyst
description: 근본 원인 분석 전문가. 버그나 에러 발생 시 체계적으로 원인을 파악합니다.
tools: Read, Grep, Glob, Bash
model: sonnet
---

당신은 시스템적 사고를 기반으로 문제의 근본 원인을 파악하는 전문가입니다.

분석 프로세스:

1. 증상 수집 및 재현
2. 가설 수립
3. 증거 수집
4. 원인 검증
5. 해결 방안 제시

표면적 증상이 아닌 근본 원인을 찾아내는 것이 목표입니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용 예시&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;나: &quot;API 응답이 느려&quot;
&amp;rarr; root-cause-analyst가 DB 쿼리, 네트워크, 캐싱 등 체계적으로 분석
&amp;rarr; &quot;N+1 쿼리 문제로 확인됨&quot; 결과 전달&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;65f5297f-b289-4d74-bda9-c32c9a16e171&quot; data-toc-id=&quot;65f5297f-b289-4d74-bda9-c32c9a16e171&quot; data-ke-size=&quot;size23&quot;&gt;에이전트를 구하는 곳&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 에이전트 템플릿을 구하는 사이트는 다음과 같습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/SuperClaude-Org/SuperClaude_Framework&quot;&gt;&lt;b&gt;SuperClaude_Framework&lt;/b&gt;&lt;/a&gt; - Claude Code를 체계적인 개발 플랫폼으로 변환하는 강력한 메타프로그래밍 프레임워크&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.aitmpl.com/agents&quot;&gt;&lt;b&gt;AI Template&lt;/b&gt;&lt;/a&gt; - 다양한 분야별 에이전트 템플릿, 실제 예제/레시피 포함&lt;/li&gt;
&lt;li&gt;GitHub에서 claude code agents 키워드로 최신 템플릿 탐색&lt;/li&gt;
&lt;li&gt;커뮤니티(예: Reddit, Discord)에서 사용자 공유 목록 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의&lt;/b&gt;: 다운로드한 에이전트는 &lt;b&gt;반드시 검토 후 사용&lt;/b&gt;하세요. 시스템 프롬프트가 예상과 다를 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;e97c0f94-0fbc-459f-8fb2-5e30864675fd&quot; data-toc-id=&quot;e97c0f94-0fbc-459f-8fb2-5e30864675fd&quot; data-ke-size=&quot;size26&quot;&gt;5️⃣ 한국어 트리거 시스템 (제 워크플로우)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 제가 사용하는 방식이며, 정답은 아닙니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;783bf5d4-7b9e-40f9-a12a-df0c75669a53&quot; data-toc-id=&quot;783bf5d4-7b9e-40f9-a12a-df0c75669a53&quot; data-ke-size=&quot;size23&quot;&gt;CLAUDE.md 설정 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 경우 ~/.claude/CLAUDE.md에 다음과 같이 설정했습니다:&lt;/p&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;#   [CRITICAL] 한국어 에이전트 자동 실행 규칙

**중요**: 다음 키워드 감지 시 **즉시 Task 도구 호출**

##   키워드별 매핑

###   분석 요청

| 키워드             | 실행 도구                                | 예시               |
| ------------------ | ---------------------------------------- | ------------------ |
| &quot;분석해줘&quot;, &quot;봐줘&quot; | Task(subagent_type=&quot;root-cause-analyst&quot;) | &quot;이 코드 분석해줘&quot; |
| &quot;왜 안돼?&quot;, &quot;원인&quot; | Task(subagent_type=&quot;root-cause-analyst&quot;) | &quot;왜 느려?&quot;         |

###  ️ 개발 요청

| 키워드         | 실행 도구                                  | 예시                   |
| -------------- | ------------------------------------------ | ---------------------- |
| &quot;리팩토링해줘&quot; | Task(subagent_type=&quot;refactoring-expert&quot;)   | &quot;이 코드 리팩토링해줘&quot; |
| &quot;최적화해줘&quot;   | Task(subagent_type=&quot;performance-engineer&quot;) | &quot;성능 최적화해줘&quot;      |
| &quot;테스트 작성&quot;  | Task(subagent_type=&quot;quality-engineer&quot;)     | &quot;테스트 작성해줘&quot;      |

###   코드 품질

| 키워드      | 실행 도구                               | 예시               |
| ----------- | --------------------------------------- | ------------------ |
| &quot;리뷰해줘&quot;  | Task(subagent_type=&quot;code-reviewer&quot;)     | &quot;이 코드 리뷰해줘&quot; |
| &quot;보안 체크&quot; | Task(subagent_type=&quot;security-engineer&quot;) | &quot;보안 체크해줘&quot;    |

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;cf422c67-6b36-44b1-ba9e-c5dea881f227&quot; data-toc-id=&quot;cf422c67-6b36-44b1-ba9e-c5dea881f227&quot; data-ke-size=&quot;size23&quot;&gt;작동 원리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중요&lt;/b&gt;: 이것은 &lt;b&gt;Claude Code의 내장 기능이 아닙니다&lt;/b&gt;.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자: &quot;이 코드 분석해줘&quot;&lt;/li&gt;
&lt;li&gt;Claude가 시스템 프롬프트로 CLAUDE.md 내용을 인식&lt;/li&gt;
&lt;li&gt;[CRITICAL] 규칙에서 &quot;분석해줘&quot; 키워드 발견&lt;/li&gt;
&lt;li&gt;Task 도구로 root-cause-analyst 호출&lt;/li&gt;
&lt;li&gt;결과 전달&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;한계&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Claude가 100% 규칙을 따르는 것은 아님 (확률적 모델)&lt;/li&gt;
&lt;li&gt;복잡한 문장에서는 키워드 감지 실패 가능&lt;/li&gt;
&lt;li&gt;명시적 호출이 더 확실함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;994be40a-b432-4531-9934-016cb298b82c&quot; data-toc-id=&quot;994be40a-b432-4531-9934-016cb298b82c&quot; data-ke-size=&quot;size23&quot;&gt;명시적 호출 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규칙에 의존하지 않고 직접 호출하는 방법:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&amp;gt; root-cause-analyst 서브에이전트를 사용하여 이 코드를 분석해줘
&amp;gt; performance-engineer 에이전트로 최적화해줘
&amp;gt; quality-engineer로 테스트를 작성해줘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식이 &lt;b&gt;더 확실하고 안정적&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;ff8eb8ba-54c7-48fe-a86c-15270edaa193&quot; data-toc-id=&quot;ff8eb8ba-54c7-48fe-a86c-15270edaa193&quot; data-ke-size=&quot;size26&quot;&gt;6️⃣ 서브에이전트 활용 방향성&lt;/h2&gt;
&lt;h3 id=&quot;3d372510-01d9-47bc-87fe-6e90469fad3a&quot; data-toc-id=&quot;3d372510-01d9-47bc-87fe-6e90469fad3a&quot; data-ke-size=&quot;size23&quot;&gt;1. 단일 책임 원칙으로 나누기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 경험상 에이전트는 &lt;b&gt;하나의 명확한 목적&lt;/b&gt;만 가질 때 가장 효과적입니다:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;❌ &quot;모든 것을 하는 슈퍼 에이전트&quot; 하나
✅ 작업별로 분리된 여러 에이전트
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 사례&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;초기: super-developer.md (코드 작성, 리뷰, 테스트, 배포 모두 담당)
&amp;rarr; 성능이 일관적이지 않음
&amp;rarr; 때로는 리뷰만 하고, 때로는 테스트를 건너뜀

개선:
- code-reviewer.md (리뷰 전문)
- quality-engineer.md (테스트 전문)
- refactoring-expert.md (리팩토링 전문)
&amp;rarr; 각 에이전트가 자신의 역할에 집중
&amp;rarr; 결과의 일관성과 품질 향상
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;8ce7e487-501b-47d1-bd64-6216fe40ff80&quot; data-toc-id=&quot;8ce7e487-501b-47d1-bd64-6216fe40ff80&quot; data-ke-size=&quot;size23&quot;&gt;2. 에이전트 체이닝 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 에이전트를 순차적으로 활용하는 방법:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 워크플로우&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;1단계: requirements-analyst
   &quot;이 기능 요구사항 정리해줘&quot;
   &amp;rarr; 기능 명세서 생성

2단계: frontend-architect
   &quot;이 명세서 기반으로 컴포넌트 구조 설계해줘&quot;
   &amp;rarr; 컴포넌트 설계도 생성

3단계: quality-engineer
   &quot;이 설계에 맞는 테스트 케이스 작성해줘&quot;
   &amp;rarr; 테스트 코드 생성

4단계: security-engineer
   &quot;보안 취약점 검토해줘&quot;
   &amp;rarr; 보안 리포트 생성
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계의 출력물이 다음 단계의 입력이 되는 구조입니다.&lt;/p&gt;
&lt;h3 id=&quot;b30e7106-0128-4c88-b737-c1afe61e2da7&quot; data-toc-id=&quot;b30e7106-0128-4c88-b737-c1afe61e2da7&quot; data-ke-size=&quot;size23&quot;&gt;3. 프로젝트별 특화 에이전트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트마다 다른 에이전트를 활용하는 것이 효과적입니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Next.js 프로젝트&lt;/b&gt; (.claude/agents/nextjs-expert.md):&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;---
name: nextjs-expert
description: Next.js App Router 전문가. 페이지/API 생성 및 최적화에 사용하세요.
tools: Read, Edit, Write, Bash
model: inherit
---

당신은 Next.js 15 App Router 전문가입니다.

App Router 패턴:

-   서버 컴포넌트 우선 사용
-   'use client' 최소화
-   metadata 자동 생성
-   동적 라우팅 ([slug], [...slug])

성능 최적화:

-   next/image 사용
-   Suspense + Streaming
-   Static Generation 활용

모든 코드는 TypeScript + Tailwind CSS로 작성합니다.

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Python 프로젝트&lt;/b&gt; (.claude/agents/python-expert.md):&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;---
name: python-expert
description: Python 코드 작성 및 최적화 전문가
tools: Read, Edit, Write, Bash
model: sonnet
---

당신은 Python 모범 사례 전문가입니다.

코드 스타일:

-   PEP 8 준수
-   Type hints 적극 활용
-   Docstring 작성

성능 최적화:

-   List comprehension 활용
-   Generator 패턴 사용
-   asyncio for I/O bound tasks

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;02fefaf1-0581-4ede-9ede-cc1b36944b32&quot; data-toc-id=&quot;02fefaf1-0581-4ede-9ede-cc1b36944b32&quot; data-ke-size=&quot;size26&quot;&gt;7️⃣ 실전 팁과 트러블슈팅&lt;/h2&gt;
&lt;h3 id=&quot;71d7ca26-e168-410e-9cdc-c32f05556cb9&quot; data-toc-id=&quot;71d7ca26-e168-410e-9cdc-c32f05556cb9&quot; data-ke-size=&quot;size23&quot;&gt;Claude 생성 초안 활용&lt;/h3&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;❌ 처음부터 수동으로 작성
✅ /agents &amp;rarr; Generate with Claude &amp;rarr; 커스터마이징

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude가 생성한 초안을 기반으로 수정하는 것이 훨씬 빠릅니다.&lt;/p&gt;
&lt;h3 id=&quot;9a8c592e-a6ac-48b7-b3b0-d5b4c9b8b457&quot; data-toc-id=&quot;9a8c592e-a6ac-48b7-b3b0-d5b4c9b8b457&quot; data-ke-size=&quot;size23&quot;&gt;description 필드 상세히 작성&lt;/h3&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;# ❌ 일반적인 설명

description: &quot;코드 리뷰를 수행합니다&quot;

# ✅ 적극적 사용 유도

description: &quot;코드 리뷰 전문가. 코드 변경 후 적극적으로 사용하세요.&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;description 필드를 상세히 작성하면 Claude가 적절한 타이밍에 에이전트를 제안합니다.&lt;/p&gt;
&lt;h3 id=&quot;08157b6b-a8df-45e5-b557-ccfe993252b9&quot; data-toc-id=&quot;08157b6b-a8df-45e5-b557-ccfe993252b9&quot; data-ke-size=&quot;size23&quot;&gt;도구 권한 제한&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# ❌ 모든 도구 허용 (tools 필드 생략)

---

## name: code-reviewer

# ✅ 필요한 도구만 허용

---

name: code-reviewer
tools: Read, Grep, Glob, Bash

---

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 도구만 허용하면 에이전트가 집중된 작업을 수행합니다.&lt;/p&gt;
&lt;h3 id=&quot;d6d2058c-6f25-489d-901b-f08fca415351&quot; data-toc-id=&quot;d6d2058c-6f25-489d-901b-f08fca415351&quot; data-ke-size=&quot;size23&quot;&gt;성능 트레이드오프 인식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지연시간&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서브에이전트는 매번 깨끗한 상태로 시작&lt;/li&gt;
&lt;li&gt;컨텍스트 수집에 약간의 지연 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제 경험&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간단한 작업: 메인 대화에서 직접 처리가 더 빠름&lt;/li&gt;
&lt;li&gt;복잡한 작업: 서브에이전트 활용이 효과적&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;8b0fd4e4-d964-43da-a42b-8e840541c67f&quot; data-toc-id=&quot;8b0fd4e4-d964-43da-a42b-8e840541c67f&quot; data-ke-size=&quot;size23&quot;&gt;트러블슈팅: 에이전트가 호출되지 않을 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제가 겪은 문제&lt;/b&gt;:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CLAUDE.md의 [CRITICAL] 표시 누락&lt;/li&gt;
&lt;li&gt;키워드 오타&lt;/li&gt;
&lt;li&gt;Claude가 규칙을 무시함&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;# CLAUDE.md 확인
cat ~/.claude/CLAUDE.md | head -n 20

# [CRITICAL] 표시 확인
grep -n &quot;CRITICAL&quot; ~/.claude/CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대안&lt;/b&gt;: 명시적으로 에이전트 이름 호출&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&amp;gt; root-cause-analyst 에이전트를 사용해줘
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;39006c2d-b802-47b7-8a87-2bf62e2a8a03&quot; data-toc-id=&quot;39006c2d-b802-47b7-8a87-2bf62e2a8a03&quot; data-ke-size=&quot;size23&quot;&gt;트러블슈팅: 에이전트 목록이 안 보일 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원인&lt;/b&gt;: 파일이 올바른 위치에 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;# 디렉토리 확인
ls -la ~/.claude/agents/

# 파일 확장자 확인 (.md 필수)
ls ~/.claude/agents/*.md

# YAML frontmatter 검증
head -n 10 ~/.claude/agents/code-reviewer.md
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;af0ef4f2-d339-4b70-8f03-a6b14552664f&quot; data-toc-id=&quot;af0ef4f2-d339-4b70-8f03-a6b14552664f&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;h3 id=&quot;77b4dc12-1cdb-465d-88a7-a6ef06b3667c&quot; data-toc-id=&quot;77b4dc12-1cdb-465d-88a7-a6ef06b3667c&quot; data-ke-size=&quot;size23&quot;&gt;제 경험 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;효과적이었던 것&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Claude 생성 초안 &amp;rarr; 커스터마이징&lt;/li&gt;
&lt;li&gt;작업별로 분리된 에이전트 (단일 책임 원칙)&lt;/li&gt;
&lt;li&gt;명시적 호출 (키워드 트리거보다 안정적)&lt;/li&gt;
&lt;li&gt;프로젝트별 특화 에이전트 (.claude/agents/)&lt;/li&gt;
&lt;li&gt;에이전트 체이닝으로 복잡한 워크플로우 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;덜 효과적이었던 것&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만능 슈퍼 에이전트 (역할이 불명확해짐)&lt;/li&gt;
&lt;li&gt;복잡한 한국어 트리거 규칙 (간단한 문장만 작동)&lt;/li&gt;
&lt;li&gt;모든 도구에 대한 무제한 권한 (집중력 저하)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;d0cd891c-9080-4632-bf31-65404bc91d2c&quot; data-toc-id=&quot;d0cd891c-9080-4632-bf31-65404bc91d2c&quot; data-ke-size=&quot;size23&quot;&gt;추천 시작 방법&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;/agents 명령어로 첫 에이전트 생성&lt;/li&gt;
&lt;li&gt;Claude가 초안 생성하도록 선택&lt;/li&gt;
&lt;li&gt;간단한 작업부터 테스트&lt;/li&gt;
&lt;li&gt;점진적으로 에이전트 확장&lt;/li&gt;
&lt;li&gt;프로젝트별 특화 에이전트 구성&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 글은 제 개인적인 경험을 바탕으로 작성되었으며, 모든 환경에서 동일하게 작동한다고 보장할 수 없습니다. 여러분의 프로젝트와 워크플로우에 맞게 조정하시길 권장합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트를 직접 만들어보고, 실험하면서 자신만의 워크플로우를 찾아가시길 바랍니다!  &lt;/p&gt;</description>
      <category>SETTING</category>
      <category>agents</category>
      <category>Ai</category>
      <category>mcp</category>
      <category>에이전트</category>
      <category>클로드</category>
      <category>클로드 잘쓰기</category>
      <category>클로드코드</category>
      <category>클로드코드 활용</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/283</guid>
      <comments>https://baekspace.tistory.com/283#entry283comment</comments>
      <pubDate>Wed, 7 Jan 2026 20:46:56 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code for Front-end #4: Claude Code를 200% 활용하는 법</title>
      <link>https://baekspace.tistory.com/282</link>
      <description>&lt;h2 id=&quot;ce9f54b3-597a-4c00-aadf-c845cbbb5e2b&quot; data-pm-slice=&quot;1 1 []&quot; data-toc-id=&quot;ce9f54b3-597a-4c00-aadf-c845cbbb5e2b&quot; data-ke-size=&quot;size26&quot;&gt;AI와 대화하는 방법이 생산성을 결정한다&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;60d5d458-183a-42ec-b682-8f66c210466e&quot; data-toc-id=&quot;60d5d458-183a-42ec-b682-8f66c210466e&quot; data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code 설치했다고 끝난 게 아닙니다. 진짜 실력은 &lt;b&gt;어떻게 말을 거는가&lt;/b&gt;에서 갈립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 도구를 써도 누군가는 1시간 걸릴 작업을 10분에 끝내고, 누군가는 여전히 &quot;왜 안 되지?&quot;를 반복합니다. 차이는 단순합니다. &lt;b&gt;프롬프트를 어떻게 쓰느냐.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 매일 쓰는 실전 프롬프트 패턴을 공개합니다. 복붙해서 바로 쓸 수 있는 것들만 모았습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;ee828785-2c73-429d-9ab0-1e8e891564a5&quot; data-toc-id=&quot;ee828785-2c73-429d-9ab0-1e8e891564a5&quot; data-ke-size=&quot;size23&quot;&gt;프롬프트의 기본 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 좋은 프롬프트는 이 4요소 구조를 따릅니다:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;[컨텍스트] + [작업] + [제약조건] + [출력 형식]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실전 예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;컨텍스트: Next.js App Router 프로젝트입니다.
작업: 사용자 프로필 페이지를 생성하세요.
제약조건: TypeScript를 사용하고, Tailwind CSS로 스타일링하세요.
출력 형식: page.tsx와 관련 컴포넌트를 별도 파일로 분리하세요.&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  버전 호환성: Next.js 13+ 버전은 모두 App Router를 지원합니다. 버전 번호를 생략하면 Claude가 프로젝트 환경에 맞게 대응합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조만 지켜도 응답 품질이 2배 이상 향상됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;5bdbb8b0-009d-45d9-8a10-57daca338c2c&quot; data-toc-id=&quot;5bdbb8b0-009d-45d9-8a10-57daca338c2c&quot; data-ke-size=&quot;size26&quot;&gt;1️⃣ &quot;분석해줘&quot; vs &quot;성능 병목 3개 찾아서 개선안까지 제시해줘&quot;&lt;/h2&gt;
&lt;h3 id=&quot;50458bad-061b-48b4-811f-e53a1b363864&quot; data-toc-id=&quot;50458bad-061b-48b4-811f-e53a1b363864&quot; data-ke-size=&quot;size23&quot;&gt;❌ 애매한 요청&lt;/h3&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;이 컴포넌트 좀 봐줘&quot;
&quot;코드 분석 좀&quot;
&quot;최적화해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude의 마음: &quot;뭘 봐드릴까요...? 뭘 분석할까요...? 어떻게 최적화할까요...?&quot;&lt;/p&gt;
&lt;h3 id=&quot;da92c3f9-417e-46ed-b9d2-787b77a200bc&quot; data-toc-id=&quot;da92c3f9-417e-46ed-b9d2-787b77a200bc&quot; data-ke-size=&quot;size23&quot;&gt;✅ 구체적인 요청&lt;/h3&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;상품 목록 컴포넌트에서 1000개 항목 렌더링 시 느린 이유 찾고,
가상화 적용 전후 비교 코드 작성해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&quot;장바구니 페이지의 리렌더링 이슈 분석해서:
1. 불필요한 리렌더링 지점 표시
2. useMemo/useCallback 적용 위치
3. 적용 후 예상 성능 개선율&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과 차이&lt;/b&gt;: 5분 vs 30초 + 정확도 3배&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;dca7d869-73f9-4a5e-9fab-c98decceb6ac&quot; data-toc-id=&quot;dca7d869-73f9-4a5e-9fab-c98decceb6ac&quot; data-ke-size=&quot;size26&quot;&gt;2️⃣ 컨텍스트는 AI의 연료입니다&lt;/h2&gt;
&lt;h3 id=&quot;132caf3f-cb29-45a7-ae59-59b5496d2cc1&quot; data-toc-id=&quot;132caf3f-cb29-45a7-ae59-59b5496d2cc1&quot; data-ke-size=&quot;size23&quot;&gt;❌ 컨텍스트 없는 요청&lt;/h3&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;API 호출 에러 나는데 고쳐줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude: &quot;어떤 API요? 어떤 에러요? 어디서요?  &quot;&lt;/p&gt;
&lt;h3 id=&quot;2b6d021a-a4d0-4a18-8c35-1faaf5ab824b&quot; data-toc-id=&quot;2b6d021a-a4d0-4a18-8c35-1faaf5ab824b&quot; data-ke-size=&quot;size23&quot;&gt;✅ 컨텍스트 충분한 요청&lt;/h3&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&quot;사용자 인증 API 호출에서 401 에러 발생.

위치:
- 파일: api/auth/login.ts
- 함수: loginUser

현재 상황:
- POST /api/auth/login 호출
- 로컬에서는 정상, 프로덕션에서만 401
- 요청 헤더: Content-Type: application/json
- 응답: {error: 'Unauthorized', code: 'AUTH_FAILED'}

확인 필요:
1. CORS 설정 이슈
2. 토큰 전달 방식
3. 환경변수 차이&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트 작성 전 다음을 확인하세요:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;2513947f-3739-48bf-8cec-1b27cc5347cb&quot; data-toc-id=&quot;2513947f-3739-48bf-8cec-1b27cc5347cb&quot; data-ke-size=&quot;size26&quot;&gt;3️⃣ 단계별 실행 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 작업은 한 번에 다 시키지 마세요. 단계별로 쪼개면 정확도가 올라갑니다.&lt;/p&gt;
&lt;h3 id=&quot;e5205dd4-de4c-4759-bfc7-3e32585f75bf&quot; data-toc-id=&quot;e5205dd4-de4c-4759-bfc7-3e32585f75bf&quot; data-ke-size=&quot;size23&quot;&gt;예시: 새 기능 구현 (즐겨찾기 기능)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1단계: 요구사항 정리&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;&quot;게시글 즐겨찾기 기능 요구사항 정리해줘:
- 사용자가 게시글을 즐겨찾기 추가/제거
- 즐겨찾기 목록 조회
- 실시간 상태 반영&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2단계: API 설계&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;&quot;위 요구사항 기반으로 API 명세 작성:
- RESTful 엔드포인트
- 요청/응답 스키마
- 에러 케이스&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3단계: 상태 관리&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;&quot;TanStack Query 훅 구현:
- useFavoritePost (mutation)
- useFavoritePosts (query)
- Optimistic update 포함&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  라이브러리 명칭: React Query는 v4부터 TanStack Query로 브랜딩 변경되었습니다. 두 이름 모두 사용 가능합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4단계: UI 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;&quot;FavoriteButton 컴포넌트 구현:
- 로딩/성공/실패 상태
- 토스트 메시지
- 접근성 고려&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;: 각 단계마다 검토하고 피드백 가능. 수정 비용 최소화.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;17d558d7-6695-4c24-8852-fa59d2e25378&quot; data-toc-id=&quot;17d558d7-6695-4c24-8852-fa59d2e25378&quot; data-ke-size=&quot;size26&quot;&gt;4️⃣ &quot;왜&quot;를 물어보는 습관&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드만 받지 말고, 이유를 물어보세요. 학습 효과가 10배입니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;useCallback을 여기 쓴 이유는?&quot;

&quot;왜 상태를 Context로 올렸어?&quot;

&quot;이 최적화가 실제로 효과 있는 케이스는?&quot;

&quot;다른 방법도 있었을 텐데 왜 이 방법을 선택했어?&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude는 답하면서 동시에 트레이드오프, 대안, 주의사항까지 설명해줍니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;da5cd1a3-ba52-4986-90fe-144d03991bae&quot; data-toc-id=&quot;da5cd1a3-ba52-4986-90fe-144d03991bae&quot; data-ke-size=&quot;size26&quot;&gt;5️⃣ 비교 요청 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택지가 있을 때 &quot;이게 나아?&quot; 대신 &quot;A와 B 비교해줘&quot;&lt;/p&gt;
&lt;h3 id=&quot;67d627d8-3437-4b47-a30e-94b4276ef931&quot; data-toc-id=&quot;67d627d8-3437-4b47-a30e-94b4276ef931&quot; data-ke-size=&quot;size23&quot;&gt;❌ 막연한 질문&lt;/h3&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;이 라이브러리 좋아? 저 라이브러리 좋아?&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;444e5dd1-6084-4aae-b921-d0ce4b6cda37&quot; data-toc-id=&quot;444e5dd1-6084-4aae-b921-d0ce4b6cda37&quot; data-ke-size=&quot;size23&quot;&gt;✅ 비교 요청&lt;/h3&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&quot;프로젝트에 적합한 상태 관리 라이브러리 비교:

프로젝트 특성:
- React 18 기반 웹 애플리케이션
- 전역 상태: 사용자 인증, UI 테마
- SSR/SSG 필요

비교 기준:
1. 서버 사이드 렌더링 호환성
2. 학습 곡선
3. 번들 사이즈
4. DevTools 지원
5. 커뮤니티 및 생태계

각 기준별 점수와 최종 추천 + 이유&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과&lt;/b&gt;: 명확한 의사결정 근거 확보&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;39216d34-e158-4f88-a020-ac72be546a9d&quot; data-toc-id=&quot;39216d34-e158-4f88-a020-ac72be546a9d&quot; data-ke-size=&quot;size26&quot;&gt;6️⃣ 에러 해결 골든 템플릿&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 발생 시 이 템플릿을 사용하세요:&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;&quot;[에러 메시지 원문 그대로]

발생 위치:
- 파일: [경로]
- 라인: [번호]
- 함수/컴포넌트: [이름]

재현 방법:
1. [단계1]
2. [단계2]
3. [에러 발생]

시도한 것:
- [시도1] &amp;rarr; [결과]
- [시도2] &amp;rarr; [결과]

의심 지점:
- [가설1]
- [가설2]

원인 분석 + 해결 방법 제시해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;93529b76-0a44-49ca-865c-396d145788d9&quot; data-toc-id=&quot;93529b76-0a44-49ca-865c-396d145788d9&quot; data-ke-size=&quot;size23&quot;&gt;실전 예시&lt;/h3&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&quot;TypeError: Cannot read property 'map' of undefined

발생 위치:
- 파일: components/item-list.tsx
- 라인: 42
- 컴포넌트: ItemList

재현 방법:
1. 목록 페이지 접속
2. 카테고리 필터 선택
3. 로딩 후 에러 발생

시도한 것:
- items가 undefined인지 체크 &amp;rarr; 여전히 에러
- optional chaining 사용 &amp;rarr; 화면 깜빡임 발생

의심 지점:
- TanStack Query에서 초기 data가 undefined
- 조건부 렌더링 타이밍 이슈

원인 분석 + 해결 방법 제시해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;c308110e-c21a-44c7-b5a6-d58d96c6e71f&quot; data-toc-id=&quot;c308110e-c21a-44c7-b5a6-d58d96c6e71f&quot; data-ke-size=&quot;size26&quot;&gt;7️⃣ 리팩토링 요청의 기술&lt;/h2&gt;
&lt;h3 id=&quot;e475e1ef-7378-4b2c-90be-514229427fe5&quot; data-toc-id=&quot;e475e1ef-7378-4b2c-90be-514229427fe5&quot; data-ke-size=&quot;size23&quot;&gt;❌ 모호한 요청&lt;/h3&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;이 코드 리팩토링해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;a0086110-9b95-4cfa-9f48-ca3e363904bc&quot; data-toc-id=&quot;a0086110-9b95-4cfa-9f48-ca3e363904bc&quot; data-ke-size=&quot;size23&quot;&gt;✅ 명확한 목표&lt;/h3&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&quot;사용자 프로필 컴포넌트 리팩토링:

현재 문제:
- 300줄 넘는 단일 컴포넌트
- useEffect 5개 중첩
- Props drilling 3단계

목표:
1. 컴포넌트 분리 (UI/로직)
2. Custom Hook으로 로직 추출
3. Props drilling &amp;rarr; Context API
4. 테스트 가능한 구조

단, 기존 API 호출 로직은 유지
(데이터 fetch 및 mutation 훅)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;8421e814-27d5-4db5-9bc2-7293dfbb2d62&quot; data-toc-id=&quot;8421e814-27d5-4db5-9bc2-7293dfbb2d62&quot; data-ke-size=&quot;size23&quot;&gt;리팩토링 체크리스트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;39474113-f104-406b-8de0-04a1bcb55d02&quot; data-toc-id=&quot;39474113-f104-406b-8de0-04a1bcb55d02&quot; data-ke-size=&quot;size26&quot;&gt;8️⃣ 점진적 개선 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완벽한 코드를 한 번에 요구하지 마세요. 단계별로 개선하세요.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;1차: &quot;기본 기능 구현 (빠르게)&quot;
2차: &quot;타입 안정성 강화&quot;
3차: &quot;에러 핸들링 추가&quot;
4차: &quot;성능 최적화&quot;
5차: &quot;접근성 개선&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계마다 테스트하고 다음으로 넘어가면 안정적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실전 적용:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;// 1차 요청
&quot;로그인 폼 기본 기능만 빠르게 구현:
- 이메일/비밀번호 입력
- 제출 시 API 호출
- 성공 시 대시보드 이동&quot;

// 2차 요청 (1차 완료 후)
&quot;위 코드에 React Hook Form + Zod 추가:
- 이메일 형식 검증
- 비밀번호 최소 8자
- 실시간 에러 표시&quot;

// 3차 요청 (2차 완료 후)
&quot;에러 핸들링 강화:
- 네트워크 에러
- 401/403/500 응답 처리
- 사용자 친화적 메시지&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;af1a8c9e-3ca7-4a16-ab69-9e6ba2e68d61&quot; data-toc-id=&quot;af1a8c9e-3ca7-4a16-ab69-9e6ba2e68d61&quot; data-ke-size=&quot;size26&quot;&gt;9️⃣ 학습 모드 활성화&lt;/h2&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;React Query의 staleTime vs cacheTime 차이를
초등학생도 이해할 수 있게 설명 +
실제 사용 예시 3가지&quot;

&quot;Next.js App Router의 Server Component가
왜 빠른지 내부 동작 원리부터 차근차근&quot;

&quot;useMemo가 오히려 성능 악화시키는 케이스
3가지 + 각각 해결법&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;팁&lt;/b&gt;: &quot;왜&quot;, &quot;어떻게&quot;, &quot;언제&quot;를 조합하면 깊이 있는 답변이 나옵니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;1a7608bb-ca34-405a-9526-9d351ebb6547&quot; data-toc-id=&quot;1a7608bb-ca34-405a-9526-9d351ebb6547&quot; data-ke-size=&quot;size26&quot;&gt;  협업 시뮬레이션 - 코드 리뷰 요청&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude를 페어 프로그래밍 파트너로 활용하세요.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&quot;내가 작성한 이 코드 리뷰해줘.
시니어 개발자 관점에서:

1. 잠재적 버그
2. 성능 이슈
3. 가독성 문제
4. 보안 취약점
5. 개선 제안

각 항목마다 심각도(상/중/하) 표시하고
우선순위 순서로 정렬해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과&lt;/b&gt;: 실제 코드 리뷰처럼 건설적인 피드백을 받습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;60383faa-2902-4b55-a212-8a46342378b3&quot; data-toc-id=&quot;60383faa-2902-4b55-a212-8a46342378b3&quot; data-ke-size=&quot;size26&quot;&gt;실전 활용 가이드&lt;/h2&gt;
&lt;h3 id=&quot;b5fb3931-2f67-4a2a-90e3-f5a959ae80db&quot; data-toc-id=&quot;b5fb3931-2f67-4a2a-90e3-f5a959ae80db&quot; data-ke-size=&quot;size23&quot;&gt;템플릿 모음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복붙해서 바로 쓰는 3가지 핵심 템플릿입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  버그 수정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;**[버그 제목]**

증상: [사용자가 겪는 문제]
재현 조건: [발생 상황]
에러 로그:

[스택 트레이스]

원인 분석 &amp;rarr; 수정 &amp;rarr; 테스트 케이스 작성
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  기능 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;**[기능명] 구현**

사용자 스토리: [역할]로서 [목적]을 위해 [기능]을 원한다

수용 조건:

-   [ ] [조건1]
-   [ ] [조건2]

기술 제약: [제약사항]

&amp;rarr; 단계별 구현 계획 먼저 세워줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  성능 최적화&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;**성능 최적화: [대상]**

현재 지표: LCP [수치] / FID [수치] / 번들 [크기]
목표: LCP &amp;lt; 2.5s / FID &amp;lt; 100ms / 번들 -30%

&amp;rarr; 병목 분석 &amp;rarr; 개선안 &amp;rarr; 예상 효과
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;1260148a-a84f-40db-820b-4036784bdb68&quot; data-toc-id=&quot;1260148a-a84f-40db-820b-4036784bdb68&quot; data-ke-size=&quot;size23&quot;&gt;금지어 목록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 표현은 피하세요:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❌ 주관적 표현✅ 객관적 기준&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;&quot;좀 더 좋게&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;&quot;응답 속도 200ms 이하로&quot;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;&quot;깔끔하게&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;&quot;함수당 20줄 이하, 중첩 3단계 이하&quot;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;&quot;잘&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;&quot;TypeScript strict 모드 통과하게&quot;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;&quot;대충&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;span&gt;&quot;MVP 수준으로 (에러 핸들링 제외)&quot;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원칙&lt;/b&gt;: 주관적 단어는 객관적 기준으로 바꾸세요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;965229f0-1b91-4ecc-816d-b40a979aa9da&quot; data-toc-id=&quot;965229f0-1b91-4ecc-816d-b40a979aa9da&quot; data-ke-size=&quot;size23&quot;&gt;고급 프롬프트 기법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;체인 프롬프트 (Multi-step Prompts)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 작업은 여러 단계로 분할합니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1단계: 분석&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&quot;현재 인증 시스템의 구조를 분석하고 다음을 파악하세요:
1. 사용 중인 인증 방식
2. 토큰 저장 위치와 방식
3. 보안 취약점&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2단계: 설계&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&quot;분석 결과를 바탕으로 JWT 기반 인증 시스템의 구조를 설계하세요:
1. 파일 구조
2. API 엔드포인트
3. 상태 관리 방식&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3단계: 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;&quot;설계한 구조대로 인증 시스템을 구현하세요.
먼저 src/lib/auth/types.ts부터 시작하세요.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;출력 형식 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경 전후 비교 요청:&lt;/p&gt;
&lt;pre class=&quot;ceylon&quot;&gt;&lt;code&gt;&quot;리팩토링 전후 코드 나란히 보여줘 (diff 형식)

### Before
```typescript
[기존 코드]

문제점:
- [문제 1]
- [문제 2]
```
### After
```typescript
[개선된 코드]
```

개선점:
- [개선 1]
- [개선 2]&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;5247c720-21cd-4a85-ba7b-219c2ed66310&quot; data-toc-id=&quot;5247c720-21cd-4a85-ba7b-219c2ed66310&quot; data-ke-size=&quot;size23&quot;&gt;실전 꿀팁 3가지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 파일 경로는 항상 명시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;✅ &quot;src/app/(pages)/products/[id]/page.tsx 수정&quot; 

❌ &quot;페이지 파일 수정&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude가 컨텍스트를 정확히 파악합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 에러 메시지는 원문 그대로&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번역하거나 요약하지 마세요. 복붙이 답입니다.&lt;/p&gt;
&lt;pre class=&quot;scilab&quot;&gt;&lt;code&gt;❌ &quot;타입 에러 나요&quot; 
✅ &quot;Type 'string | undefined' is not assignable to type 'string'&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 우선순위 명시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;ldif&quot;&gt;&lt;code&gt;&quot;다음 순서로 진행:
1순위: 보안 이슈 수정
2순위: 성능 최적화
3순위: 코드 정리&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;167e0b98-e5af-4cf7-93e3-0f62d5c51d1c&quot; data-toc-id=&quot;167e0b98-e5af-4cf7-93e3-0f62d5c51d1c&quot; data-ke-size=&quot;size23&quot;&gt;프롬프트 품질 체크리스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;효과적인 프롬프트를 작성했는지 확인하세요:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;명확성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[ ] 작업 내용이 구체적으로 명시되어 있는가?&lt;/li&gt;
&lt;li&gt;[ ] 기술 스택과 도구가 명시되어 있는가?&lt;/li&gt;
&lt;li&gt;[ ] 파일 경로가 정확한가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완결성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[ ] 필요한 모든 컨텍스트를 제공했는가?&lt;/li&gt;
&lt;li&gt;[ ] 제약조건이 명시되어 있는가?&lt;/li&gt;
&lt;li&gt;[ ] 예상 출력 형식이 정의되어 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행가능성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[ ] 작업이 명확한 단계로 나뉘어 있는가?&lt;/li&gt;
&lt;li&gt;[ ] 각 단계가 검증 가능한가?&lt;/li&gt;
&lt;li&gt;[ ] 성공 기준이 정의되어 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;효율성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[ ] 불필요한 정보가 없는가?&lt;/li&gt;
&lt;li&gt;[ ] 우선순위가 명확한가?&lt;/li&gt;
&lt;li&gt;[ ] 예시 코드가 포함되어 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;7d69cddc-5f85-4167-a18e-041592ff70a3&quot; data-toc-id=&quot;7d69cddc-5f85-4167-a18e-041592ff70a3&quot; data-ke-size=&quot;size26&quot;&gt;마무리: 프롬프트는 대화입니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 명령을 실행하는 도구가 아니라 &lt;b&gt;함께 문제를 푸는 동료&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h3 id=&quot;1d164692-0b55-4b9a-9386-ef0e280f9f0b&quot; data-toc-id=&quot;1d164692-0b55-4b9a-9386-ef0e280f9f0b&quot; data-ke-size=&quot;size23&quot;&gt;핵심 원칙&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;구체적이고 명확하게&lt;/b&gt; - 모호함을 제거하고 정확한 요구사항 전달&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구조화된 형식 사용&lt;/b&gt; - 일관된 템플릿으로 정보 전달&lt;/li&gt;
&lt;li&gt;&lt;b&gt;충분한 컨텍스트 제공&lt;/b&gt; - 프로젝트 환경과 현재 상태 공유&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단계별 분할&lt;/b&gt; - 복잡한 작업을 관리 가능한 단위로 분해&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검증 가능한 출력&lt;/b&gt; - 성공 기준과 테스트 방법 명시&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;ee4c2765-df18-46a0-8241-015e5a2b3559&quot; data-toc-id=&quot;ee4c2765-df18-46a0-8241-015e5a2b3559&quot; data-ke-size=&quot;size23&quot;&gt;실천 가이드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령하지 말고 &lt;b&gt;협의&lt;/b&gt;하세요&lt;/li&gt;
&lt;li&gt;결과만 받지 말고 &lt;b&gt;이유&lt;/b&gt;를 물어보세요&lt;/li&gt;
&lt;li&gt;한 번에 끝내려 하지 말고 &lt;b&gt;단계별&lt;/b&gt;로 진행하세요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 오늘 공유한 패턴들, 내일 당장 써보세요. 일주일만 써봐도 체감합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Claude Code, 이제 제대로 부려먹을 시간입니다.&lt;/b&gt;  &lt;/p&gt;</description>
      <category>SETTING</category>
      <category>Ai</category>
      <category>ai 잘쓰기</category>
      <category>AI 프롬프트</category>
      <category>claude-code</category>
      <category>claudecode</category>
      <category>mcp</category>
      <category>클로드</category>
      <category>클로드코드</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/282</guid>
      <comments>https://baekspace.tistory.com/282#entry282comment</comments>
      <pubDate>Wed, 7 Jan 2026 20:45:20 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code for Front-end #3: MCP 서버로 Claude Code 능력 확장하기</title>
      <link>https://baekspace.tistory.com/281</link>
      <description>&lt;h2 id=&quot;b8ac3181-d1e0-49a6-93fa-fb4983d62ced&quot; data-pm-slice=&quot;1 0 []&quot; data-toc-id=&quot;b8ac3181-d1e0-49a6-93fa-fb4983d62ced&quot; data-ke-size=&quot;size26&quot;&gt;프로덕션 레벨 개발을 위한 7가지 필수 MCP 서버 활용법&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;641373d7-0dfc-41a1-b8e6-4d2c5e9b4509&quot; data-toc-id=&quot;641373d7-0dfc-41a1-b8e6-4d2c5e9b4509&quot; data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;1️⃣&amp;nbsp;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 쓰면서 이런 생각 해보신 적 있나요?&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;음... React 공식 문서 찾아보고 돌아와야겠네&quot;

&quot;이 UI 컴포넌트 만드는 데 시간이 너무 오래 걸리는데...&quot;

&quot;코드베이스가 너무 커서 뭐가 어디 있는지 찾기도 힘들어&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저도 그랬습니다. Claude Code는 분명 강력한 도구인데, 뭔가 2% 부족한 느낌이었죠. 그 2%를 채워주는 게 바로 &lt;b&gt;MCP 서버&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 저는 7개의 MCP 서버를 프로젝트에 도입한 후 &lt;b&gt;개발 생산성이 3배 이상 향상&lt;/b&gt;되었습니다. 이 글에서는 그 경험을 바탕으로 실전에서 검증한 MCP 서버들과 활용법을 공유하고자 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;6af1598f-405a-4b91-9d1a-4220ebc4216d&quot; data-toc-id=&quot;6af1598f-405a-4b91-9d1a-4220ebc4216d&quot; data-ke-size=&quot;size26&quot;&gt;2️⃣&amp;nbsp;MCP란 무엇인가?&lt;/h2&gt;
&lt;h3 id=&quot;a1dbfc13-8d64-4350-b1f3-feb6e54bfd39&quot; data-toc-id=&quot;a1dbfc13-8d64-4350-b1f3-feb6e54bfd39&quot; data-ke-size=&quot;size23&quot;&gt;기본 개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP(Model Context Protocol)는 AI 모델과 외부 시스템 간 표준화된 통신 프로토콜입니다. Claude Code에 새로운 능력을 부여하는 플러그인 시스템으로 이해할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;beccfafd-f719-4a21-9d25-4898ef42f1e2&quot; data-toc-id=&quot;beccfafd-f719-4a21-9d25-4898ef42f1e2&quot; data-ke-size=&quot;size23&quot;&gt;아키텍처 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 클라이언트-서버 구조로 동작하며, Claude Code가 클라이언트 역할을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 구성 요소&lt;/b&gt;:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Tools&lt;/b&gt;: 실행 가능한 함수 인터페이스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명확한 입력 파라미터 스키마 정의&lt;/li&gt;
&lt;li&gt;구조화된 결과 반환&lt;/li&gt;
&lt;li&gt;에러 처리 및 재시도 로직 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Resources&lt;/b&gt;: 외부 데이터 소스 접근
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 시스템, 데이터베이스, API 엔드포인트&lt;/li&gt;
&lt;li&gt;읽기/쓰기 권한 설정&lt;/li&gt;
&lt;li&gt;캐싱 및 동기화 전략&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Prompts&lt;/b&gt;: 재사용 가능한 프롬프트 템플릿
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파라미터화된 구조&lt;/li&gt;
&lt;li&gt;컨텍스트 주입 및 변수 치환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;7cc4ce45-87e5-49ef-8e72-fbf2c1c499df&quot; data-toc-id=&quot;7cc4ce45-87e5-49ef-8e72-fbf2c1c499df&quot; data-ke-size=&quot;size26&quot;&gt;3️⃣&amp;nbsp;MCP 서버 설정 가이드&lt;/h2&gt;
&lt;h3 id=&quot;07535e94-b194-447a-ab2e-4a43f0817a6c&quot; data-toc-id=&quot;07535e94-b194-447a-ab2e-4a43f0817a6c&quot; data-ke-size=&quot;size23&quot;&gt;설정 파일 위치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code의 설정 파일은 기본적으로 프로젝트의 .claude 디렉토리에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 2편에서와 같이 프로젝트에서만 사용하는 MCP가 아닌 전역적으로 사용하는 MCP는 ~/.claude.json 파일에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 mcp를 세팅하는 파일의 예시입니다.&lt;/p&gt;
&lt;h3 id=&quot;6ad7ce18-fcc8-4529-8f9b-43779a11fac0&quot; data-toc-id=&quot;6ad7ce18-fcc8-4529-8f9b-43779a11fac0&quot; data-ke-size=&quot;size23&quot;&gt;기본 설정 구조&lt;/h3&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;// mcp.json 또는 ~/.claude.json
{
    &quot;mcpServers&quot;: {
        &quot;context7&quot;: {
            &quot;command&quot;: &quot;npx&quot;,
            &quot;args&quot;: [&quot;-y&quot;, &quot;@upstash/context7-mcp@latest&quot;]
        },
        &quot;sequential-thinking&quot;: {
            &quot;command&quot;: &quot;npx&quot;,
            &quot;args&quot;: [&quot;-y&quot;, &quot;@modelcontextprotocol/server-sequential-thinking&quot;]
        },
        &quot;magic&quot;: {
            &quot;type&quot;: &quot;stdio&quot;,
            &quot;command&quot;: &quot;npx&quot;,
            &quot;args&quot;: [&quot;@21st-dev/magic&quot;],
            &quot;env&quot;: {
                &quot;TWENTYFIRST_API_KEY&quot;: &quot;&quot;
            }
        },
        &quot;playwright&quot;: {
            &quot;command&quot;: &quot;npx&quot;,
            &quot;args&quot;: [&quot;@playwright/mcp@latest&quot;]
        },
        &quot;serena&quot;: {
            &quot;command&quot;: &quot;uvx&quot;,
            &quot;args&quot;: [&quot;--from&quot;, &quot;git+https://github.com/oraios/serena&quot;, &quot;serena&quot;, &quot;start-mcp-server&quot;]
        },
        &quot;notion&quot;: {
            &quot;type&quot;: &quot;http&quot;,
            &quot;url&quot;: &quot;&amp;lt;https://mcp.notion.com/mcp&amp;gt;&quot;
        },
        &quot;figma&quot;: {
            &quot;type&quot;: &quot;http&quot;,
            &quot;url&quot;: &quot;&amp;lt;https://mcp.figma.com/mcp&amp;gt;&quot;
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;9a292aad-b8bb-495b-b0d6-ca4b4dcb2518&quot; data-toc-id=&quot;9a292aad-b8bb-495b-b0d6-ca4b4dcb2518&quot; data-ke-size=&quot;size26&quot;&gt;4️⃣&amp;nbsp;실전 MCP 서버 7종 실전 가이드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설치 방법 2가지 중 하나를 선택하여 설치합니다.&lt;/li&gt;
&lt;li&gt;설치 방법은 각 서버의 공식 문서를 참고하면 더 자세한 내용을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;55c60fd6-3eae-48a8-a820-ec360ec56259&quot; data-toc-id=&quot;55c60fd6-3eae-48a8-a820-ec360ec56259&quot; data-ke-size=&quot;size23&quot;&gt;1. Sequential-Thinking - 체계적 사고 엔진&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 문제를 단계별로 분석하고 논리적으로 해결책을 도출해야 할 때, Sequential-Thinking MCP는 Claude의 사고 과정을 체계화하는 데 도움을 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단계별 문제 분해&lt;/b&gt;: 복잡한 문제를 작은 단위로 나누어 순차적으로 해결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가설 검증&lt;/b&gt;: 여러 가능성을 검토하고 최적의 해결책 도출&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사고 과정 추적&lt;/b&gt;: 각 단계의 추론 과정을 투명하게 공개&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실전 활용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;복잡한 버그 디버깅&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;이 React 컴포넌트에서 무한 리렌더링이 발생하는 원인을 단계별로 분석해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sequential-Thinking이 수행할 수 있는 분석 예시:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;컴포넌트 의존성 그래프 분석&lt;/li&gt;
&lt;li&gt;useEffect 의존성 배열 검토&lt;/li&gt;
&lt;li&gt;상태 업데이트 패턴 확인&lt;/li&gt;
&lt;li&gt;부모-자식 컴포넌트 간 props 전달 추적&lt;/li&gt;
&lt;li&gt;최종 원인 특정 및 해결책 제시&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아키텍처 설계&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;마이크로서비스 아키텍처로 전환하기 위한 단계별 마이그레이션 전략을 수립해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설정 방법:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;claude mcp add sequential-thinking npx @modelcontextprotocol/server-sequential-thinking&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;&quot;sequential-thinking&quot;: {
  &quot;command&quot;: &quot;npx&quot;,
  &quot;args&quot;: [&quot;-y&quot;, &quot;@modelcontextprotocol/server-sequential-thinking&quot;]
},&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;215a0843-eab1-4fa3-8484-79d63b66c0bf&quot; data-toc-id=&quot;215a0843-eab1-4fa3-8484-79d63b66c0bf&quot; data-ke-size=&quot;size23&quot;&gt;2. Magic - UI 컴포넌트 생성 마법사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수천 개의 검증된 UI 패턴을 기반으로 프로덕션 레벨의 컴포넌트 생성을 지원합니다. &lt;a href=&quot;http://21st.dev&quot;&gt;21st.dev&lt;/a&gt;의 방대한 컴포넌트 라이브러리를 활용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;컴포넌트 생성&lt;/b&gt;: /ui 명령으로 즉시 UI 컴포넌트 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디자인 시스템 통합&lt;/b&gt;: 기존 디자인 시스템과 자연스럽게 통합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;접근성 준수&lt;/b&gt;: WCAG 2.1 AA 기준 자동 적용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반응형 설계&lt;/b&gt;: 모든 디바이스에 최적화된 레이아웃&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실전 활용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;복잡한 폼 생성&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;/ui 다단계 회원가입 폼 with 유효성 검사&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;생성되는 컴포넌트:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const MultiStepForm = () =&amp;gt; {
    const [currentStep, setCurrentStep] = useState(0)
    const [formData, setFormData] = useState({
        // 구조화된 폼 데이터
    })

    // 유효성 검사 로직
    // 단계별 네비게이션
    // 접근성 속성 자동 적용
    // 에러 핸들링
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대시보드 위젯&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;/ui 실시간 매출 차트 대시보드 카드&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설정 방법:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;npx @21st-dev/cli@latest install &amp;lt;client&amp;gt; --api-key &amp;lt;key&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;&quot;magic&quot;: {
    &quot;type&quot;: &quot;stdio&quot;,
    &quot;command&quot;: &quot;npx&quot;,
    &quot;args&quot;: [&quot;@21st-dev/magic&quot;],
    &quot;env&quot;: {
        &quot;TWENTYFIRST_API_KEY&quot;: &quot;&quot;
    }
},&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;2cf1ea84-96b3-4ceb-81a0-5aaf808ed4da&quot; data-toc-id=&quot;2cf1ea84-96b3-4ceb-81a0-5aaf808ed4da&quot; data-ke-size=&quot;size23&quot;&gt;3. Context7 - 최신 문서 실시간 참조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레임워크와 라이브러리는 빠르게 변화합니다. Context7은 항상 최신 공식 문서를 참조하여 정확한 코드를 생성하는 데 도움을 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;실시간 문서 조회&lt;/b&gt;: 공식 문서의 최신 버전 자동 참조&lt;/li&gt;
&lt;li&gt;&lt;b&gt;버전별 API 지원&lt;/b&gt;: 특정 버전에 맞는 정확한 구현&lt;/li&gt;
&lt;li&gt;&lt;b&gt;마이그레이션 가이드&lt;/b&gt;: 버전 업그레이드 시 변경사항 안내&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실전 활용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Next.js 15 최신 기능 활용&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;Next.js 15의 새로운 Partial Prerendering을 구현해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7이 공식 문서를 참조하여:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최신 API 사용법 확인&lt;/li&gt;
&lt;li&gt;Breaking changes 체크&lt;/li&gt;
&lt;li&gt;Best practices 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;React 19 신기능&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;React 19의 use() 훅을 활용한 데이터 페칭 구현&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설정 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;claude mcp add context7 -- npx -y @upstash/context7-mcp&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;&quot;context7&quot;: {
    &quot;command&quot;: &quot;npx&quot;,
    &quot;args&quot;: [&quot;-y&quot;, &quot;@upstash/context7-mcp@latest&quot;]
},&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;a1de6a0f-a4b0-4be3-b942-cdb53c8effb6&quot; data-toc-id=&quot;a1de6a0f-a4b0-4be3-b942-cdb53c8effb6&quot; data-ke-size=&quot;size23&quot;&gt;4. Serena - 프로젝트 메모리 시스템&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 코드베이스를 효과적으로 이해하고, 프로젝트 컨텍스트를 세션 간에 유지합니다. 심볼 수준의 코드 분석이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;심볼 탐색&lt;/b&gt;: 함수, 클래스, 변수를 정확하게 찾고 분석&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로젝트 메모리&lt;/b&gt;: 아키텍처 결정사항, 코딩 컨벤션 저장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의존성 추적&lt;/b&gt;: 코드 변경 시 영향받는 부분 자동 감지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리팩토링 지원&lt;/b&gt;: 안전한 심볼 이름 변경 및 이동&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실전 활용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 온보딩&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;/sc:load 프로젝트 구조와 주요 패턴을 분석해줘&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Serena가 수행하는 작업:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&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;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;심볼 리팩토링 전체 예시&lt;/b&gt;:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Before&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// utils/api.ts
export function getUserData(id: string) {
    return fetch(`/api/users/${id}`)
}

// components/Profile.tsx
import { getUserData } from '@/utils/api'

export default function Profile({ userId }: { userId: string }) {
    const data = await getUserData(userId)
    return &amp;lt;div&amp;gt;{data.name}&amp;lt;/div&amp;gt;
}

// components/Settings.tsx
import { getUserData } from '@/utils/api'

export default function Settings({ currentId }: { currentId: string }) {
    const user = await getUserData(currentId)
    return &amp;lt;div&amp;gt;{user.email}&amp;lt;/div&amp;gt;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프롬프트&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;getUserData 함수를 fetchUserProfile로 모든 참조와 함께 변경해줘&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;After (Serena 자동 리팩토링)&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// utils/api.ts
export function fetchUserProfile(id: string) {
    return fetch(`/api/users/${id}`)
}

// components/Profile.tsx
import { fetchUserProfile } from '@/utils/api'

export default function Profile({ userId }: { userId: string }) {
    const data = await fetchUserProfile(userId)
    return &amp;lt;div&amp;gt;{data.name}&amp;lt;/div&amp;gt;
}

// components/Settings.tsx
import { fetchUserProfile } from '@/utils/api'

export default function Settings({ currentId }: { currentId: string }) {
    const user = await fetchUserProfile(currentId)
    return &amp;lt;div&amp;gt;{user.email}&amp;lt;/div&amp;gt;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ Serena가 자동으로 처리한 작업&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수명 변경: getUserData &amp;rarr; fetchUserProfile&lt;/li&gt;
&lt;li&gt;모든 import 문 업데이트 (2개 파일)&lt;/li&gt;
&lt;li&gt;모든 함수 호출 업데이트 (2개 파일)&lt;/li&gt;
&lt;li&gt;타입 정의 일관성 유지&lt;/li&gt;
&lt;li&gt;의존성 그래프 자동 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설정 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;claude mcp add-json &quot;serena&quot; '{&quot;command&quot;:&quot;uvx&quot;,&quot;args&quot;:[&quot;--from&quot;,&quot;git+https://github.com/oraios/serena&quot;,&quot;start-mcp-server&quot;]}'&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;&quot;serena&quot;: {
    &quot;command&quot;: &quot;uvx&quot;,
    &quot;args&quot;: [&quot;--from&quot;, &quot;git+https://github.com/oraios/serena&quot;, &quot;serena&quot;, &quot;start-mcp-server&quot;]
},&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;08715227-b4a0-40f1-9922-19abc49bbfc4&quot; data-toc-id=&quot;08715227-b4a0-40f1-9922-19abc49bbfc4&quot; data-ke-size=&quot;size23&quot;&gt;5. Playwright - E2E 테스트 자동화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 브라우저에서 사용자 시나리오를 테스트하고, 시각적 회귀를 방지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;브라우저 자동화&lt;/b&gt;: Chrome, Firefox, Safari 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스크린샷 캡처&lt;/b&gt;: 시각적 회귀 테스트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 모킹&lt;/b&gt;: API 응답 시뮬레이션&lt;/li&gt;
&lt;li&gt;&lt;b&gt;접근성 테스트&lt;/b&gt;: WCAG 준수 자동 검증&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실전 활용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자 플로우 테스트&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;결제 프로세스 전체 E2E 테스트 작성&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성되는 테스트:&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;test(&quot;결제 플로우&quot;, async ({ page }) =&amp;gt; {
    // 1. 상품 선택
    await page.goto(&quot;/products&quot;)
    await page.click('[data-testid=&quot;product-1&quot;]')

    // 2. 장바구니 추가
    await page.click('button:has-text(&quot;장바구니 담기&quot;)')

    // 3. 결제 진행
    await page.goto(&quot;/checkout&quot;)
    await page.fill('[name=&quot;cardNumber&quot;]', &quot;4242424242424242&quot;)

    // 4. 결제 완료 확인
    await expect(page).toHaveURL(&quot;/order/complete&quot;)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설정 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;claude mcp add playwright npx @playwright/mcp@latest&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;&quot;playwright&quot;: {
    &quot;command&quot;: &quot;npx&quot;,
    &quot;args&quot;: [&quot;@playwright/mcp@latest&quot;]
},&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;c2184531-3cb2-4856-8e16-0b66018fe70f&quot; data-toc-id=&quot;c2184531-3cb2-4856-8e16-0b66018fe70f&quot; data-ke-size=&quot;size23&quot;&gt;6. Figma MCP Server - 디자인과 코드의 완벽한 통합&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;베타 주의사항: Figma MCP 서버는 현재 오픈 베타 중입니다. 일부 기능이 제한될 수 있으며, 성능 이슈가 발생할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Figma MCP 서버는 디자인 파일을 개발 워크플로우에 직접 통합하여, AI가 디자인 정보와 컨텍스트를 이해하고 코드를 생성할 수 있게 합니다. Model Context Protocol의 표준 인터페이스를 통해 데이터 소스와 AI 에이전트가 상호작용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용 요구사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;플랜&lt;/b&gt;: Professional, Organization, Enterprise 플랜의 Dev 또는 Full 시트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지원 도구&lt;/b&gt;: MCP를 지원하는 코드 에디터 (VS Code, Cursor, Windsurf, Claude Code)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프레임에서 코드 생성&lt;/b&gt;: Figma 프레임을 선택하여 즉시 코드로 변환. 새로운 플로우 구축이나 앱 기능 반복 개발에 최적&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디자인 컨텍스트 추출&lt;/b&gt;: 변수, 컴포넌트, 레이아웃 데이터를 IDE로 직접 가져오기. 디자인 시스템과 컴포넌트 기반 워크플로우에 특히 유용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Make 리소스 검색&lt;/b&gt;: Make 파일에서 코드 리소스를 수집하여 LLM에 컨텍스트로 제공. 프로토타입에서 프로덕션 애플리케이션으로 전환 시 도움&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Code Connect로 일관성 유지&lt;/b&gt;: 실제 컴포넌트를 재사용하여 출력 품질 향상. 생성된 코드를 코드베이스와 일관되게 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;연결 방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Figma MCP 서버는 두 가지 방식으로 연결 가능합니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Remote MCP Server&lt;/b&gt;: Figma의 호스팅 엔드포인트에 직접 연결 (데스크톱 앱 불필요)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Local MCP Server&lt;/b&gt;: 로컬 머신의 Figma 데스크톱 앱을 통해 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Remote Server 설정 (Claude Code)&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;터미널에서 다음 명령 실행:&lt;/li&gt;
&lt;li&gt;claude mcp add --transport http figma &amp;lt;&lt;a href=&quot;https://mcp.figma.com/mcp&quot;&gt;https://mcp.figma.com/mcp&lt;/a&gt;&amp;gt;&lt;/li&gt;
&lt;li&gt;Claude에서 /mcp 입력하여 MCP 서버 관리 후 figma 선택&lt;/li&gt;
&lt;li&gt;Authenticate 선택하여 인증 진행&lt;/li&gt;
&lt;li&gt;Allow Access 클릭하여 권한 부여&lt;/li&gt;
&lt;li&gt;Claude Code에서 Authentication successful. Connected to figma 메시지 확인&lt;/li&gt;
&lt;li&gt;/mcp 명령으로 연결 상태 최종 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설정 코드 (Remote)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;figma&quot;: {
  &quot;url&quot;: &quot;https://mcp.figma.com/mcp&quot;,
  &quot;type&quot;: &quot;http&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실전 활용 예시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디자인 시스템 컴포넌트 동기화&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;Figma의 Button 컴포넌트를 모든 variant와 함께 React 코드로 변환&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디자인 토큰 추출&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;Figma에서 색상, 타이포그래피, 스페이싱 변수를 추출하여 CSS 변수로 변환&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프레임을 컴포넌트로 변환&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;선택한 Figma 프레임을 반응형 React 컴포넌트로 변환 (Tailwind CSS 사용)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: Remote 서버는 점진적으로 배포 중입니다. MCP 클라이언트에서 0개의 도구가 표시되면 아직 액세스가 활성화되지 않은 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 id=&quot;93d5151b-5276-4f12-ae0a-72bbdf292aa6&quot; data-toc-id=&quot;93d5151b-5276-4f12-ae0a-72bbdf292aa6&quot; data-ke-size=&quot;size23&quot;&gt;7. Notion MCP - AI와 워크스페이스의 완벽한 연결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Notion MCP는 AI 도구가 Notion 워크스페이스에 안전하게 액세스할 수 있도록 하는 호스팅 서버입니다. Model Context Protocol(MCP) 오픈 표준을 통해 AI 어시스턴트가 Notion 워크스페이스와 상호작용할 수 있습니다. ChatGPT, Cursor, Claude 같은 인기 AI 어시스턴트와 원활하게 작동하도록 설계되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 필요한가?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;간편한 설정&lt;/b&gt;: OAuth를 통한 간단한 연결, 지원되는 AI 도구에서 원클릭 설치&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전체 워크스페이스 액세스&lt;/b&gt;: AI 도구가 사용자와 동일하게 Notion 페이지를 읽고 쓸 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI 최적화&lt;/b&gt;: 효율적인 데이터 포맷팅으로 AI 에이전트를 위해 특별히 구축&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 활용 사례&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;문서 생성&lt;/b&gt;: 연구 및 프로젝트 데이터에서 PRD, 기술 스펙, 아키텍처 문서 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색 및 답변&lt;/b&gt;: AI가 모든 Notion 및 연결된 워크스페이스 콘텐츠를 검색&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작업 관리&lt;/b&gt;: 작업 설명에서 코드 스니펫 생성 및 프로젝트 상태 자동 업데이트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보고서 작성&lt;/b&gt;: 여러 소스에서 릴리즈 노트, 프로젝트 업데이트, 성과 보고서 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;캠페인 계획&lt;/b&gt;: 포괄적인 브리프 생성 및 마케팅 채널 전반의 진행 상황 추적&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설정 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;claude mcp add --transport http notion &amp;lt;https://mcp.notion.com/mcp&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;notion&quot;: {
  &quot;type&quot;: &quot;http&quot;,
  &quot;url&quot;: &quot;&amp;lt;https://mcp.notion.com/mcp&amp;gt;&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;4667887c-1a77-4dbe-8189-80f2f116a111&quot; data-toc-id=&quot;4667887c-1a77-4dbe-8189-80f2f116a111&quot; data-ke-size=&quot;size23&quot;&gt;실전 활용 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 문서 자동 생성&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;이번 스프린트의 완료된 태스크를 기반으로 릴리즈 노트를 Notion에 작성&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기술 스펙 작성&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;코드베이스 분석 결과를 바탕으로 API 문서를 Notion에 생성&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;회의록 정리&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;오늘 회의 내용을 정리해서 Notion 회의록 데이터베이스에 추가&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 상태 업데이트&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;Git 커밋 기반으로 프로젝트 진행 상황을 Notion 대시보드에 업데이트&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;09b16153-e8eb-48ec-870c-6ead6a893672&quot; data-toc-id=&quot;09b16153-e8eb-48ec-870c-6ead6a893672&quot; data-ke-size=&quot;size26&quot;&gt;5️⃣&amp;nbsp;실전 워크플로우&lt;/h2&gt;
&lt;h3 id=&quot;890e629c-cd9e-499a-8e4c-5329fdb76bc2&quot; data-toc-id=&quot;890e629c-cd9e-499a-8e4c-5329fdb76bc2&quot; data-ke-size=&quot;size23&quot;&gt;풀스택 기능 개발 워크플로우&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. Figma: 디자인 컴포넌트 추출 및 동기화
   &amp;darr;
2. Magic: UI 컴포넌트 생성 및 보완
   &amp;darr;
3. Sequential-Thinking: 비즈니스 로직 설계
   &amp;darr;
4. Context7: 최신 API 패턴 확인
   &amp;darr;
5. Serena: 코드베이스 통합 및 리팩토링
   &amp;darr;
6. Playwright: E2E 테스트 작성
   &amp;darr;
7. Notion: 기능 문서화&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;a09d8c3b-e4cf-4ac7-8906-e548cf66d626&quot; data-toc-id=&quot;a09d8c3b-e4cf-4ac7-8906-e548cf66d626&quot; data-ke-size=&quot;size23&quot;&gt;대규모 리팩토링 워크플로우&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. Serena: 의존성 그래프 분석
   &amp;darr;
2. Sequential-Thinking: 리팩토링 전략 수립
   &amp;darr;
3. Context7: 새 API 마이그레이션 가이드
   &amp;darr;
4. Playwright: 회귀 테스트
   &amp;darr;
5. Notion: 변경사항 문서화&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;7a9b7aff-113c-4b32-8360-1d1bb13257e7&quot; data-toc-id=&quot;7a9b7aff-113c-4b32-8360-1d1bb13257e7&quot; data-ke-size=&quot;size23&quot;&gt;버그 수정 워크플로우&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. Sequential-Thinking: 버그 원인 분석
   &amp;darr;
2. Serena: 영향 범위 파악
   &amp;darr;
3. Context7: 올바른 해결책 확인
   &amp;darr;
4. Playwright: 버그 재현 테스트
   &amp;darr;
5. Notion: 버그 리포트 작성&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;d63a5f2e-169f-4aa7-8792-6f70a41cf1d4&quot; data-toc-id=&quot;d63a5f2e-169f-4aa7-8792-6f70a41cf1d4&quot; data-ke-size=&quot;size26&quot;&gt;6️⃣ 단계별 추천&lt;/h2&gt;
&lt;h3 id=&quot;f5fa47e6-8371-40f9-9dd7-207fc5f1db04&quot; data-toc-id=&quot;f5fa47e6-8371-40f9-9dd7-207fc5f1db04&quot; data-ke-size=&quot;size23&quot;&gt;입문자를 위한 시작 순서&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Context7&lt;/b&gt; 설치: 최신 문서 참조를 위한 기본 도구&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Magic&lt;/b&gt; 설치: UI 컴포넌트 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Playwright&lt;/b&gt; 설치: 테스트 자동화&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;27934318-5daa-4569-9077-f11bfa6b98d0&quot; data-toc-id=&quot;27934318-5daa-4569-9077-f11bfa6b98d0&quot; data-ke-size=&quot;size23&quot;&gt;중급자 확장 패키지&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Serena&lt;/b&gt; 추가: 프로젝트 메모리 관리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sequential-Thinking&lt;/b&gt; 추가: 복잡한 문제 해결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Notion&lt;/b&gt; 연동: 문서화 시스템&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;23c22a31-5cc5-40b6-b12a-4902c83c11ff&quot; data-toc-id=&quot;23c22a31-5cc5-40b6-b12a-4902c83c11ff&quot; data-ke-size=&quot;size23&quot;&gt;고급 사용자를 위한 확장&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Figma&lt;/b&gt;: 디자인 시스템 통합&lt;/li&gt;
&lt;li&gt;추가 MCP 서버 탐색 및 커스터마이징&lt;/li&gt;
&lt;li&gt;모든 MCP 조합 최적화&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;3aa5f098-2954-4d43-a0fd-ac66e975b8a6&quot; data-toc-id=&quot;3aa5f098-2954-4d43-a0fd-ac66e975b8a6&quot; data-ke-size=&quot;size26&quot;&gt;7️⃣&amp;nbsp;마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 7개의 핵심 MCP 서버는 제가 실제 프로덕션 환경에서 검증한 조합입니다. 각 서버는 특정 영역에서 Claude Code의 능력을 향상시키며, 함께 사용했을 때 시너지 효과를 발휘할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&quot;c7ba615f-28bd-4630-98ac-7fc9ef8c5b86&quot; data-toc-id=&quot;c7ba615f-28bd-4630-98ac-7fc9ef8c5b86&quot; data-ke-size=&quot;size23&quot;&gt;핵심 메시지&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;점진적 도입&lt;/b&gt;: 모든 MCP를 한 번에 설치하기보다는 필요에 따라 추가하세요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;워크플로우 중심&lt;/b&gt;: 자신의 개발 패턴에 맞는 MCP를 선택하세요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조합의 힘&lt;/b&gt;: 단일 MCP보다 적절한 조합이 더 효과적일 수 있습니다&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;d9927106-a5ec-4593-ac34-151c7a3fa200&quot; data-toc-id=&quot;d9927106-a5ec-4593-ac34-151c7a3fa200&quot; data-ke-size=&quot;size23&quot;&gt;실전 팁&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Sequential-Thinking + Serena = 체계적인 코드 분석&lt;/li&gt;
&lt;li&gt;Magic + Figma = 디자인 시스템 자동화&lt;/li&gt;
&lt;li&gt;Context7 + Notion = 최신 문서 관리 체계&lt;/li&gt;
&lt;li&gt;Serena + Playwright = 리팩토링 후 자동 검증&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 단순한 코드 보조 도구가 아닌, 실제 팀의 일원으로 활용할 수 있는 단계로 나아가 보세요.!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;</description>
      <category>SETTING</category>
      <category>CC</category>
      <category>cc mcp</category>
      <category>Claude</category>
      <category>claude-code</category>
      <category>claudecode mcp</category>
      <category>claude_code</category>
      <category>mcp</category>
      <category>클로드</category>
      <category>클로드코드</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/281</guid>
      <comments>https://baekspace.tistory.com/281#entry281comment</comments>
      <pubDate>Wed, 7 Jan 2026 20:43:15 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code for Front-end #2: .claude 디렉토리</title>
      <link>https://baekspace.tistory.com/280</link>
      <description>&lt;h2 id=&quot;b659338e-b67a-428a-89ca-2daa84c79bde&quot; data-pm-slice=&quot;1 1 []&quot; data-toc-id=&quot;b659338e-b67a-428a-89ca-2daa84c79bde&quot; data-ke-size=&quot;size26&quot;&gt;모든 프로젝트에 적용되는 나만의 AI 환경 - 프로젝트를 넘나들며 일관성 유지하기&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;4f85e0c2-b8aa-4453-a6c5-12a6cdb7515d&quot; data-toc-id=&quot;4f85e0c2-b8aa-4453-a6c5-12a6cdb7515d&quot; data-ke-size=&quot;size26&quot;&gt;1️⃣ 들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1편에서 프로젝트별 CLAUDE.md 작성법을 소개했습니다. 각 프로젝트에서 Claude Code가 의도를 정확히 이해하고 코드를 작성할 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 여러 프로젝트를 진행하면서 한 가지 패턴을 발견했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모든 &lt;/b&gt;CLAUDE.md&lt;b&gt;에서 반복되는 내용이 존재합니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# 프로젝트 A의 CLAUDE.md
- TypeScript에서 `any` 타입 사용 금지
- TODO 주석 대신 완전한 구현
- Git feature branch 전략 사용
- 한국어로 응답

# 프로젝트 B의 CLAUDE.md
- TypeScript에서 `any` 타입 사용 금지 (또 쓴다...)
- TODO 주석 대신 완전한 구현 (또 쓴다...)
- Git feature branch 전략 사용 (또...)
- 한국어로 응답 (또...)

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 규칙들은 &lt;b&gt;프로젝트별이 아니라 개인의 코딩 철학&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하는 방법이 바로 ~/.claude/ 디렉토리입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;3452bee8-e68c-4f81-af0d-b4f2ffd8ab0b&quot; data-toc-id=&quot;3452bee8-e68c-4f81-af0d-b4f2ffd8ab0b&quot; data-ke-size=&quot;size26&quot;&gt;2️⃣ .claude 디렉토리란?&lt;/h2&gt;
&lt;h3 id=&quot;b10ce0df-3995-498d-90e2-04702ac712d1&quot; data-toc-id=&quot;b10ce0df-3995-498d-90e2-04702ac712d1&quot; data-ke-size=&quot;size23&quot;&gt;홈 디렉토리의 전역 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;~/.claude/ 는 홈 디렉토리에 만드는 전역 설정 공간입니다.&lt;/p&gt;
&lt;pre class=&quot;tex&quot;&gt;&lt;code&gt;# macOS/Linux
~/.claude/
└── CLAUDE.md

# Windows
C:\\\\Users\\\\[username]\\\\.claude\\\\
└── CLAUDE.md

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;4b39f008-3bd0-4e5b-940f-3dd229779465&quot; data-toc-id=&quot;4b39f008-3bd0-4e5b-940f-3dd229779465&quot; data-ke-size=&quot;size23&quot;&gt;설정 우선순위 체계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 다음 순서로 설정을 읽어옵니다:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. Claude Code 기본 동작
   &amp;darr;
2. ~/.claude/CLAUDE.md (전역 설정)
   &amp;darr;
3. [프로젝트]/CLAUDE.md (프로젝트 설정)
   &amp;darr;
4. 대화 중 명시적 지시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중요한 점&lt;/b&gt;: 아래 단계가 위 단계를 덮어씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전역: &quot;한국어로 응답&quot;&lt;/li&gt;
&lt;li&gt;프로젝트: &quot;영어로 응답&quot; (문서화 프로젝트)&lt;/li&gt;
&lt;li&gt;결과: 해당 프로젝트에서는 영어로 응답&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;b66b1e33-12c3-405d-8876-813c1d8db492&quot; data-toc-id=&quot;b66b1e33-12c3-405d-8876-813c1d8db492&quot; data-ke-size=&quot;size23&quot;&gt;언제 전역으로 빼야 하는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전역으로 관리 (&lt;/b&gt;~/.claude/&lt;b&gt;)&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개인 코딩 철학 (any 금지, DRY 원칙)&lt;/li&gt;
&lt;li&gt;선호하는 응답 언어&lt;/li&gt;
&lt;li&gt;Git 워크플로우&lt;/li&gt;
&lt;li&gt;보편적인 품질 기준&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트별 관리 (&lt;/b&gt;CLAUDE.md&lt;b&gt;)&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기술 스택 (Next.js, React, Vue)&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;판단 기준&lt;/b&gt;: &quot;다른 프로젝트에서도 똑같이 적용할 규칙인가?&quot;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;d6859d96-0dec-46f3-ae7a-08506b0669a7&quot; data-toc-id=&quot;d6859d96-0dec-46f3-ae7a-08506b0669a7&quot; data-ke-size=&quot;size26&quot;&gt;3️⃣ ~/.claude/CLAUDE.md 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 설정에서 제가 실제로 사용하는 규칙들을 소개합니다.&lt;/p&gt;
&lt;h3 id=&quot;87540e7a-2357-40ca-bd3d-08b58e3b9bb3&quot; data-toc-id=&quot;87540e7a-2357-40ca-bd3d-08b58e3b9bb3&quot; data-ke-size=&quot;size23&quot;&gt;1. 언어 설정&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;# Language Preference

Always respond in Korean using formal language (존댓말).
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;효과&lt;/b&gt;: 모든 프로젝트에서 한국어로 대화할 수 있습니다. 영어 문서화가 필요한 프로젝트에서만 CLAUDE.md에서 재정의합니다.&lt;/p&gt;
&lt;h3 id=&quot;f970a9ad-049a-4300-8f1c-5997930444fa&quot; data-toc-id=&quot;f970a9ad-049a-4300-8f1c-5997930444fa&quot; data-ke-size=&quot;size23&quot;&gt;2. TypeScript 규칙&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;## TypeScript Rules

### Type Safety
- **Never use `any` type** - Always use `unknown`, specific types, or generics
- Prefer interfaces over types for object shapes
- Always define return types for functions
- Use strict mode settings

### Examples

```typescript
// ❌ Bad
function fetchData(): any {
  return fetch('/api/data')
}

// ✅ Good
interface ApiResponse {
  data: User[]
  total: number
}

async function fetchData(): Promise&amp;lt;ApiResponse&amp;gt; {
  const response = await fetch('/api/data')
  return response.json()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;효과&lt;/b&gt;: Claude Code가 절대 any 타입을 제안하지 않습니다.&lt;/p&gt;
&lt;h3 id=&quot;b8aae27a-6839-44a9-8216-c21947f8e303&quot; data-toc-id=&quot;b8aae27a-6839-44a9-8216-c21947f8e303&quot; data-ke-size=&quot;size23&quot;&gt;3. 코드 품질 기준&lt;/h3&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;## Code Quality Standards

### Implementation Rules
- **No TODO comments** - Implement completely or don't start
- **No magic numbers/strings** - Define as constants
- **No console.log** - Use proper logging utility
- **Maximum function length**: 30 lines
- **Maximum component length**: 150 lines

### Examples

```typescript
// ❌ Bad
function calculate(price: number) {
  // TODO: add tax calculation
  return price * 1.1  // Magic number
}

// ✅ Good
const TAX_RATE = 0.1

function calculate(price: number): number {
  return price * (1 + TAX_RATE)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;효과&lt;/b&gt;: Claude Code가 항상 완전한 코드를 작성하며, magic number를 상수로 정의합니다.&lt;/p&gt;
&lt;h3 id=&quot;e198c1aa-0748-4208-b2eb-c03fb09c849c&quot; data-toc-id=&quot;e198c1aa-0748-4208-b2eb-c03fb09c849c&quot; data-ke-size=&quot;size23&quot;&gt;4. Git 워크플로우&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;## Git Workflow

### Branch Strategy
- Always work on feature branches
- Never commit directly to `main` or `master`
- Use meaningful commit messages

### Commit Message Format
- feat: Add user authentication 
- fix: Resolve login redirect issue 
- refactor: Simplify API client logic

### Before Commit Checklist
1. Run linting: `npm run lint`
2. Run tests: `npm run test`
3. Review changes: `git diff`

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;효과&lt;/b&gt;: Claude Code가 Git 작업 시 항상 feature branch를 사용하고, 린트/테스트를 실행합니다.&lt;/p&gt;
&lt;h3 id=&quot;b6ccc083-8338-412e-9fd8-c35d289a4e9e&quot; data-toc-id=&quot;b6ccc083-8338-412e-9fd8-c35d289a4e9e&quot; data-ke-size=&quot;size23&quot;&gt;5. 금지 패턴&lt;/h3&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## Prohibited Patterns

- ❌ **No `any` type**
- ❌ **No TODO comments**
- ❌ **No console.log** (use logger)
- ❌ **No inline styles** (use CSS/Tailwind)
- ❌ **No magic numbers** (define constants)
- ❌ **No partial implementations** (complete or don't start)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;효과&lt;/b&gt;: 이런 패턴을 제안하면 즉시 거부하고 대안을 제시합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;b03e0d2c-5968-422d-b492-02ce1bac72bc&quot; data-toc-id=&quot;b03e0d2c-5968-422d-b492-02ce1bac72bc&quot; data-ke-size=&quot;size26&quot;&gt;4️⃣ 실제 적용 효과&lt;/h2&gt;
&lt;h3 id=&quot;e8aa55c8-3612-4e93-b76e-6b2b0804bd6f&quot; data-toc-id=&quot;e8aa55c8-3612-4e93-b76e-6b2b0804bd6f&quot; data-ke-size=&quot;size23&quot;&gt;Before: 반복 설명의 지옥&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 A에서&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;나: &quot;API 호출 함수 만들어줘&quot;
Claude: [함수 생성]
나: &quot;any 타입 쓰지 마&quot;
Claude: [수정]
나: &quot;console.log 빼고&quot;
Claude: [수정]
나: &quot;TODO 주석 말고 완전히 구현해&quot;
Claude: [수정]

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 B로 이동&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;나: &quot;API 호출 함수 만들어줘&quot;
Claude: [함수 생성]
나: &quot;any 타입 쓰지 마&quot; (또...)
Claude: [수정]
나: &quot;console.log 빼고&quot; (또...)
Claude: [수정]

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;759c0156-50d6-4c96-b438-23bedc04cdfa&quot; data-toc-id=&quot;759c0156-50d6-4c96-b438-23bedc04cdfa&quot; data-ke-size=&quot;size23&quot;&gt;After: 한 번 설정, 영원히 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;~/.claude/CLAUDE.md 설정 후&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;나: &quot;API 호출 함수 만들어줘&quot;
Claude: [any 없이, console.log 없이, 완전히 구현된 함수 생성]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 프로젝트에서 동일하게 적용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;e2c9b0dd-40ff-481e-bc5c-5ecfaae564c6&quot; data-toc-id=&quot;e2c9b0dd-40ff-481e-bc5c-5ecfaae564c6&quot; data-ke-size=&quot;size26&quot;&gt;5️⃣ 전역 vs 프로젝트별 분리 전략&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 실제로 사용하는 분리 기준입니다.&lt;/p&gt;
&lt;h3 id=&quot;089fa32b-7c4b-4dad-80eb-505d32a61a2d&quot; data-toc-id=&quot;089fa32b-7c4b-4dad-80eb-505d32a61a2d&quot; data-ke-size=&quot;size23&quot;&gt;전역 설정 (~/.claude/CLAUDE.md)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 언어 및 커뮤니케이션&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 응답 언어 (한국어/영어)
- 응답 스타일 (formal/casual)
- 설명 상세도&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 코딩 철학&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- TypeScript any 금지
- DRY (Don't Repeat Yourself) 원칙
- SOLID 원칙
- 완전 구현 원칙 (No TODO)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Git 워크플로우&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- Feature branch 전략
- Commit message 형식
- PR 전 체크리스트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 품질 기준&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 함수 최대 길이
- 컴포넌트 최대 길이
- 네이밍 컨벤션 기본 원칙
- 금지 패턴&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;f236c760-24df-41aa-ab78-2eff36228659&quot; data-toc-id=&quot;f236c760-24df-41aa-ab78-2eff36228659&quot; data-ke-size=&quot;size23&quot;&gt;프로젝트별 설정 (CLAUDE.md)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 기술 스택&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 프레임워크 (Next.js, React, Vue)
- 상태 관리 (Zustand, Redux, Recoil)
- 스타일링 (Tailwind, styled-components)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 프로젝트 구조&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;- 디렉토리 구조
- 파일 명명 규칙
- Import 경로 (alias)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 개발 명령어&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- dev server 실행
- build 명령어
- 테스트 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 특화 패턴&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- API 호출 패턴
- 컴포넌트 템플릿
- 상태 관리 패턴&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;0a8ff8c2-5357-446b-880e-f621361ad1a1&quot; data-toc-id=&quot;0a8ff8c2-5357-446b-880e-f621361ad1a1&quot; data-ke-size=&quot;size23&quot;&gt;판단 플로우차트&lt;/h3&gt;
&lt;pre class=&quot;objectivec&quot;&gt;&lt;code&gt;규칙을 추가하려고 합니다
    &amp;darr;
이 규칙이 모든 프로젝트에 적용되나요?
    &amp;darr;
YES &amp;rarr; ~/.claude/CLAUDE.md
NO &amp;rarr; [프로젝트]/CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;190b22b3-3e72-4626-b47e-80d9902fe957&quot; data-toc-id=&quot;190b22b3-3e72-4626-b47e-80d9902fe957&quot; data-ke-size=&quot;size26&quot;&gt;6️⃣ 작성 팁&lt;/h2&gt;
&lt;h3 id=&quot;f513bfa8-c4ec-479f-ab22-1b55a3924cd2&quot; data-toc-id=&quot;f513bfa8-c4ec-479f-ab22-1b55a3924cd2&quot; data-ke-size=&quot;size23&quot;&gt;1. 점진적으로 추가하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 완벽한 전역 설정을 만들 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;권장 방법&lt;/b&gt;:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로젝트 2-3개에서 CLAUDE.md 작성&lt;/li&gt;
&lt;li&gt;반복되는 규칙 발견&lt;/li&gt;
&lt;li&gt;전역으로 이동&lt;/li&gt;
&lt;li&gt;프로젝트별 CLAUDE.md에서 제거&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;# 프로젝트 A, B, C 모두에서 발견
&quot;TypeScript any 금지&quot;
&amp;rarr; ~/.claude/CLAUDE.md로 이동

# 프로젝트 A, B, C의 CLAUDE.md에서 제거
이제 각 프로젝트는 자신만의 특화 설정만 유지&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;6496b055-7742-44ad-b760-ce30c00f7864&quot; data-toc-id=&quot;6496b055-7742-44ad-b760-ce30c00f7864&quot; data-ke-size=&quot;size23&quot;&gt;2. 실제 사용 기반&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이론이 아닌 실제 사용 패턴을 문서화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;❌ 나쁜 예&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# 읽은 책의 모든 원칙을 나열
- SOLID 원칙
- GoF 디자인 패턴 23가지
- Clean Code 규칙 100가지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 좋은 예&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# 실제로 자주 지적하는 것만
- any 타입 금지 (매번 지적함)
- TODO 주석 금지 (매번 지적함)
- console.log 금지 (매번 지적함)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;73fe7a8a-b6bc-4152-b2e7-86d081a0b93b&quot; data-toc-id=&quot;73fe7a8a-b6bc-4152-b2e7-86d081a0b93b&quot; data-ke-size=&quot;size23&quot;&gt;3. 정기적 업데이트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;워크플로우가 변경되면 설정도 함께 업데이트합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;업데이트 시점&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 금지 패턴 발견 시&lt;/li&gt;
&lt;li&gt;개발 프로세스 변경 시&lt;/li&gt;
&lt;li&gt;새 프레임워크 도입 시&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;# 6개월 전: React Class Components 사용
## Component Rules
- Use class components with lifecycle methods
- Use HOCs (Higher-Order Components) for reusability
- Manage state with this.setState()

# 현재: React Hooks 전환 완료
## Component Rules
- Use functional components with Hooks
- Use custom hooks for reusability
- No class components (use hooks instead)
- Prefer useState and useEffect&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;f3aeea78-91b7-4990-8191-81b06dfcd0a5&quot; data-toc-id=&quot;f3aeea78-91b7-4990-8191-81b06dfcd0a5&quot; data-ke-size=&quot;size26&quot;&gt;7️⃣ 완성 템플릿&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 복사해서 사용할 수 있는 ~/.claude/CLAUDE.md 템플릿입니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# Global Claude Code Configuration

This file contains my personal coding preferences applied to all projects.

## Language Preference

Always respond in Korean using formal language.

## TypeScript Rules

### Type Safety
- Never use `any` type - use `unknown`, specific types, or generics
- Prefer interfaces over types for object shapes
- Always define return types for functions

### Examples

```typescript
// ❌ Bad
function getData(): any {
  return fetch('/api/data')
}

// ✅ Good
interface ApiResponse {
  data: unknown
  status: number
}

async function getData(): Promise {
  const response = await fetch('/api/data')
  return response.json()
}
```

## Code Quality Standards

### Implementation Rules
- No TODO comments - implement completely or don't start
- No magic numbers/strings - define as constants
- No console.log - use proper logging utility
- Maximum function length: 30 lines
- Maximum component length: 150 lines

### Examples

```typescript
// ❌ Bad
function calculate(price: number) {
  // TODO: add tax calculation
  console.log(price)
  return price * 1.1
}

// ✅ Good
const TAX_RATE = 0.1

function calculate(price: number): number {
  logger.debug('Calculating price with tax', { price })
  return price * (1 + TAX_RATE)
}
```

## Git Workflow

### Branch Strategy
- Always work on feature branches
- Never commit directly to `main` or `master`
- Branch naming: `feature/feature-name`, `fix/bug-name`

### Commit Message Format
```
feat: Add new feature
fix: Resolve bug
refactor: Improve code structure
docs: Update documentation
test: Add or update tests
```

### Before Commit Checklist
1. Run linting
2. Run tests
3. Review changes with `git diff`

## Prohibited Patterns

- ❌ No `any` type
- ❌ No TODO comments
- ❌ No console.log
- ❌ No inline styles
- ❌ No magic numbers
- ❌ No partial implementations

## Naming Conventions

### General Rules
- **Constants**: UPPER_SNAKE_CASE
- **Functions**: camelCase
- **Classes/Components**: PascalCase
- **Files**: Match the export (PascalCase for components, camelCase for utilities)

### Examples

```typescript
// Constants
const API_BASE_URL = '&amp;lt;https://api.example.com&amp;gt;'
const MAX_RETRY_COUNT = 3

// Functions
function fetchUserData() {}
function calculateTotalPrice() {}

// Classes/Components
class UserService {}
function UserProfile() {}

// Files
UserProfile.tsx // Component file
apiClient.ts // Utility file
```

## Code Organization

### Import Order
1. External packages (react, next, lodash)
2. Internal absolute imports (@/components, @/lib)
3. Relative imports (./components, ../utils)

### File Structure
- Keep files under 300 lines
- One component per file
- Co-locate related files (component + styles + tests)

## Comments

### When to Comment
- ✅ Complex algorithms
- ✅ Business logic rationale
- ✅ Non-obvious workarounds

### When NOT to Comment
- ❌ Obvious code behavior
- ❌ TODO/FIXME (complete or create ticket)
- ❌ Commented-out code (use git history)

## Testing

### Test Coverage Goals
- Critical paths: 80%+
- Utilities: 90%+
- Components: 70%+

### Test Naming
```typescript
// ✅ Good
describe('UserService', () =&amp;gt; {
  it('should return user data when user exists', () =&amp;gt; {})
  it('should throw error when user not found', () =&amp;gt; {})
})
```

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;afbfb577-7624-4f86-a3af-6ed673bbe9b0&quot; data-toc-id=&quot;afbfb577-7624-4f86-a3af-6ed673bbe9b0&quot; data-ke-size=&quot;size26&quot;&gt;8️⃣ 정리&lt;/h2&gt;
&lt;h3 id=&quot;694115b9-2f3e-4603-8a97-dbfefd918898&quot; data-toc-id=&quot;694115b9-2f3e-4603-8a97-dbfefd918898&quot; data-ke-size=&quot;size23&quot;&gt;핵심 포인트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전역 설정의 힘&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 번 작성으로 모든 프로젝트 적용&lt;/li&gt;
&lt;li&gt;반복 설명 제거&lt;/li&gt;
&lt;li&gt;일관된 코드 품질 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;분리 전략&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전역: 개인 철학, 언어, 워크플로우&lt;/li&gt;
&lt;li&gt;프로젝트별: 기술 스택, 구조, 특화 패턴&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;작성 팁&lt;/b&gt;:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;점진적으로 추가&lt;/li&gt;
&lt;li&gt;실제 사용 기반&lt;/li&gt;
&lt;li&gt;정기적 업데이트&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;21926f1a-c684-4a85-8c73-3ae596ccb711&quot; data-toc-id=&quot;21926f1a-c684-4a85-8c73-3ae596ccb711&quot; data-ke-size=&quot;size23&quot;&gt;시작하기&lt;/h3&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 1. ~/.claude 디렉토리 생성
mkdir ~/.claude

# 2. CLAUDE.md 생성
touch ~/.claude/CLAUDE.md

# 3. 위 템플릿 복사하여 시작
# 4. 프로젝트별 CLAUDE.md에서 중복 제거

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;d7cd3513-be3e-450f-974a-e22032656356&quot; data-toc-id=&quot;d7cd3513-be3e-450f-974a-e22032656356&quot; data-ke-size=&quot;size23&quot;&gt;체크리스트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[ ] ~/.claude 디렉토리 생성&lt;/li&gt;
&lt;li&gt;[ ] CLAUDE.md 작성 (언어, TypeScript, 품질 기준)&lt;/li&gt;
&lt;li&gt;[ ] 프로젝트별 CLAUDE.md에서 중복 제거&lt;/li&gt;
&lt;li&gt;[ ] 새 프로젝트에서 테스트&lt;/li&gt;
&lt;li&gt;[ ] 정기적 업데이트 계획&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;93ea751d-1c39-4e02-bd1e-e0c35bbbd716&quot; data-toc-id=&quot;93ea751d-1c39-4e02-bd1e-e0c35bbbd716&quot; data-ke-size=&quot;size23&quot;&gt;효과&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;반복 설명 제거&lt;/b&gt;: 모든 프로젝트에 자동 적용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일관된 품질&lt;/b&gt;: 개인 기준 유지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시간 절약&lt;/b&gt;: 프로젝트 전환 시 재설명 불필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;팀 온보딩&lt;/b&gt;: 새 팀원이 규칙 이해 용이&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;45e65432-288c-4485-983b-8e779b6fe6bb&quot; data-toc-id=&quot;45e65432-288c-4485-983b-8e779b6fe6bb&quot; data-ke-size=&quot;size23&quot;&gt;베스트 프랙티스&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;점진적 추가&lt;/b&gt;: 필요한 규칙부터 하나씩 추가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실제 사용 기반&lt;/b&gt;: 이론이 아닌 실제 사용 패턴 문서화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정기적 업데이트&lt;/b&gt;: 워크플로우 변화에 맞춰 수정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;플래그 활용&lt;/b&gt;: 상황에 맞는 플래그 조합 실험&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.claude 디렉토리는 프로젝트를 넘나들며 일관성을 유지하는 핵심입니다. 지금 바로 ~/.claude/ 디렉토리를 만들고 나만의 AI 환경을 구축해보세요.&lt;/p&gt;</description>
      <category>SETTING</category>
      <category>.claude</category>
      <category>CC</category>
      <category>claude-code</category>
      <category>claude_code</category>
      <category>클로드</category>
      <category>클로드코드</category>
      <category>클로드코드 세팅</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/280</guid>
      <comments>https://baekspace.tistory.com/280#entry280comment</comments>
      <pubDate>Wed, 7 Jan 2026 20:41:32 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code for Front-end #1: CLAUDE.md 작성 가이드</title>
      <link>https://baekspace.tistory.com/279</link>
      <description>&lt;h2 id=&quot;25017c79-bc89-4f36-ab79-025817d0091d&quot; data-pm-slice=&quot;1 1 []&quot; data-toc-id=&quot;25017c79-bc89-4f36-ab79-025817d0091d&quot; data-ke-size=&quot;size26&quot;&gt;Claude Code와 대화하는 법 - AI에게 프로젝트를 설명하는 가장 효과적인 방법&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;ace8cce4-6cf7-43ca-becd-5baef8143ab7&quot; data-toc-id=&quot;ace8cce4-6cf7-43ca-becd-5baef8143ab7&quot; data-ke-size=&quot;size26&quot;&gt;1️⃣ 들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code에게 &quot;회원가입 페이지 만들어줘&quot;라고 요청했을 때 두 가지 상황이 벌어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상황 A&lt;/b&gt;: &quot;어떤 필드가 필요하신가요? 어떤 상태 관리 라이브러리를 쓰시나요? 스타일링은 어떻게 하시나요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상황 B&lt;/b&gt;: &quot;React Hook Form과 Zod로 이메일/비밀번호/비밀번호 확인 필드를 만들었습니다. Zustand로 인증 상태를 관리하고 Tailwind로 스타일링했습니다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이는 단 하나, &lt;a href=&quot;http://CLAUDE.md&quot;&gt;&lt;b&gt;CLAUDE.md&lt;/b&gt;&lt;/a&gt;&lt;b&gt; 파일의 존재 여부&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;8ce0778b-966d-4ef7-b4d4-a63110b390cd&quot; data-toc-id=&quot;8ce0778b-966d-4ef7-b4d4-a63110b390cd&quot; data-ke-size=&quot;size26&quot;&gt;2️⃣ CLAUDE.md가 필요한 이유&lt;/h2&gt;
&lt;h3 id=&quot;e70d986e-5454-4697-9a95-218cc4e6e0ea&quot; data-toc-id=&quot;e70d986e-5454-4697-9a95-218cc4e6e0ea&quot; data-ke-size=&quot;size23&quot;&gt;AI 코딩 도구의 핵심은 &quot;컨텍스트&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Claude Code는 대화할 때마다 프로젝트의 맥락을 파악해야 합니다. CLAUDE.md는 프로젝트의 설명서로, 한 번 작성하면 Claude Code는 모든 대화에서 이 정보를 참고합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 세션을 시작할 때 다음 순서로 컨텍스트를 구성합니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;b&gt;전역 설정 로드&lt;/b&gt;: ~/.claude/ 디렉토리의 공통 규칙&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;b&gt;프로젝트 설정 파싱&lt;/b&gt;: CLAUDE.md 읽기 및 분석&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;환경 정보 수집&lt;/b&gt;: Git 상태, 파일 구조, 의존성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. &lt;b&gt;컨텍스트 통합&lt;/b&gt;: 우선순위에 따른 규칙 병합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 CLAUDE.md는 프로젝트별 컨텍스트의 &lt;b&gt;단일 진실 공급원(Single Source of Truth)&lt;/b&gt;으로 작동합니다.&lt;/p&gt;
&lt;h3 id=&quot;6f166f42-0c8f-4c45-ac98-fa001916d639&quot; data-toc-id=&quot;6f166f42-0c8f-4c45-ac98-fa001916d639&quot; data-ke-size=&quot;size23&quot;&gt;매번 설명하는 것 vs 한 번 작성하는 것&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CLAUDE.md 없이 작업할 때&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;개발자: &quot;API 호출 함수 만들어줘&quot;
Claude: &quot;어떤 HTTP 클라이언트를 사용하시나요?&quot;
개발자: &quot;axios요&quot;
Claude: &quot;base URL은 어디에 있나요?&quot;
개발자: &quot;.env에 API_URL이요&quot;
Claude: &quot;에러 처리는 어떻게 하시나요?&quot;
개발자: &quot;try-catch로 하고 toast로 보여줘요&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CLAUDE.md 작성 후&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;개발자: &quot;API 호출 함수 만들어줘&quot;
Claude: &quot;axios 인스턴스 사용해서 POST 요청 함수를 만들었습니다.
        에러는 try-catch로 처리하고 toast로 사용자에게 보여줍니다.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;0efb4e23-92a3-446f-9dd8-1a78ab00221e&quot; data-toc-id=&quot;0efb4e23-92a3-446f-9dd8-1a78ab00221e&quot; data-ke-size=&quot;size23&quot;&gt;실제 비교 사례&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 요청, 다른 결과:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// CLAUDE.md 없을 때
async function fetchData(id: string) {
    // TODO: implement API call
    throw new Error(&quot;Not implemented&quot;)
}

// CLAUDE.md 있을 때
async function fetchData(id: string) {
    try {
        const response = await apiClient.get(`/items/${id}`)
        return response.data
    } catch (error) {
        toast.error(&quot;데이터를 불러오는데 실패했습니다.&quot;)
        throw error
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;9fd744d1-c079-47f0-be92-8394e3e509eb&quot; data-toc-id=&quot;9fd744d1-c079-47f0-be92-8394e3e509eb&quot; data-ke-size=&quot;size26&quot;&gt;3️⃣ 기본 구조 잡기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLAUDE.md는 5개 핵심 섹션으로 구성합니다.&lt;/p&gt;
&lt;h3 id=&quot;bb6a1737-da06-490b-b17b-e470777f4489&quot; data-toc-id=&quot;bb6a1737-da06-490b-b17b-e470777f4489&quot; data-ke-size=&quot;size23&quot;&gt;1. 프로젝트 개요 (Project Overview)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 정체성을 한눈에 보여줍니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;## Project Overview

This is a web application built with modern React ecosystem.

**Tech Stack**:

-   Framework: Next.js 15.0.0 with App Router
-   Language: TypeScript 5.3
-   Package Manager: pnpm 8.9.0
-   State Management: Zustand + TanStack Query
-   Styling: Tailwind CSS
-   Form: React Hook Form + Zod&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;필수 정보&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프레임워크 및 버전 (API 호환성에 필수)&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기술적 중요성&lt;/b&gt;: 버전 정보는 API 호환성, 기능 가용성, 마이그레이션 경로 결정에 필수적입니다.&lt;/p&gt;
&lt;h3 id=&quot;1b655de3-94d7-489d-a2a1-210f98400169&quot; data-toc-id=&quot;1b655de3-94d7-489d-a2a1-210f98400169&quot; data-ke-size=&quot;size23&quot;&gt;2. 디렉토리 구조 (Directory Structure)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude가 파일을 어디에 만들어야 하는지 판단할 수 있게 합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;## Directory Structure

src/
├── app/
│ ├── (pages)/ # Route groups (괄호는 라우팅 제외용 그룹 폴더를 의미)
│ │ ├── [id]/ # Dynamic routes
│ │ └── page.tsx # Route pages
│ ├── (shared)/ # Shared components
│ ├── api/ # API routes (route.ts)
│ └── layout.tsx # Root layout
├── components/ # Reusable components
├── lib/ # Utilities
└── types/ # Type definitions

### Directory Naming Rules

-   `(name)/`: Route groups - URL에 포함되지 않음
-   `[param]/`: Dynamic route segments
-   `_name/`: Private folders - 라우팅에서 제외

### Directory Roles

-   `(pages)/`: Route group for page components
-   `(shared)/`: Shared resources across the app
-   `api/`: Next.js API routes (route.ts files)
-   `components/`: Reusable UI components&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Before&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;개발자: &quot;UserProfile 컴포넌트 만들어줘&quot;
Claude: &quot;어디에 만들까요?&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;After&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;개발자: &quot;UserProfile 컴포넌트 만들어줘&quot;
Claude: &quot;src/components/user/UserProfile.tsx에 생성했습니다&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설계 원칙&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 디렉토리 구조와 동기화 유지&lt;/li&gt;
&lt;li&gt;특수 폴더 규칙 명시 (route groups, dynamic segments)&lt;/li&gt;
&lt;li&gt;각 디렉토리의 역할 1줄 설명&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2bdd3e29-5afe-4129-a084-452129ee5c57&quot; data-toc-id=&quot;2bdd3e29-5afe-4129-a084-452129ee5c57&quot; data-ke-size=&quot;size23&quot;&gt;3. 코드 컨벤션 (Code Conventions)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일관된 코드 스타일을 유지합니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## Code Conventions

### Naming Rules

-   **Components**: PascalCase (`UserCard.tsx`, `DashboardLayout.tsx`)
-   **Pages**: 항상 `page.tsx` (Next.js App Router)
-   **Layouts**: 항상 `layout.tsx` (Next.js App Router)
-   **API Routes**: 항상 `route.ts` (Next.js App Router)
-   **Utilities**: camelCase (`formatDate.ts`, `apiClient.ts`)
-   **Hooks**: `use` prefix (`useAuth.ts`, `useDebounce.ts`)
-   **Types**: PascalCase with suffix (`UserData.types.ts`)
-   **Constants**: UPPER_SNAKE_CASE (`API_BASE_URL`, `MAX_RETRY_COUNT`)

### TypeScript Guidelines

**Type vs Interface 선택 기준:**

-   ✅ `interface`: 객체 shape 정의, 확장 가능한 구조
-   ✅ `type`: Union, Intersection, Utility types, Tuple, Function types

**규칙:**

-   **Never use `any`** - use `unknown` with type guards
-   Always define prop types for components
-   Use explicit return types for functions

### Examples

\```typescript
// ❌ Bad
function getData(): any {
return fetch('/api/data')
}

// ✅ Good
interface ApiResponse {
data: User[]
total: number
}

async function getData(): Promise&amp;lt;ApiResponse&amp;gt; {
const response = await fetch('/api/data')
return response.json()
}
\```

### Prohibited Patterns

-   ❌ **No `any` type** - Use specific types or `unknown`
-   ❌ **No TODO comments** - Complete implementations only
-   ❌ **No inline styles** - Use Tailwind classes
-   ❌ **No magic numbers** - Define as constants
-   ❌ **No console.log** - Use proper logging utility&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;적용 기준&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ESLint 규칙과 동기화&lt;/li&gt;
&lt;li&gt;Prettier 설정 반영&lt;/li&gt;
&lt;li&gt;프레임워크 권장사항 준수&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;c189a572-c7f1-4a4a-8ff1-c587fecc1183&quot; data-toc-id=&quot;c189a572-c7f1-4a4a-8ff1-c587fecc1183&quot; data-ke-size=&quot;size23&quot;&gt;4. 개발 명령어 (Essential Commands)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude가 직접 명령어를 실행할 수 있게 합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;## Essential Commands

### Development
```bash
pnpm dev              # Run dev server (port 3000)
pnpm dev:turbo        # Run with Turbopack
```

### Build &amp;amp; Quality Checks
```bash
pnpm build            # Production build
pnpm lint             # ESLint check
pnpm format           # Prettier formatting
pnpm test             # Run tests
pnpm typecheck        # TypeScript check
```

### Package Management
```bash
pnpm install              # Install dependencies
pnpm add [package]        # Add package
pnpm add -D [package]     # Add dev dependency
```

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자주 사용하는 명령어만 선별&lt;/b&gt;하세요. 모든 명령어를 나열할 필요는 없습니다.&lt;/p&gt;
&lt;h3 id=&quot;780edd40-a96e-4c10-829f-1b9361d91376&quot; data-toc-id=&quot;780edd40-a96e-4c10-829f-1b9361d91376&quot; data-ke-size=&quot;size23&quot;&gt;5. 실용적 예제 (Common Patterns)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 만드는 패턴을 템플릿화합니다.&lt;/p&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;## Common Patterns

### Client Component with State
```typescript
'use client'

import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'

export function UserList() {
  const { data, isLoading } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  })

  if (isLoading) return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;

  return (
    &amp;lt;ul&amp;gt;
      {data?.map((user) =&amp;gt; (
        &amp;lt;li key={user.id}&amp;gt;{user.name}&amp;lt;/li&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  )
}
```

### API Client Pattern
```typescript
// lib/apiClient.ts
import axios from 'axios'

export const apiClient = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  headers: {
    'Content-Type': 'application/json',
  },
})

apiClient.interceptors.request.use((config) =&amp;gt; {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})
```

### React Query Mutation
```typescript
// api/user.mutation.ts
import { useMutation } from '@tanstack/react-query'
import { apiClient } from '@/lib/apiClient'

export const useCreateUser = () =&amp;gt; {
  return useMutation({
    mutationFn: async (data: CreateUserRequest) =&amp;gt; {
      const response = await apiClient.post('/users', data)
      return response.data
    },
    onSuccess: () =&amp;gt; {
      toast.success('생성되었습니다')
    },
    onError: (error) =&amp;gt; {
      toast.error('생성에 실패했습니다')
    }
  })
}
```&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;b146011a-e11e-4777-b354-8bdda35d0a6a&quot; data-toc-id=&quot;b146011a-e11e-4777-b354-8bdda35d0a6a&quot; data-ke-size=&quot;size26&quot;&gt;4️⃣ 작성 후 테스트하기&lt;/h2&gt;
&lt;h3 id=&quot;c1769aeb-97a9-49f5-bc07-e7d19df34591&quot; data-toc-id=&quot;c1769aeb-97a9-49f5-bc07-e7d19df34591&quot; data-ke-size=&quot;size23&quot;&gt;Claude Code에게 질문해보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLAUDE.md를 작성한 후 실제로 테스트해보세요.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;테스트 질문 1: &quot;새로운 페이지를 만들려면 어디에 파일을 생성해야 해?&quot;
기대 응답: &quot;src/app/(pages)/ 디렉토리에 page.tsx 파일을 생성하면 됩니다.&quot;

테스트 질문 2: &quot;API 호출은 어떻게 해야 해?&quot;
기대 응답: &quot;apiClient를 사용하고 React Query의 useMutation을 활용하세요.&quot;

테스트 질문 3: &quot;개발 서버 실행 명령어는?&quot;
기대 응답: &quot;pnpm dev 명령어로 개발 서버를 실행할 수 있습니다.&quot;

테스트 질문 4: &quot;컴포넌트 파일명은 어떻게 지어?&quot;
기대 응답: &quot;PascalCase로 작성합니다. 예: UserCard.tsx&quot;

테스트 질문 5: &quot;any 타입 사용해도 돼?&quot;
기대 응답: &quot;any 타입은 사용 금지입니다. unknown이나 구체적인 타입을 사용하세요.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;9f62e79b-53de-4512-8cbf-ee51e3420b2e&quot; data-toc-id=&quot;9f62e79b-53de-4512-8cbf-ee51e3420b2e&quot; data-ke-size=&quot;size23&quot;&gt;응답 품질 확인 체크리스트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&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;li&gt;금지 패턴을 피하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;ac4cff8c-dffb-47de-b1a8-43c40d737902&quot; data-toc-id=&quot;ac4cff8c-dffb-47de-b1a8-43c40d737902&quot; data-ke-size=&quot;size23&quot;&gt;구문 검증&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마크다운 구문을 검증합니다:&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# markdownlint를 사용한 린팅
npx markdownlint-cli CLAUDE.md

# 또는 VS Code 확장 프로그램 사용
# markdownlint extension by David Anson&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;f128abd6-a2f7-46d4-9255-8e756e0bb2e0&quot; data-toc-id=&quot;f128abd6-a2f7-46d4-9255-8e756e0bb2e0&quot; data-ke-size=&quot;size26&quot;&gt;5️⃣ 완성 템플릿&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 바로 복사해서 사용할 수 있는 CLAUDE.md 템플릿입니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# CLAUDE.md

This file provides guidance to Claude Code when working with this repository.

## Project Overview

This is a **[프로젝트 유형]** built with:
- **Framework**: [Next.js 15 / React / Vue]
- **Language**: TypeScript
- **Package Manager**: [pnpm / npm / yarn]
- **State Management**: [Zustand / Recoil / Redux]
- **Styling**: [Tailwind CSS / styled-components]
- **Form**: [React Hook Form / Formik]

## Directory Structure

```
src/
├── app/              # [설명]
├── components/       # [설명]
├── lib/             # [설명]
└── types/           # [설명]
```

### Directory Roles
- `app/`: [역할 설명]
- `components/`: [역할 설명]
- `lib/`: [역할 설명]

## Code Conventions

### Naming Rules
- **Components**: PascalCase (`UserCard.tsx`)
- **Utilities**: camelCase (`formatDate.ts`)
- **Constants**: UPPER_SNAKE_CASE (`API_BASE_URL`)

### TypeScript Guidelines
- Never use `any` type
- Prefer interfaces over types
- Define prop types for all components

### Prohibited Patterns
- ❌ TODO comments
- ❌ console.log
- ❌ Inline styles

## Essential Commands

### Development
```bash
[dev command]          # Run dev server
[build command]        # Production build
[test command]         # Run tests
```

### Package Management
```bash
[install command]      # Install dependencies
[add command]          # Add new package
```

## Common Patterns

### Component Template
```typescript
[자주 사용하는 컴포넌트 패턴]
```

### API Call Pattern
```typescript
[API 호출 패턴]
```

### State Management
```typescript
[상태 관리 패턴]
```

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;b3aa527b-5bec-49b8-b168-5331fd4db8eb&quot; data-toc-id=&quot;b3aa527b-5bec-49b8-b168-5331fd4db8eb&quot; data-ke-size=&quot;size26&quot;&gt;6️⃣ 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLAUDE.md는 프로젝트와 AI 사이의 공통 언어입니다.&lt;/p&gt;
&lt;h3 id=&quot;0f3048f2-9d0c-4ba8-b2eb-2e9f81912fd5&quot; data-toc-id=&quot;0f3048f2-9d0c-4ba8-b2eb-2e9f81912fd5&quot; data-ke-size=&quot;size23&quot;&gt;핵심 체크리스트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[ ] 프로젝트 개요 작성 (기술 스택, 버전)&lt;/li&gt;
&lt;li&gt;[ ] 디렉토리 구조 설명 (파일 위치 가이드)&lt;/li&gt;
&lt;li&gt;[ ] 코드 컨벤션 명시 (네이밍, 패턴, 금지사항)&lt;/li&gt;
&lt;li&gt;[ ] 개발 명령어 정의 (dev, build, lint, test)&lt;/li&gt;
&lt;li&gt;[ ] 실용적 예제 포함 (자주 쓰는 패턴)&lt;/li&gt;
&lt;li&gt;[ ] 작성 후 테스트 (질문으로 검증)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;81490bdb-f8c6-43c2-939a-c0b81f626078&quot; data-toc-id=&quot;81490bdb-f8c6-43c2-939a-c0b81f626078&quot; data-ke-size=&quot;size23&quot;&gt;효과&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;반복 설명 제거&lt;/b&gt; &amp;rarr; 개발 시간 단축&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일관된 코드 품질&lt;/b&gt; 유지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;팀원 온보딩 시간&lt;/b&gt; 감소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI와의 협업 효율&lt;/b&gt; 극대화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;8026aca9-477d-4df9-8689-2bef07cd9987&quot; data-toc-id=&quot;8026aca9-477d-4df9-8689-2bef07cd9987&quot; data-ke-size=&quot;size23&quot;&gt;베스트 프랙티스&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;정기적 업데이트&lt;/b&gt;: 프로젝트 진화에 따라 CLAUDE.md 동기화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실제 사용 기반&lt;/b&gt;: 팀이 실제로 따르는 규칙만 문서화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구체적 예시&lt;/b&gt;: 추상적 설명보다 코드 예시 선호&lt;/li&gt;
&lt;li&gt;&lt;b&gt;버전 명시&lt;/b&gt;: 의존성 버전 명확히 기록&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;30분 투자로 수십 시간의 반복 설명을 절약할 수 있습니다. 지금 바로 프로젝트 루트에 CLAUDE.md를 만들어보세요.&lt;/b&gt;&lt;/p&gt;</description>
      <category>SETTING</category>
      <category>CC</category>
      <category>Claude</category>
      <category>claude-code</category>
      <category>claude.md</category>
      <category>claudecode</category>
      <category>클로드</category>
      <category>클로드코드</category>
      <category>클로드코드 세팅</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/279</guid>
      <comments>https://baekspace.tistory.com/279#entry279comment</comments>
      <pubDate>Wed, 7 Jan 2026 20:39:40 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 엔진(V8)의 작동 원리</title>
      <link>https://baekspace.tistory.com/277</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;V8과 자바스크립트 엔진 개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 자바스크립트는 어떻게 실행되는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 사람이 작성한 텍스트 코드를 &lt;b&gt;브라우저&lt;/b&gt; 나 &lt;b&gt;Node.js&lt;/b&gt; 내부의 자바스크립트 엔진(V8 등)이 해석하고 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 실행 과정은 크게 파싱 -&amp;gt; 컴파일 -&amp;gt; 실행 순으로 진행되며, 최적화도 동시에 수행된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 엔진이 무엇이고 V8은 왜 유명한가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진은 JS 코드를 실행하는 핵심 시스템이고, V8은 성능과 범용성에서 가장 뛰어난 엔진이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;엔진 :&lt;/b&gt; 자바스크립트 코드를 파싱, 컴파일, 실행하는 시스템&lt;/li&gt;
&lt;li&gt;&lt;b&gt;V8&lt;/b&gt; : 구글이 만든 JS 엔진으로, 빠른 속도, JIT 컴파일, Node.js에서 사용되면서 JS 생태계를 주도&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. &lt;b&gt;자바스크립트 엔진의 정의&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진은 JS 소스 코드를 읽고(파싱) -&amp;gt; 기계어로 바꾸고(컴파일) -&amp;gt;실행하는 소프트웨어이다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저(Chrome)나 런타임(Node.js) 내부에 포함되어 있으며, 사용자가 작성한 코드가 실제로 작동할 수 있게 만들어주는 실행기(interpreter + compiler)이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2 V8 엔진이 유명한 이유&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style6&quot;&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;/td&gt;
&lt;td&gt;JIT 컴파일러와 다양한 최적화 기술로 빠른 실행 속도 확보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js의 엔진&lt;/td&gt;
&lt;td&gt;브라우저 외에도 서버(Node.js)에서 JS를 실행할 수 있게 한 기발&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebAssembly 지원&lt;/td&gt;
&lt;td&gt;JS 외에도 다른 언어로 작성된 코드를 실행할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;크로스 플랫폼&lt;/td&gt;
&lt;td&gt;Window, Linux, macOS 등 어디서든 작동 가능&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;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3. V8 엔진이 탑재된 대표 플랫폼&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Google Chrome&lt;/li&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;Electron (데스크탑 앱 프레임워크)&lt;/li&gt;
&lt;li&gt;Deno&lt;/li&gt;
&lt;li&gt;Cloudflare Workers / Vercel Edge Functions 등 서버리스 플랫폼&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 인터프리터 vs 컴파일러 비교&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. 요약&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;th&gt;인터프리터(Interpreter)&lt;/th&gt;
&lt;th&gt;컴파일러 (Compiler)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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;/td&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;td&gt;컴파일 타임에 전체 에러 확인 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;/td&gt;
&lt;td&gt;JS, Python&lt;/td&gt;
&lt;td&gt;C, C++, Rust&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. JIT 컴파일&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JIT : Just-In-Time Compiler&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JIT 컴파일은 실행 중(Interpreting 도중)에 &lt;b&gt;자주 실행되는 부분&lt;/b&gt;을 기계어로 변환하여 실행 속도를 높이는 하이브리드 방식이다.&lt;/p&gt;
&lt;h5&gt;&amp;nbsp;&lt;/h5&gt;
&lt;h5&gt;3.2.1. &lt;b&gt;동작 흐름&lt;/b&gt;&lt;/h5&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인터프리터로 전체코드 실행&lt;/li&gt;
&lt;li&gt;실행 중 프로파일링 ( 자주 쓰이는 코드 확인)&lt;/li&gt;
&lt;li&gt;자주 쓰이는 코드 (&lt;code&gt;핫 코드&lt;/code&gt;) 를 JIT 컴파일러가 기계어로 변환&lt;/li&gt;
&lt;li&gt;이후 이 기계어 코드로 빠르게 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;&amp;nbsp;&lt;/h5&gt;
&lt;h5&gt;3.2.2. JIT와 다른 컴파일 방식들&lt;/h5&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style6&quot;&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;AOT (Ahead-Of-Time)&lt;/td&gt;
&lt;td&gt;실행 전에 전체 소스코드를 미리 기계어로 변환&lt;/td&gt;
&lt;td&gt;C, C++, Rust, Swift&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JIT (Just-In-Time)&lt;/td&gt;
&lt;td&gt;실행 중에 필요한 부분만 기계어로 컴파일&lt;/td&gt;
&lt;td&gt;Java, JS(V8), Kotlin/Native 일부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인터프리팅&lt;/td&gt;
&lt;td&gt;기계어 변환 없이, 바로 한 줄씩 실행&lt;/td&gt;
&lt;td&gt;JS, Python, Ruby&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;V8 내부 구조&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8은 파서 -&amp;gt; 인터프리터 (Ignition) -&amp;gt; JIT (TurboFan)로 구성되어, 실행 중 최적화를 반복하여 성능을 끌어올리는 구조이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. V8 흐름&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스코드&lt;br /&gt;-&amp;gt; Parser (파서)&lt;br /&gt;-&amp;gt; AST (추상 구문 트리)&lt;br /&gt;-&amp;gt; Interpreter (Ignition)&lt;br /&gt;-&amp;gt; Bytecode 생성 및 실행&lt;br /&gt;-&amp;gt; Hot Code 감지&lt;br /&gt;-&amp;gt; JIT Compiler (TurboFan)&lt;br /&gt;-&amp;gt; 최적화된 기계어 코드&lt;br /&gt;-&amp;gt; CPU에서 실행&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 상세 설명&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. Parser (파서)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트 소스를 읽어서 &lt;b&gt;AST(abstract syntax tree)로&lt;/b&gt; 변환&lt;/li&gt;
&lt;li&gt;문법 오류 체크도 이 단계에서 처리됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. Ignition (인터프리터)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AST를 &lt;b&gt;바이트코드(bytecode)로&lt;/b&gt; 변환하고 즉시실행&lt;/li&gt;
&lt;li&gt;빠른 시작이 가능함&lt;/li&gt;
&lt;li&gt;코드 실행 중 어떤 함수가 자주 호출되는지 프로파일링을 시작함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3. Profiler (프로파일러)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 함수/루프가 자주 실행되는지 기록 -&amp;gt; &lt;b&gt;Hot Code&lt;/b&gt; 식별&lt;/li&gt;
&lt;li&gt;성능을 향상할 수 있는 대상만 JIT 컴파일러로 넘김&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.4. TurboFan (JIT 컴파일러)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hot Code를 받아서 &lt;b&gt;기계어&lt;/b&gt;로 컴파일&lt;/li&gt;
&lt;li&gt;이 코드는 이후 재사용됨 -&amp;gt; 실행 속도 급격히 향상&lt;/li&gt;
&lt;li&gt;다양한 최적화 수행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Inline Caching&lt;/b&gt; : 객체 프로퍼티 접근을 캐싱&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Escape Analysis&lt;/b&gt; : 객체가 함수 밖에서 사용되지 않으면 스택에 할당&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dead Code Elimination&lt;/b&gt; : 실행되지 않는 코드는 제거&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Function Inlining&lt;/b&gt; : 자주 호출되는 함수 내부로 코드 삽입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Constant Folding&lt;/b&gt; : 계산 가능한 상수는 미리 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.5. Garbage Collector ( GC )&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요 없는 메모리를 자동으로 회수함&lt;/li&gt;
&lt;li&gt;V8은 Generational GC 방식 사용:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;New Space (짧게 살아남는 객체)&lt;/li&gt;
&lt;li&gt;Old Space (오래 살아있는 객체)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;부가 구성요소&lt;/h5&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IC (Inline Cache) : 객체 접근을 캐싱하여 반복 성능 향상&lt;/li&gt;
&lt;li&gt;Hidden Class / Map : 객체 구조를 정형화시켜 빠르게 접근 가능하게 함&lt;/li&gt;
&lt;li&gt;Zone memory allocator : 단기 메모리 관리를 위한 전용 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Ignition / TurboFan&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ignition : 바이트 코드를 생성하고 실행하는 인터프리터&lt;/li&gt;
&lt;li&gt;TurboFan : 실행 중인 핫코드를 기계어로 최적화 컴파일하는 JIT 컴파일러&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. Ignition : &lt;b&gt;바이트코드를 실행하는 인터프리터&lt;/b&gt;&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;/td&gt;
&lt;td&gt;자바스크립트 소스코드를 바이트코드로 변환하고 실행함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특징&lt;/td&gt;
&lt;td&gt;빠른 시작(Startup Time), 저메모리 소비&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도입 배경&lt;/td&gt;
&lt;td&gt;기존에는 AST -&amp;gt; 기계어로 전환하던 방식에서 더 유연한 실행을 위해서 등장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;빠르게 실행 시작 가능하며, JIT 컴파일 대상 탐지 가능 (프로파일링 포함)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;function add(a, b){
    return a + b
}

add(1, 2)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 함수가 처음 실행될 때, Ignition이 바이트코드를 만들어 바로 실행한다.&lt;/li&gt;
&lt;li&gt;반복 실행되면 이 함수는 'Hot Code'로 감지되고, TruboFan에게 넘겨진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. TurboFan : &lt;b&gt;기계어로 변환하는 JIT 컴파일러&lt;/b&gt;&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;/td&gt;
&lt;td&gt;자주 실행되는 코드(핫코드)를 JIT 컴파일하여 네이티브 코드로 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특징&lt;/td&gt;
&lt;td&gt;SSA 기반 중간 표현(IR)을 사용하여 다양한 최적화 수행&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;h4 data-ke-size=&quot;size20&quot;&gt;3.3. SSA 기반 중간 표현&lt;/h4&gt;
&lt;h5&gt;SSA란?&lt;/h5&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Static Single Assignment - 정적 단일 할당&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;모든 변수는 딱 한 번만 할당된다.&quot;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;어떤 값이 어디서 생성되고, 어디로 흐르는지를 명확하게 추적할 수 있게 만드는 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;예시&lt;/h5&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;// 기존코드 

let x = 1;
x = x + 2;
x = x * 3;

// SSA로 변환하면
x1 = 1 // 최초 할당
x2 = x1 + 2 // x1의 결과로 x2 생성
x3 = x2 * 3 // x2의 결과로 x3 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수명이 바뀌면서 모든 값이 어떤 연산 결과인지 명확하게 추적할 수 있게 된다.&lt;/p&gt;
&lt;h5&gt;중요한 이유?&lt;/h5&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;x3는 x2가 있어야만 계산이 되고&lt;/li&gt;
&lt;li&gt;x2는 x1이 있어야만 계산이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프 형태로 &lt;b&gt;데이터 흐름을 추적할 수 있게 된다.&lt;/b&gt; -&amp;gt; 컴파일러 입장에서 최적화하기 좋은 조건&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;x2가 필요 없는 값이면 x3도 필요 없다&quot;&lt;/b&gt; -&amp;gt; 불필요한 연산 통째로 제거 가능&lt;/p&gt;
&lt;h5&gt;최적화 기법&lt;/h5&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style6&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;최적화 기법&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Dead Code Elimination&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;결과에 영향을 주지 않는 연산 제거&lt;/td&gt;
&lt;td&gt;let a = 5; &amp;rarr; a 안 쓰면 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Constant Folding&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;고정된 값을 미리 계산&lt;/td&gt;
&lt;td&gt;2 + 3 &amp;rarr; 5로 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Value Numbering&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;같은 연산은 하나만 남김&lt;/td&gt;
&lt;td&gt;a + b, a + b &amp;rarr; 한 번만 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Copy Propagation&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;중간 변수 제거&lt;/td&gt;
&lt;td&gt;let x = y; let z = x; &amp;rarr; z = y;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Loop-Invariant Code Motion&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;루프 밖으로 고정 연산 이동&lt;/td&gt;
&lt;td&gt;for (...) { const x = 3 + 4; } &amp;rarr; 루프 밖으로 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;즉, &lt;b&gt;값이 &amp;ldquo;언제&amp;rdquo;, &amp;ldquo;어디서&amp;rdquo;, &amp;ldquo;무엇으로&amp;rdquo; 바뀌는지를 정확히 알 수 있기 때문에&lt;/b&gt;, 공격적인 최적화가 가능&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.4. Ignition과 TurboFan의 협업&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style6&quot;&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;1단계&lt;/td&gt;
&lt;td&gt;Ignition이 바이트코드 생성 및 초기 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2단계&lt;/td&gt;
&lt;td&gt;실행 중 반복된 함수/루프를 HotCode로 식별&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3단계&lt;/td&gt;
&lt;td&gt;TurboFan이 해당 코드 JIT 컴파일-&amp;gt; 네이티브 기계어 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4단계&lt;/td&gt;
&lt;td&gt;이후부터는 기계어 코드가 재사용되어 성능 급상승&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 결론&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TurboFan의 핵심 : '실행하면서 배우고, 자주 쓰는 코드는 더 빠르게 실행되도록 만든다.'&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;V8은 코드 실행 중에도 계속해서 학습하고, 분석하고, 최적화한다.&lt;/li&gt;
&lt;li&gt;이를 통해 한 번 실행된 코드라도 점점 더 빠르게 동작하게 만드는 것이 이 구조의 핵심이다.&lt;/li&gt;
&lt;li&gt;V8은 JS 엔진이라기보단 하나의 런타임 머신에 가깝다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;V8 엔진이 좋아하는 코드 스타일과 싫어하는 패턴&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JIT-friendly VS JIT-unfriendly&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;V8은 타입이 일관되고 구조가 고정된 코드를 좋아하며, 동적으로 구조가 바뀌는 객체나 예외적 흐름을 싫어한다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. V8이 좋아하는 코드 스타일 (JIT-Friendly)&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style6&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;패턴&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;일관된 객체 구조&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;프로퍼티 순서와 수가 고정되어야 Hidden Class를 재사용할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;일관된 변수 타입&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;타입이 계속 바뀌지 않아야 인라인 캐싱이 잘 작동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;짧고 명확한 함수&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;인라인 최적화 및 빠른 실행 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;배열은 같은 타입만 사용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;배열 내부에 숫자, 문자열, 객체가 섞이면 최적화 깨짐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;클래스 사용 권장&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;클래스는 Hidden Class를 잘 활용하기에 성능상 이점 존재&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;반복문에서 상수는 루프 밖으로 이동&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;for 내부에서 불필요한 연산 제거 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;try-catch는 제한적으로만 사용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;예외 흐름은 최적화의 적 &amp;rarr; 별도 핸들링 로직으로 분리 추천&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. V8이 싫어하는 코드 스타일 (JIT Unfriendly)&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style6&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;패턴&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;동적 프로퍼티 추가/삭제&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;객체 구조(Hidden Class)가 계속 바뀌면 JIT 포기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;타입이 자주 바뀌는 변수&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;let x = 1; x = '문자열'; &amp;rarr; 인라인 캐싱 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;배열에 타입 섞기&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;[1, 2, &quot;3&quot;, {a: 1}] &amp;rarr; 일반 객체 배열로 강등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;delete 키워드 사용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Hidden Class 깨짐, JIT 무력화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;객체에 프로퍼티 순서가 자주 바뀜&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;내부적으로 새 Hidden Class를 생성해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;with, eval 사용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스코프 예측이 어려워 최적화 불가 영역으로 간주됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;클로저 안에서 객체 참조가 바뀌는 패턴&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;캐시 무효화 발생 &amp;rarr; 느려짐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 예시 코드&lt;/h3&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;// JIT-friendly
function createUser(name, age){
    return {name, age}
}

// JIT-unfriendly
function createUser(name, age){
    const obj = {}
    if(name) obj.name = name;
    if(age) obj.age = age;
    return obj;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Hidden Class&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8의 핵심 최적화 전략 중 하나, JS처럼 동적인 언어에서도 정적 언어처럼 빠르게 객체에 접근할 수 있도록 만들어주는 구조&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hidden Class는 V8이 객체 구조를 정적으로 가정하고 최적화하기 위해 내부적으로 생성하는 '클래스 설계도'이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1 왜 필요한가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JS는 객체 구조가 동적으로 바뀔 수 있는 언어이다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;const obj = {}
obj.name = &quot;Baek&quot;
obj.age = 32&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;obj는 객체지만, 실제로는 &lt;b&gt;런타임에서만 구조가 결정됨&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;일반적으로는 프로퍼티 접근 시 &lt;code&gt;obj['name']&lt;/code&gt;처럼 처리해야 함 -&amp;gt; 느림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 V8은 객체가 만들어지는 순서와 구조를 분석해서 '이 구조는 이런 프로퍼티를 갖는다'라는 &lt;b&gt;설계도(클래스)&lt;/b&gt;&lt;br /&gt;를 내부적으로 생성한다 -&amp;gt; &lt;code&gt;Hidden Class&lt;/code&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2. Hidden Class 예시&lt;/h4&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const user = {}      // HiddenClass A 생성 
user.name = &quot;Baek&quot;;  // HiddenClass B 생성 (name 있음)
user.age = 32        // HiddenClass C 생성 (name, age 있음)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;user 객체의 구조가 변경될 때마다 V8은 새로운 Hidden Class를 생성한다.&lt;/li&gt;
&lt;li&gt;객체마다 해당 클래스에 대한 포인터를 저장해 둔다.&lt;/li&gt;
&lt;li&gt;프로퍼티 접근 시 이 클래스를 참조해서 빠르게 오프셋을 계산한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, user.name 은 내부적으로 'C클래스에서 name은 0번째 위치'처럼 인덱스로 바로 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h5&gt;4.2.1. 의문&lt;/h5&gt;
&lt;pre class=&quot;monkey&quot;&gt;&lt;code&gt;동적으로 객체를 만들 때 프로퍼티를 하나씩 추가하면,
예를 들어 빈 객체에서 시작해서 3개의 속성을 추가하면 Hidden Class가 3개 생긴다고 들었습니다.

그러면 A &amp;rarr; B &amp;rarr; C처럼 계속 생성된다는 건데,
이 Hidden Class들이 계속 메모리에 남는 거라면 너무 비효율적인 거 아닌가요?

어딘가에 저장되고 있다는 건, 결국 리소스 낭비 아닌가요?

그리고 한 번에 객체를 만들 때, 예를 들어
const obj = { name: &quot;&quot;, age: &quot;&quot; }
처럼 선언하면 이건 Hidden Class가 생기는 게 아니라 그냥 객체일 뿐인가요?&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hidden Class는 단기적인 메모리 비용을 감수해서 장기적인 실행 속도 최적화를 이끌어내는 구조이며, 오히려 성능 향상에 훨씬 이득이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Hidden Class는 항상 생성된다 (정적/동적 상관없이)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체를 만들 때 V8은 항상 Hidden Class를 생성한다.&lt;/li&gt;
&lt;li&gt;즉, { name: &quot;&quot;, age: &quot;&quot; }처럼 한 번에 선언하더라도 내부적으로는 Hidden Class가 만들어진다.&lt;/li&gt;
&lt;li&gt;이 경우에도 Hidden Class는 생성된다. 다만 &lt;b&gt;초기 상태에서 완성된 구조&lt;/b&gt;이기 때문에 &lt;b&gt;추가 전이 없이 Hidden Class 1개만&lt;/b&gt; 생성되어 최적화 관점에서 가장 유리한 케이스가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Hidden Class가 계속 생성되는 것은 맞다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각각의 상태 (A, B, C)에 대해 Hidden Class를 만들고, 내부적으로 클래스 전이 트리로 연결해 둔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그럼 메모리 낭비 아냐? &amp;rarr; 아니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hidden Class는 재사용됨. 같은 구조를 가진 객체가 또 생기면 기존 Hidden Class를 그대로 쓴다.&lt;/li&gt;
&lt;li&gt;또한 V8은 이 전이 정보를 효율적인 &lt;b&gt;트리 구조&lt;/b&gt;로 관리하고, 메모리 낭비를 줄이기 위해 불필요한 클래스는 &lt;b&gt;GC로 제거&lt;/b&gt;할 수 있다.&lt;/li&gt;
&lt;li&gt;무엇보다, 이 구조 덕분에 obj.name 같은 속성 접근을 일반 객체처럼 느리게 하지 않고, 내부적으로는 &lt;b&gt;정적 오프셋 기반&lt;/b&gt;으로 obj[0]처럼 빠르게 접근할 수 있음. &amp;rarr; 이게 V8의 JIT 최적화 성능의 핵심&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hidden Class는 객체의 속성 추가 순서가 일치하면, V8은 기존에 만든 Hidden Class 전이 경로를 그대로 재사용한다&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;const obj = {};
obj.name = 'Kim';     // Hidden Class A &amp;rarr; B
obj.age = 30;         // Hidden Class B &amp;rarr; C

const obj2 = {};
obj2.name = 'Kimss';  // ❗ A &amp;rarr; B (재사용됨)
obj2.age = 31;        // ❗ B &amp;rarr; C (재사용됨)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8 입장에서 보면 obj2도 똑같은 속성 추가 순서로 객체를 만들었기 때문에 Hidden Class A &amp;rarr; B &amp;rarr; C 전이 경로를 그대로 다시 따라감&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;포인트&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;최초 객체&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;프로퍼티 추가 순서 따라 Hidden Class A &amp;rarr; B &amp;rarr; C 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;다음 객체&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;동일한 순서로 추가되면 기존 전이 트리를 &lt;b&gt;그대로 재사용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;V8 입장&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;ldquo;이 구조는 이미 봤다!&amp;rdquo; &amp;rarr; 새 Hidden Class 생성 ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;메모리 낭비 없이, 빠른 객체 구조 캐시 + JIT 최적화 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.3. Hidden Class가 깨지는 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조가 일관되지 않으면 V8은 Hidden Class를 재활용할 수 없고, 매번 새로 만들어야 해서 최적화가 깨진다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서 변경, delete, eval, 동적 프로퍼티 등&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;const user1 = {};
user1.name = 'A';
user1.age = 20;

const user2 = {};
user2.age = 30;
user2.name = 'B';  // 순서가 다르기 때문에 다른 Hidden Class 생성됨&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.4 Hidden Class를 잘 활용하기 위해서&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로퍼티는 &lt;b&gt;항상 동일한 순서로 추가&lt;/b&gt;하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;속성 추가 순서와 구조가 같다면 Hidden Class는 무조건 재사용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;그래서 &amp;ldquo;객체를 만들 때 항상 같은 순서로, 같은 구조로&amp;rdquo; 만드는 습관이 &lt;b&gt;V8 JIT 최적화에서 매우 중요한 포인트&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;객체 구조는 가급적 &lt;b&gt;초기화 시점에 완성&lt;/b&gt;하기&lt;/li&gt;
&lt;li&gt;delete는 사용 자제 &amp;rarr; undefined 할당 추천&lt;/li&gt;
&lt;li&gt;클래스를 사용하면 Hidden Class를 &lt;b&gt;일관되게 유지하기 쉬움&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>chrome</category>
      <category>JIT</category>
      <category>js</category>
      <category>js 엔진</category>
      <category>nodejs</category>
      <category>V8</category>
      <category>v8 엔진</category>
      <category>엔진</category>
      <category>컴파일러</category>
      <category>프론트엔드 심화</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/277</guid>
      <comments>https://baekspace.tistory.com/277#entry277comment</comments>
      <pubDate>Sat, 12 Apr 2025 02:06:02 +0900</pubDate>
    </item>
    <item>
      <title>웹 성능 최적화 - 애니메이션 최적화</title>
      <link>https://baekspace.tistory.com/276</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VTZTc/btsM6foK1wS/GX8XnWO0B4nP6wur7UNBw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VTZTc/btsM6foK1wS/GX8XnWO0B4nP6wur7UNBw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VTZTc/btsM6foK1wS/GX8XnWO0B4nP6wur7UNBw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVTZTc%2FbtsM6foK1wS%2FGX8XnWO0B4nP6wur7UNBw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt; ️ 애니메이션 최적화&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  쟁크(Jank) 현상&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;color: #0e0e0e;&quot; data-ke-style=&quot;style1&quot;&gt;애니메이션이 부드럽지 않고 끊기는 현상&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DOM 업데이트 시 Reflow가 발생함&lt;/li&gt;
&lt;li&gt;Reflow는 레이아웃 계산과 Paint 재수행을 유발&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이로 인해 &lt;/span&gt;&lt;b&gt;프레임 드랍&lt;/b&gt;&lt;span&gt;과 &lt;/span&gt;&lt;b&gt;성능 저하&lt;/b&gt;&lt;span&gt; 발생&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Reflow&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;color: #0e0e0e;&quot; data-ke-style=&quot;style1&quot;&gt;레이아웃을 다시 계산하는 과정&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;처리 순서&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DOM + CSSOM 파싱&lt;/li&gt;
&lt;li&gt;Render Tree 생성&lt;/li&gt;
&lt;li&gt;Layout 계산 및 배치&lt;/li&gt;
&lt;li&gt;Paint&lt;/li&gt;
&lt;li&gt;Composite&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Repaint&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;color: #0e0e0e;&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;레이아웃 재계산 없이 &lt;/span&gt;&lt;b&gt;화면을 다시 그리는 과정&lt;br /&gt;&lt;/b&gt;&lt;span&gt;예: &lt;/span&gt;color&lt;span&gt;, &lt;/span&gt;background-color&lt;span&gt; 변경 등&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;처리 순서&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DOM + CSSOM 파싱&lt;/li&gt;
&lt;li&gt;Render Tree 생성&lt;/li&gt;
&lt;li&gt;Layout 계산 및 배치 (변경 없을 시 생략 가능)&lt;/li&gt;
&lt;li&gt;Paint&lt;/li&gt;
&lt;li&gt;Composite&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  애니메이션 성능 최적화 전략&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;color: #0e0e0e;&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span&gt;부드러운 애니메이션을 위해 &lt;/span&gt;&lt;b&gt;Reflow/Repaint를 최소화&lt;br /&gt;&lt;/b&gt;가능하면 &lt;span&gt;&lt;b&gt;GPU 가속&lt;/b&gt;&lt;/span&gt;을 활용 (&lt;span&gt;transform&lt;/span&gt;, &lt;span&gt;opacity&lt;/span&gt; 등)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;❌ Reflow를 유발하는 CSS 속성&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레이아웃에 영향을 주는 속성들&lt;/li&gt;
&lt;li&gt;width&lt;span&gt;, &lt;/span&gt;height&lt;span&gt;, &lt;/span&gt;margin&lt;span&gt;, &lt;/span&gt;padding&lt;span&gt;, &lt;/span&gt;border&lt;/li&gt;
&lt;li&gt;top&lt;span&gt;, &lt;/span&gt;left&lt;span&gt;, &lt;/span&gt;right&lt;span&gt;, &lt;/span&gt;bottom&lt;span&gt;, &lt;/span&gt;display&lt;span&gt;, &lt;/span&gt;position&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;❌ Repaint를 유발하는 CSS 속성&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시각적 스타일에 영향을 주는 속성들&lt;/li&gt;
&lt;li&gt;color&lt;span&gt;, &lt;/span&gt;background-color&lt;span&gt;, &lt;/span&gt;border-color&lt;span&gt;, &lt;/span&gt;box-shadow&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭕️ Reflow &amp;amp; Repaint 모두 피할 수 있는 속성&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPU 렌더링 최적화 가능&lt;/li&gt;
&lt;li&gt;transform&lt;span&gt;, &lt;/span&gt;opacity&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>reflow</category>
      <category>repaint</category>
      <category>애니메이션최적화</category>
      <category>웹성능</category>
      <category>웹성능최적화</category>
      <category>쟁크현상</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/276</guid>
      <comments>https://baekspace.tistory.com/276#entry276comment</comments>
      <pubDate>Wed, 2 Apr 2025 17:58:58 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Tutorial(9) - findBy</title>
      <link>https://baekspace.tistory.com/273</link>
      <description>&lt;div class=&quot;book-toc&quot;&gt;
&lt;div class=&quot;book-toc&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;findBy&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Testing Library에서 DOM요소를 찾을 때 많이 쓰이는 메서드 중 하나인 &lt;code&gt;findBy&lt;/code&gt;는 비동기적으로 요소를 찾을 때 유용하다.&lt;br /&gt;API 호출이나, 타이머처럼 일정 시간이 지난 후에 렌더링 되는 요소를 테스트할 때 주로 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;findBy&lt;/code&gt;는 Promise를 반환한다. 지정된 시간 내에 해당 요소를 찾으면 resolve, 그렇지 않으면 reject 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용시기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 패칭 이후 나타나는 요소&lt;/li&gt;
&lt;li&gt;setTimeout, setInterval 등으로 일정 시간이 지난 후 나타나는 요소&lt;/li&gt;
&lt;li&gt;상태 변경으로 인한 렌더링 결과물&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;findBy&lt;/code&gt;와 &lt;code&gt;getBy&lt;/code&gt;는 유사하지만 비동기 요소를 기다린다는 점에서 다르다.&lt;br /&gt;promise를 반환하기 때문에 주로 async/await와 함께 사용된다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;const element = await findByText(&quot;로딩 완료&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;작성 예시&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useEffect, useState } from &quot;react&quot;

const UserProfile = () =&amp;gt; {
    const [loading, setLoading] = useState(true)
    const [user, setUser] = useState(&quot;&quot;)

    useEffect(() =&amp;gt; {
        setTimeout(() =&amp;gt; {
            setUser(&quot;홍길동&quot;)
            setLoading(false)
        }, 2000)

    }, [])

    if (loading) {
        return &amp;lt;div&amp;gt;로딩 중...&amp;lt;/div&amp;gt;
    }
    return &amp;lt;div&amp;gt;사용자 이름: {user}&amp;lt;/div&amp;gt;
}

export default UserProfile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { render, screen } from '@testing-library/react';
import UserProfile from './UserProfile';

test('사용자 이름을 API 호출 후 렌더링한다', async () =&amp;gt; {
  render(&amp;lt;UserProfile /&amp;gt;);
  expect(screen.getByText('로딩 중...')).toBeInTheDocument();
  const userNameElement = await screen.findByText('사용자 이름: 홍길동');
  expect(userNameElement).toBeInTheDocument();
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 실패한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 기본적으로 findBy가 기다리는 시간은 1000ms로 설정되어 있기 때문이다.&lt;br /&gt;하지만 작성한 컴포넌트는 2000ms 뒤에 상태를 바꾸는 동작을 하기 때문에 실패하는 코드가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해서 findBy~ ()의 3번째 인자값을 이용해서 시간을 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;const userNameElement = await screen.findByText(&quot;사용자 이름: 홍길동&quot;,{},{
    timeout:2000
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 작성된 테스트코드를 보면 timeout을 이용해서 기다리는 시간을 늘려 2초를 기다리는 테스트 코드로 만들었다. 그 결과 해당 테스트 코드는 통과할 수 있게 되었다.&lt;/p&gt;
&lt;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(776px, 726px);&quot; aria-expanded=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>REACT</category>
      <category>react testing</category>
      <category>RTL</category>
      <category>TDD</category>
      <category>리액트</category>
      <category>리액트 테스팅</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/273</guid>
      <comments>https://baekspace.tistory.com/273#entry273comment</comments>
      <pubDate>Wed, 18 Sep 2024 16:27:57 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Tutorial (8) - getAllBy..., textMatch</title>
      <link>https://baekspace.tistory.com/272</link>
      <description>&lt;h1&gt;&lt;b&gt;getAllBy-&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전까지 &lt;code&gt;getBy-&lt;/code&gt;를 이용해서 한 개의 요소를 찾는 방법을 배웠다. 여러 개의 요소가 있는 상황을 테스트하는 상황도 있을 것이다. 이때 사용할 수 있는 방법으로 &lt;code&gt;getAllBy-&lt;/code&gt;가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getAllBy-는 getBy-와 비슷하지만, 여러 개의 요소를 찾을 때 사용한다.&lt;br /&gt;이전 글들은 1개의 요소를 찾을 때 사용했지만, 이번 글에서는 여러 개의 요소를 찾을 때 사용하는 방법을 알아보자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용법&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;const elements = screen.getAllByRole(&quot;textbox&quot;)
expect(elements).toHaveLength(2) // textBox가 2개 있을 때&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주의사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;getAllBy-는 요소가 없을 때 에러를 발생시킨다.&lt;/li&gt;
&lt;li&gt;getAllBy-는 요소가 1개일 때도 에러를 발생시킨다.&lt;/li&gt;
&lt;li&gt;getAllBy-는 요소가 2개 이상일 때 사용한다.&lt;/li&gt;
&lt;li&gt;getAllBy-는 배열을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;getBy-와 getAllBy-의 차이점&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;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;getBy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;하나의 요소&lt;/td&gt;
&lt;td&gt;에러 발생&lt;/td&gt;
&lt;td&gt;첫 번째 요소만 반환&lt;/td&gt;
&lt;td&gt;요소가 &lt;b&gt;하나&lt;/b&gt;일 것으로 예상될 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getAllBy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;요소들의 배열&lt;/td&gt;
&lt;td&gt;에러 발생&lt;/td&gt;
&lt;td&gt;모든 요소를 배열로 반환&lt;/td&gt;
&lt;td&gt;요소가 &lt;b&gt;여러 개&lt;/b&gt;일 것으로 예상될 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;&lt;b&gt;textMatch&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;textMatch는 React Testing Library에서 사용하는 옵션 중 하나로, 요소를 찾을 때 &lt;b&gt;텍스트 일치 조건&lt;/b&gt;을 지정하는 방법이다.&lt;br /&gt;주로 &lt;code&gt;getByRole&lt;/code&gt;, &lt;code&gt;getByText&lt;/code&gt;, &lt;code&gt;findByText&lt;/code&gt; 등의 메서드와 함께 사용하며, 특정 텍스트와의 일치 기준을 설정할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단순 텍스트 일치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기본적인 방식으로, 텍스트가 완전히 일치하는 경우에만 요소를 찾는다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;screen.getByText(&quot;Hello, World!&quot;, { exact: true })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exact 옵션은 텍스트가 완전히 일치하는지 여부를 설정하는 옵션이다. 기본값은 true이며, false로 설정하면 대소문자를 구분하지 않고 일치하는 요소를 찾는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정규 표현식 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규 표현식을 사용하여 텍스트를 찾을 수 있다. 정규 표현식을 사용하면 텍스트의 일부만 일치하거나 대소문자를 구분하지 않고 찾을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;screen.getByText(/hello/i) // 대소문자 구분 없이 hello를 찾음
screen.getByText(/world/) // world를 포함하는 텍스트를 찾음&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 사용하여 텍스트를 찾을 수 있다. 함수를 사용하면 텍스트를 동적으로 찾을 수 있다.&lt;br /&gt;함수의 인자값은 2가지이다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;(content?: string, element?: Element | null) =&amp;gt; boolean&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// hello로 시작하는 텍스트를 찾음
screen.getByText((content, element) =&amp;gt; content.startsWith(&quot;hello&quot;))

// p 태그 중 hello를 포함하는 텍스트를 찾음
screen.getByText((content, element) =&amp;gt; {
    return content.includes(&quot;hello&quot;) &amp;amp;&amp;amp; element.tagName === &quot;P&quot;
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(799px, 2056px);&quot; aria-expanded=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>jest</category>
      <category>react test</category>
      <category>RTL</category>
      <category>TDD</category>
      <category>Testing</category>
      <category>리액트 테스트</category>
      <category>테스트</category>
      <category>테스트코드 작성</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/272</guid>
      <comments>https://baekspace.tistory.com/272#entry272comment</comments>
      <pubDate>Wed, 11 Sep 2024 20:41:27 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Tutorial(7) - getByDisplayValue, getByAltText, getByTitle, getByTestId</title>
      <link>https://baekspace.tistory.com/271</link>
      <description>&lt;h1&gt;&lt;b&gt;getByDisplayValue()&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폼 요소(&lt;code&gt;&amp;lt;input/&amp;gt;&lt;/code&gt;,&lt;code&gt;&amp;lt;textarea/&amp;gt;&lt;/code&gt; 등)에 표시된 텍스트 값을 기반으로 DOM요소를 찾는 데 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 폼 입력 필드의 값을 확인하거나 해당 값을 가진 요소를 대상으로 테스트할 때 유용하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용 예시&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { render, screen } from '@testing-library/react';

test('input 요소에 입력된 값을 찾습니다.', () =&amp;gt; {
  render(&amp;lt;input value=&quot;테스트 값&quot; /&amp;gt;);

  const inputElement = screen.getByDisplayValue('테스트 값');
  expect(inputElement).toBeInTheDocument();
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 getByDisplayValue('테스트 값')은 value 속성이 '테스트 값'인 input 요소를 찾는다.&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;getByAltText()&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;img, area, input 요소에서 alt 속성을 기반으로 찾는다. 주로 이미지를 대상으로 접근성 테스트나 이미지의 존재 여부를 확인할 때 유용하게 사용된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용 예시&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { render, screen } from '@testing-library/react'

test('이미지 요소에 설정된 alt 텍스트를 통해 이미지를 찾습니다.', () =&amp;gt; {
  render(&amp;lt;img alt=&quot;테스트 이미지&quot; src=&quot;/test.png&quot; /&amp;gt;)

  const imageElement = screen.getByAltText('테스트 이미지')
  expect(imageElement).toBeInTheDocument()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;b&gt;getByTitle()&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 요소의 title 속성을 기반으로 DOM 요소를 찾는데 사용된다. title 속성은 주로 요소에 대한 추가 정보를 제공하는 툴팁 형태로 나타나며, 접근성을 개선하거나 사용자에게 추가적인 정보를 제공할 때 유용하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용 예시&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { render, screen } from '@testing-library/react'

test('title 속성을 가진 요소를 찾습니다.', () =&amp;gt; {
  render(&amp;lt;button title=&quot;저장&quot;&amp;gt;Save&amp;lt;/button&amp;gt;)

  const buttonElement = screen.getByTitle('저장')
  expect(buttonElement).toBeInTheDocument()s
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;b&gt;getByTestId()&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM 요소를 특정 테스트 ID 속성을 통해 찾는데 사용된다.&lt;br /&gt;요소를 선택하는 데 있어 명확한 방식으로 요소에 &lt;code&gt;data-testid&lt;/code&gt; 라는 속성을 추가하여 테스트에서 쉽게 선택할 수 있도록 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 테스트에서만 사용되고, 실제로 UI에서는 보이지 않는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용 예시&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { render, screen } from '@testing-library/react'

test('data-testid를 통해 요소를 찾습니다.', () =&amp;gt; {
  render(&amp;lt;div data-testid=&quot;custom-element&quot;&amp;gt;테스트&amp;lt;/div&amp;gt;)

  const element = screen.getByTestId('custom-element')
  expect(element).toBeInTheDocument()
});
&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(85px, 2001px);&quot; aria-expanded=&quot;false&quot;&gt;
&lt;div id=&quot;tippy-1&quot; style=&quot;z-index: 100000200; visibility: hidden; position: absolute; inset: auto auto 0px 0px; margin: 0px; transform: translate3d(438.5px, -20px, 0px); pointer-events: none;&quot; data-tippy-root=&quot;&quot;&gt;
&lt;div class=&quot;tippy-box&quot; style=&quot;max-width: 350px; transition-duration: 250ms;&quot; tabindex=&quot;-1&quot; role=&quot;mtttooltip&quot; data-state=&quot;hidden&quot; data-theme=&quot;custom&quot; data-animation=&quot;fade&quot; data-placement=&quot;top&quot;&gt;
&lt;div class=&quot;tippy-content&quot; style=&quot;transition-duration: 250ms;&quot; data-state=&quot;hidden&quot;&gt;&lt;span&gt;getByTestId&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;tippy-arrow&quot; style=&quot;position: absolute; left: 0px; transform: translate3d(52.5px, 0px, 0px);&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>getbyalttext</category>
      <category>getbydisplayvalue</category>
      <category>getbytestid</category>
      <category>getbytitle</category>
      <category>react testing</category>
      <category>RTL</category>
      <category>TDD</category>
      <category>리액트 테스트</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/271</guid>
      <comments>https://baekspace.tistory.com/271#entry271comment</comments>
      <pubDate>Sun, 8 Sep 2024 19:48:10 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Tutorial(6) - getByLabelText(), getByPlaceholderText(), getByText()</title>
      <link>https://baekspace.tistory.com/270</link>
      <description>&lt;h1&gt;&lt;b&gt;1. getByLabelText()&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getByLabelText 함수는 폼 요소를 테스트할 때 주로 사용되다.&lt;br /&gt;&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; 요소에 연결되어 있는 폼 요소(ex: input, textarea 등)를 쉽게 찾을 수 있게 도와준다.&lt;br /&gt;label에 연결된 폼 요소는 보통 접근성을 위해서 사용되며, &lt;code&gt;getByLabelText&lt;/code&gt;를 사용하면 사용자가 실제로 인터페이스와 상호작용하는 방식과 유사한 방식으로 테스트할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 폼 요소가 있을 때&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;label for=&quot;username&quot;&amp;gt;Username&amp;lt;/label&amp;gt;
&amp;lt;input id=&quot;username&quot; /&amp;gt;

&amp;lt;label for=&quot;password&quot;&amp;gt;Password&amp;lt;/label&amp;gt;
&amp;lt;input id=&quot;password&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 코드를 작성 시 getByLabelText()를 이용해서 &lt;code&gt;input&lt;/code&gt;요소를 선택할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;text('폼 요소 테스트', () =&amp;gt;{
    render(&amp;lt;FormComponent/&amp;gt;)

    const usernameInput = screen.getByLabelText(&quot;Username&quot;)
    const passwordInput = screen.getByLabelText(&quot;Password&quot;)

    export(usernameInput).toBeInTheDocument()
    export(passwordInput).toBeInTheDocument()
}) &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;getByLabelText('Username')&lt;/code&gt;은 &quot;Username&quot;이라는 텍스트가 들어간 &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;과 연결된 &lt;code&gt;input&lt;/code&gt; 요소를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트에서 특정 폼 요소를 정확히 선택하고, 접근성을 높이는데 매우 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 여러 개의 요소가 있을 시 테스트는 실패한다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;&amp;lt; form&amp;gt;
    &amp;lt;label htmlFor=&quot;email1&quot;&amp;gt;Email&amp;lt;/label&amp;gt;
     &amp;lt;input id=&quot;email1&quot; type=&quot;email&quot; /&amp;gt; 

     &amp;lt;label htmlFor=&quot;email2&quot;&amp;gt;Email&amp;lt;/label&amp;gt; 
     &amp;lt;textArea id=&quot;email2&quot; type=&quot;email&quot; /&amp;gt;
&amp;lt;/form&amp;gt;


// 위의 폼 요소에서 테스트를 진행할 때

test('중복된 레이블 텍스트를 가진 요소-실패케이스', ()=&amp;gt;{
    render(&amp;lt;Form /&amp;gt;)
    const emailInput = screen.getByLabelText('Email')

    // 어느 'Email' 레이블과 연결된 요소를 선택할지 모르기 때문에 오류가 난다. 
    expect(emailInput).toBeInTheDocument()
})

test('중복된 레이블 텍스트를 가진 요소-성공케이스', ()=&amp;gt;{
    const emailInput = screen.getByLabelText('Email', { 
        selector: 'input' 
    })
    // selector 옵션을 사용하여 두 번째 textarea 요소를 선택합니다.
    expect(emailInput).toBeInTheDocument()
    const emailTextarea = screen.getByLabelText('Email', { 
        selector: 'textarea' 
    })
    expect(emailTextarea).toBeInTheDocument()
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소가 input과 textarea로 다르기 때문에 &lt;code&gt;getByLabelText()의&lt;/code&gt; 두 번째 인자 값인 option을 이용하여 어떤 요소를 선택할지 지정을 하면 테스트를 성공할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;2. getByPlaceholderText()&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 필드('input', 'textarea' 등)에서 &lt;code&gt;placeholder&lt;/code&gt; 속성값을 기반으로 요소를 찾을 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;label&lt;/code&gt;이 없는 경우에도 사용자 인터페이스에서 입력 요소를 쉽게 찾을 수 있게 도와준다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용 예시&lt;/h2&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;function Form() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input placeholder=&quot;Enter your name&quot; /&amp;gt;
      &amp;lt;input placeholder=&quot;Enter your email&quot; /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}




  // 폼 요소에 대한 테스트를 진행할 수 있습니다.
test('getByPlaceholderText를 사용한 요소 선택', () =&amp;gt; {
  render(&amp;lt;Form /&amp;gt;);

  const nameInput = screen.getByPlaceholderText('Enter your name');
  const emailInput = screen.getByPlaceholderText('Enter your email');

  expect(nameInput).toBeInTheDocument();
  expect(emailInput).toBeInTheDocument();
});

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주로 사용되는 상황&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;label&lt;/code&gt;이 없는 입력 필드 : label이 없고 placeholder 속성만 있는 경우&lt;/li&gt;
&lt;li&gt;&lt;code&gt;placeholder&lt;/code&gt; 텍스트를 이용해서 간결하게 테스트를 작성할 때&lt;/li&gt;
&lt;li&gt;사용자가 폼을 작성할 때 placeholder는 중요한 힌트를 제공하기 때문에, 이를 기반으로 테스트하는 것이 사용자 경험을 검증하는데 도움이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;3. getByText()&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기본적이고 많이 쓰이는 함수로, 요소의 텍스트 콘텐츠를 기반으로 특정 요소를 선택할 때 사용한다.&lt;br /&gt;주로 p태그, div태그, span 태그에 감싸있는 특정 텍스트를 선택할 때 사용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용예시&lt;/h2&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;function Component() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Welcome to My Website&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;This is a paragraph with some text.&amp;lt;/p&amp;gt;
      &amp;lt;button&amp;gt;Click Me&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}


test('getByText()를 이용한 요소 선택', ()=&amp;gt;{
    renter(&amp;lt;Component /&amp;gt;)

    const headingElement = screen.getByText('Welcome to My Website')
    expect(headingElement).toBeInTheDocument()

    const buttonElement = screen.getByText('Click Me')
    expect(buttonElement).toBeInTheDocument()

    const paragraphElement = screen.getByText('This is a paragraph with some text.')
    expect(paragraphElement).toBeInTheDocument();
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 h1, p, button 관계없이 text를 포함하는 요소를 선택할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 &lt;code&gt;getByText()&lt;/code&gt;는 정확하게 일치하는 텍스트를 찾는다 하지만 두 번째 인자값인 option을 이용해서 부분적으로 일치하는 텍스트를 찾을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;test('getByText()를 이용한 요소 선택', ()=&amp;gt;{
    renter(&amp;lt;Component /&amp;gt;)

    const headingElement = screen.getByText('Welcome to My', {
        exact:false
    })
    expect(headingElement).toBeInTheDocument()
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exact를 false로 지정하여 부분적으로 일치하는 요소를 찾을 수 있고, 또 다른 방법으로는 selector를 이용해서 특정한 HTML 태그를 지정해서 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;test('getByText()를 이용한 요소 선택', ()=&amp;gt;{
    renter(&amp;lt;Component /&amp;gt;)

    const buttonElement = screen.getByText('Click Me', { 
        selector: 'button' 
    }); 
    expect(buttonElement).toBeInTheDocument();
})
&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(42px, 4375px);&quot; aria-expanded=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>jest</category>
      <category>react test</category>
      <category>RTL</category>
      <category>TDD</category>
      <category>Testing</category>
      <category>테스트</category>
      <category>테스트 라이브러리</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/270</guid>
      <comments>https://baekspace.tistory.com/270#entry270comment</comments>
      <pubDate>Sun, 1 Sep 2024 16:59:00 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Tutorial(5) - getByRole, getByRole option</title>
      <link>https://baekspace.tistory.com/269</link>
      <description>&lt;h1&gt;&lt;b&gt;getByRole&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근성 트리를 통해 요소를 찾아내는 데 사용하는 메서드이다.&lt;br /&gt;사용자가 페이지에서 상호작용 할 수 있는 요소를 찾아내는 데 도움을 준다.&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;주요 개념&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 역할 기반 탐색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;getByRole&lt;/code&gt;은 HTML의 &lt;code&gt;role&lt;/code&gt; 속성에 따라 요소를 찾아낸다. 이 &lt;code&gt;role&lt;/code&gt;은 스크린 리더와 같은 접근성 도구가 웹 페이지를 해석할 때 사용하는 중요한 속성이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 일치 기준&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 역할 기반으로 찾지만 추가적으로 &lt;code&gt;name&lt;/code&gt;옵션을 사용하여 요소의 텍스트나 &lt;code&gt;aria-label&lt;/code&gt;과 같은 접근성 속성도 검사할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 다양한 HTML 요소 탐색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼, 링크, 이미지, 체크박스 등 다양한 HTML 요소를 탐색할 수 있다.&lt;br /&gt;&lt;code&gt;&amp;lt;button&amp;gt;, &amp;lt;a&amp;gt;&lt;/code&gt;&lt;code&gt;, &amp;lt;input type='checkbox'&amp;gt;&lt;/code&gt; 등은 각각 &lt;code&gt;button&lt;/code&gt;, &lt;code&gt;link&lt;/code&gt; , &lt;code&gt;checkbox와&lt;/code&gt; 같은 역할을 한다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;'getByRole'을 사용하는 이유&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근성을 테스트하기 위해서 가장 권장되는 메서드이기 때문에 &lt;code&gt;getByRole&lt;/code&gt;을 사용한다.&lt;br /&gt;&lt;code&gt;getByRole&lt;/code&gt;메서드를 사용하면 접근성 트리를 기반으로 요소를 찾기 때문에 실제 사용자 경험과 가장 가까운 테스트를 작성할 수 있다.&lt;/p&gt;
&lt;h1&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;getByRole Option - name&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;getByRole('textbox')&lt;/code&gt;를 통해서 input 태그를 찾을 수 있다.&lt;br /&gt;하지만 테스트하는 요소에서 input과 textarea 2개를 동시에 쓸 경우 테스트에서 실패를 한다.&lt;br /&gt;그 이유는 input과 textarea는 text를 입력할 수 있는 동일한 역할로 인식하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getByRole은 요소 한 개를 찾는 메서드이기 때문에 여러 요소를 찾게 되면 테스트에서 결과를 실패로 반환한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결하기 위해서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제를 해결하기 위해서 getByRole의 옵션 추가로 작성하여 해결할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;name 옵션 이용하기&lt;/li&gt;
&lt;li&gt;ex) getByRole(role, {name: &quot;&quot;})&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;접근 가능한 name&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. label&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;label for=&quot;username&quot;&amp;gt;Username&amp;lt;/label&amp;gt;
&amp;lt;input id=&quot;username&quot; type=&quot;text&quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, input 요소의 접근 가능한 이름은 &quot;Username&quot;이다.&lt;br /&gt;대소문자를 구분하기 때문에 주의해서 사용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;// pass
getByRole('textbox',{
    name: 'Username'
})

//fail
getByRole('textbox',{
    name:'username'
})

//(정규식을 사용해서 대소구분 없이 사용가능하다 : /username/i)
getByRole('textbox', { 
    name: /username/i   // 정규식 사용, 대소문자 무시
})&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 버튼의 텍스트 콘텐츠&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; , &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input type=&quot;button&quot;&amp;gt;&lt;/code&gt; 과 같은 요소의 텍스트 콘텐츠는 그 요소의 접근 가능한 이름이 된다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;button&amp;gt;Submit&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, 버튼의 접근 가능한 이름은 &quot;Submit&quot; 이 된다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;getByRole('button', {name:&quot;Submit&quot;})&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. aria-label 속성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;aria-label&lt;/code&gt;속성은 요소의 접근 가능한 이름을 명시적으로 정의한다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;&amp;lt;input aria-label=&quot;Search&quot; type=&quot;text&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, &lt;code&gt;input&lt;/code&gt; 요소의 접근가능한 이름은 &quot;Search&quot;가 된다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;getByRole('textbox', { name: 'Search' });&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;getByRole Option - level&lt;/b&gt;&lt;/h1&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;&amp;gt;
    &amp;lt;h1&amp;gt;Job application form&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Section 1&amp;lt;/h2&amp;gt;
&amp;lt;/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;h1~h6의 경우 heading을 이용해서 요소를 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위에서의 h1~h6는 모두 heading의 역할을 가지고 있기 때문에 구분을 해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, 사용할 수 있는 option으로 &lt;code&gt;level&lt;/code&gt;이라는 것이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;h1~h6까지 차례로 level 1~6이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// &amp;lt;h1&amp;gt;Job application form&amp;lt;/h1&amp;gt;
getByRole('heading', {
    level:1 
})


// &amp;lt;h2&amp;gt;Section 1&amp;lt;/h2&amp;gt;
getByRole('heading', {
    level:2
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;여러 가지 options&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getByRole에는 위에서 말한 2가지 옵션뿐 아니라 여러 가지가 더 있다.&lt;br /&gt;&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;level&lt;/code&gt;, &lt;code&gt;hidden&lt;/code&gt;, &lt;code&gt;selected&lt;/code&gt;, &lt;code&gt;checked&lt;/code&gt;...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 비슷한 맥락으로 해당 요소를 찾을 수 있도록 도와주는 역할이고 option을 이용해서 더 명확한 요소를 지정할 수 있다는 것을 알고 있으면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 자세한 옵션은 공식문서를 참고하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://testing-library.com/docs/queries/byrole&quot;&gt;https://testing-library.com/docs/queries/byrole&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(864px, 1948px);&quot; aria-expanded=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>getbyrole</category>
      <category>jest</category>
      <category>react test</category>
      <category>react testing</category>
      <category>RTL</category>
      <category>TDD</category>
      <category>리액트 tdd</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/269</guid>
      <comments>https://baekspace.tistory.com/269#entry269comment</comments>
      <pubDate>Sun, 25 Aug 2024 16:02:17 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Tutorial(4) - RTL Queries</title>
      <link>https://baekspace.tistory.com/268</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;RTL Queries는 React Testing Library (RTL)에서 제공하는 DOM 요소를 선택하고 검증하기 위한 함수들을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTL은 React 컴포넌트의 UI와 상호작용을 테스트하기 위해서 사용되는 라이브러리로 이 라이브러리의 핵심 기능 중 하나가 queries이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;RTL에서 제공하는 주요 queries&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. getBy&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조건에 맞는 단일 요소를 반환, 찾지 못하면 테스트가 실패한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// 'submit' 텍스트를 포함하는 요소를 반환
getByText('submit')&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. queryBy&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조건에 맞는 단일 요소를 반환, 찾지 못하면 'null'을 반환&lt;/li&gt;
&lt;li&gt;요소가 1개 이상 있을 경우 에러를 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// 'submit' 텍스트를 포함하는 요소를 반환하거나 null을 반환
queryByText('submit')&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. findBy&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비동기적으로 단일 요소를 찾으며, Promise를 반환&lt;/li&gt;
&lt;li&gt;요소를 찾으면 해당 요소를 반환하고, 찾지 못하면 에러 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// 비동기적으로 'submit' 텍스트를 포함하는 요소를 찾는다.
findByText('submit')&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.getAllBy&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조건에 맞는 모든 요소를 배열로 반환&lt;/li&gt;
&lt;li&gt;하나도 없을 경우 테스트가 실패한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;//'submit'텍스트를 포함하는 모든 요소를 배열로 반환
getAllByText('submit')&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. queryAllBy&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조건에 맞는 모든 요소를 배열로 반환&lt;/li&gt;
&lt;li&gt;요소가 없으면 빈 배열을 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// 'submit'텍스트를 포함하는 모든 요소를 배열로 반환
// 없으면 []
queryAllByText('submit')&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. findAllBy&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비동기적으로 조건에 맞는 모든 요소를 배열로 반환한다.&lt;/li&gt;
&lt;li&gt;Promise를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// 비동기적으로 'submit'텍스트를 포함하는 모든 요소를 반환
findAllByText('submit')&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실제 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 쿼리를 형성하기 위해서는 예시에서 본 것처럼 접미사를 포함하여 작성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접미사는 &lt;code&gt;Role&lt;/code&gt;, &lt;code&gt;LabelText&lt;/code&gt;, &lt;code&gt;PlaceHolderText&lt;/code&gt;, &lt;code&gt;Text&lt;/code&gt;, &lt;code&gt;DisplayValue&lt;/code&gt;, &lt;code&gt;AltText&lt;/code&gt;, &lt;code&gt;Title&lt;/code&gt;, &lt;code&gt;TestId&lt;/code&gt; 가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;접미사를 포함한 예시&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;getByRole()
queryByLabelText()
findByPlaceholderText()
getAllbyText()
queryByDisplayValue()
findAllByAltText()
getByTitle()
getByTestId()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(851px, 1910px);&quot; aria-expanded=&quot;false&quot;&gt;
&lt;div id=&quot;tippy-1&quot; style=&quot;z-index: 100000200; visibility: hidden; position: absolute; inset: auto auto 0px 0px; margin: 0px; transform: translate3d(122px, -20px, 0px); pointer-events: none;&quot; data-tippy-root=&quot;&quot;&gt;
&lt;div class=&quot;tippy-box&quot; style=&quot;max-width: 350px; transition-duration: 250ms;&quot; tabindex=&quot;-1&quot; role=&quot;mtttooltip&quot; data-state=&quot;hidden&quot; data-theme=&quot;custom&quot; data-animation=&quot;fade&quot; data-placement=&quot;top&quot;&gt;
&lt;div class=&quot;tippy-content&quot; style=&quot;transition-duration: 250ms;&quot; data-state=&quot;hidden&quot;&gt;&lt;span&gt;getByRole() queryByLabelText() findByPlaceholderText() getAllbyText() queryByDisplayValue() findAllByAltText() getByTitle() getByTestId()&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;tippy-arrow&quot; style=&quot;position: absolute; left: 0px; transform: translate3d(369px, 0px, 0px);&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/268</guid>
      <comments>https://baekspace.tistory.com/268#entry268comment</comments>
      <pubDate>Thu, 15 Aug 2024 15:55:35 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Tutorial(3) - Assertion, Matchers</title>
      <link>https://baekspace.tistory.com/267</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Assertions&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트에서 코드의 특정 조건이나 표현이 예상대로 작동하는지를 확인하는 구문을 의미한다.&lt;br /&gt;테스트 중인 코드가 기대한 결과를 반환하는지, 특정 상태에 있는지를 확인하기 위해서 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;Assertion&lt;/code&gt;은 코드의 동작이 기대와 일치하는지 확인해 주고, 예상과 일치하지 않을 경우 테스트를 실패로 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 통과/실패로 만들어 예상과 실제 결과 간의 불일치를 조기에 발견할 수 있게 도와준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;expect(value).toBe(expectedValue)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 구문이 Assertion으로 value가 expectedValue와 동일한지 검사한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Matchers&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 대상 코드의 결과를 예상한 값과 비교하는 데 사용되는 함수이다. expect와 함께 사용되고, 올바로 작동하는지 확인하는 Assertion을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 값을 비교하는 것뿐만 아니라, 요소 존재 여부, 텍스트 내용, 스타일 등 다양한 조건을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;toBeNull&lt;/code&gt;, &lt;code&gt;toBeTruthy&lt;/code&gt;, &lt;code&gt;toContain&lt;/code&gt; 등이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;jest에서 사용할 수 있는 matcher&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jestjs.io/docs/using-matchers&quot;&gt;https://jestjs.io/docs/using-matchers&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;DOM 테스트&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM을 테스트하기 위해서 Jest DOM을 추가적으로 설치하여 테스트할 수 있다.&lt;br /&gt;jest DOM을 이용하면 프런트엔드 프레임워크에서 컴포넌트를 테스트할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jest DOM을 이용하면 일반적인 Jest Matcher보다 DOM 요소에 특화된 매처를 제공하므로 훨씬 의미 있는 테스트를 진행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 jest에 포함되지 않은 &lt;code&gt;toBeInTheDocument&lt;/code&gt;, &lt;code&gt;toHaveTextContent&lt;/code&gt;, &lt;code&gt;toBeVisible&lt;/code&gt; 등의 Matcher가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;jestDOM에서 사용할 수 있는 Matcher&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/testing-library/jest-dom&quot;&gt;https://github.com/testing-library/jest-dom&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jest-dom의 깃허브에서 사용할 수 있는 matcher와 언제 사용하면 좋을지에 대한 내용, 예시를 포함하고 있으니 확인해 보면 좋을 것 같다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;무엇에 대해서 테스트&lt;/b&gt;를 진행할 것인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;늘 테스트 코드를 작성하고 싶다고 생각하고, 테스트 코드가 좋다는 것은 알고 있었다. 하지만 무엇을 테스트해야 하며, 어떤 것을 테스트할지 고민을 하다가 결국 못하는 상황의 반복이었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;어떤 것을 테스트해야 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 코드에 대해서 명확하게 설정되어 있는 규칙은 없다. 하지만 리엑트에서 테스트할 가치가 있는 것에 대한 가이드라인은 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트가 오류 없이 잘 렌더링 되는가?&lt;/li&gt;
&lt;li&gt;props와 함께 컴포넌트가 잘 렌더링 되는가?&lt;/li&gt;
&lt;li&gt;다른 상태에서 컴포넌트가 잘 렌더링 되는가?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, nav bar가 로그인 상태에서는 보이지만 로그아웃 된 상태에서는 안 보여야 하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;컴포넌트가 이벤트에 올바르게 작동하는가?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, 버튼이나, form 컨트롤 같은 컴포넌트에 적용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;어떤 것을 테스트하지 말아야 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 테스트를 하지 않는 것이 좋은 경우가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구현 세부정보&lt;/li&gt;
&lt;li&gt;서드파티 코드 ( 내가 작성하지 않은 코드 )
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 사람이 만든 라이브러리 같은 경우 테스트를 진행하지 않아도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용자 관점에서 중요하지 않는 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;이렇게 총 7가지의 가이드라인은 기초적인 것이며, 이것을 바탕으로 테스트 코드를 작성하면서 서비스를 구현하면 좋을 것 같다.&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>jest</category>
      <category>jestdom</category>
      <category>react test</category>
      <category>test</category>
      <category>프론트엔드 테스트</category>
      <category>프론트엔드 테스트 코드</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/267</guid>
      <comments>https://baekspace.tistory.com/267#entry267comment</comments>
      <pubDate>Sat, 10 Aug 2024 13:24:09 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Tutorial (2) - Code Coverage</title>
      <link>https://baekspace.tistory.com/266</link>
      <description>&lt;h1&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;1. Code Coverage&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트가 얼마나 코드베이스를 덮고 있는지 측정한다.&lt;/li&gt;
&lt;li&gt;코드 적용 범위는 테스트된 소프트 웨어 코드의 양을 이해하는데 도움이 되는 측정 기준이다.&lt;/li&gt;
&lt;li&gt;테스트의 품질을 평가하는 데 도움이 되는 매우 유용한 측정 기준이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Coverage의 종류&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Statement coverage (명령문 커버리지)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령문 커버리지는 코드의 각 명령문이 최소한 한 번은 실행되었는지를 확인한다.&lt;/li&gt;
&lt;li&gt;코드의 모든 명령문이 실행되었는지 확인하여, 테스트가 충분히 이루어졌는지 확인한다.&lt;/li&gt;
&lt;li&gt;아래의 예시에서는 2번 실행되었는지 여부를 체크할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;function add(a,b) {
    let sum = a + b // 명령문 1
    return sum;  // 명령문 2
} &lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Branches coverage (브랜치 커버리지)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 내의 모든 조건문의 모든 분기가 실행되었는 지를 확인한다.&lt;/li&gt;
&lt;li&gt;예를 들어 if, else, switch 등의 모든 가능한 경로가 테스트되었는지를 확인한다.&lt;/li&gt;
&lt;li&gt;브랜치 커버리지는 if문의 true와 fasle 분기가 모두 실행되었는지 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function isEven(numn){
    if(num % 2 === 0){ // 브랜치 1
        return true;
    }else{ // 브랜치 2
        return false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Function coverage (함수 커버리지)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 내의 모든 함수가 최소한 한 번은 호출되었는지를 확인한다.&lt;/li&gt;
&lt;li&gt;즉, 테스트가 코드의 모든 함수를 호출했는지 평가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function multiply(a, b) { // 함수 1 
    return a * b; 
} 
function divide(a, b) { // 함수 2 
    if (b !== 0) { 
        return a / b; 
    } else { 
        throw new Error(&quot;Division by zero&quot;); 
    } 
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Line coverage (라인 커버리지)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드의 각 라인이 최소한 한 번은 실행되었는지를 확인한다.&lt;/li&gt;
&lt;li&gt;코드의 정확성과 품질을 평가하는데 좋은 지표가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function calculateTotal(price, tax) {
    let total = price + tax; // 라인 1
    if (total &amp;gt; 100) { // 라인 2
        return total - 10; // 라인 3
    }
    return total; // 라인 4
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;2. Coverage 사용하기&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;coverage를 사용하기 위해서 --coverage 옵션을 추가하여 사용할 수 있다.&lt;br /&gt;기존에 프로젝트에서 &lt;b&gt;test&lt;/b&gt;라는 스크립트를 이용해서 테스트를 하고 있었고, coverage용 스크립트를 추가로 작성해 준다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
    &quot;start&quot;: &quot;react-scripts start&quot;,
    &quot;build&quot;: &quot;react-scripts build&quot;,
    &quot;test&quot;: &quot;react-scripts test&quot;,
    &quot;eject&quot;: &quot;react-scripts eject&quot;,
    &quot;coverage&quot;: &quot;react-scripts test --coverage&quot; // test와 동일한 스크립트에 --coverage를 추가한다.
},&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성하고 나면 &lt;code&gt;npm run coverage&lt;/code&gt;를 이용해서 coverage 기능을 사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행해 보면 아래와 같은 결과가 나온다. 위에서 설명했던 커버리지를 종류별로 볼 수 있게 된다.&lt;br /&gt;![[jest-4.png]]&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jest-4.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDJzI7/btsISHiZglG/ecpbrbAUAsSj0Bl2kGwO7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDJzI7/btsISHiZglG/ecpbrbAUAsSj0Bl2kGwO7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDJzI7/btsISHiZglG/ecpbrbAUAsSj0Bl2kGwO7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDJzI7%2FbtsISHiZglG%2FecpbrbAUAsSj0Bl2kGwO7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;348&quot; data-filename=&quot;jest-4.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼센티지가 높을수록 많은 코드를 테스트했다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;coverage에 --watchAll 옵션을 추가해서 사용하게 되면 파일이 변경될 때 자동으로 실행하는 것뿐 아니라 조금 더 자세한 결과를 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jest-5.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bco1wE/btsISpiAcOm/URq0CPCSJXjv7dt24rike1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bco1wE/btsISpiAcOm/URq0CPCSJXjv7dt24rike1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bco1wE/btsISpiAcOm/URq0CPCSJXjv7dt24rike1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbco1wE%2FbtsISpiAcOm%2FURq0CPCSJXjv7dt24rike1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;258&quot; data-filename=&quot;jest-5.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;포함할 폴더 지정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jest coverage를 이용할 때 coverage에 감지되는 디렉터리를 지정할 수 있다.&lt;br /&gt;이 또한, &lt;b&gt;Script를&lt;/b&gt; 이용할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;--collectCoverageFrom&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collectCoverageFrom 옵션을 이용해서 코드 커버리지 수집 시 어떠한 파일을 포함할지를 지정할 수 있다.&lt;br /&gt;특정 파일이나 디렉터리만 커버리지 리포트에 포함할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;react-scripts test&quot;,
    &quot;coverage&quot;: &quot;react-scripts test --coverage --watchAll --collectCoverageFrom='src/component/**/*.{ts,tsx}'&quot;
  },
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 스크립트를 통해서 src/component 안에 있는 ts, tsx 파일만 리포트에 포함시키도록 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jest-6.png&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMYYHX/btsITh5dl17/XdMPkmBbglTOBduiueatlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMYYHX/btsITh5dl17/XdMPkmBbglTOBduiueatlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMYYHX/btsITh5dl17/XdMPkmBbglTOBduiueatlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMYYHX%2FbtsITh5dl17%2FXdMPkmBbglTOBduiueatlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;204&quot; data-filename=&quot;jest-6.png&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제외하고 싶은 파일 패턴&lt;/h3&gt;
&lt;pre class=&quot;brainfuck&quot;&gt;&lt;code&gt; &quot;coverage&quot; : &quot;react-scripts test --coverage --watchAll --collectCoverageFrom='src/component/**/*.{ts,tsx}' --collectCoverageFrom='!src/component/**/*.{types, stories, constants,test,spec}.{ts,tsx}'&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collectCoverageFrom 옵션의 경우 연속해서 쓸 수 있다.&lt;br /&gt;위의 예시는 ts, tsx 파일 앞에 types, stories, contants, test, spec 이 붙은 파일을 리포트에서 제외하겠다는 의미로 사용할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표 설정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 진행하면서 해당 테스트의 코드 커버리지의 목표치를 &lt;code&gt;coveragethreshold&lt;/code&gt;를 이용하여 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 옵션을 이용하여 각 커버리지에 대한 최소한의 커버리지 비율을 지정할 수 있고, 설정한 목표치에 충족하지 않으면 테스트가 실패한다. 목표를 설정하여 코드 품질을 유지하고 테스트 커버리지를 높일 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;coverageThreshold 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 방법은 package.json에 &lt;code&gt;jest&lt;/code&gt; 필드를 이용하여 설정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;jest&quot;: {
    &quot;coverageThreshold&quot;: {
      &quot;global&quot;: {
        &quot;branches&quot;: 80,
        &quot;functions&quot;: 90,
        &quot;lines&quot;: 90,
        &quot;statements&quot;: 90
      },
      &quot;./src/componentA.js&quot;: { 
          &quot;branches&quot;: 70, 
          &quot;functions&quot;: 80, 
          &quot;lines&quot;: 85, 
          &quot;statements&quot;: 85
       }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시는 전역적으로 설정하는 것과 특정 파일에 대한 설정을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정된 커버리지 목표를 충족하지 못하면 테스트가 실패한다.&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/266</guid>
      <comments>https://baekspace.tistory.com/266#entry266comment</comments>
      <pubDate>Fri, 2 Aug 2024 00:33:35 +0900</pubDate>
    </item>
    <item>
      <title>React Testing Tutorial (1) - Filtering, Grouping</title>
      <link>https://baekspace.tistory.com/265</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Filtering Tests&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jest의 Watch 모드에서 특정 테스트만 실행하고 싶을 때, &lt;code&gt;t&lt;/code&gt;,&lt;code&gt;p&lt;/code&gt;를 누르면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;p&lt;/code&gt;를 누르면 테스트 파일명을 입력할 수 있는 프롬프트가 나타난다. 테스트 파일명에 대한 정규식을 입력하면 해당 정규식을 만족하는 테스트 파일만 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 방법으로는 &lt;code&gt;t&lt;/code&gt;를 누르면 테스트명을 입력할 수 있는 프롬프트가 나타난다. &lt;code&gt;t&lt;/code&gt;는 테스트명에 대한 정규식을 입력할 수 있게 해 준다. 이 경우 해당 정규식을 만족하는 테스트만 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jest-1.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTgixl/btsIKKMIUDN/GjLHik9cKDpyvatUpXUndK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTgixl/btsIKKMIUDN/GjLHik9cKDpyvatUpXUndK/img.png&quot; data-alt=&quot;jest watch 모드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTgixl/btsIKKMIUDN/GjLHik9cKDpyvatUpXUndK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTgixl%2FbtsIKKMIUDN%2FGjLHik9cKDpyvatUpXUndK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;822&quot; height=&quot;238&quot; data-filename=&quot;jest-1.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;jest watch 모드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jest-2.png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyH2AR/btsIIy71W1H/KQ8K55iryKjwIeRUEmnuo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyH2AR/btsIIy71W1H/KQ8K55iryKjwIeRUEmnuo0/img.png&quot; data-alt=&quot;test name&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyH2AR/btsIIy71W1H/KQ8K55iryKjwIeRUEmnuo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyH2AR%2FbtsIIy71W1H%2FKQ8K55iryKjwIeRUEmnuo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;258&quot; data-filename=&quot;jest-2.png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;test name&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jest-3.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JAny7/btsIH2ICVpX/KYSvehHjIOkvzIo7LcKvx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JAny7/btsIH2ICVpX/KYSvehHjIOkvzIo7LcKvx0/img.png&quot; data-alt=&quot;file name&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JAny7/btsIH2ICVpX/KYSvehHjIOkvzIo7LcKvx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJAny7%2FbtsIH2ICVpX%2FKYSvehHjIOkvzIo7LcKvx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;254&quot; data-filename=&quot;jest-3.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;file name&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드를 이용한 방법 - &lt;code&gt;test.only&lt;/code&gt;, &lt;code&gt;test.skip&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;test.only&lt;/code&gt;를 사용하면 해당 테스트만 실행된다. &lt;code&gt;test.skip&lt;/code&gt;을 사용하면 해당 테스트는 실행되지 않는다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;test.only(&quot;this will be the only test that runs&quot;, () =&amp;gt; {
    expect(true).toBe(false)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;test.only&lt;/code&gt;가 사용되었기 때문에 해당 테스트만 실행된다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;test.skip(&quot;this test will be skipped&quot;, () =&amp;gt; {
    expect(true).toBe(false)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;test.skip&lt;/code&gt;이 사용되었기 때문에 해당 테스트는 실행되지 않는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Grouping Tests&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;describe&lt;/code&gt;를 이용한 그룹화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 그룹화하여 관리할 수 있다. &lt;code&gt;describe&lt;/code&gt; 함수를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;describe(name, fn)&lt;/code&gt; 함수는 두 개의 인자를 받는다. 첫 번째 인자는 그룹의 이름이고, 두 번째 인자는 그룹에 속하는 테스트들을 정의하는 함수이다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;describe(&quot;group&quot;, () =&amp;gt; {
    test(&quot;test1&quot;, () =&amp;gt; {
        expect(true).toBe(true)
    })
    test(&quot;test2&quot;, () =&amp;gt; {
        expect(true).toBe(true)
    })
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;group&lt;/code&gt;이라는 그룹에 &lt;code&gt;test1&lt;/code&gt;, &lt;code&gt;test2&lt;/code&gt;라는 두 개의 테스트가 속해있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;describe 함수 또한 filtering을 할 수 있다. &lt;code&gt;describe.only&lt;/code&gt;를 사용하면 해당 그룹만 실행된다. &lt;code&gt;describe.skip&lt;/code&gt;을 사용하면 해당 그룹은 실행되지 않는다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;describe.only(&quot;group&quot;, () =&amp;gt; {
    test(&quot;test1&quot;, () =&amp;gt; {})
    test(&quot;test2&quot;, () =&amp;gt; {})
})

describe.skip(&quot;group&quot;, () =&amp;gt; {
    test(&quot;test3&quot;, () =&amp;gt; {})
    test(&quot;test4&quot;, () =&amp;gt; {})
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 describe 함수는 중첩해서 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;describe(&quot;group1&quot;, () =&amp;gt; {
    test(&quot;test1&quot;, () =&amp;gt; {})
    test(&quot;test2&quot;, () =&amp;gt; {})
    describe(&quot;group2&quot;, () =&amp;gt; {
        test(&quot;test3&quot;, () =&amp;gt; {})
        test(&quot;test4&quot;, () =&amp;gt; {})
    })
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Filename Conventions&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jest는 테스트 파일을 찾을 때 특정한 파일명 규칙을 따른다. Jest는 &lt;code&gt;__tests__&lt;/code&gt; 디렉토리에 있는 파일과 &lt;code&gt;test.js&lt;/code&gt;나 &lt;code&gt;spec.js&lt;/code&gt;로 끝나는 파일을 찾아서 테스트 파일로 인식한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 파일 구조를 가진 프로젝트가 있다고 가정하자.&lt;/p&gt;
&lt;pre class=&quot;x86asm&quot;&gt;&lt;code&gt;.
├──/__tests__
│└── my-test.js
└── my-test.spec.js&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 프로젝트에서 &lt;code&gt;my-test.js&lt;/code&gt;와 &lt;code&gt;my-test.spec.js&lt;/code&gt; 파일은 Jest에 의해 테스트 파일로 인식된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 테스트 중인 코드 옆에 테스트 파일을 두고 상대경로가 짧아지도록 하는 것이 좋다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Aliases&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;it과 test&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;it&lt;/code&gt;과 &lt;code&gt;test&lt;/code&gt;는 동일한 함수이다. 둘 중 어떤 것을 사용해도 상관없다. &lt;code&gt;it&lt;/code&gt;은 &lt;code&gt;test&lt;/code&gt;의 alias이다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;test(&quot;this is a test&quot;, () =&amp;gt; {})
it(&quot;this is a test&quot;, () =&amp;gt; {})&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;fit과 test.only&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;fit&lt;/code&gt;과 &lt;code&gt;test.only&lt;/code&gt;는 동일한 함수이다. 둘 중 어떤 것을 사용해도 상관없다. &lt;code&gt;fit&lt;/code&gt;은 &lt;code&gt;test.only&lt;/code&gt;의 alias이다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;test.only(&quot;this is the only test that runs&quot;, () =&amp;gt; {})
fit(&quot;this is the only test that runs&quot;, () =&amp;gt; {})&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;xit과 test.skip&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;xit&lt;/code&gt;과 &lt;code&gt;test.skip&lt;/code&gt;은 동일한 함수이다. 둘 중 어떤 것을 사용해도 상관없다. &lt;code&gt;xit&lt;/code&gt;은 &lt;code&gt;test.skip&lt;/code&gt;의 alias이다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;test.skip(&quot;this test will be skipped&quot;, () =&amp;gt; {})
xit(&quot;this test will be skipped&quot;, () =&amp;gt; {})&lt;/code&gt;&lt;/pre&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/265</guid>
      <comments>https://baekspace.tistory.com/265#entry265comment</comments>
      <pubDate>Tue, 23 Jul 2024 13:34:21 +0900</pubDate>
    </item>
    <item>
      <title>웹 페이지 렌더링 방식 이해: CSR, SSR, SSG, ISG</title>
      <link>https://baekspace.tistory.com/264</link>
      <description>&lt;h1&gt;&lt;b&gt;웹 페이지를 표시하는 방법&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지를 렌더링 하는 방법에는 &lt;b&gt;CSR, SSR, SSG, ISG&lt;/b&gt; 가 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React를 처음 배울 때 CSR, SSR의 기본적인 차이점에 대해서 공부했었고, 이때의 개념으로 CSR, SSR을 사용해 왔다. 하지만 Next를 공부하면서 SSG, ISG에 대한 개념을 접하게 되었고 이 부분에 대해서 미흡하여 글을 작성했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;1. CSR : 클라이언트 사이드 렌더링&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 렌더링 작업이 사용자 브라우저에서 진행된다.&lt;/li&gt;
&lt;li&gt;페이지 로딩 후 필요한 데이터만 서버로부터 비동기적으로 불러와서 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 인터랙션이 매우 빠르고, 애플리케이션의 동적인 부분을 효과적으로 처리할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 로딩 시 모든 스크립트를 불러와야 해서 시간이 오래 걸릴 수 있다&lt;/li&gt;
&lt;li&gt;검색 엔진 최적화(SEO)에 불리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;보안
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;코드 스플리팅&lt;/b&gt; : JS 번들을 여러 개의 청크로 나누어 필요한 시점에만 로드하도록 구성한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSR 또는 프리렌더링&lt;/b&gt; : 검색 엔진 최적화를 위해서 서버 사이드 렌더링으로 변경하거나 프리렌더링 기술을 사용하여 초기 페이지 로딩 시 검색 엔진이 크롤링할 수 있는 HTML을 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동적 임포트&lt;/b&gt; : 사용자 경험에 필수적이지 않은 컴포넌트는 동적으로 불러와서 처리한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;delphi&quot; data-ke-language=&quot;delphi&quot;&gt;&lt;code&gt;sequenceDiagram
    participant 사용자
    participant 브라우저
    participant 서버
    사용자-&amp;gt;&amp;gt;브라우저: 페이지 요청     
    브라우저-&amp;gt;&amp;gt;서버: HTML/CSS/JS 요청     
    서버-&amp;gt;&amp;gt;브라우저: HTML/CSS/JS 응답     
    브라우저-&amp;gt;&amp;gt;브라우저: 페이지 렌더링     
    브라우저-&amp;gt;&amp;gt;서버: API 요청(데이터 필요)     
    서버-&amp;gt;&amp;gt;브라우저: API 응답     
    브라우저-&amp;gt;&amp;gt;브라우저: 데이터로 페이지 업데이트&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;2. SSR : 서버 사이드 렌더링&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버에서 HTML을 완성하여 브라우저로 보내주고, 브라우저는 이를 받아서 화면에 표시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 페이지 로딩 속도가 빨라 사용자가 컨텐츠를 빠르게 볼 수 있으며, SEO에 유리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 페이지 요청에 대해 서버에서 렌더링을 해야 하기 때문에 서버 부하가 크다.&lt;/li&gt;
&lt;li&gt;사용자 인터랙션이 CSR에 비해 느리다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;보안
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;캐싱&lt;/b&gt; : 페이지나 데이터를 캐시 하여 반복적인 서버 요청을 줄인다. 서버 부담을 감소하고 응답 시간을 단축할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;부하 분산&lt;/b&gt; : 로드 밸런서를 활용하여 요청을 여러 서버에 분산시키고, 서버의 과부하를 방지한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 패칭 최적화&lt;/b&gt; : 데이터를 필요한 최소한으로 불러오고, 사용자의 인터랙션에 따라 데이터를 추가로 불러오는 방식으로 구성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;delphi&quot; data-ke-language=&quot;delphi&quot;&gt;&lt;code&gt;sequenceDiagram
    participant 사용자
    participant 브라우저
    participant 서버
    사용자-&amp;gt;&amp;gt;브라우저: 페이지 요청
    브라우저-&amp;gt;&amp;gt;서버: 페이지 요청
    서버-&amp;gt;&amp;gt;서버: HTML 렌더링
    서버-&amp;gt;&amp;gt;브라우저: 렌더링된 HTML 응답
    브라우저-&amp;gt;&amp;gt;브라우저: 페이지 표시&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;3. SSG : 정적 사이트 생성 (Static Site Generation)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 타임에 모든 페이지를 HTML로 미리 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매우 빠른 로딩 속도와 높은 보안성을 제공한다.&lt;/li&gt;
&lt;li&gt;서버 부하가 거의 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사이트에 실시간으로 변하는 데이터를 표현하기 어렵다.&lt;/li&gt;
&lt;li&gt;업데이트가 필요한 경우 전체 사이트를 다시 빌드해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;보안
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ISG&lt;/b&gt; : 특정 페이지만 재생성하여 전체 사이트의 재빌드 없이 최신 데이터를 반영할 수 있다.&lt;/li&gt;
&lt;li&gt;클라이언트 사이드 자바스크립트를 사용하여 최신 데이터를 동적으로 불러온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;delphi&quot; data-ke-language=&quot;delphi&quot;&gt;&lt;code&gt;sequenceDiagram
    participant 사용자
    participant 브라우저
    participant 서버
    사용자-&amp;gt;&amp;gt;브라우저: 페이지 요청
    브라우저-&amp;gt;&amp;gt;서버: HTML 요청
    Note right of 서버: 빌드 타임에 생성된 HTML
    서버-&amp;gt;&amp;gt;브라우저: 정적 HTML 응답
    브라우저-&amp;gt;&amp;gt;브라우저: 페이지 표시&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;4. ISG : 점진적 정적 재생성 (Incremental Static Regeneration)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSG의 개념을 확장하여, 사이트가 이미 빌드된 후에도 특정 페이지를 요청받을 때 해당 페이지만 다시 생성하고 캐시를 업데이트할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 사이트의 빠른 로딩 속도와 보안성을 유지하면서, 필요에 따라 특정 페이지만 업데이트할 수 있는 유연성을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체적으로 SSG에 비해 구현 복잡성이 증가한다.&lt;/li&gt;
&lt;li&gt;재생성 주기 관리가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;보안
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;재생성 전략 최적화&lt;/b&gt; : 트래픽이 많은 시간대에 재생성을 피하고, 낮은 트래픽 시간대에 자동으로 페이지를 재생성하도록 스케줄링한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효율적인 캐시 관리&lt;/b&gt; : 캐시 된 페이지의 만료 신간을 적절하게 설정하고, 필요할 때만 페이지를 재생성하도록 구현한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;delphi&quot; data-ke-language=&quot;delphi&quot;&gt;&lt;code&gt;sequenceDiagram
    participant 사용자
    participant 브라우저
    participant 서버
    사용자-&amp;gt;&amp;gt;브라우저: 페이지 요청
    브라우저-&amp;gt;&amp;gt;서버: HTML 요청
    alt 페이지가 캐시에 있음
        서버-&amp;gt;&amp;gt;브라우저: 캐시된 HTML 응답
    else 페이지 재생성 필요
        서버-&amp;gt;&amp;gt;서버: HTML 재생성
        서버-&amp;gt;&amp;gt;브라우저: 새로운 HTML 응답
    end
    브라우저-&amp;gt;&amp;gt;브라우저: 페이지 표시&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;각 렌더링 방식의 우선순위&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아래의 상황은 일반적인 상황일 때를 말한 것이다. 각 프로젝트에 따라서 다를 수 있기 때문에 프로젝트에 맞춰서 선택해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;업데이트 주기에 따라 (업데이트 주기가 짧을수록)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSR &amp;gt; SSR &amp;gt; ISG &amp;gt; SSG&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SEO(검색 엔진 최적화)의 중요도에 따라&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR &amp;gt; ISG &amp;gt; SSG &amp;gt; CSR&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;초기 로딩 속도에 따라&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG &amp;gt; ISG &amp;gt; SSR &amp;gt; CSR&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;사용자 인터랙션의 복잡성에 따라&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSR &amp;gt; SSR &amp;gt; ISG &amp;gt; SSG&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;트래픽 부하 대응에 따라&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG &amp;gt; ISG &amp;gt; SSR &amp;gt; CSR&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;보안성에 따라&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG &amp;gt; ISG &amp;gt; SSR &amp;gt; CSR&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>csr</category>
      <category>isg</category>
      <category>rendering</category>
      <category>ssg</category>
      <category>SSR</category>
      <category>랜더링</category>
      <category>랜더링 방식</category>
      <category>렌더링</category>
      <category>서버 사이드</category>
      <category>클라이언트사이드</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/264</guid>
      <comments>https://baekspace.tistory.com/264#entry264comment</comments>
      <pubDate>Wed, 10 Jul 2024 16:24:20 +0900</pubDate>
    </item>
    <item>
      <title>네트워크 관리사 2급 실기 기출문제 - 라우터</title>
      <link>https://baekspace.tistory.com/263</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;모드 종류&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;사용자 모드&lt;/b&gt; : Router&amp;gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관리자 모드&lt;/b&gt; : Router#&lt;/li&gt;
&lt;li&gt;&lt;b&gt;설정모드&lt;/b&gt; : Router(config)#&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라인설정모드&lt;/b&gt; : Router(config-line)#&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인터페이스 모드&lt;/b&gt; : Router(config-if)#&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;FastEthernet 0/0의 IP를 192.168.0.100/24 로 설정&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
en
conf t
interface fastethernet 0/0
ip add 192.168.0.100 255.255.255.0
no shutdown
exit
exit
copy r s&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Serial 2/0 대역폭 2048로 설정&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;
en

conf t

interface serial 2/0

bandwidth 2048

exit

exit

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Serial 2/0 클럭속도 72k&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;en
conf t
interface serial 2/0
clock rate 72000
exit
exit
copy r s&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;FastEthernet 0/0 Description : ICQA 설정&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;
en

conf t

interface fastethernet 0/0

description ICQA

exit

exit

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Serial 2/0을 사용가능하게 IP 주소를 192.168.0.101/24와 두번째 IP 192.168.0.102.24로 설정&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
en

conf t

interface serial 2/0

ip add 192.168.0.101 255.255.255.0

ip add 192.168.0.102 255.255.255.0 secondary

no shutdown

exit

exit

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 게이트웨이 설정 IP: 192.168.0.10 +&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;
en
conf t
ip default-gateway 192.168.0.10
exit
copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 네트워크를 192.168.0.10 으로 설정&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;en
conf t
ip default-network 192.168.0.10
exit
copy r s&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DHCP 네트워크를 192.168.100.0/24 서버이름 : icqa +&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
en

conf t

ip dhcp pool icqa

network 192.168.100.0 255.255.255.0

exit

exit

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Telnet password를 icqa로 변경 +&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;
en

conf t

line vty 0 4

password icqa

login # 반드시 로그인

exit

exit

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Telnet 연결 후 3분 50초 동안 입력 없으면 세션 자동 종료 +&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;
en
conf t
line vty 0 4
exec-timeout 03 50
exit
exit
copy r s&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;console 0의 패스워드를 ICQA로 설정하고 로그인&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;
en

conf t

line console 0

password ICQA

login

exit

exit

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Serial 2/0 활성화&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;
en

conf t

interface serial 2/0

no shutdown

exit

exit

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hostname을 network2로 변경하고 console 0의 password를 router5로 변경 후 로그인 +&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;
en

conf t

hostname network2

line console 0

password router 5

login

exit

exit

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인터페이스 정보를 확인하고 저장&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
en

show interface

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;접속한 사용자 정보 확인 후 저장&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
en

show user

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;라우팅 테이블 정보를 확인하고 저장 +&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
en

show ip route

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;플래시 정보를 확인하고 저장 +&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;
en

show flash

copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로세스 정보를 확인하고 저장 +&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;en
show process
copy r s&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;router에 access-list 1이 설정되어 있을 때 fastethernet 0/0에 적용하시오&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pc -&amp;gt; router : in&lt;br /&gt;router -&amp;gt; pc : out&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;en
conf t
ip access-group 1 in 
ip access-group 1 out
exit
exit
copy r s
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;router 1 에 OSPF 를 설정하시오 x&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;en
conf t
router ospf 1
network 192.70.100.0 0.0.0.255 a&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정적 라우팅&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-06-22 오전 1.24.33.png&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qupjK/btsH9vpeXBi/3hkV7xXg49TTjfMndT6gZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qupjK/btsH9vpeXBi/3hkV7xXg49TTjfMndT6gZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qupjK/btsH9vpeXBi/3hkV7xXg49TTjfMndT6gZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqupjK%2FbtsH9vpeXBi%2F3hkV7xXg49TTjfMndT6gZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;910&quot; height=&quot;498&quot; data-filename=&quot;스크린샷 2024-06-22 오전 1.24.33.png&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;접기/펼치기&lt;/summary&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;en
conf t
ip route 192.188.20.0 255.255.255.0 176.18.1.2
exit
copy r s&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>네관 2급 실기</category>
      <category>네관 실기</category>
      <category>네관2급</category>
      <category>라우터</category>
      <category>실기</category>
      <category>실기 기출문제</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/263</guid>
      <comments>https://baekspace.tistory.com/263#entry263comment</comments>
      <pubDate>Sat, 22 Jun 2024 01:31:43 +0900</pubDate>
    </item>
    <item>
      <title>Formik 이쁘게 사용하기</title>
      <link>https://baekspace.tistory.com/262</link>
      <description>&lt;h1&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;목차&lt;/span&gt;&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Formik이란?&lt;/li&gt;
&lt;li&gt;Formik과 React-Hook-Form&lt;/li&gt;
&lt;li&gt;하지만 난 Formik을 쓴다.&lt;/li&gt;
&lt;li&gt;그렇다면 어떻게 해야 할까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Formik이란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;formik은 React 애플리케이션에서 폼 상태 관리를 쉽게 도와주는 &lt;b&gt;라이브러리&lt;/b&gt;이다.&lt;br /&gt;복잡한 폼 입력, 검증, 폼의 제출 처리를 위한 로직을 간단하게 구현할 수 있도록 설계되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 개발자들은 React-Hook-Form과 비교하여 사용 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Formik과 React-Hook-Form&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Formik과 React-Hook-Form(이하 'RHF')의 큰 차이점은 상태를 이용하여 관리하는가? 아닌가?로 나뉘는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Formik은 React의 State를 통해서 관리하고, RHF은 'uncontrolled components'를 사용하여 성능을 최적화한다. 그렇기 때문에 &lt;b&gt;Formik은 입력 필드가 많아지면 상태에 의해 많은 리렌더링이 일어나 성능에 영향을 줄 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RHF은 폼 데이터를 DOM 수준에서 관리하기 때문에 처리가 효율적이고 상호 작용이 가능하다. 하지만상태를 사용하지 않기 때문에 현재 상태에 대한 추적이 어렵다. 현재 상태를 동기화하거나 조건부 논리를 폼상태로 관리해야 할 땐 어려울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 보안하기 위해서 RHF는 &lt;b&gt;watch&lt;/b&gt; 함수를 이용해서 특정 필드나 모든 폼의 필드를 감시하고 상태 변화를 감지하는 기능을 추가했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Controlled Component(제어 컴포넌트)와 Uncontrolled Component(비제어 컴포넌트)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어 컴포넌트는 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트할 수 있다.&lt;br /&gt;'신뢰 가능한 단일 출처'로 만들어 두 요소를 결합시킨 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 비제어 컴포넌트는 입력 데이터의 상태를 직접 관리하지 않는다. Dom 자체가 데이터를 처리하고 저장하는 역할을 한다. 전통적인 HTML 폼 입력과 유사하며, React에서는 &lt;b&gt;&lt;i&gt;ref&lt;/i&gt;&lt;/b&gt;를 이용해서 필요할 때 입력 요소의 현재 값을 직접 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에 RHF와 Formik을 간단히 비교했을 때&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;RHF(React-Hook-Form)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;종속성 없음&lt;/li&gt;
&lt;li&gt;번들 크기 작음&lt;/li&gt;
&lt;li&gt;리렌더 횟수 적음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Formik&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 라이브러리의 종속성&lt;/li&gt;
&lt;li&gt;번들 크기 큼 (RHF와 비교)&lt;/li&gt;
&lt;li&gt;키 입력 시 상태 변경에 의해 리렌더&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;하지만 난 Formik을 쓴다.&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 프로젝트를 진행 중인 입장에서 RHF과 Formik을 간단하게 비교해 봤다. 정말 간단하게 비교한 내용이고 성능과 렌더링 횟수를 직접 분석한 글도 찾아봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 RHF을 쓰면 간단하게 쓸 수 있다고 생각한 것도 많았다. 하지만 현재 프로젝트를 진행 중이기 때문에 이미 Formik을 쓰고 있었다.&lt;br /&gt;프로젝트를 진행하면서 Form 관련해서 더 최적화를 하고 싶었기 때문에 RHF을 알아보게 되었고, 유효성 검사가 편리하고, 코드의 간결성함에 매력을 느꼈지만, &lt;b&gt;단순히 이러한 이유 때문에 기존의 스택을 대체하는 것은 실질적인 문제가 있었다...&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 사용 중인 기술을 뒤엎어 새로운 라이브러리를 사용한다는 것은 상당한 비용과, 시간이 들고 프로젝트의 기존 코드베이스에 깊이 통합된 라이브러리를 변경하면, 많은 부분에서 수정이 필요할 뿐 아니라 새로운 에러 발생 가능성이 있다고 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 지금부터 작성하는 코드를 RHF으로 변환하는 것은 프로젝트 빌드 크기에 영향을 미칠 수 있다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그렇다면 어떻게 해야 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 바꾸려고 생각했던 가장 큰 이유 중 하나는 Formik을 효과적으로 사용하고 있지 못한다고 판단했다. 처음 프로젝트에 참여할 때 Formik을 처음 쓰게 되었고, 기존 코드에 작성 방식을 따르며 사용하고 있었지만, 기존 코드를 제대로 알고 사용한 것이 아니었고, 문제없이 작동이 되었기에 더 깊게 생각하지 않았던 것이 이 사태를 불러온 것 같다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그래서&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; 공식문서부터&lt;/span&gt; 차근차근 읽으며 사용법을 다시 익히기로 했다&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 기존에 쓰던 useFormik() 대신 사용하기&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// before
const formik = useFormik({
    initialValues: {},    
    onSubmit: (values) =&amp;gt; {},    
    validationSchema: Yup.object({}),
})

return (
    &amp;lt;form&amp;gt;
        &amp;lt;input/&amp;gt;
        &amp;lt;button type=&quot;submit&quot;/&amp;gt;
    &amp;lt;/form&amp;gt;
)


//after

return (
    &amp;lt;Formik initialValues={initialValues} onSubmit={handleSubmit} validationSchema={validationSchema}&amp;gt;
        {(props)=&amp;gt;(
            &amp;lt;Form&amp;gt;
                &amp;lt;Field&amp;gt;&amp;lt;/Field&amp;gt;    
                &amp;lt;button type=&quot;submit&quot;&amp;gt;&amp;lt;/button&amp;gt;
            &amp;lt;/Form&amp;gt;
        )}
    &amp;lt;/Formik&amp;gt;
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useFormik() 대신 Formik 컴포넌트를 이용하기로 했다.&lt;br /&gt;Formik 컴포넌트를 직접 사용함으로써 리렌더링의 최적화를 할 수 있게 되었다.&lt;br /&gt;기존에는 onChange로 인해서 키를 누를 때마다 리렌더링이 일어났지만 Formik, Form, Field 컴포넌트를 이용하면서 리렌더링이 최소화되었다.&lt;br /&gt;&lt;i&gt;(Form을 쓰는 상단에서 리렌더링이 되는 것이 아닌 Field 내부에서 리렌더링이 되는 것으로 추측됨.)&lt;/i&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.  Formik 컴포넌트 안에 props 사용하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Formik 컴포넌트 안에서 쓰는 props의 역할은 폼의 상태 관리와 동작 제어를 할 때 사용된다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;&amp;lt;Formik&amp;gt;
    {(props)=&amp;gt;(
        &amp;lt;Form&amp;gt;
            // formik의 values에 접근 가능
            {props.valuse} 
             //ddsfs//df
             &amp;lt;button disabled={props.isSubmitting}/&amp;gt;
        &amp;lt;/Form&amp;gt;
    )}
&amp;lt;/Formik&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 2개를 사용해 봤는데, values를 이용해서 formik의 값에 접근할 수 있고 isSubmitting을 이용해서 onSubmit 함수가 동작하는 상태를 boolean 타입으로 알 수 있다.&lt;br /&gt;이를 이용해서 버튼 비활성화나 버튼의 UI를 변경할 수 있고, 중복 제출을 막을 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Field 컴포넌트&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Field 컴포넌트는 기본적으로 input과 같다.&lt;/li&gt;
&lt;li&gt;name
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;formik의 values에 대응하는 키값을 넣어서 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;type
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;input의 타입을 바꿀 수 있다 ( input, checkbox...)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;as
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;select&quot; , &quot;textarea&quot;, &quot;input&quot;으로 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;fsharp&quot;&gt;&lt;code&gt;&amp;lt;Field name=&quot;email&quot; type=&quot;&quot; as=&quot;&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 유효성 검사 및 ErrorMessage 컴포넌트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Yup을 이용하여 유효성 검사를 하는 방식을 선택하였고, Yup에 유효성 검사로직이 잘 구현되어 있기 때문에 해당 라이브러리를 추가 설치하여 사용했다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install Yup&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Formik의 props 중 validationSchema를 이용하여 적용하였다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;/*

valuse = {
    name : &quot;&quot;
    emaile :&quot;&quot;
    option : [
        {id:&quot;&quot;,value:&quot;&quot;}
        {id:&quot;&quot;,value:&quot;&quot;}
    ]
    checked : []
}

*/


const validationSchema = Yup.object({
    // string 타입검사
    name: Yup.string().required('필수 입력사항입니다.'),
    //string, email 형식검사
    email: Yup.string().email('이메일 형식이 아닙니다.').required('필수 입력사항입니다.'),
    // 배열의 각 요소에 대한 검사
    option: Yup.array().of(
        Yup.object().shape({
            value: Yup.string().required('필수 입력사항입니다.'),
        }),
    ),
    // 배열 타입의 value에 대한 검사
    checked: Yup.array().min(1, '최소 1개 이상 선택해주세요.'),
})

&amp;lt;Formik validationSchema={validationSchema}&amp;gt;
    // 중략..
    &amp;lt;div&amp;gt;
        email
        &amp;lt;Field name=&quot;email&quot; type=&quot;&quot; as=&quot;text&quot; /&amp;gt;
        &amp;lt;ErrorMessage name=&quot;email&quot; component={'div'} /&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/Formik&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 유효성 검사 후 error에 대해서 지정한 message를 출력해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 &lt;b&gt;component이다.&lt;/b&gt;&lt;br /&gt;간혹 ErrorMessage에서 component에 CSS가 적용이 안된다고 하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;component를 안 쓰게 되면 text로 들어가기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wn5lo/btsH2EtqhuO/6g4hOQD6jQeTKKTsio9HeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wn5lo/btsH2EtqhuO/6g4hOQD6jQeTKKTsio9HeK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;174&quot; data-filename=&quot;Pasted image 20240617170532.png&quot; style=&quot;width: 33.5497%; margin-right: 10px;&quot; data-widthpercent=&quot;33.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wn5lo/btsH2EtqhuO/6g4hOQD6jQeTKKTsio9HeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwn5lo%2FbtsH2EtqhuO%2F6g4hOQD6jQeTKKTsio9HeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZSSOK/btsH1PvARfC/gcRqEVEVbCvcqIujD95Qf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZSSOK/btsH1PvARfC/gcRqEVEVbCvcqIujD95Qf0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;180&quot; data-filename=&quot;Pasted image 20240617170558.png&quot; data-widthpercent=&quot;66.06&quot; style=&quot;width: 65.2875%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZSSOK/btsH1PvARfC/gcRqEVEVbCvcqIujD95Qf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZSSOK%2FbtsH1PvARfC%2FgcRqEVEVbCvcqIujD95Qf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;922&quot; height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(ErrorMessage의 CSS가 적용이 안된다면 component를 확인해 보길...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 compnent를 이용하면 컴포넌트를 이용하여 만들 수 있다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20240617171003.png&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUNZIW/btsH1jcHS8i/btfKSX4WSNKKDmT1vqRKik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUNZIW/btsH1jcHS8i/btfKSX4WSNKKDmT1vqRKik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUNZIW/btsH1jcHS8i/btfKSX4WSNKKDmT1vqRKik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUNZIW%2FbtsH1jcHS8i%2FbtfKSX4WSNKKDmT1vqRKik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;174&quot; data-filename=&quot;Pasted image 20240617171003.png&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 1은 input에 대한 CSS가 전혀 없지만 기존에 만든 Input 컴포넌트를 이용해서 component속성에 넣어주면 위와 같이 적용된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Field, ErrorMessage 컴포넌트에서 모두 사용가능&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;div&amp;gt;
    email
    &amp;lt;Field name=&quot;email&quot; component={Input} /&amp;gt;
    &amp;lt;ErrorMessage name=&quot;email&quot; component={'div'} className=&quot;text-xl text-fm-text-800&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 중복되는 Field 코드 제거&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Form을 만들 때 보통 여러 개의 input과 같이 만들게 될 것이다.&lt;br /&gt;그러면 각 value 마다 Field, ErrorMessage를 만드는 것은 매우 코드가 지저분하게 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;&amp;gt;
    &amp;lt;div&amp;gt;
        name:
        &amp;lt;Field name=&quot;name&quot; /&amp;gt;
        &amp;lt;ErrorMessage name=&quot;name&quot; component={Button} /&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        id:
        &amp;lt;Field name=&quot;id&quot; /&amp;gt;
        &amp;lt;ErrorMessage name=&quot;id&quot; component={Button} /&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        pw:
        &amp;lt;Field name=&quot;pw&quot; /&amp;gt;
        &amp;lt;ErrorMessage name=&quot;pw&quot; component={Button} /&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 조금 더 간단하고, 재사용성을 늘리기 위해서 아래와 같이 묶어서 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;const FormikInput = ({ label, ...props }) =&amp;gt; {    
    const [field, meta] = useField(props.name) // 공식문서에는 props로 되어있는데 TS오류로 props.name을 쓰면 문제 없이 작동했다. (이유를 모르겠네....)

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;label htmlFor={props.name}&amp;gt;{label}&amp;lt;/label&amp;gt;
            &amp;lt;Input {...field} {...props} /&amp;gt;
            {meta.touched &amp;amp;&amp;amp; meta.error ? &amp;lt;Button&amp;gt;{meta.error}&amp;lt;/Button&amp;gt; : null}
        &amp;lt;/div&amp;gt;

    )
}

return (
    // 중략
    &amp;lt;FormikInput label=&quot;name&quot; name=&quot;name&quot; /&amp;gt;
    &amp;lt;FormikInput label=&quot;id&quot; name=&quot;id&quot; /&amp;gt;
    &amp;lt;FormikInput label=&quot;pw&quot; name=&quot;pw&quot; /&amp;gt;

)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6. FieldArray: 값이 배열인 필드 (ex. checkbox)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체크박스에 대한 formik을 사용할 때 보통 value의 타입이 배열일 것이다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;//예를 들면
initalValues = {
    option: [
        { id: 1, title: 'option1', value: '1' },
        { id: 2, title: 'option2', value: '1' },
    ],
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는 를 이용한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;&amp;lt;FieldArray name=&quot;option&quot;&amp;gt;
    {(arrayHelper) =&amp;gt; (
        &amp;lt;&amp;gt;
        {values.option &amp;amp;&amp;amp;
            values.option.length &amp;gt; 0 &amp;amp;&amp;amp;
            values.option.map((option, index) =&amp;gt; (
                    &amp;lt;div key={option.id}&amp;gt;        
                        &amp;lt;div&amp;gt;{option.title}&amp;lt;/div&amp;gt;
                        &amp;lt;Field name={`option.${index}.value`} /&amp;gt;
                        &amp;lt;Button
                            type={'button'}        
                            onClick={() =&amp;gt; arrayHelper.remove(index)}
                            className=&quot;inline&quot;
                        &amp;gt;
                            -
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;ErrorMessage
                            name={`option.${index}.value`}
                            component={'div'}
                            className=&quot;text-xl text-fm-text-800&quot;
                        /&amp;gt;
                    &amp;lt;/div&amp;gt;
                ))}
            // 배열에 추가하는 버튼
            &amp;lt;Button
                type=&quot;button&quot;
                onClick={() =&amp;gt;
                    arrayHelper.push({
                        id: values.option.length + 1,
                        title: `option${values.option.length + 1}`,
                        value: '',
                    })
                }
            &amp;gt;
                +
            &amp;lt;/Button&amp;gt;
        &amp;lt;/&amp;gt;
    )}
&amp;lt;/FieldArray&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 구현한 FieldArray 컴포넌트이다.&lt;br /&gt;arrayHelper를 통해서 option의 배열옵션을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 버튼의 onClick 이벤트를 보면 arrayHelper.remove, arrayHelper.push 등 배열 메서드를 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8BD8C18E-D6E7-4FFF-9FA9-23CEBED2EC71.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfTV4f/btsH0XuntnF/PuPYkUkPnzxkoVxPE8tSEk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfTV4f/btsH0XuntnF/PuPYkUkPnzxkoVxPE8tSEk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfTV4f/btsH0XuntnF/PuPYkUkPnzxkoVxPE8tSEk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bfTV4f/btsH0XuntnF/PuPYkUkPnzxkoVxPE8tSEk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;375&quot; data-filename=&quot;8BD8C18E-D6E7-4FFF-9FA9-23CEBED2EC71.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 프로젝트를 진행했다면 React-Hook-Form을 썼을 것 같지만 지금 내 상황에서는 Formik을 더 잘 쓰는 것이 낫다고 생각했고, 더 잘 쓰기 위해서 공식문서를 살펴봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RHF을 한 번도 써본 적이 없기 때문에 직접적인 비교를 할 수는 없지만, Formik이 나쁜, 못 쓸만한 라이브러리는 아니라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태를 이용한 Form 데이터 관리가 많다면 Formik도 좋은 선택지인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>errormessage</category>
      <category>formik</category>
      <category>formik vs react-hook-form</category>
      <category>formik vs useformik</category>
      <category>rhf</category>
      <category>useformik</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/262</guid>
      <comments>https://baekspace.tistory.com/262#entry262comment</comments>
      <pubDate>Mon, 17 Jun 2024 18:00:11 +0900</pubDate>
    </item>
    <item>
      <title>웹페이지 성능 최적화 - 웹사이트 로딩시간을 6.3초에서 0.6초로 줄이기</title>
      <link>https://baekspace.tistory.com/261</link>
      <description>&lt;h1&gt;목차&lt;/h1&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. 분석 및 해결&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. 결과&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h1&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발에서 사용자 경험은 매우 중요하다.&lt;br /&gt;이를 향상시키기 위해서 'Lighthouse'라는 도구를 활용할 수 있다. 이 도구는 현재 구현 중인 웹사이트를 분석하여 개선이 필요한 부분을 알려주고, 어떤 점을 개선해야 할지 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;사진1.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dq83Z0/btsGMDJRnsQ/nIPUzZmNnDDGh0lpZBBMA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dq83Z0/btsGMDJRnsQ/nIPUzZmNnDDGh0lpZBBMA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dq83Z0/btsGMDJRnsQ/nIPUzZmNnDDGh0lpZBBMA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdq83Z0%2FbtsGMDJRnsQ%2FnIPUzZmNnDDGh0lpZBBMA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1610&quot; height=&quot;484&quot; data-filename=&quot;사진1.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;사진2.png&quot; data-origin-width=&quot;2184&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DNKut/btsGMHyzbkp/mD4XTfMtGcXxFYS0kYTTZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DNKut/btsGMHyzbkp/mD4XTfMtGcXxFYS0kYTTZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DNKut/btsGMHyzbkp/mD4XTfMtGcXxFYS0kYTTZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDNKut%2FbtsGMHyzbkp%2FmD4XTfMtGcXxFYS0kYTTZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2184&quot; height=&quot;816&quot; data-filename=&quot;사진2.png&quot; data-origin-width=&quot;2184&quot; data-origin-height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(성능 참.. 처참하다...)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;결론을 먼저 보여주자면&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;사진3.png&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LPWZC/btsGLFOMxQQ/OvTCEkCxjk4C7yPpr7wdp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LPWZC/btsGLFOMxQQ/OvTCEkCxjk4C7yPpr7wdp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LPWZC/btsGLFOMxQQ/OvTCEkCxjk4C7yPpr7wdp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLPWZC%2FbtsGLFOMxQQ%2FOvTCEkCxjk4C7yPpr7wdp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1444&quot; height=&quot;462&quot; data-filename=&quot;사진3.png&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;사진4.png&quot; data-origin-width=&quot;2150&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QXBwH/btsGM2CknEP/BKGMa7VmnaGdNzUauRJNdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QXBwH/btsGM2CknEP/BKGMa7VmnaGdNzUauRJNdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QXBwH/btsGM2CknEP/BKGMa7VmnaGdNzUauRJNdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQXBwH%2FbtsGM2CknEP%2FBKGMa7VmnaGdNzUauRJNdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2150&quot; height=&quot;824&quot; data-filename=&quot;사진4.png&quot; data-origin-width=&quot;2150&quot; data-origin-height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 숫자로 보면 성능점수가 2배로 증가했다.&lt;br /&gt;다른 것보다 LCP가 6.3초에서 0.6초로 끌어올렸다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;분석 및 해결&lt;/b&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 앞의 문제와 결론을 보면 약간의 어그로성이 있다..^^&lt;br /&gt;누군가는 나랑 비슷한 상황이 있을 것 같아서, 이런 방법을 초기에 생각하지 못해서 도움이 될까 블로그에 작성했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;환경&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Next v13.5.6&lt;/li&gt;
&lt;li&gt;tailwind&lt;/li&gt;
&lt;li&gt;S3 - 이미지 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 문제를 보면 내 웹사이트는 LCP에서 가장 큰 문제가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LCP는 웹페이지 로딩 성능을 측정하는 지표이고, 사용자가 페이지를 방문했을 때 가장 큰 컨텐츠 요소가 화면에 완전히 표시되기까지 걸리는 시간을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 완전히 표시되는 것을 기준으로 하기 때문에 사용자 경험에 직접적인 영향을 미친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해서 가장 먼저 생각했던 방법은 이미지의 크기를 줄이는 것이라 생각했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. 이미지 크기 줄이기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 사이트를 방문했을 때 이미지를 요청해서 다운받는 과정이 있다. 이미지가 크면 그 이미지를 다운로드하고 보여주기까지 느릴 수밖에 없다고 생각했고 이미지 크기를 줄이기로 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1. 업로드하는 S3에 avif, webp 확장자로 올리기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;사진5.png&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sAjak/btsGNz0I39x/LAVo4jSgO0bu9SviqYSoE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sAjak/btsGNz0I39x/LAVo4jSgO0bu9SviqYSoE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sAjak/btsGNz0I39x/LAVo4jSgO0bu9SviqYSoE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsAjak%2FbtsGNz0I39x%2FLAVo4jSgO0bu9SviqYSoE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;724&quot; height=&quot;138&quot; data-filename=&quot;사진5.png&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 png로 올리는 것이 아니라 직접 avif 확장자로 변환해서 업로드하는 방식으로 바꿔봤다.&lt;br /&gt;기본적으로 불러오는 이미지의 크기가 작기 때문에 속도에 개선은 됐지만 좀 미미한 것 같았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-2. Next.js에서 제공하는 최적화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 이미지에서 최적화 기능이 제공된다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 이미지를 avif나 webp 방식으로 변환해서 올리는 과정은 중간에 변환하는 과정이 있어서 불편하다...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 next에서 기본제공하는 것이 있었다..&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;&amp;lt;Image
    // unoptimized // default : false
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;unoptimized&quot; 기능은 기본적으로 꺼져있다. 메인화면의 Visual(banner)를 압축하지 않고 보여주겠다며 작성했었는데.. 너무 안 좋은 선택이었다. ( 가만히 두면 될걸.....ㅋㅋ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;unoptimized = {false}&lt;/b&gt; 돌려놨다. ( 정확히는 해당 속성을 지웠다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 next.config.js를 수정하였다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;//next.config.js

images: {
    formats: ['image/avif', 'image/webp'],
},&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지의 포맷을 avif와 webp로 지정하였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이렇게 했을 때 기존보다 약간의 개선은 있었지만 드라마틱한 변화는 없었다.&lt;/b&gt;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2. Visual 컴포넌트 수정&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분에서 좋은 결과를 얻었는데, 웹 성능을 크게 고려하지 않고 화면을 만들다 보니 신경을 쓰지 못했던 부분이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심은 클라이언트 사이드 -&amp;gt; 서버사이드 컴포넌트로 변경했다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 만든 웹사이트는 반응형을 고려하여 작업을 하다 보니 View port에 따라서 맞는 이미지를 보여주는 식으로 작업을 했다. 그래서 클라이언트 사이드에서 viewport를 체크하고 브레이크 포인트에 따라서 조건부렌더링을 이용해서 작업했는데, 이 부분이 &lt;b&gt;LCP 성능 저하&lt;/b&gt;의 큰 영향이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 서버사이드에서 HTML을 생성해서 브라우저에서 받아 화면이 그려지고 클라이언트 측에서 뷰포트를 측정하고 맞는 이미지를 가져와서 사용자에게 보여주는! 이 과정으로 인해 느렸던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 클라이언트 사이드에서 서버사이드로 변경하였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기존&lt;/h3&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;// 기존

const Visual = () =&amp;gt;{
    const [client, setClient] = useState(false)
    const renderSize = useRecoilValue(SettingState) // 상태로 가지고 있었음..
    useEffect(() =&amp;gt; {
        setClient(true)
    }, [])
    if (client)
        return (
         &amp;lt;div className=&quot;relative h-[260px] w-full justify-center overflow-hidden md:h-[480px] pc:h-[400px]&quot;&amp;gt;
            {renderSize === 'mobile' ? (
                &amp;lt;Image
                    src={s3.path/main_mobile}
                    alt={'banner'}
                    fill
                    sizes=&quot;100%&quot;
                    priority
                /&amp;gt;
            ) : renderSize === 'pc' ? (
                &amp;lt;Image                
                    src={s3.path/main_pc}
                    alt={'banner'}
                    fill
                    sizes=&quot;100%&quot;
                    priority
                /&amp;gt;
            ) : (
                &amp;lt;Image
                    src={s3.path/main_tablet}
                    alt={'banner'}
                    fill
                    sizes=&quot;100%&quot;
                    priority
                /&amp;gt;
            )}
        &amp;lt;/div&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;변경&lt;/h3&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
const Visual = () =&amp;gt;{
    return (
        &amp;lt;div className=&quot;relative h-[260px] w-full justify-center overflow-hidden md:h-[480px] pc:h-[400px]&quot;&amp;gt;
            &amp;lt;Image
                className=&quot;block md:hidden&quot;
                src={s3.path/main_mobile}
                alt={'banner'}
                fill
                sizes=&quot;100%&quot;
                priority
            /&amp;gt;

            &amp;lt;Image
                className=&quot;hidden md:block pc:hidden&quot;
                src={s3.path/main_tablet}
                alt={'banner'}
                fill
                sizes=&quot;100%&quot;
                priority
            /&amp;gt;

            &amp;lt;Image
                className=&quot;hidden pc:block&quot;
                src={s3.path/main_pc}
                alt={'banner'}
                fill
                sizes=&quot;100%&quot;
                priority
            /&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 모든 필요 이미지를 처음에 만들고 CSS의 Display:none/block과 미디어쿼리를 이용해서 사용자에게 보일 요소와 안보일 요소를 지정한다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 처음에 보여준 사진처럼 &lt;b&gt;6.3초에서 0.6초&lt;/b&gt;로 초기 화면을 보여주는데 많은 이득을 챙길 수 있었다. 모든 페이지에 이렇게 해야 한다는 것은 아니지만 적어도 처음 들어오는 메인화면의 Visual 영역은 이렇게 해서 웹사이트에서 이탈을 막을 수 있으면 더 좋을 것 같다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;고려해야 할 점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 이미지 최적화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 진입할 때 이미지를 S3에서 요청해서 가져온다. 이 부분에서 이미지의 크기가 컸다면 아마 시간은 더 걸렸을 것이다. 하지만 위에서 첫 번째 해결방법으로 이미지를 최적화했던 방식을 같이 생각해 보면 기존엔 &lt;b&gt;300KB&lt;/b&gt;의 이미지를 불러왔지만 &lt;b&gt;20KB&lt;/b&gt; 대 3개(각 디바이스)의 이미지로 가져온 것으로 단순 산술로도 충분히 이득인 작업이었다고 생각한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 슬라이드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 이 부분은 구현하기 전이지만 슬라이드 배너일 경우 5장의 슬라이더면 15개의 이미지가 나올 것 같다.. none/block 방식으로 모든 이미지를 미리 요청하고 화면을 보여주는 것이 맞는 방법일지는 확실하지 않지만 나쁘지 않았던 방식이라 생각한다. 다른 웹 몇 군데에서도 이렇게 하는 것 같으니... 일단 더 찾아봐야 할 것 같다.&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>LCP</category>
      <category>Next</category>
      <category>Next/Image</category>
      <category>웹 최적화</category>
      <category>이미지 압축</category>
      <category>최적화</category>
      <category>프론트엔드</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/261</guid>
      <comments>https://baekspace.tistory.com/261#entry261comment</comments>
      <pubDate>Fri, 19 Apr 2024 18:23:38 +0900</pubDate>
    </item>
    <item>
      <title>V8 엔진 메모리 구조 - 힙(Heap)과 스택(Stack)</title>
      <link>https://baekspace.tistory.com/260</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 싱글 스레드이기 때문에 V8은 자바스크립트 하나의 컨텍스트당 한 개의 프로세스를 사용한다. 만약 서비스 워커를 사용한다면 워커 또한 각각 새로운 V8 프로세스를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중인 프로그램은 V8 프로세스에서 할당된 일정량의 메모리로 표현되고 이를 &lt;b&gt;Resident Set이라고&lt;/b&gt; 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;v8 engine.png&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMqHsq/btsFFXb64GO/Kqerq0EfszL6KO0wLoJL7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMqHsq/btsFFXb64GO/Kqerq0EfszL6KO0wLoJL7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMqHsq/btsFFXb64GO/Kqerq0EfszL6KO0wLoJL7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMqHsq%2FbtsFFXb64GO%2FKqerq0EfszL6KO0wLoJL7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-filename=&quot;v8 engine.png&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8 프로세스(Resident Set) 하나는 크게 하나의 힙 메모리와 스택 메모리로 나뉜다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스택(Stack) 메모리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택 메모리는 함수 호출과 실행 컨텍스트, 그리고 원시타입의 변수 값을 저장하는 데 사용된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;실행 컨텍스트와 함수 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 호출 시 생성되는 실행 컨텍스트를 순서대로 저장하는 구조이다. 실행 컨텍스트는 해당 함수의 변수, 매개변수, 반환 주소 및 기타 실행에 필요한 정보를 포함한다.&lt;/li&gt;
&lt;li&gt;함수가 호출될 때마다 새로운 실행 컨텍스가 스택의 맨 위에 push 되고, 함수 실행이 완료되면 pop 되어 제거된다.&lt;/li&gt;
&lt;li&gt;LIFO(Last In, First Out) 원칙을 따른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;원시 타입의 데이터 저장&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스택 메모리는 원시 타입의 데이터를 저장하는 데 사용된다. 원시 값은 변수에 직접 할당되며, 함수의 실행 컨텍스트 내에 위치한다.&lt;/li&gt;
&lt;li&gt;원시 타입의 데이터는 값 자체가 스택에 저장되기 때문에 데이터를 전달할 때는 값의 복사본이 생성되어 전달된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효율적인 메모리 관리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스택 메모리는 자동으로 관리되며, 함수 호출과 반환 과정에서 데이터의 할당과 해제가 빠르게 이루어진다.&lt;/li&gt;
&lt;li&gt;재귀 함수의 과도한 호출로 인해서 &lt;i&gt;스택 오버플로&lt;/i&gt; 가 발생할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;스코프와 변수의 접근성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;스택에 저장된 변수는 해당 변수가 선언된 함수의 스코프 내에서만 접근할 수 있다.&lt;/li&gt;
&lt;li&gt;함수가 종료되면 해당 함수의 실행 컨텍스트와 함께 스택에서 제거되므로, 해당 함수의 로컬변수에는 더 이상 접근할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;힙(Heap) 메모리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙은 동적으로 할당된 객체들이 저장되는 메모리 영역이다. JS에서 객체, 문자열, 클로저 등은 힙에 저장된다. 힙은 구조화되지 않은, 즉 고정되지 않는 메모리 할당에 사용되며, 가비지 컬렉터(GC)에 의해 관리된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;New Space (Young Generation)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;New space는 빈번하게 발생하는 소규모의 가비지 컬렉션을 위해 설계된 영역이다.&lt;/li&gt;
&lt;li&gt;새로 만들어지는 모든 객체를 저장하고 이 객체들은 짧은 생명주기를 가진다.&lt;/li&gt;
&lt;li&gt;주로 Scavenge GC(마이너 GC) 알고리즘을 사용한다.&lt;/li&gt;
&lt;li&gt;Semi-space라 불리는 두 개의 부분으로 나뉜다. (From-sapce와 To-space)&lt;/li&gt;
&lt;li&gt;New Space 영역의 크기는 &lt;code&gt;--min_semi_space_size&lt;/code&gt;와 &lt;code&gt;--max_semi_space_size&lt;/code&gt; V8엔진의 플래그 값을 사용해 조정할 수 있다.&lt;/li&gt;
&lt;li&gt;일부 객체가 &lt;b&gt;여러 번의 가비지 컬렉션을&lt;/b&gt; 살아남는 경우, 더 긴 생명주기를 가질 가능성이 높다고 판단되어 Old Space로 이동된다. (promotion)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Old Space (Old Generation)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;New Space에서 살아남은 객체들이 이동하는 영역이다.&lt;/li&gt;
&lt;li&gt;Mark-Sweep 및 Mark-Compact GC(메이저 GC) 알고리즘을 사용한다. (Incremental Marking 알고리즘도 있다.)&lt;/li&gt;
&lt;li&gt;Old Space 영역의 크기는 &lt;code&gt;--initial_old_space_size&lt;/code&gt;와 &lt;code&gt;--max_old_space_size&lt;/code&gt; V8 엔진의 플래그 값을 사용해 조정할 수 있다.&lt;/li&gt;
&lt;li&gt;Old pointer space, Old data space로 나뉜다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;old pointer space : 살아남은 객체들을 가지며, 이 객체는 다른 객체를 가리키는 포인터를 포함하고 있어, 객체 간의 관계를 나타낸다.&lt;/li&gt;
&lt;li&gt;스칼라 값(원시 데이터)이나 포인터가 아닌 데이터를 저장한다. number, string, boolean 값 등이 포함될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Large Object Space&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적인 가비지 컬렉션 과정에서 처리하기에 너무 큰 객체들은 별도로 관리하기 위한 공간이다. 대형 객체들은 이 공간에 직접 할당되며, 가능한 복사를 피하여 관리된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Code Space&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JIT 컴파일러에 의해 컴파일된 실행 가능한 코드를 저장한다. 이 공간은 코드를 효율적 실행을 위해 최적화되어 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cell Space, Property Cell Space, Map Space
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 영역은 모두 같은 크기의 객체들을 포함하며, 특수한 목적을 가진 메모리 공간들이다. 그래서 저장되는 &lt;b&gt;객체의 종류에 제약을 두어 가비지 컬렉션보다 단순하고 효율적&lt;/b&gt;으로 만든다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cell Space&lt;/b&gt; : Cells를 저장하는 공간으로 Cell은 작은 데이터 조작이나, 단일 포인터를 저장하는 데 사용한다. &lt;b&gt;간단한 데이터 구조나 참조를 위한 공간으로 활용할 수 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Property Cell Space&lt;/b&gt; : Property Cells를 저장하는 공간이다. 객체의 속성에 대한 메타 데이터나 속성의 값 자체를 저장하는 데 사용한다. 객체의 속성정보를 중앙화하여 관리함으로써, &lt;b&gt;속성 접근 시의 성능 최적화에 기여한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Map Space : Map은 V8 엔진에서 객체의 구조를 설명하는 데 사용되는 중요한 메타데이터이다. &lt;b&gt;객체의 타입, 속성의 구조, 프로토타입 체인 정보 등을 담고 있다.&lt;/b&gt; 이를 통해 객체의 속성 접근과 타입 최적화가 이루어진다. &lt;i&gt;객체 지향 프로그래밍에서의 클래스와 유사한 역할을 함&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;출처 : ToastUI 블로그 -&lt;a href=&quot;https://ui.toast.com/weekly-pick/ko_20200228&quot;&gt;https://ui.toast.com/weekly-pick/ko_20200228&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>v8엔진</category>
      <category>메모리</category>
      <category>메모리 구조</category>
      <category>메모리구조</category>
      <category>스택</category>
      <category>힙</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/260</guid>
      <comments>https://baekspace.tistory.com/260#entry260comment</comments>
      <pubDate>Mon, 11 Mar 2024 16:12:41 +0900</pubDate>
    </item>
    <item>
      <title>Mockoon을 활용한 프론트엔드 개발 효율성 향상: 가상서버 및 Proxy 설정</title>
      <link>https://baekspace.tistory.com/259</link>
      <description>&lt;h1&gt;&lt;b&gt;1. Mockoon이란?&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockoon은 로컬 환경에서 REST API 모킹을 설계하고 실행할 수 있는 가장 간편하고 빠른 방법을 제공한다. 또한 시간을 절약할 수 있고, OpenAPI 사양과 호환이 되는 완벽한 도구이다. (출처 : &lt;a href=&quot;https://mockoon.com/&quot;&gt;https://mockoon.com/&lt;/a&gt;)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mockoon을 사용하는 이유&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 발생&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작은 개발을 진행하면서 일반적인 플로우는 아래와 같을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Gu28/btsFqNmkRwm/pnmK8he10gtjbXsGj8WeaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Gu28/btsFqNmkRwm/pnmK8he10gtjbXsGj8WeaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Gu28/btsFqNmkRwm/pnmK8he10gtjbXsGj8WeaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Gu28%2FbtsFqNmkRwm%2FpnmK8he10gtjbXsGj8WeaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;918&quot; height=&quot;182&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2~3번이 바뀔 수 있고, 같이 진행될 수 있지만...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하고 싶은 말은 &lt;b&gt;&lt;code&gt;백엔드가 만들어져야 프론트엔드가 확실하게 만들어진다.&lt;/code&gt;&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 우리 팀은 프론트엔드는 백엔드가 작업을 할 때 퍼블리싱을 하고, json파일을 만들어서 필요한 데이터를 임시로 만들어서 작업을 한 후 백엔드가 완료가 되면 프론트에서 API를 연결하여 실제 백엔드에서 오는 요청에 따라서 처리하는 과정으로 만들고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;1704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ytUYE/btsFm6txyGY/ZH06313kcPfUWOgFHbU1Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ytUYE/btsFm6txyGY/ZH06313kcPfUWOgFHbU1Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ytUYE/btsFm6txyGY/ZH06313kcPfUWOgFHbU1Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FytUYE%2FbtsFm6txyGY%2FZH06313kcPfUWOgFHbU1Uk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1126&quot; height=&quot;1704&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;1704&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 병목 현상이 있었고, 임시 데이터를 실제 API 및 실제 요청 데이터로 바꾸면서 생기는 에러, 딜레이(정적인 데이터가 아니라 요청해서 가져오는 과정이기 때문에...)를 컨트롤할 수 없고 API가 완성된 후 다시 시간을 들여서 고쳐야 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 문제를 파악한 후 들은 생각은 아래와 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제&amp;nbsp;백엔드&amp;nbsp;API와&amp;nbsp;같은&amp;nbsp;결과를&amp;nbsp;줄&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;서버를&amp;nbsp;열어서&amp;nbsp;프론트에서&amp;nbsp;요청과&amp;nbsp;응답으로&amp;nbsp;확실하게&amp;nbsp;화면을&amp;nbsp;만들기&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 한편으로 생각한 것은 &lt;code&gt;당장 프론트도 만들기 벅찬데 무슨 서버까지 관리를 하냐...&lt;/code&gt; 그러면서 Mocking에 대해서 찾아보던 중 &lt;b&gt;&lt;code&gt;Mockoon에&lt;/code&gt;&lt;/b&gt; 대해서 알게 되었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mockoon은 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockoon의 핵심 기능은 처음 설명한 것과 같다. 로컬 환경에서 REST API 모킹을 할 수 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 API가 없지만 만들어서 쓸 수 있다는 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockoon은 로컬 환경에서 REST API 모킹을 설계하고 실행할 수 있는 가장 간편하고 빠른 방법을 제공한다. 또한 시간을 절약할 수 있고, OpenAPI 사양과 호환이 되는 완벽한 도구이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 로컬 환경에서 REST API 모킹을 설계하고 실행할 수 있다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 Mockoon을 처음 보자마자 POSTMAN이 떠올랐다. 그래서 간단한 기능을 쉽게 쓸 수 있을 것이라 생각했다. (앱을 설치해서 사용한다. CLI로도 사용할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀은 프론트랑 백엔드랑 분리되어 있으며 프론트에서 API를 요청하는 곳이 DEV환경 서버에 직접 요청해서 가져온다. 그래서 dev환경 서버에 배포되어 있지 않은 API를 접근할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Mockoon을 이용하면 간단하게 로컬환경에서 서버를 열고, 해당 서버에 엔드포인트를 만들어서 요청을 하고 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_1.png&quot; data-origin-width=&quot;4096&quot; data-origin-height=&quot;2304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqIK4Q/btsFuKh9ovI/9DBJAY3kl7cGoodAGZPDDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqIK4Q/btsFuKh9ovI/9DBJAY3kl7cGoodAGZPDDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqIK4Q/btsFuKh9ovI/9DBJAY3kl7cGoodAGZPDDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqIK4Q%2FbtsFuKh9ovI%2F9DBJAY3kl7cGoodAGZPDDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4096&quot; height=&quot;2304&quot; data-filename=&quot;mockoon_1.png&quot; data-origin-width=&quot;4096&quot; data-origin-height=&quot;2304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;get요청으로 /newTest로 엔드포인트를 지정해서 만든 것이다. 왼쪽에 보면 localhost:5555로 서버가 되어있는 것이 보일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, newTest는 &lt;code&gt;http://localhost:5555/newTest&lt;/code&gt; 로 요청을 보내면 {&quot;test&quot;:true}를 응답으로 받는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 간단하게 로컬환경에서 설정 및 사용할 수 있다. (뒤에서 자세히 설명할 예정)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. OpenAPI 사양과 호환&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_2.png&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rq4xM/btsFmgi185z/hRHQPhVuVyhuthOqKJiFUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rq4xM/btsFmgi185z/hRHQPhVuVyhuthOqKJiFUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rq4xM/btsFmgi185z/hRHQPhVuVyhuthOqKJiFUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frq4xM%2FbtsFmgi185z%2FhRHQPhVuVyhuthOqKJiFUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;390&quot; height=&quot;180&quot; data-filename=&quot;mockoon_2.png&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger에서 캡처한 화면이다.&lt;br /&gt;여기서 OAS3가 핵심인데, OAS는 OpenAPI Specification 3.0으로 RESTful API를 위한 표준 언어에 구애받지 않는 인터페이스 정의이다. 이 사양을 통해 사람과 컴퓨터 모드 소스코드, 추가문서, 네트워크 트래픽 검사 없이도 서비스의 기능을 발견하고 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger는 OpenAPI 사양을 활용한 것이고 이걸 Mockoon에서도 사용할 수 있다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;2. Mockoon-설치&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Mockoon 설치&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1. 설치하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mockoon.com/&quot;&gt;https://mockoon.com/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_5.png&quot; data-origin-width=&quot;1750&quot; data-origin-height=&quot;1224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSWkqt/btsFoDEzE4Q/QPlWigMdE4pww9bKb2D3q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSWkqt/btsFoDEzE4Q/QPlWigMdE4pww9bKb2D3q1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSWkqt/btsFoDEzE4Q/QPlWigMdE4pww9bKb2D3q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSWkqt%2FbtsFoDEzE4Q%2FQPlWigMdE4pww9bKb2D3q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1750&quot; height=&quot;1224&quot; data-filename=&quot;mockoon_5.png&quot; data-origin-width=&quot;1750&quot; data-origin-height=&quot;1224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 홈페이지에서 다운로드를 이용해서 진행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_6.png&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;1124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WFaby/btsFp8c557O/Hsh7KK9GCCaHXraTFm0w2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WFaby/btsFp8c557O/Hsh7KK9GCCaHXraTFm0w2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WFaby/btsFp8c557O/Hsh7KK9GCCaHXraTFm0w2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWFaby%2FbtsFp8c557O%2FHsh7KK9GCCaHXraTFm0w2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3004&quot; height=&quot;1124&quot; data-filename=&quot;mockoon_6.png&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;1124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 OS에 맞는 앱을 설치하거나, CLI 환경을 이용해서 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 앱을 설치해서 사용했고, Docs 기능별로 CLI를 사용하는 방법도 있으니 찾아보면서 하면 어렵지 않을 것이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Mockoon 세팅&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1. 기본 세팅&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_7.png&quot; data-origin-width=&quot;4096&quot; data-origin-height=&quot;2304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/omRdW/btsFqW4yrn4/Kmf83Dt0KskVurQmZUV0m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/omRdW/btsFqW4yrn4/Kmf83Dt0KskVurQmZUV0m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/omRdW/btsFqW4yrn4/Kmf83Dt0KskVurQmZUV0m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FomRdW%2FbtsFqW4yrn4%2FKmf83Dt0KskVurQmZUV0m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4096&quot; height=&quot;2304&quot; data-filename=&quot;mockoon_7.png&quot; data-origin-width=&quot;4096&quot; data-origin-height=&quot;2304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setting 탭에서 5555라고 쓰여있는 곳이 port를 의미하는 것이다.&lt;br /&gt;로컬 환경에서 mocking을 위한 툴이기 때문에 &lt;code&gt;localhost:port&lt;/code&gt;로 진입이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;중요 !! : Router 탭 옆에 재생버튼을 눌러야 서버가 열린다.&amp;nbsp;&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-2. router 설정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_8.png&quot; data-origin-width=&quot;4088&quot; data-origin-height=&quot;1196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5BRtX/btsFmjs9Qj5/eGMovhcWd5kibFOhGpkoT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5BRtX/btsFmjs9Qj5/eGMovhcWd5kibFOhGpkoT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5BRtX/btsFmjs9Qj5/eGMovhcWd5kibFOhGpkoT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5BRtX%2FbtsFmjs9Qj5%2FeGMovhcWd5kibFOhGpkoT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4088&quot; height=&quot;1196&quot; data-filename=&quot;mockoon_8.png&quot; data-origin-width=&quot;4088&quot; data-origin-height=&quot;1196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 router는 +를 이용해서 HTTP route로 설정할 수 있다.&lt;br /&gt;Method를 선택하고, End-Point를 지정한 후 응답 body를 작성해 주면 된다.&lt;br /&gt;원한다면 응답 코드도 바꿀 수 있고, 중간에 Response 1 옆의 + 를 이용하면 여러 시나리오를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;200, 401,404 등등... 그리고 사용해보지 않았지만 응답을 랜덤 하게 주는 것도 가능해서 랜덤 한 응답에 대응하는 것도 미리 구현할 수 있는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 기본적인 &lt;code&gt;http:localhost:5555/newTest&lt;/code&gt; 라우터가 완성됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-3. Header 설정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_9.png&quot; data-origin-width=&quot;4092&quot; data-origin-height=&quot;718&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KQXbu/btsFm4pcnmE/JAUEIUGcyCJd5JFvMfpRGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KQXbu/btsFm4pcnmE/JAUEIUGcyCJd5JFvMfpRGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KQXbu/btsFm4pcnmE/JAUEIUGcyCJd5JFvMfpRGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKQXbu%2FbtsFm4pcnmE%2FJAUEIUGcyCJd5JFvMfpRGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4092&quot; height=&quot;718&quot; data-filename=&quot;mockoon_9.png&quot; data-origin-width=&quot;4092&quot; data-origin-height=&quot;718&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 하면서 CORS error가 발생해서 해결하기 위해 Header에 설정을 추가해 줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Header 탭을 이용해서 모든 요청에 대해서 지정한 Header를 포함해서 작성할 수 있다. 하단에 있는 CORS headers 버튼만으로도 한 번에 다 추가되니 값만 원하는 대로 바꿔서 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-4. Swagger에 만들어진 API를 Mockoon에 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 swagger 주소에 &lt;code&gt;-json&lt;/code&gt;을 붙이면 JSON 형태로 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://example.swagger-json&quot;&gt;https://example.swagger-json&amp;nbsp;&lt;/a&gt; &amp;lt;&amp;lt; 이런 식으로..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_3.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be2wWJ/btsFuMfWDhN/OmePh8ec0yXDOIM5QnEesK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be2wWJ/btsFuMfWDhN/OmePh8ec0yXDOIM5QnEesK/img.png&quot; data-alt=&quot;JSON으로 작성된 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be2wWJ/btsFuMfWDhN/OmePh8ec0yXDOIM5QnEesK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe2wWJ%2FbtsFuMfWDhN%2FOmePh8ec0yXDOIM5QnEesK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1346&quot; height=&quot;317&quot; data-filename=&quot;mockoon_3.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JSON으로 작성된 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용을 .json파일로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_4.png&quot; data-origin-width=&quot;2194&quot; data-origin-height=&quot;1382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcmBbE/btsFsnAOF6i/YMHkq8JiYDUIrYnHhvk7cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcmBbE/btsFsnAOF6i/YMHkq8JiYDUIrYnHhvk7cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcmBbE/btsFsnAOF6i/YMHkq8JiYDUIrYnHhvk7cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcmBbE%2FbtsFsnAOF6i%2FYMHkq8JiYDUIrYnHhvk7cK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2194&quot; height=&quot;1382&quot; data-filename=&quot;mockoon_4.png&quot; data-origin-width=&quot;2194&quot; data-origin-height=&quot;1382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import를 이용해서 Swagger에서 가져온 json을 넣어주면 Mockoon에서 쓸 수 있는 .json 형태로 다시 변환해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mockoon.json(mockoon으로 변환한 JSON)에는 port 등 기본적인 설정이 되어 있기 때문에 git을 이용해서 공유해서 팀원들끼리 공유도 쉽게 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;swagger.json -&amp;gt; mockoon.json으로 변환해서 mockoon.json 파일을 열면 사용 가능하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-5.  Proxy 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockoon을 사용하고자 하는 이유 중 가장 큰 이유를 차지하는 Proxy기능이다. &lt;b&gt;Proxy&lt;/b&gt; 설정을 이용해서 실제 서버와 Mockoon을 동시에 사용하면서 Mockoon이 일종의 Proxy서버의 역할을 하게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-4에서 이미 정의되어 있는 API를 Swagger.json으로 사용하는 방법을 적었지만, &lt;b&gt;&lt;i&gt;나는 쓰지 않고 있다.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내가 쓰는 방법은 Proxy 설정을 활성화하고 TargetURL을 지정한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_0.png&quot; data-origin-width=&quot;1806&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sS3xs/btsFpSOZFPw/d12AyKSGtxmzvrN8ccOAUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sS3xs/btsFpSOZFPw/d12AyKSGtxmzvrN8ccOAUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sS3xs/btsFpSOZFPw/d12AyKSGtxmzvrN8ccOAUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsS3xs%2FbtsFpSOZFPw%2Fd12AyKSGtxmzvrN8ccOAUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1806&quot; height=&quot;462&quot; data-filename=&quot;mockoon_0.png&quot; data-origin-width=&quot;1806&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Proxy탭에서 Target URL을 지정하고 &lt;code&gt;Enable proxy mode&lt;/code&gt;를 체크하여 활성화해 준다.&lt;br /&gt;Proxy 설정이 되어있는지 확인하는 방법은 좌측에 서버 이름 옆에 마크유무에 따라서 활성화상태인지 아닌지 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Proxy 설정을 해서 얻는 가장 큰 장점은 &lt;b&gt;기존에 완성되어 있는 API를 그대로 쓸 수 있다.&lt;/b&gt; 는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockoon을 통해 가상서버를 실행할 때, 지정된 엔드포인트가 없는 경우 요청이 TargetURL로 전송된다.&lt;br /&gt;이 방식을 통해 백엔드 개발이 완료되기 전에 요청경로와 응답데이터를 설정하여 실제 요청 경로로의 요청/응답 처리 로직을 개발할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3MW6s/btsFpS9k66v/TD2NSUx5ri9R2mnGUTwRPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3MW6s/btsFpS9k66v/TD2NSUx5ri9R2mnGUTwRPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3MW6s/btsFpS9k66v/TD2NSUx5ri9R2mnGUTwRPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3MW6s%2FbtsFpS9k66v%2FTD2NSUx5ri9R2mnGUTwRPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;852&quot; height=&quot;742&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실제 개발 중인 페이지에서 DevTools의 네트워크 탭&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_10.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VGY9w/btsFqYnKXG4/Rj6jafe51WJcuBzUu0zia1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VGY9w/btsFqYnKXG4/Rj6jafe51WJcuBzUu0zia1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VGY9w/btsFqYnKXG4/Rj6jafe51WJcuBzUu0zia1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVGY9w%2FbtsFqYnKXG4%2FRj6jafe51WJcuBzUu0zia1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1100&quot; height=&quot;360&quot; data-filename=&quot;mockoon_10.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Mockoon 라우터&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mockoon_11.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;1092&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBe9ev/btsFm5ak7yJ/qOqTaiYZlGkch24wOSNFEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBe9ev/btsFm5ak7yJ/qOqTaiYZlGkch24wOSNFEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBe9ev/btsFm5ak7yJ/qOqTaiYZlGkch24wOSNFEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBe9ev%2FbtsFm5ak7yJ%2FqOqTaiYZlGkch24wOSNFEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1596&quot; height=&quot;1092&quot; data-filename=&quot;mockoon_11.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;1092&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 사진은 localhost:5555에서 요청/응답을 받은 결과물이다. 200으로 성공적으로 응답을 받았다. 하지만 아래 mockoon에 세팅되어 있는 라우터는 &lt;code&gt;/newTest&lt;/code&gt; 1개밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 결과물이 Proxy를 이용한 것이다. &lt;code&gt;/client/~~&lt;/code&gt; 로 요청했지만, Mcokoon에는 엔드포인트가 없기 때문에 지정해 놓은 target URL인 실제 API에 요청을 해서 응답을 전달해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;3. Mockoon 활용 후&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mockoon을 활용한 개발 프로세스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockoon의 여러 특징과 장점을 활용하여 우리 팀의 프론트엔드의 개발 프로세스가 아래와 같은 플로우로 바뀌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;1402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2rLTA/btsFobBpDb6/HoVROysxTVnWz6vvniQH3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2rLTA/btsFobBpDb6/HoVROysxTVnWz6vvniQH3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2rLTA/btsFobBpDb6/HoVROysxTVnWz6vvniQH3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2rLTA%2FbtsFobBpDb6%2FHoVROysxTVnWz6vvniQH3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1306&quot; height=&quot;1402&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;1402&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드가 완성되기 전에 작업을 진행해야 하는 경우나, 가상 서버를 세팅해서 요청/응답 로직이 필요하다면 Mockoon을 활용하는 방법을 생각해 보면 좋을 것 같다.&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/259</guid>
      <comments>https://baekspace.tistory.com/259#entry259comment</comments>
      <pubDate>Sun, 3 Mar 2024 14:42:29 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript - Utility Types</title>
      <link>https://baekspace.tistory.com/258</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. keyof&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface User{
    id:number
    name:string
    age:number
    gender:&quot;M&quot; | &quot;F&quot;
}

type UserKey = keyof User // &quot;id&quot; | &quot;name&quot; | &quot;age&quot; | &quot;gender&quot;

const uk:UserKey = &quot;id&quot;
const uk2:UserKey = &quot;score&quot; // 오류 발생&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keyof를 이용해서 객체 타입의 모든 키를 문자열 또는 숫자 리터럴 유티온 타입으로 추출할 수 있게 해 준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;keyof 예시 1&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 키를 매개변수로 받는 함수를 정의할 때, 해당 객체 타입의 &lt;b&gt;keyof를&lt;/b&gt; 이용하여 타입 안전성을 보장할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface Person {
    name: string
    age: number
}

type PersonKey = keyof Person // 'name' | 'age'

function getProperty&amp;lt;T, K extends keyof T&amp;gt; (obj: T, key: K){
    return obj[key]
}

const person: Person = { name: &quot;Alice&quot;, age:30 }
const personName = getProperty(person, &quot;name&quot;) // 가능 - &quot;Alice&quot;
const personAge = getProperty (person, &quot;age&quot;) // 가능 - 30
const personGender = getProperty (person, &quot;gender&quot;) // Person에 gender가 없기 때문에 오류&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;keyof 예시 2&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체타입을 매핑하여 새로운 타입을 생성할 수 있다&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;type ReadonlyPerson = {
    readonly [P in keyof Person]: Person[P]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keyof는 typescript에서 타입의 안전성을 강화하고 타입 간의 관계를 더 명확하게 표현할 수 있도록 도와준다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Partial&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Partial은 타입을 옵셔널로 바꿔주는 역할을 한다. 기존에 선언되어 있던 interface가 옵셔널이 아니지만 옵셔널로 필요한 상황이 있을 때 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Partial&amp;lt;T&amp;gt;로&lt;/code&gt; 작성할 수 있고 T는 변경할 interface가 들어온다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;interface User {
    id: number
    name: string
    age: number
    gender: &quot;M&quot; | &quot;F&quot;
}

// Partial&amp;lt;User&amp;gt;은 아래와 같은 형태라고 생각하면 된다.

//  interface User {
//        id?: number
//        name?: string
//        age?: number
//        gender?: &quot;M&quot; | &quot;F&quot;
//  }



let admin: Partial&amp;lt;User&amp;gt; = {
    id:1,
    name:&quot;Bob&quot;,
    job : &quot;manager&quot;  // 오류 발생 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User의 interface를 옵셔널로 만든 것이기 때문에 User에 없는 job을 넣게 되면 오류가 발생한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Required&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Partial과 반대로 모두 필수로 바꿔준다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;interface User{
    id: number
    name: stirng
    age?: number
}


let admin: Required&amp;lt;User&amp;gt; = {
    id: 1,
    name: &quot;Bob&quot;,
    age : 30 // age를 안쓰면 오류 발생
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User에서는 age가 옵셔널이었지만 admin 타입을 Required로 지정하였기 때문에 age도 필수로 넣어줘야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Readonly&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 typescript의 class를 공부하면서 사용해 본 readonly랑 역할은 같다.&lt;br /&gt;하지만 &lt;code&gt;Readonly&amp;lt;T&amp;gt;는&lt;/code&gt; 타입을 사용할 때 기존에 있던 타입에 readonly를 할당하는 것과 같다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;interface User{
    id: number
    name: string
    age?: number
}

let admin: Readonly&amp;lt;User&amp;gt; = {
    id: 1,
    name: &quot;Bob&quot;,
}


admin.id=24 // 오류발생&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Readonly를 이용해서 타입을 지정했기 때문에 id를 수정할 수 없게 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. Record&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Record&amp;lt;K,T&amp;gt;&lt;/code&gt;에서 K는 key를 뜻하고, T는 type을 뜻한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;사용 예시 1&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저의 학년별 점수를 입력한다 했을 때&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
interface Score {
    &quot;1&quot;: &quot;A&quot; | &quot;B&quot; | &quot;C&quot; | &quot;D&quot;
    &quot;2&quot;: &quot;A&quot; | &quot;B&quot; | &quot;C&quot; | &quot;D&quot;
    &quot;3&quot;: &quot;A&quot; | &quot;B&quot; | &quot;C&quot; | &quot;D&quot;    
    &quot;4&quot;: &quot;A&quot; | &quot;B&quot; | &quot;C&quot; | &quot;D&quot;
}

const score :Score = {    
    1: &quot;A&quot;,    
    2: &quot;B&quot;,    
    3: &quot;A&quot;,    
    4: &quot;C&quot;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성할 수 있을 것이다. 이런 형태를 Recode를 사용한다면 더 간단하게 작성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
type Grade = &quot;1&quot; | &quot;2&quot; | &quot;3&quot; | &quot;4&quot;
type Score = &quot;A&quot; | &quot;B&quot; | &quot;C&quot; | &quot;D&quot;

const score :Record&amp;lt;Grade,Score&amp;gt;= {
    1: &quot;A&quot;,    
    2: &quot;B&quot;,    
    3: &quot;A&quot;,    
    4: &quot;C&quot;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성할 수 있고, 객체의 key에는 Grade로 지정한 것만, 객체의 value에는 Score로 지정한 것만 사용가능하게 되어 타입을 지정하는데 안전성을 높일 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;사용 예시 2&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;interface User {
    id:number
    name:string
    age:number
}

function isValid(user:User) {
    const result = {
        id: user.id &amp;gt; 0,
        name: user.name !== &quot;&quot;,
        age: user.age &amp;gt;0 
    }
    return result
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 예시 코드가 있을 때 result의 타입을 Recode를 이용해서 지정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;...

function isValid(user: User) {
    const result:Record&amp;lt;keyof User, boolean&amp;gt; = {
        id: user.id &amp;gt; 0,
        name: user.name !== &quot;&quot;,
        age: user.age &amp;gt;0 
    }
    return result
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keyof를 이용해서 User 인터페이스의 key값을 얻을 수 있고 result의 value 값은 boolean이므로 위의 예시와 같이 사용이 가능하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. Pick&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Pick&amp;lt;T, K&amp;gt;&lt;/code&gt;는 타입에서 원하는 key를 선택해서 사용하고 싶을 때 Pick을 사용한다.&lt;br /&gt;이미 정의되어 있는 타입이 여러 개일 때 일부만 가져와서 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;interface User {
    id:number    
    name:string    
    age:number    
    gender : &quot;M&quot; | &quot;F&quot;
}



const admin: Pick&amp;lt;User, &quot;id&quot; | &quot;name&quot;&amp;gt; = {
    id: 1,
    name : &quot;Bob&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. Omit&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Omit&amp;lt;T, K&amp;gt;는&lt;/code&gt; Pick의 반대이다.&lt;br /&gt;Type에서 일부를 선택했던 pick과 달리 Omit은 일부를 제외하고 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
interface User {
    id:number
    name:string
    age:number
    gender : &quot;M&quot; | &quot;F&quot;
}


const admin: Omit&amp;lt;User, &quot;age&quot; | &quot;gender&quot;&amp;gt; = {
    id: 1,
    name : &quot;Bob&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8. Exclude&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Exclude&amp;lt;T1, T2&amp;gt;는&lt;/code&gt; 두 가지 타입을 이용해서 작성을 하는데 T1의 타입에서 T2와 공통된 타입을 지우고 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Omit과 비슷하다고 느낄 수 있다 하지만 Omit 은 객체타입에서 특정 속성을 제외할 때 사용하고, Exclude는 유니언 타입에서 특정 멤버 타입을 제외할 때 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Omit은 객체의 구조를 변경하지만 Exclude는 타입의 구성을 변경한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type T1 = string | number | boolean
type T2 = string | boolean

type ExcludeType = Exclude&amp;lt;T1, T2&amp;gt; // number만 남게 된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;9. NonNullable&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;NonNullable&amp;lt;type&amp;gt;은&lt;/code&gt; 타입에서 null, undefined를 제외한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;type T1 = string | null | undefined | void
type T2 = NonNullable&amp;lt;T1&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>JavaScript/TypeScript</category>
      <category>typescript utility</category>
      <category>utility types</category>
      <category>유틸리티 타입</category>
      <category>타입스크립트</category>
      <category>타입스크립트 유틸리티</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/258</guid>
      <comments>https://baekspace.tistory.com/258#entry258comment</comments>
      <pubDate>Fri, 23 Feb 2024 13:47:01 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript - Generic</title>
      <link>https://baekspace.tistory.com/257</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭을 이용하면 class, function, interface를 다양한 타입으로 재사용할 수 있다.&lt;br /&gt;선언할 때 파라미터만 적어두고 사용할 때 타입을 지정해 주는 방법이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본적인 제네릭 사용 방법&lt;/h2&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function getSize(arr : number[]) :number {
    return arr.length
}

const arr1 = [1,2,3]
getSize(arr1) // 3

const arr2 = [&quot;1&quot;,&quot;2&quot;,&quot;3&quot;]
getSize(arr2) // 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 형태로 만들었지만 매개변수가 number이냐, string이냐 에 따라서 에러가 생기는 부분이 있을 것이다.&lt;br /&gt;이걸 빠르게 해결하기 위해선&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;유니온을 이용한 방법&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;function getSize (arr: number[] | string []) : number{
    return arr.length
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 arr의 타입을 여러 개 지정한다면 가능하다.. (유니온 타입으로 하는 방법 말고 오버로드해서 사용할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 arr가 확실하게 정해진 것이 아니고 boolean 타입이나 object 타입으로 들어올 경우도 있다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const arr3 = [true, true, false]

const arr4 = [{}, {}, {name : &quot;baekspace&quot;}]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 여러 개의 타입이 들어온다면 모든 것을 다 지정해 주는 것보다 제네릭을 이용해서 작성하는 것이 효율적이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;제네릭을 이용한 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 썼던 getSize()를 제네릭을 이용하여 작성하면 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;function getSize&amp;lt;T&amp;gt;(arr:T[]):number{  // 가능
    return arr.length
}

function getSize&amp;lt;Key&amp;gt;(arr:Key[]):number{  // 가능
    return arr.length
}

const arr1 = [1,2,3]
getSize&amp;lt;number&amp;gt;(arr1) // 가능
getSize(arr1) // 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 T는 반드시 T라고 적을 필요는 없다. 많은 곳에서 이렇게 쓰고 있기 때문에 T를 적었지만, A라고 해도 되고 Key라고 해도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 때는 &amp;lt;&amp;gt;를 이용해서 타입을 지정해 주면 된다. (안 쓰더라도 타입스크립트가 유추는 한다.)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;interface에서 제네릭 사용 방법&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;interface Mobile&amp;lt;T&amp;gt; {
    name: string
    price: number
    option: T
}

// object의 타입이 지정되어있다면 그대로 쓸 수 있음.
// &amp;lt;object&amp;gt;  -&amp;gt;  &amp;lt;{color:string; coupon:boolean}&amp;gt;
const m1 :Mobile&amp;lt;object&amp;gt; = {
    name:&quot;s22&quot;,
    price: 1000,
    option :{
        color:&quot;red&quot;,
        coupon:false
    }

}

const m2 :Mobile&amp;lt;string&amp;gt; = {
    name:&quot;s21&quot;,
    price: 900,
    option :&quot;good&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭을 이용해서 하나의 타입만 받지만 여러 형태의 객체를 만들 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;제네릭 응용&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;interface User{
    name:string
    age:number
}

interface Car{
    name:string
    color:string
}

interface Book{
    price:number
}

const user :User = {name:&quot;a&quot;, age:10}
const car:Car = {name:&quot;bmw&quot;, color:&quot;red&quot;}
const book:Book = {price :3000}  

function showName (data) : string{
    return data.name
}


showName(user)
showName(car)
showName(book)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 아무것도 선언하지 않으면 data가 any 타입으로 메서드를 호출하는 곳에서는 문제가 없지만 book를 인자값으로 넣어주는 마지막 메서드를 사용할 때 문제가 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 data에 제네릭으로 타입을 지정한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;function showName&amp;lt;T&amp;gt;(data:T) : string{
    return data.name
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성하고 나면&lt;br /&gt;&lt;code&gt;return data.name&lt;/code&gt; 에 오류가 발생한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Property 'name' does not exist on type 'T'.(2339)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인자값으로 넣어주는 book에는 name이 없기 때문이다. 그래서 name이 있는 인자값만 호출할 수 있도록 T를 더 명확하게 쓰면 된다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;function showName&amp;lt;T extends {name:string}&amp;gt;(data:T) : string{
    return data.name
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 타입을 지정하게 되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어떠한 타입이 들어올 때 그 타입은 name 객체를 확장한 형태이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 것을 알려줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 name이 string 이여야 하고, name이 없는 book의 경우에는 인자 값으로 사용할 수 없기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;showName(book)&lt;/code&gt; 은 사용할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;interface Car{
    name:boolean
    color:string
}

...

showName(car) // 사용 불가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 {name:string} 객체를 확장한 타입이 들어올 것이라고 했지만 Car의 name 타입을 바꾸게 된다면 이것 또한 name이 string이 아니기 때문에 에러를 발생하고, &lt;code&gt;showName(car)&lt;/code&gt; 또한 사용할 수 없다.&lt;/p&gt;</description>
      <category>JavaScript/TypeScript</category>
      <category>generic</category>
      <category>TS</category>
      <category>제네릭</category>
      <category>타입스크립트</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/257</guid>
      <comments>https://baekspace.tistory.com/257#entry257comment</comments>
      <pubDate>Thu, 22 Feb 2024 16:16:12 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript - class (feat.예시코드)</title>
      <link>https://baekspace.tistory.com/256</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;접근 제한자 - public, private, protected&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 제한자를 이용해서 class의 멤버의 접근 범위를 제한할 수 있다.&lt;br /&gt;접근 제한자는 public, private, protected 3가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;public&lt;/b&gt; : 어디서나 접근이 가능하다. public으로 지정하거나 아무것도 지정하지 않으면 기본적으로 public이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;private&lt;/b&gt; : 해당 클래스의 내부에서만 접근이 가능하다. 상속을 하더라도 접근할 수 없다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;protected&lt;/b&gt; : 해당 클래스, 파생된 클래스 내부에서만 접근이 가능하다. (인스턴스에서 접근이 불가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. public&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;
class Car {
    color : string;
    public wheel : number;
    constructor( color : string , wheel:number) {
        this.color = color
        this.wheel = wheel
    }

    start(){
        console.log('start')
    }
}

const bmw = new Car(&quot;red&quot;, 4)
console.log(bmw.color) // 접근 제한자를 지정하지 않아도 기본적으로 public이다.
console.log(bmw.wheel) // 접근 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. private&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;class Car {
    private name : string = &quot;car&quot;
    color : string
    constructor( color : string ) {
        this.color = color
    }

    start(){
        console.log('start')
        console.log(this.name ) // 여기서는 사용 가능하다.
    }
}

class Bmw extends Car {
    constructor(color:string){
        super(color)
    }
    showName (){
        console.log(this.name) // Error : name이 private 이기 때문에 접근할 수 없다.
    }
}


const z4 = new Bmw(&quot;black&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name을 private로 지정할 경우 상속을 받았다 하더라도 사용할 수 없고 private를 지정한 클래스 내부에서만 쓸 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Property 'name' is private and only accessible within class 'Car'.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;private 대신 '#'을 붙여도 된다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;class Car2 {
    #name : string = &quot;car2&quot;
    start(){
        console.log(this.#name)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. protected&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;
class Car {
    protected name : string = &quot;car&quot;
    public name2 : string = &quot;car2&quot;
    color : string

    constructor( color : string ) {
        this.color = color
    }
    start(){
        console.log('start')
    }
}



class Bmw extends Car {
    constructor(color:string){
        super(color)
    }
    showName (){
        console.log(this.name)
    }
}




const z4 = new Bmw(&quot;black&quot;)

console.log(z4.name) // protected 인 name은 오류
console.log(z4.name2) // public 인 name2는 접근가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class 내부에서 사용할 때는 protected와 public이 같은 것 같지만 인스턴스를 생성하게 되면 차이점이 생긴다.&lt;br /&gt;protected를 지정한 name은 클래스 인스턴스에서 접근이 &lt;b&gt;불가능&lt;/b&gt;하고 public인 name2만 클래스 인스턴스에서 접근이 가능하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;readonly, static, abstract&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. readonly&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 제한자는 아니지만 typescript에서 제공하는 키워드인 &lt;b&gt;readonly는&lt;/b&gt; 속성이 선언된 후에 변경할 수 없음을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 속성의 가시성이나 접근성을 제한하지 않고, 속성의 불변성을 선언할 때 사용한다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;class Car {
    readonly make: string
    readonly model: string
    constructor(make: string, model: string) {
        this.make = make
        this.model = model
    }
}

const myCar = new Car(&quot;Toyota&quot;, &quot;Corolla&quot;)
console.log(myCar.make) // Toyota
console.log(myCar.model) // Corolla
myCar.make = &quot;Honda&quot;; // 오류: 'make'는 읽기 전용 속성이므로 할당할 수 없습니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;5. static&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;class Car {
    readonly make: string
    readonly model: string
    static wheels = 4

    constructor(make: string, model: string) {
        this.make = make
        this.model = model
    }

    start(){
        console.log('start')
        console.log(this.wheels) // 오류발생
        console.log(Car.wheels) // 클래스를 직접 참조
    }

    static stop(){
        console.log('stop')
    }
}


const myCar = new Car(&quot;Toyota&quot;, &quot;Corolla&quot;)
console.log(myCar.wheels) // 오류발생
console.log(Car.wheel) // 클래스를 직접 참조&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static 키워드를 붙인 속성이나 메서드는 인스턴스에 속하는 것이 아닌 해당 클래스 자체에 속하게 된다.&lt;br /&gt;그래서 해당 클래스를 이용해서 인스턴스를 생성하지 않고도 접근이 가능하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;6. abstract&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추상 클래스&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;abstract class Car {
    color : string

    constructor( color : string ) {
        this.color = color
    }

    start(){
        console.log('start')
    }

    abstract doSomething():void
}

const myCar = new Car(&quot;red&quot;) // Error: 추상 클래스는 인스턴스로 생성할 수 없다.

class Bmw extends Car {
    constructor(color:string){
        super(color)
    }
    doSomething(){
        console.log('go...!')
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상 클래스는 &lt;b&gt;abstract&lt;/b&gt; 키워드를 클래스 앞에 선언하여 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상 클래스의 가장 큰 특징은 추상 클래스를 이용하여 직접 인스턴스를 만들 수 없다. 추상 클래스는 다른 클래스에 상속해서 사용한다. 또, abstract를 이용해서 추상 메서드를 만들 때는 로직이 없이 작성한 후 상속받은 클래스에서 구체적인 구현을 해줘야 한다.&lt;/p&gt;</description>
      <category>JavaScript/TypeScript</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/256</guid>
      <comments>https://baekspace.tistory.com/256#entry256comment</comments>
      <pubDate>Thu, 22 Feb 2024 11:48:10 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript - function</title>
      <link>https://baekspace.tistory.com/255</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;this&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface User{
    name:string;
}

const Sam:User = {
    name:'Sam'
}

function showName(this:User, age:number, gender:'m'|'f') { // this를 사용하기 위해서 매개변수에 this와 this의 타입을 지정한다.
    console.log(this.name, age, gender)
}

const a = showName.bind(Sam);
a(30, 'm')
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this를 Sam으로 바인딩하고 this를 사용하기 위해서 Sam의 타입과 같은 User로 명시해 준다.&lt;br /&gt;이렇게 하면 this를 이용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 첫 번째 매개변수로 'this' 타입을 지정하는 것은 'this'의 컨텍스트를 명시적으로 선언하는 방법이다. 함수가 호출될 때 'this'가 가리킬 객체의 타입을 지정해 주는 것으로, 실제 함수 호출 시에는 'this' 매개변수를 제공하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;bind에 대해서 공부하기 [[&lt;a href=&quot;https://youtu.be/KfuyXQLFNW4?si=JtEKSdzzneXpFjVL%5D%5D&quot;&gt;https://youtu.be/KfuyXQLFNW4?si=JtEKSdzzneXpFjVL]]&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;overloading&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 함수 이름에 대해 여러 개의 타입 서명을 선언하는 기능을 말한다.&lt;br /&gt;이를 통해서 함수가 다양한 타입의 인수를 받아들일 수 있도록 하며 각각의 인수 타입 조합에 따라 다른 타입의 반환 값을 가질 수 있게 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오버로드 서명 선언 : 함수의 여러 호출 가능한 서명을 선언한다. 이 서명에는 구현을 포함하지 않는다.&lt;/li&gt;
&lt;li&gt;구현 서명 선언 : 실제 함수 구현과 매칭되는 서명. 이 서명은 오버로드 서명보다 더 일반적인 형태를 가지며, 실제 함수 로직을 포함한다.&lt;/li&gt;
&lt;li&gt;함수 구현 : 실제 로직을 구현한다. 구현 내에서는 인수의 타입을 체크하여 다양한 시나리오에 맞게 로직을 분기처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시 1&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;//오버로드 서명
function greet(name:string): string;
function greet(age:number): string;

// 구현 서명 및 함수 구현
function greet(nameOrAge: string | number): string {
    if(typeof nameOrAge === &quot;string&quot;) {
        return `Hello, ${nameOrAge}` // 이름이 들어올 때
    } else {
        return `You are ${nameOrAge} years old` // 나이가 들어올 때 
    }
} 


//사용 시
const greeting1 = greet(&quot;Baekspace&quot;)
const greeting2 = great(30)

console.log(greeting1) // &quot;Hello Baekspace&quot;
console.log(greeting2) // &quot;You are 30 years old&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시 2&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface User{
    name: string;
    age: number;
}

function join(name: string, age: string): string;
function join(name: string, age: number): User;
function join(name: string, age: number | string): User | string {
    if(typeof age === &quot;number&quot;) {
        return {
            name,
            age,
        };
    }else{
        return &quot;나이는 숫자로 입력해주세요.&quot;;
    }
}


const sam: User = join(&quot;Sam&quot;, 30); 
console.log(sam)// {name:&quot;Sam&quot;, age:30} 출력

const jane: string = join(&quot;Jane&quot;, &quot;30&quot;); 
console.log(jane)// &quot;나이는 숫자로 입력해주세요 출력&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오버로드 서명은 구현보다 위에 선언되어야 한다.&lt;/li&gt;
&lt;li&gt;구현 서명은 공개되지 않으며, 외부에서는 볼 수 없다.&lt;/li&gt;
&lt;li&gt;구현은 모든 오버로드 서명을 충족할 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[코딩앙마 : &lt;a href=&quot;https://youtu.be/prfgfj03_VA?si=GkfK6PdhXkkIpDLz&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/prfgfj03_VA?si=GkfK6PdhXkkIpDLz&lt;/a&gt;]&lt;/p&gt;</description>
      <category>JavaScript/TypeScript</category>
      <category>TS</category>
      <category>ts function</category>
      <category>typeScript</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/255</guid>
      <comments>https://baekspace.tistory.com/255#entry255comment</comments>
      <pubDate>Thu, 22 Feb 2024 11:35:16 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript - interface</title>
      <link>https://baekspace.tistory.com/254</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;index signature&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Index Signature은 객체의 속성 이름과 타입이 미리 정의되어 있지 않을 때, 객체에 동적으로 속성을 추가할 수 있는 방법을 제공한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;기본형태&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;interface Obj{
    [key : T] : U
}

// T : 인덱스의 타입 (대부분 string 이나 number)
// U : 값의 타입&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;type Score = &quot;A&quot; | &quot;B&quot; | &quot;C&quot;

interface User {
    name: string
    age: number
    [grade: number]: Score
}


let user: User = {
    name: &quot;xx&quot;,
    age: 30,
    1: &quot;A&quot;,
    2: &quot;B&quot;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index signature를 사용하면 타입 체크 시 유연성이 증가하지만, 해당 객체의 명확한 구조를 판단할 수 없기 때문에 주의해서 사용해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;implements&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스가 특정 인터페이스를 충족시키도록 강제하는 데 사용한다. 인터페이스는 메서드와 속성의 이름 및 타입을 정의할 수 있는 구조적 타이핑 시스템의 일부이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스가 인터페이스를 구현할 때, 그 클래스는 인터페이스에 정의된 모든 속성과 메서드를 구현해야 한다.&lt;br /&gt;이를 통해서 typescript는 타입 안정성을 제공하며, 객체 지향 디자인 원칙을 따르는 코드를 작성하는데 도움이 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;interface Animal {
    speak() : string;
}

class Dog implements Animal{
    speak(){
        return '멍!'
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dog 클래스는 Animal 인터페이스를 구현하기 때문에 speak 메서드가 포함되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Dog 클래스에서 speak 메서드가 없다면 오류가 발생할 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;핵심&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스는 여러 인터페이스를 구현할 수 있으며 각 인터페이스는 쉼표로 구분된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class A implements a,b,c {}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'implements'는 클래스가 인터페이스의 구조적 계약을 준수하도록 강제하여, 타입 안정성과 예측 가능한 코드 구조를 제공한다.&lt;/li&gt;
&lt;li&gt;인터페이스 내의 속성이 선택적(optional) 일 때, 클래스는 그 속성을 반드시 구현할 필요가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;interface Employee {
    id:number;
    name:string;
    email?:string;
}


class PartTimeEmployee implements Employee {
    id:number
    name:string

    constructor(id:number, name:string){
        this.id = id;
        this.name = name;
    }
}

class FullTimeEmployee implements Employee {
    id:number
    name:string
    email:string

    constructor(id:number, name:string, email:string) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PartTimeEmployee와 FullTimeEmployee 모두 Employee 인터페이스를 이용하여 구현했지만, email은 선택적 요소로 필요한 클래스에서만 포함하여 구현할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;extends&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스를 확장할 때 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;interface Car {
    color:string;
    wheel:number;
    start():void;
}

interface Bmw extends Car{
    door:number;
    stop():void;
}

interface Toy {
    type : string
}

const bmw :Bmw = {
    door: 4,
    wheel: 4,
    color: 'blue',
    stop : () =&amp;gt; {
        console.log(&quot;stop!!&quot;)
    },
    start: ()=&amp;gt;{
        console.log('start!!')
    }
}

interface BmwToyCar extends Bmw, Toy {
    price : number
}

const bmwtoycar : BmwToyCar = {
    price: 0,    
    door: 0,    
    stop: ()=&amp;gt;{},    
    color: &quot;&quot;,    
    wheel: 0,    
    start: ()=&amp;gt;{},    
    material: &quot;&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bmw 인터페이스를 이용하게 되면 Bmw 내부에서 쓴 것뿐만 아니라 Car 안에 포함되어 있는 속성도 다 작성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;implements와 마찬가지로 여러 인터페이스를 이용해서 확장할 때 쉼표로 구분한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[코딩앙마 : &lt;a href=&quot;https://youtu.be/OIMPLNICzoc?si=xi9nbAoIzi_76lcg&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/OIMPLNICzoc?si=xi9nbAoIzi_76lcg&lt;/a&gt;]&lt;/p&gt;</description>
      <category>JavaScript/TypeScript</category>
      <category>Interface</category>
      <category>TS</category>
      <category>ts interface</category>
      <category>타입스크립트</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/254</guid>
      <comments>https://baekspace.tistory.com/254#entry254comment</comments>
      <pubDate>Thu, 22 Feb 2024 11:32:25 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript - Closure(클로저)</title>
      <link>https://baekspace.tistory.com/253</link>
      <description>&lt;h1&gt;Closure&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다. 클로저는 자바스크립트에서 매우 중요한 개념으로 함수가 생성될 때의 환경을 기억하게 해 주며, 이를 통해서 여러 패턴, 기법을 구현할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클로저의 정의와 작동원리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클로저는 함수가 선언된 시점의 스코프에 있는 모든 변수에 대한 참조를 유지한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수가 실행될 때마다 해당 스코프에 접근할 수 있음을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;클로저는 함수가 실행되어 외부 스코프에서의 실행이 종료되어도 해당 함수의 스코프 내에 선언된 변수들에 대한 접근을 가능하게 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트의 함수는 외부 스코프에 정의된 모든 변수에 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클로저 예시&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;
function outerFunction (arg) {
    var variableInOuterFunction = arg

    function bar() {
        console.log(variableInOuterFunction)
    }

    bar()
}

outerFunction(&quot;hello closure&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 함수가 외부 범위에서 변수에 액세스 하려는 것을 확인할 수 있다.&lt;br /&gt;외부 함수의 변수는 내부 함수에 의해 닫혔거나 바인딩되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저는 &lt;i&gt;함수가 선언될 때의 주변환경을 기억하는 함수&lt;/i&gt; 라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 함수는 자신이 생성될 때의 환경에 있던 변수들을 기억하여 나중에도 계속 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 패턴 같은 객체를 쉽게 구성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;
function createCounter() {
    let val = 0;
    return {
        increment() { val++ },
        getVal() { return val }
    }
}

let counter = createCounter();
counter.increment();
console.log(counter.getVal()); 
counter.increment();
console.log(counter.getVal()); 
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 패턴은 데이터를 캡슐화하고 공개 인터페이스를 통해 데이터에 접근하는 방법을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'createCounter' 함수는 'val'이라는 변수를 가지고 있으며, 이 변수는 함수가 반환하는 객체의 'increment' 메서드와 'getVal' 메서드를 통해서만 접근이 가능하다. 'createCounter' 함수가 호출될 때마다 새로운 'val' 변수가 생성되고, 이 변수는 각각의 &lt;i&gt;counter&lt;/i&gt; 인스턴스에 대해 고유하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;외부에서 직접 접근할 수 없게 함으로써 데이터를 안전하게 보호한다.&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클로저의 이점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 효율성 : 특정 데이터에 대한 접근을 필요한 함수에만 제한하여 메모리 사용을 최적화할 수 있다.&lt;/li&gt;
&lt;li&gt;코드의 모듈성과 재사용성 증가 : 공통 기능을 가진 함수를 쉽게 생성하고 재사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클로저 주의사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 누수 : 클로저를 부적절하게 사용하게 되면 사용하지 않은 메모리가 해제되지 않아 메모리 누수가 발생한다.&lt;/li&gt;
&lt;li&gt;성능 고려사항 : 불필요한 메모리 사용을 피하기 위해서 적절히 사용해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저의 이점과 주의사항을 보면 &lt;b&gt;메모리 효율성&lt;/b&gt;과 &lt;b&gt;메모리 누수&lt;/b&gt;가 쓰여있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리를 효율적으로 쓰는데 누수가 생긴다..?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저의 사용 방식에 따라 두 가지 특성이 공존할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메모리 효율성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저는 특정 함수에서만 필요한 데이터에 대한 접근을 제한함으로써 메모리를 효율적으로 사용할 수 있게 해 준다. 예를 들어, 클로저를 사용하여 특정 함수에 대한 설정이나 상태를 저장하는 경우, 이 데이터는 해당 함수가 실행될 때만 메모리에 유지되고, 다른 부분의 코드에서는 접근할 수 없기 때문에 메모리를 절약할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;함수가 실행될 때마다 필요한 데이터를 생성하고 불필요할 때는 자동으로 정리하여 메모리 사용을 최소화한다.&lt;/i&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메모리 누수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, 클로저를 부적절하게 사용하면 누수가 발생할 수 있다. 클로저는 외부 함수의 변수에 대한 참조를 유지하기 때문에, 이 변수들은 내부 함수가 살아 있는 한 &lt;b&gt;가비지 컬렉터&lt;/b&gt;에 의해 수집되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 클로저가 큰 데이터 구조를 참조하고 있고, 이 클로저가 오랜 시간 동안 메모리에서 제거되지 않는다면, 필요하지 않은 데이터가 메모리에 계속 남아 있게 되어 메모리 누수가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;효율적인 클로저 사용법&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클로저가 필요한 경우에만 사용하고, 불필요한 참조를 피해야 한다.&lt;/li&gt;
&lt;li&gt;큰 데이터 구조를 참조하는 클로저는 주의해서 사용해야 하며, 가능한 한 생명주기를 짧게 유지해야 한다.&lt;/li&gt;
&lt;li&gt;클로저를 사용한 후에는 클로저가 참조하는 외부 변수를 해제하거나 null로 설정하여 가비지 컬렉터가 수집할 수 있도록 해야 한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>JavaScript</category>
      <category>Closure</category>
      <category>javascript 클로저</category>
      <category>js</category>
      <category>js closure</category>
      <category>클로저</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/253</guid>
      <comments>https://baekspace.tistory.com/253#entry253comment</comments>
      <pubDate>Wed, 21 Feb 2024 14:38:08 +0900</pubDate>
    </item>
    <item>
      <title>브라우저 탭간 통신하기 : BroadCast Channel API ( feat. Next.js)</title>
      <link>https://baekspace.tistory.com/252</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;broadcast_channel.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPDd8R/btsDwDtesgt/InDnvNY1nNnsMtOFFNju5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPDd8R/btsDwDtesgt/InDnvNY1nNnsMtOFFNju5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPDd8R/btsDwDtesgt/InDnvNY1nNnsMtOFFNju5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPDd8R%2FbtsDwDtesgt%2FInDnvNY1nNnsMtOFFNju5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;broadcast_channel.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;브라우저 탭 사이에 데이터 공유하기&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트 작업을 하면서 A태그의 &lt;code&gt;target:_blank&lt;/code&gt;를 이용해서 새 탭을 열어서 작업을 하는 과정이 있었다.&lt;br /&gt;기존에 열고 있던 페이지가 (&lt;i&gt;A&lt;/i&gt;) 새로운 탭이 (B)라고 했을 때, A와 B는 동일한 오리진으로 로컬 스토리지를 공유할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 로컬 스토리지에서 Access Token을 관리하는 로직으로 프로젝트를 만들고 있다.&lt;br /&gt;그래서 처음 진입을 하는 과정에서는 액세스 토큰을 사용할 때 문제가 없었다. 하지만 B에서 작업을 하던 중 Access Token의 만료로 다시 재발급을 받아 로컬 스토리지를 바꿨을 때 A에서는 아직 기존에 있는 Access Token을 들고 있었고 어떠한 작업을 할 때 Access Token이 필요하지만 과거의 Access Token을 가지고 있었기 때문에 에러를 발생하는 문제가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해서 B페이지에서 새로운 token으로 갱신을 할 때 기존에 있는 페이지에서도 바꿔주면 되지 않을까?라는 생각을 했고 그러기 위해서 탭 간 통신을 할 수 있는 과정이 필요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 상황은 내가 겪을 상황이지만 정리하자면&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;내 애플리케이션의 상태를 탭 사이에 공유해야 한다.&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방법&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Local Storage Event
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본적으로 로컬 스토리지가 변하는 것에 대해서 이벤트를 감지할 수 있다고 있다. 만약에 단순히 로컬 스토리지만 관리한다고 했을 때는 이것을 써도 좋을 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;window.postMessage
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;window 객체 안에 있는 postMessage를 이용하는 방법도 있다. 다른 탭이나 윈도우, ifame 간에 메시지를 전달할 수 있다.&lt;/li&gt;
&lt;li&gt;서로 다른 origin에서도 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;service worker&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹의 백그라운드 작업을 처리하는 데 사용되고, 푸시 알림, 네트워크 요청 캐시 등의 기능을 제공&lt;/li&gt;
&lt;li&gt;탭 간의 통신보다는 오프라인 경험, 데이터 캐싱, 백그라운드 동기화에 적합&lt;/li&gt;
&lt;li&gt;serviceworker.js를 만들어 관리해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;BroadCast Channel API&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 Origin을 가진 다양한 브라우저 컨텍스트 사이에서 간단하게 데이터 교환을 할 수 있는 API&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;동일한 Origin 내에서 데이터 공유 및 실시간 커뮤니케이션에 이상적이다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선택&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&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;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 세 가지를 고려해서 &lt;code&gt;broadcast channel API&lt;/code&gt;를 선택하게 되었다.&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;BroadCast Channel API&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BroadCast Channel API는 같은 오리진에서 사용할 수 있다는 점으로 송수신자를 별도로 표기하지 않아도 된다. 마지막까지 봤었던 window.postMessage는 다른 오리진간에도 가능하기 때문에 보안을 위해서 메시지를 보낸 출처를 확인하는 과정이 있었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;postMessage 예시&lt;/h3&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;// 송신자 
const childWindow = window.open('https://example.com/child-page.html'); 

// 메시지 전송 
childWindow.postMessage('데이터', 'https://example.com');



//수신자

// 메시지 수신 리스너 설정 
window.addEventListener('message', (event) =&amp;gt; { 
// 보안을 위해 메시지를 보낸 출처 확인 
    if (event.origin !== 'https://example.com') return;
    console.log('Received message:', event.data); 
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 broadcast channel API는 동일 오리진이기 때문에 다른 검증과정은 없고 인스턴스를 통해서 송수신을 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Broadcast Channel API 예시&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;//송신자

const channel = new BroadcastChannel('채널이름')
channel.postMessage('데이터')

//수신자

const channel = new BroadcastChannel('채널이름'); 
// 메시지 수신 리스너 설정 
channel.onmessage = (event) =&amp;gt; { console.log('Received message:', event.data); };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 간단하게 쓸 수 있었고 다른 여러 창이 열려있는 상황에서도 동기화를 시킬 수 있다고 생각했고, 그래서 BroadCast Channel API를 이용하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BroadCast Channel API&lt;/b&gt;는 브라우저 기반 API이기 때문에 클라이언트 사이드에서만 사용할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떻게 사용했는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 기반 API이므로 클라이언트 사이드에서 써야 한다. 우리의 프로젝트에서 어디에서나 작동하는 client side 컴포넌트는 root에 react-qeury(tanstack-query)를 세팅한 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 react-query를 세팅한 컴포넌트에서 수신을 하면 recoil을 통해서 로컬스토리지를 변경하는 로직을 추가하였다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;//rootwrap.tsx


const channel = new BroadcastChannel('bc')

channel.onmessage = (e) =&amp;gt; {
    if (e.data) {
        setAccessToken(e.data) // recoil
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 우리 프로젝트에서 토큰이 만료되면 토큰이 만료됐다는 에러를 던지고 그걸 브라우저에서 감지하게 되면 재발급을 한 후 새로운 토큰을 이용해서 재요청을 진행한다.&lt;br /&gt;그래서 토큰을 재발급받는 과정 뒤에 postMessage를 사용하였다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const sendMessage = (message: string) =&amp;gt; {
    if (typeof window !== 'undefined') {
        const channel = new BroadcastChannel('bc')        
        channel.postMessage(message)
    }
}


// 토큰 발급 받은 후
sendMessage(accesstoken)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 과정을 통해서 동일한 오리진의 다른 탭에서 토큰을 재발급받더라도 토큰 받을 후 로직에 sendMessage를 통해서 모든 탭의 토큰에 최신화를 시켜줄 수 있는 과정을 만들었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Broadcast Channel API 원리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20240116150352.png&quot; data-origin-width=&quot;1534&quot; data-origin-height=&quot;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XIWms/btsDxKSVk8I/UpOAR0k65ikz0MLlEWjP3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XIWms/btsDxKSVk8I/UpOAR0k65ikz0MLlEWjP3K/img.png&quot; data-alt=&quot;출처 : MDN web Docs - Broadcast Channel API&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XIWms/btsDxKSVk8I/UpOAR0k65ikz0MLlEWjP3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXIWms%2FbtsDxKSVk8I%2FUpOAR0k65ikz0MLlEWjP3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1534&quot; height=&quot;884&quot; data-filename=&quot;Pasted image 20240116150352.png&quot; data-origin-width=&quot;1534&quot; data-origin-height=&quot;884&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : MDN web Docs - Broadcast Channel API&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 BroadcastChannel의 원리는 크게 3단계이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;채널 생성 및 관리&lt;/li&gt;
&lt;li&gt;메시지 큐잉 및 전달&lt;/li&gt;
&lt;li&gt;이벤트 전파&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 채널 생성 및 관리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저는 각각의 &lt;code&gt;BroadcastChannel&lt;/code&gt; 인스턴스에 대한 참조를 관리한다. 주로 브라우저의 메모리 내에서 채널 이름을 키로 해서 관리된다.&lt;/li&gt;
&lt;li&gt;채널을 동일한 이름으로 생성된 모든 BroadcastChannel 인스턴스에 자동으로 연결된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 메시지 큐잉 및 전달&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지가 postMessage()를 통해서 전송되면, 브라우저는 해당 메시지를 내부적인 메세지 큐에 넣는다.&lt;/li&gt;
&lt;li&gt;브라우저는 동일한 채널에 가입된 모든 인스턴스들에게 메시지를 전달한다. &lt;b&gt;비동기적으로 수행되며, 발신 컨텍스트는 수신 컨텍스트의 응답을 기다리지 않는다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 이벤트 전파&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지가 큐에 삽입되면, 브라우저는 해당 채널에 모든 컨텍스트에 대해 이벤트를 발생시킨다.&lt;/li&gt;
&lt;li&gt;각 컨텍스트는 자신의 이벤트 루프 내에서 message 이벤트를 수신하며, 등록된 onMessage 이벤트 핸들러가 메시지를 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Broadcast Channel API 특징&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 동기화와 동시성 제어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저는 각 &lt;code&gt;BroadcastChannel&lt;/code&gt;인스턴스 간의 동기화를 담당한다. 다른 탭이나 윈도우가 동시에 동일한 채널에 접근하더라도 메시지가 올바르게 전달되도록 보장한다.&lt;/li&gt;
&lt;li&gt;메시지는 순차적으로, 전송된 순서대로 처리되며, 이를 통해서 동시성 문제를 방지한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 보안 및 격리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;BroadcastChannel&lt;/code&gt; 은 &lt;b&gt;동일 출처 정책&lt;/b&gt;을 준수한다. 채널이 출처가 다른 탭이나 윈도우 간에 격리되어 있음을 의미한다.&lt;/li&gt;
&lt;li&gt;내부적으로 브라우저는 출처 정보를 사용하여 각 채널 범위를 결정하고, 오직 같은 출처의 컨텍스트만 메시지를 수신할 수 있도록 한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>broadcast</category>
      <category>브라우저</category>
      <category>브라우저 api</category>
      <category>브로드캐스트</category>
      <category>탭</category>
      <category>탭간 통신</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/252</guid>
      <comments>https://baekspace.tistory.com/252#entry252comment</comments>
      <pubDate>Tue, 16 Jan 2024 17:19:37 +0900</pubDate>
    </item>
    <item>
      <title>StyleXjs : StyleX 세팅 및 기초 (feat. NEXT)</title>
      <link>https://baekspace.tistory.com/251</link>
      <description>&lt;h1&gt;목차&lt;/h1&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;1. StyleX 세팅 및 기초&lt;/h4&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;2. 설치&lt;/h4&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;3. 오류&lt;/h4&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;4. styleX의 핵심 기능&lt;/h4&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;5. 핵심 조건&lt;/h4&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 기본적인 사용법&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;6.1 stylex.create&lt;/li&gt;
&lt;li&gt;6.2 Pseudo-classes&lt;/li&gt;
&lt;li&gt;6.3 media queries (반응형)&lt;/li&gt;
&lt;li&gt;6.4 여러 스타일 지정하기&lt;/li&gt;
&lt;li&gt;6.5 stylex.props&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;7. 예시 코드&lt;/h4&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;299&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfiAAD/btsCzSjWC6r/4NXWH8eS9rfQLoa86Kse5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfiAAD/btsCzSjWC6r/4NXWH8eS9rfQLoa86Kse5k/img.png&quot; data-alt=&quot;로고부터 Atomic...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfiAAD/btsCzSjWC6r/4NXWH8eS9rfQLoa86Kse5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfiAAD%2FbtsCzSjWC6r%2F4NXWH8eS9rfQLoa86Kse5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;944&quot; height=&quot;299&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;299&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로고부터 Atomic...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;StyleX 세팅 및 기초&lt;/h1&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;공식문서 : &lt;a href=&quot;https://stylexjs.com/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://stylexjs.com/&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;StyleX&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;&quot; data-og-host=&quot;stylexjs.com&quot; data-og-source-url=&quot;https://stylexjs.com/&quot; data-og-image=&quot;&quot; data-og-url=&quot;https://stylexjs.com/&quot;&gt;&lt;a href=&quot;https://stylexjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stylexjs.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('\'\'');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;StyleX&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stylexjs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;현재 프론트엔드 개발에서 React는 중요한 역할을 하고 있다. React를 개발한 Metark StyleX 라는&amp;nbsp; 새로운 CSS-in-JS 라이브러리를 만들었는데, 이는 React와 잘 호환될 것으로 추측된다. Next.js 또한 React 기반 프레임워크이므로 Next.js와 React를 사용하는 프로젝트에서는 StyleX를 쓰게 될 것이라 생각하고 StyleX의 세팅 및 기초에 대해서 알아봤다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsIV33/btsCyzk2XfK/kbYtFnVXbwBi8wmYYYpgtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsIV33/btsCyzk2XfK/kbYtFnVXbwBi8wmYYYpgtK/img.png&quot; data-alt=&quot;꽤 빠르게 올라갈 것 같은 느낌이다...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsIV33/btsCyzk2XfK/kbYtFnVXbwBi8wmYYYpgtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsIV33%2FbtsCyzk2XfK%2FkbYtFnVXbwBi8wmYYYpgtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;260&quot; height=&quot;155&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;꽤 빠르게 올라갈 것 같은 느낌이다...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;웹 앱 스타일링을 위한 간단하고 사용하기 쉬운 JS 구문 및 컴파일러&lt;br /&gt;인라인스타일과 정적 CSS의 장점을 결합한 방식이다.&lt;br /&gt;스타일을 정의하고 사용하기 위해서는 컴포넌트 내의 로컬 지식만 있으면 되고, 미디어 쿼리와 같은 기능을 유지하면서 특정성 문제를 피할 수 있다.&lt;br /&gt;StyleX는 충돌이 없는 Atomic CSS를 사용하여 최적화된 스타일을 빌드한다.&amp;nbsp;&lt;br /&gt;&lt;s&gt;(요즘 Atomic이 트랜드인건가..?)&lt;/s&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;확장성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Atomic CSS로 CSS 파일 크기를 최소화한다.&lt;/li&gt;
&lt;li&gt;컴포넌트 수가 증가하더라도 CSS 크기가 균등하게 유지된다.&lt;/li&gt;
&lt;li&gt;커지는 코드베이스 내에서도 스타일은 읽기 쉽고 유지보수가 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;각 스타일 규칙을 가장 작은 단위로 분해하여 개별 클래스로 만드는 방식이다.&lt;br /&gt;프로젝트의 컴포넌트 수가 증가하더라도 CSS 파일 크기가 균등하게 유지된다.&lt;br /&gt;결과적으로 이를 통해서 중복을 줄이고 CSS 파일 크기를 최소화할 수 있다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;예측 가능성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요소에 적용된 클래스 이름은 해당 요소에만 직접 스타일을 적용할 수 있다.&lt;/li&gt;
&lt;li&gt;특정성 문제가 없다.&lt;/li&gt;
&lt;li&gt;마지막으로 적용된 스타일이 항상 승리한다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;클래스 이름은 각 요소에만 적용되기 때문에 다른 요소에는 영향을 주지 않으며 이로 인해 CSS의 특정성 문제가 발생하지 않는다.&lt;br /&gt;또한, 마지막에 작성된 스타일이 항상 우선적으로 적용된다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;재조합 (요소를 분해 조합하여 재사용 가능하다)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조건부 스타일 적용&lt;/li&gt;
&lt;li&gt;컴포넌트와 파일 경계를 넘어 임의의 스타일을 병합하고 조합한다.&lt;/li&gt;
&lt;li&gt;컴포넌트 내의 로컬 상수와 표현식을 사용하여 스타일을 DRY하게 유지하거나 성능에 대해 걱정하지 않고 스타일을 반복한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;조건부 스타일을 통해서 상황에 맞는 다양한 스타일링이 가능하고, 스타일을 자유롭게 병합하고 조합할 수 있다. 그리고 여러 컴포넌트에서 공통으로 사용되는 색상이나 글꼴과 같은 상수를 로컬 상수로 선언하여 재사용할 수 있다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;빠름&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;런타임에 스타일 주입이 없다.&lt;/li&gt;
&lt;li&gt;모든 스타일은 컴파일 타임에 정적 CSS 파일에 번들링된다.&lt;/li&gt;
&lt;li&gt;클래스 이름을 병합하는 런타임을 최적화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;런타임에서 스타일을 주입하는 것이 아닌 컴파일 타임에 정적 CSS 파일에 번들링 되기 때문에 빠르다. 또한, 클래스 이름을 병합하는 런타임을 최적화하여 빠르게 렌더링 할 수 있다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;타입 안정성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입 안정성 API&lt;/li&gt;
&lt;li&gt;타입 안정성 스타일&lt;/li&gt;
&lt;li&gt;타입 안정성 테마&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;설치&lt;/h1&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;$ npm install --save @stylexjs/stylex

$ npm install --save-dev @stylexjs/babel-plugin
# next.js
$ npm install --save-dev @stylexjs/nextjs-plugin
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Next에서 사용하기 위해서 .babelrc.js 파일을 생성하고 아래와 같이 작성한다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;module.exports = {
    presets: [&quot;next/babel&quot;],
    plugins: [
        [
            &quot;@stylexjs/babel-plugin&quot;,
            {
                dev: process.env.NODE_ENV === &quot;development&quot;,
                runtimeInjection: false,
                genConditionalClasses: true,
                treeshakeCompensation: true,
                unstable_moduleResolution: {
                    type: &quot;commonJS&quot;,
                    rootDir: __dirname,
                },
            },
        ],
    ],
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 next.config.js 파일에 아래와 같이 작성한다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;/** @type {import('next').NextConfig} */
const styleXPlugin = require(&quot;@stylexjs/nextjs-plugin&quot;)

const nextConfig = {}

module.exports = styleXPlugin({
    rootDir: __dirname,
})(nextConfig)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;dev-runtime 설치&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;컴파일러 및 빌드 프로세서를 이용하지 않고 styleX를 사용하기 위해선 dev-runtime을 설치해야 한다.&lt;/p&gt;
&lt;pre class=&quot;q&quot;&gt;&lt;code&gt;$ npm install --save-dev @stylexjs/dev-runtime&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 설치를 하면 프로덕션에 배포할 준비가 될 때까지 추가 설정 없이 @stylexjs/stylex를 가져와서 사용할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 아직 설정하는 방법을 못 찾았다...&lt;br /&gt;아시는 분 공유 좀...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;ESLint 플러그인&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;StyleX 컴파일러는 스타일의 유효성을 검사하지 않으며 많은 유효하지 않은 스타일을 컴파일할 수 있다. 스타일을 작성할 때 스타일의 유효성을 검사하기 위해서는 ESLint 플러그인을 설치해야 한다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;$ npm install --save-dev @stylexjs/eslint-plugin&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 .eslintrc.js 파일에 아래와 같이 작성한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;module.exports = {
    extends: &quot;next/core-web-vitals&quot;,
    plugins: [&quot;@stylexjs&quot;],
    rules: {
        // The Eslint rule still needs work, but you can
        // enable it to test things out.
        &quot;@stylexjs/valid-styles&quot;: &quot;error&quot;,
        &quot;ft-flow/space-after-type-colon&quot;: 0,
        &quot;ft-flow/no-types-missing-file-annotation&quot;: 0,
        &quot;ft-flow/generic-spacing&quot;: 0,
    },
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;오류&lt;/h1&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Syntax error: &quot;next/font&quot; requires SWC although Babel is being used due to a custom babel config being present.&lt;br /&gt;styleX에서 직접 언급한 내용을 따르면&lt;br /&gt;This is an issue that we cannot fix on our end. StyleX is dependent on a Babel plugin. Using it within NextJS means using Webpack+Babel and not SWC.&lt;br /&gt;next/font only works if you use SWC. Sorry, there's nothing we can do about this.&lt;br /&gt;라고 했다.&lt;br /&gt;마지막줄에 &quot;SWC를 사용하는 경우에만 작동합니다. 죄송하지만 이 문제에 대해서는 저희가 할 수 있는 일이 없습니다.&quot;&lt;br /&gt;라고 하는 것을 보면 StyleX에서 할 수 있는 건 없는 것 같고 이 오류가 발생하지 않게 하기 위해서&lt;br /&gt;next/font를 사용하지 않는 방법으로 가야 할 것 같다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;기존&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import type { Metadata } from &quot;next&quot;
import { Inter } from &quot;next/font/google&quot;
import &quot;./globals.css&quot;

const inter = Inter({ subsets: [&quot;latin&quot;] })

export const metadata: Metadata = {
    title: &quot;Create Next App&quot;,
    description: &quot;Generated by create next app&quot;,
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
    return (
        &amp;lt;html lang=&quot;en&quot;&amp;gt;
            &amp;lt;body className={inter.className}&amp;gt;{children}&amp;lt;/body&amp;gt;
        &amp;lt;/html&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아래와 같이 수정한다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;수정&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import type { Metadata } from &quot;next&quot;
import &quot;./globals.css&quot;

export const metadata: Metadata = {
    title: &quot;Create Next App&quot;,
    description: &quot;Generated by create next app&quot;,
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
    return (
        &amp;lt;html lang=&quot;en&quot;&amp;gt;
            &amp;lt;body className={&quot;&quot;}&amp;gt;{children}&amp;lt;/body&amp;gt;
        &amp;lt;/html&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;font를 사용하지 않고 className을 빈 문자열로 설정한다.&lt;/p&gt;
&lt;h1&gt;styleX의 핵심 기능&lt;/h1&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;styleX의 핵심은 두 가지 기능으로 요약될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stylex.create&lt;/li&gt;
&lt;li&gt;stylex.props&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;스타일을 생성하는 데 사용되는 create 함수와 스타일을 적용하는 데 사용되는 props 함수이다.&lt;/p&gt;
&lt;h1&gt;핵심 조건&lt;/h1&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;StyleX는 사전 컴파일에 의존하므로 모든 스타일을 정적으로 분석할 수 있어야 한다.&lt;br /&gt;즉, 모든 &quot;원시 스타일 객체&quot;는 오직 하나만 포함해야 한다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;가능한 것&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&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;li&gt;'null' or 'undefined'&lt;/li&gt;
&lt;li&gt;상수, 간단한 표현식, 내장 메서드 (예:.toString())&lt;/li&gt;
&lt;li&gt;동적 스타일을 위한 화살표 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;불가능한 것&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수호출 ( StyleX 함수 제외)&lt;/li&gt;
&lt;li&gt;다른 모듈에서 가져온 값 (.stylex.js 파일에서 stylex를 사용하여 만든 CSS 변수 제외)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;기본적인 사용법&lt;/h1&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;stylex.create&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;import * as stylex from &quot;@stylex/stylex&quot;
const styles = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        color: &quot;rgb(60, 60, 60)&quot;,
    },
    highlighted: {
        color: &quot;rebeccapurple&quot;,
    },
})&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;Pseudo-classes&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의사 클래스 (예 : &lt;code&gt;:hover&lt;/code&gt;)는 스타일 객체의 키로 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;const styles = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        color:{
            default: &quot;rgb(60, 60, 60)&quot;,
            &quot;:hover&quot;: &quot;rebeccapurple&quot;,
        },
        }
    },
})&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;원하는 속성에 default를 이용해서 기본값을 지정하고 hover, active, focus 등의 의사 클래스를 키로 스타일을 지정할 수 있다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;media queries (반응형)&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;styleX를 이용한 반응형 스타일링은 아래와 같이 작성한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const styles = stylex.create({
    wrapper: {
        display: &quot;flex&quot;,
        width: {
            default: &quot;200px&quot;,
            &quot;@media (min-width: 600px)&quot;: &quot;400px&quot;,
        },
    },
})&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;조금 더 편하게 쓰기 위해서 범위를 지정하고 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const MEDIA_MOBILE = '@media (min-width: 600px)' as const;
const styles = stylex.create({
    wrapper: {
        display: &quot;flex&quot;,
        width: {
            default: &quot;200px&quot;,
            [MEDIA_MOBILE]: &quot;400px&quot;,
        },
    },
})&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이를 이용해서 활용하는 방법은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;const styles = stylex.create({
    button: {
        backgroundColor: {
            default: &quot;blue&quot;,
            [MEDIA_MOBILE]: &quot;red&quot;,
            &quot;:hover&quot;: {
                default: &quot;skyblue&quot;,
                [MEDIA_MOBILE]: &quot;black&quot;,
            },
        },
    },
})&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;여러 스타일 지정하기&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;const styles = stylex.create({
    // ...지정 스타일
})
const text = stylex.create({
    // ...지정 스타일
})&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;stylex.props&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;지정한 스탕을 적용하기 위해서는 stylex.props를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import * as stylex from &quot;@stylex/stylex&quot;
const styles = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        color: &quot;rgb(60, 60, 60)&quot;,
    },
    highlighted: {
        color: &quot;rebeccapurple&quot;,
    },
})
function MyComponent({ highlighted }) {
    return (
        &amp;lt;div {...stylex.props(styles.wrapper)}&amp;gt;
            &amp;lt;div {...stylex.props(styles.container)}&amp;gt;
                &amp;lt;p {...stylex.props(styles.button, text.base)}&amp;gt;Hello world&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;예시 코드&lt;/h1&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;create-next-app을 이용해서 예시 코드를 작성해 보았다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// layout.tsx
import type { Metadata } from &quot;next&quot;
import &quot;./globals.css&quot;

export const metadata: Metadata = {
    title: &quot;Create Next App&quot;,
    description: &quot;Generated by create next app&quot;,
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
    return (
        &amp;lt;html lang=&quot;en&quot;&amp;gt;
            &amp;lt;body&amp;gt;{children}&amp;lt;/body&amp;gt;
        &amp;lt;/html&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;// page.tsx
import * as stylex from &quot;@stylexjs/stylex&quot;

export default function Home() {
    return (
        &amp;lt;div {...stylex.props(styles.wrapper)}&amp;gt;
            &amp;lt;div {...stylex.props(styles.container)}&amp;gt;
                &amp;lt;p {...stylex.props(styles.button, text.base)}&amp;gt;Hello world&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

const MEDIA_MOBILE = &quot;@media (min-width: 600px)&quot;
const styles = stylex.create({
    wrapper: {
        width: &quot;100vw&quot;,
        height: &quot;100vh&quot;,
    },
    container: {
        display: &quot;flex&quot;,
        width: &quot;100%&quot;,
        height: &quot;100%&quot;,
        flexDirection: &quot;column&quot;,
        alignItems: &quot;center&quot;,
        justifyContent: &quot;center&quot;,
        backgroundColor: &quot;rgb(255, 171, 171)&quot;,
    },
    button: {
        backgroundColor: {
            default: &quot;blue&quot;,
            [MEDIA_MOBILE]: &quot;red&quot;,
            &quot;:hover&quot;: {
                default: &quot;skyblue&quot;,
                [MEDIA_MOBILE]: &quot;black&quot;,
            },
        },
        width: {
            default: &quot;200px&quot;,
            [MEDIA_MOBILE]: &quot;400px&quot;,
        },
        height: &quot;50px&quot;,
        justifyContent: &quot;center&quot;,
        alignItems: &quot;center&quot;,
        display: &quot;flex&quot;,
        cursor: &quot;pointer&quot;,
    },
})
const text = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        cursor: &quot;pointer&quot;,
        color: {
            default: &quot;rgb(255, 255, 255)&quot;,
            &quot;:hover&quot;: &quot;rgb(0, 0, 0)&quot;,
        },
    },
    highlighted: {
        color: &quot;rebeccapurple&quot;,
    },
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;942&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHjqtW/btsCANiqEne/pwB8TuOyORwPKfQc2WXbh0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHjqtW/btsCANiqEne/pwB8TuOyORwPKfQc2WXbh0/img.gif&quot; data-alt=&quot;예시 코드의 결과물&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHjqtW/btsCANiqEne/pwB8TuOyORwPKfQc2WXbh0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bHjqtW/btsCANiqEne/pwB8TuOyORwPKfQc2WXbh0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;628&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;942&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 코드의 결과물&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/251</guid>
      <comments>https://baekspace.tistory.com/251#entry251comment</comments>
      <pubDate>Tue, 26 Dec 2023 00:25:21 +0900</pubDate>
    </item>
    <item>
      <title>231110 - 카카오 로그인 (feat. KOE010)</title>
      <link>https://baekspace.tistory.com/250</link>
      <description>&lt;h1&gt;카카오 로그인 토큰 받기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오 로그인 중 2단계에 해당하는 토큰을 받아오는 과정에서 axios로는 정상적으로 받아오는데 fetch로는 받아오지 못하는 문제가 발생했다.&lt;br /&gt;처음에 fetch를 사용했던 방법은&lt;/p&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;const url = `https://kauth.kakao.com/oauth/token`
const body = {
        grant_type: 'authorization_code',
        client_id: process.env.REST_API as string,
        redirect_uri: process.env.REDIRECT as string,
        code,
    }
const result = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
        },
        body: JSON.stringify(body),
    })
const data = await result.json()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 방법은 아래와 같은 오류(KOE010)를 발생했다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{
  error: 'invalid_client',
  error_description: 'Bad client credentials',
  error_code: 'KOE010'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오 로그인 공식 문서를 보면 해당&amp;nbsp;오류는 &lt;code&gt;클라이언트 시크릿(Client secret) 기능을 사용하는 앱에서 토큰 요청 시 client_secret 값을 누락했거나 잘못된 값을 전달한 경우&lt;/code&gt; 발생한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 나는 클라이언트 시크릿 기능을 활성화하지 않은 상태이기 때문에 이 오류가 발생할 수 없다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;다른 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;axios로 요청을 보내는 방법을 사용했더니 정상적으로 토큰을 받아왔다. 그래서 axios와 fetch의 차이점을 찾아보았다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;차이점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Content-Type과 요청 본문의 형식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오 API는 일반적으로 application/x-www-form-urlencoded 형식의 데이터로 요청을 받는다. &lt;b&gt;axios를 사용할 때&lt;/b&gt;, 요청 본문은 자동으로 application/x-www-form-urlencoded 형식으로 변환됩니다. headers에서 설정한 Content-Type과 일치합니다.&lt;br /&gt;&lt;b&gt;반면, fetch를 사용할 때는&lt;/b&gt; JSON.stringify(body)를 사용하여 요청 본문을 JSON 형식으로 변환합니다. 그러나 headers에서 설정한 Content-Type은 application/x-www-form-urlencoded이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 형식이 맞지 않아 오류가 발생한 것이다. 형식을 맞춰 주기 위해서 fetch를 사용할 때는 URLSearchParams를 사용하여 요청 본문을 application/x-www-form-urlencoded 형식으로 변환해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;어떻게 보면 너무 어이없는 실수를 한 것 같다.&lt;/i&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해결&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;axios 방식&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;const url = `https://kauth.kakao.com/oauth/token`
const body = {
        grant_type: 'authorization_code',
        client_id: process.env.REST_API as string,
        redirect_uri: process.env.REDIRECT as string,
        code,
    }
const {data} = await axios.post(
    url,
    {
        grant_type: 'authorization_code',
        client_id: process.env.REST_API as string,
        redirect_uri: process.env.REDIRECT as string,
        code,
    },
    {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
        },
    },
)&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;fetch 방식&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;const url = `https://kauth.kakao.com/oauth/token`
const fetchBody = new URLSearchParams({
        grant_type: 'authorization_code',
        client_id: process.env.REST_API as string,
        redirect_uri: process.env.REDIRECT as string,
        code,
})

const result = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
        },
        body: fetchBody,
    })
const data = await result.json()
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>axios</category>
      <category>Fetch</category>
      <category>KOE010</category>
      <category>카카오 로그인</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/250</guid>
      <comments>https://baekspace.tistory.com/250#entry250comment</comments>
      <pubDate>Fri, 10 Nov 2023 21:11:28 +0900</pubDate>
    </item>
    <item>
      <title>231109 -  Monorepo를 이용한 Next.js 프로젝트 구성하기 (feat. pnpm)</title>
      <link>https://baekspace.tistory.com/249</link>
      <description>&lt;h1&gt;Monorepo를 이용한 Next.js 프로젝트 구성하기 (feat. pnpm)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모노레포를 위한 pnpm 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 모노레포의 패키지는 pnpm을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pnpm : &lt;a href=&quot;https://pnpm.io/ko/installation&quot;&gt;https://pnpm.io/ko/installation&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pnpm은 npm과 비슷한 패키지 매니저이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하드 링크와 심볼릭 링크를 사용하여 중복된 패키지를 여러 프로젝트에서 재사용한다. ( 디스크 공간 절약 )&lt;/li&gt;
&lt;li&gt;즉, npm은 패키지를 중복해서 설치하지만 pnpm은 패키지를 공유한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pnpm은 패키지를 병렬로 설치하기 때문에 npm보다 빠르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# homebrew로 설치
$ brew install pnpm

# Powershell
$ iwr https://get.pnpm.io/install.ps1 -useb | iex


# npm으로 설치
$ npm install -g pnpm


# 다른 방법도 많이 있다..&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;루트 지정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 진행할 루트 디렉토리를 지정하고 해당 디렉토리에서 pnpm init을 실행한다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ mkdir next-monorepo
$ cd next-monorepo
$ pnpm init
$ pnpm -v # 버전 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json이 생성이 된다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;name&quot;: &quot;monorepotest2&quot;,
    &quot;version&quot;: &quot;1.0.0&quot;,
    &quot;description&quot;: &quot;&quot;,
    &quot;main&quot;: &quot;index.js&quot;, // 필요 없으므로 삭제
    &quot;packageManager&quot;: &quot;pnpm@8.10.2&quot;
    &quot;scripts&quot;: {
        &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;
    },
    &quot;keywords&quot;: [],
    &quot;author&quot;: &quot;&quot;,
    &quot;license&quot;: &quot;ISC&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json 세팅 이후 프로젝트에 맞게 디렉토리 구조를 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;디렉토리 구조 설정&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ mkdir packages
$ mkdir packages/components
$ mkdir packages/api
$ mkdir packages/utils

$ mkdir apps
$ mkdir apps/client
$ mkdir apps/admin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트마다 원하는 디렉토리 구조는 다르겠지만 예시로 위와 같이 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;components 디렉토리는 공통으로 사용할 컴포넌트를 모아놓는다.&lt;br /&gt;api 디렉토리는 api 요청을 모아놓는다.&lt;br /&gt;utils 디렉토리는 공통으로 사용할 유틸리티 함수를 모아놓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 만들어진 디렉토리들을 monorepo로 사용하기 위해서 워크스페이스 설정을 해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;워크스페이스 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 디렉토리에 &lt;code&gt;pnpm-workspace.yaml&lt;/code&gt; 파일을 생성한다.&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;packages:
    - &quot;packages/*&quot;
    - &quot;apps/*&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pnpm-workspace.yaml 파일은 pnpm이 모노레포를 인식할 수 있도록 해주는 파일이다.&lt;br /&gt;위와 같이 설정하면 packages와 apps 디렉토리를 모두 인식한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 디렉토리들 내에 위치한 &lt;b&gt;각각의 package.json 파일들&lt;/b&gt;을 인식하여 의존성을 관리한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;현재까지 디렉토리 구조&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt; next-monorepo
 ┣  apps
 ┃ ┣  admin
 ┃ ┗  client
 ┣  packages
 ┃ ┣  api
 ┃ ┣  components
 ┃ ┗  utils
 ┣  package.json
 ┗  pnpm-workspace.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;각 프로젝트에 Next 설치&lt;/h2&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;# next-monorepo 디렉토리에서

$ cd apps/client
$ pnpx create-next-app@latest .

$ cd apps/admin
$ pnpx create-next-app@latest .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 서비스를 위한 apps 디렉토리 내부에서 next를 설치한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next도 동일하게 쓰는 건데 왜 따로 설치하는지 의문이 들 수 있다.&lt;br /&gt;하지만 각각의 서비스마다 필요한 패키지가 다를 수 있기 때문에 따로 설치한다.&lt;br /&gt;또한 독립적인 배포를 위해서도 따로 설치한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 실행하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 page.tsx 파일에 Hello, Client!! 와 Hello, Admin!! 을 출력하는 코드를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xmKNl/btsz1eRq65z/8OFX80EKLLZcYai6iIHKj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xmKNl/btsz1eRq65z/8OFX80EKLLZcYai6iIHKj0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;137&quot; data-filename=&quot;스크린샷 2023-11-08 오전 11.26.52.png&quot; style=&quot;width: 50.749%; margin-right: 10px;&quot; data-widthpercent=&quot;51.35&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xmKNl/btsz1eRq65z/8OFX80EKLLZcYai6iIHKj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxmKNl%2Fbtsz1eRq65z%2F8OFX80EKLLZcYai6iIHKj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;337&quot; height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKSeGL/btsz1J4w7kt/p573OWOSTKYhFnQzSVMTnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKSeGL/btsz1J4w7kt/p573OWOSTKYhFnQzSVMTnk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;317&quot; data-origin-height=&quot;136&quot; data-filename=&quot;스크린샷 2023-11-08 오전 11.27.03.png&quot; style=&quot;width: 48.0882%;&quot; data-widthpercent=&quot;48.65&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKSeGL/btsz1J4w7kt/p573OWOSTKYhFnQzSVMTnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKSeGL%2Fbtsz1J4w7kt%2Fp573OWOSTKYhFnQzSVMTnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;317&quot; height=&quot;136&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 디렉토리에서 --filter을 이용해서 해당 서비스를 실행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;pnpm --filter [서비스명] [명령어]&lt;/code&gt; 명령어를 작성해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 클라이언트를 실행하기 위해서 루트 디렉토리에서 client 실행
$ pnpm --filter client dev

# 어드민을 실행하기 위해서 루트 디렉토리에서 admin 실행
$ pnpm --filter admin dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 --filter 서비스명을 지정하는 것이 번거롭기 때문에 루트에 있는 package.json의 script를 이용해서 지정하여 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;name&quot;: &quot;monorepotest2&quot;,
    &quot;version&quot;: &quot;1.0.0&quot;,
    &quot;description&quot;: &quot;&quot;,
    &quot;main&quot;: &quot;index.js&quot;,
    &quot;scripts&quot;: {
        &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;,
        &quot;client&quot;: &quot;pnpm --filter client&quot;,
        &quot;admin&quot;: &quot;pnpm --filter admin&quot;
    },
    &quot;keywords&quot;: [],
    &quot;author&quot;: &quot;&quot;,
    &quot;license&quot;: &quot;ISC&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 아래와 같이 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 루트 디렉토리에서
$ pnpm client dev
$ pnpm admin dev&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;현재까지 디렉토리 구조&lt;/h3&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt; next-monorepo
 ┣  apps
 ┃ ┣  admin
 ┃ ┃ ┗  (next 설치됨)
 ┃ ┗  client
 ┃ ┃ ┗  (next 설치됨)
 ┣  node_modules
 ┣  packages
 ┃ ┣  api
 ┃ ┣  components
 ┃ ┗  utils
 ┣  package.json
 ┗  pnpm-workspace.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 프로젝트에 next를 설치했지만 루트 디렉토리에 node_modules가 생성되었다.&lt;br /&gt;이는 pnpm이 모노레포를 인식할 수 있도록 해주는 pnpm-workspace.yaml 파일이 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 각 프로젝트에 공통된 패키지가 있다면 루트에서 설치하여 각 프로젝트는 하나의 패키지로 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h1&gt;공통 패키지&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공통 패키지 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 각 프로젝트에 recoil을 설치하고 싶다면 루트 디렉토리에서 설치하면 된다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;# 루트 디렉토리에서
$ pnpm install recoil&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-08 오전 11.58.04.png&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PsGWi/btsz1rXh6yl/kopwkGVIPAuTAYjTxzfAU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PsGWi/btsz1rXh6yl/kopwkGVIPAuTAYjTxzfAU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PsGWi/btsz1rXh6yl/kopwkGVIPAuTAYjTxzfAU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPsGWi%2Fbtsz1rXh6yl%2FkopwkGVIPAuTAYjTxzfAU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;714&quot; height=&quot;102&quot; data-filename=&quot;스크린샷 2023-11-08 오전 11.58.04.png&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERR_PNPM_ADDING_TO_ROOT&amp;thinsp; Running this command will add the dependency to the workspace root, which might not be what you want - if you really meant it, make it explicit by running this command again with the -w flag (or --workspace-root). If you don't want to see this warning anymore, you may set the ignore-workspace-root-check setting to true.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 해당 패키지가 루트 디렉토리에 설치되었기 때문에 발생하는 에러이다. 프로젝트 내부에서 설치해야 할 것을 잘못 설치하는 상황을 방지하기 위해서 발생하는 에러이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 우리는 루트 디렉토리에 설치하고 싶다. &lt;b&gt;이를 위해서는 &lt;code&gt;-w&lt;/code&gt; 를 추가해 주면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;# 루트 디렉토리에서
$ pnpm install recoil -w&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-08 오후 12.02.08.png&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9Q1Qf/btsz6jiUnWn/xBvItz2Yp5sWmIqdB2iqIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9Q1Qf/btsz6jiUnWn/xBvItz2Yp5sWmIqdB2iqIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9Q1Qf/btsz6jiUnWn/xBvItz2Yp5sWmIqdB2iqIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9Q1Qf%2Fbtsz6jiUnWn%2FxBvItz2Yp5sWmIqdB2iqIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;706&quot; height=&quot;156&quot; data-filename=&quot;스크린샷 2023-11-08 오후 12.02.08.png&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이렇게 문제없이 설치가 된다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공통 패키지 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통으로 사용하는 components, api, utils를 packages 디렉토리를 만들었고&lt;br /&gt;하위에 각 디렉토리를 만들었을 때 각각의 디렉토리마다 package.json을 생성해야 한다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;# /packages 각 디렉토리에서
$ npm init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 디렉토리마다 package.json을 생성하고 해당 필요한 패키지를 설치한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt; packages
 ┣  api
 ┃ ┗  package.json
 ┣  components
 ┃ ┗  package.json
 ┗  utils
 ┃ ┗  package.json&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공통 패키지 작성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;components 디렉토리에 Button 컴포넌트를 만들고 각각의 프로젝트에서 사용하기 위해서 package.json을 지정해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공통으로 사용할 컴포넌트&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// /packages/components/Button.tsx
import React from &quot;react&quot;

interface ButtonProps {
    children: React.ReactNode
}

export const Button = ({ children }: ButtonProps) =&amp;gt; {
    return &amp;lt;button&amp;gt;{children}&amp;lt;/button&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통으로 사용할 컴포넌트를 만든 후 사용할 프로젝트에서 해당 패키지를 설치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 1.5rem; font-style: oblique;&quot;&gt;  Client 측에서 components 패키지를 이용하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 루트 디렉토리에서
$ pnpm --filter client add components --workspace
$ pnpm client add components --workspace
$ pnpm client add utils --workspace
$ pnpm client add api --workspace


# 즉
$ pnpm --filter [서비스명] add [패키지명] --workspace&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pnpm client&lt;/code&gt;으로 쓸 수 있는 이유는 위에서 루트 package.json에 script로 작성해 뒀기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지를 이용하겠다는 명령어를 쓰고 나면 client 디렉토리의 package.json이&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;dependencies&quot;: {
        &quot;components&quot;: &quot;workspace:^&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 추가됐을 것이다. 이는 해당 프로젝트에서 components 패키지를 사용하겠다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 node_modules 디렉토리에 components 디렉토리가 생성되어 있을 것이다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;
 node_modules
 ┗  components
 ┃ ┣  Button.tsx
 ┃ ┗  index.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 1.5rem; font-weight: bold;&quot;&gt;  반드시 공유하고 싶은 디렉토리(패키지)는 package.json이 있어야 한다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴포넌트 내부 디렉토리 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 컴포넌트가 많을 때 아래와 같은 경우로 디렉토리를 설정할 경우도 있다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;
 components
┣  Box
┃ ┗  index.tsx
┣  Icon
┃ ┣  ArrowDown.tsx
┃ ┗  index.ts
┣  boxIcon
┃ ┗  index.tsx
┣  button
┃ ┗  index.tsx
┣  input
┃ ┗  index.tsx
┣  index.ts
┗  package.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지로 내보내는 components 디렉토리 내부에 각각의 컴포넌트를 디렉토리로 만들 경우 index를 이용해서 내보내고 components/index.ts를 만들어 각각의 디렉토리도 내보내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Icon을 예시로 살펴보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ArrowDown.tsx로 만든 컴포넌트를 내보내기 위해서 Icon/index.ts를 만들어야 한다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;// /packages/components/Icon/index.ts
export * from &quot;./ArrowDown&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 components/index.ts를 만들어서 각각의 디렉토리를 내보내야 한다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;// /packages/components/index.ts

//...
export * from &quot;./Box&quot;
export * from &quot;./Icon&quot;
// ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 components 패키지를 설치한 프로젝트에서 아래와 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// (client 프로젝트에서 사용할 곳)/page.tsx

import { Button, Box, Icon } from &quot;components&quot;

//...
return (
    &amp;lt;&amp;gt;
        &amp;lt;Button /&amp;gt;
        &amp;lt;Box /&amp;gt;
        &amp;lt;Icon /&amp;gt;
    &amp;lt;/&amp;gt;
)
//...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-09 오후 4.17.11.png&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FJ8f3/btsz11qjFsJ/Ucja5PBAe7W5Ty1aTuQG0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FJ8f3/btsz11qjFsJ/Ucja5PBAe7W5Ty1aTuQG0k/img.png&quot; data-alt=&quot;모노레포를 이용한 components 패키지 완성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FJ8f3/btsz11qjFsJ/Ucja5PBAe7W5Ty1aTuQG0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFJ8f3%2Fbtsz11qjFsJ%2FUcja5PBAe7W5Ty1aTuQG0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1234&quot; height=&quot;812&quot; data-filename=&quot;스크린샷 2023-11-09 오후 4.17.11.png&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;812&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모노레포를 이용한 components 패키지 완성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-11-09 오후 4.18.40.png&quot; data-origin-width=&quot;445&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u3gRF/btsz5j4NgKb/CeKuaszqFFBk5IAXznUKR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u3gRF/btsz5j4NgKb/CeKuaszqFFBk5IAXznUKR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u3gRF/btsz5j4NgKb/CeKuaszqFFBk5IAXznUKR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu3gRF%2Fbtsz5j4NgKb%2FCeKuaszqFFBk5IAXznUKR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;445&quot; height=&quot;176&quot; data-filename=&quot;스크린샷 2023-11-09 오후 4.18.40.png&quot; data-origin-width=&quot;445&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #777777; text-align: center;&quot;&gt;모노레포를 이용한 components 패키지 완성!!!&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>MonoRepo</category>
      <category>next 모노레포</category>
      <category>모노레포</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/249</guid>
      <comments>https://baekspace.tistory.com/249#entry249comment</comments>
      <pubDate>Thu, 9 Nov 2023 16:23:37 +0900</pubDate>
    </item>
    <item>
      <title>Next 13 공식문서 읽기 (3)</title>
      <link>https://baekspace.tistory.com/248</link>
      <description>&lt;h1&gt;라우팅 기본&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 애플리케이션의 뼈대는 라우팅이다.&lt;br /&gt;라우팅의 기본 개념과 Next.js에서 라우팅 처리하는 방법에 대해서 소개한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;용어&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;tree.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn7dOO/btszU2CNJ5Y/XnN4TIq9apoOdOmiGtLQBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn7dOO/btszU2CNJ5Y/XnN4TIq9apoOdOmiGtLQBK/img.png&quot; data-alt=&quot;next13 tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn7dOO/btszU2CNJ5Y/XnN4TIq9apoOdOmiGtLQBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn7dOO%2FbtszU2CNJ5Y%2FXnN4TIq9apoOdOmiGtLQBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;832&quot; data-filename=&quot;tree.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;832&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;next13 tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트리 (Tree) : 계층 구조를 시각화하기 위한 규칙.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부모 및 자식 구성 요소가 있는 구성 요소 트리&lt;/li&gt;
&lt;li&gt;폴더 구조
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하위 트리 : 트리의 일부, 새 루트(first)에서 시작하여 Leaves(last)에서 끝난다.&lt;/li&gt;
&lt;li&gt;루트 : 트리 또는 하위트리 (&lt;i&gt;예: 루트 레이아웃&lt;/i&gt;)의 첫 번째 노드&lt;/li&gt;
&lt;li&gt;Leaf : URL 경로의 마지막 세그먼트와 같이 하위 트리에 자식이 없는 노드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;url.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yU5lf/btszXFmMmHX/LILJQBz7YKXAv92RvrpziK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yU5lf/btszXFmMmHX/LILJQBz7YKXAv92RvrpziK/img.png&quot; data-alt=&quot;next 13 url&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yU5lf/btszXFmMmHX/LILJQBz7YKXAv92RvrpziK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyU5lf%2FbtszXFmMmHX%2FLILJQBz7YKXAv92RvrpziK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;371&quot; data-filename=&quot;url.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;next 13 url&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URL 세그먼트 : 슬래시로 구분된 URL 경로의 일부&lt;/li&gt;
&lt;li&gt;URL 경로 : 도메인 다음에 오는 URL의 일부 ( 세그먼트로 구성된다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;App Router&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js v13은 React Server Components 기반의 새로운 App router를 선보였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&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 data-ke-size=&quot;size16&quot;&gt;App Router는 App이라는 새로운 디렉터리에서 작동한다. App 디렉터리는 페이지 디렉터리와 함께 작동하여 점진적인 채택을 허용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 동작을 위해 페이지 디렉터리에 다른 경로를 유지하면서 애플리케이션의 일부 경로를 새로운 동작으로 선택할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;i&gt;중요&lt;/i&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App Router는 Page Router보다 우선이다. 디렉터리가 동일한 URL 경로로 사용하지 않아야 하며, 충돌을 방지하기 위해서 Build 과정에서 오류가 발생한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20231106220201.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7cIYo/btszUs9xWj0/mN2oiHDB0NDpgSQMcatko0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7cIYo/btszUs9xWj0/mN2oiHDB0NDpgSQMcatko0/img.png&quot; data-alt=&quot;기본 내부 구성요소&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7cIYo/btszUs9xWj0/mN2oiHDB0NDpgSQMcatko0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7cIYo%2FbtszUs9xWj0%2FmN2oiHDB0NDpgSQMcatko0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;444&quot; data-filename=&quot;Pasted image 20231106220201.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기본 내부 구성요소&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;기본적인 앱 내부 구성요소&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권장 사항:서버 구성 요소를 처음 사용하는 경우 서버 페이지를 확인한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;폴더 및 파일 역할&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 파일 시스템 기반 라우터를 사용한다. ( 파일을 이용하여 라우터를 설정한다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;page.js&lt;/li&gt;
&lt;li&gt;Route 세그먼트에 표시되는 UI를 만들기 위해서 파일을 사용한다.(특수파일 : loading, layout, error... )&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;경로 세그먼트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로의 각 폴더는 Route Segment를 나타낸다. 각 Route Segment는 URL 경로의 해당 세그먼트에 매핑된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20231106221732.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNUv9Y/btszYJa5h8q/CBOn5toS39UF5XkEXtKgkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNUv9Y/btszYJa5h8q/CBOn5toS39UF5XkEXtKgkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNUv9Y/btszYJa5h8q/CBOn5toS39UF5XkEXtKgkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNUv9Y%2FbtszYJa5h8q%2FCBOn5toS39UF5XkEXtKgkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;594&quot; data-filename=&quot;Pasted image 20231106221732.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;중첩 라우팅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩 라우팅은 폴더를 서로 중첩하여 만들 수 있다. 예를 들어 App 디렉터리에서 두 개의 새 폴더를 중첩하여 &lt;code&gt;/dashboard/setting&lt;/code&gt; 경로를 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/dashboard/setting&lt;/code&gt; 는 세 가지의 세그먼트로 구성된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;/&lt;/code&gt; : 루트 세그먼트&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dashboard&lt;/code&gt; : 세그먼트&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setting&lt;/code&gt; : Leaf 세그먼트&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파일 규칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 중첩된 경로에서 특정 동작을 사용하여 UI를 만드는 특수한 파일을 지원한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;layout&lt;/code&gt; : 세그먼트 및 해당 자식에 대한 공유 UI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;page&lt;/code&gt; : 경로의 고유 UI 및 경로를 공개적으로 접근할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loading&lt;/code&gt; : 세그먼트 및 해당 자식에 대한 Loading UI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;not-found&lt;/code&gt; : 세그먼트 및 해당 자식에 대한 Not-Found UI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;error&lt;/code&gt; : 세그먼트 및 해당 자식에 대한 Error UI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;global-error&lt;/code&gt; : 글로벌 Error UI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;route&lt;/code&gt; : Server-side API 엔드포인트&lt;/li&gt;
&lt;li&gt;&lt;code&gt;template&lt;/code&gt; : layout과 비슷하지만 자식요소마다 새로운 인스턴스를 생성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default&lt;/code&gt; : 병렬 Route에 대한 폴백 UI&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구성요소 계층&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터 세그먼트의 특수 파일에 정의된 반응 구성요소는 특정 계층으로 렌더링 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;layout&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;template&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;error.js&lt;/code&gt; : error boundary&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loading.js&lt;/code&gt; : suspense boundary&lt;/li&gt;
&lt;li&gt;&lt;code&gt;not-found.js&lt;/code&gt; : error boundary&lt;/li&gt;
&lt;li&gt;&lt;code&gt;page.js&lt;/code&gt; or 중첩된 &lt;code&gt;layout.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20231107195632.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;641&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTViya/btszUxXgcIg/mZv7Tj3jLgNLunQ5V1dSb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTViya/btszUxXgcIg/mZv7Tj3jLgNLunQ5V1dSb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTViya/btszUxXgcIg/mZv7Tj3jLgNLunQ5V1dSb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTViya%2FbtszUxXgcIg%2FmZv7Tj3jLgNLunQ5V1dSb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;641&quot; data-filename=&quot;Pasted image 20231107195632.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;641&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩 라우터에서 세그먼트의 구성요소는 상위 세그먼트 구성요소 내부에 중첩된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20231107195853.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;863&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPJcx6/btszUbUxRoR/xk3PukxQpA37ifIvDs2hLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPJcx6/btszUbUxRoR/xk3PukxQpA37ifIvDs2hLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPJcx6/btszUbUxRoR/xk3PukxQpA37ifIvDs2hLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPJcx6%2FbtszUbUxRoR%2Fxk3PukxQpA37ifIvDs2hLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;863&quot; data-filename=&quot;Pasted image 20231107195853.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Colocation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특수 파일 외에도 App 디렉터리의 폴더 내에 자신의 파일을 배치할 수 있는 옵션이 있다. (예: 구성요소, 스타일, 테스트 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폴더는 route로 정의되지만 page.js 또는 route.js에서 반환되는 내용만 주소로 지정이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20231107200845.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1011&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uvgA4/btszUriIJh6/GoHs72fk05EXwxAkcsq0k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uvgA4/btszUriIJh6/GoHs72fk05EXwxAkcsq0k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uvgA4/btszUriIJh6/GoHs72fk05EXwxAkcsq0k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuvgA4%2FbtszUriIJh6%2FGoHs72fk05EXwxAkcsq0k1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;1011&quot; data-filename=&quot;Pasted image 20231107200845.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1011&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;/component/button : 라우팅 불가&lt;/li&gt;
&lt;li&gt;/lib/constants : 라우팅 불가&lt;/li&gt;
&lt;li&gt;/dashboard : 라우팅 가능 ( /dashboard/page 아님!)&lt;/li&gt;
&lt;li&gt;/dashboard/nav : 라우팅 불가&lt;/li&gt;
&lt;li&gt;/api : 라우팅 가능 ( /api/route 아님!)&lt;/li&gt;
&lt;li&gt;/api/db : 라우팅 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;고급 라우팅 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 라우터는 고급 라우팅 패턴을 구현하는데 도움이 되는 규칙을 제공한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;병렬 라우트&lt;/b&gt; :독립적으로 탐색할 수 있는 두 개 이상의 페이지를 동일한 페이지에서 동시에 표시할 수 있다. 자체 하위 탐색이 있는 분할 보기 (예: 대시보드)에 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라우팅 가로채기&lt;/b&gt; : 경로를 가로채서 다른 경로의 컨텍스트에 표시할 수 있다. 현재 페이지의 컨텍스트를 유지할 때 사용할 수 있다. (예 : 하나의 작업을 편집하거나 피드에서 사진을 확장하는 동안 모든 작업을 볼 수 있다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 패턴을 통해서 복잡했던 기능을 쉽게 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nextjs.org&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;사진 및 내용 출처 : NEXT 공식문서 (https://nextjs.org)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>FrontEnd/NEXT</category>
      <category>next 13</category>
      <category>next 공식문서</category>
      <category>공식문서 읽기</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/248</guid>
      <comments>https://baekspace.tistory.com/248#entry248comment</comments>
      <pubDate>Tue, 7 Nov 2023 20:45:28 +0900</pubDate>
    </item>
    <item>
      <title>Next 13 공식문서 읽기 (2)</title>
      <link>https://baekspace.tistory.com/247</link>
      <description>&lt;h1&gt;Next.js 프로젝트 구조&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최상위 파일 및 폴더, 구성파일 및 라우팅 규칙에 대한 내용을 설명한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최상위 폴더&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;app&lt;/b&gt; : 앱라우터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;pages&lt;/b&gt; : 페이지 라우터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;public&lt;/b&gt; : 서비스의 정적 자산(데이터)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;src&lt;/b&gt; : 애플리케이션 원본 폴더 (선택사항 )&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최상위 파일&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;next.config.js&lt;/b&gt; : Next.js에 대한 구성 파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;package.json&lt;/b&gt; : 프로젝트 종속성 및 스크립트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;instrumentation.ts&lt;/b&gt; : OpenTelementry / instrumentation file (이 부분에 대한 개념은 아직... 애플리케이션의 모니터링을 위한 것 같다.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;middleware.ts&lt;/b&gt; : Next.js 요청 미들웨어&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.env&lt;/b&gt; : 환경변수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.env.local&lt;/b&gt; : 로컬 환경변수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.env.production&lt;/b&gt; : 배포 환경변수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.env.development&lt;/b&gt; : 개발 환경변수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.eslintrc.json&lt;/b&gt; : ESLint의 구성파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.gitignore&lt;/b&gt; : 무시할 git 파일/폴더 설정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;next-env.d.ts&lt;/b&gt; : Next.js의 스크립트 선언 파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;tsconfig.json&lt;/b&gt; : 타입스크립트 설정 파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;jsconfig.json&lt;/b&gt; : 자바스크립트 설정 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;앱 라우팅 규칙&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 라우팅 파일&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;layout&lt;/b&gt; : 레이아웃 파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;page&lt;/b&gt; : 페이지 파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;loading&lt;/b&gt; : 로딩 UI&lt;/li&gt;
&lt;li&gt;&lt;b&gt;not-found&lt;/b&gt; : Not found UI&lt;/li&gt;
&lt;li&gt;&lt;b&gt;error&lt;/b&gt; : 에러 UI&lt;/li&gt;
&lt;li&gt;&lt;b&gt;global-error&lt;/b&gt; : 전역 오류 UI&lt;/li&gt;
&lt;li&gt;&lt;b&gt;route&lt;/b&gt; : 라우터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;template&lt;/b&gt; : 템플릿 파일(탐색 중인 자식에 대한 새로운 레이아웃)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;default&lt;/b&gt; : 병렬 라우터 fallback 페이지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 중첩 라우팅&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;folder&lt;/b&gt; : route segment&lt;/li&gt;
&lt;li&gt;&lt;b&gt;folder/folder&lt;/b&gt; : 중첩 route segment&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &quot;&lt;a href=&quot;http://localhost:3000/about&amp;quot;%EC%97%90&quot;&gt;http://localhost:3000/about&quot;에&lt;/a&gt; 대한 라우터는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트에서 about 폴더를 만들어서 사용한다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/app
    /about
        page.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 /about에 대한 라우팅을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 &quot;&lt;a href=&quot;http://localhost:3000/about/me&amp;quot;%EC%97%90&quot;&gt;http://localhost:3000/about/me&quot;에&lt;/a&gt; 대한 라우팅은&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/app
    /about
        /me
            page.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방법으로 라우팅을 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 동적 경로 ( Dynamic Routes)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;[folder]&lt;/code&gt;&lt;/b&gt; : 동적 경로 세그먼트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;[...folder]&lt;/code&gt;&lt;/b&gt; : Catch-all 경로 세그먼트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;[[...folder]]&lt;/code&gt;&lt;/b&gt; : Catch-all 경로 세그먼트 (옵션)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 라우트 그룹 및 Private 폴더&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;(folder)&lt;/code&gt;&lt;/b&gt;: 라우팅에 영향을 주지 않고 경로를 그룹화 (group)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;_folder&lt;/code&gt;&lt;/b&gt; : 라우팅에서 폴더 및 모든 자식 세그먼트 숨김 ( private )&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 병렬 및 인터셉트 라우트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;@folder&lt;/b&gt; : 명명된 슬롯&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(.)folder&lt;/b&gt; : 동일 레벨에서 가로채기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(..)folder&lt;/b&gt; : 한 단계 위에서 가로채기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(..)(..)folder&lt;/b&gt; : 두 단계 위에서 가로채기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(...)folder&lt;/b&gt; : 루트에서 가로채기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 메타데이터 파일 규칙&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-1. 앱 아이콘&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;이름&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;확장자&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;favicon&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.ico&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;파비콘 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;icon&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.ico, .jpg, .png, ...&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;앱 아이콘 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;icon&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.js, .ts, .tsx&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;생성된 앱 아이콘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;apple-icon&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.ico, .jpg, .png, ...&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;Apple 앱 아이콘 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;apple-icon&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.js, .ts, .tsx&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;생성된 Apple 앱 아이콘&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-2. 오픈그래프 및 트위터 이미지&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;이름&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;확장자&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;opengraph-image&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.jpg, .png, .gif, .jpeg&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;오픈그래프 이미지 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;opengraph-image&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.js, .ts, .tsx&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;생성된 오픈그래프 이미지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;twitter-image&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.jpg, .png, .gif, .jpeg&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;트위터 이미지 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;twitter-image&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.js, .ts, .tsx&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;생성된 트위터 이미지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-3. SEO&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;이름&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;확장자&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;sitemap&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.xml&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;사이트맵 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;sitemap&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.ts, .js&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;생성된 사이트맵&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;robots&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.txt&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;로봇 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;robots&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;.js, .ts&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;생성된 로봇 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://nextjs.org&quot;&gt;사진 및 내용 출처 : NEXT 공식문서 (&lt;/a&gt;&lt;a href=&quot;https://nextjs.org)&quot;&gt;https://nextjs.org)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>FrontEnd/NEXT</category>
      <category>Next</category>
      <category>next 공식문서</category>
      <category>SEO</category>
      <category>오픈그래프</category>
      <category>파일규칙</category>
      <category>확장자</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/247</guid>
      <comments>https://baekspace.tistory.com/247#entry247comment</comments>
      <pubDate>Sun, 5 Nov 2023 02:16:32 +0900</pubDate>
    </item>
    <item>
      <title>Next 13 공식문서 읽기 (1)</title>
      <link>https://baekspace.tistory.com/246</link>
      <description>&lt;h1&gt;NextJS 공식문서 읽기&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/docs&quot;&gt;https://nextjs.org/docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Next.js란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀스택 웹 애플리케이션을 구축하기 위한 리액트 프레임워크이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 컴포넌트를 사용하여 UI를 구축하고 Next.js를 사용하여 추가적인 기능과 최적화 작업을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서 번들링, 컴파일 등 React에 필요한 툴링을 추상화하여 자동으로 구성한다. &lt;br /&gt;&lt;br /&gt;이를 통해 설정에 시간을 할애하는 대신 애플리케이션 구축을 하는데에 집중할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Next.js의 주요 특징&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 라우팅&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레이아웃, 중첩라우팅, 로딩, 에러 처리 등을 지원하는 서버 컴포넌트 위에 구축된 파일 시스템 기반 라우터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 렌더링&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트 및 서버 컴포넌트를 사용한 클라이언트 및 서버 측 렌더링&lt;/li&gt;
&lt;li&gt;Next.js를 사용하는 서버에서 정적 및 동적 렌더링을 통해서 최적화했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 데이터 패칭&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 컴포넌트에서 async/await 기능을 이용해서 데이터 가져오기를 간소화하고 요청 메모, 데이터 캐싱 및 재검증을 위한 확장된 fetch API를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 스타일링&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CSS 모듈, 테일윈드, CSS-in-JS 등 선호하는 스타일링 방법 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 최적화&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지, 글꼴 및 자바스크립트 최적화를 통해 응용 프로그램의 핵심 웹 바이탈, 사용자 환경을 개선할 수 있다. (최적화해서 성능을 좋게 했다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6. 타입스크립트&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입스크립트에 대한 향상된 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;App router VS Page Router&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에는 App router와 page router라는 두 개의 다룬 라우터가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 라우터는 서버 컴포넌트 및 스트리밍과 같은 리액트 최신 기능을 사용할 수 있는 최신 라우터이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 라우터는 Next.js에서 기존에 사용하던 라우터로 서버가 렌더링 한 React 응용 프로그램을 구축할 수 있고, 이전 Next.js 응용 프로그램에서 계속 지원된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Next.JS 설치하기&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시스템 요구사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node.js v18.17 이상&lt;/li&gt;
&lt;li&gt;window(WSL포함), macOS, Linux 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자동 설치&lt;/h2&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ npx create-next-app@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어를 이용하여 앱을 시작한다. 명령어를 이용하면 자동으로 설정을 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어를 입력하게 되면 프롬프트가 나타나는데&lt;br /&gt;프로젝트 이름, 타입스크립트, ESLint, 테일윈드, &quot;src/&quot; 디렉터리, App Router, alias, configured의 사용여부를 확인하여 프로젝트의 이름으로 디렉터리를 만들고 필요한 종속성을 설치한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;수동 설치는...&lt;/b&gt; 공식문서에서 확인하기..
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/docs/getting-started/installation&quot;&gt;https://nextjs.org/docs/getting-started/installation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;디렉터리 만들기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Next.js는 파일 시스템 라우팅을 사용한다. 즉, 애플리케이션의 경로는 파일(디렉터리) 구조에 따라 결정된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app/ 디렉터리 밑에 layout.tsx와 page.tsx 파일을 추가하면 사용자가 애플리케이션의 루트로 진입할 때 렌더링 된다. (''/'')&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20231103011600.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dacZP1/btszH6xveRr/KJVhlPX5nVcfhF2a0QoGI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dacZP1/btszH6xveRr/KJVhlPX5nVcfhF2a0QoGI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dacZP1/btszH6xveRr/KJVhlPX5nVcfhF2a0QoGI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdacZP1%2FbtszH6xveRr%2FKJVhlPX5nVcfhF2a0QoGI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;363&quot; data-filename=&quot;Pasted image 20231103011600.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// app/layout.tsx

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    &amp;lt;html lang=&quot;en&quot;&amp;gt;
      &amp;lt;body&amp;gt;{children}&amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// app/page.tsx

export default function Page() {
  return &amp;lt;h1&amp;gt; Hello, Next.js! &amp;lt;/h1&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 layout과 page의 초기를 설정할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;public 디렉터리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지, 글꼴 등 정적 자산을 저장할 public 디렉터리를 만들고, public 디렉터리 내부의 파일은 기본 URL(/)에서 시작하는 코드로 참조할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발 서버 실행&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;&lt;b&gt;npm run dev&lt;/b&gt;&quot;를 이용해서 개발 서버를 실행할 수 있다.&lt;/li&gt;
&lt;li&gt;실행 완료 후 'http://localhost:3000'로 애플리케이션을 볼 수 있다.&lt;/li&gt;
&lt;li&gt;layout.tsx를 편집하고 저장하게 되면 브라우저에서 업데이트된 결과를 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://nextjs.org&quot;&gt;사진 및 내용 출처 : NEXT 공식문서 (&lt;/a&gt;&lt;a href=&quot;https://nextjs.org)&quot;&gt;https://nextjs.org)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>FrontEnd/NEXT</category>
      <category>next 13</category>
      <category>next13</category>
      <category>nextJS</category>
      <category>nextjs v13</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/246</guid>
      <comments>https://baekspace.tistory.com/246#entry246comment</comments>
      <pubDate>Fri, 3 Nov 2023 01:46:45 +0900</pubDate>
    </item>
    <item>
      <title>231010 - NextJS 클라이언트에서 S3 업로드</title>
      <link>https://baekspace.tistory.com/245</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;오늘 내가 배운 것&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 문제 발생&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 원인 및 목적&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 문제 발생&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 413 Error 발생&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-10-10 오후 8.36.32.png&quot; data-origin-width=&quot;205&quot; data-origin-height=&quot;42&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/unDVz/btsxtk7HycZ/5oTKOamdhnUTSzb4bhJWu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/unDVz/btsxtk7HycZ/5oTKOamdhnUTSzb4bhJWu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/unDVz/btsxtk7HycZ/5oTKOamdhnUTSzb4bhJWu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FunDVz%2Fbtsxtk7HycZ%2F5oTKOamdhnUTSzb4bhJWu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;102&quot; data-filename=&quot;스크린샷 2023-10-10 오후 8.36.32.png&quot; data-origin-width=&quot;205&quot; data-origin-height=&quot;42&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 원인 및 목적&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js를 이용하고 있는 프로젝트는 클라이언트 측에서 빠르게 업로드를 하기 위해서 S3에 업로드를 하고 URL을 반환받아서 DB에 저장하는 방식을 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 요청할 때 보내는 데이터의 크기가 크기 때문에 413 Error가 발생하였다. 요청 크기에 제한이 있어서 발생할 수 있는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 시도한 방법과 해결한 방법을 적어놨는데, 이 방법이 효율적인지는 알 수 없지만 방법 중에 하나이기 때문에 언젠가 쓸 수 있는 방법이라고 생각한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-1. NextJS pages/api 라우터 이용 ( multer, config ) ( 실패 )&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트는 NextJS를 이용하기 때문에 pages/api 라우터를 이용해서 S3에 업로드하려고 했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업로드할 동영상 파일을 선택한 뒤, formData 형식으로 변환한다.&lt;/li&gt;
&lt;li&gt;변환된 formData를 multer를 이용해 api 라우터를 통해 S3에 업로드한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 과정으로 클라이언트단에서 업로드를 처리하려 했으나 API에서 Request를 받는 과정에서 413 Error가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;413 에러는 Payload Too Large로 요청 데이터의 크기가 너무 커서 발생하는 에러이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 먼저 검색해서 나온 해결 방법은 'experimental' 속성을 이용하여 크기 제한을 늘리는 것이었다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// next.config.js
module.exports = {
    experimental: {
        serverActions: true,
        serverActionsBodySizeLimit: &quot;500mb&quot;,
    },
}

// pages/api - video.js
//  pages/api 라우터 부분은 대략적으로 흐름만 확인..

export const config = {
    api: {
        bodyParser: false,
        responseLimit: false,
    },
}

export const upload = multer({
    storage: multerS3({
        s3: s3 as S3,
        bucket: 'mybucket', // 버킷 이름
        key(req, file, callback) {
            const ext = path.extname(file.originalname)
            const basename = path.basename(file.originalname, ext)
            callback(null, `interview/${basename}_${Date.now()}${ext}`)
        },
    }),
    limits: {
        fileSize: 500 * 1024 * 1024, // 500MB
    },
}).single('file')

export default (req: MulterRequest, res: NextApiResponse) =&amp;gt; {
    if (req.method === 'POST') {
        upload(req as any, res as any, (err) =&amp;gt; {
            if (err) {
                return res.status(400).send(err)
            }
            if (!req.file) {
                return res.status(400).send('No file uploaded')
            }
            return res.status(200).json({ url: req.file.location })
        })
    } else {
        res.status(405).end()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;하지만 실. 패.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 내용을 보면 bodyParser을 비활성화하는 로직이나 multer에서 걸릴만한 fileSize를 늘려주는 방법을 했지만 제대로 동작하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 검색해서 얻은 결과는 pages/api 방식은 요청을 보내는데 제한이 있다는 점이었고, 이 부분에 대해서 제한을 해제하는 방법이 쉽지 않거나 없다는 점이다. (나는 못 찾았다.. 시도한 여러 방법이 다 실패.. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 라우터에서 업로드를 하는 것이 아닌 바로 클라이언트에서 S3로 직접 업로드하는 방식을 택했다.&lt;br /&gt;서버의 요청 크기제한 문제를 피할 수 있고, 서버의 부하를 줄일 수 있다고 생각했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-2. Client 측에서 미리 서명된 S3 URL을 이용하여 업로드하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 라우터를 이용해서 S3에 업로드를 할 위치 및 서명된 url을 생성한다. (이때, AWS의 키나 정보들이 서버 사이드로 동작하기 때문에 보안 측면에서 조금 이점이 있다고 생각한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서명된 URL은 S3에 업로드할 파일의 위치와 파일명을 포함하고 있으며, 이 URL을 이용하여 클라이언트에서 업로드를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;AWS S3와 같은 스토리지 서비스에서 자주 사용되는 방법인데, 서명된 URL을 이용하여 업로드를 진행하면 서버에서 업로드를 하지 않기 때문에 서버의 부하를 줄일 수 있고, 클라이언트에서 직접 업로드를 하기 때문에 빠르게 업로드를 할 수 있다.&lt;br /&gt;또한 만료 시간을 설정할 수 있기 때문에 보안 측면에서도 안전하다. (만료 시간이 지나면 해당 URL은 더 이상 사용할 수 없다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 URL을 이용하여 데이터를 업로드하기 위해서 'put'요청을 하고, 다운로드(view)를 위해서 'get'요청을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밑에 코드는 upload를 위한 코드이기 때문에, 해당 url로 put 요청을 하는 코드이다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// pages/api - video.ts

import AWS from &quot;aws-sdk&quot;
import { NextApiRequest, NextApiResponse } from &quot;next/types&quot;

AWS.config.update({
    region: &quot;my-region&quot;,
    accessKeyId: &quot;aws-key&quot;,
    secretAccessKey: &quot;aws-secretsKey&quot;,
})
const s3 = new AWS.S3()

export default (req: NextApiRequest, res: NextApiResponse) =&amp;gt; {
    const newFileName = `${Date.now()}_${req.body.fileName}`
    const params = {
        Bucket: &quot;my bucket&quot;,
        Key: `interview/${newFileName}`, // 버킷안에 interview 디렉토리의 newFileName이름으로 저장
        Expires: 60, // 서명된 url은 1분간 유효하다.
    }

    s3.getSignedUrl(&quot;putObject&quot;, params, (err, url) =&amp;gt; {
        if (err) {
            res.status(500).json({ error: &quot;Internal Server Error&quot; })
        } else {
            // url은 서명된 url이며, fileUrl은 DB에 저장 될 S3 버킷의 pathName이다.
            res.status(200).json({ url, fileUrl: `/interview/${newFileName}` })
        }
    })
}

// Client Component - submitHandler에서 아래의 방법으로 업로드하고, 반환받은 fileUrl을 DB에 저장한다.

try {
    setLoading(true)
    const config = {
        headers: { &quot;Content-Type&quot;: &quot;video/*&quot; },
        api: { bodyParser: false },
    }
    const {
        data: { url: signedURL, fileUrl }, // api 라우터에서 signedURL과 path로 저장할 fileUrl을 반환 받는다.
    } = await axios.post(&quot;/api/upload/video-url&quot;, { fileName: file.name }) // 업로드 했을 때 file의 상태로 데이터를 가지고 있고, file.name을 이용해서 url을 만들 기 위해서 api라우터로 보내준다.

    const data = await axios.put(signedURL, file, config) // 서명된 url에 put 요청으로 file을 보낸다.
    const submitData = await updataCourseApplyStep2({ id, interview_url: fileUrl })
    setLoading(false)
    if (submitData.updated) setIsStep(step + 1)
} catch (e) {
    console.log(e)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;드디어 성. 공.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클린 코드, 효율적인 코드인지는 모르겠지만 해당 방법을 통해서 body의 내용이 큰 동영상 파일을 업로드할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;backend를 이용한 방법이나 AWS Ramda를 이용하는 방법도 생각해 봤지만 클라이언트단에서 하는 방법을 생각했고, AWS Ramda를 익히고 적용하기엔 조금 오래 걸릴 것 같아서 위의 방법을 이용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>s3 서명된 url</category>
      <category>s3업로드</category>
      <category>서명된 url</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/245</guid>
      <comments>https://baekspace.tistory.com/245#entry245comment</comments>
      <pubDate>Wed, 11 Oct 2023 10:20:08 +0900</pubDate>
    </item>
    <item>
      <title>230925 - React 컴포넌트의 유연성과 최적화 ( 조건부 렌더링, 다이나믹 컴포넌트, 고차컴포넌트(HOC))</title>
      <link>https://baekspace.tistory.com/243</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1.&amp;nbsp;조건부&amp;nbsp;렌더링&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;2.&amp;nbsp;다이나믹&amp;nbsp;컴포넌트&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;3.&amp;nbsp;고차&amp;nbsp;컴포넌트(HOC)&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h1&gt;React Component의 유연성과 최적화&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React를 이용하면서 컴포넌트를 다룰 때, 렌더링 최적화하는 여러 방법이 있다.&lt;br /&gt;여러 방법 중 3가지를 최근에 접하게 되었고, 이에 대해서 살펴보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 방식의 렌더링 최적화 측면과 적용 상황을 정리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 조건부 렌더링&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-1. 렌더링 최적화&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건부 렌더링은 필요한 부분만 렌더링을 수행하기 때문에 렌더링 성능에 도움을 줄 수 있다. 불필요한 컴포넌트의 렌더링을 방지하기 때문에 페이지의 성능을 향상할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-2. 적용 상황&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 조건에 따른 뷰 변경이 필요할 때 사용한다. 자주 변경되는 UI 부분에 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의&lt;/b&gt; : 복잡한 조건이나 중첩된 조건이 많아지면 코드가 복잡해질 수 있다. 그리고 컴포넌트의 라이프사이클을 고려할 때는 조건부 렌더링만으로 부족할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-3. 적용 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;const App = (props) =&amp;gt; {
    if (props.isLoggedIn) {
        return &amp;lt;Dashboard /&amp;gt;
    } else {
        return &amp;lt;Login /&amp;gt;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 다이나믹 컴포넌트&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-1. 렌더링 최적화&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 분할과 지연 로딩을 통한 성능 최적화에 유용할 수 있다. 예를 들어, 다이나믹 임포트를 통해 런타임에 필요한 컴포넌트만 로딩할 수 있습니다. 그렇기 때문에 초기 로딩 성능을 개선할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-2. 적용 상황&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 컴포넌트 중 하나를 동적으로 선택해야 할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의&lt;/b&gt; : 모든 컴포넌트가 공통된 props나 상태를 공유해야 하는 경우에는 적합하지 않을 수 있다. SSR을 사용하는 경우 다이나믹 임포트를 주의해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-3. 적용 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const Dashboard = () =&amp;gt; &amp;lt;div&amp;gt;Dashboard&amp;lt;/div&amp;gt;
const Login = () =&amp;gt; &amp;lt;div&amp;gt;Login&amp;lt;/div&amp;gt;

const App = (props) =&amp;gt; {
    const { id } = props
    const DynamicComponent = props.componentType === &quot;Dashboard&quot; ? Dashboard : Login

    return &amp;lt;DynamicComponent id={id} /&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 고차 컴포넌트(HOC)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Higher-Order Component로 다른 컴포넌트를 인자로 받아 새로운 컴포넌트를 반환한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-1. 렌더링 최적화&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고차 컴포넌트 자체는 렌더링 성능을 직접적으로 최적화하는 것은 아니지만 고차 컴포넌트를 통해 렌더링 로직을 분리하거나 제어할 수 있기 때문에 간접적으로 최적화에 도움을 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의&lt;/b&gt; : 코드의 재사용성에는 좋지만 너무 많은 로직을 HOC로 분리하게 되면 컴포넌트 구조가 복잡해질 수 있다. props 네이밍 충돌이 발생할 가능성이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-2. 적용 상황&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 재사용성과 유지 보수성을 높이는데 주로 사용된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-3. 적용 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// id에 따른 컴포넌트 변경
const withLogger = (WrappedComponent) =&amp;gt; {
    return (props) =&amp;gt; {
        return &amp;lt;WrappedComponent {...props} /&amp;gt;
    }
}

const MyComponent = (props) =&amp;gt; {
    return &amp;lt;div&amp;gt;{props.id}&amp;lt;/div&amp;gt;
}

const MyComponentWithLogger = withLogger(MyComponent)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>REACT</category>
      <category>react 설계</category>
      <category>고차컴포넌트</category>
      <category>다이나믹 컴포넌트</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/243</guid>
      <comments>https://baekspace.tistory.com/243#entry243comment</comments>
      <pubDate>Mon, 25 Sep 2023 18:20:09 +0900</pubDate>
    </item>
    <item>
      <title>230920 - Next param</title>
      <link>https://baekspace.tistory.com/242</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1. URL의 param 얻기&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;URL의 param 얻기&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRUD를 할 때 특정 글을 보거나 수정하거나 삭제하기 위해서 동적 매개 변수인 url의 param를 이용해서 해당 글을 찾아서 그 내용을 보여주거나, 수정하고, 삭제할 수 있는 기능을 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커뮤니티 게시글에 대한 url이 아래와 같을 때&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;https://a.com/user/:id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 id값이 특정 게시물마다 바뀌며 이 id 값을 param이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1-1. Client Component&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 컴포넌트에서 params 얻기 위해서 useParams라는 client component hook을 이용해서 얻을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;const params = useParams()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 hook은 클라이언트 컴포넌트에서만 사용 가능하므로, 서버 컴포넌트에서는 사용할 수 없다. 그렇기 때문에 클라이언트 컴포넌트에서 params를 얻기 위해서 새로운 파일로 해당 로직을 분리하고 상단에 'use client'를 이용해서 client compnent로 변경하게 되면 useParams()를 이용해서 id 값을 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1-2. Server Component&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 컴포넌트를 이용하는 방법은 서버 사이드 라우팅 라이브러리를 이용하는 방법과 Next.js에서 제공하는 getServerSideProps, getStaticProps를 이용하는 방법이 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export async function getServerSideProps(context) {
    const { id } = context.params

    return {
        props: {
            id,
        },
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js의 페이지 컴포넌트에서만 이용이 가능하다. 이를 이용해서 렌더가 되기 전 server에서 처리 후 데이터를 클라이언트 측으로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>next param</category>
      <category>next params</category>
      <category>동적라우팅</category>
      <category>라우팅</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/242</guid>
      <comments>https://baekspace.tistory.com/242#entry242comment</comments>
      <pubDate>Thu, 21 Sep 2023 09:02:12 +0900</pubDate>
    </item>
    <item>
      <title>230919 - Next Component, Cache</title>
      <link>https://baekspace.tistory.com/241</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1.&amp;nbsp;Component&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;2.&amp;nbsp;사용&amp;nbsp;예시&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;3.&amp;nbsp;Cache&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-19 오전 10.45.54.png&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdfvL0/btsuTQzmG8h/ICtkmhdX0qMss8vhzpPMI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdfvL0/btsuTQzmG8h/ICtkmhdX0qMss8vhzpPMI1/img.png&quot; data-alt=&quot;출처 : 생활코딩 Next 13강의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdfvL0/btsuTQzmG8h/ICtkmhdX0qMss8vhzpPMI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdfvL0%2FbtsuTQzmG8h%2FICtkmhdX0qMss8vhzpPMI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2880&quot; height=&quot;1800&quot; data-filename=&quot;스크린샷 2023-09-19 오전 10.45.54.png&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : 생활코딩 Next 13강의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777; text-align: center;&quot;&gt;출처 : 생활코딩 Next 13 강의&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;1. Component&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Client Component&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react를 사용하면서 여태까지 구현해왔던 방식이다. 주로 브라우저에서 실행된다.&lt;br /&gt;useState, useEffect, onClick, onChange, useRouter, useParams... 과 같이 클라이언트 단에서 조작할 수 있는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자와 상호 작용을 할 수 있는 컴포넌트이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Server Component&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버단에서 조작을 위해서 사용하는 방식이다.&lt;br /&gt;secure data나 cookie, header에 접근할 수 있다.&lt;br /&gt;Server Component 에서는 Client Component를 이용하게 되면 에러를 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자에게 단순히 화면을 보여주는 역할을 하는 컴포넌트이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Next.js 컴포넌트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next.js에서는 기본적으로 서버 컴포넌트로 간주하기 때문에 클라이언트 컴포넌트를 이용하기 위해서 특정한 조치가 있어야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'use client'를 이용하면 클라이언트 컴포넌트가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;2. 사용 예시&lt;/b&gt;&lt;/h1&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;&quot;use client&quot;
import Link from &quot;next/link&quot;
import &quot;./globals.css&quot;
import { useEffect, useState } from &quot;react&quot;

export default function RootLayout({ children }) {
    const [topics, setTopics] = useState([])
    useEffect(() =&amp;gt; {
        fetch(&quot;http://localhost:9999/topics&quot;)
            .then((res) =&amp;gt; res.json())
            .then((res) =&amp;gt; setTopics(res))
    }, [])
    return // &amp;lt;&amp;gt;렌더 내용&amp;lt;/&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드는 클라이언트 컴포넌트로 JS가 실행되지 않으면 상태를 가져올 수 없다. 이런 것을 해결하기 위해서 클라이언트 컴포넌트를 서버 컴포넌트로 바꾸어 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'use client' 를 없앤다.&lt;/li&gt;
&lt;li&gt;상태 관련 클라이언트 메서드(useState, useEffect..)를 지운다.&lt;/li&gt;
&lt;li&gt;async-await를 이용하여 비동기를 동기적으로 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import Link from &quot;next/link&quot;
import &quot;./globals.css&quot;

export default async function RootLayout({ children }) {
    const resp = await fetch(&quot;http://localhost:9999/topics&quot;)
    const topics = await resp.json()
    return // &amp;lt;&amp;gt;렌더 내용&amp;lt;/&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게하면 서버에서 최종적인 정적의 데이터만 사용자에게 넘겨서 화면을 그려준다. ( JS 내용은 제외된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 데이터, 파일으로 화면을 그리게 되면 장점이 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자에게 JS를 전송하지 않기 때문에 용량이 적어진다.&lt;/li&gt;
&lt;li&gt;접근하는 서버가 같은 서버라면 빠르게 처리하여 보여줄 수 있다.&lt;/li&gt;
&lt;li&gt;서버에서 렌더링이 끝난 후 데이터를 보내기 때문에 JS가 실행되지 않더라도 화면을 그리는데 문제가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;3. Cache&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서는 기본적으로 요청과 응답에 의해서 얻은 데이터를 보관하는 캐시의 기능을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 기능은 기존에 요청해서 얻은 데이터를 저장시켜두고 다시 로드가 되었을 때, 저장되어 있는 데이터를 가져오는 것을 말한다. 이를 이용하여 성능을 좋게 만들 수 있지만 상황에 따라서 새롭게 업데이트 된 내용을 불러와서 보여야 하는 경우에는 옳지 않은 방법일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 여러 방법을 이용해서 이 기능을 끄거나 주기를 짧게 하여 데이터를 계속 최신화 하는 방법이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주기적으로 업데이트 하기&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;fetch(&quot;https://...&quot;, { next: { revalidate: 10 } })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 뒤에 있는 숫자(10초)주기로 데이터를 업데이트하는 방식이다.&lt;br /&gt;지정된 숫자를 이용해서 했을 때, 그 주기에 맞춰지기 전까지는 직전 데이터를 이용한다. ( 10초의 경우 10초가 되기 전에 데이터가 바뀌어도 기존의 데이터를 사용함)&lt;br /&gt;그래서 0을 넣어 매번 업데이트를 할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;cache 기능을 비활성화 하기&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fetch(&quot;https://...&quot;, { cache: &quot;no-store&quot; })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 이용해서 캐싱자체를 안하는 방법을 구현 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>Cache</category>
      <category>넥스트 캐싱</category>
      <category>서버 컴포넌트</category>
      <category>캐시</category>
      <category>캐싱</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/241</guid>
      <comments>https://baekspace.tistory.com/241#entry241comment</comments>
      <pubDate>Tue, 19 Sep 2023 23:04:54 +0900</pubDate>
    </item>
    <item>
      <title>230918 - Next Routing (라우팅)</title>
      <link>https://baekspace.tistory.com/240</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;Routing&lt;br /&gt;2.&amp;nbsp;Dynamic&amp;nbsp;routing&lt;br /&gt;3.&amp;nbsp;Next.js&amp;nbsp;Routing&lt;br /&gt;4.&amp;nbsp;SPA&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;1. Routing&lt;/b&gt;&lt;/h1&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;https://example.com/dashboard/analytics&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;example.com&lt;/b&gt; : Domain : example.com은 도메인이라고 부른다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;dashboard/analytics&lt;/b&gt; : 도메인 뒤를 path라고 부른다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;dashboard&lt;/b&gt;, &lt;b&gt;analytics&lt;/b&gt; : path의 구성요소로 segment라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;라우팅이라는 것은 경로에 따라서 어떤 컨텐츠를 어떤 방식으로 보여줄 것인가를 결정하는 것이라고 한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;2. Dynamic routing&lt;/b&gt;&lt;/h1&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;다이나믹 라우팅은 동적라우팅, 적응형 라우팅이라고도 한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;https://example.com/read/1
https://example.com/read/2&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;위의 경로에서 1이나 2처럼 어떤 값이 올지 모르는(동적으로 바뀌는) 것을 말한다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;3. Next.js Routing&lt;/b&gt;&lt;/h1&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3-1. 기본&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Next.js에서 라우팅을 위해서 디렉토리를 이용해서 할 수 있다.&lt;br /&gt;path가 '/create'라고 한다면&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;https://localhost:3000/create&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;src/app/create 디렉토리를 이용해서 라우팅을하고 create/page.js의 파일을 이용해서 화면을 그릴 수 있다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;|- /src
|--- /app
|----- /create
|------- /layout.js
|------- /page.js
|----- /read
|------- /page.js&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;3-2. 다이나믹 라우팅&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;동적으로 변하는 id값을 가진 라우팅상황에서 path가 '/read/[id]'라고 한다면&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;https://localhost:3000/read/1
https://localhost:3000/read/2&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;src/app/read/[id] 디렉토리를 이용해서 라우팅하고 [id]/page.js 파일을 이용해서 화면을 그릴 수 있다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;|- /src
|--- /app
|----- /read
|------- /[id]
|--------- /page.js&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// src/app/read/[id]/page.js
const Read = (props) =&amp;gt; {
    return (
        &amp;lt;&amp;gt;
            &amp;lt;h2&amp;gt;Read&amp;lt;/h2&amp;gt;
            param : {props.params.id}
        &amp;lt;/&amp;gt;
    )
}

export default Read&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-18 오후 12.13.04.png&quot; data-origin-width=&quot;193&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oRLWs/btsugSTKbiy/NkSNqIMTfmuOhDadpgBRrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oRLWs/btsugSTKbiy/NkSNqIMTfmuOhDadpgBRrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oRLWs/btsugSTKbiy/NkSNqIMTfmuOhDadpgBRrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoRLWs%2FbtsugSTKbiy%2FNkSNqIMTfmuOhDadpgBRrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;193&quot; height=&quot;188&quot; data-filename=&quot;스크린샷 2023-09-18 오후 12.13.04.png&quot; data-origin-width=&quot;193&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;4. SPA&lt;/b&gt;&lt;/h1&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Next.js는 SSR이나 SSG에 강점이 있지만 SPA방식으로 이용할 수 있다.&lt;br /&gt;예를 들어 a태그를 이용해서 화면을 변경할 때 layout을 제외한 일부분만 변경되는 화면이여도 a태그를 이용한 화면에서는 서버를 이용해서 화면을 다시 그려서 클라이언트로 보내주기 때문에 HTML 전체를 다시 요청해서 그려서 사용자에게 보여준다.&lt;br /&gt;이 과정에서 리소스를 많이 사용하기 때문에 서비스를 제공하는 개발자(제공자)가 비용이 많이 들어갈 뿐만 아니라 서비스를 이용하는 이용자도 시간을 사용하게 된다.&lt;br /&gt;이를 해결하기 위해서 a태그 대신 next/link의 Link 컴포넌트를 이용할 수 있다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;Link 컴포넌트&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// a태그
&amp;lt;h1&amp;gt;
    &amp;lt;a href=&quot;/&quot;&amp;gt;WEB&amp;lt;/a&amp;gt;
&amp;lt;/h1&amp;gt;

// Link 컴포넌트
&amp;lt;h1&amp;gt;
    &amp;lt;Link href=&quot;/&quot;&amp;gt;WEB&amp;lt;/Link&amp;gt;
&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Link 컴포넌트를 이용한 후 해당 페이지를 이동하기 전 hover 이벤트 발생시에 해당 라우터에서 데이터를 프리패치 할 수 있는 기능이 있다. 프리패치 기능은 대상 페이지의 데이터를 미리 로드하여 사용자가 해당 링크를 클릭했을 때 빠르게 페이지를 보여줄 수 있도록 도와준다.&lt;br /&gt;Link 컴포넌트를 사용하면 서버의 부하를 줄이고, 사용자에게 더 나은 경험을 제공할 수 있습니다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>넥스트</category>
      <category>넥스트 라우팅</category>
      <category>라우팅</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/240</guid>
      <comments>https://baekspace.tistory.com/240#entry240comment</comments>
      <pubDate>Mon, 18 Sep 2023 18:14:49 +0900</pubDate>
    </item>
    <item>
      <title>230918 - NEXT.JS 사용하기</title>
      <link>https://baekspace.tistory.com/239</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1.&amp;nbsp;NextJS란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;2.&amp;nbsp;Next의&amp;nbsp;특징&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;3.&amp;nbsp;설치하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. NextJS란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 React를 기반으로 만들어진 웹 프레임워크이다.&lt;br /&gt;서버 사이드 렌더링(SSR), 정적 사이트 생성(SSG), 클라이언트 사이드 렌더링(CSR) 등 다양한 렌더링 방식을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Next의 특징&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;SSR과 SSG 지원&lt;/b&gt; : 서버사이드 렌더링과 정적 사이트 생성을 쉽게 구현할 수 있어서 검색 엔진 최적화(SEO)에 유리하다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;빠른 성능&lt;/b&gt; : 코드 스플리팅, 레이지 로딩, 프리페칭 등을 자동으로 지원하여 애플리케이션 성능을 최적화한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;API Routes&lt;/b&gt; : 'pages/api' 디렉토리를 이용하여 API 엔드포인트를 생성할 수 있다. (프론트 서버의 개념)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;동적 라우팅&lt;/b&gt; : 파일과 폴더 구조를 이용하여 라우팅을 자동으로 설정한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;# next 최신버전을 현재 디렉토리에 설치하기
npx create-next-app@latest .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-18 오후 6.01.30.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ASSy2/btsudO5omNS/NFQcKz4h9vhrXBYWOb0KQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ASSy2/btsudO5omNS/NFQcKz4h9vhrXBYWOb0KQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ASSy2/btsudO5omNS/NFQcKz4h9vhrXBYWOb0KQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FASSy2%2FbtsudO5omNS%2FNFQcKz4h9vhrXBYWOb0KQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;185&quot; data-filename=&quot;스크린샷 2023-09-18 오후 6.01.30.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>Next</category>
      <category>next 설치하기</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/239</guid>
      <comments>https://baekspace.tistory.com/239#entry239comment</comments>
      <pubDate>Mon, 18 Sep 2023 18:04:33 +0900</pubDate>
    </item>
    <item>
      <title>230829 - Frequency Counter (빈도 카운터) 패턴</title>
      <link>https://baekspace.tistory.com/238</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1.&amp;nbsp;Frequency&amp;nbsp;Counter&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;2.&amp;nbsp;&amp;nbsp;두 가지 배열이 주어졌을 때 첫 번째 배열의 각 요소가 두 번째 배열에 있는지 확인하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;3.&amp;nbsp;시간&amp;nbsp;복잡도&amp;nbsp;VS&amp;nbsp;공간&amp;nbsp;복잡도&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;1. Frequency Counter&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 빈도수를 계산하기 위한 알고리즘 패턴&lt;br /&gt;배열이나 문자열 등의 자료구조에서 특정 요소가 몇 개 있는지 셀 때 주로 사용된다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;// [1,2,1,3]
{
    &quot;1&quot;: 2,
    &quot;2&quot;: 1,
    &quot;3&quot;: 1
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키는 배열의 요소, 값은 해당 요소의 빈도수이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;주로 사용하는 상황&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;두 개의 배열이나 문자열이 같은 요소로 이루어져 있는지 확인하기&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;배열이나 문자열에서 특정 요소의 빈도수를 찾기&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;데이터의 분포를 빠르게 파악하기&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;&lt;br /&gt;2.&amp;nbsp; 두가지 배열이 주어졌을 때 첫 번째 배열의 각 요소가 두 번째 배열에 있는지 확인&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;방법 1&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 배열의 제곱과 2번 배열이 맞는지 반복문을 통해서 검증한다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;function same(arr1, arr2) {
    if (arr1.length !== arr2.length) return false
    for (let i = 0; i &amp;lt; arr1.length; i++) {
        let correctIndex = arr2.indexOf(arr1[i] ** 2)
        if (correctIndex === -1) return false
        arr2.splice(correctIndex, 1)
    }
    return true
}

console.log(same([1, 2, 3], [4, 1, 9])) // true
// console.log(same([1, 2, 3], [4, 1, 4])) // false
// console.log(same([1, 2, 3], [4, 1, 9, 4])) // false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 시간 복잡도가 O(n^2)이다. for문을 1번밖에 사용하지 않았지만 indexOf()가 내부적으로 반복문을 사용하기 때문에 O(n^2)이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;방법 2&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈도 카운터 패턴을 이용하는 방법&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;function same2(arr1, arr2) {
    if (arr1.length !== arr2.length) return false
    let frequencyCounter1 = {}
    let frequencyCounter2 = {}
    for (let val of arr1) {
        frequencyCounter1[val] = (frequencyCounter1[val] || 0) + 1
    }
    for (let val of arr2) {
        frequencyCounter2[val] = (frequencyCounter2[val] || 0) + 1
    }
    for (let key in frequencyCounter1) {
        if (!(key ** 2 in frequencyCounter2)) return false
        if (frequencyCounter2[key ** 2] !== frequencyCounter1[key]) return false
    }
    return true
}

console.log(same2([1, 2, 3], [4, 1, 9])) // true
// console.log(same2([1, 2, 3], [4, 1, 4])) // false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 루프를 이용해서 시간이 오래 걸릴 거라 생각했지만 중첩이 아니기 때문에 3n 일 뿐이다 - &lt;b&gt;두 개의 루프가 하나의 중첩 루프보다 효율적이다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 same2는 O(3n)이지만 빅오 표기법은 O(n)로 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;3. 시간 복잡도 VS 공간 복잡도&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 빅오 표기법은 시간 복잡도에 대한 표현이었다.&lt;br /&gt;방법 1과 2의 공간 복잡도를 보면 각각 O(1), O(n)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 생기는 궁금증은&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;same은 O(1) 공간복잡도이고, O(n^2) 시간 복잡도이고,&lt;br /&gt;same2는 O(n) 공간복잡도이고, O(n) 시간 복잡도이다&lt;br /&gt;&lt;br /&gt;그러면 뭐가 더 효율적일까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제는 로직을 사용하는 자원(시간, 공간)에 따라 달라지는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거여서 좋다, 저거여서 좋다 보다는 처리를 빨리해서 값을 보여줘야 하는 경우에는 시간 복잡도가 더 효율적인 로직을 이용해야 하고, 한정되어 있는 메모리를 이용해야 하는 경우에는 공간 복잡도가 효율적인 것이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 상황을 잘 고려한 로직이 필요할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>frequency counter</category>
      <category>배열 비교</category>
      <category>빈도 카운터</category>
      <category>빈도수</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/238</guid>
      <comments>https://baekspace.tistory.com/238#entry238comment</comments>
      <pubDate>Wed, 30 Aug 2023 01:53:13 +0900</pubDate>
    </item>
    <item>
      <title>개발자 공부순서 - 로드맵</title>
      <link>https://baekspace.tistory.com/237</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;독학 또는 추가적인 학습을 위한 로드맵 - &lt;a href=&quot;https://roadmap.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://roadmap.sh/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693321776203&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Developer Roadmaps - roadmap.sh&quot; data-og-description=&quot;Community driven roadmaps, articles and guides for developers to grow in their career.&quot; data-og-host=&quot;roadmap.sh&quot; data-og-source-url=&quot;https://roadmap.sh/&quot; data-og-url=&quot;https://roadmap.sh/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/h30we/hyTL50va8n/X8jigbN1fXZ8lsVwM8ceo0/img.png?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260&quot;&gt;&lt;a href=&quot;https://roadmap.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://roadmap.sh/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/h30we/hyTL50va8n/X8jigbN1fXZ8lsVwM8ceo0/img.png?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Developer Roadmaps - roadmap.sh&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Community driven roadmaps, articles and guides for developers to grow in their career.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;roadmap.sh&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;요즘은 이것저것 개념 공부, 적용해 보는 과정이고.. 추후에 어떻게 공부할지 찾아보다가 찾은 사이트..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 포지션의 개발자가 어떤 순서대로 공부를 해야 할지 모른다면 위의 링크를 이용해서 필요한 것, 중요한 것을 파악하고 공부의 순서를 정하면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2H9H6/btssk9pZ29U/s6WMLSkgSvFLceoqvk2k20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2H9H6/btssk9pZ29U/s6WMLSkgSvFLceoqvk2k20/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;616&quot; data-filename=&quot;스크린샷 2023-08-30 오전 12.08.43.png&quot; style=&quot;width: 48.0585%; margin-right: 10px;&quot; data-widthpercent=&quot;48.62&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2H9H6/btssk9pZ29U/s6WMLSkgSvFLceoqvk2k20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2H9H6%2Fbtssk9pZ29U%2Fs6WMLSkgSvFLceoqvk2k20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkHTiq/btssqng9Qpc/vYUXIPZFbbK8gLtHVljFN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkHTiq/btssqng9Qpc/vYUXIPZFbbK8gLtHVljFN0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;583&quot; data-filename=&quot;스크린샷 2023-08-30 오전 12.08.50.png&quot; style=&quot;width: 50.7787%;&quot; data-widthpercent=&quot;51.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkHTiq/btssqng9Qpc/vYUXIPZFbbK8gLtHVljFN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkHTiq%2Fbtssqng9Qpc%2FvYUXIPZFbbK8gLtHVljFN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;583&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측은 포지션에 따른 로드맵이고 우측은 스킬에 대한 로드맵으로 되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-08-30 오전 12.05.43.png&quot; data-origin-width=&quot;749&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHaiaX/btssABdY38k/BX9sPuL6nS0caNRznY131k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHaiaX/btssABdY38k/BX9sPuL6nS0caNRznY131k/img.png&quot; data-alt=&quot;블록체인 개발자 로드맵&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHaiaX/btssABdY38k/BX9sPuL6nS0caNRznY131k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHaiaX%2FbtssABdY38k%2FBX9sPuL6nS0caNRznY131k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;749&quot; height=&quot;816&quot; data-filename=&quot;스크린샷 2023-08-30 오전 12.05.43.png&quot; data-origin-width=&quot;749&quot; data-origin-height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;블록체인 개발자 로드맵&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록체인 개발자를 생각한다면 위의 로드맵 순서대로 진행할 수 있겠다. 꼭 이 순서대로 진행해야 하는 것은 아니겠지만 기본적인 순서를 생각하기엔 좋은 사이트인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-08-30 오전 12.09.23.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dINarU/btsshqyCHYj/3MKGIwxH8IQiz3UMPNddo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dINarU/btsshqyCHYj/3MKGIwxH8IQiz3UMPNddo0/img.png&quot; data-alt=&quot;JS에 대한 로드맵&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dINarU/btsshqyCHYj/3MKGIwxH8IQiz3UMPNddo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdINarU%2FbtsshqyCHYj%2F3MKGIwxH8IQiz3UMPNddo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;648&quot; data-filename=&quot;스크린샷 2023-08-30 오전 12.09.23.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JS에 대한 로드맵&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 포지션에 대한 로드맵뿐 아니라 스킬의 로드맵도 있는데 그중 JavaScript에 대한 내용이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 이용해서 어떤 개념을 순서대로 익혀야 할지 감도 잡히는 것 같다. 또 이걸 이용해서 개발자 면접 준비하는 것도 좋지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 점은 각 개념에 대해서 간단한 설명과 참고할 사이트도 있어서.... 너무 좋...다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-08-30 오전 12.15.59.png&quot; data-origin-width=&quot;1335&quot; data-origin-height=&quot;937&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3RXMu/btsskZUSCx3/VEZKdqcEnbn0SDXGANtF71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3RXMu/btsskZUSCx3/VEZKdqcEnbn0SDXGANtF71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3RXMu/btsskZUSCx3/VEZKdqcEnbn0SDXGANtF71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3RXMu%2FbtsskZUSCx3%2FVEZKdqcEnbn0SDXGANtF71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1335&quot; height=&quot;937&quot; data-filename=&quot;스크린샷 2023-08-30 오전 12.15.59.png&quot; data-origin-width=&quot;1335&quot; data-origin-height=&quot;937&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작</category>
      <category>개발자 로드맵</category>
      <category>로드맵</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/237</guid>
      <comments>https://baekspace.tistory.com/237#entry237comment</comments>
      <pubDate>Wed, 30 Aug 2023 00:23:25 +0900</pubDate>
    </item>
    <item>
      <title>230823 - 알고리즘 적용하는 위치에 따른 방법 3가지 (SHA-256)</title>
      <link>https://baekspace.tistory.com/236</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1.&amp;nbsp;Node.js&amp;nbsp;환경&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;2.&amp;nbsp;브라우저&amp;nbsp;환경&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;3.&amp;nbsp;crypto-js&amp;nbsp;라이브러리&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;4. Node.js VS 브라우저 VS 라이브러리 비교&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록체인을 하면서 해시 알고리즘을 이용하는 경우가 많이 있다. 그래서 오늘은 여러 암호화하는 방법 중 &lt;b&gt;해시 알고리즘을 적용하는 위치에 따른 방법&lt;/b&gt;을 찾아봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면 3가지가 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Node.js 환경에서 내장 모듈&lt;/li&gt;
&lt;li&gt;브라우저 환경에서 API 사용&lt;/li&gt;
&lt;li&gt;라이브러리 (Node 환경, 브라우저 환경)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화를 위한 여러 알고리즘, 방식이 있지만 SHA-256을 이용하여 해시 값을 얻는 예시로 진행하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SHA-256 :&lt;/b&gt; 256비트의 해시 값을 생성하는 암호화 해시 함수, 해싱 알고리즘으로 한번 해싱된 데이터는 원래 데이터로 되돌릴 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해싱된 데이터가 맞는지 확인하는 방법은 새로 입력받은 데이터를 해싱한 후 같은 기존에 저장하고 있는 데이터와 비교하여 입력받은 데이터와 저장 데이터가 서로 같은지 확인한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Node.js 환경&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js환경에서는 &amp;lsquo;crypto&amp;rsquo; 내장 모듈을 이용해서 SHA-256 해시를 생성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const crypto = require(&quot;crypto&quot;);

const sha256 = (data) =&amp;gt; {
    const hash = crypto.createHash('sha256');
    hash.update(data);
    return hash.digest('hex');
}

const myData = 'baekspace';
const myHash = sha256(myData);

console.log(myHash);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;br /&gt;2. 브라우저 환경&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서는 &amp;lsquo;subtleCrypto&amp;rsquo; API를 이용하여 SHA-256 해시를 생성할 수 있다. - crypto.subtle.digest()&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const sha256 = async (data) =&amp;gt; {
    const msgUint8 = new TextEncoder().encode(data) 
    const hashBuffer = await crypto.subtle.digest(&quot;SHA-256&quot;, msgUint8)
    const hashArray = Array.from(new Uint8Array(hashBuffer)) 
    const hashHex = hashArray.map((b) =&amp;gt; b.toString(16).padStart(2, &quot;0&quot;)).join(&quot;&quot;) 
    return hashHex
}

const myData = 'baekspace';
sha256(myData).then(hash =&amp;gt; console.log(hash))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 입력받은 값을 msgUint8 변수로 변환하는 이유는 문자열을 바이트 배열로 변환하여 해싱 알고리즘이 처리할 수 있는 형태로 변경시키기 위한 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 해시화를 한 후 다시 uint8Array로 변환한 다음 문자열로 변경해 주는 과정을 거친다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;3. crypto-js 라이브러리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버, 브라우저 모두에서 사용할 수 있는 라이브러리를 이용한 방식이다.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;const CryptoJS = require(&quot;crypto-js&quot;);

const myData = 'baekspace';
const myHash = CryptoJS.SHA256(myData).toString(CryptoJS.enc.Hex);

console.log(myHash);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;4. Node.js VS 브라우저 VS 라이브러리 비교&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 세 가지 모두 같은 결과를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 특징을 살펴보면&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Node.js의 &amp;lsquo;crypto&amp;rsquo; 모듈&lt;/b&gt;: Node.js의 내장 모듈이므로 추가적인 라이브러리 설치 없이 사용할 수 있으며, 성능도 최적화되어 있을 가능성이 높다. 서버 측에서 사용하는 경우에 적합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;브라우저의 &amp;lsquo;subtleCrypto&amp;rsquo; API&lt;/b&gt;: 브라우저가 지원하는 웹 암호화 API로, 클라이언트 측에서 안전하게 암호화 작업을 하고 싶을 때 사용할 수 있다. 브라우저에 최적화되어 있고, 웹 보안 관점에서 권장되는 방식&lt;/li&gt;
&lt;li&gt;&amp;lsquo;&lt;b&gt;crypto-js&amp;rsquo; 라이브러리&lt;/b&gt;: 클라이언트와 서버 모두에서 사용할 수 있는 라이브러리이다. 사용하기 쉽고 다양한 암호화 옵션이 있지만 외부 라이브러리이므로 성능과 크기 측면에서 다른 방식보다 미세하게 떨어질 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 알고 있던 방법은 Node.js 환경, 라이브러리를 이용해서 해싱하는 것이었는데, 이번에는 서버단까지 데이터를 str형태로 끌고 가서 변환하고 싶지 않았고, 블록체인 특성상 프론트에서 처리하는 방식이 옳다고 생각했기 때문에 브라우저에서 해싱하는 방법을 찾았던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;암호화 또는 해싱이 필요한 위치를 고려해서 사용해야 한다&lt;/b&gt;는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>sha-256</category>
      <category>암호화</category>
      <category>해시화</category>
      <category>해싱</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/236</guid>
      <comments>https://baekspace.tistory.com/236#entry236comment</comments>
      <pubDate>Thu, 24 Aug 2023 00:04:12 +0900</pubDate>
    </item>
    <item>
      <title>230821 - 빅오 표기법 ( Big O natation)</title>
      <link>https://baekspace.tistory.com/235</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1. 빅오 표기법이란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;2. 표기방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;3. 잘못된 표기방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;4. 복잡도 알고리즘 비교&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;5. 시간 복잡도와 공간 복잡도의 상호 관계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1.&lt;/b&gt; &lt;b&gt;빅오 표기법이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빅오 표기법(Big O notation)은 알고리즘의 효율성을 설명할 때 주로 사용되는 수학적 표기법이다. 이 표기법은 입력 크기가 커짐에 따라 어떻게 알고리즘이 수행되는지, 얼마나 많은 리소스(시간, 메모리 등)가 필요하는지를 설명하는 데 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빅오 표기법을 이용해서 시간 복잡도, 공간 복잡도에 따라 표기를 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시간 복잡도&lt;/b&gt; : 알고리즘이 실행되는데 필요한 단계의 수나 계산 시간을 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공간 복잡도&lt;/b&gt; : 알고리즘이 실행되는 동안 필요한 메모리 양을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 표기 방법&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;O(1)&lt;/span&gt; : 상수 시간/공간 복잡도 :&lt;/b&gt; 입력 크기와 관계없이 일정한 시간/공간이 소요된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예) 단순 연산, 배열의 특정 인덱스 접근&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;O(log n)&lt;/span&gt; : 로그 시간/공간 복잡도 :&lt;/b&gt; 로그배만큼 시간/공간이 소요된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예) 이진 탐색&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;O(n)&lt;/span&gt; : 선형 시간/공간 복잡도 : 입력 크기에 비례하여 시간/공간이 소요된다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예) 단순 탐색, 동적 배열, for문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;O(n log n)&lt;/span&gt; : 선형 로그 시간/공간 복잡도 : 입력 크기와 로그배만큼 시간/공간이 소요된다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예) 병합정렬, 퀵 정렬&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;O(n^2)&lt;/span&gt; : 제곱 시간/공간 복잡도 : 입력 크기의 제곱에 비례하는 시간/공간이 소요된다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예) 버블 정렬, 이중 for문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;O(2^n)&lt;/span&gt; : 지수 시간/공간 복잡도&lt;/b&gt; : 입력 크기에 대해 지수적으로 시간/공간이 소요된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예) 피보나치수열, 재귀&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 잘못된 표기방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;상수항, 영향력이 없는 항들은 무시한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(2n) &amp;rarr; O(n)&lt;/li&gt;
&lt;li&gt;O(500) &amp;rarr; O(1)&lt;/li&gt;
&lt;li&gt;O(13n^2) &amp;rarr; O(n2)&lt;/li&gt;
&lt;li&gt;O(2n+100) &amp;rarr; O(n)&lt;/li&gt;
&lt;li&gt;O(100n+10) &amp;rarr; O(n)&lt;/li&gt;
&lt;li&gt;O(n^2+100n) &amp;rarr; O(n^2)&lt;/li&gt;
&lt;li&gt;O(100n^2+100) &amp;rarr; O(n^2)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 복잡도 알고리즘 비교&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;O(1)&lt;/b&gt; &amp;gt; &lt;b&gt;O(log n)&lt;/b&gt; &amp;gt; &lt;b&gt;O(n)&lt;/b&gt; &amp;gt; &lt;b&gt;O(n log n)&lt;/b&gt; &amp;gt; &lt;b&gt;O(n^2) &amp;gt; O(2^n)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수 &amp;gt; 로그 &amp;gt; 선형 &amp;gt; 선형 로그 &amp;gt; 제곱 &amp;gt; 지수 순으로 효율적이라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수가 가장 빠르고 효율적이며, 지수 복잡도로 갈수록 입력크기가 커지면 느려지기 때문에 피하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 시간 복잡도와 공간 복잡도의 상호 관계&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시간 복잡도&lt;/b&gt;와 &lt;b&gt;공간 복잡도&lt;/b&gt;는 서로 상호 관계가 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 메모리(공간)를 더 사용하게 되면 연산을 줄일 수 있고, 반대로 시간을 더 사용하면 메모리를 절약할 수 있다. 대표적으로 캐싱이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;캐싱 : 연산 결과를 저장하여 나중에 같은 계산을 할 때 값을 빠르게 얻을 수 있지만 값을 저장할 메모리가 필요하다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-08-22 오전 1.09.36.png&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;497&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oiAPM/btsrUue4n5I/OL2itVlIjEEoJrQOco12k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oiAPM/btsrUue4n5I/OL2itVlIjEEoJrQOco12k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oiAPM/btsrUue4n5I/OL2itVlIjEEoJrQOco12k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoiAPM%2FbtsrUue4n5I%2FOL2itVlIjEEoJrQOco12k1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;889&quot; height=&quot;497&quot; data-filename=&quot;스크린샷 2023-08-22 오전 1.09.36.png&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;497&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 : &lt;a href=&quot;https://www.udemy.com/course/best-javascript-data-structures/&quot;&gt;https://www.udemy.com/course/best-javascript-data-structures/&lt;/a&gt;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>빅오표기법</category>
      <category>알고리즘</category>
      <category>유데미</category>
      <category>자료구조</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/235</guid>
      <comments>https://baekspace.tistory.com/235#entry235comment</comments>
      <pubDate>Tue, 22 Aug 2023 01:34:43 +0900</pubDate>
    </item>
    <item>
      <title>230817 - GETH를 이용한 Ethereum private network 구축하기(POA)</title>
      <link>https://baekspace.tistory.com/234</link>
      <description>&lt;h1&gt;Private Network&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. geth 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS 설치 방법&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;#brew 설치 확인하기
$ brew -v
$ brew tap ethereum/ethereum
$ brew install ethereum

# ethereum 업데이트하기
$ brew update
$ brew upgrade
$ brew reinstall ethereum&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서 : &lt;a href=&quot;https://geth.ethereum.org/docs/getting-started/installing-geth&quot;&gt;https://geth.ethereum.org/docs/getting-started/installing-geth&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1692272685977&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Installing Geth | go-ethereum&quot; data-og-description=&quot;Guide to installing Geth&quot; data-og-host=&quot;geth.ethereum.org&quot; data-og-source-url=&quot;https://geth.ethereum.org/docs/getting-started/installing-geth&quot; data-og-url=&quot;https://geth.ethereum.org/docs/getting-started/installing-geth&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/g7YNB/hyTFkb2BhP/scXJnNPNWEVsrCpKk6QJQk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/73ikh/hyTFfu2IN6/P1jKW3ZHYIkOmeaAkvVWl0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://geth.ethereum.org/docs/getting-started/installing-geth&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://geth.ethereum.org/docs/getting-started/installing-geth&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/g7YNB/hyTFkb2BhP/scXJnNPNWEVsrCpKk6QJQk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/73ikh/hyTFfu2IN6/P1jKW3ZHYIkOmeaAkvVWl0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Installing Geth | go-ethereum&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Guide to installing Geth&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;geth.ethereum.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. genesis.json 생성하기&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;config&quot;: {
        &quot;chainId&quot;: 119, // 원하는 chainId를 지정한다.
        &quot;homesteadBlock&quot;: 0,
        &quot;eip150Block&quot;: 0,
        &quot;eip155Block&quot;: 0,
        &quot;eip158Block&quot;: 0,
        &quot;byzantiumBlock&quot;: 0,
        &quot;constantinopleBlock&quot;: 0,
        &quot;petersburgBlock&quot;: 0,
        &quot;istanbulBlock&quot;: 0,
        &quot;berlinBlock&quot;: 0,
        &quot;clique&quot;: {
            &quot;period&quot;: 5, //블록생성 주기
            &quot;epoch&quot;: 30000 // 블록을 그룹화하는 단위 30000개 블록마다 스냅샷을 생성한다.
        } // POA 합의 알고리즘 사용을 위해서 clique 속성을 이용한다.
    },
    &quot;difficulty&quot;: &quot;1&quot;,
    &quot;gasLimit&quot;: &quot;8000000&quot;,
    &quot;extradata&quot;: &quot;0x0000000000000000000000000000000000000000000000000000000000000000a22061Eb8c9702Cfac2d9d8ce6dfF9A195d05e810000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000&quot;, // extradata를 이용해서 초기 참여자를 지정할 수 있다.
    &quot;alloc&quot;: {
        &quot;a22061Eb8c9702Cfac2d9d8ce6dfF9A195d05e81&quot;: { &quot;balance&quot;: &quot;100000000000000000000&quot; },
        &quot;12ec9720a6Cc8e48aD745eadeB49f2C38eC5Ef59&quot;: { &quot;balance&quot;: &quot;400000&quot; },
        &quot;13169CD3EA4041A546F7c70906fFFAa26D939b87&quot;: { &quot;balance&quot;: &quot;100000000000000000000&quot; }
    } // 노드를 실행할 때 최초에 넣어줄 잔액이다. wei단위로 1eth = 10^18 wei이다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;genesis.json에서 계정을 작성할 때는 '0x'를 제외하고 작성한다.&lt;br /&gt;extradata에는 0의 개수 그대로 유지해서 작성해야 한다. 위의 예시에서는 a22~e81까지가 하나의 계정이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Keystore&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;geth account new --datadir $PWD&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어를 실행하면 비밀번호를 생성하라는 문구가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;'Your new account is locked with a password. Please give a password. Do not forget this password.'&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 비밀번호는 생성한 key를 unlock 할 때 사용한다.&lt;br /&gt;계정이 잠금상태일 때는 트랜잭션에 서명을 할 수 없지만 비밀번호를 통해서 unlock을 한 후 트랜잭션 서명에 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키는 여러 개를 사용할 수 있으며, 노드를 실행할 때 언락을 할 계정을 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;--unlock '0xKEY1,0xKEY2...'&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-08-18 오전 9.59.36.png&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBxkN8/btsrqIzL9yQ/SKtbDfdtlx9h5qbwdciq90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBxkN8/btsrqIzL9yQ/SKtbDfdtlx9h5qbwdciq90/img.png&quot; data-alt=&quot;key 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBxkN8/btsrqIzL9yQ/SKtbDfdtlx9h5qbwdciq90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBxkN8%2FbtsrqIzL9yQ%2FSKtbDfdtlx9h5qbwdciq90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1406&quot; height=&quot;372&quot; data-filename=&quot;스크린샷 2023-08-18 오전 9.59.36.png&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;key 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 비밀번호를 txt 파일로 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드를 실행할 때 unlock 플래그를 이용해서 키를 잠금 해제하고 노드에서 사용하게 되는데 이를 쉽게 하기 위해서 txt파일로 지정해서 사용할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Key가 여러 개인 경우에는 줄 바꿈을 통해서 각 키에 대한 비밀번호를 적어주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-08-17 오후 7.53.45.png&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pl8mH/btsrqHtXB5j/WY8iuCfU1WAbaJMwJHjycK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pl8mH/btsrqHtXB5j/WY8iuCfU1WAbaJMwJHjycK/img.png&quot; data-alt=&quot;줄바꿈을 이용해서 여러개의 비밀번호 지정하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pl8mH/btsrqHtXB5j/WY8iuCfU1WAbaJMwJHjycK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPl8mH%2FbtsrqHtXB5j%2FWY8iuCfU1WAbaJMwJHjycK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;794&quot; height=&quot;530&quot; data-filename=&quot;스크린샷 2023-08-17 오후 7.53.45.png&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;줄바꿈을 이용해서 여러개의 비밀번호 지정하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 제네시스 파일로 초기화&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;# 생성한 제네시스 블록을 이용해서 초기화하기
$ geth init --datadir $PWD genesis.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어를 이용해서 커스텀한 제네시스 블록으로 초기화할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 노드 실행 및 피어 맺기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드를 실행하기 위해서 여러 명령어, 플래그를 이용한다.&lt;br /&gt;명령어에 대한 자세한 설명은 &lt;b&gt;'geth -h'를&lt;/b&gt; 이용하여 확인할 수 있다.&lt;br /&gt;각 노드의 port는 겹치지 않도록 지정을 해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;node 1&lt;/h3&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;geth \
--authrpc.addr 127.0.0.1 \
--authrpc.port 8551 \
--datadir $PWD \
--syncmode 'full' \
--allow-insecure-unlock \
--networkid 119 \
--maxpeers 5 \
--http \
--http.port 8545 \
--http.addr &quot;0.0.0.0&quot; \
--http.corsdomain &quot;\*&quot; \
--http.api &quot;admin,eth,debug,miner,net,txpool,personal,web3&quot; \
--ws \
--ws.port 3335 \
--ws.api eth,net,web3 \
--port 30303 \
--nodiscover \
--unlock '0xa22061eb8c9702cfac2d9d8ce6dff9a195d05e81' \
--password $PWD/password.txt \
--miner.etherbase &quot;0xa22061eb8c9702cfac2d9d8ce6dff9a195d05e81&quot; \
console
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;node 2&lt;/h3&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;geth \
--authrpc.addr 127.0.0.1 \
--authrpc.port 8552 \
--datadir $PWD \
--syncmode 'full' \
--allow-insecure-unlock \
--networkid 119 \
--maxpeers 5 \
--http \
--http.port 8546 \
--http.addr &quot;0.0.0.0&quot; \
--http.corsdomain &quot;\*&quot; \
--http.api &quot;admin,eth,debug,miner,net,txpool,personal,web3&quot; \
--ws \
--ws.port 3334 \
--ws.api eth,net,web3 \
--port 30304 \
--unlock '0x13169CD3EA4041A546F7c70906fFFAa26D939b87' \
--bootnodes &quot;enode://2549091a058dc0f3312654556bca621a84226b6e45270b838572b68ebdb1a7e8d9438ea9575dac31a8859b83b453a177eb58ac50a2940bab7a53bea69688ef56@127.0.0.1:30303&quot; \
--password $PWD/password.txt \
console&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 노드를 실행할 때 명령어의 차이점이 있는데 --miner.etherbase, --nodiscover, --bootnodes 각각이 있거나 없을 수 있다.&lt;br /&gt;(위의 모든 명령어는 필요한 것을 사용하면 된다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;--miner.etherbase : 채굴 시 보상을 위해서 계정을 지정하는 것인데, POA에서는 채굴에 대한 보상이 없기 때문에 실질적으로 리워드가 주어지진 않지만, 계정을 지정해 주어야 마이닝을 실행할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 실행 후 콘솔에서 miner.setEtherbase(&quot;0x를 포함한 계정&quot;)을 이용해서 설정 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;--nodiscover : 노드가 다른 노드를 자동으로 찾지 못하게 한다. 특정 피어와만 연결하려면 이 옵션을 사용하고 --bootnodes 옵션으로 연결하려는 피어를 지정한다.&lt;/li&gt;
&lt;li&gt;--bootnodes : 노드가 다른 노드와 연결할 때 사용한다. 실행 중인 'enode' URL을 지정해주어야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 실행 후 콘솔에서 admin.addPeer(&quot;enode URL&quot;)을 이용해서 설정 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 노드를 실행하고 피어를 맺을 수 있다. console 명령어를 이용해서 JS console을 열었고 지정했던 api를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-08-17 오후 7.26.31.png&quot; data-origin-width=&quot;2179&quot; data-origin-height=&quot;1410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6f5h2/btsrrZOna52/sP2fAsU5tG4JUXZM0Km1U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6f5h2/btsrrZOna52/sP2fAsU5tG4JUXZM0Km1U0/img.png&quot; data-alt=&quot;위 : enode 아래 : 잠금 해제된 key&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6f5h2/btsrrZOna52/sP2fAsU5tG4JUXZM0Km1U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6f5h2%2FbtsrrZOna52%2FsP2fAsU5tG4JUXZM0Km1U0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2179&quot; height=&quot;1410&quot; data-filename=&quot;스크린샷 2023-08-17 오후 7.26.31.png&quot; data-origin-width=&quot;2179&quot; data-origin-height=&quot;1410&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 : enode 아래 : 잠금 해제된 key&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. 블록생성하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네시스 블록으로 참여자를 1번 노드의 계정만 했기 때문에 해당 블록만 mining을 진행할 수 있다.&lt;br /&gt;그러므로 1번 노드에서 miner.start()를 이용해서 블록을 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 --miner.etherbase 명령어를 안 쓰게 되면 Etherbase 관련 에러가 발생한다.&lt;br /&gt;Etherbase 관련 에러가 발생하면 miner.setEtherbase(&quot;0x를 포함한 계정&quot;)를 이용해서 지정해 주고 miner.start 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초에 실행할 때 참여자를 1명만 지정했기 때문에 혼자서 mining이 가능하지만 2~3명 이상이 되면&lt;br /&gt;&lt;b&gt;Block sealing failed, err=&quot;signed recently, must wait for others&quot;&lt;/b&gt; 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참여자가 여럿일 때 혼자서 계속 블록을 생성할 수 있는 것이 아닌 다른 참여자도 miner.start를 지정해주어야 한다.&lt;br /&gt;모두 마이닝을 시작하게 되면 2개 이상의 노드에서 번갈아가며 블록을 생성한다.&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>geth</category>
      <category>POA</category>
      <category>private network</category>
      <category>프라이빗 네트워크</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/234</guid>
      <comments>https://baekspace.tistory.com/234#entry234comment</comments>
      <pubDate>Thu, 17 Aug 2023 20:49:05 +0900</pubDate>
    </item>
    <item>
      <title>230814 - Electron 구조</title>
      <link>https://baekspace.tistory.com/233</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1. Main Process&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;2. Renderer&amp;nbsp;Process&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;3. Main-Renderer&amp;nbsp;통신&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;일렉트론 구조&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일렉트론은 크게 2가지의 프로세스를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Main Process와 Renderer Process이다.&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Main Process&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 프로세스는 package.json의 main으로 지정해두었던 main 스크립트를 실행하는 프로세스를 말한다. 메인프로세스에서 실행되는 스크립트는 웹페이지를 GUI로 표시한다. Electron 앱은 항상 하나의 메인 프로세스만을 가진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Main Process의 기능 및 역할&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;애플리케이션의 생명 주기 관리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션의 시작부터 종료까지 전체 생명 주기를 제어하고 필요한 로직을 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;브라우저 윈도우 생성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lsquo;BrowserWindow&amp;rsquo;객체를 이용하여 브라우저 창을 생성하고 웹 페이지를 로드하여, GUI로 보여준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네이티브 기능 접근&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메인 프로세스는 운영 체제의 파일 시스템, 네트워크, 하드웨어 등의 네이티브 자원에 직접 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;렌더러 프로세스와의 통신&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메인프로세스는 렌더러 프로세스와 IPC를 통해 통신한다.이를 이용해서 네이티브 기능과 웹페이지 간의 상호작용을 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Renderer Process&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더러 프로세스는 웹 페이지의 렌더링과 관련된 모든 작업을 처리하며 Chromium을 사용해서 웹페이지를 보여준다. 크로미움을 사용하기 때문에 크로미움의 멀티 프로세스, 아키텍쳐를 사용한다. 각각의 Electron 웹페이지는 자체 프로세스로 동작한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Renderer Process 기능 및 역할&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;웹 페이지 렌더링&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렌더러 프로세스는 Chrominum의 웹 렌더링 엔진을 사용하여 웹 페이지를 렌더링한다. 웹 페이지는 HTML/CSS, JS같은 웹 기술을 이용해서 UI를 구성할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;각 창마다 독립적인 프로세스&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Electron에서 각 브라우저 윈도우는 독립된 렌더러 프로세스를 가진다. 그래서 하나의 윈도우에서 오류가 발생하더라도 다른 윈도우에는 영향을 미치지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DOM과 JS API 접근&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렌더러 프로세스는 웹 브라우저와 같이 DOM을 직접 조작하고 JS API를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메인 프로세스와의 통신&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렌더러 프로세스는 메인 프로세스와 IPC를 통해 통신할 수 있으며, 이를 이용해서 네이티브 자원에 접근하거나 다른 렌더러 프로세스와 상호 작용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렌더러 프로세스는 직접 네이티브 자원에 접근할 수 없고 메인 프로세스를 통해 요청해야 하기 때문에 악의적인 코드의 실행을 제한할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Main-Renderer 통신&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인프로세스와 렌더러 프로세스가 서로 통신하는 방법으로 remote 모듈과 IPC 모듈을 이용할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;remote 모듈&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 프로세스에서만 사용가능한 API들을 렌더러 프로세스에서 이용할 수 있게 해주는 모듈이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IPC 모듈&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ipcMain&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;main 프로세스에서 renderer 프로세스로 비동기 통신을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ipcRenderer&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;renderer 프로세스에서 main 프로세스로 비동기 통신을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;IPC 모듈을 이용하여 메세지 주고 받기&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모듈 import&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1691996835801&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//main Process
const { ipcMain } = require(&quot;electron&quot;)

//renderer Process
const { ipcRenderer } = require(&quot;electron&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;br /&gt;main Process&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1691996926558&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이벤트 수신을 위한 등록
ipcMain.on(&quot;eventName&quot;, (event, args) =&amp;gt;{
	//코드 작성
	event.reply(&quot;responseEventName&quot;, responseArgs)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;renderer Process&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1691996946734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//main 프로세스로 메세지 전송 
ipcRenderer.send(&quot;eventName&quot;, args)

//응답 대기
ipcRenderer.on(&quot;responseEventName&quot;, (event, args)=&amp;gt;{
	//응답 처리
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;통신을 이용하여 할 수 있는 기능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;b&gt;창 관리&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 렌더러 프로세스에서 사용자의 액션에 따라서 새 창을 열고 기존 창을 닫는 창 관리 작업을 할 수 있다.&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;b&gt;네이티브 자원 조작&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 메인 프로세스는 OS 수준의 네이티브 자원에 접근할 수 있어서 렌더러 프로세스에서 메인 프로세스로 요청을 보내 파일 시스템 작업, 네트워크 통신, 하드웨어 정보 수집 등을 할 수 있다.&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;b&gt;상태 공유&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 여러 렌더러 프로세스간에 상태를 공유해야할 때, 메인 프로세스를 중앙 상태 저장소로 이용하여 데이터를 공유할 수 있다.&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;b&gt;보안 제어 :&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;일부 보안에 민감한 작업은 메인 프로세스에서만 동작이 가능하고 이러한 작업을 렌더러 프로세스에서는 요청하고 응답 받을 수 있다.&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;b&gt;네이티브 메뉴 및 알림&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 렌더러 프로세스에서 특정 이벤트 발생 시, 메인 프로세스에 알림을 보내 네이티브 메뉴를 변경하거나 시스템 알림을 표시할 수 있다.&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;b&gt;외부 프로세스 관리 :&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;렌더러 프로세스에서 요청을 통해 메인프로세스가 외부 프로세스를 시작하고 제어할 수 있도록 한다.&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;b&gt;사용자 설정 관리 :&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용자 설정을 메인 프로세스에서 관리하고, 렌더러 프로세스가 해당 설정을 읽고 변경할 수 있도록 설정 가능하다.&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;b&gt;플러그인 또는 확장 기능&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 렌더러 프로세스에서 발생하는 특정 이벤트에 대한 응답으로 메인 프로세스에서 추가 기능을 수행하도록 하여 플러그인, 또는 확장 기능을 구현할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 프로세스와 렌더러 프로세스 간의 통신은 Electron 애플리케이션의 복잡한 작업을 가능하게 하고, 사용자 인터페이스와 백엔드 로직간의 상호작용을 지원한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Process.png&quot; data-origin-width=&quot;3161&quot; data-origin-height=&quot;1757&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfcHp9/btsq2iIskFs/fSNr6niCvm3nhwALZHZ61k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfcHp9/btsq2iIskFs/fSNr6niCvm3nhwALZHZ61k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfcHp9/btsq2iIskFs/fSNr6niCvm3nhwALZHZ61k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfcHp9%2Fbtsq2iIskFs%2FfSNr6niCvm3nhwALZHZ61k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3161&quot; height=&quot;1757&quot; data-filename=&quot;Process.png&quot; data-origin-width=&quot;3161&quot; data-origin-height=&quot;1757&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>electron</category>
      <category>ipc 통신</category>
      <category>일렉트론</category>
      <category>일렉트론 구조</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/233</guid>
      <comments>https://baekspace.tistory.com/233#entry233comment</comments>
      <pubDate>Mon, 14 Aug 2023 16:18:41 +0900</pubDate>
    </item>
    <item>
      <title>CSS를 이용해서 Element를 숨기는 방법</title>
      <link>https://baekspace.tistory.com/232</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;br /&gt;Element 숨기기&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Element를 숨기는 방법은 여러 방법이 있지만 그중에서 비용이 적게 들고 쉽게 할 수 있는 방법으로 CSS를 이용하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 형태는 아래와 같다.&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;yLQdbxO&quot; data-user=&quot;JJJ-the-animator&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/JJJ-the-animator/pen/yLQdbxO&quot;&gt;
  Untitled&lt;/a&gt; by JJJ (&lt;a href=&quot;https://codepen.io/JJJ-the-animator&quot;&gt;@JJJ-the-animator&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 2번을 숨길 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS를 이용해서 Element를 숨기는 대표적인 방법 3가지는 아래와 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. display 속성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;display : none &lt;/b&gt;을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;PoxrmBL&quot; data-user=&quot;JJJ-the-animator&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/JJJ-the-animator/pen/PoxrmBL&quot;&gt;
  Untitled&lt;/a&gt; by JJJ (&lt;a href=&quot;https://codepen.io/JJJ-the-animator&quot;&gt;@JJJ-the-animator&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. visibility 속성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;visibility : hidden &lt;/b&gt;을 이용한다. ( 2번 자리에 마우스를 올려보세요. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;poQXeBm&quot; data-user=&quot;JJJ-the-animator&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/JJJ-the-animator/pen/poQXeBm&quot;&gt; Untitled&lt;/a&gt; by JJJ (&lt;a href=&quot;https://codepen.io/JJJ-the-animator&quot;&gt;@JJJ-the-animator&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. opacity 속성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;opacity: 0&lt;/b&gt; 을 이용한다. ( 2번 자리에 마우스를 올려보세요. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;zYMVwLw&quot; data-user=&quot;JJJ-the-animator&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/JJJ-the-animator/pen/zYMVwLw&quot;&gt;
  Untitled&lt;/a&gt; by JJJ (&lt;a href=&quot;https://codepen.io/JJJ-the-animator&quot;&gt;@JJJ-the-animator&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hidden을 제외한 아무것도 설정하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 보면 특징이 보이는데, 각 속성을 이용했을 때의 특징은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. display 속성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 페이지에서 요소를 아예 제거한다. 그래서 해당 요소가 차지하는 공간도 없어지므로 옆에 있던 3번이 1번 옆으로 붙게 된다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. visibility 속성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 페이지에서 요소가 제거되는 것은 아니지만 보이지 않는다. 마우스 이벤트가 작동하지 않는다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. opacity 속성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 페이지에서 요소가 제거되는 것은 아니지만 보이지 않는다. 2번 visibility와 비슷하지만 투명도만 0이 된 것이기 때문에 마우스 이벤트가 적용이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>HTML &amp;amp; CSS</category>
      <category>css로 엘리먼트 숨기기</category>
      <category>element 숨기기</category>
      <category>엘리먼트 숨기기</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/232</guid>
      <comments>https://baekspace.tistory.com/232#entry232comment</comments>
      <pubDate>Fri, 11 Aug 2023 18:54:05 +0900</pubDate>
    </item>
    <item>
      <title>230811 - Electron.js 기초</title>
      <link>https://baekspace.tistory.com/231</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1. Eletron.js&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;2. 사용하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Electron.js&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일렉트론은 일반적인 웹 기술을 이용하여 데스크톱 애플리케이션을 만들기 위한 프레임워크이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JS, HTML/CSS를 이용하여 크로스 플랫폼 데스크톱 애플리케이션을 개발할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Electron은 Chrominum 웹 브라우저와 Node.js 런타임을 결합하여 사용하는 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;크로스 플랫폼&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크로스 플랫폼은 하나의 코드를 이용하여 여러 OS 환경, 디바이스에서 실행될 수 있게 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 React-Native를 이용해서 하나의 코드로 IOS, Android 환경에서 사용하는 애플리케이션을 빌드를 했던 것처럼 Electron도 하나의 코드를 이용해서 MacOS, Window, Linux OS에서 사용할 수 있는 애플리케이션을 만드는 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 기반&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Electron은 웹을 기반으로 되어있기 때문에 웹페이지를 만들어 보았던 개발자가 쉽게 할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;네이티브 기능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 런타임을 내장하고 있기 때문에 시스템 리소스에 접근이 가능하여 기본적인 OS 네이티브 기능을 활용할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;대표적인 애플리케이션&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Visual Studio Code&lt;/li&gt;
&lt;li&gt;Slack&lt;/li&gt;
&lt;li&gt;Discode&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등이 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용하기 전에 Node가 설치되어 있어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node가 설치되어 있다면 아래를 진행하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 기본 설치&lt;/h3&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;# electron-app 디렉토리 만들기
npx create-react-app electron-app&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRA를 진행한다. electron-app이라는 디렉토리를 생성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 electron-app으로 진입한다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;cd electron-app&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;electron을 설치한다.&lt;/p&gt;
&lt;pre class=&quot;q&quot;&gt;&lt;code&gt;npm install -save-dev electron&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. package.json 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scripts와 main을 지정해주어야 한다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
        &quot;start&quot;: &quot;react-scripts start&quot;,
        &quot;build&quot;: &quot;react-scripts build&quot;,
        &quot;test&quot;: &quot;react-scripts test&quot;,
        &quot;eject&quot;: &quot;react-scripts eject&quot;,
        &quot;electron&quot;: &quot;electron .&quot; 
    },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;electron . &amp;ldquo; 명령어를 이용해서 개발 모드에서 앱을 열 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;&lt;b&gt;npm run electron&lt;/b&gt;&amp;lsquo;으로 electron . 명령어를 사용하기 위해서 scripts에 지정을 해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 main을 지정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;main&amp;rdquo;은 electron 애플리케이션의 entry point이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성한 코드가 일렉트론 앱에서 실행될 때 &lt;b&gt;entery point인 main.js&lt;/b&gt;를 이용해서 열리고 main.js에 앱에 열릴 내용을 지정해 준다고 생각하면 된다. 기본적인 앱의 설정 내용들은 여기서 지정한다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;main&quot;: &quot;src/main.js&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 작성되는 package.json은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;name&quot;: &quot;react-electron&quot;,
    &quot;version&quot;: &quot;0.1.0&quot;,
    &quot;private&quot;: true,
    &quot;main&quot;: &quot;src/main.js&quot;, // 추가
    &quot;dependencies&quot;: {
        &quot;@testing-library/jest-dom&quot;: &quot;^5.17.0&quot;,
        &quot;@testing-library/react&quot;: &quot;^13.4.0&quot;,
        &quot;@testing-library/user-event&quot;: &quot;^13.5.0&quot;,
        &quot;react&quot;: &quot;^18.2.0&quot;,
        &quot;react-dom&quot;: &quot;^18.2.0&quot;,
        &quot;react-scripts&quot;: &quot;5.0.1&quot;,
        &quot;web-vitals&quot;: &quot;^2.1.4&quot;
    },
    &quot;scripts&quot;: {
        &quot;start&quot;: &quot;react-scripts start&quot;,
        &quot;build&quot;: &quot;react-scripts build&quot;,
        &quot;test&quot;: &quot;react-scripts test&quot;,
        &quot;eject&quot;: &quot;react-scripts eject&quot;,
        &quot;electron&quot;: &quot;electron .&quot; // 추가
    },
    &quot;eslintConfig&quot;: {
        &quot;extends&quot;: [
            &quot;react-app&quot;,
            &quot;react-app/jest&quot;
        ]
    },
    &quot;browserslist&quot;: {
        &quot;production&quot;: [
            &quot;&amp;gt;0.2%&quot;,
            &quot;not dead&quot;,
            &quot;not op_mini all&quot;
        ],
        &quot;development&quot;: [
            &quot;last 1 chrome version&quot;,
            &quot;last 1 firefox version&quot;,
            &quot;last 1 safari version&quot;
        ]
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. src/main.js&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src 디렉토리 안에 main.js를 추가하여 앱의 기본 설정 및 앱을 열었을 때 바라보게 되는 파일이나 주소를 지정해 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 electron의 공식문서에서 가져온 내용이다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// main.js

// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('path')

const createWindow = () =&amp;gt; {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // and load the index.html of the app.
  mainWindow.loadFile('index.html')

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() =&amp;gt; {
  createWindow()

  app.on('activate', () =&amp;gt; {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () =&amp;gt; {
  if (process.platform !== 'darwin') app.quit()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mainWindow.loadFile(&amp;rdquo;index.html&amp;rdquo;)에 의해서 index.html 파일을 읽는다는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 이용해서 배포된 주소로 앱을 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;// index.html 파일 열기
mainWindow.loadFile(&quot;index.html&quot;) 

//https://example.com 웹사이트를 앱으로 만들 수 있다.
mainWindow.loadURL(&quot;https://example.com&quot;) &lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 실행하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main.js와 loadURL을 지정한 후에 지정해 두었던 scripts를 이용해서 app을 볼 수 있다. ( 빌드되어 파일로 나오는 것이 아니라 확인하는 것이다. 실제 설치 파일로 추출하기 위해선 추가적인 작업이 필요하다.)&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;npm run electron&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAR2lT/btsqZ2jXT6M/Ho7WkMfaNNWawIt9ZzM891/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAR2lT/btsqZ2jXT6M/Ho7WkMfaNNWawIt9ZzM891/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;987&quot; data-filename=&quot;스크린샷 2023-08-11 오후 5.53.52.png&quot; style=&quot;width: 45.8109%; margin-right: 10px;&quot; data-widthpercent=&quot;46.35&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAR2lT/btsqZ2jXT6M/Ho7WkMfaNNWawIt9ZzM891/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAR2lT%2FbtsqZ2jXT6M%2FHo7WkMfaNNWawIt9ZzM891%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1237&quot; height=&quot;987&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RU5c5/btsqZbaGdPl/nEwkv95oGhmCQ6NNpmZvU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RU5c5/btsqZbaGdPl/nEwkv95oGhmCQ6NNpmZvU1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;943&quot; data-filename=&quot;스크린샷 2023-08-11 오후 5.53.54.png&quot; style=&quot;width: 53.0263%;&quot; data-widthpercent=&quot;53.65&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RU5c5/btsqZbaGdPl/nEwkv95oGhmCQ6NNpmZvU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRU5c5%2FbtsqZbaGdPl%2FnEwkv95oGhmCQ6NNpmZvU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1368&quot; height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 web으로는 주소창 등 브라우저의 기본적인 것이 보이지만 electron으로는 브라우저의 기능을 제외하고 내용만 확인이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <category>electron</category>
      <category>일렉트론</category>
      <category>일렉트론 기본</category>
      <category>일렉트론 기초</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/231</guid>
      <comments>https://baekspace.tistory.com/231#entry231comment</comments>
      <pubDate>Fri, 11 Aug 2023 18:08:37 +0900</pubDate>
    </item>
    <item>
      <title>230728 - Hardhat</title>
      <link>https://baekspace.tistory.com/230</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_hardhaticon.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0miGg/btspkLDTLfo/XZPl38ciAOczstJ5mUyKO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0miGg/btspkLDTLfo/XZPl38ciAOczstJ5mUyKO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0miGg/btspkLDTLfo/XZPl38ciAOczstJ5mUyKO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0miGg%2FbtspkLDTLfo%2FXZPl38ciAOczstJ5mUyKO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;300&quot; data-filename=&quot;edited_hardhaticon.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;br /&gt;오늘 내가 배운 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size24&quot;&gt;1. Hardhat&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;Hardhat이란?&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하드햇이란 트리플과 비슷한 역할을 하는 Ethereum smartcontract를 개발, 컴파일, 테스트, 디버깅을 하기 위한 도구이자 프레임워크이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 설치&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;# hardhat 기본설치
$ npm init
$ npm install --save-dev hardhat&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ npx hardhat&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npx를 이용하여 프로젝트를 생성할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-28 오전 11.21.33.png&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk4e6C/btspfOOHTcm/4CQtEGYXhmtERY6dy0vzbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk4e6C/btspfOOHTcm/4CQtEGYXhmtERY6dy0vzbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk4e6C/btspfOOHTcm/4CQtEGYXhmtERY6dy0vzbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk4e6C%2FbtspfOOHTcm%2F4CQtEGYXhmtERY6dy0vzbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1074&quot; height=&quot;610&quot; data-filename=&quot;스크린샷 2023-07-28 오전 11.21.33.png&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JS뿐 아니라 TS에 대한 기본적인 설정이 되어 있는 프로젝트를 생성할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-28 오전 11.23.46.png&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvag8J/btspd4qZbkX/rkQCSq5Kd9JnSxr0i7xgbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvag8J/btspd4qZbkX/rkQCSq5Kd9JnSxr0i7xgbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvag8J/btspd4qZbkX/rkQCSq5Kd9JnSxr0i7xgbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvag8J%2Fbtspd4qZbkX%2FrkQCSq5Kd9JnSxr0i7xgbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1906&quot; height=&quot;580&quot; data-filename=&quot;스크린샷 2023-07-28 오전 11.23.46.png&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되면 아래와 같은 디렉토리가 생긴다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-28 오전 11.29.51.png&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNUJes/btspfOOHZo6/eFKQN2i1PyrisTNsJJ4KOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNUJes/btspfOOHZo6/eFKQN2i1PyrisTNsJJ4KOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNUJes/btspfOOHZo6/eFKQN2i1PyrisTNsJJ4KOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNUJes%2FbtspfOOHZo6%2FeFKQN2i1PyrisTNsJJ4KOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;462&quot; height=&quot;454&quot; data-filename=&quot;스크린샷 2023-07-28 오전 11.29.51.png&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 명령어 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료된 후&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ npx hardhat&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어를 다시 사용하게 되면 하드햇에서 제공하는 명령어나 옵션을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진!!!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 13가지의 명령어가 있고 각 명령어는 아래와 같이 사용한다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;# 초기화
$ npx hardhat clean
$ npx hardhat [명령어]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 각 명령어의 역할&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;check : 사용자 정의 확인 작업을 수행한다. hardhat.config 파일에 지정되어 있는 로직을 이용해서 검사할 수 있다.&lt;/li&gt;
&lt;li&gt;clean : Hardhat의 캐시를 지우고 모든 컴파일된 스마트 컨트랙트를 삭제한다. 프로젝트를 깨끗한 상태로 재설정하는 데 사용한다.&lt;/li&gt;
&lt;li&gt;compile : 프로젝트의 모든 스마트 컨트랙트를 컴파일한다. 컴파일 과정에서는 스마트 컨트랙트의 소스코드를 EVM 바이트코드로 변환한다.&lt;/li&gt;
&lt;li&gt;console : Hardhat의 콘솔을 연다. 스마트 컨트랙트와 직접 상호 작용할 수 있다.&lt;/li&gt;
&lt;li&gt;coverage: 테스트에 대한 코드 커버리지 보고서를 생성한다. 어떤 코드가 테스트 코드에 포함되었는지, 누락되었는지 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;flatten: 스마트 컨트랙트와 그 종속성을 평면화하여 출력한다. 여러 소스 파일에 걸쳐 분산된 스마트 컨트랙트를 단일 파일 합치는 과정이 있다.&lt;/li&gt;
&lt;li&gt;gas-reporter:merge : 트랜잭션의 가스사용량을 추적하고 보고하는 도구이다. merge를 통해서 여러 가스보고서를 병합한다.&lt;/li&gt;
&lt;li&gt;help : 도움말을 출력해 준다. 특정 명령어에 대한 도움말은 &quot;npx hardhat help [명령어]&quot;를 이용한다.&lt;/li&gt;
&lt;li&gt;node: Hardhat Network위에 JSON-RPC 서버를 시작한다. 이를 이용해서 로컬에서 블록체인 시뮬레이션을 실행할 수 있다.&lt;/li&gt;
&lt;li&gt;run : 프로젝트를 컴파일한 후 사용자 정의 스크립트를 실행한다.&lt;/li&gt;
&lt;li&gt;test : test 디렉토리에 지정되어 있는 파일을 이용하여 Mocha테스트를 진행한다.&lt;/li&gt;
&lt;li&gt;typechain : 컴파일된 스마트 컨트랙트에 대한 Typechain 타이핑을 생성한다. TypeScript에서 타입 안전성을 확보할 수 있다.&lt;/li&gt;
&lt;li&gt;verify : Etherscan에서 컨트랙트를 점증한다. 컨트랙트가 소스코드와 일치하는지 확인하는 과정이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 각 옵션의 역할&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;--config : 사용할 Hardhat config 파일을 지정한다. Default로 현재 작업 디렉토리의 hardhat.config를 이용한다.&lt;/li&gt;
&lt;li&gt;--emoji : hardhat 로그메시지에서 이모티콘을 사용하도록 설정한다.&lt;/li&gt;
&lt;li&gt;--flamegraph : hardhat작업의 CPU 프로필을 수집하고, 그 결과를 flamegraph 형태로 출력한다.&lt;/li&gt;
&lt;li&gt;--help : 도움말 메시지를 보여준다.&lt;/li&gt;
&lt;li&gt;--max-memory : hardhat이 사용할 수 있는 최대 메모리 양을 MB eksdnlfh tjfwjdgksek.&lt;/li&gt;
&lt;li&gt;--network : 이 옵션은 연결할 Ethereum 네트워크를 설정한다. config 파일에 정의된 네트워크를 이용해야 한다.&lt;/li&gt;
&lt;li&gt;--show-stack-traces : 에러가 발생했을 때 스택 추적을 보여주도록 설정한다.&lt;/li&gt;
&lt;li&gt;--tsconfig: 사용할 TypeScript config를 지정한다.&lt;/li&gt;
&lt;li&gt;--typecheck : script, test의 TS 타입 검사를 활성화한다.&lt;/li&gt;
&lt;li&gt;--verbose : Hardhat의 상세 로그를 활성화한다.&lt;/li&gt;
&lt;li&gt;--version : 현재 사용 중인 hardhat의 버전을 출력한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. Hardhat 네트워크 지정하기&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;// hardhat.config.ts

import { HardhatUserConfig } from &quot;hardhat/config&quot;
import &quot;@nomicfoundation/hardhat-toolbox&quot;

const config: HardhatUserConfig = {
    solidity: &quot;0.8.18&quot;,
    networks: {
        ganache: {
            url: &quot;http://127.0.0.1:8545&quot;, // Ganache URL
            chainId: 1337,
            accounts: require(&quot;./accounts.json&quot;).privateKey,
        },
        arbitrum: {
            url: &quot;&quot;, // arbitrum-goerli 네트워크 infura
            chainId: 421613,
            accounts: require(&quot;./accounts.json&quot;).privateKey,
        },
        goerli: {
            url: &quot;&quot;, // ethereum-goerli 네트워크 infura
            chainId: 5,
            accounts: require(&quot;./accounts.json&quot;).privateKey,
        },
    },
}

export default config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hardhat network를 설정하기 위해서 위에 같이 config를 이용하여 네트워크를 관리할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. Hardhat Scripts&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hardhat에 scripts 디렉토리의 scripts 파일을 이용하여 Ethereum 네트워크와 상호작용을 하거나 컨트렉트 배포와 같은 복잡한 작업을 자동화할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;//scripts/deploy.ts

import { ethers } from &quot;hardhat&quot;

async function main() {
    const currentTimestampInSeconds = Math.round(Date.now() / 1000)
    const unlockTime = currentTimestampInSeconds + 60

    const lockedAmount = ethers.parseEther(&quot;0.001&quot;)

    const lock = await ethers.deployContract(&quot;Lock&quot;, [unlockTime], {
        value: lockedAmount,
    })

    await lock.waitForDeployment()

    console.log(
        `Lock with ${ethers.formatEther(lockedAmount)}ETH and unlock timestamp ${unlockTime} deployed to ${lock.target}`
    )
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) =&amp;gt; {
    console.error(error)
    process.exitCode = 1
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 프로젝트 설치 시 예시로 있는 scripts 파일이다.&lt;br /&gt;배포를 위한 추가적인 scripts를 작성하여 배포를 진행할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8. 배포하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포하기 위해서는 run 명령어가 필요하다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;$ npx hardhat run --network goerli scripts/[스크립트파일이름]

#예시
$ npx hardhat run --network goerli scripts/deploy.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 과정을 통해서 hardhat을 이용하여 지정해 놓은 goerli 네트워크에 배포를 진행할 수 있게 된다.&lt;/p&gt;</description>
      <category>시작/TIL(Today I Learned)</category>
      <author>백씨네</author>
      <guid isPermaLink="true">https://baekspace.tistory.com/230</guid>
      <comments>https://baekspace.tistory.com/230#entry230comment</comments>
      <pubDate>Fri, 28 Jul 2023 13:59:35 +0900</pubDate>
    </item>
  </channel>
</rss>