<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>big-choi.log</title>
        <link>https://velog.io/</link>
        <description>Software Engineer</description>
        <lastBuildDate>Sat, 17 Jan 2026 13:53:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>big-choi.log</title>
            <url>https://velog.velcdn.com/images/boseong-choi/profile/5340b7fb-21e2-46e2-9a73-a1e26e682598/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. big-choi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/boseong-choi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[HTTP 통신과 API 설계의 기초]]></title>
            <link>https://velog.io/@boseong-choi/HTTP-%ED%86%B5%EC%8B%A0%EA%B3%BC-API-%EC%84%A4%EA%B3%84%EC%9D%98-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@boseong-choi/HTTP-%ED%86%B5%EC%8B%A0%EA%B3%BC-API-%EC%84%A4%EA%B3%84%EC%9D%98-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sat, 17 Jan 2026 13:53:24 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="🌐-http-통신과-api-설계의-기초">🌐 HTTP 통신과 API 설계의 기초</h2>
<p>클라이언트와 서버가 데이터를 주고받는 가장 기본적인 약속인 <strong>HTTP</strong>에 대해 정리해보려 합니다. 프론트엔드 개발을 하다 보면 API 명세서를 보고 <code>axios</code>나 <code>fetch</code>로 데이터를 요청하게 되는데요. 이때 우리가 사용하는 메서드나 상태 코드가 어떤 의미를 갖는지 명확히 이해하는 것이 중요합니다.</p>
<hr>
<h3 id="1️⃣-http-메서드--이-요청의-목적이-무엇인가요">1️⃣ HTTP 메서드 = 이 요청의 목적이 무엇인가요?</h3>
<p>HTTP(HyperText Transfer Protocol)는 브라우저와 서버 간의 데이터를 주고받기 위한 규칙입니다. 여기서 <strong>메서드(Method)</strong>는 서버에게 보내는 &#39;동사&#39;와 같아서, 해당 요청으로 무엇을 하고 싶은지를 나타냅니다.</p>
<table>
<thead>
<tr>
<th>메서드</th>
<th>의미</th>
<th>특징</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>GET</strong></td>
<td>데이터 조회</td>
<td>서버 상태를 변경하지 않음 (안전함)</td>
<td><code>GET /users/1</code> (1번 유저 조회)</td>
</tr>
<tr>
<td><strong>POST</strong></td>
<td>데이터 생성</td>
<td>서버에 새로운 리소스를 만듦</td>
<td><code>POST /users</code> (새 유저 등록)</td>
</tr>
<tr>
<td><strong>PUT</strong></td>
<td>전체 수정</td>
<td>기존 데이터를 새로운 데이터로 덮어씀</td>
<td><code>PUT /users/1</code> (정보 전체 교체)</td>
</tr>
<tr>
<td><strong>PATCH</strong></td>
<td>부분 수정</td>
<td>변경이 필요한 필드만 수정함</td>
<td><code>PATCH /users/1</code> (이메일만 변경)</td>
</tr>
<tr>
<td><strong>DELETE</strong></td>
<td>데이터 삭제</td>
<td>지정한 리소스를 삭제함</td>
<td><code>DELETE /users/1</code> (유저 삭제)</td>
</tr>
</tbody></table>
<blockquote>
<p>프론트엔드에서 <code>axios.get</code>이나 <code>axios.post</code>를 선택하는 기준은 이 설계 의도에 있습니다.</p>
</blockquote>
<hr>
<h3 id="2️⃣-상태-코드-status-code--서버로부터-온-신호">2️⃣ 상태 코드 (Status Code) = 서버로부터 온 신호</h3>
<p>상태 코드는 서버가 요청을 처리한 결과를 숫자로 요약해서 알려주는 신호입니다. 프론트엔드에서는 이 코드를 바탕으로 사용자에게 성공 메시지를 보여줄지, 혹은 에러 페이지로 보낼지 결정하게 됩니다.</p>
<ul>
<li><strong>200 OK</strong>: 요청 성공! (조회나 수정이 잘 되었을 때)</li>
<li><strong>201 Created</strong>: 새로운 리소스 생성 성공 (주로 POST 요청 후 응답)</li>
<li><strong>400 Bad Request</strong>: 요청 자체가 잘못됨 (필수 값 누락, 형식 오류 등)</li>
<li><strong>401 Unauthorized</strong>: 인증되지 않음 (로그인이 필요하거나 토큰이 만료됨)</li>
<li><strong>403 Forbidden</strong>: 권한 없음 (로그인은 했지만 접근 권한이 없는 페이지)</li>
<li><strong>404 Not Found</strong>: 리소스 없음 (잘못된 URL이거나 삭제된 데이터)</li>
<li><strong>500 Internal Server Error</strong>: 서버 내부 오류 (서버의 에러)</li>
</ul>
<hr>
<h3 id="3️⃣-request와-response의-구조">3️⃣ Request와 Response의 구조</h3>
<p>데이터 통신은 크게 &#39;요청(Request)&#39;과 &#39;응답(Response)&#39;으로 나뉩니다.</p>
<h4 id="3-1-request-클라이언트-→-서버">3-1. Request (클라이언트 → 서버)</h4>
<p>클라이언트가 서버로 보내는 메시지입니다. 메서드, URL, 헤더, 그리고 데이터를 담는 <strong>Body</strong>로 구성됩니다.</p>
<pre><code class="language-json">// POST /login 요청 예시
Headers: { &quot;Content-Type&quot;: &quot;application/json&quot; }
Body: { &quot;email&quot;: &quot;test@test.com&quot;, &quot;password&quot;: &quot;1234&quot; }
</code></pre>
<h4 id="3-2-response-서버-→-클라이언트">3-2. Response (서버 → 클라이언트)</h4>
<p>서버가 처리 후 보내주는 메시지입니다. 상태 코드와 헤더, 그리고 서버가 돌려주는 데이터인 <strong>Body</strong>가 포함됩니다.</p>
<pre><code class="language-json">// 서버의 응답 예시
{ &quot;id&quot;: 1, &quot;name&quot;: &quot;Choi&quot; }
</code></pre>
<p><code>axios</code>를 사용하면 <code>response.status</code>로 상태 코드를, <code>response.data</code>로 바디 데이터를 쉽게 꺼내 쓸 수 있습니다.</p>
<hr>
<h3 id="4️⃣-json-직렬화와-역직렬화">4️⃣ JSON 직렬화와 역직렬화</h3>
<p>데이터를 전송할 때는 자바스크립트 객체 그대로 보낼 수 없습니다. 그래서 <strong>JSON</strong>이라는 문자열 형식을 빌려 통신합니다.</p>
<ul>
<li><strong>직렬화(Serialization)</strong>: <code>JSON.stringify(obj)</code>를 통해 JS 객체를 문자열로 변환합니다. 서버로 보낼 때 사용해요.</li>
<li><strong>역직렬화(Deserialization)</strong>: <code>JSON.parse(string)</code>를 통해 문자열을 다시 JS 객체로 변환합니다. 서버 응답을 사용할 때 필요하죠.</li>
</ul>
<blockquote>
<p>Axios는 기본적으로 JavaScript 객체를 JSON으로 자동 직렬화(Serialization)하여 서버로 전송하고, 서버에서 온 JSON 응답을 객체로 자동 역직렬화(Deserialization/Parsing)하여 제공하는 기능을 갖추고 있습니다. </p>
</blockquote>
<hr>
<h3 id="5️⃣-통신-전체-흐름-한눈에-보기">5️⃣ 통신 전체 흐름 한눈에 보기</h3>
<p>프론트엔드가 <code>axios.post(&#39;/login&#39;, data)</code>를 호출하면 어떤 일이 벌어질까요?</p>
<ol>
<li><strong>프론트</strong>: 데이터를 JSON으로 직렬화하여 HTTP 요청을 보냅니다.</li>
<li><strong>네트워크</strong>: Method, URL, Body 등이 담긴 패킷이 서버로 전달됩니다.</li>
<li><strong>서버</strong>: 요청을 받아 JSON을 역직렬화하고 비즈니스 로직을 처리합니다.</li>
<li><strong>서버 응답</strong>: 결과 데이터와 상태 코드를 담아 다시 프론트로 보냅니다.</li>
<li><strong>프론트</strong>: 응답받은 데이터를 역직렬화하여 화면에 렌더링합니다.</li>
</ol>
<hr>
<h3 id="6️⃣-에러-응답-구조-통일의-중요성">6️⃣ 에러 응답 구조 통일의 중요성</h3>
<p>협업 시 서버에서 내려주는 에러 구조가 제각각이면 프론트엔드 코드는 매우 복잡해집니다.</p>
<ul>
<li><strong>문제 상황</strong>: 어떤 API는 <code>{ &quot;msg&quot;: &quot;error&quot; }</code>, 어떤 API는 <code>{ &quot;error_message&quot;: &quot;error&quot; }</code>라고 준다면?</li>
<li><strong>해결 방안</strong>: 아래와 같이 에러 구조를 약속(규약)하는 것이 좋습니다.</li>
</ul>
<pre><code class="language-json">{
  &quot;code&quot;: &quot;AUTH_001&quot;,
  &quot;message&quot;: &quot;로그인이 필요합니다.&quot;
}
</code></pre>
<p>이렇게 구조가 통일되면 공통 에러 처리 로직을 만들기 수월해지고, 사용자에게 일관된 UX를 제공할 수 있으며, 코드의 유지보수성도 비약적으로 향상됩니다.</p>
<hr>
<h3 id="🔚-마치며">🔚 마치며</h3>
<p>오늘 정리한 HTTP 메서드, 상태 코드, 그리고 JSON 통신 흐름은 웹 개발의 뼈대와 같습니다. 이 기초가 탄탄해야 더 복잡한 비동기 처리나 상태 관리도 수월하게 해낼 수 있습니다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI가 엔트로픽의 업무 방식을 어떻게 변화시켰나? (번역)
]]></title>
            <link>https://velog.io/@boseong-choi/AI%EA%B0%80-%EC%97%94%ED%8A%B8%EB%A1%9C%ED%94%BD%EC%9D%98-%EC%97%85%EB%AC%B4-%EB%B0%A9%EC%8B%9D%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%B3%80%ED%99%94%EC%8B%9C%EC%BC%B0%EB%82%98-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@boseong-choi/AI%EA%B0%80-%EC%97%94%ED%8A%B8%EB%A1%9C%ED%94%BD%EC%9D%98-%EC%97%85%EB%AC%B4-%EB%B0%A9%EC%8B%9D%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%B3%80%ED%99%94%EC%8B%9C%EC%BC%B0%EB%82%98-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Thu, 15 Jan 2026 03:55:10 GMT</pubDate>
            <description><![CDATA[<p>아래 내용은 지난 12월 엔트로픽이 발간한 내용에 대한 요약 및 번역본입니다.</p>
<hr>
<h2 id="개요">개요</h2>
<p>Anthropic은 2025년 8월, 132명의 엔지니어와 연구원을 대상으로 설문조사를 실시하고, 53명과 심층 인터뷰를 진행했으며, 내부 Claude Code 사용 데이터를 분석하여 AI가 업무를 어떻게 변화시키고 있는지 조사했습니다.</p>
<hr>
<h2 id="핵심-발견-사항">핵심 발견 사항</h2>
<h3 id="설문조사-데이터">설문조사 데이터</h3>
<ol>
<li><strong>Claude 주요 사용처</strong>: 코드 오류 수정(디버깅)과 코드베이스 이해가 가장 일반적</li>
<li><strong>생산성 향상</strong>: 직원들은 업무의 약 60%에서 Claude를 사용하며, 50% 생산성 향상을 보고 (1년 전 대비 2~3배 증가)</li>
<li><strong>새로운 업무 창출</strong>: Claude 지원 업무의 27%는 AI 없이는 수행하지 않았을 작업 (스케일링 프로젝트, 편의 도구 제작, 탐색적 작업 등)</li>
<li><strong>완전 위임 비율</strong>: 대부분의 직원이 Claude를 자주 사용하지만, 완전히 위임할 수 있는 업무는 0~20% 정도로 보고</li>
</ol>
<h3 id="정성적-인터뷰">정성적 인터뷰</h3>
<ol>
<li><strong>AI 위임에 대한 직관 발달</strong>: 검증이 쉬운 작업, 위험도가 낮은 작업, 지루한 작업을 위임하는 경향. 신뢰 수준이 점진적으로 증가</li>
<li><strong>스킬 확장과 우려</strong>: &quot;풀스택&quot;으로 영역 확장 가능해졌지만, 깊이 있는 기술 역량 퇴화에 대한 우려도 존재</li>
<li><strong>코딩 장인정신에 대한 변화</strong>: 일부는 AI 지원을 환영하고 결과물에 집중, 다른 일부는 직접 코딩하는 것을 그리워함</li>
<li><strong>직장 내 사회적 역학 변화</strong>: Claude가 동료에게 가던 질문의 첫 번째 창구가 됨. 멘토링과 협업 기회 감소 우려</li>
<li><strong>커리어 불확실성</strong>: 단기적으로는 낙관적이나 장기적으로는 불확실. &quot;매일 출근해서 스스로를 실직시키는 느낌&quot;이라는 의견도</li>
</ol>
<h3 id="claude-code-사용-트렌드">Claude Code 사용 트렌드</h3>
<ol>
<li><strong>더 복잡한 작업을 더 자율적으로 처리</strong>: 6개월 전에는 평균 10개 작업 후 사람 개입 필요 → 현재는 약 20개 작업까지 자율 처리</li>
<li><strong>&quot;페이퍼컷&quot; 수정</strong>: Claude Code 작업의 8.6%가 유지보수성을 위한 리팩토링 등 사소하지만 삶의 질을 개선하는 작업</li>
<li><strong>모두가 &quot;풀스택&quot;화</strong>: 보안팀은 익숙하지 않은 코드 분석, 연구팀은 프론트엔드 시각화 구축 등 각 팀이 핵심 전문성 외 영역으로 확장</li>
</ol>
<hr>
<h2 id="상세-발견-사항">상세 발견 사항</h2>
<h3 id="어떤-코딩-작업에-claude를-사용하는가">어떤 코딩 작업에 Claude를 사용하는가?</h3>
<table>
<thead>
<tr>
<th>작업 유형</th>
<th>매일 사용 비율</th>
</tr>
</thead>
<tbody><tr>
<td>디버깅</td>
<td>55%</td>
</tr>
<tr>
<td>코드 이해</td>
<td>42%</td>
</tr>
<tr>
<td>새 기능 구현</td>
<td>37%</td>
</tr>
<tr>
<td>리팩토링</td>
<td>중간</td>
</tr>
<tr>
<td>고수준 설계/기획</td>
<td>낮음</td>
</tr>
<tr>
<td>데이터 사이언스</td>
<td>낮음</td>
</tr>
</tbody></table>
<h3 id="생산성-변화-1년-전-→-현재">생산성 변화 (1년 전 → 현재)</h3>
<table>
<thead>
<tr>
<th>지표</th>
<th>1년 전</th>
<th>현재</th>
</tr>
</thead>
<tbody><tr>
<td>업무 중 Claude 사용 비율</td>
<td>28%</td>
<td>59%</td>
</tr>
<tr>
<td>생산성 향상</td>
<td>+20%</td>
<td>+50%</td>
</tr>
</tbody></table>
<p>14%의 응답자는 100% 이상의 생산성 향상을 보고</p>
<h3 id="ai-위임-기준">AI 위임 기준</h3>
<p>엔지니어들이 Claude에게 위임하는 작업 유형:</p>
<table>
<thead>
<tr>
<th>위임 조건</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>낮은 복잡도</strong></td>
<td>&quot;내가 맥락이 부족하지만 복잡도도 낮다고 생각되는 것&quot;</td>
</tr>
<tr>
<td><strong>검증 용이</strong></td>
<td>&quot;검증 노력이 생성 노력보다 크지 않은 모든 것&quot;</td>
</tr>
<tr>
<td><strong>명확하게 정의된 작업</strong></td>
<td>&quot;프로젝트의 하위 구성요소가 나머지와 충분히 분리되어 있을 때&quot;</td>
</tr>
<tr>
<td><strong>코드 품질이 중요하지 않음</strong></td>
<td>&quot;일회성 디버깅이나 연구 코드는 바로 Claude에게&quot;</td>
</tr>
<tr>
<td><strong>반복적이거나 지루한 작업</strong></td>
<td>&quot;내가 할 의욕이 없을수록 Claude에게 맡길 가능성 높음&quot;</td>
</tr>
</tbody></table>
<h3 id="직접-수행하는-작업">직접 수행하는 작업</h3>
<ul>
<li>고수준/전략적 사고가 필요한 작업</li>
<li>조직적 맥락이나 &quot;취향&quot;이 필요한 설계 결정</li>
<li>단, 이 경계는 모델이 발전함에 따라 계속 재협상 중</li>
</ul>
<h3 id="스킬-변화">스킬 변화</h3>
<p><strong>확장되는 역량:</strong></p>
<blockquote>
<p>&quot;프론트엔드, 트랜잭셔널 데이터베이스, API 코드 등 이전에는 건드리기 두려웠던 것들을 이제 능숙하게 다룰 수 있다&quot;</p>
</blockquote>
<p><strong>우려 사항:</strong></p>
<blockquote>
<p>&quot;직접 어려운 이슈를 디버깅하면 문서와 코드를 읽으면서 시스템 작동 방식에 대한 모델을 구축하게 된다. Claude가 바로 문제로 데려다주기 때문에 그런 과정이 많이 줄었다&quot;</p>
</blockquote>
<p><strong>&quot;감독의 역설&quot;:</strong></p>
<blockquote>
<p>&quot;Claude를 효과적으로 사용하려면 감독이 필요하고, Claude를 감독하려면 AI 과다 사용으로 퇴화될 수 있는 바로 그 코딩 스킬이 필요하다&quot;</p>
</blockquote>
<h3 id="직장-내-사회적-변화">직장 내 사회적 변화</h3>
<ul>
<li>Claude가 동료에게 가던 질문의 첫 번째 창구가 됨</li>
<li>일부: &quot;동료의 시간을 뺏는 것에 대한 부담감 감소&quot; 긍정적</li>
<li>일부: &quot;사람들과 일하는 것을 좋아하는데 그들이 덜 &#39;필요&#39;해져서 슬프다&quot;</li>
<li>시니어 엔지니어: &quot;주니어들이 질문을 덜 하게 되어 아쉽다. 하지만 그들은 분명 더 효과적으로 답을 얻고 더 빨리 배운다&quot;</li>
</ul>
<h3 id="커리어-전망">커리어 전망</h3>
<p><strong>역할 변화:</strong></p>
<ul>
<li>코드 작성자 → AI 시스템 관리자로 전환</li>
<li>&quot;70% 이상이 새 코드 작성자가 아닌 코드 리뷰어/수정자가 됨&quot;</li>
<li>&quot;1개, 5개, 100개의 Claude에 대한 책임을 지는 것&quot;이 미래 역할</li>
</ul>
<p><strong>장기적 불확실성:</strong></p>
<blockquote>
<p>&quot;단기적으로는 낙관적이지만 장기적으로는 AI가 모든 것을 하게 되어 나와 많은 다른 사람들을 무의미하게 만들 것 같다&quot;</p>
</blockquote>
<hr>
<h2 id="claude-code-사용-변화-2025년-2월-→-8월">Claude Code 사용 변화 (2025년 2월 → 8월)</h2>
<table>
<thead>
<tr>
<th>지표</th>
<th>2월</th>
<th>8월</th>
<th>변화</th>
</tr>
</thead>
<tbody><tr>
<td>평균 작업 복잡도 (1-5)</td>
<td>3.2</td>
<td>3.8</td>
<td>+19%</td>
</tr>
<tr>
<td>연속 자동 도구 호출 수</td>
<td>9.8</td>
<td>21.2</td>
<td>+116%</td>
</tr>
<tr>
<td>사람 개입 횟수</td>
<td>6.2</td>
<td>4.1</td>
<td>-33%</td>
</tr>
</tbody></table>
<h3 id="작업-유형-분포-변화">작업 유형 분포 변화</h3>
<table>
<thead>
<tr>
<th>작업 유형</th>
<th>2월</th>
<th>8월</th>
</tr>
</thead>
<tbody><tr>
<td>새 기능 구현</td>
<td>14.3%</td>
<td>36.9%</td>
</tr>
<tr>
<td>코드 설계/기획</td>
<td>1.0%</td>
<td>9.9%</td>
</tr>
</tbody></table>
<h3 id="팀별-claude-code-활용-패턴">팀별 Claude Code 활용 패턴</h3>
<table>
<thead>
<tr>
<th>팀</th>
<th>주요 사용 패턴</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Pre-training</strong></td>
<td>새 기능 구축 54.6% (추가 실험 실행)</td>
</tr>
<tr>
<td><strong>Alignment &amp; Safety</strong></td>
<td>프론트엔드 개발 7.5% (데이터 시각화)</td>
</tr>
<tr>
<td><strong>Security</strong></td>
<td>코드 이해 48.9% (보안 영향 분석)</td>
</tr>
<tr>
<td><strong>비기술직</strong></td>
<td>디버깅 51.5% (네트워크, Git 문제해결)</td>
</tr>
</tbody></table>
<hr>
<h2 id="시사점">시사점</h2>
<ol>
<li><strong>생산성 향상은 실재하나 복잡함</strong>: 단순히 시간 단축이 아닌 출력량 증가가 주요 동인</li>
<li><strong>스킬 퇴화 우려는 진지하게 고려 필요</strong>: 특히 주니어 개발자의 기초 역량 습득에 대한 고민 필요</li>
<li><strong>협업 패턴 변화</strong>: 멘토링, 동료 간 학습 기회 감소에 대한 대응 필요</li>
<li><strong>적절한 위임 전략이 핵심</strong>: 검증 가능하고, 잘 정의되고, 위험도 낮은 작업부터 시작</li>
<li><strong>역할 진화 준비</strong>: 코드 작성자 → AI 관리자/감독자로의 전환에 대비</li>
</ol>
<hr>
<p><em>출처: <a href="https://www.anthropic.com/research/how-ai-is-transforming-work-at-anthropic">Anthropic Research - How AI Is Transforming Work at Anthropic</a> (2025.12.02)</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS + Fastify에서 로깅 시스템 구축하기]]></title>
            <link>https://velog.io/@boseong-choi/NestJS-Fastify%EC%97%90%EC%84%9C-%EB%A1%9C%EA%B9%85-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@boseong-choi/NestJS-Fastify%EC%97%90%EC%84%9C-%EB%A1%9C%EA%B9%85-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 15 Dec 2025 04:32:35 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>백엔드 서버를 운영하다 보면 &quot;왜 이 API가 느리지?&quot;, &quot;왜 이 요청이 실패했지?&quot; 같은 질문을 자주 마주하게 됩니다. 이런 문제를 해결하려면 <strong>체계적인 로깅 시스템</strong>이 필수입니다.</p>
<p>이 글에서는 NestJS + Fastify 환경에서 <strong>Request Log</strong>와 <strong>Service Log</strong>를 어떻게 구현했는지, 그리고 이 둘이 어떻게 협력하는지 설명하겠습니다.</p>
<hr>
<h2 id="📋-두-가지-로그-정책">📋 두 가지 로그 정책</h2>
<p>구축한 로깅 시스템은 크게 두 가지로 나뉩니다.</p>
<table>
<thead>
<tr>
<th>로그 유형</th>
<th>목적</th>
<th>기록 방식</th>
<th>기록 시점</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Request Log</strong></td>
<td>HTTP 요청/응답 기록</td>
<td>자동 (미들웨어)</td>
<td>요청 시작 &amp; 응답 완료</td>
</tr>
<tr>
<td><strong>Service Log</strong></td>
<td>비즈니스 로직 추적</td>
<td>수동 (개발자 작성)</td>
<td>로직 실행 중</td>
</tr>
</tbody></table>
<p><strong>Request Log</strong>는 &quot;누가, 언제, 어디로, 얼마나 걸렸는지&quot;를 기록하고,
<strong>Service Log</strong>는 &quot;내부에서 무슨 일이 일어났는지&quot;를 기록합니다.</p>
<hr>
<h2 id="🌐-request-log-구현">🌐 Request Log 구현</h2>
<h3 id="기술-스택-선택">기술 스택 선택</h3>
<p>Request Log를 구현하기 위해 <strong>nestjs-pino</strong>를 선택했습니다.</p>
<pre><code class="language-bash">npm install nestjs-pino pino-http pino-pretty</code></pre>
<p><strong>선택 이유</strong></p>
<ul>
<li>NestJS의 Logger 인터페이스를 그대로 유지하면서</li>
<li>내부적으로 고성능 Pino 엔진 사용</li>
<li>민감 정보 자동 마스킹 지원</li>
<li>로컬에서는 예쁜 출력, 운영에서는 JSON 출력</li>
</ul>
<h3 id="loggermodule-구현">LoggerModule 구현</h3>
<pre><code class="language-typescript">// src/common/modules/logger/logger.module.ts
import { randomUUID } from &#39;node:crypto&#39;;
import { Module } from &#39;@nestjs/common&#39;;
import { LoggerModule as PinoLoggerModule } from &#39;nestjs-pino&#39;;

@Module({
  imports: [
    PinoLoggerModule.forRootAsync({
      useFactory: () =&gt; {
        const isLocal = process.env.NODE_ENV === &#39;local&#39;;

        return {
          pinoHttp: {
            // 기본 메타데이터
            base: {
              service: process.env.SERVICE_NAME || &#39;my-api&#39;,
              env: process.env.NODE_ENV || &#39;local&#39;
            },

            // 민감정보 마스킹 설정
            redact: {
              paths: [
                &#39;req.headers.cookie&#39;,
                &#39;req.headers.authorization&#39;,
                &#39;req.body.password&#39;,
                &#39;req.body.email&#39;,
                &#39;*.token&#39;,
                &#39;*.secret&#39;
              ],
              remove: true
            },

            // 요청마다 고유 ID 생성
            genReqId: (req) =&gt; {
              const existingId = req.headers[&#39;x-request-id&#39;];
              return (Array.isArray(existingId) ? existingId[0] : existingId) || randomUUID();
            },

            // 추가 컨텍스트 정보 수집
            customProps: (req) =&gt; ({
              userId: req.user?.userId || null,
              clientIp: req.ips?.[0] || req.ip || req.socket?.remoteAddress
            }),

            level: isLocal ? &#39;debug&#39; : &#39;info&#39;,

            // 로컬: 예쁜 출력, 운영: JSON
            transport: isLocal
              ? {
                  target: &#39;pino-pretty&#39;,
                  options: {
                    singleLine: false,
                    translateTime: &#39;SYS:yyyy-mm-dd HH:MM:ss.l&#39;,
                    colorize: true
                  }
                }
              : undefined,

            // 성공/에러 메시지 포맷
            customSuccessMessage: (req, res, responseTime) =&gt; {
              return `${req.method} ${req.url} ${res.statusCode} - ${Math.round(responseTime)}ms`;
            },

            customErrorMessage: (req, res, err) =&gt; {
              return `${req.method} ${req.url} ${res.statusCode} - ERROR: ${err.message}`;
            }
          }
        };
      }
    })
  ],
  exports: [PinoLoggerModule]
})
export class LoggerModule {}</code></pre>
<h3 id="maints-설정">main.ts 설정</h3>
<pre><code class="language-typescript">// src/main.ts
import { NestFactory } from &#39;@nestjs/core&#39;;
import { FastifyAdapter, NestFastifyApplication } from &#39;@nestjs/platform-fastify&#39;;
import { Logger } from &#39;nestjs-pino&#39;;
import { AppModule } from &#39;./app.module&#39;;

async function bootstrap() {
  const app = await NestFactory.create&lt;NestFastifyApplication&gt;(
    AppModule,
    new FastifyAdapter({
      logger: false  // Fastify 기본 로거 비활성화
    }),
    { bufferLogs: true }
  );

  // nestjs-pino의 Logger로 교체
  app.useLogger(app.get(Logger));

  await app.listen(3000, &#39;0.0.0.0&#39;);
}
bootstrap();</code></pre>
<h3 id="request-log-출력-예시">Request Log 출력 예시</h3>
<p><strong>로컬 환경:</strong></p>
<pre><code>[14:32:15.123] INFO: GET /api/orders 200 - 152ms
    service: &quot;my-api&quot;
    env: &quot;local&quot;
    userId: &quot;user-12345&quot;
    clientIp: &quot;::1&quot;</code></pre><p><strong>운영 환경 (JSON)</strong></p>
<pre><code class="language-json">{&quot;level&quot;:30,&quot;time&quot;:1702647135123,&quot;service&quot;:&quot;my-api&quot;,&quot;userId&quot;:&quot;user-12345&quot;,&quot;responseTime&quot;:152,&quot;msg&quot;:&quot;GET /api/orders 200 - 152ms&quot;}</code></pre>
<hr>
<h2 id="📊-service-log-구현">📊 Service Log 구현</h2>
<h3 id="왜-service-log가-필요한가">왜 Service Log가 필요한가?</h3>
<p>Request Log만으로는 &quot;API가 왜 느린지&quot;, &quot;어느 단계에서 실패했는지&quot; 알 수 없습니다.
Service Log는 비즈니스 로직의 내부 흐름을 상세히 기록합니다.</p>
<h3 id="구현-패턴">구현 패턴</h3>
<pre><code class="language-typescript">// src/orders/orders.service.ts
import { Injectable, Logger, BadRequestException } from &#39;@nestjs/common&#39;;

@Injectable()
export class OrdersService {
  private readonly logger = new Logger(OrdersService.name);

  async createOrder(dto: CreateOrderDto) {
    const startTime = Date.now();

    // Stage 1: 요청 수신
    this.logger.log(&#39;[Stage 1/4] 주문 생성 요청 수신&#39;, {
      productId: dto.productId,
      quantity: dto.quantity
    });

    // Stage 2: Validation
    this.logger.debug(&#39;[Stage 2/4] Validation 시작&#39;);

    if (dto.quantity &lt;= 0) {
      this.logger.warn(&#39;[Validation 실패] 유효하지 않은 수량&#39;, {
        quantity: dto.quantity,
        reason: &#39;수량은 1 이상이어야 합니다&#39;
      });
      throw new BadRequestException(&#39;수량은 1 이상이어야 합니다&#39;);
    }

    this.logger.debug(&#39;[Stage 2/4] Validation 통과&#39;);

    // Stage 3: 비즈니스 로직 처리
    this.logger.debug(&#39;[Stage 3/4] 주문 처리 중...&#39;);

    const order = await this.orderRepository.save({
      productId: dto.productId,
      quantity: dto.quantity,
      totalPrice: dto.quantity * 10000
    });

    this.logger.log(&#39;[Stage 3/4] 주문 생성 완료&#39;, { orderId: order.id });

    // Stage 4: 완료
    const duration = Date.now() - startTime;
    this.logger.log(&#39;[Stage 4/4] 처리 완료&#39;, {
      orderId: order.id,
      duration: `${duration}ms`
    });

    return order;
  }
}</code></pre>
<h3 id="service-log-출력-예시">Service Log 출력 예시</h3>
<p><strong>정상 처리</strong></p>
<pre><code>[14:32:15.050] INFO: [Stage 1/4] 주문 생성 요청 수신 { productId: &#39;PROD-001&#39;, quantity: 5 }
[14:32:15.060] DEBUG: [Stage 2/4] Validation 시작
[14:32:15.065] DEBUG: [Stage 2/4] Validation 통과
[14:32:15.100] DEBUG: [Stage 3/4] 주문 처리 중...
[14:32:15.140] INFO: [Stage 3/4] 주문 생성 완료 { orderId: &#39;ORD-123&#39; }
[14:32:15.142] INFO: [Stage 4/4] 처리 완료 { duration: &#39;92ms&#39; }</code></pre><p><strong>Validation 실패</strong></p>
<pre><code>[14:32:15.050] INFO: [Stage 1/4] 주문 생성 요청 수신 { productId: &#39;PROD-001&#39;, quantity: -1 }
[14:32:15.060] DEBUG: [Stage 2/4] Validation 시작
[14:32:15.062] WARN: [Validation 실패] 유효하지 않은 수량 { quantity: -1, reason: &#39;수량은 1 이상이어야 합니다&#39; }</code></pre><hr>
<h2 id="🔄-request-log의-작동-원리-인터셉트-방식">🔄 Request Log의 작동 원리 (인터셉트 방식)</h2>
<p>Request Log가 어떻게 자동으로 기록되는지, 그 흐름을 단계별로 설명하겠습니다.</p>
<h3 id="전체-흐름도">전체 흐름도</h3>
<p><img src="https://velog.velcdn.com/images/boseong-choi/post/066453b5-ac80-417c-9f94-4473d67b2707/image.png" alt=""></p>
<h3 id="핵심-포인트">핵심 포인트</h3>
<p><strong>pino-http가 인터셉트하는 방식</strong></p>
<ol>
<li><strong>요청 시작 시</strong>: 미들웨어로서 요청을 가로채 정보 수집 (로그 출력 X)</li>
<li><strong>응답 완료 시</strong>: 등록해둔 콜백이 실행되어 최종 로그 출력</li>
</ol>
<p>이 방식 덕분에 개발자가 별도 코드를 작성하지 않아도 모든 API에 자동 적용되며<code>responseTime</code>을 정확히 측정 가능해 집니다. (요청 시작 ~ 응답 완료) 또한, <code>req.user</code> 등 Guard에서 설정한 정보도 로그에 포함 가능합니다.</p>
<hr>
<h2 id="💡-실무-적용-팁">💡 실무 적용 팁</h2>
<h3 id="1-x-request-id-활용">1. X-Request-Id 활용</h3>
<p>프론트엔드에서 디버깅 시 직접 ID를 지정할 수 있습니다</p>
<pre><code class="language-javascript">fetch(&#39;/api/orders&#39;, {
  headers: { &#39;X-Request-Id&#39;: `debug-${Date.now()}` }
});</code></pre>
<h3 id="2-민감-정보-마스킹-필수">2. 민감 정보 마스킹 필수</h3>
<p>운영 환경에서는 개인정보가 로그에 남지 않도록 주의</p>
<pre><code class="language-typescript">redact: {
  paths: [&#39;req.body.password&#39;, &#39;req.body.email&#39;, &#39;*.token&#39;],
  remove: true  // 해당 필드 완전 제거
}</code></pre>
<h3 id="3-로그-레벨-활용">3. 로그 레벨 활용</h3>
<pre><code class="language-typescript">this.logger.debug(&#39;...&#39;);  // 개발 시에만 확인할 상세 정보
this.logger.log(&#39;...&#39;);    // 일반적인 정보
this.logger.warn(&#39;...&#39;);   // 주의가 필요한 상황
this.logger.error(&#39;...&#39;);  // 에러 상황</code></pre>
<hr>
<h2 id="마치며">마치며</h2>
<p>로깅 시스템을 구축하면서 느낀 점은, 좋은 로그는 문제 해결 시간을 극적으로 줄여준다는 것입니다.</p>
<p>Request Log와 Service Log를 조합하면</p>
<ul>
<li>전체적인 트래픽 흐름 파악 (Request Log)</li>
<li>내부 비즈니스 로직 추적 (Service Log)</li>
</ul>
<p>이 두 가지를 모두 할 수 있어, 장애 대응과 성능 최적화에 큰 도움이 됩니다.</p>
<hr>
<h3 id="참고-자료">참고 자료</h3>
<ul>
<li><a href="https://github.com/iamolegga/nestjs-pino">nestjs-pino 공식 문서</a></li>
<li><a href="https://getpino.io/">Pino 공식 문서</a></li>
<li><a href="https://docs.nestjs.com/techniques/logger">NestJS Logging 가이드</a>
```</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[회사에 코딩 컨벤션을 도입한 후기]]></title>
            <link>https://velog.io/@boseong-choi/%ED%9A%8C%EC%82%AC%EC%97%90-%EC%BD%94%EB%94%A9-%EC%BB%A8%EB%B2%A4%EC%85%98%EC%9D%84-%EB%8F%84%EC%9E%85%ED%95%9C-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@boseong-choi/%ED%9A%8C%EC%82%AC%EC%97%90-%EC%BD%94%EB%94%A9-%EC%BB%A8%EB%B2%A4%EC%85%98%EC%9D%84-%EB%8F%84%EC%9E%85%ED%95%9C-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Fri, 12 Dec 2025 09:27:13 GMT</pubDate>
            <description><![CDATA[<h1 id="🎯-회사에-코딩-컨벤션-도입기">🎯 회사에 코딩 컨벤션 도입기</h1>
<blockquote>
<p>💡 <strong>TL;DR</strong>: AI 시대에도 코딩 컨벤션은 필수입니다. Prettier + ESLint + AI 프롬프트 조합으로 팀 전체가 일관된 코드를 작성하게 된 경험을 공유합니다.</p>
</blockquote>
<hr>
<h2 id="1-왜-코딩-컨벤션인가">1. 왜 코딩 컨벤션인가?</h2>
<blockquote>
<p><strong>코딩 컨벤션(Code Convention)</strong>: 팀이 일관된 코드 스타일과 규칙을 사용하기 위해 정한 약속.</p>
</blockquote>
<h3 id="도입을-고민하게-된-계기">도입을 고민하게 된 계기</h3>
<h4 id="i-개발자마다-다른-코딩-스타일">I. 개발자마다 다른 코딩 스타일</h4>
<p>가장 큰 이유였습니다. 협업 프로젝트가 있을 때마다 개발팀의 스타일이 제각각이라 가독성이 좋지 않았습니다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>개발자 A</th>
<th>개발자 B</th>
</tr>
</thead>
<tbody><tr>
<td>세미콜론</td>
<td>필수 사용</td>
<td>생략</td>
</tr>
<tr>
<td>따옴표</td>
<td>쌍따옴표 <code>&quot;</code></td>
<td>작은따옴표 <code>&#39;</code></td>
</tr>
<tr>
<td>들여쓰기</td>
<td>Tab 4칸</td>
<td>Space 2칸</td>
</tr>
<tr>
<td>import 정렬</td>
<td>무작위</td>
<td>알파벳순</td>
</tr>
</tbody></table>
<p><strong>결과적으로 발생한 문제들</strong></p>
<p>🔴 코드 리뷰 시 스타일 논쟁에 시간 낭비
🔴 신규 팀원 온보딩 시 &quot;이 프로젝트는 이렇게 해요&quot;라는 불필요한 설명 증가
🔴 Git diff가 실제 로직 변경이 아닌 포맷팅 변경으로 오염</p>
<h4 id="ii-프롬프트-엔지니어링과-ai-코딩">II. 프롬프트 엔지니어링과 AI 코딩</h4>
<p>회사에 <strong>Cursor IDE</strong>를 적극 도입하면서 생산성은 이전과 비교할 수 없을 정도로 향상되었습니다. 하지만 한 가지 문제가 있었습니다.</p>
<blockquote>
<p><strong>AI는 기본적으로 우리 팀의 코딩 컨벤션을 모릅니다.</strong></p>
</blockquote>
<p>AI가 생성한 코드가 다음과 같은 문제를 일 수 있습니다.</p>
<pre><code class="language-typescript">// AI가 생성한 코드 (우리 컨벤션과 다름)
import {Controller} from &quot;@nestjs/common&quot;
import {UserService} from &quot;./user.service&quot;

export class UserController {
    constructor(private userService: UserService) {}
}</code></pre>
<pre><code class="language-typescript">// 우리 팀 컨벤션
import { Controller } from &#39;@nestjs/common&#39;;

import { UserService } from &#39;./user.service&#39;;

export class UserController {
  constructor(private readonly userService: UserService) {}
}</code></pre>
<p>따라서 <strong>바이브 코딩(Vibe Coding)</strong> 을 하더라도 코딩 컨벤션을 지켜야 한다는 건 변함이 없었습니다.</p>
<hr>
<h2 id="2-코딩-컨벤션-도입이-실패하는-이유">2. 코딩 컨벤션 도입이 실패하는 이유</h2>
<p>많은 팀이 코딩 컨벤션 도입을 시도하지만 실패하는 경우가 많습니다. 왜 그럴까요?</p>
<table>
<thead>
<tr>
<th>원인</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>📌 <strong>각자 다른 IDE 환경</strong></td>
<td>VS Code, WebStorm, Cursor 등 각자 다른 IDE를 사용하면 설정 공유가 어려움</td>
</tr>
<tr>
<td>📌 <strong>세팅의 복잡성</strong></td>
<td>ESLint, Prettier, EditorConfig 등 도구가 많고 설정 파일이 복잡함</td>
</tr>
<tr>
<td>📌 <strong>규칙을 지키기 어려움</strong></td>
<td>&quot;저장할 때 자동 포맷팅&quot; 설정을 안 하면 매번 수동으로 해야 함</td>
</tr>
<tr>
<td>📌 <strong>강제성 부재</strong></td>
<td>CI/CD 파이프라인에서 검사하지 않으면 결국 개인 재량에 맡겨짐</td>
</tr>
<tr>
<td>📌 <strong>너무 많은 규칙</strong></td>
<td>처음부터 모든 규칙을 <code>error</code>로 설정하면 개발자 피로도 급증</td>
</tr>
<tr>
<td>📌 <strong>문서화 부재</strong></td>
<td>&quot;왜 이 규칙인지&quot; 설명 없이 규칙만 강요하면 저항이 생김</td>
</tr>
</tbody></table>
<h3 id="우리-팀의-해결-원칙">우리 팀의 해결 원칙</h3>
<pre><code>1. Prettier가 포맷 최종 결정권자 → 포매팅 논쟁 종결
2. 가독성 &gt; 개인 취향 → 정답이 없는 부분은 Prettier 기본값 + 팀 최소 합의
3. 경고 중심 도입 → 대부분 warn으로 시작, 익숙해지면 error로 승격
4. 자동화 우선 → 저장 시 포맷/자동수정, 커밋 전 자동 검사
5. 규칙은 짧게 → 꼭 필요한 소수 규칙만, 나머지는 리뷰로 보완</code></pre><hr>
<h2 id="3-코딩-컨벤션-도입-방법">3. 코딩 컨벤션 도입 방법</h2>
<h3 id="3-1-공통-설정-파일-구성">3-1. 공통 설정 파일 구성</h3>
<p>모든 IDE에서 공통으로 읽을 수 있는 <strong>4가지 핵심 설정 파일</strong>을 프로젝트 루트에 배치합니다.</p>
<h4 id="editorconfig---ide-공통-설정"><code>.editorconfig</code> - IDE 공통 설정</h4>
<pre><code class="language-ini">root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true</code></pre>
<h4 id="prettierrcjson---코드-포맷팅"><code>.prettierrc.json</code> - 코드 포맷팅</h4>
<pre><code class="language-json">{
  &quot;printWidth&quot;: 100,
  &quot;tabWidth&quot;: 2,
  &quot;singleQuote&quot;: true,
  &quot;semi&quot;: true,
  &quot;trailingComma&quot;: &quot;none&quot;,
  &quot;bracketSpacing&quot;: true,
  &quot;arrowParens&quot;: &quot;always&quot;,
  &quot;endOfLine&quot;: &quot;lf&quot;
}</code></pre>
<h4 id="eslintrccjs---코드-품질-검사"><code>.eslintrc.cjs</code> - 코드 품질 검사</h4>
<pre><code class="language-javascript">module.exports = {
  root: true,
  plugins: [&#39;@typescript-eslint&#39;, &#39;import&#39;, &#39;unused-imports&#39;],
  extends: [
    &#39;eslint:recommended&#39;,
    &#39;plugin:@typescript-eslint/recommended&#39;,
    &#39;plugin:import/recommended&#39;,
    &#39;plugin:prettier/recommended&#39;  // Prettier와 충돌 방지
  ],
  rules: {
    &#39;prettier/prettier&#39;: [&#39;warn&#39;],
    &#39;prefer-const&#39;: &#39;error&#39;,
    &#39;@typescript-eslint/no-unused-vars&#39;: [&#39;warn&#39;, { argsIgnorePattern: &#39;^_&#39; }],
    &#39;import/order&#39;: [&#39;warn&#39;, {
      groups: [&#39;builtin&#39;, &#39;external&#39;, &#39;internal&#39;, &#39;parent&#39;, &#39;sibling&#39;, &#39;index&#39;],
      &#39;newlines-between&#39;: &#39;always&#39;,
      alphabetize: { order: &#39;asc&#39;, caseInsensitive: true }
    }],
    &#39;unused-imports/no-unused-imports&#39;: &#39;warn&#39;
  }
};</code></pre>
<h3 id="3-2-ai-ide용-프롬프트-설정">3-2. AI IDE용 프롬프트 설정</h3>
<h4 id="cursor-vs-code-계열">Cursor (VS Code 계열)</h4>
<p><strong>Settings &gt; Cursor Settings &gt; Rules for AI</strong>에 팀 컨벤션 프롬프트 입력</p>
<pre><code># 코딩 컨벤션 규칙
- Prettier 규칙 준수: singleQuote, semi, trailingComma: none
- import 정렬: builtin → external → internal → parent/sibling
- 변수/함수: camelCase, 컴포넌트/클래스: PascalCase
- const 우선, async/await 사용
- 메서드는 동사로 시작 (예: checkSomething, getUserById)</code></pre><h4 id="jetbrains-webstorm-intellij-등">JetBrains (WebStorm, IntelliJ 등)</h4>
<p>프로젝트 루트에 <code>.aiassistant/rules/</code> 폴더를 생성하고 마크다운 파일로 프롬프트 작성</p>
<pre><code>📁 프로젝트 루트
└── 📁 .aiassistant
    └── 📁 rules
        └── 📄 coding-convention-prompt.md</code></pre><h3 id="3-3-packagejson-스크립트-추가">3-3. package.json 스크립트 추가</h3>
<pre><code class="language-json">{
  &quot;scripts&quot;: {
    &quot;lint&quot;: &quot;eslint . --ext .ts,.tsx,.js --max-warnings=0&quot;,
    &quot;lint:fix&quot;: &quot;eslint . --ext .ts,.tsx,.js --fix&quot;,
    &quot;format&quot;: &quot;prettier --write .&quot;,
    &quot;format:check&quot;: &quot;prettier --check .&quot;
  }
}</code></pre>
<hr>
<h2 id="4-트러블-슈팅">4. 트러블 슈팅</h2>
<h3 id="🔧-crlf-vs-lf-문제">🔧 CRLF vs LF 문제</h3>
<p><strong>증상</strong></p>
<p>Windows 개발자와 Mac/Linux 개발자가 협업할 때 줄바꿈 문자 충돌</p>
<p><strong>해결 방법</strong></p>
<ol>
<li><p><code>.gitattributes</code> 파일 추가</p>
<pre><code>* text=auto eol=lf
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf</code></pre></li>
<li><p><code>.prettierrc.json</code>에 <code>&quot;endOfLine&quot;: &quot;lf&quot;</code> 설정</p>
</li>
<li><p>Git 전역 설정</p>
<pre><code class="language-bash">git config --global core.autocrlf input  # Mac/Linux
git config --global core.autocrlf true   # Windows</code></pre>
</li>
</ol>
<h3 id="🔧-eslint--prettier-충돌">🔧 ESLint + Prettier 충돌</h3>
<p><strong>증상</strong></p>
<p>ESLint와 Prettier가 서로 다른 포맷을 요구</p>
<p><strong>해결 방법</strong></p>
<pre><code class="language-bash">npm install -D eslint-config-prettier eslint-plugin-prettier</code></pre>
<p><code>.eslintrc.cjs</code>의 <code>extends</code> 마지막에 추가</p>
<pre><code class="language-javascript">extends: [
  // ... 다른 설정들
  &#39;plugin:prettier/recommended&#39;  // 반드시 마지막에!
]</code></pre>
<h3 id="🔧-import-순서-자동-정렬-안-됨">🔧 import 순서 자동 정렬 안 됨</h3>
<p><strong>증상</strong> </p>
<p><code>import/order</code> 규칙이 경고만 주고 자동 수정 안 됨</p>
<p><strong>해결 방법</strong></p>
<ol>
<li><p><code>eslint-import-resolver-typescript</code> 설치</p>
<pre><code class="language-bash">npm install -D eslint-import-resolver-typescript</code></pre>
</li>
<li><p>설정 추가</p>
<pre><code class="language-javascript">settings: {
  &#39;import/resolver&#39;: {
    typescript: { project: &#39;.&#39; }
  }
}</code></pre>
</li>
</ol>
<h3 id="🔧-미사용-import-자동-삭제">🔧 미사용 import 자동 삭제</h3>
<p><strong>증상</strong></p>
<p>사용하지 않는 import가 남아있음</p>
<p><strong>해결 방법</strong></p>
<pre><code class="language-bash">npm install -D eslint-plugin-unused-imports</code></pre>
<pre><code class="language-javascript">plugins: [&#39;unused-imports&#39;],
rules: {
  &#39;unused-imports/no-unused-imports&#39;: &#39;warn&#39;
}</code></pre>
<hr>
<h2 id="5-결과-및-기대효과">5. 결과 및 기대효과</h2>
<h3 id="📊-정량적-효과">📊 정량적 효과</h3>
<table>
<thead>
<tr>
<th>지표</th>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody><tr>
<td>코드 리뷰 시 스타일 코멘트</td>
<td>~30%</td>
<td><strong>&lt; 5%</strong></td>
</tr>
<tr>
<td>신규 팀원 온보딩 시간</td>
<td>1주일</td>
<td><strong>2-3일</strong></td>
</tr>
<tr>
<td>Git diff 노이즈</td>
<td>잦음</td>
<td><strong>거의 없음</strong></td>
</tr>
<tr>
<td>AI 코드 수정 필요 빈도</td>
<td>매번</td>
<td><strong>자동 적용</strong></td>
</tr>
</tbody></table>
<h3 id="✅-정성적-효과">✅ 정성적 효과</h3>
<ul>
<li><strong>코드 리뷰 품질 향상</strong>: 스타일 논쟁 대신 로직과 설계에 집중</li>
<li><strong>인지 부하 감소</strong>: 어떤 파일을 열어도 익숙한 구조</li>
<li><strong>AI 협업 효율화</strong>: 프롬프트에 컨벤션을 넣어 처음부터 올바른 코드 생성</li>
<li><strong>팀 문화 개선</strong>: &quot;내 스타일&quot; 대신 &quot;팀 표준&quot;이라는 인식 정착</li>
</ul>
<hr>
<h2 id="📚-참고-자료">📚 참고 자료</h2>
<ul>
<li><a href="https://prettier.io/docs/en/">Prettier 공식 문서</a></li>
<li><a href="https://eslint.org/docs/latest/">ESLint 공식 문서</a></li>
<li><a href="https://typescript-eslint.io/">TypeScript ESLint</a></li>
<li>사내 코딩 컨벤션 가이드 (사내 Confluence)</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠버네티스 소개]]></title>
            <link>https://velog.io/@boseong-choi/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@boseong-choi/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Tue, 10 Jun 2025 01:00:52 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 포스트는 인프런 강의를 수강한 뒤 개인적으로 요약·정리한 블로그 글입니다.</p>
</blockquote>
<hr>
<h2 id="관련-용어-발음-정리">관련 용어 발음 정리</h2>
<table>
<thead>
<tr>
<th align="center">영어 약어/단어</th>
<th align="left">정의</th>
<th align="center">발음 예시</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>master</strong></td>
<td align="left">클러스터 전체를 관리하는 제어 노드(Control Plane)</td>
<td align="center">마스터</td>
</tr>
<tr>
<td align="center"><strong>node</strong></td>
<td align="left">애플리케이션이 실제로 돌아가는 워커 머신</td>
<td align="center">노드</td>
</tr>
<tr>
<td align="center"><strong>k8s</strong></td>
<td align="left">Kubernetes의 축약 표현 (k + 8글자 + s)</td>
<td align="center">쿠버네티스</td>
</tr>
<tr>
<td align="center"><strong>kubectl</strong></td>
<td align="left">Kubernetes를 제어하기 위한 CLI(Command Line Interface)</td>
<td align="center">큐브 컨트롤, 큐브 시티엘</td>
</tr>
<tr>
<td align="center"><strong>etcd</strong></td>
<td align="left">Kubernetes의 분산 키-값 저장소(데이터베이스)</td>
<td align="center">엣지디, 이티시디</td>
</tr>
<tr>
<td align="center"><strong>Pod</strong></td>
<td align="left">Kubernetes에서 가장 작은 배포 단위(컨테이너 묶음)</td>
<td align="center">파드, 팟</td>
</tr>
<tr>
<td align="center"><strong>Service</strong></td>
<td align="left">Pod들의 네트워크 접근점을 추상화한 리소스</td>
<td align="center">서비스</td>
</tr>
<tr>
<td align="center"><strong>Ingress</strong></td>
<td align="left">클러스터 외부에서 들어오는 HTTP/HTTPS 트래픽을 라우팅</td>
<td align="center">인그레스</td>
</tr>
<tr>
<td align="center"><strong>Namespace</strong></td>
<td align="left">클러스터를 가상으로 분리하는 논리적 공간</td>
<td align="center">네임스페이스</td>
</tr>
<tr>
<td align="center"><strong>ConfigMap</strong></td>
<td align="left">설정(환경변수·명령줄 인자 등)을 저장하는 리소스</td>
<td align="center">컨피그맵</td>
</tr>
<tr>
<td align="center"><strong>Secret</strong></td>
<td align="left">암호·토큰·인증서 같은 민감 정보를 저장하는 리소스</td>
<td align="center">시크릿</td>
</tr>
<tr>
<td align="center"><strong>PV / PVC</strong></td>
<td align="left">PersistentVolume / PersistentVolumeClaim (영구 스토리지)</td>
<td align="center">피브이 / 피브이시</td>
</tr>
<tr>
<td align="center"><strong>Helm</strong></td>
<td align="left">Kubernetes 패키지 매니저 (Charts 사용)</td>
<td align="center">헬름</td>
</tr>
<tr>
<td align="center"><strong>Istio</strong></td>
<td align="left">서비스 메시(Service Mesh) 구현체</td>
<td align="center">이스티오</td>
</tr>
<tr>
<td align="center"><strong>Knative</strong></td>
<td align="left">Kubernetes 위에서 서버리스(Function) 실행을 돕는 플랫폼</td>
<td align="center">케이 네이티브</td>
</tr>
</tbody></table>
<hr>
<h2 id="1-쿠버네티스란-무엇인가">1. 쿠버네티스란 무엇인가?</h2>
<ol>
<li><p><strong>왜 필요한가?</strong></p>
<ul>
<li>전통적 서버 운영: VM 혹은 물리 서버에 애플리케이션 직접 배포 → 수동 스케일링·업데이트</li>
<li>문제점: 사람 손이 많이 가고, 설정이 복잡, 장애 대응이 느림</li>
</ul>
</li>
<li><p><strong>쿠버네티스의 해결책</strong></p>
<ul>
<li><strong>자동화</strong>: 컨테이너화된 애플리케이션 배포·스케일·롤백을 자동 처리</li>
<li><strong>추상화</strong>: Pod·Service 같은 논리 단위로 묶어 인프라 세부사항 숨김</li>
<li><strong>확장성</strong>: 자동 오토스케일(AutoScaling) 지원</li>
<li><strong>고가용성</strong>: 노드 장애 시 자동 복구</li>
</ul>
</li>
<li><p><strong>핵심 컴포넌트 구조</strong></p>
<pre><code class="language-mermaid">graph TD
  A[Control Plane] --&gt;|API 요청| B[kube-apiserver]
  B --&gt; C[kube-scheduler]
  B --&gt; D[kube-controller-manager]
  B --&gt; E[etcd]
  F[Worker Node] --&gt;|Pod 관리| G[kubelet]
  F --&gt;|네트워크| H[kube-proxy]
  G --&gt; Pod[Pod]</code></pre>
<ul>
<li><p><strong>Control Plane</strong></p>
<ul>
<li><code>kube-apiserver</code>: API 요청 처리</li>
<li><code>etcd</code>: 클러스터 상태 저장</li>
<li><code>kube-scheduler</code>: Pod 배치 결정</li>
<li><code>kube-controller-manager</code>: 레플리카 관리 등 컨트롤러 실행</li>
</ul>
</li>
<li><p><strong>Worker Node</strong></p>
<ul>
<li><code>kubelet</code>: Control Plane과 통신하며 Pod 실행</li>
<li><code>kube-proxy</code>: 네트워크 룰 설정</li>
<li>컨테이너 런타임(Docker, containerd)</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>왜 Kubernetes인가?</strong></p>
<ul>
<li>Google Borg/Omega 경험 기반 설계</li>
<li>CNCF 졸업 프로젝트로 활발한 커뮤니티·생태계</li>
<li>AWS·GCP·Azure·온프레미스 모두 동일한 운용 경험 제공</li>
</ul>
</li>
</ol>
<hr>
<h2 id="2-cloud-native란">2. Cloud Native란?</h2>
<ol>
<li><p><strong>정의</strong>
클라우드 장점을 최대한 활용하는 애플리케이션 설계·구축 패러다임</p>
<ul>
<li>컨테이너화, 마이크로서비스, 선언적 인프라(IaC), CI/CD 등 포함</li>
</ul>
</li>
<li><p><strong>과거 vs 현재</strong>
<img src="https://velog.velcdn.com/images/boseong-choi/post/ef968b73-c0d0-453c-8a67-c92863c2c666/image.png" alt=""></p>
</li>
</ol>
<ul>
<li><strong>과거</strong>: 모놀리식→수동 프로비전→장애 시 수동 복구</li>
<li><strong>현재</strong>: 수십~수백 개 컨테이너 동적 운용→자동 스케일·롤백→일관된 환경</li>
</ul>
<ol start="3">
<li><p><strong>구성 요소</strong></p>
<ul>
<li><strong>컨테이너</strong>: 애플리케이션 + 의존성 경량 패키지</li>
<li><strong>마이크로서비스</strong>: 기능별 독립 서비스</li>
<li><strong>서비스 메시</strong>: Istio 등으로 트래픽·보안·모니터링</li>
<li><strong>DevOps</strong>: CI/CD로 배포 자동화</li>
<li><strong>IaC</strong>: Terraform·Helm·GitOps</li>
</ul>
</li>
</ol>
<hr>
<h2 id="3-주요-리소스-관계">3. 주요 리소스 관계</h2>
<ol>
<li><code>Deployment</code> → <code>ReplicaSet</code> → <code>Pod</code></li>
<li><code>Service</code> → Pod 그룹에 IP·DNS 제공</li>
<li><code>Ingress</code> → Service에 외부 트래픽 라우팅</li>
<li><code>ConfigMap</code>/<code>Secret</code> → Pod에 설정·민감정보 주입</li>
<li><code>PVC</code> → Pod에 영구 볼륨 마운트</li>
</ol>
<hr>
<h2 id="4-쿠버네티스-배포deployment-개념-및-아키텍처">4. 쿠버네티스 배포(Deployment) 개념 및 아키텍처</h2>
<h3 id="41-deployment란">4.1 Deployment란?</h3>
<ul>
<li><strong>Deployment</strong>: 원하는 Pod 사양·개수(desired state)를 선언 → ReplicaSet 생성·관리</li>
<li><strong>지원 기능</strong>: 롤링 업데이트, 롤백, 자동 스케일링</li>
</ul>
<h3 id="42-주요-구성-요소">4.2 주요 구성 요소</h3>
<ul>
<li><strong>ReplicaSet</strong>: Pod 복제본(desired number) 유지</li>
<li><strong>Rolling Update</strong>: 순차적 교체로 무중단 배포</li>
<li><strong>Rollback</strong>: 문제 발생 시 이전 Revision으로 복귀</li>
</ul>
<h3 id="43-배포-흐름-단계별-설명">4.3 배포 흐름 단계별 설명</h3>
<ol>
<li><strong>명세 제출</strong>: <code>kubectl apply -f deployment.yaml</code></li>
<li><strong>API Server 저장</strong>: Deployment 오브젝트(etcd)에 기록</li>
<li><strong>컨트롤러 동작</strong>: Deployment 컨트롤러가 ReplicaSet 생성/업데이트</li>
<li><strong>스케줄링 &amp; 실행</strong>: kube-scheduler→kubelet→컨테이너 기동</li>
<li><strong>상태 조정</strong>: Controller Manager가 desired vs actual 비교 후 조정</li>
</ol>
<h3 id="44-배포-전략-비교">4.4 배포 전략 비교</h3>
<table>
<thead>
<tr>
<th>전략</th>
<th>설명</th>
<th>사용 사례</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Rolling Update</strong></td>
<td>순차 교체로 무중단 업그레이드</td>
<td>서비스 다운타임 최소화 필요 시</td>
</tr>
<tr>
<td><strong>Recreate</strong></td>
<td>기존 파드 전부 삭제 후 일괄 생성</td>
<td>짧은 다운타임 허용 가능 시</td>
</tr>
<tr>
<td><strong>Blue/Green</strong></td>
<td>Blue/Green 환경 동시 유지 후 트래픽 전환</td>
<td>안전한 롤백·검증 환경 분리 시</td>
</tr>
<tr>
<td><strong>Canary Release</strong></td>
<td>일부 사용자에만 새 버전 배포 후 확대</td>
<td>리스크 최소화, 기능 실험 단계에서</td>
</tr>
</tbody></table>
<hr>
<p>✨ 이 글에서 쿠버네티스의 핵심 개념과 아키텍처, 배포 전략까지 한눈에 살펴보았습니다.
다음 포스트에서는 실제 <code>deployment.yaml</code> 예제를 작성하고, Helm Chart를 이용해 배포를 자동화하는 과정을 단계별로 다뤄볼게요. 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[컨테이너 오케스트레이션]]></title>
            <link>https://velog.io/@boseong-choi/%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%98%A4%EC%BC%80%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@boseong-choi/%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%98%A4%EC%BC%80%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98</guid>
            <pubDate>Wed, 30 Apr 2025 03:55:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/boseong-choi/post/7e5e7ca3-3f27-475a-92b6-6b0eff5eab58/image.png" alt=""></p>
<hr>
<h2 id="🧩-컨테이너-오케스트레이션">🧩 컨테이너 오케스트레이션</h2>
<blockquote>
<p>복잡한 컨테이너 환경을 효과적으로 관리하기 위한 도구</p>
</blockquote>
<hr>
<h2 id="🧱-기존-방식의-한계">🧱 기존 방식의 한계</h2>
<ul>
<li>서버 환경을 아무리 문서화해도 환경/버전이 바뀌면 잘 안 돌아가는 경우 많음</li>
<li>설정 관리 도구 등장<br>대표 도구: <code>CHEF</code>, <code>Puppet Labs</code>, <code>Ansible</code><br>하지만…<ul>
<li>배우기 어렵고</li>
<li>설정이 복잡해지면 도구 자체도 복잡해짐</li>
</ul>
</li>
<li>가상머신(VM)의 등장<ul>
<li>클라우드와 찰떡궁합은 아님</li>
<li>특정 벤더에 종속</li>
<li>부팅이 느려서 유연성 떨어짐</li>
</ul>
</li>
</ul>
<hr>
<h2 id="🐳-도커docker의-등장">🐳 도커(Docker)의 등장</h2>
<p>도커는 이 문제들을 상당히 해결해줬음.</p>
<ul>
<li>실행 환경을 <strong>컨테이너로 추상화</strong></li>
<li>도커만 설치되어 있으면 <strong>어디서든 동일하게 실행 가능</strong></li>
<li>사용법이 상대적으로 쉬움 → 서버 운영 복잡성 ↓</li>
</ul>
<h3 id="📌-컨테이너의-특징">📌 컨테이너의 특징</h3>
<ul>
<li>VM보다 빠르고 효율적</li>
<li>이미지 기반 배포 및 롤백 가능</li>
<li>언어나 프레임워크에 상관없이 동일한 방식으로 앱 관리</li>
<li>개발/테스트/운영 환경 통일 가능</li>
<li>특정 클라우드 벤더에 종속되지 않음</li>
</ul>
<hr>
<h2 id="📦-컨테이너화의-흐름">📦 컨테이너화의 흐름</h2>
<pre><code class="language-plaintext">코드 작성 → Docker build → Ship → Run</code></pre>
<ul>
<li>DB, Redis, Jenkins 등도 컨테이너화하여 관리 가능</li>
<li>&quot;이미지화&quot;된 애플리케이션을 어디서든 실행 가능하게 만들어 줌</li>
</ul>
<hr>
<h2 id="🤯-컨테이너가-많아지면">🤯 컨테이너가 많아지면?</h2>
<blockquote>
<p>컨테이너가 10개, 100개, 1000개가 되면?</p>
</blockquote>
<ul>
<li><code>docker run</code>만으로는 관리가 점점 어려워짐</li>
<li>배포, 업데이트, 서비스 연결, 장애 대응까지 <strong>운영 자동화</strong>에 대한 요구 증가</li>
</ul>
<hr>
<h2 id="🛠-도커-이후의-고민들">🛠 도커 이후의 고민들</h2>
<h3 id="🚀-컨테이너-배포는-어떻게">🚀 컨테이너 배포는 어떻게?</h3>
<pre><code class="language-bash">docker stop app &amp;&amp; docker run ...</code></pre>
<ul>
<li>수동 작업 많음</li>
<li>빈 서버 찾기도 힘듦</li>
<li>롤백/업데이트 불편</li>
</ul>
<h3 id="🧭-서비스-검색은-어떻게">🧭 서비스 검색은 어떻게?</h3>
<ul>
<li>마이크로서비스 구조에서 컨테이너가 많아질수록 통신이 복잡</li>
<li>로드밸런서 설정도 번거롭고, 자동화 필요</li>
</ul>
<h3 id="🌐-서비스-노출은-어떻게">🌐 서비스 노출은 어떻게?</h3>
<ul>
<li>가장 쉬운 방식: Nginx 같은 프록시 서버로 연결</li>
<li>하지만 프록시 설정을 컨테이너 증가에 맞춰 자동화해야 함</li>
</ul>
<h3 id="📊-모니터링과-확장">📊 모니터링과 확장</h3>
<ul>
<li>컨테이너 상태 이상 여부 체크</li>
<li>CPU나 메모리 사용량에 따라 <strong>자동 확장/축소</strong> 필요</li>
</ul>
<hr>
<h2 id="🧠-컨테이너-오케스트레이션의-필요성">🧠 컨테이너 오케스트레이션의 필요성</h2>
<blockquote>
<p>컨테이너를 <strong>자동으로 배포, 관리, 확장, 모니터링</strong>해주는 시스템</p>
</blockquote>
<h3 id="주요-기능-요약">주요 기능 요약</h3>
<ol>
<li><p><strong>클러스터 단위 관리</strong></p>
<ul>
<li>개별 노드가 아닌 <strong>클러스터 단위</strong>로 컨테이너를 관리</li>
<li>SSH로 접속하지 않고, 마스터 노드에 명령 전달</li>
<li>노드 간 통신이 원활해야 함</li>
</ul>
</li>
<li><p><strong>상태 관리(State Management)</strong></p>
<ul>
<li>예: Replica를 2 → 3으로 설정하면, 자동으로 컨테이너 3개 유지</li>
<li>시스템이 자동으로 상태를 유지해줌</li>
</ul>
</li>
<li><p><strong>스케줄링</strong></p>
<ul>
<li>리소스 상황에 따라 컨테이너 배치 자동 조정</li>
</ul>
</li>
<li><p><strong>버전 관리</strong></p>
<ul>
<li>Rollout, Rollback 같은 배포 이력 관리 기능 제공</li>
</ul>
</li>
<li><p><strong>서비스 디스커버리</strong></p>
<ul>
<li>컨테이너가 DNS 이름 기반으로 자동으로 서비스에 접근 가능</li>
<li>예: <code>user-service</code> → <code>http://user-service</code></li>
</ul>
</li>
<li><p><strong>볼륨 스토리지</strong></p>
<ul>
<li>상태 유지가 필요한 서비스(DB 등)를 위한 <strong>Persistent Volume</strong></li>
<li>NFS, AWS EBS, Ceph 등 외부 스토리지 연동 가능</li>
</ul>
</li>
</ol>
<hr>
<h2 id="🤖-왜-쿠버네티스kubernetes인가">🤖 왜 쿠버네티스(Kubernetes)인가?</h2>
<blockquote>
<p>구글이 만든 오픈소스 오케스트레이션 플랫폼. 도커 이후의 실질적인 표준.</p>
</blockquote>
<h3 id="핵심-특징">핵심 특징</h3>
<ul>
<li><code>Borg</code>라는 구글 내부 컨테이너 시스템에서 출발</li>
<li>가장 큰 커뮤니티와 생태계를 가진 오케스트레이션 도구</li>
</ul>
<h3 id="쿠버네티스를-선택하는-이유">쿠버네티스를 선택하는 이유</h3>
<ol>
<li><p><strong>자가 치유(Self-healing)</strong></p>
<ul>
<li>실패한 컨테이너를 자동으로 재시작</li>
</ul>
</li>
<li><p><strong>자동 확장(Auto-scaling)</strong></p>
<ul>
<li>부하에 따라 컨테이너 수 자동 조절</li>
</ul>
</li>
<li><p><strong>버전 관리와 롤백</strong></p>
<ul>
<li>이전 상태로 쉽게 복구 가능</li>
</ul>
</li>
<li><p><strong>멀티 클라우드 호환성</strong></p>
<ul>
<li>GCP, AWS, Azure, 온프레미스 등 어디서든 실행 가능</li>
</ul>
</li>
<li><p><strong>강력한 생태계</strong></p>
<ul>
<li>다양한 오픈소스 프로젝트와 연동 가능<br>(예: <code>Kubeflow</code>, <code>Tekton</code>, <code>Istio</code>, <code>KNative</code> 등)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="🏁-마무리">🏁 마무리</h2>
<p>컨테이너는 <strong>Dev-Ops</strong> 전체 프로세스를 변화시켰고,<br><strong>오케스트레이션</strong>은 이를 실전에서 <strong>쓸 수 있게 만들어 주는 핵심 기술</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React + TS 환경에서 MSW 도입 후기]]></title>
            <link>https://velog.io/@boseong-choi/React-TS%EC%97%90-MSW-%EB%8F%84%EC%9E%85-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@boseong-choi/React-TS%EC%97%90-MSW-%EB%8F%84%EC%9E%85-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Fri, 05 Jan 2024 04:18:31 GMT</pubDate>
            <description><![CDATA[<h3 id="1-도입-배경">1. 도입 배경</h3>
<p>이전 팀 프로젝트에서 Spring + Docker + Redis 기반의 백엔드 환경 설치 과정에 오류가 생겨 제공 받지 못했다. API 구현 및 명세서는 아직 준비중이었고, 이상적인 개발 과정은 기획 및 <code>MVP 모델수립 -&gt; 백엔드 개발 -&gt; 프론트엔드 개발</code>이겠지만 여태까지 겪었던 현업 및 팀 프로젝트의 과정은 위 3가지의 과정을 병행해야 하는 경우가 많았다. </p>
<p>결국에 백엔드의 API를 제공받고 활용해야 하며 API에 대해 종속적이라면, 해당 부분이 완성되기 전까지는 프론트엔드에서 개발을 진행할 수 없고, 그 부분이 진행된 후에나 개발이 가능하다. 따라서, 마냥 기다리기에는 시간이 부족할 것으로 예상되어 MSW를 이용한 API Mocking하기로 했다.</p>
<p>이렇게 하면 지속적으로 기능 구현이 가능하고, 실제 API 연동 시에도 간편하게 코드를 교체할 수 있다. 또한, 주요 기능 중 회원가입을 먼저 구현하기로 했기 때문에 OAuth, 소셜 로그인 및 accessToken 등에 대해서 자유로워서 MSW를 적극 도입하기로 했다.</p>
<hr>
<h3 id="2-msw의-진행-과정">2. MSW의 진행 과정</h3>
<p>MSW(Mock Service Worker)는 API Mocking 라이브러리로, 네트워크 요청을 가로채서 모의 응답(Mocked response)을 보내주는 역할을 한다. 따라서 별도의 Mock 서버를 구축하지 않아도 API를 네트워크 수준에서 Mocking 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/boseong-choi/post/22a9cc5d-737c-4d78-a0f9-cf57e81ce6a9/image.png" alt=""></p>
<ol>
<li>브라우저에서 요청을 보낸다</li>
<li>Service Worker에서 요청을 받고 그 요청을 복제한다. 이후 MSW에게 전송한다.</li>
<li>MSW는 요청과 일치하는 목업을 생성한다.</li>
<li>MSW가 모킹한 응답을 다시 Service Worker에게 전송한다.</li>
<li>Service Worker는 모킹된 응답을 브라우저에게 보내고 브라우저는 이 응답을 받는다.</li>
</ol>
<hr>
<h3 id="3-설치-과정">3. 설치 과정</h3>
<h4 id="프론트엔드-개발-환경--vite--react--typescript">프론트엔드 개발 환경 : Vite + React + TypeScript</h4>
<h4 id="1-msw-설치">1. MSW 설치</h4>
<pre><code>npm install msw</code></pre><p><img src="https://velog.velcdn.com/images/boseong-choi/post/34b97e21-bf93-4189-a269-b125b345bca9/image.png" alt=""></p>
<p>패키지 설치가 끝나면 자동으로 public 폴더에 <code>mockServiceWorker.js</code> 파일이 생성된다.</p>
<h4 id="2-msw-사용">2. MSW 사용</h4>
<ul>
<li>src 폴더 안에 mocks라는 폴더를 만든다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boseong-choi/post/48e43abf-91b1-4179-a538-87e427c09e57/image.png" alt=""></p>
<ul>
<li>handler 함수 생성</li>
</ul>
<pre><code class="language-typescript">//     src\mocks\handlers.ts

import { rest } from &#39;msw&#39;

const mockData = [&#39;test1&#39;, &#39;test2&#39;, &#39;test3&#39;]
const tempEmail = &#39;temp@gmail.com&#39;

const emailAPI = {
  sendVerificationEmail: `/auth/verification/send/${tempEmail}`,
}

export const handlers = [
  rest.get(&#39;/test&#39;, (_req, res, ctx) =&gt; {
    // 가짜 응답 데이터 생성 (원하는대로 수정 가능)
    return res(ctx.status(200), ctx.json(mockData))
  }),

  rest.post(&#39;/auth/signup&#39;, (_req, res, ctx) =&gt; {
    // 가짜 응답 데이터 생성 (원하는대로 수정 가능)
    return res(ctx.status(201))
  }),

  rest.post(emailAPI.sendVerificationEmail, (_req, res, ctx) =&gt; {
    const randomCode = Math.floor(Math.random() * 1000000) + 100000
    randomCode.toString()
    return res(
      ctx.status(200),
      ctx.json({
        message: &#39;Verification email sent successfully&#39;,
        email: tempEmail,
        code: randomCode,
      })
    )
  }),
]</code></pre>
<p>REST API 방식의 HTTP 메서드를 작성할 수 있다. 위 코드는 MSW를 사용하여 회원가입 기능 중 이메일 관련 API를 모킹하는 예시이다.</p>
<ul>
<li>service worker 생성</li>
</ul>
<pre><code class="language-typescript">// src\mocks\browsers.ts

import { setupWorker } from &#39;msw&#39;
import { handlers } from &#39;./handlers&#39;

export const worker = setupWorker(...handlers)</code></pre>
<p>setupWorker() 함수는 브라우저에서 API mocking을 활성화할 수 있도록 클라이언트-작업자 간 통신을 준비한다.</p>
<ul>
<li>MSW 실행</li>
</ul>
<p>마지막으로, MSW를 실행시키기 위한 코드를 추가한다. 환경 변수에 따라 MSW를 사용하기 위한 분기를 포함하여 현재 실행중인 환경이 dev 환경일때만 실행할 수 있도록 한다.</p>
<pre><code class="language-typescript">// src\main.tsx

if (process.env.NODE_ENV === &quot;development&quot;) {
    worker.start();
}</code></pre>
<p>브라우저 개발자 도구에서 <code>[MSW] Mocking enabled.</code> 라는 메세지가 보이면 MSW가 정상적으로 실행 중인 상태다.</p>
<hr>
<h3 id="4-후기">4. 후기</h3>
<p>MSW를 사용하면 API 요청 및 응답을 가로채고 모의 데이터로 대체할 수 있어서 테스트 및 개발이 훨씬 용이해졌다. 특히 백엔드 API가 아직 완전히 개발되지 않았을 때나 여러 상황을 시뮬레이션 하기에 적합했다. 또한, 백엔드 API의 구현 여부와 상관없이 프론트단을 독립적으로 개발할 수 있어서 좋았다.</p>
<p>하지만 실제 API와 모킹된 데이터 간의 차이가 발생할 수 있어서 특정 케이스를 놓치거나 예상치 못한 문제가 발생할 수 있는 점과, 환경 설정 및 초기 세팅이 쉽지 않았다는 느낌도 들었다. 이 점은 다음 협업 때 MSW를 사용한다면 개선할 수 있을 것 같다.</p>
<hr>
<h3 id="5-참고자료">5. 참고자료</h3>
<p><a href="https://jaypedia.tistory.com/382">https://jaypedia.tistory.com/382</a>
<a href="https://tech.kakao.com/2021/09/29/mocking-fe/">https://tech.kakao.com/2021/09/29/mocking-fe/</a>
<a href="https://mswjs.io/docs/integrations/browser">https://mswjs.io/docs/integrations/browser</a>
<a href="https://mswjs.io/docs/api/setup-worker">https://mswjs.io/docs/api/setup-worker</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Todo App]]></title>
            <link>https://velog.io/@boseong-choi/TypeScript-Todo-App</link>
            <guid>https://velog.io/@boseong-choi/TypeScript-Todo-App</guid>
            <pubDate>Mon, 04 Sep 2023 09:44:41 GMT</pubDate>
            <description><![CDATA[<p>그동안 배운 타입스크립트 문법으로 간단한 투두 리스트를 구현해보기로 하였다. 구현하면서 배우지 못한 문법도 추가로 작성할 예정이며, 기본적인 템플릿은 AI로 생성했다.</p>
<hr>

<h3 id="01-html">01. HTML</h3>
<pre><code class="language-HTML">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;styles.css&quot; /&gt;
    &lt;title&gt;Todo App&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div class=&quot;todo-container&quot;&gt;
      &lt;h1&gt;Todo App&lt;/h1&gt;
      &lt;input type=&quot;text&quot; id=&quot;taskInput&quot; placeholder=&quot;할 일 추가&quot; /&gt;
      &lt;button id=&quot;addTask&quot;&gt;추가&lt;/button&gt;
      &lt;ul id=&quot;taskList&quot;&gt;
        &lt;!-- 동적으로 추가할 내용 --&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
    &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<ol>
<li>할 일 목록을 배열로 초기 생성한다.</li>
<li><code>input</code> <code>button</code> <code>ul</code> 태그의 요소를 가져온다.</li>
<li>할 일 추가 함수를 작성한다.</li>
<li>렌더링 함수를 작성한다.</li>
<li>버튼 클릭 이벤트 처리</li>
</ol>
<hr>


<h3 id="02-typescript">02. TypeScript</h3>
<h4 id="i-task-객체">i. Task 객체</h4>
<pre><code class="language-ts">type Task = {
  id: number;
  text: string;
  completed: boolean;
};</code></pre>
<p>코드를 구조화하고, 유지보수를 쉽게 하기 위하여 객체로 생성했다. <code>id</code>는 고유 식별자, <code>text</code> 는 내용, <code>completed</code>는 완료 여부 속성이다.</p>
<h4 id="ii-tasks-배열">ii. tasks 배열</h4>
<pre><code class="language-ts">const tasks: Task[] = [];</code></pre>
<p><code>Tasks</code> 객체를 담는 배열. 추가한 리스트를 관리한다.</p>
<h4 id="iii-html-요소-가져오기">iii. HTML 요소 가져오기</h4>
<pre><code class="language-ts">const taskInput = document.getElementById(&#39;taskInput&#39;) as HTMLInputElement;
const addTaskButton = document.getElementById(&#39;addTask&#39;) as HTMLButtonElement;
const taskList = document.getElementById(&#39;taskList&#39;) as HTMLUListElement;</code></pre>
<p>HTML에서 <code>taskInput</code> <code>addTaskButton</code> <code>taskList</code> 요소를 가져온다. taskInput은 할 일을 입력하는 입력 필드, addTaskButton은 할 일을 추가하는 버튼, taskList는 할 일 목록을 표시하는 <code>&lt;ul&gt;</code> 요소를 나타낸다.</p>
<p><strong>as 키워드</strong></p>
<pre><code class="language-ts">let title = document.querySelector(&#39;#title&#39;);
title.innerHTML = &#39;Hi&#39;</code></pre>
<p>타입스크립트는 위와 같이 작성하면 에러를 발생시킨다. <code>title</code> 변수의 타입이 <code>HTMLElement | null</code> 이기 때문에 타입 확정이 불가능한 상태에서 DOM 조작을 금지시킨다. </p>
<p>따라서 Type Narrowing 또는 <strong>Type Assertion</strong>을 통해 타입을 확정시킨다. <code>as</code> 키워드는 Type Assertion으로 사용되며 컴파일러에게 <strong>&quot;이 변수는 이러한 타입이다&quot;</strong> 라고 명시해준다.</p>
<h4 id="iv-할-일-추가-함수">iv. 할 일 추가 함수</h4>
<pre><code class="language-ts">// 할 일 추가 함수
function addTask() {
  const text = taskInput.value.trim();
  if (!text) return;
  const newTask: Task = {
    id: tasks.length + 1,
    text,
    completed: false,
  };

  tasks.push(newTask);
  taskInput.value = &#39;&#39;;
  renderTask(newTask);
}</code></pre>
<p><code>addTask</code> 함수는 사용자가 input 태그에 입력한 값을 가져오고, 새로운 Task 객체를 만든다음에 tasks 배열에 푸시한다. 그리고 나서 input 태그 필드를 비우고, 렌더링하는 함수인 renderTask를 실행한다.</p>
<h4 id="v-렌더링-함수--버튼-클릭-이벤트">v. 렌더링 함수 &amp; 버튼 클릭 이벤트</h4>
<pre><code class="language-ts">function renderTask(task: Task) {
  const taskItem = document.createElement(&#39;li&#39;);
  taskItem.innerHTML = `
        &lt;input type=&quot;checkbox&quot; id=&quot;task-${task.id}&quot; ${task.completed ? &#39;checked&#39; : &#39;&#39;}&gt;
        &lt;label for=&quot;task-${task.id}&quot;&gt;${task.text}&lt;/label&gt;
    `;
  taskList.appendChild(taskItem);
}

// 할 일 추가 버튼 클릭 이벤트 처리
addTaskButton.addEventListener(&#39;click&#39;, addTask);</code></pre>
<p><code>renderTask</code> 함수는 <code>li</code> 요소를 생성하고 Task 객체를 HTML 목록으로 렌더링한다. 이후 추가 버튼에 이벤트 리스너를 추가해서 버튼을 클릭했을 때 addTask 함수가 실행되도록 추가한다.</p>
<hr>

<h3 id="03-전체코드typescript">03. 전체코드(TypeScript)</h3>
<pre><code class="language-ts">// app.ts

// Task 타입 정의
type Task = {
  id: number;
  text: string;
  completed: boolean;
};

// 할 일 목록
const tasks: Task[] = [];

// HTML 요소 가져오기
const taskInput = document.getElementById(&#39;taskInput&#39;) as HTMLInputElement;
const addTaskButton = document.getElementById(&#39;addTask&#39;) as HTMLButtonElement;
const taskList = document.getElementById(&#39;taskList&#39;) as HTMLUListElement;

// 할 일 추가 함수
function addTask() {
  const text = taskInput.value.trim();
  if (!text) return;

  const newTask: Task = {
    id: tasks.length + 1,
    text,
    completed: false,
  };

  tasks.push(newTask);
  taskInput.value = &#39;&#39;;
  renderTask(newTask);
}

// 할 일 렌더링 함수
function renderTask(task: Task) {
  const taskItem = document.createElement(&#39;li&#39;);
  taskItem.innerHTML = `
        &lt;input type=&quot;checkbox&quot; id=&quot;task-${task.id}&quot; ${task.completed ? &#39;checked&#39; : &#39;&#39;}&gt;
        &lt;label for=&quot;task-${task.id}&quot;&gt;${task.text}&lt;/label&gt;
    `;
  taskList.appendChild(taskItem);
}

// 할 일 추가 버튼 클릭 이벤트 처리
addTaskButton.addEventListener(&#39;click&#39;, addTask);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입 별칭 (Type Aliases)]]></title>
            <link>https://velog.io/@boseong-choi/%ED%83%80%EC%9E%85-%EB%B3%84%EC%B9%AD-Type-Aliases</link>
            <guid>https://velog.io/@boseong-choi/%ED%83%80%EC%9E%85-%EB%B3%84%EC%B9%AD-Type-Aliases</guid>
            <pubDate>Thu, 31 Aug 2023 06:51:46 GMT</pubDate>
            <description><![CDATA[<h3 id="01-타입-별칭-type-aliases-이란">01. 타입 별칭 (Type Aliases) 이란?</h3>
<p>타입스크립트에서 <strong>타입 별칭(Type Aliases)</strong>은 특정 타입에 이름을 부여하여 재사용 가능한 변수를 말한다. 타입 별칭을 사용하면 반복적으로 작성해야 하는 복잡한 타입을 간결하게 표현하거나, 여러 곳에서 사용되는 타입을 중앙에서 관리하며 유지보수하기 용이해진다.</p>
<pre><code class="language-ts">type TypeName = TypeDefinition;</code></pre>
<p><code>TypeName</code>은 타입 별칭의 이름이며, <code>TypeDefinition</code>은 해당 별칭이 나타내는 실제 타입을 정의한다.</p>
<p>오브젝트 타입도 저장이 가능하다. 아래 코드에서 <code>Point</code>와 <code>Person</code>은 각각 별칭이며, 객체 타입을 간결하게 표현할 수 있다. 함수의 파리미터나 리턴값으로 사용될 때 유용하다.</p>
<pre><code class="language-ts">type Point = {
  x: number;
  y: number;
};

type Person = {
  name: string;
  age: number;
};
</code></pre>
<pre><code class="language-ts">function distance(p1: Point, p2: Point): number {
  // ...
}

const john: Person = {
  name: &quot;John&quot;,
  age: 30,
};</code></pre>
<hr>

<h3 id="02-optional">02. Optional</h3>
<pre><code class="language-ts">type Square = {
  color?: string;
  width: number;
};</code></pre>
<p><code>Square</code> 타입은 color 속성과 width 속성을 가진 객체를 표현한다. color 속성 뒤에 붙은 물음표(?)는 해당 속성이 <strong>옵셔널(선택적)</strong>이라는 것을 나타낸다. </p>
<p>Square 타입으로 선언된 객체는 color 속성을 포함할 수도 있고, 포함하지 않을 수도 있지 <code>width</code> 속성은 반드시 존재해야 하는 필수 속성이다. </p>
<p><img src="https://velog.velcdn.com/images/boseong-choi/post/d1b3dbd5-a74a-4626-a54d-e1a1f03ac7c7/image.png" alt=""></p>
<hr>

<h3 id="03-union-types">03. Union Types</h3>
<p>Type Aliases도 <code>|</code> 키워드를 사용하여 Union Type을 만들 수 있다. <code>&amp;</code> 기호를 쓴다면 인터섹션 타입(Intersection Type)을 만들 수 있다. </p>
<blockquote>
<p><strong>인터섹션 타입(Intersection Type)</strong>
여러 타입을 모두 만족하는 하나의 타입을 의미</p>
</blockquote>
<pre><code class="language-ts">type ID = number | string;

type Address = {
  street: string;
  city: string;
};

type DetailedPerson = Person &amp; Address;</code></pre>
<hr>

<h3 id="04-function">04. Function</h3>
<p>함수의 파라미터와 리턴값도 타입지정이 가능하다.</p>
<pre><code class="language-ts">// 타입 별칭 정의
type Person = {
  name: string;
  age: number;
};

// 함수에서 타입 별칭 사용
function greet(person: Person): string {
  return `안녕하세요, ${person.name}님! 당신은 ${person.age}살이군요.`;
}

// 예제
const user: Person = { name: &#39;Alice&#39;, age: 30 };
const greeting: string = greet(user);

console.log(greeting); // &quot;안녕하세요, Alice님! 당신은 30살이군요.&quot;</code></pre>
<p><code>Person</code>이라는 타입 별칭을 정의하고, greet라는 함수의 파라미터로 사용했다. 함수 내부에서는 person.name 및 person.age를 사용하여 인사말을 생성하고. <code>user</code> 객체를 만들어 함수에 전달하고 함수 호출 결과를 출력한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수 타입 선언]]></title>
            <link>https://velog.io/@boseong-choi/%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8</link>
            <guid>https://velog.io/@boseong-choi/%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8</guid>
            <pubDate>Mon, 28 Aug 2023 08:16:36 GMT</pubDate>
            <description><![CDATA[<p><strong>해당 글은 개인 공부용도로 작성되었으므로 참고바랍니다.</strong></p>
<hr>

<p>타입스크립트는 변수와 파라미터, 반환 값 등의 타입을 명시적으로 선언할 수 있다. 이로 인해 코드 작성 시 타입 관련 오류를 미리 찾을 수 있고, 런타임 에러를 사전에 방지할 수 있다.</p>
<hr>

<h3 id="01-함수의-타입-선언">01. 함수의 타입 선언</h3>
<blockquote>
<p>JavaScript</p>
</blockquote>
<pre><code class="language-js">function sum(a, b) {
  return a + b;
}</code></pre>
<blockquote>
<p>TypeScript</p>
</blockquote>
<pre><code class="language-ts">function sum(a: number, b: number): number {
  return a + b;
}</code></pre>
<p>함수에 타입을 지정할 때는 2곳에 지정이 가능하다.</p>
<ol>
<li>파라미터</li>
<li>리턴 값</li>
</ol>
<hr>


<h3 id="02-void-타입">02. Void 타입</h3>
<p><code>void</code> 타입은 함수의 리턴 타입 중 하나로, 함수가 아무런 값을 반환하지 않을 때 사용된다. 로그를 출력하는 함수나 데이터를 저장하는 함수 등이 해당된다. <code>이벤트 핸들러</code> 함수도 주로 특정 이벤트가 발생했을 때 호출되며 반환 값이 필요하지 않은 경우가 많은데, 이런 경우에도 void 타입을 사용할 수 있다.</p>
<pre><code class="language-ts">function sum (a: number, b: number): void {
  return a + b // number 형식은 void 형식에 할당할 수 없다.
}</code></pre>
<hr>


<h3 id="03-인자">03. 인자</h3>
<p>자바스크립트는 선언한 함수의 파라미터 개수와 인자의 개수가 일치하지 않더라도 에러를 발생시키지 않는다. 하지만 타입스크립트는 에러를 발생시키므로 함수의 인자를 모두 필수 값으로 간주한다.</p>
<pre><code class="language-ts">function sum (a: number, b: number): number {
  return a + b
}

sum(1) // 2개의 인수가 필요한데 1개의 인수를 가져옴.
sum(1,2)
sum(1,2,3) // 2개의 인수가 필요한데 3개의 인수를 가져옴.</code></pre>
<p>위 특성은 정의된 파라미터 갯수 만큼 인자를 넘기지 않아도 되는 자바스크립트의 특성과 반대된다. 만약 이러한 특성을 살리고 싶다면 <code>?</code>를 이용해서 옵션 정의를 할 수 있다.</p>
<pre><code class="language-ts">function option (a?: number) {
  return a;
}

sum(10);
sum();</code></pre>
<p>하지만 아래와 같은 코드는 에러를 발생시킨다. 변수의 타입이 아직 확정되지 않았으므로 값을 변경시킬 수 없기 때문이다. 타입스크립트에서 <code>+</code> 연산자는 2개의 피연산자가 모두 숫자인 경우에만 수학적으로 덧셈을 수행하는데, 피연산자가 <code>string</code> 일 가능성이 있으므로 에러를 발생시킨다.</p>
<pre><code class="language-ts">function plusOne(x :number | string){ 
    return x + 1 
} </code></pre>
<pre><code class="language-ts">function myFunc(x? :number) :number { 
    return x * 2 
}  </code></pre>
<p>파라미터를 옵션으로 정의할 경우 <code>number | undefined</code> 타입으로 정의되기 때문에 타입을 확정할 수 없어 에러를 발생시킨다. </p>
<hr>


<h3 id="04-type-narrowing">04. Type Narrowing</h3>
<pre><code class="language-ts">function plusOne(x :number | string){ 
    return x + 1 // &#39;+&#39; 연산자를 적용할 수 없다.
} </code></pre>
<p>타입스크립트에서 + 연산자는 2개의 피연산자가 모두 숫자인 경우에만 수학적으로 덧셈을 수행하는데, 피연산자가 string 일 가능성이 있으므로 에러를 발생시킨다. </p>
<p>이러한 경우 <code>Type Narrowing</code>을 주로 해준다. <code>Type Narrowing</code>은 변수의 타입을 좁혀나가는 프로세스를 말한다. </p>
<pre><code class="language-ts">function plusOne(x :number | string){
    if (typeof x === &#39;number&#39;){
        return x + 1;
    } else if (typeof x === &#39;string&#39;){
        return Number(x) + 1;
    }
}</code></pre>
<p><code>if문</code>과 <code>typeof</code> 키워드로 typeof x === &#39;number&#39; 조건에서는 숫자 타입인 경우에는 그대로 x + 1을 반환하고, typeof x === &#39;string&#39; 조건에서는 문자열을 숫자로 변환한 후에 Number(x) + 1을 반환하는 방식으로 처리할 수 있다. 과정이 번거롭긴 하지만 타입이 확실하지 않을 때 생기는 부작용을 막을 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[기본 타입 (2)]]></title>
            <link>https://velog.io/@boseong-choi/%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85-2</link>
            <guid>https://velog.io/@boseong-choi/%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85-2</guid>
            <pubDate>Thu, 24 Aug 2023 07:15:22 GMT</pubDate>
            <description><![CDATA[<p><strong>해당 글은 개인 공부용도로 작성되었으므로 참고바랍니다.</strong></p>
<hr>

<h3 id="any">Any</h3>
<p>들어올 타입이 확실치 않거나 알지 못할 때 타입을 표현해야 할 수도 있다. 클라이언트로부터 받은 데이터, 서드 파티 라이브러리 같은 동적인 컨텐츠가 주로 그렇다. 이 경우 타입 검사를 하지 않고, 그 값들이 컴파일 시간에 검사를 통과하길 원할 때 <code>any</code> 타입을 사용한다.</p>
<pre><code class="language-ts">let notSure: any = 4;
notSure = &quot;maybe a string instead&quot;;
notSure = false;</code></pre>
<hr>

<h3 id="unknown">Unknown</h3>
<p><code>any</code> 와 마찬가지로 모든 종류의 값과 메소드를 할당할 수 있지만, 사용하기 전에 타입을 체크하거나 변환해야한다. unknown 타입을 사용하면 컴파일러가 타입 검사를 엄격하게 강제하며, 안전한 타입 변환을 해야만 해당 값의 속성과 메소드에 접근할 수 있다</p>
<pre><code class="language-ts">let y: unknown = 5;

if (typeof y === &quot;string&quot;) {
    console.log(y.toUpperCase()); // 문자열인 경우에만 실행됨
}</code></pre>
<hr>

<h3 id="union-types">Union Types</h3>
<p>number나 string 타입이 들어올 것이라고 예상할 때, OR 연산자랑 비슷하게 <code>|</code> 연산자를 사용하여 타입을 정의할 수 있다.</p>
<pre><code class="language-ts">let age: string | number = 25;
let arr: (number | string)[] = [1,&#39;2&#39;,3];
let obj: { a: string | number } = { a: &#39;3&#39;};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[기본 타입 (1)]]></title>
            <link>https://velog.io/@boseong-choi/%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@boseong-choi/%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85</guid>
            <pubDate>Tue, 22 Aug 2023 05:20:21 GMT</pubDate>
            <description><![CDATA[<h4 id="해당-글은-개인-공부용도로-작성되었으므로-참고바랍니다">해당 글은 개인 공부용도로 작성되었으므로 참고바랍니다.</h4>
<hr>

<h3 id="01-기본-타입">01. 기본 타입</h3>
<p>TypeScript는 JavaScript와 거의 동일한 데이터 타입을 지원하며, 열거 타입을 사용하여 더 편리하게 사용할 수 있다.</p>
<p>가장 기본적인 데이터 타입은 JavaScript, TypeScript에서 boolean 값이라고 일컫는 참/거짓(true/false) 값이다.</p>
<pre><code class="language-ts">let isDone: boolean = false;</code></pre>
<p><strong>변수명: 타입</strong> 으로 인해 isDone 이라는 변수는 boolean 타입이 되며 숫자나 문자등을 할당하면 에러가 발생한다.</p>
<p><strong>배열</strong> 타입은 두 가지 방법으로 쓸 수 있다. 첫 번째 방법은, 배열 요소들을 나타내는 타입 뒤에 <code>[]</code>를 쓰는 것이다.</p>
<pre><code class="language-ts">let list: number[] = [1, 2, 3];</code></pre>
<p>두 번째 방법은 제네릭 배열 타입을 쓸 수 있다. </p>
<pre><code class="language-ts">let list: Array&lt;number&gt; = [1, 2, 3];</code></pre>
<p><strong>object</strong> 자료안에 들어갈 타입은 내가 만들 object와 똑같은 모습으로 지정하면 된다.</p>
<pre><code class="language-ts">let myInfo : { age : number } = { age : 20 }</code></pre>
<hr>

<h3 id="02-타입-명시">02. 타입 명시</h3>
<p>타입스크립트를 사용한다고 해서 모든 변수에 타입을 지정해야 하는 것은 아니다. 변수 생성시 타입스크립트가 타입을 자동으로 부여해주기 때문이다.</p>
<h4 id="내가-생각하는-타입-명시를-해야할-때와-하지-않아도-될-경우">내가 생각하는 타입 명시를 해야할 때와 하지 않아도 될 경우</h4>
<ol>
<li>변수의 타입을 명시해야 하는 경우</li>
</ol>
<ul>
<li>함수의 파라미터와 리턴 값 : 함수의 경우 타입을 예상하기 어렵기 때문이다.</li>
<li>객체 또는 배열의 타입이 동적일 때</li>
<li>타입 예상이 어려울 때</li>
</ul>
<ol start="2">
<li>변수의 타입을 명시하지 않아도 되는 경우</li>
</ol>
<ul>
<li><p>간단한 리터럴
<code>let name = &#39;kim&#39;;</code> 과 같이 타입을 유추하기 쉽고 자동으로 string 타입이 할당되기 때문이다.</p>
</li>
<li><p>변수의 타입이 확실한 경우</p>
</li>
</ul>
<hr>

<h3 id="03-예제">03. 예제</h3>
<p>개인정보를 담고 있는 객체를 만들고, 타입을 지정한다.</p>
<pre><code class="language-ts">let person : {
  firstName : string;
  lastName : string;
  age : number;
  isStudent : boolean;
} = {
  firstName : &quot;John&quot;,
  lastName : &quot;Doe&quot;,
  age : 25,
  isStudent : true,
};</code></pre>
<p>만약 해당 객체를 여러 곳에서 재사용하고, 구조가 복잡하거나 가독성을 높이려고 할 때는 타입 정의를 사용할 수 있다. 인터페이스 같은 개념인 것 같다.</p>
<pre><code class="language-ts">// 개인 정보를 담는 객체 타입 정의
type Person = {
  firstName: string;
  lastName: string;
  age: number;
  isStudent: boolean;
};

// 개인 정보 객체 생성
const person: Person = {
  firstName: &quot;John&quot;,
  lastName: &quot;Doe&quot;,
  age: 25,
  isStudent: true,
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[41장 타이머 ~ 44장 REST API]]></title>
            <link>https://velog.io/@boseong-choi/41%EC%9E%A5-%ED%83%80%EC%9D%B4%EB%A8%B8-44%EC%9E%A5-REST-API</link>
            <guid>https://velog.io/@boseong-choi/41%EC%9E%A5-%ED%83%80%EC%9D%B4%EB%A8%B8-44%EC%9E%A5-REST-API</guid>
            <pubDate>Sun, 30 Jul 2023 07:47:09 GMT</pubDate>
            <description><![CDATA[<h2 id="41장-타이머">41장 타이머</h2>
<p>함수를 명시적으로 호출하지 않고 일정 시간이 지난 다음에 호출을 예약하려면 타이머 함수를 쓰면 됨. setTimeout 과 setInterval 은 일정 시간이 지나면 콜백 함수가 호출된다. setTimout은 타이머가 한번만 동작하고 setInterval은 반복적으로 동작.</p>
<h3 id="413-디바운스와-스로틀">41.3 디바운스와 스로틀</h3>
<p>scroll, mousemove 같은 이벤트는 console.log 이벤트 행위에 비해 좀 과도하게 호출되는 경향이 있는것 같다. 이때 디바운스랑 스로틀을 써서 과도한 이벤트 호출을 방지할 수도 있음. </p>
<h4 id="디바운스">디바운스</h4>
<p>setTimeout과 비슷하게 일정시간동안 이벤트가 발생되지 않으면 이벤트 핸들러가 한번만 호출.</p>
<h4 id="스로틀">스로틀</h4>
<p>일정시간 간격으로 이벤트 핸들러가 최대 한번만 호출되도록 한다.</p>
<h2 id="42장-비동기-프로그래밍">42장 비동기 프로그래밍</h2>
<p>자바스크립트 엔진은 한 번에 하나의 태스크만 실행할 수 있는 <strong>싱글 스레드</strong> 방식으로 동작한다. 때문에 시간이 걸리는 태스크를 실행하는 경우 <strong>블로킹</strong>이 발생한다.</p>
<p>현재 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식을 <strong>동기처리</strong>라고 하고 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식을 <strong>비동기처리</strong>라고 한다.</p>
<p>타이머 함수인 setTimeout과 setTimeinterval, HTTP 요청, 이벤트 핸들러는 비동기 처리 방식으로 동작한다.</p>
<h3 id="이벤트-루프와-태스크-큐">이벤트 루프와 태스크 큐</h3>
<p>이벤트 루프는 동시성을 지원하는 것이며 콜 스택이 비어 있고 태스큐 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적으로 콜 스택으로 이동시킨다.</p>
<p>태스크 큐는 setTimeout과 setTimeinterval과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 장소.</p>
<p>비동기 함수인 setTimeout의 콜백 함수는 태스크 큐에 푸시되어 대기하다가 콜 스택이 비게 되면, 콜 스택에 푸시되어 실행된다. </p>
<h2 id="43장-ajax">43장 AJAX</h2>
<p>브라우저가 서버에게 비동기 방식으로 데이터를 요청하고 서버가 응답한 데이터를 받아서 동적으로 렌더링하는 프로그래밍 방식을 말한다.</p>
<h4 id="장점">장점</h4>
<ol>
<li>변경할 부분을 갱신하는데 필요한 데이터만 서버로부터 받기 때문에 불필요한 데이터 통신이 발생하지 않는다.</li>
<li>변경할 필요가 없는 부분으 다시 렌더링하지 않는다. 따라서 화면이 순간적으로 깜빡이는 현상이 발생하지 않는다.</li>
<li>비동기 방식으로 동작하기 때문에 서버가 요청을 보낸 이후 블로킹이 발생하지 않는다.</li>
</ol>
<h3 id="json">JSON</h3>
<p>자바스크립트의 객체 리터럴과 유사하게 키와 값으로 구성된 순수한 텍스트다. </p>
<h2 id="44장-rest-api">44장 REST API</h2>
<p><a href="https://velog.io/@boseong-choi/RESTful-API%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C">https://velog.io/@boseong-choi/RESTful-API%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[40장 이벤트]]></title>
            <link>https://velog.io/@boseong-choi/40%EC%9E%A5-%EC%9D%B4%EB%B2%A4%ED%8A%B8</link>
            <guid>https://velog.io/@boseong-choi/40%EC%9E%A5-%EC%9D%B4%EB%B2%A4%ED%8A%B8</guid>
            <pubDate>Thu, 27 Jul 2023 07:27:08 GMT</pubDate>
            <description><![CDATA[<h3 id="401-이벤트-드리븐-프로그래밍">40.1 이벤트 드리븐 프로그래밍</h3>
<p>브라우저는 처리해야 할 특정 사건이 발생하면 이를 감지하여 이벤트를 발생시킨다. 이때 이벤트가 발생했을 때 호출될 함수를 이벤트 핸들러라 하고, 이벤트가 발생했을 때 브라우저에게 이벤트 핸들러 호출을 위임하는 것을 이벤트 핸들러 등록이라 한다.</p>
<p>브라우저는 사용자의 버튼 클릭을 감지하여 클릭 이벤트를 발생시킬 수 있다. 그리고 클릭 이벤트가 발생하면 특정함수를 호출하도록 브라우저에게 위임할 수 있다. 프로그램의 흐름을 이벤트 중심으로 제어하는 프로그래밍 방식을 이벤트 드리븐 프로그래밍이라 한다.</p>
<h3 id="402-이벤트-타입">40.2 이벤트 타입</h3>
<ol>
<li>마우스 이벤트</li>
<li>키보드 이벤트</li>
<li>포커스 이벤트</li>
<li>폼 이벤트
기타 등등</li>
</ol>
<h3 id="403-이벤트-핸들러-등록">40.3 이벤트 핸들러 등록</h3>
<p>이벤트 핸들러는 이벤트가 발생했을 때 브라우저에 호출을 위임한 함수. 이벤트가 발생하면 브라우저에 의해 호출될 함수가 이벤트 핸들러다.</p>
<ol>
<li><p>어트리뷰트 방식
HTML 요소의 어트리뷰트에는 이벤트 핸들러 어트뷰리트가 있다. 가급적 사용하지 말자.</p>
</li>
<li><p>프로퍼티 방식
이벤트 핸들러 프로퍼티에 함수를 바인딩하면 이벤트 핸들러가 등록된다.</p>
<pre><code class="language-js">const $button = document.querySelector(&#39;button&#39;);
</code></pre>
</li>
</ol>
<p>// 이벤트 핸들러 프로퍼티에 이벤트 핸들러를 바인딩한다.
$button.onclick = function () {
  // $button -&gt; 이벤트 타깃, onclick -&gt; 이벤트 타입, function () {} -&gt; 이벤트 핸들러
  console.log(&#39;button clicked&#39;);
};</p>
<pre><code>단, 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만 바인딩할 수 있다는 단점이 있다.

3. addEventListener 메서드
`이벤트 타깃.addEventListener(이벤트 타입, 이벤트 핸들러[, 옵션]);`
addEventListener 메서드는 이벤트 핸들러 프로퍼티에 바인딩된 이벤트 핸들러에 영향을 주지 않는다. 따라서 버튼 요소에서 클릭이벤트가 발생하면 2개의 이벤트 모두 호출된다. 단, 참조가 동일한 이벤트 핸들러를 중복 등록할 수 없다.

### 40.4 이벤트 핸들러 제거
addEventListener 메서드로 등록한 이벤트 핸들러는 removeEventListener 메서드로 제거할 수 있다. 인수는 addEventListener 메서드와 동일하게 전달한다. 하지만, 인수가 일치하지 않으면 제거되지 않는다.

removeEventListener 메서드에 인수로 전달한 이벤트 핸들러는 addEventListener 메서드로 등록한 이벤트 핸들러와 동일한 함수 객체여야 한다. 따라서 무명 함수를 사용한 이벤트 핸들러는 제거할 수 없다.

무명함수에 대해 직접 접근하기 어렵기 때문. 제거하려면 addEventListener 메서드에 전달한 이벤트 핸들러를 변수에 할당하여 사용해야 한다.

``` js
$button.addEventListener(&#39;click&#39;, () =&gt; {
  console.log(&#39;button clicked&#39;);
});

$button.removeEventListener(&#39;click&#39;, () =&gt; {
  console.log(&#39;button clicked&#39;);
}); // 제거되지 않는다.</code></pre><p>이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러는 removeEventListener 메서드로 제거할 수 없다. 이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러는 null을 할당하여 제거한다.</p>
<h3 id="405-이벤트-객체">40.5 이벤트 객체</h3>
<p>생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달된다.</p>
<pre><code class="language-js">function showCoords(event) {
  $msg.textContent = `X: ${event.clientX}, Y: ${event.clientY}`;
}

document.onclick = showCoords;</code></pre>
<p>이벤트 객체 : <code>PointerEvent {isTrusted: true, pointerId: 0, width: 1, height: 1, pressure: 0, …}</code>
클릭 이벤트 = document.onclick = showCoords; -&gt; 클릭 이벤트가 발생하면 showCoords 함수가 호출된다.</p>
<p>이때 이벤트 객체가 showCoords 함수의 첫 번째 인수로 전달되고 파라미터 event에 할당된다.
함수 이름만을 쓰는 이유는 이벤트의 결과값을 내가 아직 모르기 때문에 브라우저에게 위임하는 것이다. 그래서 암묵적으로 함수 이름을 쓰는 것이다라고 이해했다.</p>
<h3 id="406-이벤트-전파">40.6 이벤트 전파</h3>
<p>DOM 요소 노드에서 발생한 이벤트는 DOM 트리를 통해 전파된다. 그림 40-8 보시면 이해가 더 쉬울듯. 상위 요소에서 하위 요소로 전파되는 것이 캡처링 단계. 이벤트가 타깃에 도달하는게 타깃 단계. 하위 요소에서 상위 요소로 전파되는 것이 버블링 단계. </p>
<p>이처럼 이벤트는 이벤트를 발생시킨 타깃은 물론 상위 DOM 요소에서도 캐치할 수 있다.</p>
<h3 id="407-이벤트-위임">40.7 이벤트 위임</h3>
<p>하위 요소에 이벤트를 걸지 않고 이벤트 전파의 특징을 활용해서 상위 요소에 이벤트를 걸어서 상위 요소 너한테 맡길게. </p>
<p>784P 맨 아래 코드는 하위 요소에 일일이 이벤트 핸들러를 등록하고 있음. 만약 하위 요소가 동적이거나 한 100개 되면은 매우 불편. 이를 보완한게 786P 상위 요소에 이벤트 핸들러 등록.</p>
<p>matches 메서드. ul → li 로 전파되는건 눈에 잘 보이는 편인데. 이게 컴포넌트 방식으로 만들거나, 구조가 복잡할경우 잘 파악이 안됨. 이럴 때 특정 노드를 탐색해서 전파가 가능한지 사용하기도 하고, 허용되지 않은 타깃이 전파되는걸 막기도 함. 예제 40-33 타깃이 #fruits 자식 요소가 아니면 얼리 리턴해줌.</p>
<h3 id="408-dom-요소의-기본-동작-조작">40.8 DOM 요소의 기본 동작 조작</h3>
<ol>
<li><p>preventDefault 
기본 동작을 중지시킨다. 회원 가입 폼이 있는데, 따로 조건 제한 안하면 공백을 입력하든 입력 안하든 form action은 하게 되어있음. 그걸 방지하기 위해 input의 값을 받아서 그게  null 또는 공백이면 e.preventDefault() 해서 제출 동작을 막을 수 있음.</p>
</li>
<li><p>stopPropagation 
전파를 막는 메서드. 캡처링/버블링 둘다.</p>
</li>
</ol>
<h3 id="409-이벤트-핸들러-내부의-this">40.9 이벤트 핸들러 내부의 this</h3>
<p>일반함수로서 호출되는 함수 내부의 this는 전역 객채를 가리킴. 근데 예외로 이벤트 핸들러 어트리뷰트 방식에서 이벤트 핸들러를 호출할 때 아규먼트를 this로 넣으면 이벤트를 바인딩한 DOM 요소를 가리킨다.  </p>
<p>그리고 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식은 내부의 this가 이벤트를 바인딩한 DOM 요소를 가리킨다. 근데 이벤트핸들러를 화살표 함수로 정의하면 상위 스코프의 this를 가리킴. 화살표 함수는 자체적으로 this 바인딩이 없기 때문에.</p>
<h3 id="4010-이벤트-핸들러에-인수-전달">40.10 이벤트 핸들러에 인수 전달</h3>
<p>원래 함수에 아규먼트를 전달하려면 호출할 때 전달해야 하는데 이벤트 핸들러 프로퍼티랑 addEventListener 메서드는 이벤트 핸들러를 브라우저가 호출함. 그래서 아규먼트를 전달할 수 없지만 방법이 하나 있음.</p>
<h3 id="4011-커스텀-이벤트">40.11 커스텀 이벤트</h3>
<p>이벤트 객체는 MoustEvent 같은 이벤트 생성자 함수로 생성할 수 있다. 
예제 40-49 MouseEvent 생성자 함수로 클릭 이벤트 타입의 이벤트 객체를 생성.</p>
<p>797p 커스텀 이벤트 디스패치 : 실제로 이벤트가 발생한 것처럼 하고 싶다. 실제로 클릭한게 아니라 커스텀 이벤트 만들어놓고 클릭이벤트가 발생한 것처럼 구현할 때.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[39장 DOM]]></title>
            <link>https://velog.io/@boseong-choi/39%EC%9E%A5-DOM</link>
            <guid>https://velog.io/@boseong-choi/39%EC%9E%A5-DOM</guid>
            <pubDate>Mon, 24 Jul 2023 05:48:24 GMT</pubDate>
            <description><![CDATA[<p>렌더링 엔진은 HTML 문서를 파싱하고 DOM을 생성한다. DOM은 HTML 문서의 계층적 구조와 정보를 표현하며 이를 제어할 수 있는 API를 제공하며, 프로퍼티와 메서드를 제공하는 트리 자료구조이다.</p>
<h3 id="391-노드">39.1 노드</h3>
<p>노드는 트리 자료구조의 기본 구성 요소이다.</p>
<h4 id="트리-자료구조">트리 자료구조</h4>
<p>하나의 루트 노드에서 시작하여 나뭇가지 형태로 연결된 노드의 집합으로 계층적 관계를 표현한다.
치상위 노드를 루트 노드, 최하위 노드를 리프 노드라 한다. 이런 형태가 노드로 구성되어 있는 것을 DOM이라 한다. 그래서 DOM 트리라고도 한다.</p>
<p>모든 노드 객체는 Object, EventTarget, Node 인터페이스를 상속받는다. 문서 노드는 Document, HTMLDocument 인터페이스를 상속받고 어트리뷰트 노드는 Attr 인터페이스를 상속받는다. 요소 노드는 Element 인터페이스를 상속받고 추가적으로 태그의 종류별로 세분화된 인터페이스를 상속받는다. </p>
<p>프로토타입 체인 관점에서 살펴보면 input 요소 노드 객체는 HTMLElement, Element, Node, EventTarget, Object의 프로토타입 체인을 갖는다.</p>
<h3 id="392-요소-노드-취득">39.2 요소 노드 취득</h3>
<h4 id="1-id를-사용하여-요소-노드를-취득하는-방법">1. id를 사용하여 요소 노드를 취득하는 방법</h4>
<p>Document.getElementById 메서드는 인수로 전달받은 id 어트리뷰트 값을 갖는 요소 노드를 탐색하여 반환한다. getElementById 메서드는 Document.prototype의 프로퍼티이므로 document 객체를 통해 호출한다.</p>
<pre><code class="language-js">// id 값이 banana인 요소 노드를 탐색하여 반환한다.
const $elem = document.getElementById(&#39;banana&#39;);

// 취득한 요소 노드의 style.color 프로퍼티 값을 변경한다.
$elem.tyle.color = &#39;red&#39;;</code></pre>
<p>HTML 문서내에 id 값이 여러 개 존재하더라도 에러가 발생하지 않는다. 이런 경우 첫 번째 요소 노드만 반환한다.</p>
<p>만약 인수로 전달된 id 어트리뷰트 값을 갖는 요소 노드가 존재하지 않으면 null을 반환한다.
HTML 요소에 id 어트리뷰트를 부여하면 id 값과 동일한 이름의 전역 변수가 암묵적으로 생성된다.</p>
<h4 id="2-태그-이름을-사용하여-요소-노드를-취득하는-방법">2. 태그 이름을 사용하여 요소 노드를 취득하는 방법</h4>
<p>Document.getElementsByTagName 메서드는 인수로 전달받은 태그 이름을 갖는 요소 노드를 탐색하여 HTMLCollection 객체를 반환한다. 
HTMLCollection 객체는 유사 배열 객체이자 이터러블이며, length 프로퍼티와 item 메서드를 갖는다. HTMLCollection 객체는 라이브 상태이므로 DOM이 변경되면 자동으로 갱신된다.</p>
<h4 id="3-클래스-이름을-사용하여-요소-노드를-취득하는-방법">3. 클래스 이름을 사용하여 요소 노드를 취득하는 방법</h4>
<p>Document.getElementsByClassName 메서드는 인수로 전달받은 클래스 이름을 갖는 요소 노드를 탐색하여 getElementByTagName 메서드와 마찬가지로 HTMLCollection 객체를 반환한다.</p>
<p>만약 인수로 전달된 class 값을 갖는 요소가 존재하지 않을 경우 빈 HTMLCollection 객체를 반환한다.</p>
<h4 id="4-css-선택자를-사용하여-요소-노드를-취득하는-방법">4. CSS 선택자를 사용하여 요소 노드를 취득하는 방법</h4>
<p>생략</p>
<h4 id="5-특정-요소-노드를-취득할-수-있는지-확인">5. 특정 요소 노드를 취득할 수 있는지 확인</h4>
<p>Document.querySelector 메서드는 인수로 전달받은 CSS 선택자를 사용하여 특정 요소 노드를 탐색하여 반환한다.
Element.prototype.matches 메서드는 인수로 전달받은 CSS 선택자를 사용하여 특정 요소 노드를 탐색하여 취득할 수 있는지 확인한다.</p>
<pre><code class="language-js">const $apple = document.querySelector(&#39;.apple&#39;);

console.log($apple.matches(&#39;.apple&#39;)); // true
console.log($apple.matches(&#39;.banana&#39;)); // false</code></pre>
<p>Element.prototype.matches 메서드는 이벤트 위임을 사용할 때 유용하다.</p>
<h3 id="393-노드-탐색">39.3 노드 탐색</h3>
<p>요소 노드를 취득한 다음, DOM 트리를 탐색하여 자식, 부모, 형제 노드를 취득할 수 있다.</p>
<h4 id="1-공백-텍스트-노드">1. 공백 텍스트 노드</h4>
<p>HTML 요소 사이의 공백 문자(스페이스, 탭, 개행 등)는 브라우저에 의해 공백 텍스트 노드로 변환된다.</p>
<h4 id="2-자식-노드-탐색">2. 자식 노드 탐색</h4>
<p><img src="https://velog.velcdn.com/images/boseong-choi/post/c218aff1-a80f-42e5-9d74-bd464aa56a1c/image.png" alt="">
<img src="https://velog.velcdn.com/images/boseong-choi/post/b76ec7ad-3e78-42a9-9d8c-eebf9eed6f0e/image.png" alt=""></p>
<h4 id="3-자식-노드-존재-확인">3. 자식 노드 존재 확인</h4>
<p>Node.prototype.hasChildNodes 메서드는 자식 노드가 존재하면 true, 그렇지 않으면 false를 반환한다.</p>
<h4 id="4-요소-노드의-텍스트-노드-탐색">4. 요소 노드의 텍스트 노드 탐색</h4>
<p>요소 노드는 자식 노드로 텍스트 노드를 가질 수 있다. 이때 텍스트 노드는 요소 노드의 첫 번째 자식 노드이다.
Node.prototype.firstChild 메서드는 요소 노드의 첫 번째 자식 노드를 탐색하여 반환한다.</p>
<h4 id="5-부모-노드-탐색">5. 부모 노드 탐색</h4>
<p>Node.prototype.parentNode 메서드는 부모 노드를 탐색하여 반환한다.</p>
<h4 id="6-형제-노드-탐색">6. 형제 노드 탐색</h4>
<p>Node.prototype.nextSibling 메서드는 형제 노드 중 다음 형제 노드를 탐색하여 반환한다.
Node.prototype.previousSibling 메서드는 형제 노드 중 이전 형제 노드를 탐색하여 반환한다.</p>
<h3 id="394-노드-정보-취득">39.4 노드 정보 취득</h3>
<p>Node.prototype.nodeType 프로퍼티는 노드의 타입을 나타내는 정수 값을 반환한다.
Node.prototype.nodeName 프로퍼티는 노드의 이름을 나타내는 문자열을 반환한다.</p>
<h3 id="395-노드-요소의-텍스트-조작">39.5 노드 요소의 텍스트 조작</h3>
<h4 id="1-nodevalue">1. nodeValue</h4>
<p>nodeValue 프로퍼티는 getter와 setter 함수를 갖는 데이터 프로퍼티이다. 따라서 참조와 할당 모두 가능하다.
노드 객체의 값을 반환하고, 노드 객체의 값이란 텍스트 노드의 텍스트다. 요소 노드의 경우 null을 반환한다.</p>
<pre><code class="language-js">&lt;div id=&quot;foo&quot;&gt;Hello&lt;/div&gt;

// 문서(document) 노드의 nodeValue 프로퍼티는 null을 반환한다.
console.log(document.nodeValue);  // null

// 요소 노드의 nodeValue 프로퍼티는 null을 반환한다.
console.log(document.getElementById(&#39;foo&#39;).nodeValue); // null

// 텍스트 노드의 nodeValue 프로퍼티는 텍스트를 반환한다.
console.log(document.getElementById(&#39;foo&#39;).firstChild.nodeValue); // Hello</code></pre>
<h4 id="2-textcontent">2. textContent</h4>
<p>nodeValue 와 마찬가지로 getter와 setter 함수를 갖는 데이터 프로퍼티이다. 요소 노드의 텍스트와 모든 자손 노드의 텍스트를 모두 취득하거나 변경할 수 있다.</p>
<p>nodeValue는 텍스트 노드의 텍스트만 취득하거나 변경할 수 있지만 textContent는 요소 노드의 텍스트와 모든 자손 노드의 텍스트를 모두 취득하거나 변경할 수 있다.</p>
<h3 id="396-dom-조작">39.6 DOM 조작</h3>
<p>새로운 노드를 생성하여 DOM에 추가하거나 기존 노드를 제거하거나 교체하는 등의 DOM을 조작하는 것.
DOM 조작에 의해 새로운 노드가 추가되거나 삭제되면 리플로우와 리페인트가 발생한다. 이는 성능에 영향을 주므로 주의해야 한다.</p>
<h4 id="innerhtml">innerHTML</h4>
<p>innerHTML 프로퍼티는 HTML 문자열을 파싱하여 DOM을 생성하고 기존의 DOM을 제거한 후 새로운 DOM을 삽입한다.
textContent 프로퍼티를 참조하면 텍스트만 취득할 수 있지만 innerHTML 프로퍼티를 참조하면 HTML 마크업이 포함된 문자열을 취득할 수 있다.</p>
<h4 id="insertadjacenthtml">insertAdjacentHTML</h4>
<p>insertAdjacentHTML 기존 요소를 제거하지 않고도 지정한 위치에 새로운 요소를 삽입할 수 있다.</p>
<h4 id="노드-생성과-추가">노드 생성과 추가</h4>
<ol>
<li>createElement</li>
<li>createTextNode</li>
<li>appendChild</li>
</ol>
<h3 id="397-어트리뷰트">39.7 어트리뷰트</h3>
<p>HTML 요소는 여러 개의 어트리뷰트를 가질 수 있다. 어트리뷰트는 HTML 요소의 부가 정보를 제공한다.</p>
<p>getAttribute, setAttribute 메서드는 요소 노드에서 직접 HTML 어트리뷰트 값을 취득하거나 변경할 수 있어서 편리하다.</p>
<h4 id="html-어트리뷰트-vs-dom-프로퍼티">HTML 어트리뷰트 vs DOM 프로퍼티</h4>
<p>요소 노드 객체에는 HTML 어트리뷰트에 대응하는 DOM 프로퍼티가 존재한다. 이 DOM 프로퍼티들은 HTML 어트리뷰트의 값을 초기값으로 가지고 있다.
DOM 프로퍼티는 HTML 어트리뷰트의 값을 변경하면 DOM 프로퍼티의 값도 자동으로 변경된다. 예를들어 input 요소에 value 어트리뷰트를 변경하면 value 프로퍼티의 값도 자동으로 변경된다.</p>
<p>그러면 HTML 어트리뷰트와 DOM 프로퍼티가 동시에 관리되고 있는 것 처럼 보이지만 사실은 그렇지 않다.</p>
<p>HTML 어트리뷰트의 역할은 변하지 않는 초기값을 제공하는 것이고, DOM 프로퍼티의 역할은 HTML 어트리뷰트의 값을 반영하는 것이다.</p>
<p>첫 렌더링 이후 HTML 어트리뷰트의 값은 DOM 프로퍼티에 의해 더 이상 참조되지 않는다.</p>
<p>즉, 요소 노드는 2개의 상태를 갖는다. 초기 상태와 최신 상태. 초기 상태는 HTML 어트리뷰트의 값이고, 최신 상태는 DOM 프로퍼티의 값이다.</p>
<h3 id="398-스타일">39.8 스타일</h3>
<p>style 프로퍼티는 인라인 스타일을 취득하거나 변경할 수 있다.
CSS 프로퍼티는 케밥 케이스를 따른다. CSS 프로퍼티 background-color는 style 프로퍼티의 backgroundColor로 변경해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[38장 브라우저의 렌더링 과정]]></title>
            <link>https://velog.io/@boseong-choi/38%EC%9E%A5-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%9D%98-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@boseong-choi/38%EC%9E%A5-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%9D%98-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Fri, 21 Jul 2023 05:31:35 GMT</pubDate>
            <description><![CDATA[<p>대부분의 프로그래밍 언어는 운영체제나 가상 머신 위에서 실행되지만 자바스크립트는 브라우저 위에서 HTML, CSS, JS를 실행한다. 이를 위해 브라우저가 HTML, CSS, JS로 작성된 텍스트 문서를 어떻게 파싱하고 렌더링하는지 알아보자.</p>
<h4 id="브라우저의-렌더링-과정">브라우저의 렌더링 과정</h4>
<ol>
<li>브라우저는 HTML, CSS, JS, 이미지, 폰트 등의 리소스를 서버로부터 요청하고 응답받는다.</li>
<li>브라우저는 서버로부터 응답받은 HTML, CSS를 파싱하여 DOM, CSSOM을 생성하고 이들을 결합하여 렌더 트리를 생성한다.</li>
<li>브라우저의 자바스크립트 엔진은 서버로부터 응답받은 JS를 파싱하여 AST(Abstract Syntax Tree)를 생성하고 바이트코드로 변환하여 실행한다.
이때 자바스크립트 엔진은 DOM API를 통해 DOM이나 CSSOM을 변경할 수 있다.</li>
<li>렌더 트리를 기반으로 HTML 요소의 레이아웃을 계산하고 브라우저 화면에 HTML 요소를 페인트하여 브라우저 화면에 표시한다.</li>
</ol>
<h3 id="381-요청과-응답">38.1 요청과 응답</h3>
<p>브라우저는 서버로부터 HTML, CSS, JS, 이미지, 폰트 등의 리소스(정적 파일 또는 서버가 동적으로 생성한 데이터)를 요청하고 응답받는다.</p>
<h3 id="382-http-11과-http-20">38.2 HTTP 1.1과 HTTP 2.0</h3>
<p>HTTP는 클라이언트와 서버 간에 요청과 응답으로 데이터를 교환하기 위한 통신 규약이다.
HTTP를 왜 만들었는가? HTTP 등장 이전에는 FTP, SMTP, NNTP 등의 프로토콜이 존재했다.
각각 목적에 맞게 설계된 프로토콜이어서 서로 다른 프로토콜을 사용하는 서버와 클라이언트는 통신할 수 없었다.
이를 해결하기 위해 HTTP가 등장했다.</p>
<p>HTTP 1.1은 1997년에 등장했고 현재까지 가장 많이 사용되는 HTTP 프로토콜이다. 하나의 연결에 하나의 요청과 응답을 처리한다.
HTTP 2.0은 하나의 연결에 여러 요청과 응답을 처리할 수 있다.
HTTP 2.0을 적용한 웹 사이트는? Google, YouTube, Facebook, Twitter, Wikipedia 등등.</p>
<h3 id="383-html-파싱과-dom-생성">38.3 HTML 파싱과 DOM 생성</h3>
<p>과정 설명</p>
<ol>
<li>요청이 발생하면 서버는 HTML, CSS, JS를 포함한 리소스를 응답한다. 이때 서버는 요청한 html 파일을 읽어들여 메모리에 저장한다음, 저장된 바이트를 인터넷을 경유하여 응답한다.</li>
<li>브라우저는 서버가 응답한 html 파일을 바이트 형태로 읽어들인다. 그리고 읽어들인 바이트를 문자열로 변환한다.</li>
<li>문자열로 변환된 문서를 읽어 들여 문법적 의미를 가진 최소 단위인 토큰으로 분해한다.</li>
<li>분해된 토큰을 객체로 변환하여 노드들을 생성한다.</li>
<li>생성된 노드들을 트리 구조로 구성한다. 이를 DOM(Document Object Model)이라 한다. CSSOM도 거의 비슷</li>
</ol>
<h3 id="385-렌더-트리">38.5 렌더 트리</h3>
<p>렌더 트리는 DOM과 CSSOM을 결합하여 생성한다.</p>
<h3 id="386-자바스크립트-파싱과-실행">38.6 자바스크립트 파싱과 실행</h3>
<p>DOM은 HTML 요소와 스타일을 변경할 수 있는 API를 제공한다. DOM API 라고 하며, getElementById, querySelector, addEventListener 등이 있다.
HTML, CSS와 마찬가지로 위에서 아래로 파싱되고 Abstract Syntax Tree(AST)를 생성한다.
AST는 자바스크립트 엔진이 자바스크립트 코드를 실행하기 위해 필요한 구문을 추상적으로 표현한 트리 자료구조이다. 그리고 AST를 기반으로 바이트코드로 변환하여 실행한다.</p>
<h3 id="387-리플로우와-리페인트">38.7 리플로우와 리페인트</h3>
<p>리플로우 : 레이아웃을 다시 계산하는 것
리페인트 : 레이아웃을 다시 그리는 것</p>
<h3 id="느낀점">느낀점</h3>
<p>브라우저 렌더링 원리를 이해하면 DOM, 노드, AJAX, 이벤트, 비동기 처리, 렌더링 최적화 등을 이해하는데 도움이 될것 같다. 면접에서도 자주 물어보는 주제라서 중요한 파트다.
Q. 브라우저의 렌더링 과정을 설명하시오.
Q. Reflow와 Repaint가 실행되는 시점.
Q. 주소창에 URL을 입력하면 어떤 일이 일어나는가?
Q. CORS를 대응하는 방법은?</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[36장 디스트럭처링 할당]]></title>
            <link>https://velog.io/@boseong-choi/36%EC%9E%A5-%EB%94%94%EC%8A%A4%ED%8A%B8%EB%9F%AD%EC%B2%98%EB%A7%81-%ED%95%A0%EB%8B%B9</link>
            <guid>https://velog.io/@boseong-choi/36%EC%9E%A5-%EB%94%94%EC%8A%A4%ED%8A%B8%EB%9F%AD%EC%B2%98%EB%A7%81-%ED%95%A0%EB%8B%B9</guid>
            <pubDate>Wed, 19 Jul 2023 05:34:37 GMT</pubDate>
            <description><![CDATA[<h3 id="디스트럭처링-할당-구조-분해-할당">디스트럭처링 할당 (구조 분해 할당)</h3>
<p>배열과 같은 이터러블 또는 객체를 destructuring(비구조화, 구조분해)하여 1개 이상의 변수에 개별적으로 할당하는 것. 필요한 값만 추출하여 변수에 할당할 때 유용하다.</p>
<h3 id="361-배열-디스트럭처링-할당">36.1 배열 디스트럭처링 할당</h3>
<pre><code class="language-js">const arr = [1,2,3]

// ES6 배열 디스트럭처링 할당
// 변수 one, two, three를 선언하고 배열 arr을 디스트럭처링하여 할당한다.
// 이때 할당 기준은 배열의 인덱스다.
const [one, two, three] = arr
console.log(one, two, three) // 1 2 3</code></pre>
<p>배열 디스트럭처링 할당을 위해서는 할당 연산자 왼쪽에 배열 형태의 변수를 선언해야 한다. 이때 변수를 배열 리터럴 형태로 선언한다.</p>
<pre><code class="language-js">const [x, y] = [1, 2]</code></pre>
<p>우변에 이터러블을 할당하지 않으면 에러가 발생한다.</p>
<p>또한 변수에 기본값을 설정해 줄 수 있으나 할당되는 값이 우선순위가 높다는 것을 기억해야 한다.</p>
<pre><code class="language-js">const arr = [1, 2];

const [one, two, three = 3] = arr;

console.log(one); // 1
console.log(two); // 2
console.log(three); // 3</code></pre>
<p>Rest 파라미터 또한 구조분해에서 사용할 수 있다.</p>
<pre><code class="language-js">const arr = [1, 2, 3];

const [one, ...other] = arr;

console.log(one); // 1
console.log(other); // [2, 3]</code></pre>
<h3 id="362-객체-디스트럭처링-할당">36.2 객체 디스트럭처링 할당</h3>
<p>객체를 구조분해하기 위해서는 변수명을 객체에서 사용하고 있는 프로퍼티키로 사용해야 한다. 즉 할당의 기준이 배열과 같이 인덱스가 아니라 프로퍼티 키. 또한 배열과 다르게 할당하고자 하는 변수들을 <code>중괄호({})</code>로 묶어줘야 한다.</p>
<pre><code class="language-js">const name = {
  firstName: &#39;bs&#39;,
  lastName: &#39;choi&#39;
}

const {lastName, firstName} = name;

console.log(firstName); // bs
console.log(lastName); // choi
</code></pre>
<p>또한 다른 변수 이름으로 할당받을 수도 있다.</p>
<pre><code class="language-js">const name = {
  firstName: &#39;bs&#39;,
  lastName: &#39;Choi&#39;
}

const {lastName: two, firstName: one} = name;

console.log(one); // bs
console.log(two); // Choi</code></pre>
<p>배열의 요소가 객체인 경우 디스트럭처링 할당과 객체 디스트럭처링 할당을 혼용할 수 있다.</p>
<pre><code class="language-js">const todos = [
  { id: 1, content: &#39;HTML&#39;, completed: true },
  { id: 2, content: &#39;CSS&#39;, completed: false },
  { id: 3, content: &#39;Javascript&#39;, completed: true },
];

// todos 배열의 첫 번째 요소인 객체로부터 id 프로퍼티만 추출한다.
const [, { id }] = todos;
console.log(id); // 2</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[31장 RegExp ~ 35장 스프레드 문법]]></title>
            <link>https://velog.io/@boseong-choi/31%EC%9E%A5-RegExp-35%EC%9E%A5-%EC%8A%A4%ED%94%84%EB%A0%88%EB%93%9C-%EB%AC%B8%EB%B2%95</link>
            <guid>https://velog.io/@boseong-choi/31%EC%9E%A5-RegExp-35%EC%9E%A5-%EC%8A%A4%ED%94%84%EB%A0%88%EB%93%9C-%EB%AC%B8%EB%B2%95</guid>
            <pubDate>Sat, 15 Jul 2023 15:59:52 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-31장-regexp-정규-표현식">📌 31장 RegExp 정규 표현식</h3>
<p>정규 표현식은 문자열을 대상으로 <strong>패턴 매칭 기능</strong>을 제공하는 문자열의 구조를 파악하는 데 사용하는 패턴</p>
<ul>
<li>문자에 한글이 들어가 있는지</li>
<li>이메일 형식이 맞는지</li>
<li>숫자로 시작하는지 등</li>
</ul>
<p>문자 형식을 검증할 때 사용한다. 거의 모든 언어에서 지원한다.</p>
<h4 id="정규-표현식이-어려운-이유">정규 표현식이 어려운 이유</h4>
<ol>
<li>정규 표현식은 암기해야 할 문법이 많다.</li>
<li>예측이 어렵다.</li>
<li>잘못 작성한 정규 표현식은 애플리케이션을 정지시킬 수 있다.</li>
</ol>
<p>그래서 대부분 라이브러리나 AI의 도움을 받는다. </p>
<hr>

<h3 id="📌-32장-string">📌 32장 String</h3>
<p>String 빌트인 객체는 문자열을 다룰 때 유용한 프로퍼티와 메소드를 제공한다. 사용법은 다른 빌트인 객체와 동일하게 new 연산자를 사용하여 인스턴스를 생성한다. Array와 마찬가지로 indexOf, slice, length 등의 프로퍼티와 메소드를 사용할 수 있다.</p>
<p>메서드를 나열하기 보다는 Array와 String을 비교하며 느낀점을 적는 것이 좋을것 같다.</p>
<p>Array 객체는 데이터를 순서대로 저장할 수 있다는 장점이 있다.
또한, Array 객체는 다양한 메서드와 속성을 제공하여 데이터를 쉽게 처리할 수 있다.</p>
<p>String 객체는 데이터를 문자열로 저장할 수 있다는 장점이 있다.
또한, Array 객체와 달리 순서가 없기 때문에 데이터를 순서대로 처리할 수 없다는 점이 있는데, 단점이자 장점같다.</p>
<p>따라서, Array 객체와 String 객체 중 어느 객체를 사용할지는 데이터의 특성에 따라 다르다.
만약, 데이터를 순서대로 저장하고, 다양한 메서드와 속성을 사용하여 데이터를 처리해야 한다면 Array 객체를 사용하는 것이 좋고 정규식이 필요하거나 문자열의 길이와 구조를 변경할 필요가 없는 경우 String 객체를 사용하는 것이 좋을 것 같다.</p>
<hr>

<h3 id="📌-33장-symbol">📌 33장 Symbol</h3>
<p>7번째 자료형. 변경 불가능한 원시 타입의 값이다. 심벌 값은 다른 값과 중복되지 않는 유일무이한 값이다. 따라서 주로 이름의 충돌 위험이 없는 <strong>유일한 객체의 프로퍼티 키</strong>를 만들기 위해 사용한다.</p>
<p>심볼 함수에는 선택적으로 설명을 붙일 수 있다. 이 설명은 디버깅 용도로만 사용되며 심벌 값에 영향을 주지 않는다. 즉, <strong>설명이 같은 심벌 값도 다른 심벌 값</strong>이다.</p>
<p>변수처럼 같은 값을 가지면 같은 변수로 취급해주는
전역 심볼을 만들어쓸 수 있다. <code>Symbol.for()</code> 를 사용하면 된다.</p>
<h4 id="337-well-known-symbol">33.7 Well-Known Symbol</h4>
<p>자바스크립트는 기본으로 제공하는 빌트인 심벌 값을 제공한다. Array나 Object 같은 자료형을 만들 때 암묵적으로 사용되는 심벌 값이다.</p>
<p>예를 들면 Array, String 등의 경우 Symbol.iterator를 키로 갖는 메서드를 가지고 있다. 이 메서드는 이터레이터를 반환한다고 규정되어있다. 빌트인 이터러블은 이터레이션 프로토콜을 준수한다.</p>
<h4 id="어디에-사용할-수-있을까">어디에 사용할 수 있을까?</h4>
<ol>
<li>유일한 식별자를 만들어야 할 때</li>
<li>숨길 수 있는 프로퍼티를 만들 때. 외부 코드에서 접근할 수 없는 프로퍼티를 만들 수 있다. 예를 들어 객체에 심벌을 사용하면 for...in, for...of 문으로 순회할 수 없다.
enumerate 할 수 없다는 뜻이다. 그래서 JSON.stringify() 에서도 제외된다.</li>
</ol>
<hr>

<h3 id="📌-34장-이터레이션-프로토콜">📌 34장 이터레이션 프로토콜</h3>
<p>ES6에서 도입된 이터레이션 프로토콜은 순회 가능한 객체를 만들기 위한 규약이다. 
이전에는 배열, 문자열 등은 각자 나름의 방식으로 순회(for문, for...in문, forEach 메서드 등)했지만, ES6에서는 순회 가능한 데이터 컬렉션을 프로토콜을 준수하는 객체로 통일하여 for...of, 스프레드 문법, 배열 디스트럭쳐링 할당의 대상으로 일원화했다.</p>
<p>이터레이션 프로토콜은 덕 타이핑을 통해 구현된다.</p>
<blockquote>
<p>덕 타이핑
사람이 오리처럼 행동하고 오리처럼 생겼으면 오리다. 실제 타입은 상관없고 구현된 메서드가 같으면 같은 타입으로 간주한다. 즉, <strong>객체의 구조가 프로토콜을 충족하면 이터러블로 간주</strong>한다.</p>
</blockquote>
<p>프로토콜을 충족하려면 객체의 프로퍼티에 Symbol.iterator가 있어야 하고, 이것은 이터레이터를 반환하는 함수여야 한다. 이터레이터는 next 메서드를 갖는다. </p>
<p>next 메서드는 다시 value와 done 프로퍼티를 갖는 객체를 반환한다. 이터레이션 프로토콜은 이터러블 프로토콜, 이터레이터 프로토콜로 구성된다.</p>
<h4 id="이터러블인지-확인하는-함수-615p">이터러블인지 확인하는 함수 (615p)</h4>
<pre><code class="language-js">const isIterable = v =&gt; v !== null &amp;&amp; typeof v[Symbol.iterator] === &#39;function&#39;;

console.log(isIterable([])); // true
console.log(isIterable(&#39;&#39;)); // true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set())); // true
console.log(isIterable({})); // false</code></pre>
<pre><code class="language-js">const obj = { a: 1, b: 2 };

console.log(Symbol.iterator in obj); // false

// 이터러블이 아닌 객체는 for...of 문에서 순회할 수 없다.
for (const p of obj) {
  console.log(p);
}</code></pre>
<p>단, 일반 객체에 스프레드 문법을 사용할 수 있도록 허용한다.</p>
<h4 id="느낀-점">느낀 점</h4>
<p>프로젝트 중에 for...in 문을 사용한 부분이 있었는데, 이 부분을 for...of 문으로 바꾸니까 값이 제대로 나오지 않았다.</p>
<p>이유를 찾으려고 에전에 검색했던 내용을 다시 찾아보았는데, 그 때 당시에는 이터러블에 대한 개념이 없어서 이해하지 못했었다.</p>
<p>이번에 이터러블에 대해 공부하고 나니까 이해가 되었다. for...in 문은 객체의 프로퍼티를 순회하지만 for...of 문은 이터러블을 순회하기 때문에 값이 제대로 나오지 않았던 것으로 이해된다.</p>
<hr>

<h3 id="📌-35장-스프레드-문법">📌 35장 스프레드 문법</h3>
<p>하나로 뭉쳐있는 여러 값들을 펼쳐서 사용할 수 있게 해주는 문법.
Array, Object, String, Map, Set, DOM collection 등 for...of 문으로 순회할 수 있는 이터러블은 모두 스프레드 문법의 대상이 될 수 있다.</p>
<pre><code class="language-js">// ...[1, 2, 3]은 [1, 2, 3]을 개별 요소로 분리한다.(-&gt; 1, 2, 3)
console.log(...[1, 2, 3]); // 1 2 3

// 문자열은 이터러블이다.
console.log(...&#39;Hello&#39;); // H e l l o

// Map과 Set은 이터러블이다.
console.log(...new Map([[&#39;a&#39;, &#39;1&#39;], [&#39;b&#39;, &#39;2&#39;]])); // [ &#39;a&#39;, &#39;1&#39; ] [ &#39;b&#39;, &#39;2&#39; ]
console.log(...new Set([1, 2, 3])); // 1 2 3

// 이터러블이 아닌 일반 객체는 스프레드 문법의 대상이 될 수 없다.
console.log(...{ a: 1, b: 2 }); // TypeError: Found non-callable @@iterator</code></pre>
<p>스프레드 문법의 결과물을 값으로 사용할 수 없고, 쉼표로 구분된 값의 목록이 필요한 문맥에서만 사용할 수 있다.</p>
<ul>
<li>함수 호출문의 인수 목록</li>
<li>배열 리터럴의 요소 목록</li>
<li>객체 리터럴의 프로퍼티 목록</li>
</ul>
<h3 id="rest-파라미터--spread-문법-비교">Rest 파라미터 / Spread 문법 비교</h3>
<ol>
<li><p>Rest 파라미터
Rest 파라미터는 함수의 파라미터 선언에서 사용되는 문법이다. Rest 파라미터는 함수에 전달된 아규먼트들을 배열로 나타낸다</p>
<p>이 배열은 함수 내에서 배열로 처리할 수 있으며, 함수 호출 시 전달된 인수의 개수와 상관없이 모든 아규먼트를 배열로 받을 수 있다. Rest 파라미터는 함수 정의에서 마지막 파라미터로만 사용할 수 있으며, 매개변수 이름 앞에 세 개의 점(...)을 붙여서 표현한다.</p>
</li>
</ol>
<pre><code class="language-js">function myFunction(...args) {
  console.log(args);
}

myFunction(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]</code></pre>
<ol start="2">
<li>Spread 문법
Spread 문법은 배열이나 객체를 펼쳐서 개별 요소로 확장하는 문법이다. Spread 문법은 배열 또는 객체 앞에 세 개의 점(...)을 붙여서 사용한다. 배열을 Spread 문법으로 확장하면 개별 요소가 된다.</li>
</ol>
<pre><code class="language-js">const arr = [1, 2, 3];
const newArr = [...arr, 4, 5];

console.log(newArr); // [1, 2, 3, 4, 5]</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[27장 배열 ~ 30장 Date]]></title>
            <link>https://velog.io/@boseong-choi/27%EC%9E%A5-%EB%B0%B0%EC%97%B4-30%EC%9E%A5-Date</link>
            <guid>https://velog.io/@boseong-choi/27%EC%9E%A5-%EB%B0%B0%EC%97%B4-30%EC%9E%A5-Date</guid>
            <pubDate>Thu, 13 Jul 2023 16:05:10 GMT</pubDate>
            <description><![CDATA[<h3 id="278-배열-메서드">27.8 배열 메서드</h3>
<p>배열에는 원본 배열을 직접 변경하는 메서드와 원본 배열을 직접 변경하지 않고 새로운 배열을 생성하여 반환하는 메서드가 있다.</p>
<h4 id="2781-arrayisarray">27.8.1 Array.isArray</h4>
<p>Array.isArray 메서드는 인수가 배열이면 true, 배열이 아니면 false를 반환한다.</p>
<h4 id="2782-arrayprototypeindexof">27.8.2 Array.prototype.indexOf</h4>
<p>Array.prototype.indexOf 메서드는 인수로 전달된 배열 요소를 검색하여 인덱스를 반환한다. 배열 요소가 여러 개 존재하더라도 처음으로 검색된 요소의 인덱스만 반환한다. 배열 요소가 존재하지 않으면 <strong>-1을 반환</strong>한다.</p>
<p>사용 예시 : 장바구니에 이미 있는 상품을 추가하려 할 때 사용</p>
<blockquote>
<p>솔직히 includes 메서드가 더 편한듯.</p>
</blockquote>
<p>push : 배열의 끝에 요소를 추가한다.
pop : 배열의 마지막 요소를 제거하고 제거한 요소를 반환한다.
unshift : 배열의 첫 번째 요소로 요소를 추가한다.
shift : 배열의 첫 번째 요소를 제거하고 제거한 요소를 반환한다.</p>
<h4 id="arrayprototypesplice-와-arrayprototypeslice-의-차이점">Array.prototype.splice 와 Array.prototype.slice 의 차이점</h4>
<p>splice : 원본 배열을 직접 변경한다.
slice : 원본 배열을 직접 변경하지 않고 새로운 배열을 생성하여 반환한다.</p>
<h3 id="28장-number">28장 Number</h3>
<ul>
<li>빌트인 객체 Number는 원시 타입인 숫자를 다룰 때 유용한 프로퍼티와 메서드를 제공한다.</li>
</ul>
<h4 id="281-number-생성자-함수">28.1 Number 생성자 함수</h4>
<p>표준 빌트인 객체인 Number는 생성자 함수다. 따라서 new 연산자와 함께 호출하여 인스턴스를 생성할 수 있다.</p>
<h4 id="563p-mathrandom">563p Math.random</h4>
<p>난수 생성하는 메서드. 0에서 1미만. 주로 단독으로 사용하지 않고 다른 메서드와 함께 사용한다. </p>
<blockquote>
<p>1부터 9까지 난수 생성 코드</p>
</blockquote>
<pre><code class="language-js">Math.floor(Math.random() * 9) + 1;</code></pre>
<blockquote>
<p>1부터 43까지 난수 생성 코드</p>
</blockquote>
<pre><code class="language-js">Math.floor(Math.random() * 43) + 1;</code></pre>
<h3 id="29장-math">29장 Math</h3>
<p>표준 빌트인 객체 Math는 상수와 함수를 위한 프로퍼티와 메서드를 제공한다. 생성자 함수가 아니고 정적 프로퍼티와 정적 메서드만 제공한다.</p>
<h3 id="30장-date를-활용한-시계-예제">30장 Date를 활용한 시계 예제.</h3>
<pre><code class="language-js">function generateLottoNumbers() {
  let lottoNumbers = new Set();

  while (lottoNumbers.size &lt; 6) {
    let randomNumber = Math.floor(Math.random() * 45) + 1;
    lottoNumbers.add(randomNumber);
  }

  return Array.from(lottoNumbers);
}

const koreanLottoNumbers = generateLottoNumbers();
console.log(koreanLottoNumbers);

let words = [&quot;spray&quot;, &quot;limit&quot;, &quot;elite&quot;, &quot;exuberant&quot;, &quot;destruction&quot;, &quot;present&quot;];
const appendedWords = words.filter((word, index, arr) =&gt; {
  arr.push(&quot;new&quot;);
  return word.length &lt; 6;
});

// 30.3 Date를 활용한 시간 예제

(function printNow() {
  const today = new Date();

  const dayNames = [&#39;(일요일)&#39;, &#39;(월요일)&#39;, &#39;(화요일)&#39;, &#39;(수요일)&#39;, &#39;(목요일)&#39;, &#39;(금요일)&#39;, &#39;(토요일)&#39;];

  // getDay: 요일을 나타내는 정수를 반환한다. (0 ~ 6)
  const day = dayNames[today.getDay()];

  const year = today.getFullYear();
  const month = today.getMonth() + 1;
  const date = today.getDate();
  let hour = today.getHours();
  let minute = today.getMinutes();
  let second = today.getSeconds();
  const ampm = hour &gt;= 12 ? &#39;PM&#39; : &#39;AM&#39;;

  // 12시간제로 변경
  hour %= 12;
  hour = hour || 12; // 0 =&gt; 12

  // 10미만인 분과 초를 2자리로 변경
  minute = minute &lt; 10 ? &#39;0&#39; + minute : minute;
  second = second &lt; 10 ? &#39;0&#39; + second : second;

  const now = `${year}년 ${month}월 ${date}일 ${day} ${hour}:${minute}:${second} ${ampm}`;

  console.log(now);

  setTimeout(printNow, 1000);
})();</code></pre>
<h3 id="느낀-점">느낀 점</h3>
<ol>
<li><p>배열 메서드가 정말 다양하다. 종류가 많기도하고 자주쓰는 메서드들이기 때문에 주의하며 사용해야 할것 같다. </p>
</li>
<li><p>Date 객체는 많이 불편하다. 날짜와 시간 처리가 복잡하고 정확성에서도 의문이 남는다. 달력을 구현할 때도 인덱스 처리 (1월은 0으로 시작) 때문에 불편했던 경험이 있다. 메서드 종류나 원리만 알고 라이브러리를 쓰는 것도 좋을듯.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[26장 ES6 함수의 추가 기능]]></title>
            <link>https://velog.io/@boseong-choi/26%EC%9E%A5-ES6-%ED%95%A8%EC%88%98%EC%9D%98-%EC%B6%94%EA%B0%80-%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@boseong-choi/26%EC%9E%A5-ES6-%ED%95%A8%EC%88%98%EC%9D%98-%EC%B6%94%EA%B0%80-%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Thu, 13 Jul 2023 14:44:11 GMT</pubDate>
            <description><![CDATA[<h3 id="261-함수의-구분">26.1 함수의 구분</h3>
<p>ES6 이전의 모든 함수는 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수 있다. </p>
<pre><code class="language-js">var foo = function () {
    return 1;
};

// 일반적인 함수로서 호출
foo(); // 1

// 생성자 함수로서 호출
new foo(); // foo {}

// 메서드로서 호출
var obj = { foo: foo };
obj.foo(); // 1</code></pre>
<h3 id="262-메서드">26.2 메서드</h3>
<p>ES6 사양에서 메서드는 메서드 축약 표현으로 정의된 함수만을 의미한다.
ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 <code>[[HomeObject]]</code>를 갖는다.</p>
<h3 id="263-화살표-함수">26.3 화살표 함수</h3>
<p>함수를 더 간결하게 작성할 수 있는 문법적인 편의를 제공하는 JavaScript의 기능이다.</p>
<pre><code class="language-js">class Prefixer {
    constructor(prefix) {
        this.prefix = prefix;
    }

    add(arr) {
        return arr.map(function (item) {
            return this.prefix + item;
        });
    }
}

const prefixer = new Prefixer(&#39;-webkit-&#39;);
console.log(prefixer.add([&#39;transition&#39;, &#39;user-select&#39;]));</code></pre>
<p>기대값 : <code>[&quot;-webkit-transition&quot;, &quot;-webkit-user-select&quot;]</code>
실제값 : <code>TypeError: Cannot read properties of undefined (reading &#39;prefix&#39;)</code></p>
<h4 id="기대값과-실제값이-다른이유">기대값과 실제값이 다른이유</h4>
<ol>
<li>메서드 내부에서 this를 사용하면 메서드를 호출한 객체를 가리킴. -&gt; Prefixer 객체</li>
<li>map 메서드의 콜백 함수 내부에서 this를 사용하면 전역 객체를 가리킴. -&gt; window</li>
<li>하지만 this는 undefined를 가리킨다.</li>
<li>왜냐하면, 클래스 내부의 모든 코드는 엄격 모드(strict mode)로 실행되고,</li>
<li>strict mode에서 함수를 일반 함수로서 호출하면 this에 undefined가 바인딩되기 때문이다.</li>
</ol>
<p>이런 원하지 않는 결과값을 해결하기 위해 480p 까지 다양한 해결법이 존재. 그 중 하나가 화살표 함수를 사용하는 것. 화살표 함수는 <strong>함수 자체의 this 바인딩을 갖고 있지 않기 때문</strong>에 상위 스코프의 this를 그대로 참조한다.</p>
<pre><code class="language-js">const person = {
    name: &#39;Lee&#39;,
    sayHi: () =&gt; console.log(`Hi ${this.name}`)
};

person.sayHi(); // Hi</code></pre>
<p>화살표 함수는 메서드로 사용하기에는 적합하지 않다. sayHi 메서드 내부의 this는 메서드를 소유한 객체, 즉 person 객체를 가리키지 않고 상위 스코프인 전역 객체를 가리키기 때문이다. super, arguments도 상위 스코프의 super, arguments를 가리킨다.</p>
<h3 id="느낀점">느낀점</h3>
<ol>
<li><p>ES6 이전의 모든 함수는 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수 있었기 때문에 문법상 가능은 하지만 결과나 성능에 대해서 원치 않는 결과를 불러올 수 있겠다라는 생각이 들었다.</p>
</li>
<li><p>화살표 함수는 constructor, prototype, super, arguments 바인딩을 갖고 있지 않기 때문에 개발자가 의도한 대로 더 간결한 코드, 가독성을 가져올 수 있는 것 같다.</p>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>