<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>young-jev.log</title>
        <link>https://velog.io/</link>
        <description>프론트엔드 뭐시기 주로 하는 사람</description>
        <lastBuildDate>Wed, 12 Feb 2025 05:12:02 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>young-jev.log</title>
            <url>https://velog.velcdn.com/images/young-jev/profile/53821c06-6759-425c-95fb-3f127f2f6528/image.webp</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. young-jev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/young-jev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[로컬에서 GitHub Actions 실행하는 법]]></title>
            <link>https://velog.io/@young-jev/%EB%A1%9C%EC%BB%AC%EC%97%90%EC%84%9C-GitHub-Actions-%EC%8B%A4%ED%96%89%ED%95%98%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@young-jev/%EB%A1%9C%EC%BB%AC%EC%97%90%EC%84%9C-GitHub-Actions-%EC%8B%A4%ED%96%89%ED%95%98%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Wed, 12 Feb 2025 05:12:02 GMT</pubDate>
            <description><![CDATA[<p>GitHub Actions는 CI/CD를 자동화하는 강력한 도구이지만, 워크플로우를 실행할 때마다 GitHub에 푸시해야 한다면 시간이 낭비될 수 있습니다. 이를 해결하기 위해, 로컬에서 GitHub Actions를 실행하는 방법을 알아보겠습니다.</p>
<h2 id="1-act-github-actions를-로컬에서-실행하기">1. act: GitHub Actions를 로컬에서 실행하기</h2>
<p><a href="https://github.com/nektos/act">act</a>는 GitHub Actions를 로컬에서 실행할 수 있도록 해주는 도구입니다. 이를 사용하면 GitHub의 실행 환경을 시뮬레이션하여 테스트할 수 있습니다.</p>
<h3 id="11-act-설치">1.1 act 설치</h3>
<h4 id="macos">macOS</h4>
<pre><code class="language-sh">brew install act</code></pre>
<h4 id="windows-scoop-사용">Windows (Scoop 사용)</h4>
<pre><code class="language-sh">scoop install act</code></pre>
<h4 id="linux-curl-사용">Linux (curl 사용)</h4>
<pre><code class="language-sh">curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash</code></pre>
<p>또는, <a href="https://github.com/nektos/act/releases">GitHub Releases</a>에서 바이너리를 다운로드하여 사용할 수도 있습니다.</p>
<h3 id="12-act-실행하기">1.2 act 실행하기</h3>
<p>설치가 완료되면, GitHub Actions을 실행할 디렉토리로 이동하여 다음 명령어를 실행합니다.</p>
<pre><code class="language-sh">act</code></pre>
<p>기본적으로 <code>.github/workflows/</code> 폴더 내의 워크플로우를 찾아 실행합니다.</p>
<h3 id="13-특정-이벤트-트리거로-실행하기">1.3 특정 이벤트 트리거로 실행하기</h3>
<pre><code class="language-sh">act push  # push 이벤트 실행
act pull_request  # pull_request 이벤트 실행
act -l  # 실행 가능한 이벤트 목록 확인</code></pre>
<h3 id="14-특정-워크플로우-실행하기">1.4 특정 워크플로우 실행하기</h3>
<p>워크플로우 파일이 <code>.github/workflows/eas-update.yml</code>로 구성되어 있을 경우, 아래 명령어로 실행할 수 있습니다.</p>
<pre><code class="language-sh">act workflow_dispatch -W .github/workflows/eas-update.yml \
  -s EXPO_TOKEN=mytoken \
  -P ubuntu-latest=catthehacker/ubuntu:act-latest \
  --container-architecture linux/amd64 </code></pre>
<h3 id="15-필요한-docker-이미지-설정하기">1.5 필요한 Docker 이미지 설정하기</h3>
<p>act는 기본적으로 <code>medium</code> 사이즈의 Docker 이미지를 사용합니다. 더 가벼운 이미지를 사용하려면 <code>-P</code> 옵션을 이용하세요.</p>
<pre><code class="language-sh">act -P ubuntu-latest=nektos/act-environments-ubuntu:18.04</code></pre>
<h2 id="2-act-사용-시-주의할-점">2. act 사용 시 주의할 점</h2>
<h3 id="21-github-secrets-처리">2.1 GitHub Secrets 처리</h3>
<p>GitHub Actions에서 <code>secrets.GITHUB_TOKEN</code>과 같은 환경 변수를 사용하는 경우, 로컬에서 실행하려면 <code>.secrets</code> 파일을 만들어야 합니다.</p>
<pre><code class="language-sh">echo &quot;GITHUB_TOKEN=mytoken&quot; &gt; .secrets
act --secret-file .secrets</code></pre>
<h3 id="22-필요한-툴이-설치되어-있어야-함">2.2 필요한 툴이 설치되어 있어야 함</h3>
<p>act는 Docker 컨테이너 내에서 실행되므로, Docker가 반드시 설치되어 있어야 합니다. 또한, GitHub Actions에서 사용하는 도구(예: Node.js, Python 등)가 로컬 환경에도 있어야 합니다.</p>
<h2 id="3-마무리">3. 마무리</h2>
<p>act를 사용하면 GitHub Actions를 로컬에서 빠르게 테스트하고 디버깅할 수 있습니다. 이를 활용하면 불필요한 푸시를 줄이고, 빠른 피드백을 얻을 수 있습니다.</p>
<p>더 자세한 사항은 <a href="https://github.com/nektos/act">act 공식 문서</a>를 참고하세요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS position: fixed가 항상 화면 기준이 아닌 이유와 예외 상황]]></title>
            <link>https://velog.io/@young-jev/CSS-position-fixed%EA%B0%80-%ED%95%AD%EC%83%81-%ED%99%94%EB%A9%B4-%EA%B8%B0%EC%A4%80%EC%9D%B4-%EC%95%84%EB%8B%8C-%EC%9D%B4%EC%9C%A0%EC%99%80-%EC%98%88%EC%99%B8-%EC%83%81%ED%99%A9</link>
            <guid>https://velog.io/@young-jev/CSS-position-fixed%EA%B0%80-%ED%95%AD%EC%83%81-%ED%99%94%EB%A9%B4-%EA%B8%B0%EC%A4%80%EC%9D%B4-%EC%95%84%EB%8B%8C-%EC%9D%B4%EC%9C%A0%EC%99%80-%EC%98%88%EC%99%B8-%EC%83%81%ED%99%A9</guid>
            <pubDate>Thu, 09 Jan 2025 04:24:37 GMT</pubDate>
            <description><![CDATA[<p><code>position: fixed</code>는 일반적으로 <strong>화면(viewport)</strong>을 기준으로 동작하지만, 특정 상황에서는 예상과 다르게 동작할 수 있습니다. 이 글에서는 <code>position: fixed</code>의 기본 동작과 예외 상황을 살펴보겠습니다.</p>
<hr>
<h2 id="기본-동작">기본 동작</h2>
<p><code>position: fixed</code>는 화면을 기준으로 요소를 고정합니다. 스크롤을 하더라도 요소는 제자리에서 움직이지 않습니다. 다음은 기본적인 예제입니다.</p>
<pre><code class="language-css">.fixed-element {
    position: fixed;
    top: 10px;
    left: 10px;
}</code></pre>
<p>위 코드를 적용하면, .fixed-element는 화면의 좌측 상단에서 항상 10px 떨어진 위치에 고정됩니다.</p>
<h2 id="예외-상황">예외 상황</h2>
<p>특정 CSS 속성이나 환경에서는 fixed가 화면(viewport)을 기준으로 동작하지 않을 수 있습니다.</p>
<p><strong>1. <code>transform</code>, <code>perspective</code>, <code>filter</code>의 영향을 받을 때</strong>
<code>fixed</code> 요소가 <code>transform</code>, <code>perspective</code>, 또는 <code>filter</code> 속성이 적용된 부모 요소의 자식일 경우, 화면이 아닌 해당 부모 요소를 기준으로 동작합니다.</p>
<pre><code class="language-css">.parent {
    transform: translate(0, 0);
}

.child {
    position: fixed;
    top: 10px;
    left: 10px;
}</code></pre>
<p>이 경우, <code>.child</code>는 부모 요소 <code>.parent</code>를 기준으로 위치가 고정됩니다. 이는 fixed가 viewport 대신 부모의 <strong>containing block</strong>에 영향을 받기 때문입니다.</p>
<p><strong>2. 모바일 환경에서의 특수 동작</strong>
모바일 브라우저에서는 <code>fixed</code>가 예상과 다르게 동작하는 경우가 종종 있습니다.
예를 들어, 소프트 키보드가 나타나거나 화면 크기가 변경될 때 fixed 요소가 재배치되지 않는 문제가 발생할 수 있습니다.</p>
<h2 id="마무리">마무리</h2>
<p><code>position: fixed</code>는 일반적으로 화면을 기준으로 동작하지만, <code>transform</code>, <code>perspective</code>, <code>filter</code> 속성의 영향을 받을 경우 부모 요소를 기준으로 동작할 수 있습니다. 이러한 특성을 이해하고 CSS를 작성하면 예상치 못한 동작을 방지할 수 있습니다.</p>
<p>웹 개발에서 작은 차이가 큰 문제를 초래할 수 있으므로, 레이아웃 설계 시 이러한 예외 상황을 고려하는 것이 중요합니다.</p>
<p>출처: <a href="https://developer.mozilla.org/ko/docs/Web/CSS/position#fixed">https://developer.mozilla.org/ko/docs/Web/CSS/position#fixed</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AV1이 실시간 스트리밍에 적합할까? VP8, VP9과의 차이점 분석]]></title>
            <link>https://velog.io/@young-jev/AV1%EC%9D%B4-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D%EC%97%90-%EC%A0%81%ED%95%A9%ED%95%A0%EA%B9%8C-VP8-VP9%EA%B3%BC%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@young-jev/AV1%EC%9D%B4-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D%EC%97%90-%EC%A0%81%ED%95%A9%ED%95%A0%EA%B9%8C-VP8-VP9%EA%B3%BC%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Thu, 12 Dec 2024 04:18:02 GMT</pubDate>
            <description><![CDATA[<h1 id="실시간-스트리밍에서-vp8-vp9-av1-코덱-비교"><strong>실시간 스트리밍에서 VP8, VP9, AV1 코덱 비교</strong></h1>
<p>실시간 스트리밍 플랫폼에서 코덱 선택은 서비스 품질, 비용 효율성, 사용자 경험을 좌우하는 중요한 요소입니다. 이번 포스트에서는 VP8, VP9, 그리고 AV1 코덱을 비교하며, 특히 하드웨어 지원이 되는 디바이스만 사용할 경우 <strong>AV1의 장점과 한계</strong>를 중심으로 살펴보겠습니다.</p>
<hr>
<h2 id="1-코덱별-특징"><strong>1. 코덱별 특징</strong></h2>
<h3 id="vp8"><strong>VP8</strong></h3>
<ul>
<li><strong>출시 시기</strong>: 2010년 (Google)</li>
<li><strong>장점</strong>:<ul>
<li>간단한 구조로 인코딩 및 디코딩 속도가 빠름.</li>
<li>다양한 기기에서 널리 지원됨.</li>
<li>실시간 스트리밍에 적합 (낮은 지연 시간).</li>
</ul>
</li>
<li><strong>단점</strong>:<ul>
<li>최신 코덱에 비해 효율성이 낮음 (대역폭 소비가 큼).</li>
</ul>
</li>
</ul>
<hr>
<h3 id="vp9"><strong>VP9</strong></h3>
<ul>
<li><strong>출시 시기</strong>: 2013년 (Google)</li>
<li><strong>장점</strong>:<ul>
<li>VP8 대비 약 30%-50% 대역폭 절감.</li>
<li>4K 콘텐츠를 위한 우수한 품질 제공.</li>
<li>주요 플랫폼과 브라우저에서 폭넓게 지원됨.</li>
<li>실시간 스트리밍에서도 적절한 성능 발휘.</li>
</ul>
</li>
<li><strong>단점</strong>:<ul>
<li>하드웨어 가속 지원이 제한적이었던 과거 디바이스에서 성능 저하.</li>
<li>AV1 대비 대역폭 효율성 부족.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="av1"><strong>AV1</strong></h3>
<ul>
<li><strong>출시 시기</strong>: 2018년 (Alliance for Open Media)</li>
<li><strong>장점</strong>:<ul>
<li>VP9 대비 약 30%, H.265 대비 약 50% 대역폭 절감.</li>
<li>무료 라이선스 (운영 비용 절감).</li>
<li>4K, 8K 콘텐츠를 위한 최고의 품질과 효율성.</li>
</ul>
</li>
<li><strong>단점</strong>:<ul>
<li>인코딩 속도가 느림 (특히 소프트웨어 기반).</li>
<li>하드웨어 가속이 없는 기기에서는 성능 저하 및 높은 지연 발생.</li>
<li>지원 디바이스 범위가 제한적.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-비용적인-측면"><strong>2. 비용적인 측면</strong></h2>
<ul>
<li><p><strong>VP8 &amp; VP9</strong>:</p>
<ul>
<li>모두 무료 라이선스 제공.</li>
<li>VP8은 상대적으로 대역폭 효율성이 낮아 네트워크 비용이 증가할 수 있음.</li>
<li>VP9은 VP8 대비 대역폭 절감 효과로 비용 효율성 우수.</li>
</ul>
</li>
<li><p><strong>AV1</strong>:</p>
<ul>
<li>무료 라이선스로 장기적인 네트워크 비용 절감 가능.</li>
<li>높은 대역폭 효율성으로 대규모 트래픽이 발생하는 서비스에서 유리.</li>
<li>초기 인코딩 인프라 투자 및 하드웨어 가속 필요.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-실시간-스트리밍-플랫폼에서의-비교"><strong>3. 실시간 스트리밍 플랫폼에서의 비교</strong></h2>
<h3 id="av1의-강점"><strong>AV1의 강점</strong></h3>
<ol>
<li><p><strong>최고의 대역폭 효율성</strong>:</p>
<ul>
<li>4K, 8K 해상도에서 낮은 대역폭으로 고품질 제공.</li>
<li>장기적으로 네트워크 비용 절감.</li>
</ul>
</li>
<li><p><strong>최신 디바이스에서 뛰어난 성능</strong>:</p>
<ul>
<li>하드웨어 가속 지원 기기에서는 낮은 지연 시간과 빠른 디코딩 가능.</li>
</ul>
</li>
<li><p><strong>무료 라이선스</strong>:</p>
<ul>
<li>H.265 등 유료 코덱 대비 비용 절감 효과.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="av1의-단점"><strong>AV1의 단점</strong></h3>
<ol>
<li><p><strong>하드웨어 지원 제한</strong>:</p>
<ul>
<li>최신 디바이스에서만 원활하게 동작.</li>
<li>구형 기기에서는 소프트웨어 디코딩으로 성능 저하.</li>
</ul>
</li>
<li><p><strong>인코딩 속도 문제</strong>:</p>
<ul>
<li>실시간 스트리밍에서 <strong>지연(latency)</strong> 증가 가능.</li>
</ul>
</li>
<li><p><strong>운영 복잡성 증가</strong>:</p>
<ul>
<li>AV1을 지원하지 않는 기기를 위해 VP8 또는 VP9 같은 백업 코덱이 필요.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="4-하드웨어-지원-디바이스만-사용할-경우"><strong>4. 하드웨어 지원 디바이스만 사용할 경우</strong></h2>
<h3 id="av1이-유리한-조건"><strong>AV1이 유리한 조건</strong></h3>
<ol>
<li><p><strong>사용자 기반이 최신 디바이스 중심</strong>:</p>
<ul>
<li>최신 스마트폰, PC, 스트리밍 기기를 사용하는 경우 AV1의 장점 극대화.</li>
</ul>
</li>
<li><p><strong>고품질 콘텐츠 제공</strong>:</p>
<ul>
<li>4K, 8K 콘텐츠를 낮은 대역폭으로 전송 가능.</li>
</ul>
</li>
<li><p><strong>대규모 트래픽 비용 절감</strong>:</p>
<ul>
<li>대역폭이 중요한 스트리밍 서비스에서 비용 절감 효과.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="av1이-한계가-있는-조건"><strong>AV1이 한계가 있는 조건</strong></h3>
<ol>
<li><p><strong>저지연 실시간 스트리밍</strong>:</p>
<ul>
<li>인코딩 지연 문제로 실시간 서비스에 부적합할 수 있음.</li>
</ul>
</li>
<li><p><strong>사용자 디바이스가 다양할 경우</strong>:</p>
<ul>
<li>하드웨어 지원이 부족한 구형 디바이스에서는 성능 문제가 발생.</li>
</ul>
</li>
<li><p><strong>멀티코덱 운영 필요</strong>:</p>
<ul>
<li>AV1과 함께 VP9/VP8을 병렬적으로 운영해야 하므로 관리 복잡도 증가.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="5-결론"><strong>5. 결론</strong></h2>
<h3 id="av1이-적합한-환경"><strong>AV1이 적합한 환경</strong></h3>
<ul>
<li><strong>최신 하드웨어 중심의 서비스</strong>:<ul>
<li>예: 최신 디바이스를 사용하는 프리미엄 구독 서비스.</li>
</ul>
</li>
<li><strong>고품질 콘텐츠 제공이 중요한 플랫폼</strong>:<ul>
<li>예: 4K, 8K 콘텐츠 중심의 OTT 서비스.</li>
</ul>
</li>
<li><strong>장기적인 대역폭 비용 절감이 목표</strong>:<ul>
<li>예: 대규모 글로벌 스트리밍 플랫폼.</li>
</ul>
</li>
</ul>
<h3 id="vp9vp8이-적합한-환경"><strong>VP9/VP8이 적합한 환경</strong></h3>
<ul>
<li><strong>저지연 실시간 스트리밍</strong>:<ul>
<li>AV1의 인코딩 속도 문제를 해결할 수 없을 경우.</li>
</ul>
</li>
<li><strong>다양한 사용자 디바이스 지원이 필요한 플랫폼</strong>:<ul>
<li>구형 디바이스까지 아우르는 서비스.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="추천-전략"><strong>추천 전략</strong></h2>
<ol>
<li><strong>최신 디바이스에서 AV1을 우선 활용</strong>:<ul>
<li>고품질 콘텐츠에 AV1을 적용.</li>
</ul>
</li>
<li><strong>백업 코덱으로 VP9 운영</strong>:<ul>
<li>AV1을 지원하지 않는 기기에 대응.</li>
</ul>
</li>
<li><strong>실시간 스트리밍은 VP9 중심</strong>:<ul>
<li>실시간성과 낮은 지연 시간을 요구하는 경우 VP9이 최적.</li>
</ul>
</li>
</ol>
<hr>
<p>이 글이 실시간 스트리밍 플랫폼에서 코덱 선택에 도움이 되길 바랍니다! 서비스 환경에 맞는 최적의 코덱을 선택해 높은 품질과 비용 효율성을 모두 잡으세요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Tree Shaking이란?]]></title>
            <link>https://velog.io/@young-jev/Tree-Shaking%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@young-jev/Tree-Shaking%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Sun, 20 Oct 2024 12:18:06 GMT</pubDate>
            <description><![CDATA[<h1 id="트리-쉐이킹-tree-shaking">트리 쉐이킹 (Tree Shaking)</h1>
<p>트리 쉐이킹은 사용되지 않는 코드를 제거하여 최종 번들 크기를 줄이는 기법입니다. 주로 모듈 번들러(예: Webpack, Rollup)와 함께 사용되며, 특히 자바스크립트 환경에서 중요하게 여겨집니다. 이 문서에서는 트리 쉐이킹의 개념, 작동 방식, 이점, 주의 사항 등을 설명합니다.</p>
<h2 id="1-트리-쉐이킹의-개념">1. 트리 쉐이킹의 개념</h2>
<p>트리 쉐이킹은 프로그래밍에서 &quot;트리&quot; 구조를 비유적으로 사용하여, 사용되지 않는 코드를 마치 나무의 잎사귀처럼 제거하는 과정을 의미합니다. 이를 통해 애플리케이션의 번들 크기를 줄이고, 성능을 향상시킬 수 있습니다.</p>
<h2 id="2-작동-방식">2. 작동 방식</h2>
<p>트리 쉐이킹은 주로 <strong>정적 분석</strong>을 통해 이루어집니다. 모듈 번들러가 코드를 분석하여 사용되지 않는 코드의 종속성을 찾고 이를 제거합니다. 이 과정에서 중요한 요소는 다음과 같습니다:</p>
<ul>
<li><strong>ES6 모듈</strong>: 트리 쉐이킹은 주로 ES6의 <code>import</code>와 <code>export</code> 구문을 사용하는 모듈에서 효과적으로 작동합니다. CommonJS 모듈 시스템에서는 트리 쉐이킹이 제한적일 수 있습니다.</li>
<li><strong>정적 분석</strong>: 코드가 어떻게 사용되는지를 정적으로 분석하여 실제로 사용되지 않는 부분을 식별합니다.</li>
</ul>
<h3 id="예시">예시</h3>
<pre><code class="language-javascript">// utils.js
export function usedFunction() {
    console.log(&quot;I am used!&quot;);
}

export function unusedFunction() {
    console.log(&quot;I am unused!&quot;);
}</code></pre>
<pre><code class="language-javascript">// main.js
import { usedFunction } from &#39;./utils&#39;;

usedFunction(); // &quot;I am used!&quot; 출력</code></pre>
<p>위 예시에서 <code>unusedFunction</code>은 <code>main.js</code>에서 사용되지 않기 때문에, 트리 쉐이킹을 통해 최종 번들에서 제거될 수 있습니다.</p>
<h2 id="3-이점">3. 이점</h2>
<ul>
<li><strong>번들 크기 감소</strong>: 사용되지 않는 코드를 제거하여 최종 번들의 크기를 줄입니다.</li>
<li><strong>성능 향상</strong>: 로딩 시간이 단축되고, 페이지의 초기 렌더링 성능이 개선됩니다.</li>
<li><strong>코드 가독성</strong>: 코드의 복잡성을 줄이고, 불필요한 부분을 제거하여 가독성을 높입니다.</li>
</ul>
<h2 id="4-주의-사항">4. 주의 사항</h2>
<ul>
<li><strong>라이브러리 호환성</strong>: 모든 라이브러리나 프레임워크가 트리 쉐이킹을 지원하는 것은 아닙니다. ES6 모듈을 지원하는 라이브러리를 사용하는 것이 중요합니다.</li>
<li><strong>동적 import</strong>: 동적으로 불러오는 코드의 경우 트리 쉐이킹이 어려울 수 있습니다. 사용 여부가 명확하지 않은 경우, 해당 코드가 번들에 남아있게 됩니다.</li>
<li><strong>enum과의 관계</strong>: TypeScript의 <code>enum</code>과 같이 런타임 객체로 변환되는 코드는 트리 쉐이킹이 효과적으로 작동하지 않을 수 있습니다.</li>
</ul>
<h2 id="결론">결론</h2>
<p>트리 쉐이킹은 애플리케이션의 성능을 최적화하는 중요한 기법입니다. ES6 모듈을 활용하고, 사용되지 않는 코드를 적극적으로 제거함으로써 최종 번들의 크기를 줄이고 성능을 향상시킬 수 있습니다. 트리 쉐이킹을 올바르게 활용하면, 효율적인 애플리케이션 개발에 큰 도움이 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript에서 Enum을 사용하면 안 되는 이유]]></title>
            <link>https://velog.io/@young-jev/TypeScript%EC%97%90%EC%84%9C-Enum%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%95%88-%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@young-jev/TypeScript%EC%97%90%EC%84%9C-Enum%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%95%88-%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Sun, 20 Oct 2024 12:13:26 GMT</pubDate>
            <description><![CDATA[<h1 id="typescript의-enum-사용을-피해야-하는-이유-트리-쉐이킹">TypeScript의 Enum 사용을 피해야 하는 이유: 트리 쉐이킹</h1>
<p>TypeScript의 <code>enum</code>은 코드의 가독성을 높이는 데 유용하지만, 성능 최적화 측면에서 몇 가지 단점을 가지고 있습니다. 특히 트리 쉐이킹(Tree Shaking)과 관련하여 <code>enum</code> 사용을 피해야 하는 이유에 대해 알아보겠습니다.</p>
<h2 id="1-트리-쉐이킹의-개념">1. 트리 쉐이킹의 개념</h2>
<p>트리 쉐이킹은 사용되지 않는 코드를 제거하여 최종 번들 크기를 줄이는 기법입니다. 주로 ES6 모듈을 사용하는 환경에서 효과적으로 작동하며, 모듈 번들러(예: Webpack, Rollup)가 이를 수행합니다. 사용되지 않는 코드가 번들에 포함되지 않도록 정적 분석을 통해 이루어집니다.</p>
<h2 id="2-enum의-구조와-동작-방식">2. Enum의 구조와 동작 방식</h2>
<p>TypeScript의 <code>enum</code>은 컴파일될 때 런타임 객체로 변환됩니다. 이로 인해 <code>enum</code>의 모든 값이 최종 번들에 포함됩니다. 예를 들어, 아래의 <code>enum</code> 예시를 살펴보겠습니다:</p>
<pre><code class="language-typescript">enum Status {
    Pending = &quot;pending&quot;,
    Approved = &quot;approved&quot;,
    Rejected = &quot;rejected&quot;,
}</code></pre>
<p>위의 코드에서 <code>Status</code> enum은 세 개의 문자열 값을 포함합니다. 코드에서 <code>Status</code> enum의 일부 값만 사용하더라도, 모든 값이 최종 번들에 포함될 수 있습니다.</p>
<h2 id="3-트리-쉐이킹과-enum의-문제점">3. 트리 쉐이킹과 Enum의 문제점</h2>
<ul>
<li><strong>런타임 객체</strong>: <code>enum</code>은 런타임에 존재하는 객체이므로, 사용되지 않는 값이 코드에 남아있게 됩니다. 이는 트리 쉐이킹이 제대로 작동하지 않는 원인이 됩니다.</li>
<li><strong>정적 분석의 한계</strong>: <code>enum</code>의 값들이 런타임에 필요하기 때문에, 사용되지 않는 값이 정적으로 분석되기 어렵습니다. 이로 인해 최종 번들에서 제거되지 않고 포함될 수 있습니다.</li>
</ul>
<h3 id="예시">예시</h3>
<pre><code class="language-typescript">function getStatusMessage(status: Status) {
    switch (status) {
        case Status.Pending:
            return &quot;Your request is pending.&quot;;
        case Status.Approved:
            return &quot;Your request has been approved.&quot;;
        case Status.Rejected:
            return &quot;Your request has been rejected.&quot;;
        default:
            return &quot;Unknown status.&quot;;
    }
}</code></pre>
<p>위 코드에서 <code>getStatusMessage</code> 함수는 <code>Status</code> enum의 값 중 일부만 사용할 수 있지만, 모든 값이 번들에 포함됩니다.</p>
<h2 id="4-대안-리터럴-타입-및-유니언-타입">4. 대안: 리터럴 타입 및 유니언 타입</h2>
<p>트리 쉐이킹을 최적화하기 위해 <code>enum</code> 대신 리터럴 타입과 유니언 타입을 사용하는 것이 좋습니다. 이를 통해 사용되지 않는 코드의 포함을 방지할 수 있습니다.</p>
<pre><code class="language-typescript">type Status = &quot;pending&quot; | &quot;approved&quot; | &quot;rejected&quot;;

function getStatusMessage(status: Status) {
    switch (status) {
        case &quot;pending&quot;:
            return &quot;Your request is pending.&quot;;
        case &quot;approved&quot;:
            return &quot;Your request has been approved.&quot;;
        case &quot;rejected&quot;:
            return &quot;Your request has been rejected.&quot;;
        default:
            return &quot;Unknown status.&quot;;
    }
}</code></pre>
<p>위와 같이 리터럴 타입을 사용하면, 코드의 가독성을 유지하면서도 트리 쉐이킹의 이점을 극대화할 수 있습니다.</p>
<h2 id="결론">결론</h2>
<p>TypeScript의 <code>enum</code>은 편리하지만, 트리 쉐이킹과 같은 성능 최적화 측면에서 문제가 될 수 있습니다. 코드의 최적화를 위해서는 리터럴 타입과 유니언 타입을 사용하는 것이 좋습니다. 이를 통해 최종 번들의 크기를 줄이고, 성능을 향상시킬 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트에서의 다중 상속]]></title>
            <link>https://velog.io/@young-jev/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C%EC%9D%98-%EB%8B%A4%EC%A4%91-%EC%83%81%EC%86%8D</link>
            <guid>https://velog.io/@young-jev/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C%EC%9D%98-%EB%8B%A4%EC%A4%91-%EC%83%81%EC%86%8D</guid>
            <pubDate>Thu, 22 Aug 2024 01:28:08 GMT</pubDate>
            <description><![CDATA[<p>최근에 면접을 봤는데 프로토타입, 자바스크립트의 다중 상속에 관해서 질문을 하시더라구요... 프로토타입은 익숙한 개념이었지만 다중 상속은 무지했습니다.. 그래서 글을 정리합니다</p>
<p>자바스크립트는 프로토타입 기반의 객체 지향 언어로, 다른 객체지향 언어들처럼 명시적인 class 기반 다중 상속을 직접 지원하지는 않습니다. 하지만, 자바스크립트는 몇 가지 다른 방법으로 다중 상속을 시뮬레이션할 수 있습니다. 이번 글에서는 자바스크립트에서 다중 상속을 구현하는 일반적인 방법들에 대해 알아보겠습니다.</p>
<h3 id="1-믹스인mixin-패턴">1. 믹스인(Mixin) 패턴</h3>
<p>믹스인은 하나의 함수나 객체에서 다른 객체의 메서드나 속성을 &quot;혼합&quot;하여 사용하는 패턴입니다. 이는 자바스크립트에서 다중 상속을 구현하는 가장 일반적인 방법 중 하나입니다.</p>
<pre><code class="language-javascript">
function mixin(target, ...sources) {
  Object.assign(target, ...sources);
}

const canEat = {
  eat() {
    console.log(&#39;Eating&#39;);
  }
};

const canWalk = {
  walk() {
    console.log(&#39;Walking&#39;);
  }
};

const canSwim = {
  swim() {
    console.log(&#39;Swimming&#39;);
  }
};

class Person {
  constructor(name) {
    this.name = name;
  }
}

mixin(Person.prototype, canEat, canWalk, canSwim);

const person = new Person(&#39;John&#39;);
person.eat(); // Eating
person.walk(); // Walking
person.swim(); // Swimming</code></pre>
<p>이 방법을 통해 Person 객체는 canEat, canWalk, canSwim의 기능을 모두 상속받아 사용할 수 있습니다.</p>
<h3 id="2-프로토타입-체인을-통한-복합-상속">2. 프로토타입 체인을 통한 복합 상속</h3>
<p>다른 방법으로는 프로토타입 체인을 사용하여 기능을 상속받을 수도 있습니다. 이 방법은 믹스인과 유사하지만, 객체의 프로토타입 체인을 직접 조작하여 상속을 설정합니다.</p>
<pre><code class="language-javascript">
function inherit(target, ...sources) {
  sources.forEach(source =&gt; {
    Object.getOwnPropertyNames(source).forEach(name =&gt; {
      target[name] = source[name];
    });
  });
  return target;
}

class Animal {
  breathe() {
    console.log(&#39;Breathing&#39;);
  }
}

const swimmer = {
  swim() {
    console.log(&#39;Swimming&#39;);
  }
};

class Fish {
  constructor() {
    inherit(this, new Animal());
  }
}

inherit(Fish.prototype, swimmer);

const fish = new Fish();
fish.breathe(); // Breathing
fish.swim(); // Swimming</code></pre>
<h3 id="3-결론">3. 결론</h3>
<p>자바스크립트에서는 다른 객체지향 언어처럼 다중 상속을 직접적으로 지원하지 않지만, 믹스인이나 프로토타입 조작과 같은 방법을 통해 다중 상속을 간접적으로 구현할 수 있습니다. 이러한 패턴들을 활용하면 재사용 가능한 코드를 작성하고 객체 간에 기능을 공유하는 것이 가능해집니다. 다만, 구체적인 구현 방식을 선택할 때에는 코드의 가독성과 유지보수성을 고려해야 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[공달이 소개 ]]></title>
            <link>https://velog.io/@young-jev/%EA%B3%B5%EB%8B%AC%EC%9D%B4-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@young-jev/%EA%B3%B5%EB%8B%AC%EC%9D%B4-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Mon, 22 Jul 2024 09:49:45 GMT</pubDate>
            <description><![CDATA[<img src='https://velog.velcdn.com/images/young-jev/post/6ee42297-4441-4ed7-b0b7-bc952d1c64f7/image.png' />

<h1 id="공달이---공유-달력--gongdal2">공달이 - 공유 달력 | GongDal2</h1>
<blockquote>
<p><strong>일정 관리, 일정 공유 앱</strong></p>
</blockquote>
<p>소그룹, 동아리, 회사, 학교 등 다양한 그룹 모임이 많아지는 시대에 맞춰 개인 일정뿐 아니라 그룹 일정을 한 눈에 관리할 수 있는 캘린더를 만들고자 이 아이디어를 시작하게 되었습니다.</p>
<h2 id="기능-설명">기능 설명</h2>
<p><strong>📌 내가 속한 그룹과 개인일정을 달력 형태로 볼 수 있습니다</strong></p>
<p align='center'>
<img width="300" alt="달력 캡처 화면" src="https://velog.velcdn.com/images/young-jev/post/b5227a86-724d-4637-9608-775b792db2c2/image.png">
</p>

<p><strong>🗓️ 일별 일정들도 리스트로 확인할 수 있습니다</strong></p>
<p align='center'>
<img width="300" alt="일정 모달 캡쳐 화면" src="https://velog.velcdn.com/images/young-jev/post/24d11d65-02f8-49f3-8828-6730b95f8ab1/image.png">
</p>

<p><strong>👥 내가 속한 그룹의 모임을 리스트로 확인할 수 있습니다</strong></p>
<p align='center'>
<img width="300" alt="그룹 리스트 캡처 화면" src="https://velog.velcdn.com/images/young-jev/post/cbfe44b7-be2b-4cc6-8ff5-f536c6712cf1/image.png">
</p>

<p><strong>🔍 특정 그룹만 필터링하여 일정을 확인할 수 있습니다.</strong></p>
<p align='center'>
<img width="300" alt="그룹 상세 캡처 화면" src="https://velog.velcdn.com/images/young-jev/post/fcc8e441-11d0-415f-9094-09dfc24c5aac/image.jpeg">
</p>  

<h2 id="🧑💻-기술적-도전">🧑‍💻 기술적 도전</h2>
<h3 id="달력"><strong>달력</strong></h3>
<ul>
<li>좋은 사용자 경험을 주기 위해 달력의 성능, 기능 들을 비교해보고, 선택한 과정을 담았습니다.<ul>
<li><a href="https://velog.io/@young-jev/%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EB%B0%9C">캘린더 컴포넌트 인터페이스 개발</a></li>
<li><a href="https://velog.io/@young-jev/React-Native-%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0">초기 로딩 속도가 오래걸려 컴포넌트를 캐싱하는 방법으로 로딩 시간을 62% 단축시키기</a></li>
<li><a href="https://velog.io/@young-jev/%EC%9D%BC%EC%A0%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EB%B0%9C">일정 컴포넌트 인터페이스 개발</a></li>
<li><a href="https://velog.io/@young-jev/%EA%B9%8A%EC%9D%B4-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89DFS%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9D%BC%EC%A0%95-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0">깊이 우선 탐색(DFS)을 활용한 일정 프로세스 성능 개선</a></li>
</ul>
</li>
</ul>
<h3 id="기타">기타</h3>
<ul>
<li><a href="https://velog.io/@young-jev/React-Native-Quick-Action%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-UX-%ED%96%A5%EC%83%81%EC%8B%9C%ED%82%A4%EA%B8%B0">React Native Quick Action을 사용해서 UX 향상시키기</a></li>
<li><a href="https://velog.io/@young-jev/React-Naitve-SNS-%EA%B3%B5%EC%9C%A0%EC%99%80-%EB%94%A5%EB%A7%81%ED%81%AC%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EA%B2%BD%ED%97%98-%ED%96%A5%EC%83%81%EC%8B%9C%ED%82%A4%EA%B8%B0">React Naitve SNS 공유와 딥링크로 사용자 경험 향상시키기</a></li>
</ul>
<h2 id="기술-스택">기술 스택</h2>
<table>
<thead>
<tr>
<th align="center">분류</th>
<th align="center">스택</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>Common</strong></td>
<td align="center">TypeScript, Node.js</td>
</tr>
<tr>
<td align="center"><strong>FrontEnd</strong></td>
<td align="center">React Native, React Query, Recoil, React Hook Form</td>
</tr>
</tbody></table>
<h2 id="팀원">팀원</h2>
<table>
<thead>
<tr>
<th align="center">FrontEnd</th>
<th align="center">BackEnd</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><p align='center'><img src="https://avatars.githubusercontent.com/u/75121415?v=4" width="100"></p></td>
<td align="center"><p align='center'><img src="https://avatars.githubusercontent.com/u/77970912?v=4" width="100"></p></td>
</tr>
<tr>
<td align="center"><p align='center'><a href="https://github.com/kkkkYoungJae">김영재</a></p></td>
<td align="center"><p align='center'><a href="https://github.com/kwonyeji500">권예지</a></p></td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next의 App Router와 기존 Page Router의 차이 비교]]></title>
            <link>https://velog.io/@young-jev/Next%EC%9D%98-App-Router%EC%99%80-%EA%B8%B0%EC%A1%B4-Page-Router%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@young-jev/Next%EC%9D%98-App-Router%EC%99%80-%EA%B8%B0%EC%A1%B4-Page-Router%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Sun, 30 Jun 2024 13:33:37 GMT</pubDate>
            <description><![CDATA[<p>Next.js 13은 새로운 App Router와 함께 중요한 업데이트를 제공합니다. 이 블로그 포스트에서는 새로운 App Router와 기존 Page Router의 차이를 탐구할 것입니다. 이들의 기능, 장점 및 잠재적인 사용 사례를 살펴보겠습니다.</p>
<h2 id="소개">소개</h2>
<p>Next.js는 서버 사이드 렌더링과 정적 사이트 생성과 같은 기능을 제공하여 오랫동안 인기 있는 React 애플리케이션 프레임워크였습니다. Next.js 13에서는 더 유연하고 확장 가능한 라우팅 시스템을 제공하기 위해 새로운 App Router가 도입되었습니다. 이를 기존의 Page Router와 비교해 보겠습니다.</p>
<h2 id="기존-page-router">기존 Page Router</h2>
<p>이전 버전의 Next.js에서는 라우팅이 파일 기반이었습니다. pages 디렉토리 내부의 파일 구조가 라우트를 정의했습니다. 예를 들어:</p>
<pre><code class="language-bash">pages/
  ├── index.js  // 라우트: /
  ├── about.js  // 라우트: /about
  └── blog/
      └── [id].js  // 동적 라우트: /blog/:id</code></pre>
<h3 id="장점">장점</h3>
<ul>
<li><strong>간단함</strong>: 이해하고 설정하기 쉬움.</li>
<li><strong>관습 우선 구성</strong>: 최소한의 설정만 필요.<h3 id="단점">단점</h3>
</li>
<li><strong>확장성 문제</strong>: 애플리케이션이 커짐에 따라 단일 pages 디렉토리에서 라우트를 관리하는 것이 번거로울 수 있음.</li>
<li><strong>제한된 커스터마이징</strong>: 고급 라우팅 시나리오에 대한 유연성이 부족함.</li>
</ul>
<h2 id="nextjs-13의-app-router">Next.js 13의 App Router</h2>
<p>App Router는 더 모듈화된 방식으로 라우트를 정의할 수 있게 하여, 더 많은 커스터마이징을 허용합니다.</p>
<h3 id="예제-구성">예제 구성</h3>
<p>App Router를 사용하면 파일 구조에서 라우트 정의를 분리하여 구성 파일에 라우트를 정의할 수 있습니다:</p>
<pre><code class="language-javascript">// app/routes.js
export default [
  {
    path: &#39;/&#39;,
    component: HomePage,
  },
  {
    path: &#39;/about&#39;,
    component: AboutPage,
  },
  {
    path: &#39;/blog/:id&#39;,
    component: BlogPost,
  },
];</code></pre>
<h3 id="장점-1">장점</h3>
<ul>
<li><strong>모듈성</strong>: 여러 파일에 라우트를 정의하여 조직화가 향상됨.</li>
<li><strong>유연성</strong>: 중첩 라우트와 가드를 포함한 고급 라우팅 논리에 대한 더 큰 제어 가능.</li>
<li><strong>향상된 성능</strong>: 대규모 애플리케이션에서도 더 나은 성능을 위해 최적화됨.<h3 id="단점-1">단점</h3>
</li>
<li><strong>학습 곡선</strong>: 기존 Page Router에 비해 설정이 더 복잡함.</li>
<li><strong>구성 오버헤드</strong>: 초기 구성에 더 많은 노력이 필요.</li>
</ul>
<h2 id="사용-사례">사용 사례</h2>
<h3 id="기존-page-router를-사용할-때">기존 Page Router를 사용할 때</h3>
<p>소규모에서 중규모 프로젝트: 작은 프로젝트에는 Page Router의 간단함이 유리함.
빠른 프로토타이핑: 쉬운 설정으로 빠른 개발 가능.</p>
<h3 id="app-router를-사용할-때">App Router를 사용할 때</h3>
<p>대규모 애플리케이션: 복잡한 라우팅 요구 사항을 가진 애플리케이션에 더 적합.
고급 라우팅 필요: 중첩 라우트, 가드, 더 유연한 라우팅 논리가 필요한 경우.</p>
<h2 id="결론">결론</h2>
<blockquote>
<p>Next.js 13의 App Router는 기존 Page Router의 한계를 해결하는 더 강력하고 유연한 라우팅 접근 방식을 제공합니다. 그러나 둘 사이의 선택은 프로젝트의 특정 요구 사항에 따라 달라집니다. 소규모 애플리케이션에는 기존 Page Router가 여전히 좋은 선택이며, 더 크고 복잡한 애플리케이션은 App Router의 고급 기능을 활용할 수 있습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[깊이 우선 탐색(DFS)을 활용한 일정 프로세스 성능 개선]]></title>
            <link>https://velog.io/@young-jev/%EA%B9%8A%EC%9D%B4-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89DFS%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9D%BC%EC%A0%95-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0</link>
            <guid>https://velog.io/@young-jev/%EA%B9%8A%EC%9D%B4-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89DFS%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9D%BC%EC%A0%95-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0</guid>
            <pubDate>Fri, 21 Jun 2024 04:13:29 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 앞선 <a href="https://velog.io/@young-jev/%EC%9D%BC%EC%A0%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EB%B0%9C">게시글</a>로 일정 컴포넌트를  만들었었는데 발생한 문제와 해결 과정을 공유해드립니다.</p>
<h1 id="문제">문제</h1>
<p>만약 일정을 삭제할 때 단순히 그 데이터만 삭제를 하는게 아니라 일정이 겹쳐져 있는 Day 컴포넌트들도 리렌더링을 해주어야합니다. </p>
<p>*<em>쉽게 말해 무슨 말이냐면 *</em></p>
<p>예를 들어 아래 이미지처럼 일정들이 있을때 &quot;일정1&quot;을 삭제한다면</p>
<p>
<img src ='https://velog.velcdn.com/images/young-jev/post/fc31ad60-6bdc-4ff6-a2aa-1511c8cce2b5/image.png' width ="300"/>
</p>

<p>아래 이미지 처럼 &quot;일정1&quot;과 같은 단계에 있는 데이터들을 삭제해줘야한다</p>
<p>
<img src ='https://velog.velcdn.com/images/young-jev/post/1b47a4f6-2322-4ce9-9987-707088dc17e4/image.png' width ="300"/>
</p>

<p>
<img src ='https://velog.velcdn.com/images/young-jev/post/fe194339-5d2e-4ba5-a693-46f643f1e4a5/image.jpeg' width ="300"/>
</p>

<p>그렇지 않으면 아래처럼 됩니다</p>
<p>
<img src ='https://velog.velcdn.com/images/young-jev/post/37267097-fba1-42d3-bb5c-6eff518be0d4/image.png' width ="300"/>
</p>

<p>아래 이미지처럼 계단식으로 쭉 연결되어있다면 연결된 모든 일정들을 렌더링 해주어야한다. 
<img src="https://velog.velcdn.com/images/young-jev/post/f82ca4c2-7f23-482c-a945-8cd15eb58456/image.png" alt=""></p>
<h2 id="해결-1">해결 1</h2>
<p>계단식으로 쭉 연결되어있는게 어디까지인지를 몰랐기때문에 처음부터 끝까지 모든 일정을 다 부르는 방식으로 했었습니다. 물론 원하는 방식으로 동작을 하긴했지만 시간, 비용적인 측면에서 너무 낭비가 있다는 것을 느꼈고 개선의 여지가 필요했습니다. 아래는 모니터링으로 확인한 결과입니다.</p>
<p>
<img src ='https://velog.velcdn.com/images/young-jev/post/ec5c3b81-2a69-4421-a004-ea96f41547e5/image.png' width ="300"/>
</p>

<blockquote>
<p> 모니터링을 확인하면 일정 한개가 삭제가 되었을때 4.3초가 걸렸습니다. 일정 한개가 삭제 되었을 뿐인데 모든 데이터를 호출하고 모든 데이터를 가공하는 형태입니다. 지금은 일정이 몇개 없어서 이정도 걸렸지만 관련이 있든 없든 일정이 많아질수록 시간은 더 증가할것입니다.</p>
</blockquote>
<h2 id="해결-2">해결 2</h2>
<p>꼭 필요한 데이터만 렌더링하자라는 생각이 들었습니다. 그래서 해당 날짜가 삭제가 되면 DFS 알고리즘을 통해 타고 타고 들어가서 시작날짜와 끝날짜를 찾자라는 계획입니다. </p>
<p>삭제한 일정의 시작, 종료 날짜로부터 시작해서 해당 날짜를 순회해서 계속 해서 탐색해 나갑니다. startingDay, endingDay를 통해 마지막 날짜를 찾을 수 있습니다. 해당 함수를 사용하면 연결되어있는 일정중 시작날짜와 끝날짜를 알 수 있어 모든 일정을 부르지 않고 필요한 데이터만 요청 할 수 있습니다.</p>
<pre><code class="language-javascript">const firstDate = getScheduleStartDate(삭제한 일정의 시작 날짜);
const lastDate = getScheduleLastDate(삭제한 일정의 종료 날짜);

const getScheduleStartDate = (startDate: string) =&gt; {
  let firstDate = format(new Date(startDate), &#39;yyyy-MM-dd&#39;);

  const DFS = (date: string) =&gt; {
    schedule.scheduleByDate[date]?.forEach((event) =&gt; {
      if (event) {
        const _fDate = fDate(event?.startDate);
        if (!event.startingDay) {
          DFS(_fDate);
        } else {
          if (isBefore(_fDate, firstDate)) {
            firstDate = _fDate;
          }
        }
      }
    });
  };

  DFS(firstDate);
  return firstDate;
};

const getScheduleLastDate = (endDate: string) =&gt; {
  let lastDate = format(new Date(endDate), &#39;yyyy-MM-dd&#39;);

  const DFS = (date: string) =&gt; {
    schedule.scheduleByDate[date]?.forEach((event) =&gt; {
      if (event) {
        const _fDate = fDate(event?.endDate);

        if (!event.endingDay) {
          DFS(_fDate);
        } else {
          if (isAfter(_fDate, lastDate)) {
            lastDate = _fDate;
            }
        }
      }
    });
  };

  DFS(lastDate);
  return lastDate;
};</code></pre>
<p>
<img src ='https://velog.velcdn.com/images/young-jev/post/fdce5273-bdd0-442a-9995-98b3ffca7547/image.png' width ="300"/>
</p>

<blockquote>
<p>DFS알고리즘을 통해 연결된 일정의 시작과 종료날짜를 찾고 그 부분만 호출해 렌더링하기때문에 시간이 4.3초에서 2.7초로 단축되었다. 하지만 탐색 경로를 보면 한번 들렸던 부분도 또 들리는 부분을 캐치했다. check 방식을 통해 들렸던 부분은 다시 들리지않는 방식을 하면 더 단축할 수 있을것같다.</p>
</blockquote>
<h2 id="해결3">해결3</h2>
<p>해결2 방법으로도 많은 시간을 단축했지만 더 단축 할 수 있는 여지가 있습니다. check 방식을 통해 들렸던 곳은 다시 들리지 않게 해보겠습니다.</p>
<p>아래 코드를 추가하고 조건문에 방문여부를 확인하는 로직을 추가하겠습니다.</p>
<pre><code class="language-javascript">const check: Record&lt;string, boolean&gt; = {};

if(!check[_fDate])</code></pre>
<pre><code class="language-javascript">const getScheduleStartDate = (startDate: string) =&gt; {
  const check: Record&lt;string, boolean&gt; = {}; // 추가
  let firstDate = format(new Date(startDate), &#39;yyyy-MM-dd&#39;);

  const DFS = (date: string) =&gt; {
    schedule.scheduleByDate[date]?.forEach((event) =&gt; {
      if (event) {
        const _fDate = fDate(event?.startDate);
        if (!event.startingDay &amp;&amp; !check[_fDate]) { // 방문여부 체크
          DFS(_fDate);
        } else {
        // 훨씬 단축됨
          check[_fDate] = true; // 방문함
          if (isBefore(_fDate, firstDate)) {
            firstDate = _fDate;
          }
        }
      }
    });
  };

  DFS(firstDate);
  return firstDate;
};

const getScheduleLastDate = (endDate: string) =&gt; {
  const check: Record&lt;string, boolean&gt; = {}; //추가
  let lastDate = format(new Date(endDate), &#39;yyyy-MM-dd&#39;);

  const DFS = (date: string) =&gt; {
    schedule.scheduleByDate[date]?.forEach((event) =&gt; {
      if (event) {
        const _fDate = fDate(event?.endDate);

        if (!event.endingDay &amp;&amp; !check[_fDate]) { // 방문여부 체크
          DFS(_fDate);
        } else {
          // 훨씬 단축됨
          check[_fDate] = true; // 방문함

          if (isAfter(_fDate, lastDate)) {
            lastDate = _fDate;
            }
        }
      }
    });
  };

  DFS(lastDate);
  return lastDate;
};</code></pre>
<p>
<img src ='https://velog.velcdn.com/images/young-jev/post/e73fe41d-fc09-41a9-b501-1c2934ade488/image.png' width ="300"/>
</p>

<blockquote>
<p>check 방식을 통해 탐색의 횟수를 줄였고 당연히 렌더링 걸리는 시간도 단축했다. </p>
</blockquote>
<h1 id="마무리">마무리</h1>
<blockquote>
<p>코딩 테스트를 준비하면서 공부한 알고리즘을 이렇게 사용해 볼 줄은 몰랐다. 솔직히 프론트엔드가 알고리즘을 그렇게까지 알아야하나라는 생각이 있었는데 이번 계기로 생각이 완전히 바뀌었다. 알고리즘이 중요하다고 많이들 들어왔지만 직접 체감을 해보니 느낌이 달랐다. 알고리즘을 적용했을뿐인데 <strong>4.3초에서 2.4초 약 44%</strong>의 성능 개선을 얻었다. 알고리즘 공부를 더 열심히 해야겠다는 욕구가 생긴 계기가 되었다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[일정 컴포넌트 인터페이스 개발]]></title>
            <link>https://velog.io/@young-jev/%EC%9D%BC%EC%A0%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@young-jev/%EC%9D%BC%EC%A0%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Thu, 20 Jun 2024 09:01:08 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요, 여러분!
현재 공유 달력 앱을 개발하고 있습니다. <strong>일정 컴포넌트를 개발하면서 겪었던 경험</strong>을 공유해보고자 합니다.</p>
<h1 id="분석">분석</h1>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/d12e822d-4f59-4609-ba8b-336ab7a359b4/image.jpeg" width="300"/>
</p>

<ol>
<li>일정은 몇 시간 단위거나 며칠 단위입니다. 같은 날 일정이라면 &quot;일정1&quot;과 같이 하루 안에 UI를 그리면 되지만 &quot;일정2&quot;처럼 며칠인 경우 가로로 길게 이어진 UI를 가집니다. </li>
</ol>
<ol start="2">
<li>컴포넌트의 순서도 시간이 빠른 일정이 먼저 렌더링 됩니다.</li>
</ol>
<h1 id="설계">설계</h1>
<ol>
<li>시작하는 시간이 제일 빠른 일정을 먼저 그리고 그 다음 먼저 끝나는 일정을 그리도록 <strong>정렬</strong>해야합니다.</li>
</ol>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/3689181b-9c16-49c5-b34f-11cc8f470314/image.png" width="300"/>
</p>

<ol start="2">
<li>단순히 그 날짜에 일정 컴포넌트를 렌더링하는게 아니라 21일에 &quot;일정2&quot;처럼 <strong>한칸을 띄우고</strong> 단계를 맞추어 주어야합니다. </li>
</ol>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/d2a3f182-0b55-4eb7-a9f7-4350e61cd96f/image.png" width="300"/>
</p>

<p>저는 <strong>null값</strong>을 넣어서 한칸을 띄우는 방식을 채택했습니다.</p>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/d42746a7-d35a-48ac-9ae6-239a0abc6c75/image.jpeg" width="300"/>
</p>

<ol start="3">
<li>만약 20일 컴포넌트를 렌더링할 순서라면 20일 컴포넌트를 그리고 그 날짜에 일정이 있으면 일정 컴포넌트를 렌더링하는 방식입니다. 이 과정을 반복하면서 그리게 됩니다. 해당하는 데이터만 있으면 되기때문에 데이터 검색 속도를 위해 <strong>Map 자료형</strong>으로 가공하여 사용합니다.</li>
</ol>
<h1 id="구현">구현</h1>
<p>먼저 일정 데이터를 API 조회를 하면 일정이 배열 형태로 응답받습니다.</p>
<pre><code class="language-javascript">[
    {
        &quot;scheduleId&quot;:114,
        &quot;name&quot;:&quot;미용실&quot;,
        &quot;startDate&quot;:&quot;2024-05-24T03:35:34.385Z&quot;,
        &quot;endDate&quot;:&quot;2024-05-24T03:35:34.385Z&quot;,
        ...
    }
    ...
]</code></pre>
<blockquote>
</blockquote>
<p>로직 순서</p>
<ol>
<li>데이터가 없으면 빈배열로 초기화</li>
<li>시작 날짜와 종료 날짜가 동일하면 한개의 데이터만 저장후 바로 종료</li>
<li>시작 날짜의 배열 Length와 종료 날짜의 배열 Length를 비교해서 큰 숫자와 동일하게 null값을 추가하여 높이를 맞춥니다 </li>
<li>시작 날짜의 startingDay와 종료 날짜의 endingDay를 true로 표시해줍니다.</li>
<li>시작 날짜와 종료 날짜사이에 배열들도 3번과 같은 의미로 null값을 추가하여 높이를 맞춰줍니다.</li>
</ol>
<pre><code class="language-javascript">const periodsData : { [date: string]: (ISchedule | null)[] } = {}

events.forEach((event) =&gt; {
      const startDate = format(event.startDate, &#39;yyyy-MM-dd&#39;);
      const endDate = format(event.endDate, &#39;yyyy-MM-dd&#39;);

      //1. 시작 날짜가 periodsData에 없으면 생성
      if (!periodsData[startDate]) {
        periodsData[startDate] = [];
      }
      //1. 종료 날짜가 periodsData에 없으면 생성
      if (!periodsData[endDate]) {
        periodsData[endDate] = [];
      }

      //2. 시작 날짜와 종료 날짜가 동일한 경우
      if (startDate === endDate) {
        periodsData[startDate].push({
          ...event,
          startingDay: true, // 일정의 시작
          endingDay: true, // 일정의 종료
        });
      } else {
        const startLength = periodsData[startDate].length;
        const endLength = periodsData[endDate].length;

        //3. 시작 날짜와 종료 날짜를 비교해서 같은 높이로 맞추기
        if (startLength &gt; endLength) {
          while (periodsData[endDate].length &lt; startLength) {
            periodsData[endDate].push(null);
          }
        } else if (endLength &gt; startLength) {
          while (periodsData[startDate].length &lt; endLength) {
            periodsData[startDate].push(null);
          }
        }

        //4. 시작 날짜에 시작 표시
        periodsData[startDate].push({
          ...event,
          startingDay: true,
          endingDay: false,
        });
        //4. 종료 날짜에 끝 표시
        periodsData[endDate].push({
          ...event,
          startingDay: false,
          endingDay: true,
        });

        //5. 시작 날짜와 종료 날짜 사이의 날짜들에 대한 표시
        let currentDate = new Date(startDate);
        currentDate.setDate(currentDate.getDate() + 1);
        while (currentDate &lt; new Date(endDate)) {
          const middleDate = format(currentDate, &#39;yyyy-MM-dd&#39;);
          if (!periodsData[middleDate]) {
            periodsData[middleDate] = [];
          }

          const middleLength = periodsData[middleDate].length;

          // 시작 날짜와 중간 날짜를 비교해서 같은 높이로 맞추기
          if (startLength &gt; middleLength) {
            while (periodsData[middleDate].length &lt; startLength) {
              periodsData[middleDate].push(null);
            }
          }

          periodsData[middleDate].push({
            ...event,
            startingDay: false,
            endingDay: false,
          });
          currentDate.setDate(currentDate.getDate() + 1);
        }
      }
    });
</code></pre>
<h1 id="결과">결과</h1>
<pre><code class="language-javascript">{
&quot;2024-06-22&quot;:[
    null,
    {
    &quot;scheduleId&quot;:122,
    &quot;name&quot;:&quot;일정2&quot;,
    &quot;startDate&quot;:&quot;2024-06-20T07:36:29.196Z&quot;,
    &quot;endDate&quot;:&quot;2024-06-22T07:36:29Z&quot;,
    &quot;startingDay&quot;:false,
    &quot;endingDay&quot;:true
    },
    ],
    ...
}</code></pre>
<p>Day 컴포넌트에서 해당하는 날짜를 검색하고 null일 경우 한칸 띄우는 View를 구성하는 것으로 해결했습니다.</p>
<h1 id="마무리">마무리</h1>
<blockquote>
<p>&quot;공달이&quot; 프로젝트를 하면서 가장 어려웠던 부분이라 생각합니다. 최대한 단순하게 구현을 해야 성능이라든지 유지 비용에 효율성을 높일 수 있다고 생각했습니다. 
초반에 분석하고 설계를 잘 해놓으면 개발하는데 속도가 붙는다고 느끼게 되었습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Naitve SNS 공유와 딥링크로 사용자 경험 향상시키기]]></title>
            <link>https://velog.io/@young-jev/React-Naitve-SNS-%EA%B3%B5%EC%9C%A0%EC%99%80-%EB%94%A5%EB%A7%81%ED%81%AC%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EA%B2%BD%ED%97%98-%ED%96%A5%EC%83%81%EC%8B%9C%ED%82%A4%EA%B8%B0</link>
            <guid>https://velog.io/@young-jev/React-Naitve-SNS-%EA%B3%B5%EC%9C%A0%EC%99%80-%EB%94%A5%EB%A7%81%ED%81%AC%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EA%B2%BD%ED%97%98-%ED%96%A5%EC%83%81%EC%8B%9C%ED%82%A4%EA%B8%B0</guid>
            <pubDate>Wed, 19 Jun 2024 09:23:04 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요, 여러분!
이번에 앱 내에 그룹 초대하기라는 SNS 공유 형태의 기능이 들어가게 되었습니다. 이에 따른 공유하기 기능과 해당 공유를 눌렀을때 앱에서 해당 화면으로 바로 이동할 수 있게 하는 코드를 작성했습니다. 작업 도중 해당 기능을 구현하는 방식에 대해 공부하게 되었습니다.</p>
<h3 id="deep-link란">Deep Link란?</h3>
<p>간단하게 설명하자면 웹 브라우저 환경에서 DNS가 존재하는 것처럼 모바일 환경에서도 특정 앱을 실행 시킬 수 있습니다. 방식에는 크게 세가지가 있습니다. 저는 여기서 1번 URI 스킴 방식으로 개발했습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>URI 스킴 방식: 앱에 URI 스킴(scheme) 등록해서 사용</li>
<li>앱 링크(App Link): 도메인 주소를 등록해서 사용 (Android only)</li>
<li>유니버셜 링크(Universal Link): 도메인 주소를 등록해서 사용 (iOS only)</li>
</ol>
<h4 id="uri-스킴-방식">URI 스킴 방식</h4>
<p>URI 스킴 방식은 현재까지 가장 많이 사용하고 있고, 가장 초기에 나온 방식입니다. 앱에 scheme을 등록하여 각 앱을 구분짓습니다. 방식은 Scheme://Path 형태이고 경우에 따라 query(?name=millo)가 붙기도 한다.</p>
<ul>
<li>Scheme: 앱을 특정 (twitter)</li>
<li>Path: 앱 내 페이지를 특정 (트위터 내 특정 페이지)</li>
<li>query: 해당 페이지로 넘겨줄 값 (선택)</li>
</ul>
<h3 id="카카오톡-공유하기">카카오톡 공유하기</h3>
<h4 id="1-react-native-kakao-share-link-설치">1. <a href="https://www.npmjs.com/package/react-native-kakao-share-link">react-native-kakao-share-link</a> 설치</h4>
<p>카카오톡 공유하기는 공식문서에 따라 설정하시면 됩니다.</p>
<h4 id="2-코드작성">2. 코드작성</h4>
<p>아래 코드처럼 작성하시면 카카오톡 앱이 깔려있을 경우 정상으로 공유가 가능합니다.</p>
<pre><code>KakaoShareLink.sendText({
        text: `안녕하세요! 공달이 앱에 초대합니다.
함께 일정을 공유하고 소통할 수 있는 새로운 캘린더 [${params.name}]가 생성되었어요. 
아래 링크를 클릭하면 바로 참여할 수 있습니다. 📅✨`,
        link: {
          webUrl: &#39;https://developers.kakao.com/&#39;,
          mobileWebUrl: &#39;https://developers.kakao.com/&#39;,
        },
        buttons: [
          {
            title: &#39;앱에서 보기&#39;,
            link: {
              androidExecutionParams: [{ key: &#39;groupKey&#39;, value: params.key }],
              iosExecutionParams: [{ key: &#39;groupKey&#39;, value: params.key }],
              webUrl: &#39;https://developers.kakao.com/&#39;,
              mobileWebUrl: &#39;https://developers.kakao.com/&#39;,
            },
          },
        ],
      })</code></pre><p>
<img src="https://velog.velcdn.com/images/young-jev/post/458aaa0d-137b-4a5b-9097-8a913245c6d3/image.jpeg" width="500"/>
</p>

<p>공유하기가 정상적으로 동작하는 모습을 확인 할 수 있습니다. &quot;앱에서 보기&quot; 버튼을 누르면 아직 아무일도 일어나지 않습니다. 추가적인 설정이 필요합니다.</p>
<h3 id="deep-link-설정">Deep Link 설정</h3>
<h4 id="1-안드로이드-설정">1. 안드로이드 설정</h4>
<p>안드로이드는 AndroidManifest.xml에서 스킴을 추가해줘야합니다. kakao{카카오 네이티브 앱키}가 Scheme이 되고, kakaolink가 Path가 됩니다.</p>
<pre><code>## ./android/app/src/main/AndroidManifest.xml

&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
  package=&quot;com.example&quot;&gt;
+   &lt;uses-permission android:name=&quot;android.permission.INTERNET&quot; /&gt;
    &lt;application&gt;
      &lt;activity&gt;
        &lt;intent-filter&gt;
            &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
            &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
        &lt;/intent-filter&gt;
+       &lt;intent-filter&gt;
+           &lt;action android:name=&quot;android.intent.action.VIEW&quot; /&gt;
+           &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; /&gt;
+           &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot; /&gt;
+           &lt;data android:host=&quot;kakaolink&quot; android:scheme=&quot;kakao{카카오 네이티브 앱키}&quot; /&gt;
+        &lt;/intent-filter&gt;
      &lt;/activity&gt;
    &lt;/application&gt;
&lt;/manifest&gt;</code></pre><h4 id="2-ios-설정">2. IOS 설정</h4>
<p>IOS도 마찬가지로 Info.plist에서 설정해주시면됩니다.</p>
<pre><code>## ./ios/{your-project-name}/Info.plist

  &lt;key&gt;CFBundleURLTypes&lt;/key&gt;
  &lt;array&gt;
+   &lt;dict&gt;
+     &lt;key&gt;CFBundleTypeRole&lt;/key&gt;
+     &lt;string&gt;Editor&lt;/string&gt;
+     &lt;key&gt;CFBundleURLSchemes&lt;/key&gt;
+     &lt;array&gt;
+       &lt;string&gt;kakao{카카오 네이티브 앱키}&lt;/string&gt;
+     &lt;/array&gt;
+   &lt;/dict&gt;
  &lt;/array&gt;
  &lt;key&gt;CFBundleVersion&lt;/key&gt;
  &lt;string&gt;1&lt;/string&gt;
+ &lt;key&gt;KAKAO_APP_KEY&lt;/key&gt;
+ &lt;string&gt;{카카오 네이티브 앱키}&lt;/string&gt;
+ &lt;key&gt;LSApplicationQueriesSchemes&lt;/key&gt;
+ &lt;array&gt;
+     &lt;!-- 카카오링크 --&gt;
+     &lt;string&gt;kakaolink&lt;/string&gt;
+ &lt;/array&gt;</code></pre><h4 id="3-react-native-코드-작성">3. React Native 코드 작성</h4>
<p>아래와 같이 입력하면 딥링크의 요청이 handleDeepLink로 들어오게 됩니다. 해당 함수에서 로직에 맞게 처리하시면 됩니다.</p>
<pre><code>  const handleDeepLink = (url: string | null) =&gt; {
    console.log(&#39;Deep Link: &#39;, url);
    ... 해당 로직 작성
  };

  useEffect(() =&gt; {
    const linkingListener = Linking.addEventListener(&#39;url&#39;, (e) =&gt; {
      handleDeepLink(e.url);
    });

    return () =&gt; {
      linkingListener.remove();
    };
  }, []);</code></pre><h3 id="결과">결과</h3>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/228ee98d-5a19-464f-b20e-421d6d409113/image.gif" width="500"/>
</p>

<h3 id="마무리">마무리</h3>
<blockquote>
<p>카카오톡 공유하기를 쉽게 이용 할 수 있었고 딥링크에 알게된 계기였습니다. 딥링크의 활용도는 무궁무진하다고 생각이 듭니다. 딥링크를 이용 할 수 있는 웹도 만들어서 연결하면 지금 기능뿐 아니라 다양한 방면으로 활용 할 수 있을 것 같다.  </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Native 캘린더 성능 개선]]></title>
            <link>https://velog.io/@young-jev/React-Native-%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0</link>
            <guid>https://velog.io/@young-jev/React-Native-%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0</guid>
            <pubDate>Tue, 18 Jun 2024 06:39:53 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요, 여러분!
앞선 게시글로 달력을 만들었었는데 문제가 생겨서 어떻게 인식하고 해결했는지에 대한 이야기를 공유하려고 합니다.</p>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/ba03a55a-5f80-462a-97f8-69c6921dec8e/image.gif" width="300">
</p>

<h3 id="문제-인식">문제 인식</h3>
<p>원했던 화면이 그려지기는 했지만 로딩 되는데에 매우 오래걸리는 문제가 있었습니다.
성능 모니터링 툴로 확인을 해보니 <strong>3.3s</strong>가 걸리는 것을 확인할 수 있었습니다.</p>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/54d88023-1fb2-45c1-b414-34d2e0fa20f6/image.png" width="300">
<br/>
 </p>


<p>구글 리서치 자료에 따르면 모바일 웹 사이트의 로딩 시간이 <strong>3초 이상일 때 32%, 5초 이상은 90%, 6초 이상은 106% 마지막으로 10초가 넘으면 123%의 이탈률</strong>이 발생한다고 합니다.</p>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/549f1771-0aa3-43bf-a522-8d1a22a83457/image.png" width="500">
<br/>
 </p>

<p><strong>우리는 아무것도 안했는데 벌써 유저 32%를 잃었습니다!!</strong>
지금은 빈 달력뿐이지만 나중에 일정, 애니메이션 등등 추가가 된다면 렌더링 시간은 더 늘어날 것입니다.</p>
<h3 id="개선-방법">개선 방법</h3>
<p>React Native에는 List를 표현하는 방법으로 ScrollView / FlatList / SectionList 를 이용하여 구현할 수 있습니다. 보통은 FlatList를 이용하여 작업을 많이 하게 됩니다.</p>
<p>React Natvie에서 List 최적화 방법이 많은데, 공식문서에서도 FlatList의 최적화 방법을 소개하고 있습니다.</p>
<p>하지만, 성능이 좋지 못한 폰에서는 리스트가 버벅거리는 현상이 있었습니다. 그렇게 해서 레퍼런스를 찾다가 발견한 List가 <strong>recyclerListView</strong>입니다.</p>
<p><strong>recyclerListView는</strong> 성능의 이점은 있지만 사용법이 복잡하고 제대로 갱신이 되지 않는 버그를 가지고 있습니다.</p>
<p>그렇게 찾아보다 알게된 라이브러리가 <strong>FlashList</strong>입니다.
<strong>FlashList는</strong> 내부적으로 <strong>recyclerListView</strong>를 가지고 있지만, 사용법이 FlatList와 같아 React Native의 FlatList를 사용해본 사람이라면 쉽게 구현할 수 있는 구조를 가지고 있습니다.</p>
<pre><code>  return (
    &lt;SafeAreaView style={{ flex: 1 }}&gt;
      &lt;FlashList // 변경
        ... 기존 코드
        estimatedItemSize={width} // 추가
      /&gt;
    &lt;/SafeAreaView&gt;
  );
};</code></pre><h3 id="개선-결과">개선 결과</h3>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/0b259587-62e2-4439-bb2e-fe1c103abfb6/image.gif" width="300" >
</p>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/1adb7999-6362-4c6a-975f-90bdf47449c2/image.png" width="300"> 
</p>


<p>기존 <strong>3.3초</strong>였던 렌더링 속도가 <strong>1.2초</strong>로 단축된 것을 확인 할 수 있습니다. 
약 <strong>63%</strong> 단축 된 결과를 얻었습니다!</p>
<p>이런 접근 방식을 통해 표시 성능을 최적화하면서도 보기 좋은 캘린더를 만들 수 있습니다.</p>
<h3 id="마무리">마무리</h3>
<blockquote>
</blockquote>
<p>성능 모니터링 툴을 이용해 성능개선을 해보니 도구나 기법에 더더욱 관심이 생기게 되었습니다. 도구를 더 잘쓰게 된다면 얻는 이점이 많다는 것을 알 수 있었습니다. 관심갖고 학습하여 더 나은 프로젝트를 만드는데 큰 도움이 되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Native Quick Action을 사용해서 UX 향상시키기]]></title>
            <link>https://velog.io/@young-jev/React-Native-Quick-Action%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-UX-%ED%96%A5%EC%83%81%EC%8B%9C%ED%82%A4%EA%B8%B0</link>
            <guid>https://velog.io/@young-jev/React-Native-Quick-Action%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-UX-%ED%96%A5%EC%83%81%EC%8B%9C%ED%82%A4%EA%B8%B0</guid>
            <pubDate>Sun, 16 Jun 2024 14:57:42 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요, 여러분!
오늘은 제가 개발 중인 캘린더 앱에 react-native-quick-actions 라이브러리를 사용해 훨씬 더 편리한 사용자 경험을 제공하는 방법을 공유하려고 합니다.</p>
<p>스마트폰을 사용하다가 앱을 길게 누르면 아래와 같은 액션 창을 본 적이 있을 겁니다. 옛날에는 앱 제거와 앱 위치 이동밖에 없었지만, 이제는 다양한 기능들로 사용자 경험을 향상시킬 수 있습니다.</p>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/ae2804aa-5a3b-486f-a634-69f1254661b0/image.PNG" width=300>
</p>

<h3 id="quick-actions-설정하기">Quick Actions 설정하기</h3>
<p>먼저, <strong><a href="https://github.com/jordanbyron/react-native-quick-actions">react-native-quick-actions</a></strong> 라이브러리를 사용하여 Quick Actions를 설정합니다. 
Quick Actions를 통해 앱을 실행하면 특정 작업을 바로 수행할 수 있습니다.</p>
<h3 id="설치">설치</h3>
<p>라이브러리를 설치합니다. 이 단계는 쉽기 때문에 따로 설명하지 않겠습니다. 설치가 완료되면 다음과 같이 코드를 작성합니다.</p>
<pre><code>import React, { useEffect } from &#39;react&#39;;
import { DeviceEventEmitter } from &#39;react-native&#39;;
import QuickActions from &#39;react-native-quick-actions&#39;;

QuickActions.setShortcutItems([
  {
    type: &#39;새로운 일정&#39;,
    title: &#39;새로운 일정&#39;,
    icon: &#39;Compose&#39;,
    userInfo: {
      url: &#39;schedule&#39;,
    },
  }
]);

const App = () =&gt; {
  const handleQuickAction = (data) =&gt; {
    if (data.type === &#39;새로운 일정&#39;) {
      // 원하는 작업 수행: 예를 들어 화면 이동
      console.log(&#39;Quick Action Triggered: 새로운 일정&#39;);
      // 네비게이션을 사용하여 특정 화면으로 이동
      // navigation.navigate(&#39;ScheduleScreen&#39;);
    }
  };

  useEffect(() =&gt; {
    const quickActionListener = DeviceEventEmitter.addListener(
      &#39;quickActionShortcut&#39;,
      handleQuickAction
    );

    return () =&gt; {
      quickActionListener.remove();
    };
  }, []);

  return (
    // 앱의 루트 컴포넌트
  );
};

export default App;
</code></pre><h4 id="코드를-간단히-설명하겠습니다">코드를 간단히 설명하겠습니다</h4>
<ol>
<li><p>QuickActions.setShortcutItems:
Quick Actions를 설정합니다. type, title, icon, userInfo 등을 정의하여 사용자가 앱을 길게 눌렀을 때 나타날 항목을 지정합니다.</p>
</li>
<li><p>DeviceEventEmitter.addListener:
Quick Action이 발생하면 handleQuickAction 함수가 호출됩니다. 이 함수에서 원하는 작업을 수행할 수 있습니다. 예를 들어, 화면을 이동시키거나 특정 작업을 바로 수행할 수 있습니다.</p>
</li>
</ol>
<p>퀵액션을 통해 앱을 실행하면 handleQuickAction에 데이터가 들어옵니다. 이 데이터를 바탕으로 화면을 이동시키거나 원하는 작업을 수행할 수 있습니다.</p>
<p>
<img src="https://velog.velcdn.com/images/young-jev/post/dc35ce47-5bd5-4bbb-8a77-28006c75ddb6/image.jpeg" width=300>
</p>

<h3 id="마무리">마무리</h3>
<blockquote>
</blockquote>
<p>간단한 기능 추가만으로도 사용자의 경험을 향상 시킬 수 있었습니다. 작은 기능들이 모여 큰 비지니스가 되는 것을 느끼게 되었습니다. 작은 기능이라 하더라도 꼼꼼하게 개발하는 개발자가 되어야겠다가 느꼈습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[캘린더 컴포넌트 인터페이스 개발]]></title>
            <link>https://velog.io/@young-jev/%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@young-jev/%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Sun, 16 Jun 2024 13:57:20 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요, 여러분! 
오늘은 제가 사이드 프로젝트로 진행하고 있는 React Native 캘린더 앱 개발 이야기를 공유하려고 합니다. 거의 모든 프로젝트의 시작 단계에서 항상 찾아보는 것처럼, 저도 괜찮은 라이브러리를 찾아보는 일부터 시작했습니다. 이 글에서는 제가 조사한 내용을 바탕으로 React Native에서 사용할 수 있는 캘린더 라이브러리들을 소개하고, 직접 캘린더를 구현했던 방법을 설명하겠습니다.</p>
<h3 id="라이브러리-탐색">라이브러리 탐색</h3>
<p>우선 저는 프로젝트를 빠르게 시작하고 싶어서 이미 존재하는 라이브러리를 사용하는 것이 좋겠다고 생각했습니다. 아래는 제가 찾아본 몇 가지 인기 있는 캘린더 라이브러리들입니다.</p>
<h4 id="1-react-native-calendars">1. <strong>react-native-calendars</strong></h4>
<p>가장 먼저 찾아본 라이브러리는 react-native-calendars입니다. 이 라이브러리는 React Native에서 가장 널리 사용되는 캘린더 라이브러리 중 하나입니다. 다양한 뷰(월간, 주간, 일간 포함)를 제공하며, 커스터마이징이 용이합니다.</p>
<p>특징:</p>
<ul>
<li>다양한 캘린더 뷰 제공</li>
<li>커스터마이즈 가능한 날짜 표시</li>
<li>이벤트, 마커 추가 기능</li>
</ul>
<h4 id="2-react-native-calendar-events">2. react-native-calendar-events</h4>
<p>또 다른 유망한 라이브러리는 react-native-calendar-events입니다. 이 라이브러리는 Native 모듈로서 기기의 기본 캘린더와 상호작용 할 수 있게 해줍니다. 즉, Google Calendar나 Apple Calendar와의 동기화가 가능합니다.</p>
<p>특징:</p>
<ul>
<li>Native Calendar API와 연동</li>
<li>일정 추가, 삭제, 검색 기능</li>
</ul>
<h4 id="3-react-native-big-calendar">3. react-native-big-calendar</h4>
<p>react-native-big-calendar는 좀 더 심플하고 직관적인 UI를 제공하는 캘린더 라이브러리입니다. 대규모의 이벤트들을 한눈에 보기 좋게 표현할 수 있으며, 사용자 친화적인 인터페이스를 제공합니다.</p>
<p>특징:</p>
<ul>
<li>월간, 주간, 일간 캘린더 뷰</li>
<li>드래그 앤 드롭 이벤트 기능</li>
<li>터치 인터랙션 지원</li>
</ul>
<blockquote>
<p>많고 좋은 라이브러리가 많았고 실제로 써봤지만 아무리 커스터마이징하여 필요한 기능을 만든다 하더라도 한계가 있었습니다. 
그래서 직접 구현하기로 결정했습니다! <del>그냥 네이티브로 만들걸..!</del></p>
</blockquote>
<h3 id="기본-ui-설계">기본 UI 설계</h3>
<pre><code>const PAST_SCROLL_RANGE = 12;
const FUTURE_SCROLL_RANGE = 12;

const Calendar = () =&gt; {
  const { width } = useWindowDimensions();

  const items: Date[] = useMemo(() =&gt; {
    const months: Date[] = [];
    for (let i = 0; i &lt;= PAST_SCROLL_RANGE + FUTURE_SCROLL_RANGE; i++) {
      const rangeDate = addMonths(new Date(), i - PAST_SCROLL_RANGE);
      months.push(rangeDate);
    }

    return months;
  }, []);

  const renderItem = useCallback(({ item }: { item: Date }) =&gt; {
    const monthStart = startOfMonth(item);
    const monthEnd = endOfMonth(monthStart);
    const startDate = startOfWeek(monthStart);
    const endDate = endOfWeek(monthEnd);

    const rows = [];

    let days = [];
    let day = startDate;

    while (day &lt;= endDate) {
      for (let i = 0; i &lt; 7; i++) {
        const formattedDate = fDate(day);
        days.push(
          &lt;View key={formattedDate} style={{ padding: 8, flex: 1 }}&gt;
            &lt;Text&gt;{formattedDate}&lt;/Text&gt;
          &lt;/View&gt;,
        );
        day = addDays(day, 1);
      }
      rows.push(
        &lt;View
          style={{
            flexDirection: &#39;row&#39;,
            flex: 1,
          }}
          key={day.toString()}
        &gt;
          {days}
        &lt;/View&gt;,
      );
      days = [];
    }

    return (
      &lt;View
        style={{
          width: width,
          height: &#39;100%&#39;,
        }}
      &gt;
        &lt;View style={{ flex: 1 }}&gt;{rows}&lt;/View&gt;
      &lt;/View&gt;
    );
  }, []);


  return (
    &lt;SafeAreaView style={{ flex: 1 }}&gt;
      &lt;FlatList
        data={items}
        keyExtractor={(item) =&gt; JSON.stringify(item)}
        renderItem={renderItem}
        horizontal
        pagingEnabled
      /&gt;
    &lt;/SafeAreaView&gt;
  );
};
export default Calendar;</code></pre><br/>

<h3 id="실행-결과">실행 결과</h3>
<img src="https://velog.velcdn.com/images/young-jev/post/bf6989fc-1aa5-47db-88b9-870ed6d0999b/image.gif" width="300">
<br/>

<h3 id="추가-기능">추가 기능</h3>
<p>여기서 캘린더 기능을 확장하여 다양한 기능을 추가할 수 있습니다. 다음은 몇 가지 추가할 수 있는 기능들입니다:</p>
<ol>
<li>일정 추가 및 관리: 각 날짜 셀을 클릭하면 일정 추가, 수정, 삭제가 가능한 팝업을 띄울 수 있습니다.</li>
<li>알림 기능: react-native-push-notification 같은 라이브러리를 사용하여 일정 전에 사용자가 알림을 받을 수 있도록 합니다.</li>
<li>다채로운 뷰 제공: 월간 뷰 외에도 주간 뷰, 일간 뷰 등을 추가하여 다양한 인터페이스를 제공합니다.</li>
<li>확장 가능한 상태 관리: 상태관리 라이브러리를 사용하여 복잡한 상태 관리를 효율적으로 처리할 수 있습니다.</li>
</ol>
<h3 id="마무리">마무리</h3>
<blockquote>
</blockquote>
<p>React Native로 캘린더 앱을 만드는 과정에서 이미 좋은 라이브러리들이 많다는 점을 알 수 있었습니다. 하지만 커스터마이징의 한계를 느꼈고, 직접 구현함으로써 프로젝트에 대한 완벽한 제어권을 확보하겠다는 결정을 하게 되었습니다. 무언가를 처음부터 구현하려는 결정은 언제나 쉽지 않지만, 프로젝트를 더 나아가게 만드는 큰 발걸음이 될 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Axios의 인터셉터, 인스턴스를 활용하여 네트워크 요청 모듈화]]></title>
            <link>https://velog.io/@young-jev/axios-%EC%9E%98-%EC%93%B0%EA%B8%B0</link>
            <guid>https://velog.io/@young-jev/axios-%EC%9E%98-%EC%93%B0%EA%B8%B0</guid>
            <pubDate>Tue, 28 May 2024 02:51:48 GMT</pubDate>
            <description><![CDATA[<p>많은 프로젝트를 진행하면서 api통신을 해야할때는 axios 라이브러리를 사용했다.
그 이유는 인스턴스와 인터셉터가 가져다 주는 편리함이 너무 좋았다 
<br/></p>
<h3 id="인스턴스">인스턴스</h3>
<pre><code>const instance = axios.create({
  // 상대적인 URL을 인스턴스 메서드에 전달하려면 baseURL을 설정하는 것은 편리하다.
  // URL(서버 주소) 예시 - http://127.0.0.1:5500
  baseURL: URL,
  // 요청이 timeout보다 오래 걸리면 요청이 중단된다.
  timeout: 1000,
  // 헤더값
  headers: {
    &#39;Content-Type&#39;: &#39;application/json&#39;,
  },
});</code></pre><ul>
<li>인스턴스는 위와같이 서버주소라던지 타임아웃, 기본 헤더값을 미리 설정하고 재사용 할 수 있다! <del>벌써 편함</del><br/>

</li>
</ul>
<h3 id="요청-인터셉터">요청 인터셉터</h3>
<pre><code>instance.interceptors.request.use(
  (config) =&gt; {
    // getToken() - 클라이언트에 저장되어 있는 액세스 토큰을 가져오는 함수
    const accessToken = getToken();

    config.headers[&#39;Content-Type&#39;] = &#39;application/json&#39;;
    config.headers[&#39;Authorization&#39;] = `Bearer ${accessToken}`;

    return config;
  },
  (error) =&gt; {
    console.log(error);
    return Promise.reject(error);
  }
);</code></pre><ul>
<li>request를 사용하면 요청이 전달되기 전에 개입 할 수 있다.</li>
<li>예시와같이 accesstoken이나 헤더 토큰을 추가하면 원래 요청에 해당 내용이 추가가 되어 실행된다<br/>

</li>
</ul>
<h3 id="응답-인터셉터">응답 인터셉터</h3>
<pre><code>instance.interceptors.response.use(
  (response) =&gt; {
    return response;
  },
  async (error) =&gt; {
    if (error.response?.status === 401) {
      // isTokenExpired() - 토큰 만료 여부를 확인하는 함수
      // tokenRefresh() - 토큰을 갱신해주는 함수
      if (isTokenExpired()) await tokenRefresh();

      const accessToken = getToken();

      error.config.headers = {
        &#39;Content-Type&#39;: &#39;application/json&#39;,
        Authorization: `Bearer ${accessToken}`,
      };

      // 중단된 요청을(에러난 요청)을 토큰 갱신 후 재요청
      const response = await axios.request(error.config);
      return response;
    }
    return Promise.reject(error);
  }
);</code></pre><ul>
<li>response를 사용하여 요청의 응답의 적용 할 수 있다</li>
<li>예시처럼 토큰만료 코드인 401을 응답 받았을때 토큰 재발행하는 함수를 실행하고 기존 요청에 새로운 토큰을 넣어 다시 시도하는 방법으로 많이 사용한다.</li>
</ul>
<br/>
<br/>

<blockquote>
<p>몇줄 안되지만 위처럼 모듈화를 해놓고 사용하면 개발할때 수고스러움을 많이 덜어준다. 개꿀</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[react-native-sqlite-storage 도입기]]></title>
            <link>https://velog.io/@young-jev/react-native-sqlite-storage-%EB%8F%84%EC%9E%85%EA%B8%B0</link>
            <guid>https://velog.io/@young-jev/react-native-sqlite-storage-%EB%8F%84%EC%9E%85%EA%B8%B0</guid>
            <pubDate>Thu, 14 Mar 2024 09:36:51 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>현업에서 간편 송금앱을 개발중인데 채팅 서비스도 포함이 되어있다. 개발하다보니 많은 문제점이 발생했다.</p>
<h2 id="문제점">문제점</h2>
<h3 id="1-생각보다-api조회가-너무-많다">1. 생각보다 Api조회가 너무 많다</h3>
<p>기존의 프로세스는 다음과 같다</p>
<ul>
<li>채팅방에 입장하면 방 정보와 채팅내용을 조회하여 렌더링한다</li>
<li>백그라운드에서 포그라운드 전환한 경우 데이터를 갱신한다. <del>채팅을 하다가 인스타하거나 유튜브 보거나</del></li>
</ul>
<p><img src="https://velog.velcdn.com/images/young-jev/post/1eb6e858-7159-473c-9fb2-2bcc5458ce2a/image.png" alt=""></p>
<p>앱에 접속해있는 상태에서는 websocket을 사용하기때문에 전혀 문제가 없었다. 하지만 백그라운드 전환한 경우에는 api통신을 통해 메시지 내역을 불러와야했다. 
백그라운드 상태일때 받은 메시지가 많으면 많을수록 데이터 통신양이나 성능에 문제가 보였고 무엇보다 채팅방에 들어올때마다 사용자에게 좋지 않은 UX를 주는게 별로였다. 사용자 입장에서 느리고 좋지않은 경험이다</p>
<h3 id="2-채팅방-처음-입장할때-대기시간이-생긴다">2. 채팅방 처음 입장할때 대기시간이 생긴다.</h3>
<p>처음 입장할때 메시지 내역을 조회한다. 아무리 페이징처리해서 소규모의 데이터만 가져온다해도 대기시간이 존재한다. 카카오톡을 비교해보면 채팅방에 들어가기 전부터 채팅내역이 있고 부드러운 경험을 주고 있는데 우리 앱은 방에들어면 몇초간 하얀 스크린을 보고있다. </p>
<p align="center"><img src="https://velog.velcdn.com/images/young-jev/post/fb7890c6-6873-4030-8e12-beea159b5083/image.jpeg" width="300" height="300"></p>

<h3 id="3-추가기능인-메시지-검색-기능-구현이-어렵다">3. 추가기능인 메시지 검색 기능 구현이 어렵다.</h3>
<p>카카오톡 기능중 아래 이미지와같이 메시지 검색기능이 있다 </p>
<p align="center"><img src="https://velog.velcdn.com/images/young-jev/post/568f5b9a-9586-49ee-9b79-00e8314d2e87/image.jpeg" width="300" height="300"></p>
우리앱에서도 구현을 하려고 할때 api통신을 통해 위치를 찾고 그만큼의 스크롤을 해줘야하는 로직이 필요하다.
구현중 백엔드개발자랑 협업중에 서버로직이 복잡하고 느리다라는 이야기를 듣게 되었고 백보다는 프론트에서 해결해야겠다는 생각이 들었다.

<h2 id="이슈-해결">이슈 해결</h2>
<p>위에 <strong>1번문제</strong>를 생각해봤을때 데이터를 프론트가 가지고있어서 api호출을 최소화하자는 생각이 들었다.
그러려면 데이터를 프론트에 저장을 해놔야한다 생각했고 로컬디비를 비교해 보았다.</p>
<ol>
<li>Firebase</li>
</ol>
<ul>
<li>데이터 타입이 단순,명료.</li>
<li>소규모 앱</li>
<li>온라인 동기화까지 고려</li>
<li>트래픽이 적으면 사용</li>
<li>배우기 쉬움</li>
<li>NoSQL</li>
</ul>
<ol start="2">
<li>Realm</li>
</ol>
<ul>
<li>Local DB 목적으로는 최적. (Cloud platform은 무조건 돈 받는다.)</li>
<li>빠른 속도</li>
<li>배우기 쉬움</li>
<li>NoSQL</li>
</ul>
<ol start="3">
<li><strong>SQLite</strong></li>
</ol>
<ul>
<li>관계형 DB</li>
<li>완전 무료, 오픈 소스</li>
<li>ORM DB에 익숙한 사람 (SQL문)</li>
</ul>
<p>각각의 장단점이 있고 상황에 맞게 쓰면 된다. 필자는 <strong>SQLite</strong> 골랐다 왜냐하면 그때는 구조가 복잡해질거같았고 스키마를 명확히 해야 할거 같았다 또, sql문이 더 익숙해서 골랐다 .
<del>하지만 지금와서는 잘못된거 같다. Realm으로 속도도 더 빠르고 생각보다 디비구조가 복잡하지않아서 오히려 더 무거워진 느낌이다. 일정을 보고 조만간 Realm으로 마이그레이션을 진행해볼까한다</del></p>
<h3 id="과정">과정</h3>
<ol>
<li>앱을 런칭하면 모든 로컬디비를 조회하여 아래와 같은 타입으로 가공해놓는다.</li>
</ol>
<p><strong>데이터 검색 효율</strong>때문에 Record타입으로 정의했다. 일반 배열을 검색하는거보다 빠른속도를 보장한다.</p>
<pre><code>messagesByRoomKey: Record&lt;string, IMessage[]&gt;;

export interface IMessage {
    ...
}</code></pre><ol start="2">
<li><p>채팅방에 들어오면 로컬디비에 마지막으로 저장된 값을 불러와서 그 값을 기준으로 이후의 메시지들을 조회하는 api를 사용한다.</p>
</li>
<li><p>조회된값을 로컬디비에 저장하면 다음에는 api 조회없이 로컬디비만으로 충분하다.</p>
</li>
</ol>
<h3 id="결과">결과</h3>
<ul>
<li>최소한의 api 조회만으로 구성했다.</li>
<li>메시지를 로컬 디비에 저장하고 미리 불러오는 과정으로 딜레이없이 렌더링을 향상했다.</li>
</ul>
<h3 id="보완해야할-점">보완해야할 점</h3>
<ul>
<li>처음부터 설계가 완벽하지못했던 점이 아쉽다. 그러다 보니 코드의 구조가 난잡하다. 혼자 개발하는게 아닌 이상 협업을 하기때문에 가독성을 보완해야 할 숙제가 생겼다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[안드로이드] 앱 삭제 했는데 데이터가 남아 있는 문제]]></title>
            <link>https://velog.io/@young-jev/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EC%82%AD%EC%A0%9C-%ED%96%88%EB%8A%94%EB%8D%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EB%82%A8%EC%95%84-%EC%9E%88%EB%8A%94-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@young-jev/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EC%82%AD%EC%A0%9C-%ED%96%88%EB%8A%94%EB%8D%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EB%82%A8%EC%95%84-%EC%9E%88%EB%8A%94-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Fri, 08 Mar 2024 00:16:17 GMT</pubDate>
            <description><![CDATA[<p>회사에서 코틀린으로 프로젝트를 진행중이었는데 분명 앱을 삭제하고 다시 설치를 했지만 shared preference 데이터가 복원되는 증상이 계속해서 발생했다.</p>
<h3 id="그래서-찾아보니-원인은-자동백업-때문이었다">그래서 찾아보니 원인은 자동백업 때문이었다</h3>
<p>Android API 23(또는 Android &quot;6.0&quot; 또는 Android &quot;Marshmellow&quot;)이후 BackupManager는 공유 환경 설정을 포함한 앱의 모든 데이터를 클라우드에 저장한다.</p>
<p>이는 안드로이드 6.0부터는 AndroidManifest.xml 파일에서 application 요소의 android:allowBackup 속성을 명시하지 않으면 자동으로 true로 설정하기 때문인데 이로 인해 사용자도 모르게 데이터가 구글 클라우드에 자동으로 백업된다.</p>
<p>결국 재설치를 하면 자료가 자동으로 백업이 되다보니 환경설정 데이터도 복원되는 것이다.</p>
<h3 id="해결방법">해결방법</h3>
<p>아래처럼 AndroidManifest.xml의 파일에 application요소에 allowBackup, fullBackupContent 요소를 false로 설정하면된다.</p>
<pre><code>// AndroidManifest.xml

 &lt;application
        ...
        android:allowBackup=&quot;false&quot;
        android:fullBackupContent=&quot;false&quot;
        ...
        &gt;</code></pre><h4 id="참고">참고</h4>
<p><a href="https://stackoverflow.com/questions/33169618/an-android-app-remembers-its-data-after-uninstall-and-reinstall">https://stackoverflow.com/questions/33169618/an-android-app-remembers-its-data-after-uninstall-and-reinstall</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WatchOS] React-Native와 WatchOS 연결하기]]></title>
            <link>https://velog.io/@young-jev/WatchOS-React-Native%EC%99%80-WatchOS-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@young-jev/WatchOS-React-Native%EC%99%80-WatchOS-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 28 Jun 2023 02:06:11 GMT</pubDate>
            <description><![CDATA[<p>저는 앱개발할때 React-Native를 주로 사용합니다
이번에 워치앱을 개발하고 싶어서 자료를 찾던중 자료찾기가 어려워서 했던 성공했던 내용 공유 드립니다</p>
<p>우선 환경은 React Native + SwiftUi 입니다</p>
<p>우선 React-Native 폴더에서 react-native-watch-connectivity 을 설치해줍니다.</p>
<pre><code>npm install react-native-watch-connectivity --save
# or
yarn add react-native-watch-connectivity

후에
cd ios
pod install</code></pre><h3 id="apptsx">App.tsx</h3>
<pre><code>import React, {useEffect, useState} from &#39;react&#39;;
import {
  Alert,
  SafeAreaView,
  Text,
  TextInput,
  TouchableOpacity,
} from &#39;react-native&#39;;
import {sendMessage, watchEvents} from &#39;react-native-watch-connectivity&#39;;

const App = () =&gt; {
  const [messageFromWatch, setMessageFromWatch] = useState(&#39;Waiting...&#39;);
  const [message, setMessage] = useState(&#39;&#39;);

  const messageListener = () =&gt;
    //여기로 메시지 들어옴 
    watchEvents.on(&#39;message&#39;, (message: any) =&gt; {
      setMessageFromWatch(message.watchMessage);
    });

  useEffect(() =&gt; {
    messageListener();
  }, []);

  return (
    &lt;SafeAreaView&gt;
      &lt;Text&gt;Received from Watch App!&lt;/Text&gt;
      &lt;Text&gt;{messageFromWatch}&lt;/Text&gt;
      &lt;Text&gt;Send to Watch App!&lt;/Text&gt;
      &lt;TextInput placeholder=&quot;Message&quot; onChangeText={setMessage}&gt;
        {message}
      &lt;/TextInput&gt;

      &lt;TouchableOpacity
        onPress={() =&gt;
          sendMessage(
            {messageFromApp: message},
            reply =&gt; {
              console.log(reply);
            },
            error =&gt; {
              if (error) {
                Alert.alert(
                  &quot;메시지 전송 실패 🤔&quot;,
                );
              }
            },
          )
        }&gt;
        &lt;Text&gt;SEND!&lt;/Text&gt;
      &lt;/TouchableOpacity&gt;
    &lt;/SafeAreaView&gt;
  );
};

export default App;
</code></pre><p>messageListener를 통해 워치로부터 메시지를 수신받고 sendMessage를 통해 워치로 메시지를 전달합니다. 간단하죠? </p>
<p><del>그리고 이제 스위프트 코드를 작성해야하는데 여기서 삽질 오지게함</del></p>
<p>잘따라오십시요 </p>
<h3 id="1-루트프로젝트에서-new--target-클릭">1. 루트프로젝트에서 new &gt; target 클릭</h3>
<p><img src="https://velog.velcdn.com/images/young-jev/post/da39efc1-e4bb-4b72-a302-79790bf7096e/image.png" alt=""></p>
<h3 id="2-watchos-탭에-app-클릭">2. watchOS 탭에 App 클릭</h3>
<p><img src="https://velog.velcdn.com/images/young-jev/post/0e9a5449-b4a2-4013-a301-acc7694e5ba0/image.png" alt=""></p>
<p>옛 버전에서는 watch app for ios 등등 여러개였었는데 통합된듯</p>
<h3 id="3-와치앱-이름과-identifier을-잘-입력해주시고-watch-app-for-existing-ios-app으로-기존-ios앱이랑-연결해주세요">3. 와치앱 이름과 Identifier을 잘 입력해주시고 Watch App for Existing iOS App으로 기존 iOS앱이랑 연결해주세요.</h3>
<p>이게 2번에서 말한 통합된 내용인듯합니다.
<img src="https://velog.velcdn.com/images/young-jev/post/25cc6c6b-b1ae-40eb-8936-4d5a79179d21/image.png" alt=""></p>
<p>저는 WatchApp이라는 이름으로 진행하겠습니다</p>
<h3 id="4-여기까지-진행하면-폴더가-다음처럼-구성될거에요">4. 여기까지 진행하면 폴더가 다음처럼 구성될거에요</h3>
<p><img src="https://velog.velcdn.com/images/young-jev/post/89c58da0-be47-4845-abbd-090240c4d4c8/image.png" alt=""></p>
<p>WachApp Watch App이 새로 생김, <del>이름뒤에 자동으로 뭐가 붙는데 거슬리기는 하나 튜토리얼이기에 넘어갑시다</del></p>
<h3 id="5-phoneconnectorswift-파일-생성합니다">5. PhoneConnector.swift 파일 생성합니다.</h3>
<p>폴더보면 PhoneConnector이라는 파일이있는데, 저는 미리 만들어놨기때문에 존재하는거지 원래 없습니다.</p>
<p>watchApp 폴더에서 new
<img src="https://velog.velcdn.com/images/young-jev/post/27138853-46a1-4e7a-90f3-e86c0963a8bb/image.png" alt="">
watchOS &gt; swift 파일 선택
<img src="https://velog.velcdn.com/images/young-jev/post/0c19db07-4c07-448f-9f39-c840f508c785/image.png" alt="">
PhoneConnector 생성
<img src="https://velog.velcdn.com/images/young-jev/post/6a90ab34-74d9-4ad3-aed9-c05bd05ea0ef/image.png" alt=""></p>
<h3 id="6-phoneconnector-코드-입력">6. PhoneConnector 코드 입력</h3>
<pre><code>import SwiftUI
import WatchKit
import WatchConnectivity


final class PhoneConnector: NSObject ,ObservableObject{
  @Published var receivedMessage = &quot;Waiting...&quot;

  var session: WCSession
  init(session: WCSession  = .default) {
    self.session = session
    super.init()
    if WCSession.isSupported() {
      session.delegate = self
      session.activate()
    }
  }
}

extension PhoneConnector: WCSessionDelegate {
  func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {

  }

  func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -&gt; Void) {

    guard let messageFromApp = message[&quot;messageFromApp&quot;] as? String else { return }


    DispatchQueue.main.async {
      self.receivedMessage = messageFromApp
    }
  }
}
</code></pre><p>라이브러리가 제공하는 기본 형태입니다. 대강 읽어보시면 세션만들어서 메시지받고 receivedMessage라는 메시지를 퍼블릭하게 공유한다 정도로 이해하면 됩니다.</p>
<h3 id="7-contentview-코드작성">7. ContentView 코드작성</h3>
<pre><code>import SwiftUI

struct ContentView: View {
  @ObservedObject var phoneConnector = PhoneConnector()

  var body: some View {
    VStack(alignment: .leading, spacing: 20) {
      VStack(alignment: .leading, spacing: 5) {
        Text(&quot;Send to Watch&quot;)
        Button {
          self.sendMessage()
        } label: {
          Text(&quot;Send&quot;)
        }
      }
      VStack(alignment: .leading, spacing: 5) {
        Text(&quot;Message from App&quot;)
        Text(self.phoneConnector.receivedMessage)
      }
    }
  }

  private func sendMessage() {
    let randomNumber = String(Int.random(in: 0..&lt;1000))
    let message: [String: Any] = [&quot;watchMessage&quot;: randomNumber]
    self.phoneConnector.session.sendMessage(message, replyHandler: nil) { (error) in
      print(error.localizedDescription)
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}</code></pre><p>phoneConnector를 연결해서 수신하고 랜덤 문자열을 송신하는 UI를 구성했습니다</p>
<h3 id="8-실행">8. 실행</h3>
<p><img src="https://velog.velcdn.com/images/young-jev/post/103d088f-128e-45c0-abff-2a0e4e720f9b/image.png" alt=""></p>
<p>watchApp을 타겟으로 실행해야합니다.
실행하면 메트로가 켜지고 ios랑 워치 시뮬레이터가 켜집니다.
<img src="https://velog.velcdn.com/images/young-jev/post/d428c180-b7e4-4ae2-8dce-d27684525913/image.png" alt=""></p>
<p>여기까지 오면 끝났습니다 </p>
<h3 id="테스트">테스트</h3>
<ol>
<li>워치 시뮬레이터에서 Send를 클릭합니다.</li>
<li>ios 시뮬레이터를 확인하면 수신이 확인됩니다.
<img src="https://velog.velcdn.com/images/young-jev/post/7c66cd5a-59ca-41e2-916d-2473964a4f37/image.png" alt="">
355라는 숫자가 새로 생겼죠? 워치앱 send를 누를때마다 갱신합니다.</li>
</ol>
<p>반대로도 테스트를 해보죠 </p>
<ol>
<li>ios 시뮬레이터에서 input을 입력하고 SEND! 클릭
<img src="https://velog.velcdn.com/images/young-jev/post/bb61aa09-a30f-42d6-b4e2-9ef1577fc31d/image.png" alt=""></li>
<li>워치 시뮬레이터를 확인하면 수신이 확인됩니다.
<img src="https://velog.velcdn.com/images/young-jev/post/b07de8d7-5723-4f8d-a7e3-d7515b311688/image.png" alt="">
잘가냐라는 메시지가 새로 생겼습니다.</li>
</ol>
<h3 id="마무리">마무리</h3>
<p>송수신 모두 잘되는거 확인했습니다.</p>
<p>해당 코드는 <a href="https://github.com/kkkkYoungJae/WatchConnectivityDemo">깃허브</a>에서 확인 할 수 있습니다.</p>
<p>다음에는 백그라운드도 올려볼게유 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Realtime database VS Firestore 무엇을 써야할까?]]></title>
            <link>https://velog.io/@young-jev/Realtime-database-VS-Firestore-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@young-jev/Realtime-database-VS-Firestore-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Fri, 16 Jun 2023 02:28:07 GMT</pubDate>
            <description><![CDATA[<h4 id="본-포스팅은-firebase에서-제공되는-realtime-database-와-firestore에-관해-과연-어떤-서비스를-써야-하는지-둘의-차이점은-무엇인지-알아보는-지극히-개인적인-내용입니다">본 포스팅은 Firebase에서 제공되는 Realtime database 와 Firestore에 관해 과연 어떤 서비스를 써야 하는지, 둘의 차이점은 무엇인지 알아보는 지극히 개인적인 내용입니다.</h4>
<hr>
<h2 id="realtime-database">Realtime database</h2>
<h4 id="1-비관계형-클라우드-데이터베이스">1. 비관계형 클라우드 데이터베이스</h4>
<ul>
<li>기존의 관계형 데이터베이스와 다른 접근 방식을 갖는다.</li>
<li>관계형에서 table의 역할은 비관계형에서는 Collection이 담당. Record(관계형)는 Document(비관계형)이 담당한다.</li>
</ul>
<h4 id="2-실시간이-가능하다">2. 실시간이 가능하다.</h4>
<ul>
<li>필요할 때 마다, HTTP 요청을 전송하고 응답 받아 데이터에 접근하는 방식이 아닌 동기화 방식을 사용한다.</li>
</ul>
<h4 id="3-오프라인-상태에서도-데이터를-유지할-수-있다">3. 오프라인 상태에서도 데이터를 유지할 수 있다.</h4>
<ul>
<li>오프라인 상태인 경우, App 로컬에 저장하고 있다가 네트워크가 연결되면 로컬에 저장했던 데이터를 동기화시킨다.</li>
</ul>
<h4 id="2-서버-없이-앱-만으로도-동작할-수-있다">2. 서버 없이 앱 만으로도 동작할 수 있다.</h4>
<ul>
<li>앱에서 직접 Firebase Realtime Database에 접근할 수 있기 때문에 별도의 서버 없이 동작할 수 있다.</li>
</ul>
</br>

<h2 id="firestore">Firestore</h2>
<p>Firebase에서 제공하는 또 다른 데이터베이스로, 비교적 최근에 나온 기능이다. </p>
<p>위에서 언급한 4가지의 기능(비관계형 데이터베이스, 실시간, 오프라인 가능, 서버 없이 동작 가능)은 똑같이 제공된다.</p>
<p>홈페이지에서 제공하는 주요 기능 리스트이다.
<strong>유연성, 표현형 쿼리, 실시간 업데이트, 오프라인 지원, 확장형 설계</strong>
<img src="https://velog.velcdn.com/images/young-jev/post/afab79b2-e725-4645-84e1-5d25351ad602/image.png" alt=""></p>
</br>

<h2 id="realtime-database-vs-cloud-firestore---차이점">Realtime Database vs Cloud Firestore  :: 차이점</h2>
<table>
<thead>
<tr>
<th align="left">Realtime Database</th>
<th>Cloud Firestore</th>
</tr>
</thead>
<tbody><tr>
<td align="left">데이터를 하나의 큰 json 덩어리로 저장함</td>
<td>문서 컬렉션으로 저장함.</td>
</tr>
<tr>
<td align="left">하나의 쿼리에는 필터링/정렬 하나만 가능함</td>
<td>하나의 쿼리에 정렬과 필터링 모두 가능함. (복합적인 쿼리 가능)</td>
</tr>
<tr>
<td align="left">깊고 좁은 쿼리 제공 -&gt; 결과값이 갖는 하위값 모두 반환하기 때문에 하위값에도 접근 가능함</td>
<td>얕고 넓은 쿼리 제공 -&gt; 결과 값이 갖는 컬렉션의 문서만 반환하고, 해당 문서가 하위에 컬렉션을 갖고 있더라도 반환 안함</td>
</tr>
<tr>
<td align="left">데이터 세트의 크기가 커질수록 쿼리 성능 떨어짐 -&gt; 깊고 좁은 쿼리를 제공하기 때문에 데이터 크기에 영향받음</td>
<td>데이터 세트의 크기는 쿼리 성능에 직접적인 영향 없음 -&gt; 하지만 요청 쿼리 결과에 따라 쿼리 성능이 영향 받을 수는 있음</td>
</tr>
<tr>
<td align="left">최대한 데이터 평면화 필요함 -&gt; 하위값도 모두 반환하기 때문에 최대한 평면적인 구조 필요함</td>
<td>하위값은 반환하지 않기 때문에 평면적인 구조는 영향이 적음</td>
</tr>
</tbody></table>
</br>

<h2 id="그렇다면-나는-어떤-데이터베이스를-써야할까">그렇다면 나는 어떤 데이터베이스를 써야할까?</h2>
<p>앱의 특성에 따라 권장하는 데이터베이스가 다르다.
각 데이터베이스별로 적합한 앱의 특성은 아래와 같다.</p>
<h4 id="1-realtime-database">1. Realtime Database</h4>
<ul>
<li>기본적인 데이터 동기화</li>
<li>적은 양의 데이터가 자주 변경</li>
<li>간단한 json 트리</li>
<li>많은 데이터베이스</li>
</ul>
<h4 id="2-cloud-firestore">2. Cloud Firestore</h4>
<ul>
<li><p>고급 쿼리, 정렬, 트랜젝션</p>
</li>
<li><p>대용량 데이터가 자주 읽힘</p>
</li>
<li><p>구조화된 컬렉션</p>
</li>
<li><p>단일 데이터베이스</p>
<p><a href="https://firebase.google.com/docs/database/rtdb-vs-firestore?hl=ko#key_considerations">권장 데이터베이스</a>의 체크리스트를 통해서 확인 할 수 있다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저 주소창에 www.google.com을 입력하면 어떤 일이 일어나나요?]]></title>
            <link>https://velog.io/@young-jev/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%A3%BC%EC%86%8C%EC%B0%BD%EC%97%90-www.google.com%EC%9D%84-%EC%9E%85%EB%A0%A5%ED%95%98%EB%A9%B4-%EC%96%B4%EB%96%A4-%EC%9D%BC%EC%9D%B4-%EC%9D%BC%EC%96%B4%EB%82%98%EB%82%98%EC%9A%94</link>
            <guid>https://velog.io/@young-jev/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%A3%BC%EC%86%8C%EC%B0%BD%EC%97%90-www.google.com%EC%9D%84-%EC%9E%85%EB%A0%A5%ED%95%98%EB%A9%B4-%EC%96%B4%EB%96%A4-%EC%9D%BC%EC%9D%B4-%EC%9D%BC%EC%96%B4%EB%82%98%EB%82%98%EC%9A%94</guid>
            <pubDate>Tue, 04 Oct 2022 13:06:57 GMT</pubDate>
            <description><![CDATA[<h3 id="1-wwwgooglecom을-브라우저-주소창에-입력한다">1. <a href="http://www.google.com%EC%9D%84">www.google.com을</a> 브라우저 주소창에 입력한다.</h3>
<h3 id="2-browser에-캐싱된-dns-기록들을-확인-wwwgooglecom-에-대응하는-ip주소가-있는지-확인한다">2. Browser에 캐싱된 DNS 기록들을 확인, <a href="http://www.google.com">www.google.com</a> 에 대응하는 IP주소가 있는지 확인한다.</h3>
<p><strong>DNS의 목적은 편의성 제공이며 캐싱은 네트워크 트래픽을 조절하고 데이터 전송 시간을 줄인다.</strong></p>
<ul>
<li>가장 먼저 <strong>브라우저 캐시</strong>를 확인한다. 브라우저는 일정기간 동안의 DNS 기록들을 저장하고 있다. DNS Query가 이 곳에서 가장 먼저 실행이 된다.</li>
<li>그 다음에는 <strong>OS 캐시</strong>를 확인한다. 브라우저 캐시에 웹사이트 이름의 IP 주소가 발견되지 않으면, 브라우저는 systemcall을 통해 OS가 저장하고 있는 DNS 기록들의 캐시에 접근한다.</li>
<li>그 다음에는 <strong>Router 캐시</strong>를 확인한다. 컴퓨터에 DNS 기록들을 찾지 못하면 브라우저는 DNS 기록을 캐싱하고 있는 Router와 통신을 해서 찾으려고 한다.</li>
<li>그래도 못 찾는다면 마지막으로, <strong>ISP 캐시</strong>를 확인한다. ISP는 DNS 서버를 구축하고 있고 브라우저가 마지막으로 DNS 기록이 있기를 바라며 접근한다.</li>
</ul>
<h3 id="3-요청한-url이-캐시에-없으면-isp의-dns-서버가-wwwgooglecom을-호스팅하고-있는-서버의-ip주소를-찾기-위해-dns-query를-날린다">3. 요청한 URL이 캐시에 없으면, ISP의 DNS 서버가 <a href="http://www.google.com%EC%9D%84">www.google.com을</a> 호스팅하고 있는 서버의 IP주소를 찾기 위해 DNS Query를 날린다.</h3>
<p>DNS Query의 목적은 여러 다른 DNS 서버들을 검색해서 해당 사이트의 IP주소를 찾는 것이다. 이러한 검색을 <strong>Recursive Search</strong>라고 부른다. IP주소를 찾을 때 까지 DNS서버에서 다른 DNS서버를 오가면서 반복적으로 검색한다.
이 상황에서, ISP의 DNS 서버를 DNS recursor라고 부르고 인터넷을 통해 다른 DNS 서버들에게 물어 물어 도메인의 이름의 올바른 IP 주소를 찾는데 책임을 갖고 있다.</p>
<h3 id="4-browser가-서버와-tcp-연결을-한다">4. Browser가 서버와 TCP 연결을 한다.</h3>
<p>브라우저가 올바른 IP 주소를 받게 되면 서버와 연결을 한다. 브라우저는 인터넷 프로토콜을 사용해서 서버와 연결이 된다. 웹사이트의 <strong>HTTP 요청의 경우 일반적으로 TCP를 사용한다.</strong>
클라이언트와 서버간 데이터 패킷들이 오가려면 TCP 연결이 되어야한다. <strong>TCP/IP three-way handshak</strong> 프로세스를 통해 연결이 이루어진다.</p>
<blockquote>
<p><strong>TCP/IP three-way handshak란?</strong></p>
</blockquote>
<ul>
<li>클라이언트 머신이 SYN 패킷을 서버에 보내고 연결을 열어달라고 물어본다.</li>
<li>서버가 새로운 연결을 시작할 수 있는 포트가 있다면 SYN/ACK 패킷으로 대답은 한다.</li>
<li>클라이언트는 SYN/ACK 패킷을 서버로부터 받으면 서버에게 ACK 패킷을 보낸다
이 과정이 끝나면 TCP 연결이 완성된다.</li>
</ul>
<h3 id="5-browser가-웹-서버에-http-요청을-한다">5. Browser가 웹 서버에 HTTP 요청을 한다.</h3>
<p>TCP 연결이 되어있다면, 데이터를 전송하면 된다. 클라이언트의 브라우저는 GET 요청을 통해 서버에게 <a href="http://www.google.com">www.google.com</a> 웹페이지(html)을 요구한다. 요청을 할 때 쿠키나 헤더 정보와 같은 자료들을 포함 할 수 있다.</p>
<h3 id="6-서버가-요청을-처리하고-response를-생성한다">6. 서버가 요청을 처리하고 response를 생성한다.</h3>
<p>서버는 웹서버를 가지고 있다. 이들은 브라우저로부터 요청을 받고 request handler한테 요청을 전달해서 요청을 읽고 response를 생성하게 된다. 이 request handler는 요청과 요청의 헤더, 쿠키를 읽어서 요청이 무엇인지 파악하고 필요하다면 서버에 정보를 업데이트 한다.</p>
<h3 id="7-서버가-http-response를-보낸다">7. 서버가 HTTP response를 보낸다.</h3>
<p>서버의 response에는 요청한 웹페이지, status code, compression type, 어떻게 인코딩 되어있는지, 어떻게 페이지를 캐싱할지 등이 포함된다.</p>
<h3 id="8-browser가-html-content를-보여준다">8. Browser가 HTML content를 보여준다.</h3>
<p>브라우저는 HTML content를 단계적으로 보여준다. 처음에는 HTML 스켈레톤을 렌더링하고 그 후에 HTML tag들을 체크해서 추가적으로 필요한 리소스등을 요청한다. 이 정적 우베페이지는 브라우저에 의해 캐싱이 되서 나중에 해당 페이지를 방문 할때 다시 서버로부터 요청하지 않는다. 드디어 원했던 <a href="http://www.google.com%EC%9D%98">www.google.com의</a> 모습이 보여지게 된다.</p>
]]></description>
        </item>
    </channel>
</rss>