<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>han_oal.log</title>
        <link>https://velog.io/</link>
        <description>노션이 터져서 임시로 사용하는 오픈 세컨 브레인</description>
        <lastBuildDate>Sun, 12 Jul 2026 05:07:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>han_oal.log</title>
            <url>https://velog.velcdn.com/images/han_oal/profile/6f702976-2fc9-4664-aa29-d4d0de83d513/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. han_oal.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/han_oal" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[# 🚛 화물복지카드 혜택 및 이용 방법 총정리]]></title>
            <link>https://velog.io/@han_oal/%ED%99%94%EB%AC%BC%EB%B3%B5%EC%A7%80%EC%B9%B4%EB%93%9C-%ED%98%9C%ED%83%9D-%EB%B0%8F-%EC%9D%B4%EC%9A%A9-%EB%B0%A9%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@han_oal/%ED%99%94%EB%AC%BC%EB%B3%B5%EC%A7%80%EC%B9%B4%EB%93%9C-%ED%98%9C%ED%83%9D-%EB%B0%8F-%EC%9D%B4%EC%9A%A9-%EB%B0%A9%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sun, 12 Jul 2026 05:07:25 GMT</pubDate>
            <description><![CDATA[<p>화물복지카드를 사용하는 화물차 운전자가 누릴 수 있는 실질적인 혜택과 이를 적용받는 구체적인 방법입니다.</p>
<hr>
<h2 id="1-화물복지카드로-얻는-4대-핵심-혜택">1. 화물복지카드로 얻는 4대 핵심 혜택</h2>
<h3 id="①-유가보조금-및-유가연동보조금-지원">① 유가보조금 및 유가연동보조금 지원</h3>
<p>주유 시 유류세 일부를 국가에서 지원하며, 국제 유가가 크게 오를 경우 추가 보조금을 지급합니다.</p>
<ul>
<li><strong>기본 보조금</strong>: 경유 리터당 <strong>292.66원</strong>, LPG 리터당 <strong>179.47원</strong> 차감 결제.</li>
<li><strong>유가연동보조금</strong>: 경유 가격이 리터당 <strong>1,700원을 초과</strong>할 경우, 초과분의 50%~70%를 정부가 추가 지원합니다. (최대 지원 한도 리터당 280원까지 상향 적용)</li>
</ul>
<h3 id="②-정유사별-현장-할인-및-적립">② 정유사별 현장 할인 및 적립</h3>
<p>정부 보조금과 별개로, 카드사와 제휴된 화물우대 주유소(SK내트럭플러스, GS칼텍스, HD현대오일뱅크, S-OIL 등)에서 자체 혜택을 제공합니다.</p>
<ul>
<li><strong>혜택</strong>: 주유소 브랜드 및 전월 실적에 따라 리터당 <strong>최대 40원 ~ 100원</strong> 추가 현장 할인 또는 청구 할인.</li>
</ul>
<h3 id="③-고속도로-통행료-결제일-할인">③ 고속도로 통행료 결제일 할인</h3>
<p>장거리 운행이 많은 화물차 맞춤형 카드 혜택입니다.</p>
<ul>
<li><strong>혜택</strong>: 고속도로 통행료(하이패스 포함) 이용 시 <strong>10% 청구 할인</strong> (카드사별 월 한도 제공).</li>
</ul>
<h3 id="④-부가세-환급지원-서비스">④ 부가세 환급지원 서비스</h3>
<p>개인사업자인 화물 차주를 위해 복잡한 세무 과정을 돕습니다.</p>
<ul>
<li><strong>혜택</strong>: 화물복지카드로 결제한 주유비 및 물류 관련 지출 내역 중 부가세 환급 대상을 자동으로 분류해 줍니다. 분기별/반기별 부가세 신고 시 번거로운 영수증 증빙 없이 카드사 웹사이트에서 &#39;부가세 환급 지원용 내역서&#39; 한 장만 출력하여 제출하면 됩니다.</li>
</ul>
<hr>
<h2 id="2-혜택을-받기-위한-구체적인-방법-이용-가이드">2. 혜택을 받기 위한 구체적인 방법 (이용 가이드)</h2>
<h3 id="①-보조금-받는-방법-그냥-카드로-결제만-하세요">① 보조금 받는 방법: &quot;그냥 카드로 결제만 하세요&quot;</h3>
<p>과거처럼 주유 영수증을 모아서 지자체에 청구할 필요가 전혀 없습니다.</p>
<ul>
<li><strong>진행 방식</strong>: 화물복지카드로 주유비를 결제하면, 시스템(UGAMS)이 실시간으로 유가보조금을 인식합니다.</li>
<li><strong>영수증 확인</strong>: 결제 후 영수증을 보면 <strong>[총 주유 금액 - 유가보조금 = 실제 본인 청구 금액]</strong> 형태로 표기되어 보조금이 즉시 차감된 것을 확인할 수 있습니다.</li>
</ul>
<h3 id="②-내-차의-월간-한도량-확인하기">② 내 차의 월간 한도량 확인하기</h3>
<p>유가보조금은 차량 톤수(t)별로 매월 지급되는 리터(L) 한도가 정해져 있으므로 이를 체크하며 운행해야 합니다.</p>
<ul>
<li><strong>확인 방법</strong>: 카드사에서 무료로 제공하는 <strong>&#39;유가보조금 잔여한도 알림 서비스(SMS)&#39;</strong>를 신청하면 주유할 때마다 남은 한도가 문자로 발송됩니다. </li>
<li><strong>앱 활용</strong>: 국토교통부 공식 앱인 <strong>&#39;화물다이너스(구 웰페어맵)&#39;</strong> 또는 각 카드사 앱을 통해서 실시간 잔여 리터 및 누적 보조금 조회가 가능합니다.</li>
</ul>
<h3 id="③-보너스-혜택-화물복지재단-복지사업-신청하기">③ 보너스 혜택: 화물복지재단 복지사업 신청하기</h3>
<p>화물복지카드를 발급받고 주유 실적이 있는 화물운전자라면 <strong>&#39;화물복지재단&#39;</strong> 회원가입 후 정부와 재단이 운영하는 다양한 복지 사업을 무료로 신청할 수 있습니다.</p>
<ul>
<li><strong>자녀 장학금</strong>: 중·고·대학생 자녀 장학금 지원 (연 50~100만 원 선발 지급).</li>
<li><strong>건강검진</strong>: 지정 병원 종합건강검진 비용 지원 (50만 원 상당).</li>
<li><strong>기타 지원</strong>: 4대 중증질환 치료비 지원, 업무 중 사고 시 생계지원금 지급 등.</li>
<li><strong>방법</strong>: 화물복지재단 홈페이지(<a href="http://www.ffw.or.kr)%EC%97%90">www.ffw.or.kr)에</a> 접속하여 회원가입 후, 공고가 뜨는 시기(보통 상반기)에 온라인으로 신청하면 됩니다.</li>
</ul>
<hr>
<h2 id="이용-시-주의사항">이용 시 주의사항</h2>
<ul>
<li><strong>1일 주유 횟수 제한</strong>: 부정수급 방지를 위해 <strong>하루에 3회(체크카드는 4회)</strong>까지만 보조금이 적용됩니다. 4번째 주유부터는 보조금 없이 전액 본인 부담으로 결제되니 주의하세요.</li>
<li><strong>주유소 유종 일치</strong>: 내 차량 등록 정보(예: 경유)와 주유한 유종이 다르게 체크되면 보조금 지급이 거절되거나 부정수급 조사를 받을 수 있습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🚛 화물복지카드가 탄생한 배경과 이유]]></title>
            <link>https://velog.io/@han_oal/%ED%99%94%EB%AC%BC%EB%B3%B5%EC%A7%80%EC%B9%B4%EB%93%9C%EA%B0%80-%ED%83%84%EC%83%9D%ED%95%9C-%EB%B0%B0%EA%B2%BD%EA%B3%BC-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@han_oal/%ED%99%94%EB%AC%BC%EB%B3%B5%EC%A7%80%EC%B9%B4%EB%93%9C%EA%B0%80-%ED%83%84%EC%83%9D%ED%95%9C-%EB%B0%B0%EA%B2%BD%EA%B3%BC-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Sun, 12 Jul 2026 05:06:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="요약">요약</h2>
<p>&quot;정부의 유류세 인상에 따른 화물 차주의 부담을 덜어주어 국가 물류망을 보호하되, 세금이 투입되는 만큼 투명하고 정확하게 관리하기 위해 도입된 전용 결제 시스템입니다.&quot;</p>
</blockquote>
<p>화물복지카드(정식 명칭: 화물자동차 유류구매카드) 제도는 갑작스러운 정부 정책 변화와 유가 상승으로부터 <strong>화물 차주의 생계를 보호하고 국가 물류망을 지키기 위해</strong> 도입되었습니다. </p>
<hr>
<h2 id="1-에너지-세제개편과-경유세-인상-2000년대-초">1. 에너지 세제개편과 경유세 인상 (2000년대 초)</h2>
<ul>
<li><strong>배경</strong>: 2000년대 초, 정부는 환경 오염 저감과 에너지 소비 구조 조정을 위해 경유와 LPG에 부과되는 세금을 단계적으로 대폭 인상하는 &#39;에너지 세제개편&#39;을 단행했습니다.</li>
<li><strong>문제 발생</strong>: 운송 비용의 상당 부분을 유류비로 지출하는 화물 차주들에게 경유 가격 급등은 곧바로 생계 위협으로 이어졌습니다.</li>
<li><strong>해당 조치</strong>: 정부는 유류세 인상으로 인한 타격을 완화해 주기 위해, 인상분만큼을 다시 환급해 주는 &#39;유가보조금 제도(2001년)&#39;를 신설했습니다.</li>
</ul>
<hr>
<h2 id="2-서면-청구의-한계와-부정수급-차단">2. 서면 청구의 한계와 부정수급 차단</h2>
<p>초기 유가보조금은 화물 차주가 주유 영수증을 직접 모아서 지자체에 서면으로 신청하고 돈을 돌려받는 아날로그 방식이었습니다. 이로 인해 두 가지 큰 문제가 발생했습니다.</p>
<ul>
<li><strong>과도한 행정력 낭비</strong>: 매달 수많은 영수증을 검토해야 하는 차주와 담당 공무원 모두에게 행정적 부담이 너무 컸습니다.</li>
<li><strong>보조금 부정수급(영수증 깡)</strong>: 영수증을 위조, 변조하거나 허위로 부풀려 결제하는 등 국가 보조금을 부정하게 수급하는 도덕적 해이가 심각했습니다.</li>
</ul>
<blockquote>
<p><strong>💡 해결책으로 &#39;화물복지카드&#39; 도입</strong>
이를 해결하기 위해 정부는 지정된 전용 카드로만 주유비를 결제하도록 바꿨습니다. 카드 결제와 동시에 주유량, 금액, 차량 정보가 유가보조금 관리시스템(UGAMS)에 실시간 등록되면서 행정 효율화와 부정수급 원천 차단이라는 두 마리 토끼를 잡게 되었습니다.</p>
</blockquote>
<hr>
<h2 id="3-국가-동맥물류망-보호-및-물가-안정">3. 국가 동맥(물류망) 보호 및 물가 안정</h2>
<ul>
<li><strong>물류 마비 방지</strong>: 화물 운송은 산업 전반에 원자재와 제품을 나르는 대한민국의 동맥입니다. 유가 부담 때문에 차주들이 운행을 포기하거나 파업을 하게 되면 국가 경제 전체가 마비(물류 대란)됩니다.</li>
<li><strong>최소한의 생계 보장</strong>: 화물복지카드는 화물 차주들에게 최소한의 유류비 마지노선을 보장해 주어 안정적인 운행을 돕습니다.</li>
<li><strong>도입 결과</strong>: 유통 비용 증가로 인한 전반적인 시장 물가 급등을 방어하고, 국가 물류 경쟁력을 유지하는 버팀목 역할을 하고 있습니다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트(Vanilla JS) vs 루아(Lua) 비교]]></title>
            <link>https://velog.io/@han_oal/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8Vanilla-JS-vs-%EB%A3%A8%EC%95%84Lua-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@han_oal/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8Vanilla-JS-vs-%EB%A3%A8%EC%95%84Lua-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Sun, 28 Jun 2026 12:11:37 GMT</pubDate>
            <description><![CDATA[<p>웹과 프론트엔드의 절대 강자 <strong>자바스크립트(JavaScript)</strong>와, 게임 엔진 및 임베디드 스크립트의 감초 <strong>루아(Lua)</strong>는 닮은 듯하면서도 완전히 다른 매력을 가진 멀티 패러다임 언어입니다. </p>
<p>두 언어의 철학, 문법, 데이터 구조, 비동기 메커니즘까지 벨로그 독자분들을 위해 모든 차이점을 세부적인 코드 예시와 함께 파헤쳐 봅니다.</p>
<hr>
<h2 id="1-핵심-설계-철학-및-용도">1. 핵심 설계 철학 및 용도</h2>
<table>
<thead>
<tr>
<th align="left">비교 항목</th>
<th align="left">자바스크립트 (JavaScript)</th>
<th align="left">루아 (Lua)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>탄생 목적</strong></td>
<td align="left">웹 브라우저 내 동적 제어 및 상호작용</td>
<td align="left">C/C++ 프로그램에 쉽게 내장(Embedded)할 수 있는 가벼운 스크립트 엔진</td>
</tr>
<tr>
<td align="left"><strong>주요 사용처</strong></td>
<td align="left">웹 프론트엔드/백엔드(Node.js), 앱 개발(RN)</td>
<td align="left">게임 개발(로블록스, 월드오브워크래프트), 임베디드, IoT</td>
</tr>
<tr>
<td align="left"><strong>엔진 크기</strong></td>
<td align="left">V8 엔진 기준 수십 MB 이상 (무겁고 복잡함)</td>
<td align="left">순수 C 언어로 작성되어 수백 KB 수준 (극도로 가볍고 빠름)</td>
</tr>
</tbody></table>
<hr>
<h2 id="2-기본-문법-및-스타일-차이">2. 기본 문법 및 스타일 차이</h2>
<p>두 언어는 코드 표기법(Syntax) 스타일에서 확연한 차이를 보입니다. 자바스크립트는 <strong>C-Style(중괄호)</strong>을, 루아는 <strong>Ada/Pascal-Style(텍스트 키워드)</strong>을 따릅니다.</p>
<h3 id="21-코드-블록-및-주석-스타일">2.1. 코드 블록 및 주석 스타일</h3>
<ul>
<li><strong>JavaScript:</strong> 중괄호 <code>{}</code>로 블록을 구분하며, 주석은 <code>//</code> 또는 <code>/* */</code>를 사용합니다. 문장 끝에 세미콜론 <code>;</code>을 붙이는 것이 관례입니다.</li>
<li><strong>Lua:</strong> <code>then</code>, <code>do</code>로 블록을 시작하고 <code>end</code>로 블록을 닫습니다. 주석은 <code>--</code> 또는 <code>--[[ --]]</code>를 사용합니다. 세미콜론 <code>;</code>은 완전히 선택 사항이며 거의 쓰지 않습니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 조건문 예시
if (score &gt;= 90) {
    console.log(&quot;A 학점&quot;);
} else {
    console.log(&quot;B 학점&quot;);
}</code></pre>
<pre><code class="language-lua">-- [Lua] 조건문 예시
if score &gt;= 90 then
    print(&quot;A 학점&quot;)
else
    print(&quot;B 학점&quot;)
end</code></pre>
<h3 id="22-변수-선언과-기본-스코프-가장-주의할-점">2.2. 변수 선언과 기본 스코프 (가장 주의할 점!)</h3>
<ul>
<li><strong>JavaScript:</strong> 키워드 없이 변수를 선언하면 에러가 나거나 strict 모드가 아닐 때 전역 변수가 됩니다. 주로 <code>let</code>, <code>const</code>를 사용해 깔끔한 <strong>블록 스코프</strong>를 만듭니다.</li>
<li><strong>Lua:</strong> 기본적으로 키워드 없이 변수를 선언하면 <strong>무조건 전역 변수(Global)</strong>가 됩니다. 지역 블록 스코프를 제한하려면 변수 앞에 <strong><code>local</code> 키워드</strong>를 반드시 명시해야 합니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 변수 스코프
function test() {
    let localVar = &quot;이것은 지역 변수&quot;;
    const constantVar = &quot;이것은 상수&quot;;
}</code></pre>
<pre><code class="language-lua">-- [Lua] 변수 스코프
function test()
    local localVar = &quot;이것은 지역 변수&quot;
    globalVar = &quot;키워드가 없으므로 전역 변수가 됨 (주의!)&quot;
end</code></pre>
<hr>
<h2 id="3-자료형-및-데이터-구조">3. 자료형 및 데이터 구조</h2>
<h3 id="31-인덱스-시작-번호-0-vs-1">3.1. 인덱스 시작 번호 (0 vs 1)</h3>
<p>개발자들 사이에서 가장 유명한 차이점입니다.</p>
<ul>
<li><strong>JavaScript:</strong> 배열의 첫 번째 요소 인덱스는 <strong><code>0</code></strong>부터 시작합니다.</li>
<li><strong>Lua:</strong> 모든 리스트와 테이블의 인덱스는 기본적으로 <strong><code>1</code></strong>부터 시작합니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 인덱싱
const arr = [&quot;사과&quot;, &quot;바나나&quot;];
console.log(arr[0]); // 출력: 사과</code></pre>
<pre><code class="language-lua">-- [Lua] 인덱싱
local arr = {&quot;사과&quot;, &quot;바나나&quot;}
print(arr[1])        -- 출력: 사과 (1부터 시작!)</code></pre>
<h3 id="32-만능-데이터-구조-object-vs-table">3.2. 만능 데이터 구조: Object vs Table</h3>
<ul>
<li><strong>JavaScript:</strong> 데이터를 표현할 때 배열 <code>[]</code>과 객체 <code>{}</code>를 명확히 구분합니다.</li>
<li><strong>Lua:</strong> 오직 <strong>테이블(Table) <code>{}</code></strong>이라는 단 하나의 데이터 구조만 존재합니다. 테이블을 순서대로 쓰면 배열이 되고, 키-값 쌍으로 쓰면 딕셔너리/객체가 됩니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 배열과 객체 분리
let userArray = [&quot;Minjo&quot;, 25];
let userObject = { name: &quot;Minjo&quot;, age: 25 };</code></pre>
<pre><code class="language-lua">-- [Lua] 모든 것은 테이블로 해결
local userArray = {&quot;Minjo&quot;, 25}                    -- 리스트 스타일 테이블
local userObject = { name = &quot;Minjo&quot;, age = 25 }    -- 딕셔너리 스타일 테이블</code></pre>
<h3 id="33-존재하지-않는-값-nullundefined-vs-nil">3.3. 존재하지 않는 값 (null/undefined vs nil)</h3>
<ul>
<li><strong>JavaScript:</strong> 값이 비어있음을 뜻하는 <code>null</code>과 정의되지 않음을 뜻하는 <code>undefined</code>가 분리되어 있습니다.</li>
<li><strong>Lua:</strong> 오직 <strong><code>nil</code></strong> 하나만 존재합니다. 테이블에서 특정 키에 <code>nil</code>을 대입하면 해당 데이터가 완전히 삭제되는 독특한 특성이 있습니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 데이터 빈 값 처리
let data = null;
let profile = { name: &quot;Minjo&quot; };
profile.name = null; // 값은 여전히 key로 남아있음</code></pre>
<pre><code class="language-lua">-- [Lua] nil 대입을 통한 요소 삭제
local profile = { name = &quot;Minjo&quot;, age = 25 }
profile.age = nil   -- age 키 자체가 테이블에서 완전히 삭제됨</code></pre>
<hr>
<h2 id="4-객체지향-프로그래밍oop-구현-방식">4. 객체지향 프로그래밍(OOP) 구현 방식</h2>
<p>두 언어 모두 클래스 기반 언어(Java, C++ 등)가 아니며, <strong>프로토타입과 메타 구조</strong>를 활용해 OOP를 흉내 냅니다.</p>
<ul>
<li><strong>JavaScript (Class Syntax):</strong> 프로토타입 기반 체인이지만, ES6 이후 <code>class</code>, <code>constructor</code>, <code>extends</code> 키워드를 도입하여 직관적인 클래스 상속 문법을 제공합니다.</li>
<li><strong>Lua (Metatable):</strong> 클래스 관련 키워드가 아예 없습니다. 대신 테이블의 동작을 재정의하는 <strong>메타테이블(Metatable)</strong>과 <code>__index</code> 메타메소드를 활용해 개발자가 직접 상속 구조를 바닥부터 구현해야 합니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 현대적인 클래스 상속
class Animal {
    constructor(name) { this.name = name; }
    speak() { console.log(this.name + &quot;가 소리를 냅니다.&quot;); }
}
class Dog extends Animal {}</code></pre>
<pre><code class="language-lua">-- [Lua] 메타테이블을 활용한 프로토타입 상속 직접 구현
Animal = {}
function Animal:new(name)
    local obj = { name = name }
    setmetatable(obj, { __index = self }) -- 상속 구조 연결
    return obj
end
function Animal:speak() print(self.name .. &quot;가 소리를 냅니다.&quot;) end

Dog = Animal:new() -- 상속 효과</code></pre>
<hr>
<h2 id="5-비동기-및-동시성-메커니즘">5. 비동기 및 동시성 메커니즘</h2>
<ul>
<li><strong>JavaScript (이벤트 루프):</strong> 싱글 스레드로 동작하지만 웹 브라우저/Node.js 환경의 <strong>이벤트 루프(Event Loop)</strong>를 기반으로 합니다. <code>Promise</code>, <code>async/await</code>를 통해 비동기 Non-blocking I/O 처리가 핵심 강점입니다.</li>
<li><strong>Lua (코루틴):</strong> 이벤트 루프가 없습니다. 대신 <strong>코루틴(Coroutine, 협력적 멀티태스킹)</strong>을 제공합니다. 실행 중인 루틴을 일시 정지(<code>coroutine.yield</code>)시키고, 필요할 때 다시 재개(<code>coroutine.resume</code>)하는 방식으로 스레드 제어권을 개발자가 동기적으로 직접 주고받습니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] Promise 기반 비동기 처리
async function fetchData() {
    let response = await fetch(&#39;https://api.example.com&#39;);
    let data = await response.json();
    console.log(data);
}</code></pre>
<pre><code class="language-lua">-- [Lua] 코루틴을 이용한 상태 일시 정지 및 재개
local co = coroutine.create(function()
    print(&quot;코루틴 시작&quot;)
    coroutine.yield() -- 중간에 제어권 반납하고 멈춤
    print(&quot;코루틴 재개됨&quot;)
end)

coroutine.resume(co) -- &quot;코루틴 시작&quot; 출력 후 대기
coroutine.resume(co) -- &quot;코루틴 재개됨&quot; 출력</code></pre>
<hr>
<h2 id="6-한눈에-보는-비교-요약표">6. 한눈에 보는 비교 요약표</h2>
<table>
<thead>
<tr>
<th align="left">특징</th>
<th align="left">자바스크립트 (JavaScript)</th>
<th align="left">루아 (Lua)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>문법 스타일</strong></td>
<td align="left">C 스타일 (<code>{}</code>, <code>//</code>, <code>;</code>)</td>
<td align="left">Ada/Pascal 스타일 (<code>end</code>, <code>--</code>)</td>
</tr>
<tr>
<td align="left"><strong>배열 인덱스</strong></td>
<td align="left"><code>0</code>번부터 시작</td>
<td align="left"><code>1</code>번부터 시작</td>
</tr>
<tr>
<td align="left"><strong>기본 변수 스코프</strong></td>
<td align="left"><code>let</code>, <code>const</code> (블록 스코프 선언 필요)</td>
<td align="left">키워드 없으면 전역(Global), <code>local</code> 선언 필수</td>
</tr>
<tr>
<td align="left"><strong>대표 데이터 구조</strong></td>
<td align="left">Array <code>[]</code>, Object <code>{}</code> 분리</td>
<td align="left">오직 Table <code>{}</code>로 통합 관리</td>
</tr>
<tr>
<td align="left"><strong>비어있는 값</strong></td>
<td align="left"><code>null</code> 및 <code>undefined</code></td>
<td align="left"><code>nil</code></td>
</tr>
<tr>
<td align="left"><strong>동시성 처리</strong></td>
<td align="left">이벤트 루프, Promise, async/await</td>
<td align="left">코루틴 (Coroutine) 체계</td>
</tr>
<tr>
<td align="left"><strong>클래스 상속</strong></td>
<td align="left"><code>class</code>, <code>extends</code> 문법 내장</td>
<td align="left">메타테이블(<code>metatable</code>)로 직접 구현</td>
</tr>
</tbody></table>
<hr>
<h2 id="💡-벨로그-업로드-총평">💡 벨로그 업로드 총평</h2>
<p><strong>자바스크립트</strong>는 거대한 웹 생태계와 강력한 비동기 처리를 위해 엔진 자체가 거대해지고 고도화된 언어라면, <strong>루아</strong>는 메인 프로그램(C/C++)을 보조하기 위해 불필요한 기능을 전부 덜어내고 극도의 가벼움과 커스텀 가능성을 추구한 언어입니다. </p>
<p>자바스크립트에 익숙한 프론트엔드 개발자가 루아(예: 로블록스 개발 등)를 접할 때는 <strong>1번 인덱스 시작</strong>과 <strong><code>local</code> 키워드 생략 시 전역 변수가 되는 점</strong>만 주의하면 빠르게 적응하실 수 있습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트(Vanilla JS) vs 루아(Lua) 비교 ]]></title>
            <link>https://velog.io/@han_oal/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8Vanilla-JS-vs-%EB%A3%A8%EC%95%84Lua-%EB%81%9D%EC%9E%A5-%EB%B9%84%EA%B5%90-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@han_oal/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8Vanilla-JS-vs-%EB%A3%A8%EC%95%84Lua-%EB%81%9D%EC%9E%A5-%EB%B9%84%EA%B5%90-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Thu, 18 Jun 2026 07:09:50 GMT</pubDate>
            <description><![CDATA[<p>웹과 프론트엔드의 절대 강자 <strong>자바스크립트(JavaScript)</strong>와, 게임 엔진 및 임베디드 스크립트의 감초 <strong>루아(Lua)</strong>는 닮은 듯하면서도 완전히 다른 매력을 가진 멀티 패러다임 언어입니다. </p>
<p>두 언어의 철학, 문법, 데이터 구조, 비동기 메커니즘까지 벨로그 독자분들을 위해 모든 차이점을 세부적인 코드 예시와 함께 파헤쳐 봅니다.</p>
<hr>
<h2 id="1-핵심-설계-철학-및-용도">1. 핵심 설계 철학 및 용도</h2>
<table>
<thead>
<tr>
<th align="left">비교 항목</th>
<th align="left">자바스크립트 (JavaScript)</th>
<th align="left">루아 (Lua)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>탄생 목적</strong></td>
<td align="left">웹 브라우저 내 동적 제어 및 상호작용</td>
<td align="left">C/C++ 프로그램에 쉽게 내장(Embedded)할 수 있는 가벼운 스크립트 엔진</td>
</tr>
<tr>
<td align="left"><strong>주요 사용처</strong></td>
<td align="left">웹 프론트엔드/백엔드(Node.js), 앱 개발(RN)</td>
<td align="left">게임 개발(로블록스, 월드오브워크래프트), 임베디드, IoT</td>
</tr>
<tr>
<td align="left"><strong>엔진 크기</strong></td>
<td align="left">V8 엔진 기준 수십 MB 이상 (무겁고 복잡함)</td>
<td align="left">순수 C 언어로 작성되어 수백 KB 수준 (극도로 가볍고 빠름)</td>
</tr>
</tbody></table>
<hr>
<h2 id="2-기본-문법-및-스타일-차이">2. 기본 문법 및 스타일 차이</h2>
<p>두 언어는 코드 표기법(Syntax) 스타일에서 확연한 차이를 보입니다. 자바스크립트는 <strong>C-Style(중괄호)</strong>을, 루아는 <strong>Ada/Pascal-Style(텍스트 키워드)</strong>을 따릅니다.</p>
<h3 id="21-코드-블록-및-주석-스타일">2.1. 코드 블록 및 주석 스타일</h3>
<ul>
<li><strong>JavaScript:</strong> 중괄호 <code>{}</code>로 블록을 구분하며, 주석은 <code>//</code> 또는 <code>/* */</code>를 사용합니다. 문장 끝에 세미콜론 <code>;</code>을 붙이는 것이 관례입니다.</li>
<li><strong>Lua:</strong> <code>then</code>, <code>do</code>로 블록을 시작하고 <code>end</code>로 블록을 닫습니다. 주석은 <code>--</code> 또는 <code>--[[ --]]</code>를 사용합니다. 세미콜론 <code>;</code>은 완전히 선택 사항이며 거의 쓰지 않습니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 조건문 예시
if (score &gt;= 90) {
    console.log(&quot;A 학점&quot;);
} else {
    console.log(&quot;B 학점&quot;);
}</code></pre>
<pre><code class="language-lua">-- [Lua] 조건문 예시
if score &gt;= 90 then
    print(&quot;A 학점&quot;)
else
    print(&quot;B 학점&quot;)
end</code></pre>
<h3 id="22-변수-선언과-기본-스코프-가장-주의할-점">2.2. 변수 선언과 기본 스코프 (가장 주의할 점!)</h3>
<ul>
<li><strong>JavaScript:</strong> 키워드 없이 변수를 선언하면 에러가 나거나 strict 모드가 아닐 때 전역 변수가 됩니다. 주로 <code>let</code>, <code>const</code>를 사용해 깔끔한 <strong>블록 스코프</strong>를 만듭니다.</li>
<li><strong>Lua:</strong> 기본적으로 키워드 없이 변수를 선언하면 <strong>무조건 전역 변수(Global)</strong>가 됩니다. 지역 블록 스코프를 제한하려면 변수 앞에 <strong><code>local</code> 키워드</strong>를 반드시 명시해야 합니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 변수 스코프
function test() {
    let localVar = &quot;이것은 지역 변수&quot;;
    const constantVar = &quot;이것은 상수&quot;;
}</code></pre>
<pre><code class="language-lua">-- [Lua] 변수 스코프
function test()
    local localVar = &quot;이것은 지역 변수&quot;
    globalVar = &quot;키워드가 없으므로 전역 변수가 됨 (주의!)&quot;
end</code></pre>
<hr>
<h2 id="3-자료형-및-데이터-구조">3. 자료형 및 데이터 구조</h2>
<h3 id="31-인덱스-시작-번호-0-vs-1">3.1. 인덱스 시작 번호 (0 vs 1)</h3>
<p>개발자들 사이에서 가장 유명한 차이점입니다.</p>
<ul>
<li><strong>JavaScript:</strong> 배열의 첫 번째 요소 인덱스는 <strong><code>0</code></strong>부터 시작합니다.</li>
<li><strong>Lua:</strong> 모든 리스트와 테이블의 인덱스는 기본적으로 <strong><code>1</code></strong>부터 시작합니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 인덱싱
const arr = [&quot;사과&quot;, &quot;바나나&quot;];
console.log(arr[0]); // 출력: 사과</code></pre>
<pre><code class="language-lua">-- [Lua] 인덱싱
local arr = {&quot;사과&quot;, &quot;바나나&quot;}
print(arr[1])        -- 출력: 사과 (1부터 시작!)</code></pre>
<h3 id="32-만능-데이터-구조-object-vs-table">3.2. 만능 데이터 구조: Object vs Table</h3>
<ul>
<li><strong>JavaScript:</strong> 데이터를 표현할 때 배열 <code>[]</code>과 객체 <code>{}</code>를 명확히 구분합니다.</li>
<li><strong>Lua:</strong> 오직 <strong>테이블(Table) <code>{}</code></strong>이라는 단 하나의 데이터 구조만 존재합니다. 테이블을 순서대로 쓰면 배열이 되고, 키-값 쌍으로 쓰면 딕셔너리/객체가 됩니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 배열과 객체 분리
let userArray = [&quot;Minjo&quot;, 25];
let userObject = { name: &quot;Minjo&quot;, age: 25 };</code></pre>
<pre><code class="language-lua">-- [Lua] 모든 것은 테이블로 해결
local userArray = {&quot;Minjo&quot;, 25}                    -- 리스트 스타일 테이블
local userObject = { name = &quot;Minjo&quot;, age = 25 }    -- 딕셔너리 스타일 테이블</code></pre>
<h3 id="33-존재하지-않는-값-nullundefined-vs-nil">3.3. 존재하지 않는 값 (null/undefined vs nil)</h3>
<ul>
<li><strong>JavaScript:</strong> 값이 비어있음을 뜻하는 <code>null</code>과 정의되지 않음을 뜻하는 <code>undefined</code>가 분리되어 있습니다.</li>
<li><strong>Lua:</strong> 오직 <strong><code>nil</code></strong> 하나만 존재합니다. 테이블에서 특정 키에 <code>nil</code>을 대입하면 해당 데이터가 완전히 삭제되는 독특한 특성이 있습니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 데이터 빈 값 처리
let data = null;
let profile = { name: &quot;Minjo&quot; };
profile.name = null; // 값은 여전히 key로 남아있음</code></pre>
<pre><code class="language-lua">-- [Lua] nil 대입을 통한 요소 삭제
local profile = { name = &quot;Minjo&quot;, age = 25 }
profile.age = nil   -- age 키 자체가 테이블에서 완전히 삭제됨</code></pre>
<hr>
<h2 id="4-객체지향-프로그래밍oop-구현-방식">4. 객체지향 프로그래밍(OOP) 구현 방식</h2>
<p>두 언어 모두 클래스 기반 언어(Java, C++ 등)가 아니며, <strong>프로토타입과 메타 구조</strong>를 활용해 OOP를 흉내 냅니다.</p>
<ul>
<li><strong>JavaScript (Class Syntax):</strong> 프로토타입 기반 체인이지만, ES6 이후 <code>class</code>, <code>constructor</code>, <code>extends</code> 키워드를 도입하여 직관적인 클래스 상속 문법을 제공합니다.</li>
<li><strong>Lua (Metatable):</strong> 클래스 관련 키워드가 아예 없습니다. 대신 테이블의 동작을 재정의하는 <strong>메타테이블(Metatable)</strong>과 <code>__index</code> 메타메소드를 활용해 개발자가 직접 상속 구조를 바닥부터 구현해야 합니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] 현대적인 클래스 상속
class Animal {
    constructor(name) { this.name = name; }
    speak() { console.log(this.name + &quot;가 소리를 냅니다.&quot;); }
}
class Dog extends Animal {}</code></pre>
<pre><code class="language-lua">-- [Lua] 메타테이블을 활용한 프로토타입 상속 직접 구현
Animal = {}
function Animal:new(name)
    local obj = { name = name }
    setmetatable(obj, { __index = self }) -- 상속 구조 연결
    return obj
end
function Animal:speak() print(self.name .. &quot;가 소리를 냅니다.&quot;) end

Dog = Animal:new() -- 상속 효과</code></pre>
<hr>
<h2 id="5-비동기-및-동시성-메커니즘">5. 비동기 및 동시성 메커니즘</h2>
<ul>
<li><strong>JavaScript (이벤트 루프):</strong> 싱글 스레드로 동작하지만 웹 브라우저/Node.js 환경의 <strong>이벤트 루프(Event Loop)</strong>를 기반으로 합니다. <code>Promise</code>, <code>async/await</code>를 통해 비동기 Non-blocking I/O 처리가 핵심 강점입니다.</li>
<li><strong>Lua (코루틴):</strong> 이벤트 루프가 없습니다. 대신 <strong>코루틴(Coroutine, 협력적 멀티태스킹)</strong>을 제공합니다. 실행 중인 루틴을 일시 정지(<code>coroutine.yield</code>)시키고, 필요할 때 다시 재개(<code>coroutine.resume</code>)하는 방식으로 스레드 제어권을 개발자가 동기적으로 직접 주고받습니다.</li>
</ul>
<pre><code class="language-javascript">// [JavaScript] Promise 기반 비동기 처리
async function fetchData() {
    let response = await fetch(&#39;https://api.example.com&#39;);
    let data = await response.json();
    console.log(data);
}</code></pre>
<pre><code class="language-lua">-- [Lua] 코루틴을 이용한 상태 일시 정지 및 재개
local co = coroutine.create(function()
    print(&quot;코루틴 시작&quot;)
    coroutine.yield() -- 중간에 제어권 반납하고 멈춤
    print(&quot;코루틴 재개됨&quot;)
end)

coroutine.resume(co) -- &quot;코루틴 시작&quot; 출력 후 대기
coroutine.resume(co) -- &quot;코루틴 재개됨&quot; 출력</code></pre>
<hr>
<h2 id="6-한눈에-보는-비교-요약표">6. 한눈에 보는 비교 요약표</h2>
<table>
<thead>
<tr>
<th align="left">특징</th>
<th align="left">자바스크립트 (JavaScript)</th>
<th align="left">루아 (Lua)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>문법 스타일</strong></td>
<td align="left">C 스타일 (<code>{}</code>, <code>//</code>, <code>;</code>)</td>
<td align="left">Ada/Pascal 스타일 (<code>end</code>, <code>--</code>)</td>
</tr>
<tr>
<td align="left"><strong>배열 인덱스</strong></td>
<td align="left"><code>0</code>번부터 시작</td>
<td align="left"><code>1</code>번부터 시작</td>
</tr>
<tr>
<td align="left"><strong>기본 변수 스코프</strong></td>
<td align="left"><code>let</code>, <code>const</code> (블록 스코프 선언 필요)</td>
<td align="left">키워드 없으면 전역(Global), <code>local</code> 선언 필수</td>
</tr>
<tr>
<td align="left"><strong>대표 데이터 구조</strong></td>
<td align="left">Array <code>[]</code>, Object <code>{}</code> 분리</td>
<td align="left">오직 Table <code>{}</code>로 통합 관리</td>
</tr>
<tr>
<td align="left"><strong>비어있는 값</strong></td>
<td align="left"><code>null</code> 및 <code>undefined</code></td>
<td align="left"><code>nil</code></td>
</tr>
<tr>
<td align="left"><strong>동시성 처리</strong></td>
<td align="left">이벤트 루프, Promise, async/await</td>
<td align="left">코루틴 (Coroutine) 체계</td>
</tr>
<tr>
<td align="left"><strong>클래스 상속</strong></td>
<td align="left"><code>class</code>, <code>extends</code> 문법 내장</td>
<td align="left">메타테이블(<code>metatable</code>)로 직접 구현</td>
</tr>
</tbody></table>
<hr>
<h2 id="💡-벨로그-업로드-총평">💡 벨로그 업로드 총평</h2>
<p><strong>자바스크립트</strong>는 거대한 웹 생태계와 강력한 비동기 처리를 위해 엔진 자체가 거대해지고 고도화된 언어라면, <strong>루아</strong>는 메인 프로그램(C/C++)을 보조하기 위해 불필요한 기능을 전부 덜어내고 극도의 가벼움과 커스텀 가능성을 추구한 언어입니다. </p>
<p>자바스크립트에 익숙한 프론트엔드 개발자가 루아(예: 로블록스 개발 등)를 접할 때는 <strong>1번 인덱스 시작</strong>과 <strong><code>local</code> 키워드 생략 시 전역 변수가 되는 점</strong>만 주의하면 빠르게 적응하실 수 있습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[로블록스(Roblox) 게임 개발 시작하기: 필수 준비물 & 권장 사양 총정리]]></title>
            <link>https://velog.io/@han_oal/%EB%A1%9C%EB%B8%94%EB%A1%9D%EC%8A%A4Roblox-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-%ED%95%84%EC%88%98-%EC%A4%80%EB%B9%84%EB%AC%BC-%EA%B6%8C%EC%9E%A5-%EC%82%AC%EC%96%91-%EC%B4%9D%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@han_oal/%EB%A1%9C%EB%B8%94%EB%A1%9D%EC%8A%A4Roblox-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-%ED%95%84%EC%88%98-%EC%A4%80%EB%B9%84%EB%AC%BC-%EA%B6%8C%EC%9E%A5-%EC%82%AC%EC%96%91-%EC%B4%9D%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 18 Jun 2026 06:32:33 GMT</pubDate>
            <description><![CDATA[<p>로블록스 게임 개발은 Unity나 Unreal 등 다른 상용 엔진에 비해 진입 장벽이 낮고 1인 개발 생태계가 매우 잘 구축되어 있습니다.</p>
<p>개발 스튜디오 설치부터 하드웨어 사양, 그리고 필요한 기술 스택까지 벨로그 독자분들을 위해 단 한 페이지로 깔끔하게 정리해 드립니다.</p>
<hr>
<h2 id="1-필수-소프트웨어--계정">1. 필수 소프트웨어 &amp; 계정</h2>
<p>로블록스 개발의 시작과 끝은 공식 툴 안에서 모두 해결됩니다. 참고로 <strong>100% 무료</strong>입니다.</p>
<ul>
<li><p><strong>로블록스 계정:</strong> 평소 게임을 플레이할 때 사용하는 계정 그대로 개발 툴에 로그인할 수 있습니다.</p>
</li>
<li><p><strong>로블록스 스튜디오 (Roblox Studio):</strong> 로블록스 전용 게임 개발 엔진입니다. 맵 빌드, 코딩(스크립팅), 애니메이션 제작, 테스트, 그리고 글로벌 출시까지 이 프로그램 하나로 전부 처리합니다.</p>
</li>
</ul>
<hr>
<h2 id="2-하드웨어-요구-사양-pc">2. 하드웨어 요구 사양 (PC)</h2>
<p>로블록스 스튜디오는 상대적으로 가벼운 엔진이지만, 맵이 커지거나 파트(물체) 개수가 많아지고 그래픽 효과를 넣기 시작하면 사양을 꽤 타게 됩니다. 쾌적한 개발을 위해 아래 사양을 체크해 보세요.</p>
<table>
<thead>
<tr>
<th align="left">부품 목록</th>
<th align="left">최소 사양</th>
<th align="left">권장 사양 (추천)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>운영체제 (OS)</strong></td>
<td align="left">Windows 10 / macOS 10.13</td>
<td align="left"><strong>Windows 11 / 최신 macOS</strong></td>
</tr>
<tr>
<td align="left"><strong>프로세서 (CPU)</strong></td>
<td align="left">1.6 GHz 이상 프로세서</td>
<td align="left"><strong>2.5 GHz 이상 (Intel i5 / AMD Ryzen 5 이상)</strong></td>
</tr>
<tr>
<td align="left"><strong>메모리 (RAM)</strong></td>
<td align="left">4 GB</td>
<td align="left"><strong>16 GB 이상 (원활한 빌드 및 멀티태스킹)</strong></td>
</tr>
<tr>
<td align="left"><strong>그래픽카드 (GPU)</strong></td>
<td align="left">내장 그래픽 카드 지원</td>
<td align="left"><strong>Nvidia GTX 1050 / AMD RX 560 급 이상의 외장 그래픽</strong></td>
</tr>
</tbody></table>
<blockquote>
<p><strong>⚠️ 주의 (기기 제한):</strong>
로블록스 &#39;플레이&#39;는 스마트폰이나 태블릿(iPad) 등 모든 기기에서 가능하지만, <strong>로블록스 &#39;스튜디오(개발)&#39;는 오직 PC 환경(Windows / Mac)에서만 실행</strong>할 수 있습니다.</p>
</blockquote>
<hr>
<h2 id="3-핵심-기술-스택-학습-준비">3. 핵심 기술 스택 (학습 준비)</h2>
<p>단순히 맵을 꾸미는 것을 넘어 진짜 &#39;게임&#39;의 규칙을 만들고 살아 움직이게 하려면 약간의 기술적 준비가 필요합니다.</p>
<h3 id="🔴-루아luau-스크립트-문법">🔴 루아(Luau) 스크립트 문법</h3>
<p>로블록스는 <strong>Lua(정확히는 자체적으로 발전시킨 Luau)</strong> 언어를 사용합니다. 파이썬이나 자바스크립트만큼 직관적이고 배우기 쉽기 때문에, 프로그래밍이 처음이시라면 아래 기초 문법 위주로 먼저 가볍게 훑어보시는 것을 추천합니다.</p>
<ul>
<li>변수 지정 (<code>local 변수명 = 값</code>)</li>
<li>조건문 (<code>if - then - else - end</code>)</li>
<li>함수 정의 (<code>local function 함수명()</code>)</li>
</ul>
<h3 id="🔵-3d-좌표계에-대한-이해">🔵 3D 좌표계에 대한 이해</h3>
<p>게임 공간이 3D이기 때문에 <code>Vector3</code>를 활용한 <strong>&lt;X, Y, Z&gt;</strong> 공간 좌표 개념이 필수적입니다. 오브젝트의 위치(<code>Position</code>), 크기(<code>Size</code>), 회전(<code>Orientation</code>)을 스크립트로 제어하는 감각을 익히게 됩니다.</p>
<hr>
<h2 id="4-디자인-및-리소스-생산-서브-툴-선택">4. 디자인 및 리소스 생산 서브 툴 (선택)</h2>
<p>스튜디오 내부 에셋(Toolbox)만으로도 개발할 수 있지만, 나만의 독창적인 아이템이나 퀄리티 높은 UI를 만들고 싶다면 아래 툴들을 병행하는 것이 좋습니다.</p>
<table>
  <tr>
    <td width="30%"><strong>서브 툴</strong></td>
    <td width="70%"><strong>개발 시 활용 목적</strong></td>
  </tr>
  <tr>
    <td><strong>블렌더 (Blender)</strong></td>
    <td>기본 블록(Part) 외에 나만의 무기, 악세서리, 정교한 캐릭터 아이템을 3D 모델링하여 가져올 때 필수 (무료)</td>
  </tr>
  <tr>
    <td><strong>피그마 / 포토샵</strong></td>
    <td>게임 내 화면에 띄울 상점 메뉴, 체력 바, 아이콘 등 2D UI(User Interface) 요소를 디자인할 때 사용</td>
  </tr>
</table>

<hr>
<h2 id="5-슬기로운-에러-해결법-커뮤니티-활용하기">5. 슬기로운 에러 해결법: 커뮤니티 활용하기</h2>
<p>개발을 하다 보면 무조건 스크립트 에러나 알 수 없는 버그에 부딪히게 됩니다. 전 세계적으로 개발자 생태계가 워낙 거대하므로 아래 창구들을 적극적으로 활용하세요.</p>
<ol>
<li><p><strong>로블록스 개발자 포럼 (DevForum):</strong> 공식 에러 해결법, API 명세, 그리고 전 세계 고수들의 팁이 모여있는 가장 거대한 해결 창구입니다. (크롬 번역기 켜고 검색으로 대부분 해석이 가능합니다.)</p>
</li>
<li><p><strong>개발자 디스코드(Discord) 커뮤니티:</strong> 국내외 활성화된 로블록스 개발 디스코드 서버에 상주하며 실시간으로 질문을 주고받으면 막히는 버그를 단 몇 분 만에 해결할 수 있습니다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[마크다운(Markdown) 문법 치트 시트]]></title>
            <link>https://velog.io/@han_oal/%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4Markdown-%EB%AC%B8%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC-%EC%B9%98%ED%8A%B8-%EC%8B%9C%ED%8A%B8</link>
            <guid>https://velog.io/@han_oal/%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4Markdown-%EB%AC%B8%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC-%EC%B9%98%ED%8A%B8-%EC%8B%9C%ED%8A%B8</guid>
            <pubDate>Thu, 18 Jun 2026 06:08:57 GMT</pubDate>
            <description><![CDATA[<p>마크다운(Markdown)의 기본 문법부터 확장 문법까지 빠짐없이 정리했습니다. </p>
<hr>
<h2 id="1-제목-headings">1. 제목 (Headings)</h2>
<p><code>#</code> 개수로 수준(Level)을 조절합니다. <code>#</code> 뒤에는 <strong>반드시 한 칸을 띄워야</strong> 합니다.</p>
<h3 id="💻-작성법">💻 작성법</h3>
<pre><code class="language-markdown"># H1 제목
## H2 제목
### H3 제목
#### H4 제목</code></pre>
<h3 id="📄-출력-결과">📄 출력 결과</h3>
<h1 id="h1-제목">H1 제목</h1>
<h2 id="h2-제목">H2 제목</h2>
<h3 id="h3-제목">H3 제목</h3>
<h4 id="h4-제목">H4 제목</h4>
<hr>
<h2 id="2-텍스트-스타일-text-emphasis">2. 텍스트 스타일 (Text Emphasis)</h2>
<p>텍스트의 강조와 스타일을 지정합니다. 기호 앞뒤에 공백이 없어야 적용됩니다.</p>
<table>
  <tr>
    <td width="50%"><strong>마크다운 작성법</strong></td>
    <td width="50%"><strong>실제 출력 결과</strong></td>
  </tr>
  <tr>
    <td>
      <code>*이타릭체*</code> 또는 <code>_이타릭체_</code><br>
      <code>**볼드체(굵게)**</code> 또는 <code>__볼드체__</code><br>
      <code>***굵은 이타릭체***</code><br>
      <code>~~취소선~~</code><br>
      <code><u>밑줄</u></code>
    </td>
    <td>
      <em>이타릭체</em> 또는 <em>이타릭체</em><br>
      <strong>볼드체(굵게)</strong> 또는 <strong>볼드체</strong><br>
      <strong><em>굵은 이타릭체</em></strong><br>
      <del>취소선</del><br>
      <u>밑줄</u>
    </td>
  </tr>
</table>

<hr>
<h2 id="3-줄바꿈-및-문단-line-breaks--paragraphs">3. 줄바꿈 및 문단 (Line Breaks &amp; Paragraphs)</h2>
<ul>
<li><strong>문단 바꾸기:</strong> 엔터(Enter)를 <strong>두 번</strong> 입력하여 중간에 빈 줄을 만듭니다.</li>
<li><strong>줄바꿈(Line Break):</strong> 문장 끝에 <strong>스페이스바를 두 번(  )</strong> 누르고 엔터를 치거나, <code>&lt;br&gt;</code> 태그를 삽입합니다.</li>
</ul>
<hr>
<h2 id="4-목록-lists">4. 목록 (Lists)</h2>
<h3 id="💻-작성법-1">💻 작성법</h3>
<pre><code class="language-markdown">* 사과
  * 소분류 (스페이스바 2칸 들여쓰기)
- 바나나

1. 첫 번째
2. 두 번째

- [ ] 할 일 1
- [x] 완료된 일 2</code></pre>
<h3 id="📄-출력-결과-1">📄 출력 결과</h3>
<ul>
<li>사과<ul>
<li>소분류</li>
</ul>
</li>
<li>바나나</li>
</ul>
<ol>
<li>첫 번째</li>
<li>두 번째</li>
</ol>
<ul>
<li><input disabled="" type="checkbox"> 할 일 1</li>
<li><input checked="" disabled="" type="checkbox"> 완료된 일 2</li>
</ul>
<hr>
<h2 id="5-링크-links-및-이미지-images">5. 링크 (Links) 및 이미지 (Images)</h2>
<h3 id="💻-작성법-2">💻 작성법</h3>
<pre><code class="language-markdown">[벨로그 바로가기](https://velog.io &quot;벨로그&quot;)
[https://google.com](https://google.com)

![이미지 설명](https://images.velog.io/velog_logo.png)</code></pre>
<h3 id="📄-출력-결과-2">📄 출력 결과</h3>
<p><a href="https://velog.io" title="벨로그">벨로그 바로가기</a><br><a href="https://google.com">https://google.com</a></p>
<p><img src="https://images.velog.io/velog_logo.png" alt="이미지 설명"></p>
<hr>
<h2 id="6-인용문-blockquotes">6. 인용문 (Blockquotes)</h2>
<h3 id="💻-작성법-3">💻 작성법</h3>
<pre><code class="language-markdown">&gt; 이것은 인용문입니다.
&gt;&gt; 인용문 안의 중첩 인용문</code></pre>
<h3 id="📄-출력-결과-3">📄 출력 결과</h3>
<blockquote>
<p>이것은 인용문입니다.</p>
<blockquote>
<p>인용문 안의 중첩 인용문</p>
</blockquote>
</blockquote>
<hr>
<h2 id="7-코드-code와-코드-블록-code-blocks">7. 코드 (Code)와 코드 블록 (Code Blocks)</h2>
<p>텍스트 중간에 코드를 넣을 때는 백틱 1개, 블록으로 넣을 때는 백틱 3개를 사용하고 언어 이름을 적어줍니다.</p>
<h3 id="💻-작성법-4">💻 작성법</h3>
<pre><code class="language-markdown">이 명령어는 `npm install` 입니다.

```javascript
function hello() {
    console.log(&quot;Hello World&quot;);
}</code></pre>
<pre><code>
### 📄 출력 결과
이 명령어는 `npm install` 입니다.

```javascript
function hello() {
    console.log(&quot;Hello World&quot;);
}</code></pre><hr>
<h2 id="8-표-tables">8. 표 (Tables)</h2>
<p>수직선(<code>|</code>)과 하이픈(<code>-</code>)으로 구성하며, 콜론(<code>:</code>)의 위치로 정렬을 결정합니다.</p>
<h3 id="💻-작성법-5">💻 작성법</h3>
<pre><code class="language-markdown">| 이름 | 나이 | 직업 |
| :--- | :---: | ---: |
| 홍길동 | 20 | 의적 (좌측) |
| 이순신 | 45 | 장군 (가운데) |</code></pre>
<h3 id="📄-출력-결과-4">📄 출력 결과</h3>
<table>
<thead>
<tr>
<th align="left">이름</th>
<th align="center">나이</th>
<th align="right">직업</th>
</tr>
</thead>
<tbody><tr>
<td align="left">홍길동</td>
<td align="center">20</td>
<td align="right">의적 (좌측)</td>
</tr>
<tr>
<td align="left">이순신</td>
<td align="center">45</td>
<td align="right">장군 (가운데)</td>
</tr>
</tbody></table>
<hr>
<h2 id="9-접기펼치기-details-및-수평선">9. 접기/펼치기 (Details) 및 수평선</h2>
<p>벨로그에서 긴 코드나 힌트를 숨겨둘 때 유용하게 쓰이는 내장 HTML 문법입니다.</p>
<h3 id="💻-작성법-6">💻 작성법</h3>
<pre><code class="language-markdown">--- (수평선)

&lt;details&gt;
&lt;summary&gt;👉 여기를 눌러서 펼치기&lt;/summary&gt;

숨겨진 내용이 여기에 나타납니다!
&lt;/details&gt;</code></pre>
<h3 id="📄-출력-결과-5">📄 출력 결과</h3>
<hr>
<details>
<summary>👉 여기를 눌러서 펼치기</summary>

<p>숨겨진 내용이 여기에 나타납니다!</p>
</details>

<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[디스코드 커뮤니티 봇 'CodyBot' 프로젝트 명세서 & 기술 아키텍처]]></title>
            <link>https://velog.io/@han_oal/%EB%94%94%EC%8A%A4%EC%BD%94%EB%93%9C-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0-%EB%B4%87-%EC%BD%94%EB%94%94%EB%B4%87CodyBot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%AA%85%EC%84%B8%EC%84%9C-%EA%B8%B0%EC%88%A0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</link>
            <guid>https://velog.io/@han_oal/%EB%94%94%EC%8A%A4%EC%BD%94%EB%93%9C-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0-%EB%B4%87-%EC%BD%94%EB%94%94%EB%B4%87CodyBot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%AA%85%EC%84%B8%EC%84%9C-%EA%B8%B0%EC%88%A0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</guid>
            <pubDate>Tue, 16 Jun 2026 07:18:36 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/han_oal/post/0f9eb85a-4c68-47c8-9771-028b42f8c8bb/image.png" alt=""></p>
<p>본 문서는 디스코드 커뮤니티 활성화를 위해 제작된 종합 게이미케이션(Gamification) 및 운영 자동화 봇인 <strong>코디봇(CodyBot)</strong>의 프로젝트 개요이자 기술 명세서입니다. 블로그 포스트(기획 의도 + 기술적 구현을 결합한 하이브리드 포트폴리오 형식)로 즉시 업로드하고 활용할 수 있도록 구성되었습니다.</p>
<hr>
<h2 id="1-프로젝트-개요--비즈니스-목적">1. 프로젝트 개요 &amp; 비즈니스 목적</h2>
<p>디스코드 서버 운영진이 직면하는 가장 큰 고민은 <strong>&quot;어떻게 유저들의 자발적인 대화와 활동을 유도하고 유지할 것인가?&quot;</strong>입니다. 코디봇은 이러한 문제를 해결하기 위해 커뮤니티에 가상 경제와 RPG 요소를 접목하여 기획되었습니다.</p>
<ul>
<li><strong>커뮤니티 게이미케이션</strong>: 유저의 활동(채팅, 음성 대화, 이미지/비디오 공유)을 감지하여 가상 재화를 지급하고, 이를 등급 승급, 1:1 트레이드, 멘토링, 관계망(클랜, 커플) 형성으로 연결시켜 생태계를 활성화합니다.</li>
<li><strong>운영 자동화</strong>: 성인 인증, 온보딩 흐름, 조건 충족 시 등급 승급 및 미활동 유저 자동 강등(Demotion) 등 반복적이고 민감한 운영 프로세스를 스케줄러로 무인화합니다.</li>
<li><strong>보안 및 트랜잭션 안전성</strong>: 데이터 조작이나 비정상적인 재화 복사 버그 등을 원천 차단하기 위해 모든 핵심 로직은 트랜잭션 기반으로 설계되었습니다.</li>
</ul>
<hr>
<h2 id="2-핵심-기술-스택">2. 핵심 기술 스택</h2>
<ul>
<li><strong>런타임</strong>: Node.js (CommonJS 패키지 모델)</li>
<li><strong>API 라이브러리</strong>: Discord.js v14</li>
<li><strong>데이터베이스</strong>: SQLite (<code>better-sqlite3</code> 모듈 활용)<ul>
<li><em>선택 이유</em>: 내장형 서버 환경에서 동기식 API 지원으로 콜백 헬을 줄이고 빠른 읽기/쓰기 성능 및 안전한 트랜잭션 단위를 구성하기에 가장 효율적입니다.</li>
</ul>
</li>
<li><strong>스케줄러</strong>: <code>node-cron</code> (승급/하락 검사, 랭킹 리더보드 동기화, 기간제 효과 만료)</li>
</ul>
<hr>
<h2 id="3-사용자-온보딩--생명주기-user-onboarding--lifecycle">3. 사용자 온보딩 &amp; 생명주기 (User Onboarding &amp; Lifecycle)</h2>
<p>새 유저가 서버에 입장한 후 온보딩 단계를 완전히 마치기 전까지는 봇의 혜택 및 악용 방지를 위한 가드(Guard)가 적용됩니다. </p>
<h3 id="31-온보딩-3단계-프로세스">3.1 온보딩 3단계 프로세스</h3>
<ol>
<li><strong>1단계: 성인 인증</strong> - <code>adult:confirm</code> 버튼 패널을 통해 인증을 진행하며 인증 완료 시 특정 채널 접근 제한이 해제됩니다.</li>
<li><strong>2단계: 멘토 지정</strong> - 신입 유저를 이끌어줄 멘토 유저를 지정하거나 건너뜁니다.</li>
<li><strong>3단계: 닉네임 설정</strong> - DB와 서버 닉네임을 동기화합니다.</li>
</ol>
<p>온보딩 3단계가 모두 완료(<code>onboarding_step = 3</code>)되고 나면 비로소 임시 등급(<code>NONE</code>)에서 정식 서비스 대상자인 <strong>STARTER</strong> 등급으로 승급되며 봇의 가상 경제 활동이 허용됩니다.</p>
<h3 id="32-온보딩--등급-순환-라이프사이클-다이어그램-mermaid">3.2 온보딩 &amp; 등급 순환 라이프사이클 다이어그램 (Mermaid)</h3>
<pre><code class="language-mermaid">graph TD
    %% 1. 입장 및 온보딩
    Start([디스코드 서버 입장]) --&gt; Onboarding1[온보딩 1단계: 성인 인증]
    Onboarding1 --&gt; Onboarding2[온보딩 2단계: 멘토 지정/스킵]
    Onboarding2 --&gt; Onboarding3[온보딩 3단계: 닉네임 설정]

    %% 2. 정식 등록 (STARTER)
    Onboarding3 --&gt;|온보딩 완료: onboarding_step = 3| Starter[rank = STARTER 역할 부여 &amp; 가드 해제]

    %% 3. 활동 및 재화 획득
    Starter --&gt; Activity[서버 활동: 채팅, 음성, 데일리 퀘스트]
    Activity --&gt;|EXP/AP 획득| Wallet[지갑 데이터 갱신 및 wallets.updated_at 최신화]

    %% 4. 승급 (Promotion)
    Wallet --&gt;|10분 주기 스케줄러 검사| PromotionCheck{승급 조건 충족?}
    PromotionCheck --&gt;|아니오| Activity
    PromotionCheck --&gt;|예: EXP/AP 차감| Promoted[등급 상승 및 승급 보상 지급]

    %% 등급 순환 구조
    Promoted --&gt; RankCycle{현재 등급}
    RankCycle --&gt;|MEMBER| Member[MEMBER 등급]
    RankCycle --&gt;|CREW| Crew[CREW 등급]
    RankCycle --&gt;|CORE| Core[CORE 등급]

    %% 활동과 미활동 루프
    Member --&gt; MemberAct[채팅/음성 활동]
    MemberAct --&gt;|wallets.updated_at 갱신| Member

    Crew --&gt; CrewAct[채팅/음성 활동]
    CrewAct --&gt;|wallets.updated_at 갱신| Crew

    Core --&gt; CoreAct[채팅/음성 활동]
    CoreAct --&gt;|wallets.updated_at 갱신| Core

    %% 5. 자동 하락 (Demotion)
    Member --&gt;|9일간 미활동| DemoteM[MEMBER → STARTER 강등]
    Crew --&gt;|9일간 미활동| DemoteC[CREW → MEMBER 강등]
    Core --&gt;|9일간 미활동| DemoteCo[CORE → CREW 강등]

    DemoteM --&gt; Starter
    DemoteC --&gt; Member
    DemoteCo --&gt; Crew</code></pre>
<hr>
<h2 id="4-등급-승급-및-자동-하락-promotion--demotion">4. 등급 승급 및 자동 하락 (Promotion &amp; Demotion)</h2>
<h3 id="41-등급별-승급-요건">4.1 등급별 승급 요건</h3>
<p>유저는 채팅이나 음성 활동 등으로 획득한 경험치(EXP)와 활동 포인트(AP)를 소모해 등급을 올릴 수 있습니다. 승급 스케줄러는 10분마다 자동으로 후보군을 조회하여 처리합니다.</p>
<table>
<thead>
<tr>
<th align="left">승급 구간</th>
<th align="center">필요 EXP (경험치)</th>
<th align="center">필요 AP (활동 포인트)</th>
<th align="left">비고</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>STARTER → MEMBER</strong></td>
<td align="center">2,500</td>
<td align="center">30</td>
<td align="left">자동 승급 스케줄러에 의한 승급 처리</td>
</tr>
<tr>
<td align="left"><strong>MEMBER → CREW</strong></td>
<td align="center">10,000</td>
<td align="center">120</td>
<td align="left">승급 완료 시 조건 재화 차감 및 승급 로그 기록</td>
</tr>
<tr>
<td align="left"><strong>CREW → CORE</strong></td>
<td align="center">35,000</td>
<td align="center">300</td>
<td align="left">승급 보상 테이블 기반 아이템 자동 지급</td>
</tr>
</tbody></table>
<h3 id="42-자동-하락-demotion-규칙">4.2 자동 하락 (Demotion) 규칙</h3>
<ul>
<li><strong>이유</strong>: 커뮤니티의 활동성을 유지하고 고등급 역할의 희소성을 보장하기 위함입니다.</li>
<li><strong>트리거</strong>: 유저의 활동으로 인한 지갑 수정일(<code>wallets.updated_at</code>) 기준 <strong>9일 동안 미활동</strong> 시 적용됩니다.</li>
<li><strong>적용</strong>: 10분마다 하락 스케줄러가 돌아가며 <code>CORE → CREW</code>, <code>CREW → MEMBER</code>, <code>MEMBER → STARTER</code> 순으로 1단계씩 강등을 실행합니다. (STARTER 등급 미만으로는 떨어지지 않습니다.)</li>
</ul>
<hr>
<h2 id="5-가상-경제--활동-보상-엔진-economy-engine">5. 가상 경제 &amp; 활동 보상 엔진 (Economy Engine)</h2>
<p>유저의 모든 유의미한 활동은 지갑의 3대 자원(<code>EXP</code>, <code>AP</code>, <code>CC</code>) 축적으로 이어집니다. 어뷰징 방지를 위해 하루 최대 한도와 분당 쿨타임이 안전하게 적용되어 있습니다.</p>
<h3 id="51-재화-획득-테이블">5.1 재화 획득 테이블</h3>
<ul>
<li><strong>CC (Credit)</strong>: 인게임 경제의 화폐 역할을 수행합니다.</li>
</ul>
<table>
<thead>
<tr>
<th align="left">활동 종류</th>
<th align="center">지급 재화 (EXP / AP / CC)</th>
<th align="left">조건 및 한도 설정</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>텍스트 채팅</strong></td>
<td align="center">EXP 5 / CC 2</td>
<td align="left">분당 1회 쿨타임, 하루 최대 30분 한도</td>
</tr>
<tr>
<td align="left"><strong>음성 채널 참여</strong></td>
<td align="center">EXP 4 / CC 3</td>
<td align="left">분당 지급, <strong>최소 2인 이상 입장 시</strong>, 하루 최대 120분 한도</td>
</tr>
<tr>
<td align="left"><strong>하루사진 (미디어)</strong></td>
<td align="center">EXP 100 / CC 150</td>
<td align="left">하루 1회, 미디어 첨부 채널</td>
</tr>
<tr>
<td align="left"><strong>투데이토픽</strong></td>
<td align="center">EXP 100 / CC 150</td>
<td align="left">하루 1회, 텍스트 또는 파일 업로드</td>
</tr>
<tr>
<td align="left"><strong>하루음악</strong></td>
<td align="center">EXP 100 / CC 150</td>
<td align="left">하루 1회, 유튜브 등 외부 음악 링크 공유</td>
</tr>
<tr>
<td align="left"><strong>심리테스트</strong></td>
<td align="center">EXP 100 / CC 150</td>
<td align="left">하루 1회, 결과 캡처 첨부</td>
</tr>
<tr>
<td align="left"><strong>활동사진 (인증)</strong></td>
<td align="center">EXP 150 / AP 5 / CC 300</td>
<td align="left">하루 2회, 이미지 첨부 및 <strong>1명 이상 멘션 태그 필수</strong></td>
</tr>
<tr>
<td align="left"><strong>활동클립 (동영상)</strong></td>
<td align="center">EXP 300 / AP 15 / CC 600</td>
<td align="left">하루 2회, 비디오 첨부 및 <strong>1명 이상 멘션 태그 필수</strong></td>
</tr>
</tbody></table>
<h3 id="52-크레딧-송금-및-거래-수수료">5.2 크레딧 송금 및 거래 수수료</h3>
<p>유저 간 자유롭게 크레딧을 주고받을 수 있으나, 가상 경제 인플레이션 및 어뷰징 방지를 위해 <strong>기본 수수료 1%(반올림)</strong>가 송금하는 원금에 더해져 추가 차감됩니다.</p>
<hr>
<h2 id="6-상점-특별-아이템-획득-및-11-트레이드-시스템">6. 상점, 특별 아이템 획득 및 1:1 트레이드 시스템</h2>
<h3 id="61-상점-및-특별-아이템-규칙-중요">6.1 상점 및 특별 아이템 규칙 (중요)</h3>
<p>상점에서 닉네임 장식, 랜덤박스, 관계 생성권을 판매하지만, 커뮤니티의 랭크 가치를 높이기 위해 특수한 규칙이 적용됩니다.</p>
<ol>
<li><strong>상점 구매 불가능한 아이템</strong>:<ul>
<li><strong>일반 등급 닉네임 색상 아이템</strong> 및 <strong>일반 랜덤박스</strong>는 상점에서 구매(<code>Buy</code>)할 수 없으며, 오직 <strong>MEMBER(멤버) 등급 승급 시 보상으로만 지급</strong>받을 수 있습니다.</li>
</ul>
</li>
<li><strong><code>price_cc</code>의 역할 전환</strong>:<ul>
<li>상점에서 구매가 불가능한 위 아이템들의 <code>price_cc</code> 값은 구매 가격이 아니라, 유저가 인벤토리에 보관 중인 해당 아이템을 상점에 <strong>판매(Sell)하여 환전할 때의 기준 금액</strong>으로 사용됩니다.</li>
</ul>
</li>
</ol>
<h3 id="62-일대일-안전-거래-trade">6.2 일대일 안전 거래 (Trade)</h3>
<ul>
<li>판매자가 구매자에게 특정 아이템과 요구 CC를 담은 제안을 발송합니다.</li>
<li>디스코드 DM으로 수신된 확인 창에서 구매자가 &#39;수락&#39; 버튼을 누르면, 봇 백엔드에서 거래 정합성(지갑 잔액 및 인벤토리 아이템 소유 상태)을 재확인한 뒤 DB 트랜잭션 처리를 거쳐 즉시 교환을 완결시킵니다.</li>
</ul>
<hr>
<h2 id="7-기술적-구현-핵심-데이터-무결성을-위한-db-트랜잭션-설계">7. 기술적 구현 핵심: 데이터 무결성을 위한 DB 트랜잭션 설계</h2>
<p>가상 경제와 아이템 교환이 이루어지는 만큼, 여러 쿼리가 동시에 실행될 때 발생할 수 있는 데이터 정합성 오류(Race Condition, 재화 무한 복사 버그 등)를 차단하는 것이 기술적으로 가장 중요했습니다.</p>
<p>코디봇은 Node.js 환경에서 작동하는 <code>better-sqlite3</code> 라이브러리의 동기식 트랜잭션 기능을 활용하여 데이터 정합성을 확실하게 보호합니다.</p>
<h3 id="71-데이터베이스-트랜잭션-래퍼-구현-dbtxjs">7.1 데이터베이스 트랜잭션 래퍼 구현 (<code>db/tx.js</code>)</h3>
<p>모든 비즈니스 로직 서비스들은 아래의 트랜잭션 래퍼를 통해 DB 쿼리 블록을 하나로 묶어 실행합니다. 블록 내의 쿼리 중 단 하나라도 실패하거나 예외가 발생하면 전체 작업이 자동으로 롤백(Rollback)됩니다.</p>
<pre><code class="language-javascript">// db/tx.js
const { db } = require(&quot;./client&quot;);

/**
 * 트랜잭션 실행 함수
 * @param {Function} fn - 트랜잭션 내부에서 안전하게 실행할 비즈니스 로직 함수
 * @returns {*} fn의 반환값
 * @throws {Error} 예외 발생 시 트랜잭션 실패 및 전체 롤백
 */
function transaction(fn) {
  const tx = db.transaction(fn);
  return tx(); // 동기적으로 트랜잭션 즉시 실행
}

module.exports = {
  transaction,
};</code></pre>
<h3 id="72-트랜잭션-활용-예시-크레딧-송금-서비스-servicescredittransferservicejs">7.2 트랜잭션 활용 예시: 크레딧 송금 서비스 (<code>services/creditTransfer.service.js</code>)</h3>
<p>아래 코드는 두 유저의 지갑 잔액을 업데이트하고 송수신 로그를 원자적으로 기록하는 실질적인 구현 예시입니다. </p>
<pre><code class="language-javascript">const { db } = require(&quot;../db/client&quot;);
const { transaction } = require(&quot;../db/tx&quot;);

function executeTransfer({ fromId, toId, amount, fee, reason, channelId }) {
  // 1. 송금자와 수신자의 지갑이 DB에 존재하는지 보장
  ensureWallet(fromId);
  ensureWallet(toId);

  // 2. 송금자의 크레딧 잔여 금액 사전 검증
  const senderWallet = db.prepare(`SELECT credit FROM wallets WHERE user_id = ?`).get(fromId);
  const total = amount + fee;
  if (!senderWallet || senderWallet.credit &lt; total) {
    return { ok: false, code: &quot;INSUFFICIENT_CREDIT&quot; };
  }

  // 3. 트랜잭션 단위 실행 (원자성 보장)
  const result = transaction(() =&gt; {
    // A. 송금 유저의 지갑에서 전체 금액(원금 + 수수료) 차감
    db.prepare(
      `UPDATE wallets
       SET credit = credit - ?, updated_at = datetime(&#39;now&#39;)
       WHERE user_id = ?`
    ).run(total, fromId);

    // B. 수신 유저의 지갑에 원금 추가
    db.prepare(
      `UPDATE wallets
       SET credit = credit + ?, updated_at = datetime(&#39;now&#39;)
       WHERE user_id = ?`
    ).run(amount, toId);

    // C. 송금 히스토리 로그 기록
    db.prepare(
      `INSERT INTO credit_log (user_id, delta, source_type, source_id, channel_id)
       VALUES (?, ?, ?, ?, ?)`
    ).run(fromId, -total, &quot;CREDIT_TRANSFER_SEND&quot;, toId, channelId);

    // D. 수신 히스토리 로그 기록
    db.prepare(
      `INSERT INTO credit_log (user_id, delta, source_type, source_id, channel_id)
       VALUES (?, ?, ?, ?, ?)`
    ).run(toId, amount, &quot;CREDIT_TRANSFER_RECEIVE&quot;, fromId, channelId);

    // 최신화된 양측 지갑 잔액 리턴
    const senderBalance = db.prepare(`SELECT credit FROM wallets WHERE user_id = ?`).get(fromId)?.credit ?? 0;
    const receiverBalance = db.prepare(`SELECT credit FROM wallets WHERE user_id = ?`).get(toId)?.credit ?? 0;

    return { senderBalance, receiverBalance };
  });

  return {
    ok: true,
    senderBalance: result.senderBalance,
    receiverBalance: result.receiverBalance,
  };
}</code></pre>
<hr>
<h2 id="8-관계망-시스템-멘토링-클랜-및-커플">8. 관계망 시스템: 멘토링, 클랜 및 커플</h2>
<h3 id="81-멘토-멘티-시스템">8.1 멘토-멘티 시스템</h3>
<ul>
<li><strong>구조</strong>: 신입 스타터 유저가 멘토 선배 유저를 등록하여 관계를 맺습니다.</li>
<li><strong>활동 관리</strong>: 멘토는 동시에 돌볼 수 있는 Active 멘티를 <strong>최대 3명</strong>으로 제한하여 활발한 밀착 관리가 이루어지도록 돕습니다.</li>
<li><strong>졸업 및 슬롯 반환</strong>: 멘티가 적극적으로 활동하여 <code>MEMBER</code> 등급으로 자동 승급하는 즉시 <strong>졸업(Graduation)</strong> 처리되며, 멘토의 멘티 자리가 한 칸 반환됩니다. 졸업 횟수가 누적된 멘토 순위 리더보드가 2시간마다 동기화됩니다.</li>
</ul>
<h3 id="82-클랜-clan-시스템">8.2 클랜 (Clan) 시스템</h3>
<ul>
<li><strong>창설</strong>: <code>CREW</code> 등급 이상만이 클랜 창설 아이템(소모성)을 사용하여 개설할 수 있습니다. </li>
<li><strong>성장과 레벨업</strong>: 클랜원들이 기부한 클랜 포인트를 활용하여 클랜을 레벨업시킬 수 있으며, 클랜 레벨이 증가함에 따라 클랜 정원과 전용 보이스/채팅 채널의 최대 생성 제한이 늘어납니다.</li>
<li><strong>클랜 창고 및 지갑</strong>: 클랜원들은 크레딧이나 인벤토리 아이템을 공동 창고에 입고/기부할 수 있으며, 클랜장의 권한 부여(직급 관리)에 따라 간부 이상만 출고할 수 있도록 통제됩니다.</li>
</ul>
<h3 id="83-커플-couple-시스템">8.3 커플 (Couple) 시스템</h3>
<ul>
<li><strong>매칭</strong>: 커플 티켓 아이템을 사용하여 상대방에게 신청을 보냅니다. 10분 내에 수락하면 성사됩니다.</li>
<li><strong>혜택</strong>: 디스코드 서버 내 전용 핑크 커플 역할 배정, 커플 전용 공동 통장 개설 및 커플 전용 보이스 채널(기간제 30일) 생성 혜택이 제공됩니다.</li>
</ul>
<hr>
<h2 id="9-결론-및-향후-개선-과제">9. 결론 및 향후 개선 과제</h2>
<p>코디봇은 단순 정보 제공형 봇에서 벗어나, 디스코드라는 채팅 공간에 <strong>RPG 게임 같은 재미와 가상 경제 규칙</strong>을 성공적으로 심어놓았습니다.</p>
<p>다만, 멀티 서버(다중 길드)를 대상으로 서비스를 확장할 경우 단일 파일형 SQLite 데이터베이스의 특성상 동시성(Locking) 한계가 발생할 수 있습니다. 향후 대규모 서버 확장을 타겟팅할 때는 클라우드 환경에 적합한 <strong>PostgreSQL</strong>과 같은 관계형 데이터베이스로 마이그레이션하고, 트랜잭션 격리 수준(Isolation Level)을 세밀하게 제어하는 개발 단계가 예정되어 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[세계 3대 원유]]></title>
            <link>https://velog.io/@han_oal/%EC%84%B8%EA%B3%84-3%EB%8C%80-%EC%9B%90%EC%9C%A0</link>
            <guid>https://velog.io/@han_oal/%EC%84%B8%EA%B3%84-3%EB%8C%80-%EC%9B%90%EC%9C%A0</guid>
            <pubDate>Tue, 16 Jun 2026 05:02:31 GMT</pubDate>
            <description><![CDATA[<h2 id="조사-배경">조사 배경</h2>
<blockquote>
<p>주식에서 쳐 맞고 외양간 고치러 가요. 🥲</p>
</blockquote>
<p>주식 매매를 시작하면서 자연스럽게 유가의 등락이 증시에 큰 영향을 준다는 걸 알게 되었습니다. 이번 미국-이란 전쟁에서 이란의 호르무즈 해협 봉쇄가 곧 유가 상승의 원인이 되었습니다. 전쟁 발발 직후 코스피는 10% 이상 급락을 했고, 전문가들은 이 날 낙폭이 당일 하락 기준 9.11 테러보다 많은 하락폭이라고 말합니다.</p>
<p>&quot;원유가 그 정도로 중요한건가?&quot; 라는 생각이 들었습니다. 내가 접할 수 있는 &#39;기름&#39;의 중요성은 차량에 채우는 소비재 따위에 불과했습니다. 아무리 기름값이 올라도 나에게는 10만원 안팎의 추가 지불 정도였으니 말입니다. 다만 주식을 하면서 세계 시장은 유가에 좌지우지 된다는 걸 몸으로 느끼게 되었고, 원유라는 것이 왜 이렇게 시장에 막대한 영향을 끼치는지 궁금해졌습니다.</p>
<h2 id="원유crude-oil란">원유(crude oil)란</h2>
<p>사전에서는 원유를 &quot;지하 유층에서 채굴한 정제하지 않은 상태의 액체 탄화수소 혼합체&quot;라고 정의하고 있는데요. 간단히 땅에서 갓 뽑아낸 가공되지 않은 기름을 <code>원유</code>라고 한다는 것입니다.</p>
<h2 id="원유는-끓여서-가공한다">원유는 끓여서 가공한다</h2>
<blockquote>
<p>인간은 원유의 등골까지 쏙쏙 빨아먹는다. 아낌없이 주는 나무인가요</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/han_oal/post/4f6767a7-fc39-43fd-a6e3-5e647319683f/image.png" alt="">
원유를 거대한 증류탑에 넣고 끓이면 끓는점에 따라 위에서 아래로 차례로 분리되는데, 이를 분별증류 라고 한답니다. 탑의 위쪽에서부터 가장 아래쪽에 남는 무거운 찌꺼기까지 버리는 것 하나 없이 탈탈 털어서 활용한다고 합니다.</p>
<p>원유의 가격이 갑자기 오르게 되면 내 생활에 어떤 제약이 생기는지 와닿게 정리해보았습니다.</p>
<h2 id="유가-상승과-일상의-관계">유가 상승과 일상의 관계</h2>
<hr>
<ul>
<li><p><strong>옷 가격이 올라간다 [나프타 ➔ 합성섬유]</strong></p>
</li>
<li><p><strong>화장품 가격이 올라간다 [나프타 ➔ 화학오일]</strong></p>
</li>
<li><p><strong>플라스틱 제품 가격이 올라간다 [나프타 ➔ 플라스틱]</strong></p>
</li>
<li><p><strong>식비가 올라간다 [중유·등유 ➔ 물류비]</strong></p>
</li>
<li><p><strong>운동화 가격이 올라간다 [나프타 ➔ 합성고무]</strong></p>
</li>
<li><p><strong>매일 쓰는 샴푸와 세안제 가격이 올라간다 [나프타 ➔ 계면활성제]</strong></p>
</li>
<li><p><strong>우산, 비닐봉지 등 잡화 가격이 올라간다 [나프타 ➔ 방수 코팅 및 필름]</strong></p>
</li>
<li><p><strong>배달비가 올라간다 [휘발유 ➔ 오토바이 연료]</strong></p>
</li>
<li><p><strong>택배비(쿠팡 로켓배송)가 올라간다 [경유 ➔ 화물 트럭 연료]</strong></p>
</li>
<li><p><strong>마트 채소 및 농산물 가격이 올라간다 [나프타 ➔ 질소 비료]</strong></p>
</li>
<li><p><strong>전기세와 난방비가 올라간다 [중유·등유 ➔ 발전 및 난방 연료]</strong></p>
</li>
<li><p><strong>인플레이션으로 인해 월세가 올라간다 [아스팔트·찌꺼기 ➔ 건축 자재]</strong></p>
</li>
<li><p><strong>자가용 엔진오일 교체비가 올라간다 [기유 ➔ 윤활유]</strong></p>
</li>
<li><p><strong>인테리어 가구 및 바닥재 가격이 올라간다 [나프타 ➔ PVC 및 합성수지]</strong></p>
</li>
<li><p><strong>항공권 가격이 올라간다 [등유 ➔ 항공유]</strong></p>
</li>
<li><p><strong>대출 이자가 올라간다 [유가 상승 ➔ 기준금리 인상]</strong></p>
</li>
</ul>
<hr>
<p>내가 타는 버스와 지하철, 매일 만지는 스마트폰 케이스와 노트북, 심지어 입고 있는 옷과 마라탕에 들어가는 채소의 비료까지 지구상에 존재하는 거의 모든 문명이 원유라는 사기템을 굳히고 쪼개서 만든 결과물이라는 것입니다.</p>
<p>좀 무섭지 않나요.</p>
<h2 id="세계-3대-원유">세계 3대 원유</h2>
<p>세계에는 수많은 유전에서 나오는 원유가 있고, 각 원유마다 고유의 특성과 성분의 차이는 물론 물류 조건과 생산 지역이 모두 다르답니다. 그래서 원유 각각의 가격을 매기기는 꽤 까다로운 편인데요. </p>
<p>이때 기준 좌표가 되어주는 것이 바로 <code>3대 원유</code>입니다. 시장에서 거래가 매우 활발해서 가격 형성이 투명하고, 믿을 수 있는 기준이 되어주기 때문이죠.</p>
<p>여기서 <code>3대 원유</code>란</p>
<ul>
<li>두바이유</li>
<li>브렌트유</li>
<li>텍사스유(WTI)</li>
</ul>
<p>위 3종의 원유를 묶어 부르는 말입니다.</p>
<p>쉽게 말해 두바이유는 아시아, 브렌트유는 유럽, 텍사스유는 북미 즉 3개의 대륙을 대표하는 녀석들입니다. 얘네들을 기준으로 삼아 다른 원유들의 품질이나 여러 조건들을 비교해가며 가격을 더하거나 빼는 식으로 전체 유가를 계산하는 시스템인 거죠.</p>
<h3 id="텍사스유wti">텍사스유(WTI)</h3>
<p>서부 텍사스 지역에서 생산되는 원유이며, 불순물(황 함량)이 가장 낮아 3대 원유 중 가장 비싼 가격에 거래된답니다. 원래 미국 내에서만 전량 소비되지만, 세계 최대 선물 시장인 <code>뉴욕상업거래소(NYMEX)</code>에 상장되어 대량 거래된다는 상징성 때문에 국제 유가의 대장 지표로 활용되곤 합니다. 육상에서 생산되어 파이프라인 운송비가 붙다 보니 과거에는 다른 원유들보다 확실히 비쌌는데, 미국 셰일 혁명 이후 공급량이 미친 듯이 늘어나면서 요즘엔 브렌트유보다 낮게 거래되는 경우도 흔합니다.</p>
<h3 id="브렌트유brant">브렌트유(brant)</h3>
<p>영국과 노르웨이 사이의 북해 유전에서 생산되는 원유들을 총칭하는 단어입니다. 서부 텍사스유와 더불어 황 성분이 적은 고품질 원유로 대접받는데요. 이 녀석의 강력한 장점은 바다(해상)에서 생산되어 바로 대형 선박에 실어 나를 수 있다는 점입니다. 전 세계로의 배달이 매우 자유롭다는 뜻이죠. 우리가 뉴스나 대중 매체에서 &#39;브렌트유&#39;를 들을 때는 보통 런던 선물시장(ICE)의 선물 가격을 의미합니다.</p>
<h3 id="두바이유dubai">두바이유(Dubai)</h3>
<p>중동 지역에서 생산되는 원유들을 총칭하는 단어입니다. 실제 두바이 땅에서만 나오는 기름은 아닌데요. 앞선 두 원유에 비하면 품질은 가장 떨어지는 편인데, 황 함량이 많고 끈적하고 묵직한 중질유에 속하기 때문입니다. WTI와 브렌트유가 선물 시장에서 주로 굴러가는 것과 달리, 두바이유는 전통적으로 묻고 더블로 바로 쏴주는 현물 거래의 비중이 크답니다.</p>
<h2 id="한국은-두바이유를-사용한다">한국은 두바이유를 사용한다</h2>
<p>대한민국이 3대 원유 중 가장 질 낮은 원유인 두바이유를 선택한 이유는 크게 3가지입니다. </p>
<p>우선 저렴한 가격입니다. 두바이유는 3대 원유 중 황 함량이 가장 높은 질 낮은 원유이며, 가격 또한 가장 저렴합니다. 가격이 싸니 일단 대량으로 떼어오기 이보다 좋을 수 없습니다.</p>
<p>둘째로 저렴한 물류비입니다. 두바이유는 중동, 즉 서아시아 쪽에 위치해 있어 지정학적으로 북미(WTI)나 유럽(브렌트유)보다 우리나라와 훨씬 가깝습니다. 배로 실어 나르는 거리가 짧으니 당연히 기본 운송비가 굳습니다.</p>
<p>마지막이자 가장 결정적인 이유는 &#39;정유 시설의 기술력&#39; 입니다. 이게 꽤 재밌는 포인트인데, 싼 게 비지떡이라고 찌꺼기(황)가 많은 질이 좋지 않은 두바이유를 가져와서 고급 휘발유로 벼려낼 수 있는 초고도화된 정제 공장을 우리나라 정유사들이 빵빵하게 지어놨기 때문입니다. </p>
<p>기술력이 괴물 수준이니 굳이 비싼 WTI나 브렌트유를 살 필요가 없는 거죠. 싼값에 두바이유를 떼어다가 기술력으로 탈탈 털어서 고품질 기름으로 가공해 쓰는 것이 훨씬 이득이니까요. 실제로 우리나라는 기름 한 방울 안 나는 나라지만, 이렇게 만든 석유 제품을 전 세계에 역수출하는 &#39;석유 제품 수출 강국&#39;이기도 하답니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프록시 서버 (proxy-server)]]></title>
            <link>https://velog.io/@han_oal/%ED%94%84%EB%A1%9D%EC%8B%9C-%EC%84%9C%EB%B2%84-proxy-server</link>
            <guid>https://velog.io/@han_oal/%ED%94%84%EB%A1%9D%EC%8B%9C-%EC%84%9C%EB%B2%84-proxy-server</guid>
            <pubDate>Tue, 22 Oct 2024 13:38:08 GMT</pubDate>
            <description><![CDATA[<h2 id="조사-배경">조사 배경</h2>
<p>네트워크 관련 책을 보다가 문뜩 Proxy(프록시) 라는 용어가 반복적으로 눈에 띄었습니다. 프록시에 대해서 잘 안다고 생각되지 않아서 이번 기회에 정확한 개념을 알아보았습니다.</p>
<h2 id="단어-자체의-의미">단어 자체의 의미</h2>
<blockquote>
<p>단어의 의미는 &#39;대리&#39; &#39;대리인&#39;이라는 의미로 직관적으로 느껴지네요</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/han_oal/post/3b914940-35b5-417b-ba64-fc1c2230b7d3/image.png" alt=""></p>
<p>프록시 서버를 직역하면 <strong>대리자[역할을 하는] 서버</strong> 라는 의미로 생각할 수 있겠습니다. </p>
<h2 id="프록시-서버">프록시 서버</h2>
<p><strong>프록시 서버(Proxy Server)</strong> 는 클라이언트와 서버 사이에 중간 역할을 하는 서버로, 클라이언트의 요청을 받아 대신 서버에 전달하고, 서버로부터 받은 응답을 클라이언트에게 전달하는 역할을 합니다. 쉽게 말하면, 클라이언트가 직접 서버에 접근하지 않고, 프록시 서버를 거쳐서 요청을 처리하게 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/han_oal/post/ab5d5f4e-7030-4422-9f6c-c6e35dc26420/image.png" alt=""></p>
<h2 id="프록시서버는-http-통신에-필수일까">프록시서버는 http 통신에 필수일까?</h2>
<p>프록시 서버는 HTTP 통신에 무조건 존재하는 것은 아닙니다. 
프록시 서버는 특정 요구나 목적에 따라 사용자가 선택적으로 사용하는 중간 서버입니다.
일반적인 HTTP 통신에서는 클라이언트(웹 브라우저)와 서버(웹사이트)가 
직접적으로 통신을 주고받습니다.</p>
<h2 id="프록시-서버의-두-가지-종류-forward-vs-reverse">프록시 서버의 두 가지 종류: Forward vs Reverse</h2>
<p>프록시 서버는 &#39;누구의 편에 서 있느냐&#39;에 따라 크게 두 가지로 나뉩니다.</p>
<p><img src="https://velog.velcdn.com/images/han_oal/post/8b24f359-4a43-4d68-83bf-72ba70bba63d/image.png" alt=""></p>
<h3 id="1-포워드-프록시-forward-proxy">1. 포워드 프록시 (Forward Proxy)</h3>
<p>일반적으로 우리가 &quot;프록시 서버 쓴다&quot;라고 할 때 가리키는 방식입니다. 클라이언트와 인터넷 사이에 위치하여, 클라이언트의 요청을 가로채서 서버에 전달합니다.</p>
<ul>
<li><strong>특징:</strong> 서버는 요청을 준 프록시 서버의 IP만 알 수 있고, 진짜 요청을 보낸 클라이언트가 누구인지는 모릅니다.</li>
<li><strong>주요 용도:</strong> 사내 보안망 구축(특정 사이트 차단), 우회 접속, 클라이언트 익명성 보장.</li>
</ul>
<h3 id="2-리버스-프록시-reverse-proxy">2. 리버스 프록시 (Reverse Proxy)</h3>
<p>인터넷과 서버(백엔드) 사이에서 서버의 앞단에 위치합니다. 클라이언트가 웹사이트에 접속할 때 실제 서버에 직접 요청하는 것이 아니라, 리버스 프록시 서버에 요청을 보냅니다.</p>
<ul>
<li><strong>특징:</strong> 클라이언트는 실제 백엔드 서버의 구조나 IP를 알 수 없습니다.</li>
<li><strong>주요 용도:</strong> - <strong>로드 밸런싱:</strong> 수많은 요청을 여러 대의 서버로 나누어 분산 처리합니다.<ul>
<li><strong>보안 및 암호화:</strong> SSL(HTTPS) 인증서 처리를 프록시가 전담하여 내부 서버의 부담을 줄이고 보안을 강화합니다.</li>
<li><strong>CORS 에러 해결:</strong> 프론트엔드 개발 시 로컬 개발 서버를 프록시로 설정하여 브라우저의 CORS 제한을 우회할 때 요긴하게 쓰입니다.</li>
</ul>
</li>
</ul>
<h2 id="프록시-서버는-언제-사용하지">프록시 서버는 언제 사용하지?</h2>
<ul>
<li>익명성 보장: IP 주소를 숨기고 싶을 때.</li>
<li>보안 강화: 회사나 기관에서 내부 서버를 외부에 노출하지 않도록 할 때.</li>
<li>캐싱: 자주 사용하는 콘텐츠를 빠르게 제공하기 위해 데이터를 저장할 때.</li>
<li>접근 제한: 특정 웹사이트에 대한 접근을 차단하거나 제한할 때.</li>
<li>우회: 인터넷 검열을 회피하고 차단된 웹사이트에 접근할 때.</li>
</ul>
<h2 id="프록시-사용방법">프록시 사용방법</h2>
<p>프록시 서버를 사용하는 방법은 여러 환경에서 적용될 수 있으며, 클라이언트 애플리케이션이 프록시 서버를 통해 인터넷과 통신하도록 설정하는 것이 핵심이다.</p>
<h3 id="1-웹-브라우저에서-프록시-설정">1. 웹 브라우저에서 프록시 설정</h3>
<p>웹 브라우저에서 프록시 서버를 설정하면 사용자가 웹사이트에 접속할 때마다 브라우저가 해당 프록시를 경유하여 요청을 보낸다. 이 방법은 보통 회사 네트워크나 보안 정책을 위해 사용된다.</p>
<h3 id="2-axios와-같은-http-라이브러리에서-프록시-설정">2. axios와 같은 HTTP 라이브러리에서 프록시 설정</h3>
<p>axios 같은 라이브러리에서 API 호출을 할 때 프록시를 설정할 수 있다.
proxy 옵션을 추가하면 된다.</p>
<pre><code class="language-javascript">import axios from &#39;axios&#39;;

const instance = axios.create({
  baseURL: &#39;https://example.com/api&#39;,
  proxy: {
    host: &#39;your-proxy-server.com&#39;, // 프록시 서버 주소
    port: 8080,                     // 프록시 서버 포트
    protocol: &#39;http&#39;                // 프록시 서버가 HTTP 또는 HTTPS인지 설정
  }
});

instance.get(&#39;/users&#39;)
  .then(response =&gt; console.log(response.data))
  .catch(error =&gt; console.error(error));
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 개념과 작동 원리]]></title>
            <link>https://velog.io/@han_oal/HTTP-%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@han_oal/HTTP-%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Tue, 22 Oct 2024 13:37:31 GMT</pubDate>
            <description><![CDATA[<h2 id="http란">HTTP란?</h2>
<p><strong>HTTP(Hypertext Transfer Protocol)</strong> 는 웹 브라우저(클라이언트)가 서버와 통신할 때 지켜야 하는 일종의 &#39;규칙(프로토콜)&#39;입니다. </p>
<p>개인적으로 이렇게 원론적인 들으면 와닫지가 않더라고요.
아래에 일상생활과 HTTP에 연관성을 간략하게 정리해보았습니다.</p>
<hr>
<h2 id="일상-생활과-http의-연관성">일상 생활과 HTTP의 연관성</h2>
<blockquote>
<p>&quot;인터넷창(웹 브라우저)을 켜고 하는 모든 행위&quot;와 &quot;스마트폰 앱이 데이터를 주고받는 모든 과정&quot;에 HTTP가 사용됩니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/han_oal/post/e726ef3f-710f-41ed-a25d-cd5eb4406a30/image.jpg" alt=""></p>
<h3 id="1-우리가-매일-쓰는-웹-브라우저스마트폰pc">1. 우리가 매일 쓰는 웹 브라우저(스마트폰&amp;PC)</h3>
<p>네이버, 구글, 유튜브 등 우리가 크롬, 사파리, 웨일 같은 브라우저 주소창에 URL을 입력하고 검색 버튼을 누르는 순간 HTTP 통신이 시작됩니다.</p>
<blockquote>
<p><strong>웹페이지 조회:</strong> 브라우저가 서버에 &quot;네이버 메인 화면(HTML, CSS, JS) 좀 줘&quot; 하고 HTTP GET 요청을 보냅니다.</p>
</blockquote>
<blockquote>
<p><strong>로그인 및 회원가입:</strong> 아이디와 비밀번호를 입력하고 버튼을 누르면, 서버로 계정 정보를 안전하게 보내기 위해 HTTP POST 요청을 보냅니다.</p>
</blockquote>
<h3 id="2-스마트폰-앱">2. 스마트폰 앱</h3>
<p>인터넷창을 켜지 않고 배달의민족, 인스타그램, 카카오톡 같은 &#39;앱&#39;을 쓸 때도 내부적으로는 전부 HTTP를 씁니다.</p>
<blockquote>
<p><strong>배달 앱:</strong> &quot;내 주변 치킨집 목록 보여줘&quot; 하고 서버에 요청하면, 서버가 치킨집 이름, 리뷰, 이미지 주소 등이 담긴 JSON 데이터를 HTTP 응답으로 보내줍니다. 앱은 그 데이터를 받아서 우리 화면에 예쁘게 그려주는 역할만 합니다.</p>
</blockquote>
<blockquote>
<p><strong>SNS 앱:</strong> 인스타그램에서 피드를 아래로 당겨서 새로고침할 때, 새로운 사진 데이터를 HTTP 통신으로 긁어옵니다.</p>
</blockquote>
<hr>
<h2 id="http의-작동-원리">HTTP의 작동 원리</h2>
<p><img src="https://velog.velcdn.com/images/han_oal/post/8811de9a-6473-4535-9489-256d17664b15/image.png" alt="">
HTTP는 <strong>요청(Request)</strong>과 <strong>응답(Response)</strong>이라는 단순한 구조로 작동합니다. 클라이언트가 먼저 행동(요청)을 취해야만 서버가 그에 따른 결과(응답)를 돌려주는 방식입니다.</p>
<h3 id="요청-request">요청 (Request)</h3>
<p>클라이언트가 서버에게 &quot;이 작업을 처리해 줘&quot;라고 요구할 때 사용됩니다. 요청 메시지는 크게 다음과 같은 요소로 구성됩니다.</p>
<ul>
<li><strong>URL:</strong> 클라이언트가 접근하고자 하는 자원(데이터)의 위치를 나타냅니다.</li>
<li><strong>메서드(Method):</strong> 요청의 종류와 목적을 나타냅니다. (예: 데이터를 가져올지, 새로 저장할지 등)</li>
<li><strong>헤더(Header):</strong> 요청에 대한 추가 정보를 담고 있습니다. 예를 들어, 브라우저 정보, 인증 토큰, 콘텐츠 타입 등이 포함됩니다.</li>
<li><strong>본문(Body):</strong> 필요할 경우 클라이언트가 서버로 실제 데이터를 실어 보냅니다. 주로 값을 새로 저장하거나 수정할 때 사용됩니다.</li>
</ul>
<h3 id="응답-response">응답 (Response)</h3>
<p>서버가 클라이언트의 요청을 처리한 후 결과를 돌려줄 때 사용됩니다. 응답 메시지는 다음과 같이 구성됩니다.</p>
<ul>
<li><strong>상태 코드(Status Code):</strong> 요청이 성공했는지 실패했는지를 알려주는 3자리 숫자입니다. <ul>
<li><code>200</code> (성공)</li>
<li><code>404</code> (찾을 수 없음 - 잘못된 주소)</li>
<li><code>500</code> (서버 내부 오류)</li>
</ul>
</li>
<li><strong>헤더(Header):</strong> 응답 데이터에 대한 추가 정보를 담고 있습니다. (예: 서버가 돌려주는 데이터의 종류, 데이터 길이 등)</li>
<li><strong>본문(Body):</strong> 서버가 클라이언트에게 보내는 실제 데이터입니다. 브라우저가 화면에 그려낼 HTML 코드나, 화면을 업데이트할 때 쓰는 JSON 데이터 등이 여기에 담깁니다.</li>
</ul>
<hr>
<h2 id="http-메서드-method">HTTP 메서드 (Method)</h2>
<p><img src="https://velog.velcdn.com/images/han_oal/post/5986828d-98c5-4da1-bacc-44d1779120fb/image.png" alt=""></p>
<p>HTTP는 다양한 메서드를 지원합니다. 가장 많이 쓰이는 핵심 메서드 5가지를 CRUD 개념으로 분류해 정리해보았습니다.</p>
<h3 id="create쓰기">Create(쓰기)</h3>
<blockquote>
<ul>
<li><strong>POST : 데이터 생성</strong></li>
</ul>
</blockquote>
<ul>
<li>서버에 새로운 리소스를 <strong>등록하거나 제출할 때</strong> 사용합니다.</li>
<li>회원가입, 글쓰기 등이 대표적이며, 저장할 데이터를 본문(Body)에 숨겨서 안전하게 전송합니다.</li>
</ul>
<h3 id="read읽기">Read(읽기)</h3>
<blockquote>
<ul>
<li><strong>GET : 데이터 조회</strong></li>
</ul>
</blockquote>
<ul>
<li>서버로부터 특정 리소스(웹페이지, 이미지, 데이터 등)를 <strong>가져올 때</strong> 사용합니다. </li>
<li>본문(Body)을 비워두는 것이 일반적이며, 필요한 데이터는 URL 뒤에 붙여서 보냅니다.</li>
</ul>
<h3 id="update덮어쓰기">Update(덮어쓰기)</h3>
<blockquote>
<ul>
<li><strong>PUT : 데이터 전체 수정</strong></li>
</ul>
</blockquote>
<ul>
<li>서버의 기존 리소스를 새로운 데이터로 <strong>통째로 교체(수정)</strong>할 때 사용합니다.</li>
<li>만약 요청한 위치에 데이터가 없다면 새로운 리소스를 생성하기도 합니다.<ul>
<li><strong>PATCH : 데이터 일부 수정</strong></li>
</ul>
</li>
<li>리소스의 전체가 아니라 <strong>특정 일부분만 수정</strong>할 때 사용합니다.</li>
<li>회원의 비밀번호만 변경하거나, 글의 제목만 바꿀 때 PUT 대신 PATCH를 쓰는 것이 권장됩니다.</li>
</ul>
<h3 id="delete삭제">Delete(삭제)</h3>
<blockquote>
<ul>
<li><strong>DELETE : 데이터 삭제</strong></li>
</ul>
</blockquote>
<ul>
<li>서버에 존재하는 특정 리소스를 <strong>삭제할 때</strong> 사용합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[6. 리액트 개발 도구로 디버깅하기]]></title>
            <link>https://velog.io/@han_oal/6.-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EA%B0%9C%EB%B0%9C-%EB%8F%84%EA%B5%AC%EB%A1%9C-%EB%94%94%EB%B2%84%EA%B9%85%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@han_oal/6.-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EA%B0%9C%EB%B0%9C-%EB%8F%84%EA%B5%AC%EB%A1%9C-%EB%94%94%EB%B2%84%EA%B9%85%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 02 Sep 2024 08:03:08 GMT</pubDate>
            <description><![CDATA[<p>ㅡ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 정리01.]]></title>
            <link>https://velog.io/@han_oal/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%A0%95%EB%A6%AC01</link>
            <guid>https://velog.io/@han_oal/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%A0%95%EB%A6%AC01</guid>
            <pubDate>Thu, 29 Aug 2024 16:43:30 GMT</pubDate>
            <description><![CDATA[<p><code>npm init</code> 입력해서 node js 프로젝트 설치 후
<code>npm install --save-dev typescript</code> </p>
<p>잘 설치되었다면
<code>pakage.json</code> 파일의 <code>devDependencies</code> 에
typescript 추가됨.</p>
<h3 id="타입스크립트-컴파일러-만들기">타입스크립트 컴파일러 만들기</h3>
<p><code>npx tsc --init</code>
tsconfig.json 생성됨</p>
<h3 id="스크립트로-tsc-빌드-문구-추가하기">스크립트로 tsc 빌드 문구 추가하기</h3>
<p><code>pakage.json</code> &gt; <code>scripts</code> 중괄호 안에</p>
<pre><code>&quot;build&quot;: &quot;tsc&quot;</code></pre><p>추가하면 됨.</p>
<h3 id="타입스크립트-파일-생성하기">타입스크립트 파일 생성하기</h3>
<p><code>.ts</code> 확장자로 파일 생성 후 
<code>npm run build</code> 로 컴파일 가능.</p>
<hr>
<h2 id="타입스크립트가-실행되는-과정">타입스크립트가 실행되는 과정</h2>
<ul>
<li>타입스크립트는 기존 자바스크립트에 문법을 추가해서 사용하는 언어
(다른말로 <code>슈퍼 셋</code>)</li>
</ul>
<ul>
<li>TSC타입 검사 : 트랜스파일 전 타입스크립트를 검사함. 이 단계에서 오류 사전 발견</li>
<li>TSC트랜스파일 : 타입스크립트 파일을 자바스크립트 파일로 변환</li>
<li>버전설정: tsconfig.json 파일에서 자바스크립트 버전을 설정(ES16 등)</li>
<li>실행</li>
</ul>
<hr>
<h2 id="타입을-정하는-방법">타입을 정하는 방법</h2>
<p>변수 이름 뒤에 <code>:</code> 을 사용 후 타입을 명시하면 됨.</p>
<pre><code>let size : number = 100;</code></pre><hr>
<h2 id="타입-오류-이해하기">타입 오류 이해하기</h2>
<p>![[Pasted image 20240827042431.png]]</p>
<p>위에서부터 밑에 까지 큰 범위에서의 오류 &gt; 작은 범위의 오류까지 친절히 알려줌.</p>
<p>에러가 있는 상태에서 <code>npm run build</code> 해서 타입 검사 하면
에러 줄 위치도 알 수 있고, <code>ctrl</code> + <code>클릭</code> 하면 해당 오류의 코드로 바로 이동</p>
<hr>
<h2 id="기본형-타입">기본형 타입</h2>
<p><code>string</code> : 문자형
<code>number</code> : 숫자형
<code>boolean</code> : 불린형
<code>undefined</code> : 언디파인드
<code>null</code> : null;</p>
<p><code>undefined</code>와 <code>null</code> 은 서로 다른 타입이다.</p>
<hr>
<h2 id="배열과-튜플">배열과 튜플</h2>
<p>타입으로 <code>특정타입의 배열</code> 을 만들고 싶으면 아래처럼</p>
<pre><code>const cart : string[] = [];</code></pre><p><code>2차원 배열</code>을 만드려면</p>
<pre><code>const carts : string[][] = [
[&#39;one&#39;, &#39;two&#39;],
[&#39;hi&#39;],
];</code></pre><p>배열은 개수가 정해져 있지 않아서 아래 코드는 오류 없이 작동함.</p>
<pre><code>let mySize : number[] = [167,28];
mySize = [167, 28, 255];
mySize = [255];
mySize = [];</code></pre><p>배열의 <code>개수</code>와 <code>타입</code>을 명확하게 정하고 싶으면
<code>튜플</code> 을 쓰면 됨.</p>
<pre><code>let mySize : [number, number, string] = [123, 23, &#39;M&#39;];</code></pre><hr>
<h2 id="객체-타입">객체 타입</h2>
<p>객체 타입 정의는 객체를 정의할 때랑 비슷하다. 다만 마지막에 쉼표 대신 세미콜론을 쓴다는 점이 다르다.</p>
<pre><code>let stock : {
 id: string;
 name: string;
 price: number;
 membersOnly: boolean;
 sizes: string[];
} = {
....
}</code></pre><p><code>객체 타입</code>을 정의하면
프로퍼티 이름을 외우고 있지 않아도 <code>vs-code</code>에서 프로퍼티 이름을 추천해줌.
인텔리전스 서제스트 단축키 : <code>ctrl + i</code></p>
<p>선택적인 프로퍼티를 <code>옵셔널 프로퍼티</code> 라고 부름.
<code>옵셔널 프로퍼티</code> 는 값을 할당하지 않아도 문제가 없음.</p>
<pre><code>let stock : {
 id: string;
 name: string;
 price: number;
 membersOnly?: boolean; // 이렇게 프로퍼티 뒷쪽에 ? 기호를 넣으면 됨.
 sizes: string[];
} = {
....
}</code></pre><p>프로퍼티의 개수를 알 수 없거나 ,정해놓고 싶지 않은 경우에
프로터티 값에 타입만 지정할 수 있다.</p>
<pre><code>let stock : {
[id: string] : number;
} = {
c001: 3;
c002: 2;
c003: 2;
c004: 2;
.
}</code></pre><hr>
<h2 id="any">any</h2>
<p>any타입은 모든 타입을 허용할 수 있는 타입.
즉, 타입스크립트 타입 검사를 무력화 시킴.</p>
<pre><code>const product = {

  id: &quot;c001&quot;,

  name: &quot;코드잇 블랙 후디&quot;,

  price: 129000,

  sizes: [&quot;M&quot;, &quot;L&quot;, &quot;XL&quot;],

};

console.log(product.reviews[2]); // 타입 명시가 없어서 에러.</code></pre><pre><code>const product : any = {

  id: &quot;c001&quot;,

  name: &quot;코드잇 블랙 후디&quot;,

  price: 129000,

  sizes: [&quot;M&quot;, &quot;L&quot;, &quot;XL&quot;],

};

console.log(product.reviews[2]); // any 사용으로 타입 사용 자유로워짐</code></pre><p>any 는 어쩔 수 없는 경우에만 사용해야함.
타입 검사를 빡세게 해서 오류를 줄이려는건데, any를 사용하면 무용지물.</p>
<p>JSON.parse 같은 경우에는
값의 타입을 미리 알 수 없는 경우가 있음.</p>
<p>&#39;as&#39; 키워드란 컴파일 단계에서 타입 검사를 할 때 
타입스크립트가 감지하지 못하는 애매한 타입 요소들을 직접 명시해주는 키워드.</p>
<p>한 가지 특징은 <code>as</code> 키워드(type assertion)는 오직 컴파일 단계에서만 실행되며 런타임 단계에서는 삭제된 채로 실행된다. 컴파일 단계에서 제대로 넘어갔다면 런타임 단계에서의 에러 발생은 일어나지 않는다는 말.</p>
<hr>
<h2 id="함수에-타입-정의하기">함수에 타입 정의하기</h2>
<p>함수 선언에서 타입을 정하기</p>
<pre><code>function addToCart(id: string, quantity: number = 1) : string {
...
}</code></pre><p>함수 자체에 타입을 정하기</p>
<pre><code>const codeitmall: {
  addToCart: (id: string, quantity?: number) =&gt; boolean;
} = {
  addToCart,
}</code></pre><p>리턴값이 없을 때는 <code>void</code> 타입을 명시해주면 됌.</p>
<pre><code>const codeitmall: {
  addToCart: (id: string, quantity?: number) =&gt; void;
} = {
  addToCart,
}</code></pre><hr>
<h2 id="enum">Enum</h2>
<p>열거형 타입을 선언할 때 <code>Enum</code>을 사용함.
열거형 타입이란 특정 상수의 집합.</p>
<p>값의 종류를 나열할 땐 Enum을 사용함.</p>
<pre><code>enum Size {
S,
M,
L,
XL,
}</code></pre><p><code>Enum</code>을 타입으로 쓸 때는 <code>Enum</code> 이름만 적으면 되고
값으로 쓸 때는 객체처럼 <code>.</code> 표기법으로 사용하면 된다.</p>
<pre><code>// enum을 타입으로 사용하기

let product : Size = Size.S; </code></pre><p><code>Enum</code> 에 값을 선언하지 않으면
순서대로 숫자형 값이 들어감.</p>
<p>이때 기본값은 0부터 시작하는 정수라는 점에 주의</p>
<pre><code>enum Size { S, M, L, XL, } 
console.log(Size.S); // 0 
console.log(Size.M); // 1 
console.log(Size.L); // 2</code></pre><p>숫자 0은 실수하기 쉽기 때문에 
Enum을 사용할 땐 되도록이면 값을 정해놓고 쓰는 게 좋음
밑에 코드처럼 임의로 값을 지정할 수 있음.</p>
<pre><code>enum Job {
    Knight = `Knight`,
    Archer = `Archer`,
    Mage = `Mage`,
    Priest = `Priest`,
    Thief = `Thief`,
}</code></pre><hr>
<h2 id="인터페이스">인터페이스</h2>
<p><code>인터페이스</code> 는 <code>객체의 구조</code>를 정의하는데 사용되는 특수한 타입.</p>
<ul>
<li>인터페이스 이름을 정할 때 첫번째 글자는 대문자로 한다. </li>
<li>interface를 사용하는 방법은 <code>interface</code>를 쓴 다음, 객체 타입처럼 만들면 됩니다.
인터페이스 선언방법<pre><code>interface Product {
  id: string;
  name: string;
  price: number;
  membersOnly?: boolean; // 옵셔널 프로퍼티
}</code></pre></li>
</ul>
<p>인터페이스 상속 방법</p>
<pre><code>interface Name {
id: string;
name: string;
price: string;
}

interface Name2 : extends Name { //Name을 상속받음

}</code></pre><p>인터페이스로 함수 정의</p>
<pre><code>interface Fuc {

  (print: Print) : void;

}</code></pre><p>함수에 직접적으로 인터페이스 연결하기</p>
<pre><code>const printProduct :PrintProductFunction = (product) =&gt;{

  console.log(`${product.name}의 가격은 ${product.price}원입니다.`);

}</code></pre><hr>
<h2 id="리터럴-타입">리터럴 타입</h2>
<p><code>리터럴 타입</code>은 값을 타입으로 하는 타입.</p>
<pre><code>let productName1 = &#39;코드잇 블랙 후드&#39;; // 타입 = string
const productName2 = &#39;코드잇 텀블러&#39;; // 타입 = 리터럴 타입</code></pre><p>타입 별칭과 함께 주로 쓰임.</p>
<hr>
<h2 id="타입-별칭-타입-엘리어스">타입 별칭 (타입 엘리어스)</h2>
<p>타입 별칭은 타입에 이름을 지정해줄 수 있는 시스템.</p>
<pre><code>type Cart = string</code></pre><p>타입 별칭은 모든 타입에 사용할 수 있음.</p>
<p>함수에 사용</p>
<pre><code>type CartResultCallback = (result: boolean) =&gt; void; 

const fuc : CartResultCallback = (result) {

  console.log(result)

}</code></pre><p>객체에 사용</p>
<pre><code>type Product {
id: string;
name: string;
}</code></pre><p>사실상 객체의 타입은 <code>타입 별칭</code> 과 <code>인터페이스</code> 사용이 가능함.
다만 객체는 <code>인터페이스</code> 를 추천한다고 함.</p>
<hr>
<h2 id="union-타입">Union 타입</h2>
<p><code>유니언 타입</code>은 변수나 함수의 매개변수가 여러 개의 타입 중 하나일 수 있음을 나타내는 방법.
이는 다양한 타입의 값들을 하나의 변수에 허용할 수 있게 해주는 기능.
유니언 타입은 파이프(<code>|</code>) 문자를 사용하여 표현됨.</p>
<pre><code>let value: string | number; 
value = &quot;Hello&quot;; // string 타입의 값 할당 가능 
value = 42; // number 타입의 값 할당 가능</code></pre><p>함수에 <code>유니언 타입</code> 을 이용하면 매개변수를 특정 값들 중 하나일 수 있음을 나타낼 수 있음.</p>
<p><code>if</code> 조건문을 이용하면 특정 타입에만 존재하는 프로퍼티를 활용할 수 있다.</p>
<pre><code class="language-javascript">interface Product {

  id: string;

  name: string;

  price: number;

  membersOnly?: boolean;

}



interface ClothingProduct extends Product {

  sizes: ClothingSize[];

  color: string;

}



interface ShoeProduct extends Product {

  sizes: number[];

  handmade: boolean;

}

function printSizes(product: ClothingProduct | ShoeProduct) {

  const availableSizes = product.sizes.join(&quot;, &quot;);

  console.log(`구매 가능한 사이즈는 다음과 같습니다: ${availableSizes}`);



  if (&quot;color&quot; in product) {

    console.log(`색상: ${product.color}`);

  }



  if (`handmade` in product) {

    console.log(

      product.handmade

        ? `이 상품은 장인이 직접 만듭니다.`

        : `이 상품은 공장에서 만들어졌습니다.`

    );

  }</code></pre>
<p><code>유니언 타입</code> 은 모든 값에 사용할 수 있다.
특히 <code>리터럴 값</code> 이랑 같이 사용하면 유용하다.</p>
<pre><code class="language-javascript">type shoesSize {
size : 100 | 200 | 300 | 400 | 500;
} 

const nikeShoes : shoesSize {
    size: 100,
}</code></pre>
<hr>
<h2 id="intersection-타입">Intersection 타입</h2>
<p><code>인터섹션 타입</code> 은 타입을 합치는 방법.
<code>&amp;(앤드퍼센트)</code> 을 사용해서 구현 가능하다.</p>
<pre><code class="language-javascript">interface Id {

  id: string;

}



interface Timestamp {

  createdAt: Date;

  updatedAt: Date;

}



type Product = Id &amp; {

  name: string;

  price: number;

  membersOnly?: boolean;

};



type User = Id &amp;

  Timestamp &amp; {

    username: string;

    email: string;

  };



  type Review = Id &amp; Timestamp &amp; {

  productId: string;

  userId: string;

  content: string;

}</code></pre>
<p>혹은 이렇게도 가능하다.</p>
<pre><code class="language-javascript">interface Person { 
    name: string; 
    age: number; 
} 

interface Employee { 
employeeId: number; 
position: string; 
} 

type PersonEmployee = Person &amp; Employee;</code></pre>
<p>오류를 일으킬 수 있는 코드의 예시이다.</p>
<pre><code class="language-javascript">interface Id {

  id: string;

}



interface Timestamp {

  createdAt: Date;

  updatedAt: Date;

}



type Product = Id &amp; Timestamp &amp; {

  name: string;

  price: number;

  membersOnly?: boolean;

  createdAt: string; // Timestamp 에 있는 중복된 프로퍼티이다.

  updatedAt: string; // Timestamp 에 있는 중복된 프로퍼티이다.

};</code></pre>
<p>이런 경우에는 
<code>Product</code> 타입 의 기존 프로퍼티가 <code>Timestamp</code> 의 프로퍼티를 덮어 쓰게 된다.
타입스크립트에서의 오류는 발생하지 않지만
자바스크립트로 트랜스파일 되고나서 작동할 때 오류가 발생할 수 있다.
가급적이면 명확한 타입 설계가 권장된다.</p>
<hr>
<h2 id="인터섹션-하면-합쳐지는-이유">인터섹션 하면 합쳐지는 이유</h2>
<p>수학에서 집합에 대해 배웠다면 생각해볼 게 있다.
두 타입을 Union 하면 왠지 두 타입의 형태를 합쳐야 할 거 같고, 
Intersection을 하면 두 타입의 공통된 부분만 타입으로 될 것 같다.
하지만 타입스크립트에서는 그렇게 작동하지 않는다.</p>
<h3 id="structural-subtyping-스트럭트럴-서브타이핑">Structural Subtyping (스트럭트럴 서브타이핑)</h3>
<p>타입스크립트에서 타입은 Structural Subtyping이라는 규칙을 따른다. 
쉽게 말해서 구조가 같으면 같은 타입이라고 판단하는 것이다. 예를 들어서 <code>a</code>라는 프로퍼티를 갖는 타입 <code>A</code>가 있다고 해보죠. 이 타입의 <code>a</code> 프로퍼티를 출력하는 <code>printA()</code>라는 함수가 있습니다. 이 함수를 아래 코드와 같이 <code>{ a: &#39;codeit&#39; }</code>, <code>{ a: &#39;codeit&#39;, b: 42 }</code>, <code>{ a: &#39;codeit&#39;, b: 42, c: true }</code>라는 객체로 실행해도 모두 올바른 타입입니다. 세 객체 모두에 <code>a</code>라는 프로퍼티가 있기 때문에 타입 <code>A</code>라고 판단하는 거죠. 이런 걸 Structural Subtyping, Structural Type System이라고 부릅니다.</p>
<pre><code class="language-javascript">interface A {
  a: string;
}

interface B {
  b: number;
}

function printA(arg: A) {
  console.log(arg.a);
}

const x = { a: &#39;codeit&#39; };
const y = { b: 42 };
const z = { a: &#39;codeit&#39;, b: 42 };
const w = { a: &#39;codeit&#39;, b: 42, c: true };

printA(x);
printA(y); // 잘못된 타입
printA(z);
printA(w);</code></pre>
<p>용어가 조금 어려워 보인다. 
쉽게 생각해서 &quot;같은 모양이 있으면 같은 타입이라고 판단한다&quot;라고 이해할 수 있다. 
앞으로의 내용을 설명하기 위해서 간단하게 아래와 같은 그림으로 나타내보겠다.
원 안에 있는 것들은 타입 A에 해당하는 객체들이다.</p>
<p>![[Pasted image 20240828062950.png]]</p>
<p>마찬가지로 타입 B까지 그림으로 표현하면 아래와 같이 표현할 수 있을 것이다.</p>
<p>![[Pasted image 20240828063012.png]]</p>
<h3 id="union-타입-살펴보기">Union 타입 살펴보기</h3>
<p>앞에서 Union 타입은 &quot;A 타입이거나 B 타입이다&quot;라고 표현했다. 이걸 보다 정확하게 그림으로 이해해 보면 아래와 같다. 그래서 <code>{ a: &#39;codeit&#39; }</code>과 <code>{ b: 42 }</code>는 물론이고 <code>{ a: &#39;codeit&#39;, b: 42 }</code>도 타입 <code>A | B</code>라고 사용할 수 있다.</p>
<p>![[Pasted image 20240828063520.png]]</p>
<pre><code class="language-javascript">interface A {
  a: string;
}

interface B {
  b: number;
}

function printAUnionB(arg: A | B) {
  // 여기서는 타입 A | B

    if (&#39;a&#39; in arg) {
    // 여기 안에서는 타입 A
    console.log(arg.a);
  }

    if (&#39;b&#39; in arg) {
    // 여기 안에서는 타입 B
    console.log(arg.b); // VS Code에서 arg에 마우스를 호버해 보세요.
  }
}

const x = { a: &#39;codeit&#39; };
const y = { b: 42 };
const z = { a: &#39;codeit&#39;, b: 42 };
const w = { a: &#39;codeit&#39;, b: 42, c: true };

printAUnionB(x);
printAUnionB(y);
printAUnionB(z);
printAUnionB(w);
</code></pre>
<h3 id="intersection-타입-살펴보기">Intersection 타입 살펴보기</h3>
<p>마찬가지로 Interesection을 살펴보자. 앞에서 <code>A &amp; B</code>라고 하면 &quot;A와 B 두 타입을 합친다&quot;라고 간단히 표현했다. 이걸 보다 정확하게 그림으로 이해해 보면 아래와 같다. <code>{ a, b }</code> 두 프로퍼티를 모두 가지고 있는 타입들이 <code>A &amp; B</code>라고 할 수 있을것이다. 그래서 객체의 모양만 본다면 마치 Intersection이 두 타입의 모양을 합치는 것처럼 생각할 수 있다.</p>
<p>![[Pasted image 20240828063739.png]]</p>
<pre><code class="language-javascript">interface A {
  a: string;
}

interface B {
  b: number;
}

function printAIntersectionB(arg: A &amp; B) {
  console.log(arg.a);
  console.log(arg.b);
}

const x = { a: &#39;codeit&#39; };
const y = { b: 42 };
const z = { a: &#39;codeit&#39;, b: 42 };
const w = { a: &#39;codeit&#39;, b: 42, c: true };

printAIntersectionB(x); // 타입 오류
printAIntersectionB(y); // 타입 오류
printAIntersectionB(z);
printAIntersectionB(w);
</code></pre>
<h3 id="정리">정리</h3>
<p>이번 레슨에서는 Structural Subtyping이라는 개념을 통해서, Union과 Intersection에 대해 그림으로 더 자세하게 알아보았다. 간단하게는 <code>A | B</code>라고 하면 &quot;A 타입이거나 B 타입이다&quot;, <code>A &amp; B</code>라고 하면 &quot;A 타입이랑 B 타입을 합친 것이다&quot;라고 생각하면 대부분의 경우 문제없이 사용할 수 있겠지만. 혹시 앞으로 타입스크립트를 사용하면서 <code>interface</code>의 상속을 활용하거나, 여러 타입을 조합해서 복잡한 타입을 다루게 될 때 오늘 배운 이 내용을 토대로 한번 생각해 보시면 혼란을 줄이는데 도움이 될 것이다.</p>
<hr>
<h2 id="keyof-와-typeof-연산자">keyOf 와 typeof 연산자</h2>
<h3 id="keyof">keyof</h3>
<p>객체 타입의 모든 키를 문자열 리터럴 유니언 타입으로 반환하는 데 사용됨.</p>
<pre><code class="language-javascript">interface User {
  id: number;
  name: string;
  email: string;
}

type UserKeys = keyof User; // &#39;id&#39; | &#39;name&#39; | &#39;email&#39;</code></pre>
<h3 id="typeof">typeof</h3>
<p>타입스크립트에서 <code>typeof</code>는 주로 두 가지 용도로 사용됨. 
런타임에 값의 타입을 확인하는 것과, 컴파일 타임에 타입을 추론하는 것.</p>
<h4 id="런타임에서의-typeof">런타임에서의 <code>typeof</code></h4>
<p>TypeScript의 <code>typeof</code>는 JavaScript와 마찬가지로 런타임에 변수나 값의 타입을 확인함.
이는 주로 조건문에서 사용됨.</p>
<pre><code class="language-javascript">let x = &quot;Hello, World!&quot;;
if (typeof x === &quot;string&quot;) {
  console.log(&quot;x는 문자열입니다.&quot;);
}</code></pre>
<h4 id="컴파일-타임에서의-typeof">컴파일 타임에서의 <code>typeof</code></h4>
<p>TypeScript에서는 <code>typeof</code>를 사용하여 변수, 객체, 또는 함수의 타입을 가져올 수 있다.
이는 특정 값이나 변수의 타입을 참조하여 동일한 타입을 다른 곳에서 재사용할 때 유용함.</p>
<pre><code class="language-javascript">let someValue = &quot;Hello, TypeScript&quot;;
let valueType: typeof someValue; // string 타입으로 추론됨

valueType = &quot;Another string&quot;; // OK
valueType = 42; // Error: Type &#39;number&#39; is not assignable to type &#39;string&#39;</code></pre>
<hr>
<h2 id="타입-별칭은-언제-쓰면-좋을까">타입 별칭은 언제 쓰면 좋을까?</h2>
<h3 id="enum과-타입-별칭">Enum과 타입 별칭</h3>
<p>아래 두 코드는 거의 같은 역할을 하는 코드이다.</p>
<h4 id="enum-을-사용한-경우">Enum 을 사용한 경우</h4>
<pre><code class="language-javascript">enum UserType {
Admin = `admin`,
User = `user`,
Guest = `guest`,
}

const role = UserType.Admin;
console.log(role === UserType.Guest);</code></pre>
<h4 id="타입-별칭과-union을-사용한-경우권장">타입 별칭과 Union을 사용한 경우(권장)</h4>
<pre><code class="language-javascript">type UserType = `admin` | `user` | `guest`

const role : UserType = &#39;admin&#39;;
console.log(role === &#39;guest&#39;);</code></pre>
<p>코드 양으로만 보면 타입 별칭을 쓰는 게 훨씬 간단해 보인다.
두 코드 사이에는 어떤 차이가 있을까?</p>
<h4 id="javascript로-트랜스파일링-했을-때">JavaScript로 트랜스파일링 했을 때</h4>
<p>우선 Enum 코드를 자바스크립트로 트랜스파일링해 보면 다음과 같다.</p>
<pre><code class="language-javascript">&quot;use strict&quot;;
var UserType;
(function (UserType) {
    UserType[&quot;Admin&quot;] = &quot;admin&quot;;
    UserType[&quot;User&quot;] = &quot;user&quot;;
    UserType[&quot;Guest&quot;] = &quot;guest&quot;;
})(UserType || (UserType = {}));
const role = UserType.Admin;
console.log(role === UserType.Guest);</code></pre>
<p>Enum은 별도의 자바스크립트 객체를 만들어서 그 객체를 사용한다. 
<code>UserType</code>은 자바스크립트에서 아래와 같은 객체인 것이다.</p>
<pre><code class="language-javascript">{ Admin: &#39;admin&#39;, User: &#39;user&#39;, Guest: &#39;guest&#39; }</code></pre>
<p>예를 들어서 가능한 <code>UserType</code> 값들을 모두 활용해서 어떤 동작을 구현하고 싶다면 Enum을 써서 <code>Object.keys()</code>라는 함수를 사용해 볼 수 있을 것이다.</p>
<pre><code class="language-javascript">console.log(Object.keys(UserType)); // [&#39;Admin&#39;, &#39;User&#39;, &#39;Guest&#39;]</code></pre>
<p>반면에 타입 별칭은 타입스크립트에서만 의미 있는 코드이다.
그래서 Enum과 달리 자바스크립트로 트랜스파일 했을 때 추가로 객체 같은 걸 만들지 않고 단순히 값만 사용하는 코드가 만들어진다.</p>
<pre><code class="language-javascript">&quot;use strict&quot;;
const role = &#39;admin&#39;;
console.log(role === &#39;guest&#39;);</code></pre>
<h4 id="어떤-걸-써야-할까">어떤 걸 써야 할까?</h4>
<p>대부분의 경우 Enum 또는 타입 별칭을 모두 사용할 수 있다. 
하지만 되도록 Enum의 목적에 맞는 경우라면 Enum 문법을 사용하시는 걸 권장한다.</p>
<h3 id="interface와-타입-별칭">Interface와 타입 별칭</h3>
<p>아래 두 코드는 거의 같은 역할을 하는 코드이다.</p>
<h4 id="interface를-사용한-경우-권장">Interface를 사용한 경우 (권장)</h4>
<pre><code class="language-javascript">interface Entity {
  id: string;
  createdAt: Date;
  updatedAt: Date;
}

interface User extends Entity {
  username: string;
  email: string;
}
</code></pre>
<h4 id="타입-별칭을-사용한-경우">타입 별칭을 사용한 경우</h4>
<pre><code class="language-javascript">type Entity = {
  id: string;
  createdAt: Date;
  updatedAt: Date;
}

type User = Entity &amp; {
  username: string;
  email: string;
}
</code></pre>
<p>Interface의 상속과 Intersection의 가장 큰 차이점은 Intersection은 두 가지 이상의 타입을 한 번에 합칠 수 있다는 것이다. 이것도 Interface로 아주 불가능하지는 않다.</p>
<pre><code class="language-javascript">interface Entity {
  id: string;
}

interface TimestampEntity extends Entity {
  createdAt: Date;
  updatedAt: Date;
}

interface User extends TimestampEntity {
  username: string;
  email: string;
}</code></pre>
<pre><code class="language-javascript">type Id = {
  id: string;
}

type Entity = {
  createdAt: Date;
  updatedAt: Date;
}

type User = Id &amp; Entity &amp; {
  username: string;
  email: string;
}</code></pre>
<h4 id="어떤-걸-써야-할까-1">어떤 걸 써야 할까?</h4>
<p>대부분의 경우 Interface와 타입 별칭을 둘 다 사용할 수 있을 것이다. 하지만 되도록 Interface의 목적에 맞는 경우라면 Interface를 사용하는 걸 권장한다.</p>
<h4 id="타입-별칭은-언제-쓰면-좋을까-1">타입 별칭은 언제 쓰면 좋을까?</h4>
<p>타입 별칭은 타입에 &#39;이름&#39;을 정하는 문법이다. 복잡한 타입을 만들고, 그 타입을 여러 곳에서 활용할 때 사용하면 된다. 예를 들자면 아래처럼 복잡한 타입을 만들고 여러 곳에서 재활용할 수 있겠다.</p>
<pre><code class="language-javascript">type Point = [number, number];
type SearchQuery = string | string[];
type Result = SuccessResult | FailedResult;
type Coupon = 
  | PromotionCoupon
  | EmployeeCoupon
  | WelcomCoupon
  | RewardCoupon
  ;</code></pre>
<hr>
<h2 id="제네릭">제네릭</h2>
<p><code>타입스크립트</code>의 <code>제네릭(Generics)</code>은 
재사용이 가능한 컴포넌트를 만들 수 있게 해주는 강력한 기능이다.</p>
<p><code>제네릭</code> 을 사용하면 타입을 마치 함수의 매개변수처럼 다룰 수 있어, 코드의 타입을 미리 지정하지 않고도 다양한 타입에 대해 동작하도록 만들 수 있다.</p>
<h3 id="제네릭의-기본-개념">제네릭의 기본 개념</h3>
<p>제네릭을 사용하면 함수, 클래스, 인터페이스 또는 타입을 정의할 때
구체적인 타입을 미리 지정하지 않고, 그 타입을 매개변수화하여 나중에 사용자가 결정하게 할 수 있다.</p>
<pre><code class="language-javascript">function identity&lt;T&gt;(value: T): T {
  return value;
}

const num = identity&lt;number&gt;(42);  // T는 number로 추론됨
const str = identity&lt;string&gt;(&quot;hello&quot;);  // T는 string으로 추론됨
</code></pre>
<p>위 예제에서 <code>identity</code> 함수는 제네릭 함수로, <code>T</code>라는 타입 매개변수를 사용한다. 
함수 호출 시 <code>T</code>가 어떤 타입인지 지정할 수 있으며, 이로 인해 다양한 타입에 대해 같은 함수를 재사용할 수 있다.</p>
<h4 id="예시-제네릭-인터페이스">예시: 제네릭 인터페이스</h4>
<pre><code class="language-javascript">interface Box&lt;T&gt; {
  value: T;
}

const numberBox: Box&lt;number&gt; = { value: 123 };
const stringBox: Box&lt;string&gt; = { value: &quot;hello&quot; };</code></pre>
<p>여기서 <code>Box</code> 인터페이스는 <code>T</code>라는 제네릭 타입 매개변수를 가지고 있다. <code>numberBox</code>와 <code>stringBox</code>는 서로 다른 타입을 가지지만, 동일한 인터페이스 <code>Box</code>를 사용하여 정의된다.</p>
<h4 id="제네릭의-장점">제네릭의 장점</h4>
<ul>
<li><strong>타입 안전성</strong>: 제네릭을 사용하면 특정 타입이 강제되므로, 타입에 대한 오류를 컴파일 타임에 잡아낼 수 있다.</li>
<li><strong>코드 재사용성</strong>: 제네릭을 사용하면 같은 로직을 다양한 타입에 대해 적용할 수 있어 코드의 중복을 줄일 수 있다.</li>
<li><strong>타입 추론</strong>: TypeScript는 제네릭 타입을 자동으로 추론할 수 있어, 코드 작성 시 타입을 명시적으로 지정하지 않아도 된다.</li>
</ul>
<h4 id="예시-제네릭-배열">예시: 제네릭 배열</h4>
<pre><code class="language-javascript">function getFirstElement&lt;T&gt;(arr: T[]): T {
  return arr[0];
}

const firstNum = getFirstElement([1, 2, 3]);  // T는 number로 추론됨
const firstStr = getFirstElement([&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]);  // T는 string으로 추론됨
</code></pre>
<p>위 예제에서는 제네릭 함수 <code>getFirstElement</code>가 배열의 첫 번째 요소를 반환한다.
이 함수는 배열의 타입에 관계없이 동작하며, <code>T</code>를 통해 배열의 요소 타입을 추론한다.</p>
<h4 id="제네릭-제약-조건">제네릭 제약 조건</h4>
<p>때때로 제네릭 타입 매개변수가 특정 타입을 상속받도록 제한해야 할 때가 있다. 
이를 <strong>제네릭 제약 조건</strong>이라고 한다.</p>
<pre><code class="language-javascript">function getLength&lt;T extends { length: number }&gt;(item: T): number {
  return item.length;
}

getLength(&quot;hello&quot;);  // OK, string은 length 속성을 가짐
getLength([1, 2, 3]);  // OK, 배열도 length 속성을 가짐
// getLength(42);  // 오류, number는 length 속성을 가지지 않음</code></pre>
<p>위 코드에서 <code>T extends { length: number }</code>는 
<code>T</code>가 <code>length</code> 속성을 가져야 한다는 제약 조건을 나타낸다.</p>
<h4 id="결론">결론</h4>
<p>TypeScript의 제네릭은 코드를 유연하고 재사용 가능하게 만들어주는 강력한 도구이다. 
제네릭을 사용하면 다양한 타입에 대해 동작하는 재사용 가능한 컴포넌트(함수, 클래스, 인터페이스 등)를 작성할 수 있으며, 타입 안전성을 유지하면서도 중복을 줄이고 코드의 가독성을 높일 수 있다.</p>
<hr>
<h2 id="알아두면-유용한-제네릭-타입들">알아두면 유용한 제네릭 타입들</h2>
<p>이번 파트에서는 타입스크립트에서 기본적으로 제공하는 제네릭 타입 중에서 알아두면 유용한 것들을 소개한다.</p>
<h3 id="javascript-기능들">JavaScript 기능들</h3>
<h4 id="queryselector-함수"><code>querySelector()</code> 함수</h4>
<p>기본적으로 어떤 DOM 노드가 리턴될지 모르기 때문에 
<code>HTMLElement</code>라는 일반적인 타입으로 정의된다. 
하지만 타입을 확신할 수 있는 경우에는 아래 코드처럼 직접 제네릭 타입을 정의해 주면 된다.</p>
<pre><code class="language-javascript">const elem = document.querySelector&lt;HTMLInputElement&gt;(&#39;input.username&#39;);</code></pre>
<h4 id="map"><code>Map</code></h4>
<p>키와 밸류를 갖는 자료구조이다. 
타입 파라미터로 키와 밸류의 타입을 정의하고 사용할 수 있다.</p>
<pre><code class="language-javascript">const productMap = new Map&lt;string, Product&gt;();
productMap.set(product1.id, product1);
productMap.set(product2.id, product2);</code></pre>
<h4 id="set"><code>Set</code></h4>
<p>배열과 비슷하지만 중복된 요소를 추가할 수 없는, 수학에서 집합에 해당하는 자료구조이다. 
타입 파라미터로 요소의 타입을 정의하고 사용할 수 있다.
\</p>
<pre><code class="language-javascript">const productSet = new Set&lt;Product&gt;(); 
productSet.add(product1); 
productSet.add(product2);</code></pre>
<h3 id="유용한-타입들">유용한 타입들</h3>
<h4 id="키와-밸류-정하기-record">키와 밸류 정하기: <code>Record</code></h4>
<p>객체에 키와 밸류 타입을 정해놓고 싶을 때 사용한다. 
<code>Map</code>과 비슷하지만 차이점은 순수한 객체에 타입만 추가한다는 점이다.</p>
<pre><code class="language-javascript">const productMap: Record&lt;string, Product&gt; = {}
productMap[&#39;c001&#39;] = product1;
productMap[&#39;c002&#39;] = product2;</code></pre>
<h4 id="객체-프로퍼티-고르기-pick">객체 프로퍼티 고르기: <code>Pick</code></h4>
<pre><code class="language-javascript">interface Product {
  id: string;
  name: string;
  price: number;
  membersOnly?: boolean;
}

type ProductInfo = Pick&lt;Product, &#39;name&#39; | &#39;price&#39;&gt;;</code></pre>
<p><code>Pick</code>으로 만든 타입은 아래와 같다. 
<code>name</code> 프로퍼티와 <code>price</code> 프로퍼티만 골라서 타입을 만들었다.</p>
<pre><code class="language-javascript">type ProductInfo = {
    name: string;
    price: number;
}</code></pre>
<h4 id="객체의-프로퍼티-생략하기-omit">객체의 프로퍼티 생략하기: <code>Omit</code></h4>
<pre><code class="language-javascript">interface Product {
  id: string;
  name: string;
  price: number;
  membersOnly?: boolean;
}

type ProductInfo = Omit&lt;Product, &#39;id&#39; | &#39;membersOnly&#39;&gt;;</code></pre>
<p><code>Omit</code>으로 만든 타입은 아래와 같다. <code>id</code>와 <code>membersOnly</code>를 제외하고 타입을 만들었다.</p>
<pre><code class="language-javascript">type ProductInfo = {
    name: string;
    price: number;
}</code></pre>
<h4 id="union-제거하기-exclude">Union 제거하기: <code>Exclude</code></h4>
<p>Union을 사용해서 <code>PromotionCoupon</code> 또는 <code>EmployeeCoupon</code> 또는 <code>WelcomCoupon</code> 또는 <code>RewardCoupon</code>인 타입을 <code>Coupon</code>이라고 했다. 여기서 <code>EmployCoupon</code>에 해당하는 것만 제거를 하고 싶을 때 <code>Exclude</code>를 사용할 수 있다.</p>
<pre><code class="language-javascript">type Coupon = 
  | PromotionCoupon
  | EmployeeCoupon
  | WelcomCoupon
  | RewardCoupon
  ;

type InternalCoupon = EmployeeCoupon;
type PublicCoupon = Exclude&lt;Coupon, InternalCoupon&gt;;
// type PublicCoupon = PromotionCoupon | WelcomCoupon | RewardCoupon</code></pre>
<h4 id="함수-파라미터-타입-가져오기-parameters">함수 파라미터 타입 가져오기: <code>Parameters</code></h4>
<p>함수 파라미터들의 타입을 함수의 타입을 통해 정의할 수 있다. 
만약 함수의 타입이 아니라, 선언된 함수라면 <code>typeof</code> 연산자로 함수의 타입으로 만들어서 사용하면 된다.</p>
<pre><code class="language-javascript">function addToCart(id: string, quantity: number = 1): boolean {
  // ...
  return true;
}

type AddToCartParameters = Parameters&lt;typeof addToCart&gt;;
// type AddToCartParameters = [id: string, quantity: number | undefined]</code></pre>
<h4 id="함수-리턴-타입-가져오기-returntype">함수 리턴 타입 가져오기: <code>ReturnType</code></h4>
<p><code>Parameters</code>와 마찬가지로 함수의 리턴 타입을 가져온다.</p>
<pre><code class="language-javascript">function addToCart(id: string, quantity: number = 1): boolean {
  // ...
  return true;
}

type AddToCartResult = ReturnType&lt;typeof addToCart&gt;;
// type AddToCartResult = boolean</code></pre>
<hr>
<h2 id="tsconfigjson-모듈-사용하기"><code>tsconfig.json</code> 모듈 사용하기</h2>
<h3 id="tsconfigjson"><code>tsconfig.json</code></h3>
<p>TypeScript 프로젝트의 <strong>구성 설정 파일</strong>로, TypeScript 컴파일러(<code>tsc</code>)의 동작 방식을 정의하는 데 사용됨.</p>
<h3 id="꼭-알아야-할-컴파일러-옵션들">꼭 알아야 할 컴파일러 옵션들</h3>
<p><code>compilerOptions</code>에서 사용할 수 있는 옵션 중에서 중요한 것들을 살펴보겠다.</p>
<h4 id="target-어떤-ecmascript-버전으로-변환할지"><code>target</code>: 어떤 ECMAScript 버전으로 변환할지</h4>
<p>타입스크립트 코드는 빌드해서 자바스크립트로 변환한다.
이때 변환할 자바스크립트의 버전을 정할 수 있다.
만약에 프로젝트가 과거 자바스크립트 표준만 지원하는 브라우저까지 
지원해야 한다면, 여기서 오래된 버전을 설정할 수 있다.
2023년 현재를 기준으로 <code>tsconfig.json</code>을 생성했을 때는, 기본적으로는 ES7(ES2016)으로 변환하도록 되어있다.</p>
<p>코드를 실행할 브라우저나 Node.js 환경에 따라 선택하시면 된다 브라우저의 경우 ES5는 거의 모든 브라우저에서 지원하고, ES6는 인터넷 익스플로러를 제외하면 대부분의 브라우저에서 지원한다. Node.js의 경우 v14 이후 버전을 쓰신다면 ES5, ES6를 대부분 지원한다.</p>
<p>잘 모르겠다면 일단은 ES6로 설정해 두고 쓰시는 걸 추천한다. 실행 환경에 따른 문법 지원 여부가 궁금하신 분들은 아래 두 링크를 참고.</p>
<ul>
<li><a href="https://caniuse.com/?search=ECMAScript">&quot;ECMAScript&quot; | Can I use... Support tables for HTML5, CSS3, etc</a></li>
<li><a href="https://kangax.github.io/compat-table/es6/">ECMAScript 6 compatibility table (kangax.github.io)</a></li>
</ul>
<h4 id="module-어떤-방식으로-모듈을-만들지"><code>module</code>: 어떤 방식으로 모듈을 만들지</h4>
<p>자바스크립트 모듈에는 크게 두 가지 방식이 있다. ES6부터 도입된 import/export 문법을 사용하는 <code>ESM(ECMAScript Module)</code> 방식이 있고, Node.js 같은 데서 기본적으로 사용하는 <code>CJS(CommonJS)</code> 방식이 있다.</p>
<p>이 옵션에서는 자바스크립트 코드로 트랜스파일할 때 어떤 모듈 문법으로 변환할지 선택할 수 있다. 다양한 옵션 값을 지원한다. ESM을 쓰시려면 <code>es6</code>, <code>es2020</code> 같이 <code>es</code>로 시작하는 값을 쓰면 되고, CJS를 쓰시려면 <code>commonjs</code>라고 쓰면 된다..</p>
<p>보통 Node.js 환경에서는 CJS를 사용하고, 프론트엔드 개발을 할 때는 보통 번들러에서 모듈을 알아서 처리해 주기 때문에 ESM, CJS 상관없이 쓰실 수 있을 것이다. 어떤 걸 선택할지 잘 모르겠다면 <code>commonjs</code>로 설정하는 걸 추천.</p>
<h4 id="esmoduleinterop-es-모듈을-안전하게-사용"><code>esModuleInterop</code>: ES 모듈을 안전하게 사용</h4>
<p>ESM 문법에서 <code>import * as moment from &#39;moment&#39;</code>라든가 <code>import moment from &#39;moment&#39;</code>라는 문법은 서로 다른데요. 이 옵션을 <code>false</code>로하면 CJS로 변환했을 때 두 코드는 같은 형태의 코드 <code>const moment = require(&#39;moment&#39;)</code>로 변환됩니다. 안전하게 모듈을 사용하려면 <code>esModuleInterop</code> 옵션은 <code>true</code>로 해놓는 것을 권장드립니다.</p>
<h4 id="forceconsistentcasinginfilenames-파일의-대소문자-구분하기"><code>forceConsistentCasingInFileNames</code>: 파일의 대소문자 구분하기</h4>
<p>macOS를 사용하시는 분들은 <code>main.ts</code>라는 파일과 <code>Main.ts</code>라는 파일이 서로 다른데요. Windows와 같이 어떤 운영체제에서는 종종 <code>main.ts</code>라는 파일과 <code>Main.ts</code>라는 파일을 동일하게 취급하기도 합니다. 이런 환경에서 개발하더라도 반드시 대소문자 구분을 명확하게 하겠다는 옵션입니다. 이 옵션도 반드시 <code>true</code>로 해 놓는 걸 권장드립니다.</p>
<h4 id="strict-엄격한-규칙들-켜기"><code>strict</code>: 엄격한 규칙들 켜기</h4>
<p>이 옵션을 켜면 <code>stict</code>와 관련된 여러 규칙들을 한꺼번에 켤 수 있다. 
어떤 옵션들이 켜지는지는 <a href="https://www.typescriptlang.org/tsconfig#strict">공식 문서의 strict 옵션</a> 설명을 참고. 여기서는 그중에서도 대표적으로 알아두면 좋은 것 두 가지를 소개한다.</p>
<ul>
<li><code>noImplicitAny</code>: 아무 타입을 직접 정의하지 않고, 타입 추론도 되지 않는 상태를 implicit any라고 한다. 쉽게 말해서 기존 자바스크립트 코드처럼 타입 없이 사용하는 걸 implicit any라고 한다.<pre><code class="language-javasccript">function printSize(product) {
                 ~~~~~~~ 타입 정의도 없고 추론할 수도 없음
console.log(product.size);
}</code></pre>
</li>
</ul>
<p>새로 시작하는 타입스크립트 프로젝트라면 무조건 켜는 걸 추천드리지만, 기존에 자바스크립트로 만든 프로젝트를 타입스크립트로 옮기는 중이라면 이 옵션을 잠시 끄는 것도 좋다. 아래처럼 설정하면 <code>strict</code> 규칙들을 한꺼번에 설정하지만 <code>noImplicitAny</code>는 설정하지 않을 수 있다.</p>
<pre><code class="language-json">&quot;strict&quot;: true,
&quot;noImplicitAny&quot;: false,</code></pre>
<p><code>strictNullChecks</code>: <code>null</code>이 될 가능성이 있다면, 이런 경우를 반드시 처리하도록 하는 옵션이다. 이것도 되도록이면 켜 놓으시는 걸 추천한다. 예를 들자면 아래와 같은 코드는 strict null check를 할 때는 오류가 난다. <code>num</code> 변수가 <code>null</code>이 될 수도 있으니. 반면에 <code>strictNullChecks</code>가 꺼져있다면 타입 오류가 나지 않는다. 대신에 실행하는 도중에 <code>num</code> 변수에 <code>null</code> 값이 들어간다면 런타임 오류가 발생한다.</p>
<pre><code class="language-javascript">let num: number | null; // ... num -= 1; ~~~</code></pre>
<p>되도록이면 <code>strictNullChecks</code>를 켜고, 이런 경우에는 반드시 null check를 하는 걸 권장한다.</p>
<pre><code class="language-javascript">let num: number | null;
// ...
if (num !== null) {
  num -= 1;
}</code></pre>
<h4 id="skiplibcheck-설치한-패키지의-타입-검사하지-않기"><code>skipLibCheck</code>: 설치한 패키지의 타입 검사하지 않기</h4>
<p><code>node_modules</code> 폴더에 설치된 패키지들의 타입 검사를 하지 않는 옵션. 패키지 개발 과정에서 대부분 타입 검사가 이뤄지기 때문에, 중복으로 하지 않아도 됨. 그래서 이 옵션을 사용하시길 추천.</p>
<h4 id="rootdir-최상위-폴더"><code>rootDir</code>: 최상위 폴더</h4>
<p>타입스크립트 컴파일러가 처리할 타입스크립트 파일들의 최상위 폴더를 정하는 옵션이다. 기본 값으로는 처리하는 파일들의 경로를 종합해서 최상위 폴더를 정한다. 만약 <code>tsconfig.json</code> 파일과 같은 폴더에 있는 <code>src</code> 폴더를 최상위 폴더로 정하고 싶다면 아래와 같이 쓰면 된다.</p>
<pre><code class="language-json">{  
  &quot;compilerOptions&quot;: {
    &quot;rootDir&quot;: &quot;src&quot;,
  }
}</code></pre>
<h4 id="outdir-자바스크립트-파일을-생성할-폴더"><code>outDir</code>: 자바스크립트 파일을 생성할 폴더</h4>
<p><code>outDir</code>에 지정된 폴더 안에다가 <code>rootDir</code>의 디렉토리 구조에 따라서 자바스크립트 파일을 만든다. 값을 지정하지 않으면 소스코드 파일과 같은 폴더에 자바스크립트 파일을 만든다.</p>
<pre><code class="language-json">{  
  &quot;compilerOptions&quot;: {
    &quot;outDir&quot;: &quot;dist&quot;,
  }
}</code></pre>
<h4 id="include와-exclude"><code>include</code>와 <code>exclude</code></h4>
<p><code>tsc</code>로 트랜스파일할 때 포함할 경로(<code>include</code>)와 포함하지 않을 경로(<code>exclude</code>)를 정해줄 수 있다. 배열로 경로 패턴을 적어주면 된다. <code>**/*</code>라는 코드는 아래의 모든 폴더, 모든 파일을 의미함. 참고로 이런 패턴을 Glob 패턴이라고 함. 혹시 궁금하신 분들은 <a href="https://ko.wikipedia.org/wiki/%EA%B8%80%EB%A1%9C%EB%B8%8C_(%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D)">위키피디아 Glob 문서</a>를 참고.</p>
<pre><code class="language-json">{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;es2016&quot;,                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    &quot;module&quot;: &quot;commonjs&quot;,                                /* Specify what module code is generated. */
    &quot;esModuleInterop&quot;: true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables &#39;allowSyntheticDefaultImports&#39; for type compatibility. */
    &quot;forceConsistentCasingInFileNames&quot;: true,            /* Ensure that casing is correct in imports. */
    &quot;strict&quot;: true,                                      /* Enable all strict type-checking options. */
    &quot;skipLibCheck&quot;: true                                 /* Skip type checking all .d.ts files. */
  }
  &quot;include&quot;: [&quot;src/**/*&quot;, &quot;tests/**/*&quot;],
  &quot;exclude&quot;: [&quot;bin/**/*&quot;],
}
</code></pre>
<h4 id="그-밖의-옵션들-활용하기">그 밖의 옵션들 활용하기</h4>
<p>옵션이 너무 많기 때문에 모든 옵션들을 전부 알고 있을 필요는 없다. 그 밖의 다른 옵션들이 궁금하신 분들은 <a href="https://www.typescriptlang.org/tsconfig">타입스크립트 공식 문서</a>를 참고하시는 걸 추천한다.</p>
<p>참고로 VS Code에서는 옵션들을 자동완성 기능으로 작성할 수 있다. 무엇을 작성해야 할지 모를 때 Windows에서는 <code>Ctrl</code> + <code>I</code>, macOS에서는 <code>Cmd</code> + <code>I</code>를 입력하면 Suggestion이 뜬다. 각 옵션에 대한 설명도 볼 수 있으니까 공부 목적이 아니라 프로젝트를 개발할 때는 이 기능을 활용하시는 걸 추천한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[05장 리액트와 상태 관리 라이브러리]]></title>
            <link>https://velog.io/@han_oal/05%EC%9E%A5-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%99%80-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
            <guid>https://velog.io/@han_oal/05%EC%9E%A5-%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%99%80-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</guid>
            <pubDate>Thu, 29 Aug 2024 07:06:00 GMT</pubDate>
            <description><![CDATA[<p>,</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[위클리페이퍼 15.]]></title>
            <link>https://velog.io/@han_oal/%EC%9C%84%ED%81%B4%EB%A6%AC%ED%8E%98%EC%9D%B4%ED%8D%BC-15-6grt2ij5</link>
            <guid>https://velog.io/@han_oal/%EC%9C%84%ED%81%B4%EB%A6%AC%ED%8E%98%EC%9D%B4%ED%8D%BC-15-6grt2ij5</guid>
            <pubDate>Sun, 25 Aug 2024 13:19:12 GMT</pubDate>
            <description><![CDATA[<h2 id="세션-기반-인증과-토큰-기반-인증을-비교해서-설명해-주세요">세션 기반 인증과 토큰 기반 인증을 비교해서 설명해 주세요.</h2>
<p><img src="https://velog.velcdn.com/images/han_oal/post/1cde8877-6374-4a43-9a67-3ca8c129e2af/image.png" alt=""></p>
<h3 id="🗝️인증과-인가">🗝️인증과 인가</h3>
<blockquote>
<h3 id="인증-authentication">인증 (Authentication)</h3>
<p>쉽게 말하자면 인증은 로그인 이다. 클라이언트가 자기자신이라고 주장하고 있는 사용자가 맞는지를 검증하는 과정이다. 예를 들어 로그인 화면에서 내가 유저 아이디를 USER1 로 입력하고 패스워드를 입력해 제출하면, 서버에서는 내가 진짜로 USER1 이라는 유저가 맞는지 확인한다.</p>
</blockquote>
<blockquote>
<h3 id="인가-authorization">인가 (Authorization)</h3>
<p>인가는 인증 작업 이후에 행해지는 작업으로, 인증된 사용자에 대한 자원에 대한 접근 확인 절차를 의미한다.</p>
</blockquote>
<hr>
<p><img src="https://velog.velcdn.com/images/han_oal/post/677b4305-ac19-401b-bb33-8394016e91a5/image.png" alt=""></p>
<h3 id="➡️http의-비상태성stateless">➡️HTTP의 비상태성(Stateless)</h3>
<blockquote>
<h4 id="http는-비상태성이라는-특성을-갖는다">HTTP는 비상태성이라는 특성을 갖는다.</h4>
<p>서버는 클라이언트의 상태를 저장하지 않으며, 따라서 이전 요청과 다음 요청의 맥락이 이어지지 않는다. HTTP는 바로 직전에 발생한 통신을 기억하지 못한다. 따라서 HTTP 단독으로는 요청한 클라이언트가 이전에 이미 인증과정을 거쳤는지 알 방법이 없다.</p>
</blockquote>
<blockquote>
<h4 id="비상태성의-문제">비상태성의 문제</h4>
<p>비상태성 자체로의 문제점은 이전 요청에 대한 정보를 기억할 수 없는 것에서 이어진다. 글을 조회하거나 작성할 때 마다 사용자에게 로그인을 하라고 요청해야한다는 것이다.</p>
</blockquote>
<blockquote>
<h4 id="그렇다면">그렇다면,</h4>
<p>사용자의 아이디와 패스워드를 브라우저에 그대로 저장해놓고, 매 요청마다 함께 그 정보를 보내는 방법은 어떨까? 이런 방식은 전송 데이터가 커져 비효율적일 뿐더러 클라이언트에 민감한 데이터가 그대로 저장되어 보안에도 굉장히 취약하다. 서버 입장에서도 매 작업마다 데이터베이스를 조회하고, 인증과정을 거치는 것이 비효율일 것 이다.</p>
</blockquote>
<blockquote>
<h4 id="이런-http-환경에서-서버는-어떤-방식으로-사용자를-인가할까">이런 HTTP 환경에서 서버는 어떤 방식으로 사용자를 인가할까?</h4>
<p>웹 어플리케이션에서는 이 문제를 <code>세션</code> 또는 <code>토큰</code>을 사용하여 문제를 해결한다. 
즉, <code>세션</code>과 <code>토큰</code>은 <code>인증</code>보다 <code>인가</code>와 관련된 기술이라고 할 수 있다.</p>
</blockquote>
<hr>
<h3 id="세션기반-인증">세션기반 인증</h3>
<p><img src="https://velog.velcdn.com/images/han_oal/post/8faea2ee-4735-461f-9b53-dd8509020211/image.png" alt=""></p>
<blockquote>
<h4 id="세션기반-인가는">세션기반 인가는</h4>
<p>사용자의 인증 정보가 서버의 <code>세션 저장소</code>에 저장되는 방식이다. 
사용자가 로그인을 하면, 해당 인증 정보를 서버의 세션 저장소에 저장하고, 사용자에게는 저장된 세션 정보의 식별자인 Session ID를 발급한다. 발급된 Session ID는 브라우저에 쿠키 형태로 저장되지만, 실제 인증 정보는 서버에 저장되어 있다.</p>
</blockquote>
<blockquote>
<p>브라우저는 인증 절차를 마친 이후의 요청마다 HTTP Cookie 헤더에 Session ID 를 함께 서버로 전송한다. 서버는 요청을 전달받고, Session ID에 해당하는 세션 정보가 세션 저장소에 존재한다면 해당 사용자를 인증된 사용자로 판단한다.</p>
</blockquote>
<hr>
<h3 id="토큰기반-인증">토큰기반 인증</h3>
<p><img src="https://velog.velcdn.com/images/han_oal/post/cb349d67-6a12-4177-a7f8-c5e6fee903a4/image.png" alt=""></p>
<blockquote>
<p>세션 기반 인증이 인증 정보를 서버에 저장하는 방식이라면, 
토큰 기반 인증은 인증 정보를 <code>클라이언트가 직접 들고 있는 방식</code>이다. 
이때 인증 정보가 토큰의 형태로 브라우저의 로컬 스토리지(혹은 쿠키)에 저장된다. 토큰의 종류에 따라 다르겠지만, 대표적인 토큰인 JWT의 경우 디지털 서명이 존재해 토큰의 내용이 위변조 되었는지 서버측에서 확인할 수 있다.</p>
</blockquote>
<blockquote>
<p>토큰 기반 인증에서는 사용자가 가지고 있는 토큰을 HTTP 의 <code>Authorization</code> 헤더에 실어 보낸다. 이 헤더를 수신한 서버는 토큰이 위변조 되었거나, 만료 시각이 지나지 않은지 확인한 이후 토큰에 담겨있는 사용자 인증 정보를 확인해 사용자를 인가한다.</p>
</blockquote>
<hr>
<hr>
<h2 id="authorization-code를-활용하는-구글-소셜-로그인을-실행하기-까지-유저-프론트엔드-백엔드-openid-connect-프로바이더-사이에-어떤-과정을-거치는지-설명해-주세요">Authorization Code를 활용하는 구글 소셜 로그인을 실행하기 까지 유저, 프론트엔드, 백엔드, OpenID Connect 프로바이더 사이에 어떤 과정을 거치는지 설명해 주세요.</h2>
<p><img src="https://velog.velcdn.com/images/han_oal/post/3b9fdaad-9d1a-43ff-86d3-546ce53c179e/image.png" alt=""></p>
<ol>
<li>유저 서비스 접근 및 간편 로그인 버튼 클릭</li>
<li>프론트엔드 간편 로그인 요청</li>
<li>OpenID Connect 프로바이더 로그인 페이지 제공, 간편 로그인 이메일/PW 요청</li>
<li>유저 간편 로그인 이메일/PW 입력하여 본인 인증</li>
<li>OpenID Connect 프로바이더 프론트엔드에 Authorization Code 발급</li>
<li>프론트엔드 백엔드에 Authorization Code 전달</li>
<li>백엔드 Authorization Code + Client ID + Client Secret 인증</li>
<li>OpenID Connect 프로바이더 백엔드에 Access Token + ID Token 발급</li>
<li>백엔드 프론트엔드로 Session ID 또는 Token 전달</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 리엑트 Deep Dive 스터디 05회차]]></title>
            <link>https://velog.io/@han_oal/%EB%AA%A8%EB%8D%98-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C</link>
            <guid>https://velog.io/@han_oal/%EB%AA%A8%EB%8D%98-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C</guid>
            <pubDate>Sun, 25 Aug 2024 11:33:06 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/han_oal/post/f989b6f8-1902-4094-ab70-bf19e188643c/image.png" alt=""></p>
<p><del>천재 해커 등장.</del></p>
<h1 id="4-서버-사이드-렌더링">4. 서버 사이드 렌더링</h1>
<blockquote>
<p>해당 장에서는 리엑트를 이용한 <code>SSR(Server-Side Rendering)</code>에 대해 알아봄.</p>
</blockquote>
<h2 id="✨서버사이드-렌더링이란">✨서버사이드 렌더링이란?</h2>
<blockquote>
<h3 id="왜-ssr에-대해-배워야할까">왜 SSR에 대해 배워야할까?</h3>
<p><code>SSR</code> 코드는 <code>SPA</code>를 만드는 것 보다 신경 쓸 점이 많음.
<code>SSR</code>이 왜 필요한지 배우지 못하면 일련의 작업들이 귀찮게만 느껴질 수 있다.</p>
</blockquote>
<h3 id="🔻싱글-페이지-애플리케이션의-세상">🔻싱글 페이지 애플리케이션의 세상</h3>
<p><img src="https://velog.velcdn.com/images/han_oal/post/6ec61724-91c7-433e-88e1-8bbdd6e68151/image.png" alt=""></p>
<blockquote>
<h4 id="싱글-페이지-애플리케이션이란">싱글 페이지 애플리케이션이란?</h4>
<p>렌더링과 라우팅에 필요한 대부분의 기능을 <code>브라우저</code>의 자바스크립트에 의존하는 방식을 의미. 
<strong><em>SSR과 SPA는 상반되는 개념.</em></strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/han_oal/post/cc8bdc11-457f-40ad-8901-f81e33948db7/image.png" alt=""><strong>* ▲하나의 페이지에서 모든 작업을 처리함*</strong></p>
<blockquote>
<h4 id="왜-싱글-페이지-라는-이름이-붙었을까">왜 싱글 페이지 라는 이름이 붙었을까?</h4>
<p>최초에 첫 페이지에서 데이터를 모두 불러온 이후에는 페이지 전환을 위한 모든 작업이 자바스크립트와 브라우저의 <code>history.pushState</code> 와 <code>history.replaceState</code> 로 이루어지기 때문에 페이지를 불러온 이후에는 서버에서 HTML을 내려받지 않고 하나의 페이지에서 모든 작업을 처리함.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/han_oal/post/8ddd5420-5843-4551-a836-1f88e69e6440/image.png" alt=""></p>
<h4 id="spa-html-코드를-들여다보면">SPA HTML 코드를 들여다보면</h4>
<p>정상 작동하는 <code>SPA HTML</code> 코드를 들여다보면 <body/> 내부에 아무런 내용이 없음. 이는 사이트 렌더링에 필요한 <code>&lt;body/&gt; 내부 내용</code>을 모두 자바스크립트 코드로 삽입한 이후에 렌더링 하기 떄문임.</p>
<blockquote>
<h4 id="최초에-서버에서-최소한의-데이터를-불러온다">최초에 서버에서 최소한의 데이터를 불러온다.</h4>
</blockquote>
<ul>
<li>다음 페이지의 렌더링에 필요한 정보만 가져옴,</li>
<li><code>&lt;body/&gt; 내부 DOM</code>을 추가,수정,삭제하는 방식의 페이지가 전환. </li>
<li>서버에서 최초 데이터를 불러온 이후부터는 이미 가지고 있는 데이터를 기반으로 거의 모든 기능이 작동. </li>
<li>자바스크립트 리소스가 커지는 단점</li>
<li>한 번 로딩된 이후에는 서버에서 리소스를 받아올 일이 적어짐(훌륭한 UI/UX를 제공한다는 장점)</li>
</ul>
<h3 id="전통적-방식">전통적 방식</h3>
<ul>
<li>페이지 전환이 발생할 때마다 새롭게 페이지를 요청</li>
<li>HTML 페이지를 다운로드해 파싱하는 작업을 거침</li>
<li>페이지를 처음부터 새로 그려야 해서 일부 사용자는 페이지가 전환될 때 부자연스러운 모습을 보게 됨.</li>
</ul>
<h3 id="spa-유행-이유">SPA 유행 이유</h3>
<ul>
<li>React,Vue 등의 등장과 함께 <code>자바스크립트</code> 의 의존도가웹페이지 전역으로 확장되면서 이를 모두 아우를 수 있는 방식인 <code>SPA</code>가 인기를 얻게 됨.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/han_oal/post/75bb6fb5-4c48-43d8-aa60-e79d1bb25364/image.png" alt=""></p>
<h3 id="jam-스택">JAM 스택</h3>
<p>JAM stack은 JavaScript와 API 그리고 Markup(HTML) 만으로 이루어진 웹의 구성</p>
<h4 id="am-stack에서-정의한-각자의-역할">AM stack에서 정의한 각자의 역할</h4>
<ul>
<li>JavaScript
Client의 모든 처리는 Javascript 에게 맡김.</li>
<li>API
모든 기능 및 비즈니스 로직은 재사용 가능한 API 로 추상화.</li>
<li>Markup
SSG 나 Template Engine (Webpack 등) 을 이용하여 Markup 을 미리 생성.</li>
</ul>
<hr>
<h3 id="🔻서버-사이드-렌더링">🔻서버 사이드 렌더링</h3>
<blockquote>
<h4 id="ssr--spa--ssr">SSR &gt; SPA &gt; SSR</h4>
</blockquote>
<ul>
<li>SPA의 성능 문제를 해결하기 위해 서버 사이드 렌더링(SSR)이 다시 주목받고 있다. </li>
</ul>
<blockquote>
<ul>
<li><strong><em>SPA</em></strong> : 사용자에게 제공되는 번들에서 렌더링 담당.
(로컬 기기에 영향을 받음 = 성능 보장 X)</li>
</ul>
</blockquote>
<ul>
<li><strong><em>SSR</em></strong> : 렌더링에 필요한 작업을 모두 서버에서 수행
(서버에서 렌더링 제공 = 안정적인 성능 보장)</li>
</ul>
<h4 id="ssr-장점">SSR 장점</h4>
<ul>
<li>최초 페이지 진입이 비교적 빠름</li>
<li>검색 엔진과 SNS 공유 등 메타데이터 제공이 쉽다</li>
<li>누적 레이아웃 이동이 적음</li>
<li>사용자의 디바이스 성능에 비교적 자유로움</li>
</ul>
<h4 id="ssr-단점">SSR 단점</h4>
<ul>
<li>소스코드를 작성할 때 항상 서버를 고려해야 함</li>
<li>적절한 서버가 구축되어 있어야 함</li>
<li>서비스 지연에 따른 문제</li>
</ul>
<h4 id="현대의-서버사이드-렌더링">현대의 서버사이드 렌더링</h4>
<p>현대 SSR은 이전 LAMP 스택에서 표현했던 방식과 조금 다름
LAMP 스택은 모든 페이지 빌드를 서버에서 렌더링해 초기 페이지 진입이 빠르다는 장점이 있지만, 이후에 라우팅이 발생할 때도 마찬가지로 서버에 의존해야 하기 때문에 싱글 페이지 렌더링 방식에 비해 라우팅이 느리다는 단점이 있음.</p>
<p>그래서 요즘의 서버 사이드 렌더링은 이 두가지 장점을 모두 취한 방식으로 작동한다.</p>
<p>최초 웹사이트 진입시에는 서버 사이드 렌더링 방식으로 서버에서 완성된 HTML을 제공받고, 이후 라우팅에서는 서버에서 내려받은 자바스크립트를 바탕으로 마치 싱글 페이지 애플리케이션처럼 작동한다. </p>
<h3 id="🔻spa와-ssr을-모두-알아야-하는-이유">🔻SPA와 SSR을 모두 알아야 하는 이유</h3>
<p>SSR 또한 모든 문제의 해결책이 될 수는 없음.
따라서 모든 방법론을 이해할 필요가 있음.</p>
<hr>
<h2 id="✨서버-사이드-렌더링을-위한-리액트-api">✨서버 사이드 렌더링을 위한 리액트 API</h2>
<h3 id="🔻rendertostring">🔻renderToString</h3>
<p>인수로 넘겨받은 리액트 컴포넌트 렌더링해 html 문자열로 반환하는 함수.</p>
<h3 id="🔻rendertostaticmarkup">🔻renderToStaticMarkup</h3>
<p><code>renderToString</code>과 매우 유사한 함수.
두 함수 모두 리액트 컴포넌트를 기준으로 HTML 문자열을 만든다는 점에서 동일함. 한 가지 유의미한 차이점은 앞서 루트 요소에 추가한 <code>data-reactroot</code> 와 같은 리액트에서만 사용하는 추가적인 DOM 속성을 만들지 않는다는 점.</p>
<h3 id="🔻rendertonodestream">🔻renderToNodeStream</h3>
<p><code>renderToString</code> 과 같은 결과물이 완전히 동일하지만 두 가지 차이점이 있음.
01. 앞에서 살펴본 두 API인 <code>renderToString</code> &amp; <code>renderToStaticMarkup</code> 은 브라우저에서 실행될 수 있지만, <code>renderToNodeStream</code> 은 실행이 불가능함.</p>
<h3 id="🔻rendertostaticnodestream">🔻renderToStaticNodeStream</h3>
<p><code>renderToString</code>에 <code>renderToStaticMarkup</code>이 있다면
<code>renderToNodeStream</code> 에는 <code>renderToStaticNodeStream</code>이 있다. <code>renderToNodeStream</code> 과 제공하는 결과물은 동일하나, <code>renderToStaticMarkup</code> 과 마찬가지로 리액트 자바스크립트에 필요한 리액트 속성이 제공되지 않음.</p>
<h3 id="🔻hydrate">🔻hydrate</h3>
<p>hydrate 함수는 앞서 살펴본 두 개의 함수.
<code>renderToString</code> 과 <code>renderToNodeStream</code> 으로 생성된 HTML 콘텐츠에 자바스크립트 핸들러나 이벤트를 붙이는 역할을 함. <code>renderToString</code> 의 결과물은 단순히 서버에서 렌더링한 HTML 결과물로 사용자에게 무언가 보여줄 수 있지만, 사용자가 페이지와 인터랙션하는 것은 불가능함. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 리엑트 Deep Dive 스터디 04회차]]></title>
            <link>https://velog.io/@han_oal/%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%97%91%ED%8A%B8-Deep-Dive-%EC%8A%A4%ED%84%B0%EB%94%94-04%ED%9A%8C%EC%B0%A8</link>
            <guid>https://velog.io/@han_oal/%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%97%91%ED%8A%B8-Deep-Dive-%EC%8A%A4%ED%84%B0%EB%94%94-04%ED%9A%8C%EC%B0%A8</guid>
            <pubDate>Thu, 22 Aug 2024 07:16:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/han_oal/post/889c2c2f-eac8-4b28-b180-bd1c1265ba17/image.png" alt=""></p>
<h1 id="3-리액트-훅-깊게-살펴보기">3 리액트 훅 깊게 살펴보기</h1>
<blockquote>
<h3 id="hooks">Hooks?</h3>
<p><code>클래스 컴포넌트</code>에서 사용하던 리엑트의 다향한 기능을 
<code>함수형 컴포넌트</code>로 <code>Hooking</code>해와서 사용할 수 있는 기능.</p>
</blockquote>
<h2 id="✨3-1-리엑트의-모든-훅-파헤치기">✨3-1 리엑트의 모든 훅 파헤치기</h2>
<h3 id="🔻usestate">🔻useState</h3>
<blockquote>
<p>함수형 컴포넌트 내부에서 상태를 정의하고, 관리할 수 있게 해줌.</p>
</blockquote>
<h3 id="🔻useeffect">🔻useEffect</h3>
<blockquote>
<p>애플리케이션 내 컴포넌트의 여러 값들을 활동을 감지해 동기적으로 사이드이펙트를 만드는 함수.</p>
</blockquote>
<ul>
<li>--!-- 생명주기 메서드를 대체하기 위해 만들어진 훅이 아니다.</li>
</ul>
<h3 id="🔻usememo">🔻useMemo</h3>
<blockquote>
<p>비용이 큰 연산에 대한 결과값을 저장해 두고, 이 저장된 값을 반환하는 훅.</p>
</blockquote>
<h3 id="🔻usecallback">🔻useCallback</h3>
<blockquote>
<p>특정 함수를 재사용하는 훅.</p>
</blockquote>
<h3 id="🔻useref">🔻useRef</h3>
<blockquote>
<p>값이 변하더라도 렌더링을 발생시키지 않는 변수 훅.</p>
</blockquote>
<h3 id="🔻usecontext">🔻useContext</h3>
<blockquote>
<p>명시적인 props 전달 없이도 선언한 하위 컴포넌트 모두에서 자유롭게 원하는 값을 사용할 수 있는 훅.</p>
</blockquote>
<h3 id="🔻usereducer">🔻useReducer</h3>
<blockquote>
<p>useState와 비슷한 형태를 띠지만 좀 더 복잡한 상태값을 미리 정의해 놓은 시나리오에 따라 관리할 수 있는 훅.</p>
</blockquote>
<ul>
<li>반환값은 길이가 2인 배열.
2개에서 3개의 인수를 필요로 함.</li>
</ul>
<blockquote>
<h4 id="사용-목적">사용 목적</h4>
<p>복잡한 형태의 state를 사전에 정의된 <code>dispatcher</code> 로만 수정할 수 있게 만들어 줌.
미리 제한한 시나리오에서 state 변경을 빠르게 확인할 수 있게끔 하는 것.</p>
</blockquote>
<h3 id="🔻useimperativehandle">🔻useImperativeHandle</h3>
<blockquote>
<p>부모에게서 넘겨받은 ref를 원하는 대로 수정할 수 있는 훅.</p>
</blockquote>
<h3 id="🔻uselayouteffect">🔻useLayoutEffect</h3>
<blockquote>
<p>dom 은 계산되었지만 이것이 화면에 반영되기 전에 하고싶은 작업이 있을 때 실행하는 훅.
자주 사용되지는 않음.</p>
</blockquote>
<h3 id="🔻usedebugvalue">🔻useDebugValue</h3>
<blockquote>
<p>디버깅 하고 싶은 정보를 이 훅에다 사용하면 리액트 개발자 도구에서 볼 수 있다.
리액트 애플리케이션을 개발하는 과정에서 사용되는 훅.</p>
</blockquote>
<h3 id="🔻훅의-규칙">🔻훅의 규칙</h3>
<blockquote>
<p>rules-of-hooks 라고 함. 이와 관련된 ESLint 규칙도 존재함.</p>
</blockquote>
<h2 id="✨3-2-사용자-정의-훅과-고차-컴포넌트-중-무엇을-써야-할까">✨3-2 사용자 정의 훅과 고차 컴포넌트 중 무엇을 써야 할까?</h2>
<h3 id="🔻사용자-정의-훅">🔻사용자 정의 훅</h3>
<blockquote>
<p>사로 다른 컴포넌트 내부에서 같은 로직을 공유하고자 할 때 주로 사용되는 훅.
리액트에서만 사용할 수 있는 방식.
기존의 훅을 기반으로 개발자가 필요한 훅을 만드는 기법.</p>
</blockquote>
<h3 id="🔻고차-컴포넌트">🔻고차 컴포넌트</h3>
<blockquote>
<p>컴포넌트 자체의 로직을 재사용하기 위한 방법.
사용자 정의 훅은 리액트 훅을 기반으로 하므로 리액트에서만 사용할 수 있는 기술이지만,
고차 컴포넌트는 고차 함수릐 일종으로, 자바스크립트의 일급 객체, 함수의 특징을 이용하므로 굳이 리엑트가 아니더라도
자바스크립트의 환경에서 널리 쓰임.</p>
</blockquote>
<h3 id="🔻사용자-정의-훅과-고차-컴포넌트-중-무엇을-써야할까">🔻사용자 정의 훅과 고차 컴포넌트 중 무엇을 써야할까?</h3>
<blockquote>
</blockquote>
<h3 id="🔻정리">🔻정리</h3>
<blockquote>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 리엑트 Deep Dive 스터디 03회차]]></title>
            <link>https://velog.io/@han_oal/%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%97%91%ED%8A%B8-Deep-Dive-%EC%8A%A4%ED%84%B0%EB%94%94-02%ED%9A%8C%EC%B0%A8-ohvj48e1</link>
            <guid>https://velog.io/@han_oal/%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%97%91%ED%8A%B8-Deep-Dive-%EC%8A%A4%ED%84%B0%EB%94%94-02%ED%9A%8C%EC%B0%A8-ohvj48e1</guid>
            <pubDate>Tue, 20 Aug 2024 07:25:13 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/han_oal/post/08c0adf7-f084-4edc-8330-a43320753920/image.png" alt=""></p>
<p>비장한 표정으로 일 하는 척 하는 나.</p>
<hr>
<h1 id="2-리액트-핵심-요소-깊게-살펴보기">2 리액트 핵심 요소 깊게 살펴보기</h1>
<h2 id="✨2-1-jsx란">✨2-1 JSX란?</h2>
<blockquote>
<h4 id="jsx란"><code>JSX</code>란?</h4>
<p><code>JSX</code>는 페이스북이 임의로 만든 새로운 문법의 자바스크립트 확장 문법, 
<code>ECMAScript(자바스크립트 표준)</code> 의 일부가 아님.</p>
</blockquote>
<blockquote>
<p><code>JSX</code>는 <code>자바스크립트 표준</code>이 아니기때문에 반드시 <code>트랜스파일러</code>를 거쳐서 자바스크립트 코드로 변환해어야 함.</p>
</blockquote>
<h3 id="🔻jsx의-정의">🔻JSX의 정의</h3>
<blockquote>
<p><code>JSX</code>는 <code>4가지 컴포넌트</code>를 기반으로 구성되어있음.</p>
</blockquote>
<blockquote>
<h4 id="jsxelement-컴포넌트"><code>JSXElement</code> 컴포넌트</h4>
<p>JSX를 구성하는 가장 기본요소, HTML 요소(element) 랑 비슷한 역할을 함.</p>
</blockquote>
<blockquote>
<h4 id="jsxattribute-컴포넌트"><code>JSXAttribute</code> 컴포넌트</h4>
<p>JSXElement의 요소의 이름으로 쓸 수 있는 것을 의미.</p>
</blockquote>
<blockquote>
<h4 id="jsxchildren-컴포넌트"><code>JSXChildren</code> 컴포넌트</h4>
<p>JSXElement에 부여할 수 있는 속성을 의미</p>
</blockquote>
<blockquote>
<h4 id="jsxstring컴포넌트"><code>JSXString</code>컴포넌트</h4>
<p>JSXElement의 자식값을 나타냄.</p>
</blockquote>
<h2 id="✨2-2-dom과-리액트-파이버">✨2-2 DOM과 리액트 파이버</h2>
<h3 id="🔻dom과-브라우저-렌더링-과정">🔻DOM과 브라우저 렌더링 과정</h3>
<blockquote>
<h4 id="dom-이란"><code>DOM</code> 이란?</h4>
<p>웹페이지에 대한 인터페이스로 브라우저가 웹페이지의 콘텐츠와 구조를 어떻게 보여줄지에 대한 정보를 담고있는 것.</p>
</blockquote>
<h3 id="🔻가상-dom의-탄생-배경">🔻가상 DOM의 탄생 배경</h3>
<blockquote>
<h4 id="렌더링-과정은-매우-복잡하고-많은-비용이-든다">렌더링 과정은 매우 복잡하고, 많은 비용이 든다.</h4>
<p>렌더링 과정을 줄이기 위해 생겨난 것이 가상 DOM.</p>
</blockquote>
<h3 id="🔻가상-dom을-위한-아키텍처-리액트-파이버">🔻가상 DOM을 위한 아키텍처, 리액트 파이버</h3>
<blockquote>
<h4 id="리엑트-파이버">리엑트 파이버?</h4>
<p> <code>파이버 재조정자(FIBER RECONCILER)</code> 가 관리함.
<code>가상 DOM</code>과 <code>실제 DOM</code>을 비교해 변경 사항을 수집하고, 만약 이 둘사이에 차이가 있으면 변경에 관련된 정보를 가지고 있는 <code>파이버</code>를 기준으로 화면에 렌더링 요청을 하는 역할을 함.
리액트에서 관리하는 평범한 자바스크립트 객체.</p>
</blockquote>
<blockquote>
<h4 id="파이버-트리는-리액트-내부에-두-개가-존재">파이버 트리는 리액트 내부에 두 개가 존재.</h4>
<p>하나는 현재 모습을 담은 파이버트리 &amp; 다른 하나는 작업 중인 상태를 나타내는 <code>workInProgress</code> 트리.</p>
</blockquote>
<h3 id="🔻파이버와-가상-dom">🔻파이버와 가상 DOM</h3>
<blockquote>
<h4 id="리액트-컴포넌트에-대한-정보를-11로-가지고-있는-것이-파이버">리액트 컴포넌트에 대한 정보를 1:1로 가지고 있는 것이 파이버</h4>
<h4 id="파이버는-리액트-아키텍처-내부에서-비동기로-이뤄짐">파이버는 리액트 아키텍처 내부에서 비동기로 이뤄짐</h4>
<h4 id="실제-브라우저-구조인-dom에-반영하는-것은-동기적으로-일어나야-함">실제 브라우저 구조인 DOM에 반영하는 것은 동기적으로 일어나야 함.</h4>
<h4 id="메모리상에서-먼저-수행해서-최종적인-결과물만-실제-브라우저-dom이-작동함">메모리상에서 먼저 수행해서 최종적인 결과물만 실제 브라우저 DOM이 작동함.</h4>
<h4 id="파이버와-가장-가상-dom은-동일한-개념이-아님">파이버와 가장 가상 DOM은 동일한 개념이 아님.</h4>
</blockquote>
<h2 id="✨2-3-클래스-컴포넌트와-함수-컴포넌트">✨2-3 클래스 컴포넌트와 함수 컴포넌트</h2>
<h3 id="🔻클래스-컴포넌트">🔻클래스 컴포넌트</h3>
<blockquote>
<p>16.8 미만으로 작성된 코드에는 클래스 컴포넌트가 대다수.
이런 오래된 코드의 유지보수 내지는 오래전에 개발된 라이브러리 등을 사용할 때
도움을 얻기 위해서는 기본적인 클래스 컴포넌트 구조를 이해해야함.</p>
</blockquote>
<blockquote>
<h4 id="클래스-컴포넌트의-한계">클래스 컴포넌트의 한계</h4>
<p>데이터 흐름을 추적하기 어려움.
애플리케이션 내부 로직의 재사용이 어려움.
기능이 많아질수록 컴포넌트의 크기가 커짐.
클래스는 함수에 비해 상대적으로 어려움.</p>
</blockquote>
<h3 id="🔻함수-컴포넌트">🔻함수 컴포넌트</h3>
<blockquote>
<p>16.8에서 함수 컴포넌트에 사용 가능한 훅이 등장하면서 리액트 개발자들 사이에서 주류가 됨.</p>
</blockquote>
<h2 id="✨2-4-렌더링은-어떻게-일어나는가">✨2-4 렌더링은 어떻게 일어나는가?</h2>
<h3 id="🔻리엑트-렌더링이란">🔻리엑트 렌더링이란?</h3>
<h3 id="🔻리액트의-렌더링이-일어나는-이유">🔻리액트의 렌더링이 일어나는 이유</h3>
<h3 id="🔻리액트의-렌더링-프로세스">🔻리액트의 렌더링 프로세스</h3>
<h3 id="🔻렌더와-커밋">🔻렌더와 커밋</h3>
<h3 id="🔻일반적인-렌더링-시나리오-살펴보기">🔻일반적인 렌더링 시나리오 살펴보기</h3>
<h2 id="✨2-5-컴포넌트와-함수의-무거운-연산을-기억해-두는-메모이제이션">✨2-5 컴포넌트와 함수의 무거운 연산을 기억해 두는 메모이제이션</h2>
<blockquote>
<p>메모이제이션?
리엑트에서 발생하는 렌더링을 최소한으로 줄이기 위해 제공되는 API 컴포넌트.</p>
</blockquote>
<blockquote>
<p>최적화 사용에 대해 2가지 주장이 갈림.</p>
</blockquote>
<h3 id="🔻주장1-섣부른-최적하는-독이다-꼭-필요한-곳에만-메모이제이션을-추가하자">🔻주장1: 섣부른 최적하는 독이다, 꼭 필요한 곳에만 메모이제이션을 추가하자</h3>
<blockquote>
<p>메모이제이션도 어디까지나 비용이 드는 작업으로 필요한 곳을 신중히 골라서 메모이제이션 해야한다는 주장.</p>
</blockquote>
<h3 id="🔻주장2-렌더링-과정의-비용은-비싸다-모조리-메모이제이션해-버리자">🔻주장2: 렌더링 과정의 비용은 비싸다, 모조리 메모이제이션해 버리자</h3>
<blockquote>
<p>모조리 메모이제이션 해버리자.</p>
</blockquote>
<blockquote>
<p>집필자의 의견:
석부른 메모이제이션을 지양하는 자세를 유지하면서,
실제 어느 지점에서 성능상 이점을 누릴 수 있는 지 살펴보는 식으로 메모이션을 적용하는 것을 권장.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[위클리페이퍼 14.]]></title>
            <link>https://velog.io/@han_oal/%EC%9C%84%ED%81%B4%EB%A6%AC%ED%8E%98%EC%9D%B4%ED%8D%BC-14</link>
            <guid>https://velog.io/@han_oal/%EC%9C%84%ED%81%B4%EB%A6%AC%ED%8E%98%EC%9D%B4%ED%8D%BC-14</guid>
            <pubDate>Sat, 17 Aug 2024 09:51:05 GMT</pubDate>
            <description><![CDATA[<h2 id="cors-에러에-대해-설명하고-어떻게-해결하면-될지-설명해주세요">CORS 에러에 대해 설명하고, 어떻게 해결하면 될지 설명해주세요.</h2>
<h3 id="❌cors-에러란">❌<code>CORS</code> 에러란?</h3>
<p><img src="https://velog.velcdn.com/images/han_oal/post/fb680581-8d45-4cf0-8ec6-530705473234/image.png" alt=""></p>
<blockquote>
</blockquote>
<p><code>CORS</code>은 악명 높은 에러로, 웹 개발을 하다 보면 반드시 한 번쯤은 마주하게 된다는 공포의 에러 메시지.</p>
<blockquote>
<p><code>CORS</code>는 <code>Cross-Origin Resource Sharing</code>의 줄임말로 <code>교차-출처 리소스 공유</code>라고도 한다.</p>
</blockquote>
<blockquote>
<p><code>CORS</code>는 한 도메인이 도메인 간의 요청을 가진 다른 도메인의 리소스에 액세스할 수 있게 해주는 보안 메커니즘으로 최신 브라우저에서 구현된 <code>동일 출처 정책</code> 때문에 등장하게 되었음.</p>
</blockquote>
<h4 id="동일-출처-정책이란">동일 출처 정책이란?</h4>
<blockquote>
</blockquote>
<p>동일한 출처의 리소스에만 접근하도록 제한하는 정책을 말함. 여기서 말하는 출처라는것은 <code>프로토콜</code>, <code>호스트명</code>, <code>포트</code>가 같다는 것을 의미한다.</p>
<h3 id="💥cors-에러-해결방법">💥<code>CORS</code> 에러 해결방법</h3>
<h4 id="서버측-해결방법">서버측 해결방법</h4>
<h5 id="01서버에서-cors-헤더-추가하기">01.서버에서 CORS 헤더 추가하기</h5>
<p>서버에서 CORS 요청을 허용하기 위해 적절한 HTTP 헤더를 설정하는 방법.</p>
<pre><code class="language-javascript">const express = require(&#39;express&#39;);
const app = express();

app.use((req, res, next) =&gt; {
    res.header(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;); // 모든 도메인 허용
    res.header(&#39;Access-Control-Allow-Methods&#39;, &#39;GET, POST, PUT, DELETE, OPTIONS&#39;);
    res.header(&#39;Access-Control-Allow-Headers&#39;, &#39;Content-Type, Authorization&#39;);
    next();
});

app.listen(3000);
</code></pre>
<h5 id="02프리플라이트-요청-처리">02.프리플라이트 요청 처리</h5>
<p>특정 요청(특히 POST, PUT, DELETE 요청)은 브라우저가 먼저 OPTIONS 요청을 보내어 서버의 CORS 정책을 확인. 서버가 이 OPTIONS 요청에 대해 적절한 응답을 반환해야 함.</p>
<h5 id="03서버-설정-파일-수정">03.서버 설정 파일 수정</h5>
<p>Apache나 Nginx와 같은 서버를 사용하는 경우, 서버 설정 파일에서 CORS 헤더를 추가할 수 있음.</p>
<hr>
<h4 id="클라이언트-측-해결-방법">클라이언트 측 해결 방법</h4>
<h5 id="01프록시-서버-사용">01.프록시 서버 사용</h5>
<p>클라이언트 측에서 직접 CORS 문제를 해결하기 어려운 경우, 중간에 프록시 서버를 두어 요청을 중계할 수 있음.</p>
<h5 id="02jsonp-사용">02.JSONP 사용</h5>
<p>JSONP는 CORS 제한을 우회하는 구식 방법입니다. 하지만, 보안상의 이유로 JSONP는 최근 웹 개발에서는 추천되지 않음.</p>
<hr>
<h2 id="seo가-무엇인지-설명하고-개선을-위해-어떤-작업을-할-수-있을지-설명해주세요">SEO가 무엇인지 설명하고, 개선을 위해 어떤 작업을 할 수 있을지 설명해주세요</h2>
<p><img src="https://velog.velcdn.com/images/han_oal/post/5c2ceddf-4c7b-48cc-8f05-593028f9c1b6/image.png" alt=""></p>
<h3 id="💯seo란">💯&#39;SEO란?</h3>
<blockquote>
<p><code>Search Engine Optimization</code> 의 약자이며, <code>검색 엔진 최적화</code> 라고도 함.
웹사이트나 웹페이지의 가시성을 향상시키기 위해 검색 <code>엔진 결과 페이지(SERP)</code>에서 순위를 높이는 일련의 전략과 기법을 말함.</p>
</blockquote>
<h3 id="🔱온사이트--오프사이트">🔱온사이트 &amp; 오프사이트</h3>
<p>SEO 검색엔진최적화를 위해서는 <code>온사이트(on-site)</code>와 <code>오프사이트(off-site)</code>를 모두 최적화해야한다. 
<code>온사이트(on-site)</code>는 내 웹 사이트의 ‘내부’를 관리하는 영역이며, <code>오프사이트(off-site)</code>는 내 웹 사이트의 ‘외부’ 를 관리하는 영역이다. 개발자의 영역은 온사이트 영역이기 때문에 온사이트만 설명하도록 하겠음.</p>
<h3 id="💥온사이트-최적화">💥온사이트 최적화</h3>
<ul>
<li>이미지 최적화
이미지 파일 크기를 줄이기, 명확한 파일 이름, <code>Alt</code> 태그를 사용하여 이미지에 대한 설명 명시</li>
</ul>
<hr>
<ul>
<li>URL 최적화
간결하고 설명적인 URL 구조를 사용(검색 엔진과 사용자가 페이지의 내용을 쉽게 이해할 수 있음)</li>
</ul>
<hr>
<ul>
<li>제목 태그 및 메타 설명 최적화
<code>&lt;title&gt;</code> (너무 긴 텍스트 사용 x, 모든 웹페이지의 이름이 같지 않도록 주의, 주요 키워드 삽입)</li>
</ul>
<p><code>&lt;description&gt;</code> 해당 웹페이지의 설명을 요약한 한 두줄의 잘 읽히는 문장을 잘 적어넣는 게 중요.
  (반복적이지 않은, 다른 내용의 키워드로 작성되는 편이 좋다)</p>
<p><code>&lt;robot&gt;</code> 웹페이지 별 검색 로봇의 접근 여부를 설정할 때 활용하는 태그 (로봇 태그의 속성을 어떻게 정의하느냐에 따라 검색 로봇이 웹페이지를 크롤링학도 색인할 수 있는 권한을 받거나 받지 못할수도 있다고 함)</p>
<p><code>&lt;canonical&gt;</code> 여러 url을 가진 웹페이지가 있을 때, 해당 페이지의 대표 url을 설정할 수 있는 태그.
(한 페이지의 대표되는 url을 지정함으로써, 검색 로봇이 웹페이지를 크롤링할 때 중복 url로 인한 페널티가 적용되게 하지 않게끔 도와주는 역할을 함)</p>
<h4 id="오픈-그래프open-graph-태그-활용">오픈 그래프(Open Graph) 태그 활용</h4>
<p>오픈 그래프 태그는 웹페이지의 링크가 카카오톡이나 기타 sns에서 공유될 때 어떻게 노출될지를 정의해주는 역할을 함.
검색엔진 최적화 과정에서 해당 웹페이지가 sns에서 얼마나 공유되고 있는지(혹은 얼마만큼 트래픽이 일어나고 있는지) 판단하는 기준이 되어 검색 상위노출을 위한 품질 평가에도 영향을 주기 때문에 중요한 역할을 함.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 리엑트 Deep Dive 스터디 02회차]]></title>
            <link>https://velog.io/@han_oal/%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%97%91%ED%8A%B8-Deep-Dive-%EC%8A%A4%ED%84%B0%EB%94%94-02%ED%9A%8C%EC%B0%A8</link>
            <guid>https://velog.io/@han_oal/%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%97%91%ED%8A%B8-Deep-Dive-%EC%8A%A4%ED%84%B0%EB%94%94-02%ED%9A%8C%EC%B0%A8</guid>
            <pubDate>Fri, 16 Aug 2024 08:09:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/han_oal/post/d87c2d44-7a7d-428d-bceb-36bc6aa3de1e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/han_oal/post/f2a6b857-be13-4a5d-b3f3-a24781f17b4e/image.png" alt=""></p>
<h1 id="15-17-까지의-정리">1.5~ 1.7 까지의 정리</h1>
<h2 id="✨1-5-이벤트-루프와-비동기-통신의-이해">✨1-5 이벤트 루프와 비동기 통신의 이해</h2>
<blockquote>
<p>자바스크립트는 싱글스레드 방식.
즉, 동기방식으로만 처리할 수 있다.</p>
</blockquote>
<blockquote>
<p>그러나 싱글 스레드 기반의 자바스크립트에서도 많은 양의 비동기 작업이 이루어짐.
이게 어떻게 가능한지 알기 위한 파트.</p>
</blockquote>
<h3 id="🔻싱글-스레드-자바스크립트">🔻싱글 스레드 자바스크립트</h3>
<blockquote>
<h4 id="스레드란">스레드란?</h4>
<p>스레드는 프로세스 내에서 실행되는 작은 단위의 작업. 하나의 프로세스는 여러 스레드를 포함할 수 있으며, 각 스레드는 독립적으로 작업을 수행할 수 있음.</p>
</blockquote>
<blockquote>
<p>스레드끼리 메모리를 공유할 수 있어 여러 가지 작업을 동시에 수행할 수 있다.</p>
</blockquote>
<blockquote>
<h4 id="왜-싱글-스레드로-설계되었을까">왜 싱글 스레드로 설계되었을까?</h4>
<p>멀티 스레드는 내부적으로 처리가 복잡하다는 단점 있음.
최초의 자바스클핍트는 브라우저에서 HTML을 그리는 데 한정적인 도움을 주는 보조적 역할.
설계 당시 현재처럼 웹페이지에서 벌어지는 다양한 일을 처리해야할 것으로 생각하지 못했을 것.</p>
</blockquote>
<h3 id="🔻이벤트-루프란">🔻이벤트 루프란?</h3>
<blockquote>
<h4 id="이벤트루프란">이벤트루프란?</h4>
<p>자바스크립트 런타임 외부에서 자바스크립트의 비동기 실행을 돕기위해 만들어진 장치.</p>
</blockquote>
<blockquote>
<h4 id="호출-스택이란">호출 스택이란?</h4>
<p>자바스크립트에서 수행해야 할 코드나 함수를 순차적으로 담아두는 스택.</p>
</blockquote>
<blockquote>
<p>이벤트 루프는 단순히 이벤트 루프만의 단일 스레드 내부에서 이 호출 스택 내부에 수행해야 할 작업이 있는지 확인하고, 수행해야 할 코드가 있다면 자바스크립트 엔진을 이용해 실행한다.</p>
</blockquote>
<blockquote>
<h4 id="태스크-큐란">태스크 큐란?</h4>
<p>실행해야 할 테스크의 집합을 의미
실행해야 할 테스크라는 것은 비동기 함수의 콜백 함수나 이벤트 핸들러 등을 의미함.</p>
</blockquote>
<h3 id="🔻태스크-큐와-마이크로-태스크-큐">🔻태스크 큐와 마이크로 태스크 큐</h3>
<blockquote>
<h4 id="마이크로-테스크-큐란">마이크로 테스크 큐란?</h4>
<p>이벤트 루프는 하나의 마이크로 태스크 큐를 갖고 있는데, 기존의 테스크 큐와는 다른 태스크를 처리한다. 여기에 들어가는 마이크로 태스크에는 대표적으로 Promise가 있다. </p>
</blockquote>
<blockquote>
<p>마이크로 태스크 큐는 기존 태스크 큐보다 우선권을 갖는다. 즉 setInterval은 Promise보다 늦게 실행됨. 마이크로 테스크 큐가 빌 때 까지는 기존 태스트 큐의 실행은 뒤로 미뤄짐.</p>
</blockquote>
<blockquote>
<h4 id="렌더링은-언제-실행될까">렌더링은 언제 실행될까?</h4>
<p>각 마이크로 태스크 큐 작업이 끝날 때 마다 한 번씩 렌더링 기회를 얻게 됨.</p>
</blockquote>
<blockquote>
<h4 id="결론적으로">결론적으로</h4>
<p>동기 코드는 물론이고 마이크로 테스크 또한 렌더링에 영양을 미칠 수 있기 때문에 이를 어떤 식으로 분리해서 사용자에게 좋은 애플리케이션 경험을 제공해 줄지 고민해 보아야 한다.</p>
</blockquote>
<hr>
<h2 id="✨1-6-리엑트에서-자주-사용하는-자바스크립트-문법">✨1-6 리엑트에서 자주 사용하는 자바스크립트 문법</h2>
<blockquote>
<p>일반적인 js 혹은 Node.js 기반의 코드와 비교했을 때 리엑트의 코드가 상대적으로 독특한 모습을 띄고있다. 리엑트의 독특한 특징을 이해하려면 리엑트에서 자주 사용되는 자바스크립트 문법을 이해해야 한다.</p>
</blockquote>
<h3 id="🔻구조-분해-할당">🔻구조 분해 할당</h3>
<blockquote>
<h4 id="구조분해할당destructuring">구조분해할당(Destructuring)</h4>
<p>배열 또는 객체의 값을 분해해 개별 변수에 즉시 할당하는 것을 의미.</p>
</blockquote>
<blockquote>
<p>주로 어떠한 객체나 배열에서 선언문 없이 즉시 분해해 변수를 선언하고 할당하고 싶을 때 사용.</p>
</blockquote>
<h3 id="🔻전개-구문">🔻전개 구문</h3>
<blockquote>
<h4 id="전개구문spread-syntax이란">전개구문(Spread Syntax)이란?</h4>
<p>배열이나 객체, 문자열과 같이 순회할 수 있는 값에 대해 전개해 간결하게 사용할 수 있는 구문.</p>
</blockquote>
<h3 id="🔻객체-초기자">🔻객체 초기자</h3>
<blockquote>
<h4 id="객체-초기자란">객체 초기자란?</h4>
<p>객체 초기자는 ECMAScript 2015에 도입된 기능으로, 객체를 선언할 때 객체에 넣고자 하는 키와 값을 가지고 있는 변수가 이미 존재한다면 해당 값을 간결하게 넣어줄 수 있는 방식</p>
</blockquote>
<h3 id="🔻array-프로토타입의-메서드">🔻Array 프로토타입의 메서드</h3>
<blockquote>
<h4 id="map">map</h4>
<p>인수로 전달받은 배열과 똑같은 길이의 새로운 배열을 반환하는 메서드.</p>
</blockquote>
<blockquote>
<h4 id="filter">filter</h4>
<p>콜백 함수를 인수로 받는데, 이 콜백 함수에서 truthy 조건을 만족하는 경우에만 해당 원소를 반환하는 메서드.</p>
</blockquote>
<blockquote>
<h4 id="reduce">reduce</h4>
<p>콜백 함수와 함께 초깃값을 추가로 인수를 받는데, 이 초깃값에 따라 배열이나 객체, 또는 그 외의 다른 무언가를 반환할 수 있는 메서드.</p>
</blockquote>
<blockquote>
<h4 id="foreach">forEach</h4>
<p>콜백함수를 받아 배열을 순회하면서 단순히 그 콜백함수를 실행시키기만 하는 메서드.</p>
</blockquote>
<h3 id="🔻삼항-조건-연산자">🔻삼항 조건 연산자</h3>
<blockquote>
<p>삼항 조건 연산자란?
자바스크립트에서 유일하게 3개의 피연산자를 취할 수 있는 문법.
특히 조건부로 렌더링하기 위해서 가장 널리 쓰이는 방법.</p>
</blockquote>
<hr>
<h2 id="✨1-7-선택이-아닌-필수-타입스크립트">✨1-7 선택이 아닌 필수, 타입스크립트</h2>
<h3 id="🔻타입스크립트란">🔻타입스크립트란?</h3>
<blockquote>
<h4 id="타입스크립트">타입스크립트?</h4>
<p>기존 자바스크립트 문법에 타입을 가미한 것.
자바스크립트는 동적 타입의 언어이기 떄문에 규모가 커질수록 발목을 잡는 경우도 있다. 이러한 것을 해결하기 위해 타입을 정적으로 선언해놓을 수 있게 하는 것이 타입스크립트이다.</p>
</blockquote>
<h3 id="🔻리액트-코드를-효과적으로-작성하기-위한-타입스크립트-활용법">🔻리액트 코드를 효과적으로 작성하기 위한 타입스크립트 활용법</h3>
<h3 id="🔻타입스크립트-전환-가이드">🔻타입스크립트 전환 가이드</h3>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[위클리페이퍼 13. ]]></title>
            <link>https://velog.io/@han_oal/%EC%9C%84%ED%81%B4%EB%A6%AC%ED%8E%98%EC%9D%B4%ED%8D%BC-13</link>
            <guid>https://velog.io/@han_oal/%EC%9C%84%ED%81%B4%EB%A6%AC%ED%8E%98%EC%9D%B4%ED%8D%BC-13</guid>
            <pubDate>Sun, 11 Aug 2024 23:27:57 GMT</pubDate>
            <description><![CDATA[<h2 id="❓리액트만-사용할-때와-비교해-nextjs를-사용하는-이유">❓리액트만 사용할 때와 비교해 Next.js를 사용하는 이유</h2>
<p><img src="https://velog.velcdn.com/images/han_oal/post/2c0e77cb-bcd8-4631-a467-fd9088c51542/image.png" alt=""></p>
<h3 id="✅1-서버-사이드-렌더링ssr과-정적-사이트-생성ssg">✅1. <strong>서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG)</strong></h3>
<ul>
<li><strong>React</strong>: 기본적으로 클라이언트 사이드에서 렌더링됩니다. 즉, JavaScript가 브라우저에서 실행된 후에야 페이지가 완전히 렌더링되므로 초기 로딩이 느릴 수 있고, 검색 엔진 최적화(SEO)에 불리할 수 있습니다.</li>
<li><strong>Next.js</strong>: 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG)을 지원합니다. SSR은 페이지를 서버에서 미리 렌더링하여 초기 로딩 속도를 개선하고, SEO에 유리한 환경을 제공합니다. SSG는 빌드 시점에 정적 HTML을 생성하여 더욱 빠른 페이지 로딩을 가능하게 합니다.</li>
</ul>
<h3 id="✅2-파일-기반-라우팅">✅2. <strong>파일 기반 라우팅</strong></h3>
<ul>
<li><strong>React</strong>: 라우팅을 위해 추가적으로 <code>react-router</code> 같은 라이브러리를 사용해야 하며, 라우트 설정을 명시적으로 구성해야 합니다.</li>
<li><strong>Next.js</strong>: 파일 기반 라우팅을 제공합니다. <code>pages/</code> 디렉토리에 파일을 추가하면 자동으로 해당 파일 이름이 라우트 경로로 설정됩니다. 이 방식은 라우팅 구성을 간편하게 만들고, 코드의 가독성을 높입니다.</li>
</ul>
<h3 id="✅3-api-routes">✅3. <strong>API Routes</strong></h3>
<ul>
<li><strong>React</strong>: 클라이언트 사이드에서 API 호출을 처리하지만, 이를 위해 백엔드 서버 또는 외부 API를 별도로 구축해야 합니다.</li>
<li><strong>Next.js</strong>: 간단한 백엔드 API를 <code>pages/api/</code> 디렉토리에서 설정할 수 있습니다. 이를 통해 서버리스 함수처럼 작동하는 API 엔드포인트를 쉽게 만들 수 있으며, 프론트엔드와 백엔드 코드가 한 프로젝트 내에 존재할 수 있어 유지보수성이 향상됩니다.</li>
</ul>
<h3 id="✅4-이미지-최적화">✅4. <strong>이미지 최적화</strong></h3>
<ul>
<li><strong>React</strong>: 이미지를 최적화하거나 로드할 때 별도의 라이브러리나 툴링이 필요합니다.</li>
<li><strong>Next.js</strong>: <code>next/image</code> 컴포넌트를 통해 자동으로 이미지 최적화를 제공하며, 다양한 장치와 화면 크기에 맞춰 이미지를 자동으로 리사이즈하고, 성능을 최적화합니다.</li>
</ul>
<h3 id="✅5-빌트인-css-및-sass-지원">✅5. <strong>빌트인 CSS 및 Sass 지원</strong></h3>
<ul>
<li><strong>React</strong>: 스타일링을 위해 CSS 파일을 직접 로드하거나, <code>styled-components</code>, <code>emotion</code> 같은 CSS-in-JS 라이브러리를 사용해야 합니다.</li>
<li><strong>Next.js</strong>: CSS와 Sass 파일을 네이티브로 지원하며, CSS 모듈도 기본적으로 사용할 수 있습니다. 또한, 글로벌 스타일을 쉽게 적용할 수 있는 설정을 제공합니다.</li>
</ul>
<h3 id="✅7-seo-최적화">✅7. <strong>SEO 최적화</strong></h3>
<ul>
<li><strong>React</strong>: 기본적으로 클라이언트 사이드 렌더링이기 때문에, SEO에 불리할 수 있습니다.</li>
<li><strong>Next.js</strong>: SSR, SSG를 통해 검색 엔진이 페이지를 크롤링하기 전에 HTML이 렌더링되어 있어, SEO에 매우 유리합니다. 또한, 메타 태그 설정 등을 보다 쉽게 관리할 수 있습니다.</li>
</ul>
<h2 id="nextjs에서-ssr을-실행하는-과정과-hydration에-대한-설명">Next.js에서 SSR을 실행하는 과정과 hydration에 대한 설명</h2>
<h3 id="✅1-서버-사이드-렌더링ssr-과정">✅1. <strong>서버 사이드 렌더링(SSR) 과정</strong></h3>
<p>![Pasted image 20240812031954.png]</p>
<p>서버 사이드 렌더링(SSR)은 사용자가 웹페이지에 접근할 때, 서버에서 페이지를 미리 렌더링하고 HTML을 생성하여 클라이언트에 전달하는 방식입니다.</p>
<h4 id="01클라이언트-요청">01.클라이언트 요청</h4>
<ul>
<li>사용자가 웹 애플리케이션에 접속하면 클라이언트는 서버에 요청을 전송합니다.</li>
</ul>
<h4 id="02서버에서-데이터-로드">02.서버에서 데이터 로드</h4>
<ul>
<li>서버는 해당 요청에 필요한 데이터를 로드합니다. 데이터는 API 호출, 데이터베이스 쿼리 등을 통해 가져올 수 있습니다.</li>
</ul>
<h4 id="03컴포넌트-렌더링">03.컴포넌트 렌더링</h4>
<ul>
<li>서버는 로드한 데이터와 함께 React 컴포넌트를 렌더링합니다. 이때, 컴포넌트는 사용자에게 보여질 최종 HTML을 생성하는데 사용됩니다.</li>
</ul>
<h4 id="04완성된-html-반환">04.완성된 HTML 반환</h4>
<ul>
<li>서버는 완성된 HTML을 클라이언트에게 반환합니다. 이 HTML에는 컴포넌트가 렌더링된 상태와 필요한 데이터가 포함됩니다.</li>
</ul>
<h4 id="05클라이언트-렌더링">05.클라이언트 렌더링</h4>
<ul>
<li>클라이언트는 받은 HTML을 브라우저에 렌더링합니다. 이후에는 클라이언트 측 JavaScript 코드가 실행되어 해당 페이지의 상호작용이 가능해집니다.</li>
</ul>
<p>페이지가 로드된 이후에는 사용자의 상호작용에 따라 클라이언트 측에서 추가적인 데이터 요청이 발생하고, 필요한 부분만 업데이트됩니다. 이는 일반적인 클라이언트 사이드 렌더링(CSR)과 유사합니다.</p>
<h3 id="✅2-hydration-과정">✅2. <strong>Hydration 과정</strong></h3>
<h2 id="hydration">Hydration</h2>
<p><img src="https://velog.velcdn.com/images/han_oal/post/62bd753a-d2a6-43b4-8ff8-5793db0a7e17/image.png" alt=""></p>
<p>Server-Side에서 렌더링 된 정적 페이지(HTML)와 번들링 된 JS 파일을 클라이언트에게 보내면, Client-Side에서 HTML 코드와 JS(React) 코드를 서로 매칭시키는 과정</p>
<blockquote>
<p><strong><em>Hydration</em></strong> 은 수분 공급이라는 의미를 갖는다.
👩‍🏫 수분기 없는 정적 웹 페이지에 물을 주는 느낌이라고 이해</p>
</blockquote>
<ul>
<li>JS 코드들이 DOM 요소 위에 물을 채우듯 필요로 하던 요소들을 채운다하여 붙여진 이름이라고 한다.</li>
</ul>
<p>Hydration은 클라이언트 사이드에서 React가 서버에서 렌더링된 HTML과 동기화되는 과정입니다. 이 과정은 아래와 같이 이루어집니다:</p>
<ol>
<li><p><strong>JavaScript 번들 다운로드</strong>:</p>
<ul>
<li>브라우저가 초기 HTML을 렌더링한 후, 서버에서 전달된 JavaScript 번들을 다운로드합니다. 이 번들은 React 컴포넌트를 포함하고 있습니다.</li>
</ul>
</li>
<li><p><strong>Hydration</strong>:</p>
<ul>
<li>다운로드된 JavaScript가 실행되면서, React는 서버에서 렌더링된 HTML과 자신의 가상 DOM(Virtual DOM)을 비교합니다.</li>
<li>이 비교를 통해 React는 어떤 부분이 이미 렌더링되었는지 파악하고, 이를 재사용합니다. 즉, 기존에 서버에서 렌더링된 HTML을 재사용하면서, React의 이벤트 핸들러나 상태 관리 기능이 활성화됩니다.</li>
</ul>
</li>
<li><p><strong>인터랙티브 페이지</strong>:</p>
<ul>
<li>Hydration이 완료되면, 페이지는 인터랙티브해집니다. 즉, 사용자가 버튼을 클릭하거나 폼을 제출하는 등 다양한 인터랙션을 할 수 있습니다.</li>
<li>이 과정에서 필요에 따라 React는 DOM을 업데이트합니다.</li>
</ul>
</li>
</ol>
<h3 id="✅3-ssr과-hydration의-중요성">✅3. <strong>SSR과 Hydration의 중요성</strong></h3>
<ul>
<li><p><strong>SSR의 장점</strong>:</p>
<ul>
<li><strong>빠른 초기 로드</strong>: 서버에서 미리 렌더링된 HTML을 제공하므로, 사용자는 초기 로드 시 빈 화면을 보지 않고 바로 콘텐츠를 볼 수 있습니다.</li>
<li><strong>SEO 최적화</strong>: 서버에서 완전한 HTML을 생성하여 검색 엔진이 페이지를 크롤링하기 쉽도록 합니다. 이는 특히 검색 엔진 최적화(SEO)에서 중요합니다.</li>
</ul>
</li>
<li><p><strong>Hydration의 역할</strong>:</p>
<ul>
<li><strong>동적 기능 추가</strong>: SSR로만 제공된 페이지는 정적인 HTML이므로, 사용자의 상호작용에 반응하지 않습니다. Hydration을 통해 페이지가 인터랙티브하게 변하고, React의 동적인 기능을 사용할 수 있게 됩니다.</li>
</ul>
</li>
</ul>
<h3 id="요약">요약</h3>
<ul>
<li><strong>SSR</strong>: 서버에서 HTML을 생성해 클라이언트에 제공하여 빠른 로딩과 SEO를 향상시킵니다.</li>
<li><strong>Hydration</strong>: 클라이언트에서 React가 서버에서 렌더링된 HTML과 동기화하여 페이지를 인터랙티브하게 만듭니다.</li>
</ul>
<p>이 두 과정이 결합되어, 사용자에게 빠르고 동적인 웹 경험을 제공합니다.</p>
]]></description>
        </item>
    </channel>
</rss>