<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>attosiss._.log</title>
        <link>https://velog.io/</link>
        <description>오늘보다 더 나은 내일이 되길 바라며</description>
        <lastBuildDate>Wed, 26 Feb 2025 11:48:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. attosiss._.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/attosisss_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[JAMstack]]></title>
            <link>https://velog.io/@attosisss_/JAMstack</link>
            <guid>https://velog.io/@attosisss_/JAMstack</guid>
            <pubDate>Wed, 26 Feb 2025 11:48:56 GMT</pubDate>
            <description><![CDATA[<h3 id="spasingle-page-application의-부흥">SPA(Single Page Application)의 부흥</h3>
<p><strong>Create React App (CRA)</strong> 를 통해 복잡한 초기 세팅 없이 React 기반의 SPA 사이트를 쉽게 만들 수 있게 되었습니다. 이런 편리함은 React 기반 웹 프로젝트 확산에 큰 기여를 했지만, 좋은 점만 있는 것은 아니었습니다. CRA로 빌드한 SPA 웹사이트는 CSR(Client Side Render)로 실행되었고, CSR의 약점도 그대로 물려받았기 때문입니다.</p>
<h3 id="spa의-단점">SPA의 단점</h3>
<p><strong>1. 초기 로딩 속도 문제</strong></p>
<ul>
<li>SPA 웹사이트는 사용자가 &quot;첫 의미 있는 페이지&quot;를 보기까지 시간이 오래 걸립니다.</li>
<li>React 프로젝트에서 규모가 커질수록 처음에 흰 화면이 오래 나오는 현상을 경험할 수 있습니다.</li>
<li>SPA는 웹 애플리케이션에 필요한 모든 정적 리소스를 최초에 한 번 다운로드하기 때문에 초기 구동 속도가 상대적으로 느립니다.</li>
</ul>
<p><strong>2. SEO(Search Engine Optimization) 문제</strong></p>
<ul>
<li><p>SPA로 제작된 웹사이트는 검색엔진 최적화(SEO)에 약합니다.</p>
</li>
<li><p>검색엔진의 웹 크롤러들은 JS를 실행하지 못하고 HTML에서만 콘텐츠를 수집하기 때문에, CSR로 실행되는 페이지를 빈 페이지처럼 인식합니다.</p>
</li>
<li><p>정보가 넘쳐나는 현대 사회에서 &quot;어떻게 하면 검색엔진에 잘 노출될 것인가?&quot;는 중요한 문제입니다.</p>
</li>
</ul>
<h3 id="해결책-ssr과-jamstack"><strong>해결책: SSR과 JAMstack</strong></h3>
<blockquote>
<p>&quot;SPA의 장점을 살리면서 SEO도 같이 챙길 수는 없을까?&quot;</p>
</blockquote>
<p>이를 위한 방안으로 React 개발자 사이에서 CSR에 SSR을 접목시킨 Next.js가 인기를 끌었고, 최근에는 Next.js만큼 주목받는 것이 있습니다.</p>
<p><strong>바로 JAMstack입니다.</strong></p>
<hr>
<h2 id="jamstack이란">JAMstack이란?</h2>
<p>JAMstack은 <strong>프런트엔드 웹 개발</strong> 방식으로, 정적 웹 사이트를 빠르게 제작하고 사용자에게 효율적으로 제공하는 접근 방식입니다.</p>
<p>JAMstack 애플리케이션에서는 가능한 한 많은 HTML을 미리 구축하여 <strong>콘텐츠 전송 네트워크(CDN</strong>) 에 저장합니다. 동적 콘텐츠를 생성하기 위해 서버 측에서 모놀리식 백엔드 애플리케이션을 실행하는 대신, 애플리케이션의 동적 구성 요소는 <strong>API 를 기반</strong>으로 합니다. 이를 통해 <strong>사용자경험은 더 빠르고, 개발자경험은 더 단순</strong>해집니다.</p>
<hr>
<h2 id="jamstack의-구성-요소">JAMstack의 구성 요소</h2>
<blockquote>
<p>📌JAMstack의 &#39;JAM&#39;은 *<em>JavaScript, API, Markup *</em>을 의미합니다.</p>
</blockquote>
<p><strong>JavaScript</strong>: 클라이언트 측 기능을 담당하는 프로그래밍 언어</p>
<ul>
<li>Client의 모든 처리는 Javascript가 수행합니다.</li>
</ul>
<p><strong>API</strong>: 다른 프로그램이나 애플리케이션에서 데이터를 요청하는 인터페이스</p>
<ul>
<li>모든 기능 및 비지니스 로직은 재사용 가능한 API가 처리합니다.</li>
</ul>
<p><strong>Markup</strong>: 브라우저에 서식 정보를 제공하는 HTML과 CSS</p>
<ul>
<li>여기서 핵심은 Markup인데 , 만드는 방법은 여러 가지가 있다!</li>
</ul>
<blockquote>
<ol>
<li>HTML을 직접 작성한다</li>
<li>Template Engine 와 같은 툴을 사용한다.</li>
<li>정적 사이트 생성기(Static Site Generator, SSG)를 이용해서 Static HTML을 생성한다.
ex) Gatsby, Next.js, Gridsome, Nuxt.js, Hugo, Jekyll 등</li>
</ol>
</blockquote>
<p>이렇게 만들어진 Static HTML은 웹 서버의 리소스를 사용할 필요없이 <strong>사용자에게 HTML만 전달</strong>해주면 됩니다. Static HTML은 CDN을 통해서 Cache하고 배포해서 빠른 속도를 유지하고, 따로 동적으로 HTML을 생성하지 않기 때문에 웹서버가 필요없어서 서버 비용이 크지 않습니다.</p>
<p>(하지만 모든 HTML을 Static HTML로 만들라는 뜻은 아닙니다.모든 Markup 을 정적으로 유지하게 되면, 최신 데이터를 유지하기 어렵기 때문입니다.)</p>
<hr>
<h3 id="jamstack-vs-전통적인-웹-개발">JAMstack vs 전통적인 웹 개발</h3>
<p><em>예제 ) 유럽 축구 경기 결과를 업데이트하는 웹 애플리케이션</em></p>
<p>❌ <strong>기존 방식</strong></p>
<ol>
<li>서버에서 실행되는 백엔드 애플리케이션이 지속적으로 최신 점수를 확인.</li>
<li>사용자가 페이지를 요청하면 서버가 HTML을 생성 하여 사용자에게 전달.</li>
<li>서버가 실행되고 HTML이 생성될 때까지 대기 시간이 발생.</li>
</ol>
<p>➡️ 결과: 속도가 느리고, 백엔드 유지 관리가 필요함.</p>
<p>✅ <strong>JAMstack 방식</strong></p>
<ol>
<li>HTML을 사전 생성 하여 CDN에 저장.</li>
<li>사용자가 페이지를 요청하면 가까운 CDN에서 즉시 전송.</li>
<li>최신 점수는 API 호출을 통해 실시간으로 업데이트.</li>
</ol>
<p>➡️ 결과: 빠른 로딩 속도 + 백엔드 유지 보수 감소</p>
<hr>
<h2 id="jamstack-애플리케이션의-백엔드-처리-방식">JAMstack 애플리케이션의 백엔드 처리 방식</h2>
<p>JAMstack에서는 서버가 아닌 API를 활용 하여 백엔드 기능을 처리합니다.</p>
<p><strong>기존 방식</strong>: 개발자가 직접 백엔드 애플리케이션을 구축 및 유지보수.
<strong>JAMstack 방식</strong>: <strong>API를 활용</strong>하여 백엔드를 대체하거나 필요한 경우 API를 직접 제작.</p>
<h3 id="마이크로서비스와의-관계">마이크로서비스와의 관계</h3>
<p>JAMstack은 <strong>API 기반</strong>이므로, <strong>마이크로서비스 아키텍처 와 밀접한 관련</strong>이 있습니다.</p>
<ol>
<li>필요할 때만 API를 호출하여 서버 자원을 절약.</li>
<li>특정 기능별로 독립적인 API를 활용하여 유지보수가 쉬움.</li>
<li>서버리스(Serverless) 환경과 결합하여 효율적인 백엔드 운영 가능.</li>
</ol>
<hr>
<h2 id="jamstack의-장점">JAMstack의 장점</h2>
<p>⭐️ <strong>성능 향상</strong>: CDN에서 제공되는 정적 HTML 파일을 활용하여 빠른 로딩 속도 제공.</p>
<p>⭐️ <strong>확장성</strong>: 프런트엔드는 가볍고, 백엔드는 API 호출 방식으로 유연하게 확장 가능.</p>
<p>⭐️ <strong>개발자 경험 개선</strong>: 백엔드 유지 관리 부담을 줄이고, 프런트엔드 개발에 집중 가능.</p>
<p>⭐️ <strong>보안 강화</strong>: 서버 측 공격에 취약한 전통적인 백엔드 구조를 배제하여 보안성 향상.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS 알고리즘] 문제 해결 방식]]></title>
            <link>https://velog.io/@attosisss_/JS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@attosisss_/JS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Thu, 10 Oct 2024 02:06:30 GMT</pubDate>
            <description><![CDATA[<h2 id="알고리즘의-정의와-필요성-그리고-방법">알고리즘의 정의와 필요성, 그리고 방법</h2>
<blockquote>
<p> <strong>알고리즘이란 무엇인가 ?</strong>
  : 특정 작업을 수행하기 위한 프로세스 또는 일련의 단계를 의미한다.</p>
</blockquote>
<ul>
<li><p>왜 알아야 할까 ?</p>
<ul>
<li><p>프로그래밍과 관련된 대부분의 것들은 일종의 알고리즘을 포함한다. 문제 해결을 잘하고 좋은 개발자가 되기 위해서 기초적인 것이며, 면접에서도 항상 나온다.</p>
</li>
<li><p>그러면 어떻게 잘할 수 있을까 ?</p>
</li>
<li><p>문제 해결을 위한 계획을 고안한다.</p>
</li>
</ul>
<ol>
<li>문제를 이해한다.</li>
<li>구체적인 예시들을 살펴본다.</li>
<li>설명해본다(break it down)</li>
<li>풀어보고 단순화한다</li>
<li>다시 보고 리팩토링한다</li>
</ol>
<ul>
<li>일반적인 문제 해결 패턴을 마스터한다.</li>
</ul>
<hr>
<h2 id="1단계--문제-이해하기">1단계 : 문제 이해하기</h2>
<ul>
<li>면접에서 문제를 풀기 위한 단계</li>
</ul>
</li>
</ul>
<ol>
<li>문제를 자신의 말로 설명할 수 있는가 ?</li>
<li>문제에 들어가는 입력은 무엇인가 ?</li>
<li>문제에 대한 솔루션에서 나오는 결과는 무엇인가?</li>
<li>입력에서 출력을 결정할 수 있는가? 다시 말하자면, 문제를 풀기 위한 충분한 정보를 갖고 있는가 ?</li>
<li>문제의 일부인 중요한 데이터 조각에 어떻게 레이블을 지정해야 하는가 ?</li>
</ol>
<ul>
<li><p>예시: 두 개의 숫자를 받아 합계를 반환하는 함수를 작성하라.</p>
<ol>
<li>더하기를 실행하라</li>
<li>정수인지 실수인지 수? 숫자의 크기는?(대부분 언어에는 상한선이 있다.)</li>
<li>정수? 실수? 문자열 ?</li>
<li>만약 숫자 하나만 입력된다면?</li>
</ol>
<hr>
</li>
</ul>
<h2 id="2단계--구체적인-예시들을-살펴본다">2단계 : 구체적인 예시들을 살펴본다.</h2>
<ul>
<li>과정<ol>
<li>단순한 예시부터 시작한다.</li>
<li>복잡한 예시로 진전한다.</li>
<li>빈 입력값과 관련된 예시를 살펴본다.</li>
<li>잘못된 입력값과 관련된 예시를 살펴본다.</li>
</ol>
</li>
</ul>
<hr>
<h2 id="3단계--분해한다설명한다break-it-down">3단계 : 분해한다.설명한다(break it down)</h2>
<ul>
<li>진행해야 할 단계들을 명시적으로 써본다<ul>
<li>이렇게 하면 코드를 작성하기 전에 작성할 코드에 대해 생각해야하며, 자세히 알아보기 전에 개념적 오해가 남아있는 부분을  파악하고 세부사항(예: 언어구문)에 대해서도 살펴보게 된다. </li>
</ul>
</li>
</ul>
<hr>
<h2 id="4단계--문제-풀고-단순화하기">4단계 : 문제 풀고 단순화하기</h2>
<ul>
<li>과정<ul>
<li>하려는 일의 핵심적인 어려운 점을 찾고
그 어려운 부분을 일단 일시적으로 무시하여</li>
<li>우선 단순화된 솔루션부터 작성한다</li>
<li>그런 다음 그 어려운 부분에 대하여 고민하고 결과물을 통합한다
예시 : 문자열을 받아서 문자열의 각 문자의 개수를 반환하는 함수를 작성하라.</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">
function charCount(str) {
    // 마지막에 반환할 빈 객체를 만든다
    const result = {};
    // 문자열의 각 문자를 돌면서
    for(let i=0; i&lt;str.length; i++) {
        const char = str[i];
        // 만약 객체에 해당 문자가 이미 들어갔으면, 문자의 개수에 1을 더한다
        if(result[char] &gt; 0) {
            result[char]++;
        }
        // 만약 객체에 해당 문자가 없다면, 객체에 더해주고 값을 0으로 지정한다
        else {
            result[char] = 1;
        };
    // 객체를 반환한다
    return result;
}
</code></pre>
<hr>
<h2 id="5단계--되돌아보고-리팩토링하라">5단계 : 되돌아보고 리팩토링하라.</h2>
<ul>
<li>리팩토링 체크 리스트<ul>
<li>결과를 확인할 수 있습니까?</li>
<li>결과를 다르게 도출할 수 있습니까?</li>
<li>한눈에 이해가 되시나요?</li>
<li>결과나 방법을 다른 문제에 사용할 수 있습니까?</li>
<li>솔루션의 성능을 향상시킬 수 있습니까?</li>
<li>리팩토링할 다른 방법을 생각할 수 있습니까?</li>
<li>다른 사람들은 이 문제를 어떻게 해결했습니까?</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">function charCount(str) {
  const result = {};
  for (let i = 0; i &lt; str.length; i++) {
    // 모두 소문자로 통일한다
    const char = str[i].toLowerCase();
    // 영문자와 숫자인 경우만 처리하도록 조건 추가
    if (/[a-z0-9]/.test(char)) {
      if (result[char] &gt; 0) {
        result[char]++;
      } else {
        result[char] = 1;
      }
    }
  }
  return result;
}
console.log(charCount(&#39;helloㄴㅇㄹ123&#39;));</code></pre>
<p>위 예시를 아래와 같이 리팩토링 할 수 있다 ⬇️</p>
<pre><code class="language-jsx">function charCount(str) {
  const result = {};
  for (let char of str) {
    // 모두 소문자로 통일한다
    char = char.toLowerCase();
    // 영문자와 숫자인 경우만 처리하도록 조건 추가
    if (/[a-z0-9]/.test(char)) {
      // 값이 이미 있는(truthy) 경우 좌측 실행, 없는 경우(falsey) 우측 실행
      result[char] = ++result[char] || 1;
    }
  }
  return result;
}
console.log(charCount(&#39;helloㄴㅇㄹ123&#39;));</code></pre>
<p>⬇️ charCodeAt을 사용하여 함수 분리하는 방법으로 또 리팩토링 
: 정규표현식으로 인한 혹시모를 문제를 방지하고, 가독성이 좋다!</p>
<pre><code class="language-jsx">function charCount(str) {
  const result = {};
  for (let char of str) {
    if (isAlphaNumeric(char)) {
      char = char.toLowerCase();
      result[char] = ++result[char] || 1;
    }
  }
  return result;
}

function isAlphaNumeric(char) {
  const code = char.charCodeAt(0);
  if (
    !(code &gt; 47 &amp;&amp; code &lt; 58) &amp;&amp; // numeric(0-9)
    !(code &gt; 64 &amp;&amp; code &lt; 91) &amp;&amp; // upper alphabet(A-Z)
    !(code &gt; 96 &amp;&amp; code &lt; 123) // lower alphabet(a-z)
  ) {
    return false;
  }
  return true;
}

console.log(charCount(&#39;helloㄴㅇㄹ123&#39;));</code></pre>
<ul>
<li>이런 식으로 리팩토링할 수 있고 , 이러한 과정이 실력 향상에 중요하다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS 알고리즘] 배열 및 객체의 성능 분석]]></title>
            <link>https://velog.io/@attosisss_/JS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B0%B0%EC%97%B4-%EB%B0%8F-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%84%B1%EB%8A%A5-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@attosisss_/JS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B0%B0%EC%97%B4-%EB%B0%8F-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%84%B1%EB%8A%A5-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Wed, 25 Sep 2024 01:16:43 GMT</pubDate>
            <description><![CDATA[<h2 id="object">Object</h2>
<p><em>특징 : Unordererd , key value pairs</em></p>
<h4 id="객체를-언제-사용하는가-">객체를 언제 사용하는가 ?</h4>
<ol>
<li>순서가 필요 없을 때</li>
<li>빠른 접근(삽입, 제거)이 필요할 때</li>
</ol>
<h4 id="big-o-of-objects">Big O of Objects</h4>
<ul>
<li>Insertion - O(1)</li>
<li>Removal - O(1)</li>
<li>Searching - O(N)</li>
<li>Access - (O1)</li>
</ul>
<p>---&gt; 따라서 , 순서가 필요 없을 때 객체는 최고의 선택이다.</p>
<h4 id="객체-메서드의-big-o">객체 메서드의 Big O</h4>
<ul>
<li>Object.keys - O(N)</li>
<li>Object.values - O(N)</li>
<li>Object.entries - O(N)</li>
<li>hasOwnProperty - O(N)</li>
</ul>
<h2 id="array">Array</h2>
<p><em>특징 : Ordered lists</em></p>
<h4 id="array를-언제-사용하는가-">Array를 언제 사용하는가 ?</h4>
<ul>
<li><p>순서가 필요할 때</p>
</li>
<li><p>빠른 접근(Access)이 필요할 떄</p>
<ul>
<li>Array의 요소 접근은 항상 빠르지만 , 삽입이나 제거는 상황에 따라 다르다.</li>
</ul>
</li>
<li><p>Big O of Arrays</p>
<ul>
<li>Insertion - 상황에 따라 가변</li>
<li>Removal - 상황에 따라 가변</li>
<li>Searching - O(N)</li>
<li>Access - O(1)</li>
</ul>
</li>
<li><p>Insertion, Removal</p>
<ul>
<li>배열의 끝에 추가 , 삭제는 O(1)이다.</li>
<li>그러나 배열의 시작지점(중간도 마찬가지)에 추가 , 삭제는 기존의 모든 요소의 위치를 변경하게 되므로, O(N)이다.<ul>
<li>따라서 배열의 시작 부분에서 추가(unshift) 및 제거(shift) 작업은 마지막 부분에 추가 또는 제거를 하는 것으로 대체할 수 있다면 피하는 것이 좋다.</li>
<li>push, pop 메서드가 shift, unshift 메서드보다 항상 빠르다.
(시작에 넣거나 빼면 처음부터 끝까지 영향을 받으면서 전부 인덱스를 다시 정리해야하기 때문)</li>
</ul>
</li>
</ul>
</li>
<li><p>배열 메서드의 Big O</p>
<ul>
<li>O(1)<ul>
<li>push , pop</li>
</ul>
</li>
</ul>
<ul>
<li><p>O(N)</p>
<ul>
<li>shift, unshift</li>
<li>concat</li>
<li>slice, splice</li>
<li>forEach / map / filter / reduce / etc</li>
</ul>
</li>
<li><p>O(N*logN)</p>
<ul>
<li>sort</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS 알고리즘] Big O 표기법]]></title>
            <link>https://velog.io/@attosisss_/JS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Big-O-%ED%91%9C%EA%B8%B0%EB%B2%95</link>
            <guid>https://velog.io/@attosisss_/JS-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Big-O-%ED%91%9C%EA%B8%B0%EB%B2%95</guid>
            <pubDate>Tue, 24 Sep 2024 04:22:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/attosisss_/post/96e1e5d2-a6d5-42e2-b4c2-5f688fe4a5ac/image.png" alt=""></p>
<pre><code> Udemy &#39;JavaScript 알고리즘 &amp; 자료구조 마스터클래스&#39; 강좌를 바탕으로 작성한 글입니다.</code></pre><h2 id="big-o-표기법">Big O 표기법</h2>
<p>같은 기능을 구현하는데도 여러 가지 접근 방법이 있다.</p>
<p>무엇이 최선인지 , 코드의 성능(performance)을 비교하는 방법 , 코드의 성능에 대해 정확한 어휘로 말할 수 있는 것이 중요하다.
코드가 느리거나 충돌할 때 , 문제가 발생할 수 있는 부분을 찾아내는 데 유용하다!</p>
<ul>
<li><strong>더 좋은 코드는 무엇인가 ?</strong><ul>
<li>더 빠른? <ul>
<li>브라우저에 내장된 timer를 사용하여 속도를 잴 수 있다.</li>
<li>시간 측정의 문제<ul>
<li>컴퓨터나 브라우저 사양에 따라 다른 시간이 기록될 수 있다.<ul>
<li>같은 머신도 다른 시간을 매번 측정할 수 있다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>메모르를 덜 쓰는?</li>
<li>가독성이 좋은 ?</li>
</ul>
</li>
</ul>
<hr>
<h2 id="timing-our-code">Timing Our Code</h2>
<ul>
<li><strong>console.time 으로 타이머 사용</strong><ul>
<li>아래 코드처럼 반복문 대신 공식을 사용하여 연산한 결과 , 시간복잡도가 낮았음을 확인할 수 있다 .</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">function addUpTo(n) {
  let total = 0;
  for (let i = 1; i &lt;= n; i++) {
    total += i;
  }
  return total;
}
console.time(&#39;test&#39;);
console.log(addUpTo(10000000000));
console.timeEnd(&#39;test&#39;);

//50000000000067860000
//test: 7.596s</code></pre>
<pre><code class="language-jsx">function addUpTo(n) {
  return (n * (n + 1)) / 2;
}

console.time(&#39;test&#39;);
console.log(addUpTo(10000000000));
console.timeEnd(&#39;test&#39;);

//50000000005000000000
//test: 3.998ms</code></pre>
<ul>
<li>브라우저 빌트인 타이머
: 확실히 반복문은 입력값이 커질수록 느려지는 것이 확인된다. 반면에 공식은 아무리 수가 커져도 속도가 항상 빠르다.</li>
</ul>
<pre><code class="language-jsx">function addUpTo(n) {
  let total = 0;
  for (let i = 1; i &lt;= n; i++) {
    total += i;
  }
  return total;
}

let t1 = performance.now();
console.log(addUpTo(1000000000));
let t2 = performance.now();

console.log(`Time Elapsed: ${(t2-t1) / 1000} seconds.`)
//50000000000067860000
//Time Elapsed: 7.5107000000001864 seconds.</code></pre>
<pre><code class="language-jsx">function addUpTo(n) {
  return (n * (n + 1)) / 2;
}

let t1 = performance.now();
console.log(addUpTo(10000000000));
let t2 = performance.now();

console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);
//50000000005000000000
//Time Elapsed: 0.00009999999962747097 seconds.
</code></pre>
<p><strong>시간의 문제</strong></p>
<ul>
<li>다른 컴퓨터는 동일한 코드에 대해서도 다른 시간을 기록할 수 있다.</li>
<li>같은 컴퓨터도 다른 시간을 매번 기록할 수 있다.</li>
<li>빠른 알고리즘에게는 속도 측정이 충분히 정확하지 않을 수 있다.</li>
</ul>
<p>** 코드에 타이밍을 설정해보지 않더라도, 시간복잡도와 같은 효율성을 이해할 수 있는 방법이 있다.**</p>
<ul>
<li>Big O 표기법</li>
</ul>
<p><strong>Performance Tracker로 각 알고리즘의 시간복잡도를 시각화하여 확인해볼 수 있다. &gt; <a href="https://rithmschool.github.io/function-timer-demo/">Performance Tracker</a></strong></p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/97c72474-28b3-492d-848b-6a7b7cc39fa5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/aa5b4d59-0409-44e2-9ecd-9c60a4be7bae/image.png" alt="">
<img src="https://velog.velcdn.com/images/attosisss_/post/eb8d87aa-04a9-448a-b411-143cc22ca886/image.png" alt=""></p>
<p><strong>BigO</strong>는 입력이 증가할 때마다 알고리즘의 <strong>런타임이 어떻게 증가하는지에 대하여 형식적으로 설명할 수 있게</strong> 해준다. 디테일은 신경 쓰지 않고 오직 광범위한 경향만 본다.</p>
<hr>
<h2 id="시간-복잡도">시간 복잡도</h2>
<p>Time complexity</p>
<ul>
<li>BigO 는 광범위한 추세만 보기에 상수는 중요하지 않다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/172a59ac-805b-4354-a397-53294633540b/image.png" alt=""></p>
<hr>
<h2 id="공간-복잡도">공간 복잡도</h2>
<p>Space Complexity</p>
<ul>
<li>자바스크립트에서 공간 복잡도(경험 법칙 , Rules of Thumb)<ul>
<li>대부분의 원시 값(boolean , number , undefined, nulls)들은 정채진 공간을 동일하게 갖는다.
(숫자가 아무리 크거나 true나 false라고 해도)</li>
<li>String은 O(n)의 공간을 요구한다(String의 길이가 n이다)</li>
<li>참조 값들은 일반적으로 O(n)의 공간을 갖는다.(Array는 길이가 n이고, Object에서는 key들의 갯수가 n이다)</li>
</ul>
</li>
</ul>
<hr>
<h2 id="logarithm-시간-복잡도">Logarithm 시간 복잡도</h2>
<ul>
<li>Log는 지수(exponentiation)의 역수(inverse)이다.
: 나눗셈과 곱셈이 쌍인 것처럼 로그와 지수도 쌍이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/4995fea9-8330-4b03-8f8a-92991088f394/image.png" alt=""></p>
<p>The logarithm of a number roughly measures the number of times you can divide that number by 2 <strong>before you get a value that&#39;s less than or equal to one.</strong></p>
<p>: 로그는 어떤 숫자에 대하여 2로 나누면서 1이하의 값을 얻게 될때까지 걸리는 횟수를 대략적으로 측정한 숫자를 의미한다.</p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/eea7f950-8ecd-40da-8158-8fb2f90ec2a3/image.png" alt=""></p>
<p><strong>로그는 언제 사용하나 ?</strong></p>
<ul>
<li>Certain searching algorithms have logarithmic time complexity. Efficient     sorting algorithms involve logarithms. Recursion sometimes involves             logarithmic space complexity.<ul>
<li>특정 검색 알고리즘에는 로그 시간 복잡도와 관련된다.<ul>
<li>효율적인 정렬 알고리즘에는 로그가 관련있다.</li>
<li>재귀는 때때로 로그 공간 복잡도와 관련이 있다.
(참고) <a href="https://visualgo.net/ko"> 자료구조와 알고리즘 시각화 사이트</a></li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[블록체인]]></title>
            <link>https://velog.io/@attosisss_/%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-056gtgjr</link>
            <guid>https://velog.io/@attosisss_/%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-056gtgjr</guid>
            <pubDate>Tue, 16 Jan 2024 09:45:20 GMT</pubDate>
            <description><![CDATA[<h2 id="블록체인-정의">블록체인 정의</h2>
<blockquote>
<p><strong>블록체인 뜻 :</strong>
블록체인이란 <strong>정보를 기록하고 저장하는 탈중앙화 시스템</strong>이다. 일련의 순서로 연결된 데이터 단위(&#39;블록&#39;)로 구성된 일종의 분산 장부 기술로 , 각 블록에는 이전 블록의 고유 번호가 담겨 있어 <strong>체인을 형성</strong>한다. 일종의 데이터베이스 역할을 하며, 암호화폐에 활용되는 것으로 가장 널리 알려져있다. 투명성과 탈중앙화 장점을 활용하여 스마트 계약이나 거버넌스 합의 플랫폼의 역할을 할 수도 있다.</p>
</blockquote>
<p>블록체인 개념은 일반적으로 특정 블록체인을 지칭하기보다는 블록체인 기술 전체를 의미한다.</p>
<p>블록체인은 시스템을 변경,해킹 또는 속이는 것을 어렵거나 불가능하게 만드는 탈중앙화 방식으로 정보를 기록하는 시스템이다. 다시 말해, <strong>블록체인이란 특정한 프로토콜에 의해 운영되는 컴퓨팅 시스템 네트워크 전체에 복제 및 배포되는 디지털 장부다.</strong></p>
<hr>
<h2 id="블록체인-기술은-어떻게-작동할까">블록체인 기술은 어떻게 작동할까?</h2>
<h3 id="블록체인-기본-개념">블록체인 기본 개념</h3>
<p>이름에서 알 수 있듯이 블록체인은 일련의 &#39;블록&#39;으로 구성돈다. &#39;블록&#39;이라는 용어는 블록체인 코드 내의 데이터 저장 단위를 의미한다. 각 블록에는 이전 블록의 고유 번호가 포함되어 있으며, 이를 통해 블록의 &#39;체인&#39;이 형성된다.</p>
<h3 id="블록체인-원리">블록체인 원리</h3>
<p>특정 블록체인에서 처음으로 생성되는 블록을 제네시스 블록이라고 한다. 이러한 첫번째 블록은 데이터베이스의 첫 번째 줄이라고 생각하면 된다. 그 자체로는 작은 데이터 모음에 불과하다. 그러나 여기에는 0001과 같은 고유한 숫자 기호가 포함된다. 두 번째 블록이 추가되면 이 블록은 0002 블록으로 식별되며 0001블록의 뒤를 따른다는 정보를 포함한다. 
이것이 체인이 형성되는 방식이다.</p>
<h3 id="블록체인-프로토콜">블록체인 프로토콜</h3>
<p>블록체인 프로토콜은 개별 애플리케이션 개발에 사용할 수 있는 다양한 유형의 블록체인 플랫폼을 의미한다.
각 블록체인 프로토콜은 기본 블록체인 원칙을 특정 산업 또는 애플리케이션에 맞게 조정한다.</p>
<hr>
<h2 id="블록체인-탈중앙화">블록체인 탈중앙화</h2>
<p>단일 서버에 저장되는 일반 데이터베이스와 달리 블록체인은 탈중앙화되어 P2P(중앙 서버를 거치지 않고 클라이언트 컴퓨터끼리 직접 통신하는 방식)네트워크에 분산되어 있다.</p>
<p>네트워크의 각 노드(일반적으로 장치 또는 일종의 서버)에는 블록체인의 사본이 있다. 새 블록이 추가되기 전에 네트워크의 대부분의 노드 운영자는 이를 확인하고 검증해야한다.
이는 불특정 다수의 사람들이 체인의 무결성을 변조하거나 중요한 정보가 누락되는 위협(예: 이전 블록에 올바르게 연결되지 않는 블록 추가 등)을 제한하는 데 도움이 된다.</p>
<p>예를 들어, 가장 유명한 암호화폐인 비트코인의 블록체인은 이미 13,000개 이상의 노드를 보유한 것으로 알려져있다. 이것이 바로 사람들이 암호화폐와 블록체인을 탈중앙화라고 설명할 때 의미하는 바이다.</p>
<p>이러한 블록체인 이코노미와 블록이 코딩되는 방식 덕분에 일단 정보가 추가되면 체인에서 정보를 제거하기 매우 어렵다.</p>
<hr>
<h2 id="블록체인-장점">블록체인 장점</h2>
<h3 id="데이터-무결성">데이터 무결성</h3>
<p>탈중앙화 네트워크 시스템으로 인해 데이터 조작 권한을 가진 특정 사람이 존재하지 않는다. 따라서 임의로 블록체인에 저장된 데이터를 변조하기가 어렵다. 일단 정보가 체인에 포함되면, 추가한 지 몇 년이 지난 후에 검토하더라도 해당 데이터의 무결성과 정확성을 확신할 수 있다.</p>
<h3 id="더-높은-투명성">더 높은 투명성</h3>
<p>대부분의 블록체인은 매우 투명하여 외부 관찰자가 체인에 저장된 데이터를 볼 수 있다, 이는 암호화폐에 특히 중요하다. 블록체인 지갑을 통해 자금의 흐름을 추적하는 것은 개인 은행 계좌를 통해 추적하는 것보다 훨씬 쉽다.이러한 투명성 덕분에 사기꾼을 더 쉽게 식별하고 추적할 수 있다.</p>
<h3 id="탈중앙화된-제어">탈중앙화된 제어</h3>
<p>블록체인 기술은 탈중앙화라는 개념을 기반으로 한다. 은행이나 신용카드 회사와 같은 대규모 조직에 의존하지 않고 사용자의 데이터와 돈을 처리한다. P2P 원장 시스템은 특정 개인이나 단체가 블록체인을 과도하게 통제하는 것을 방지한다.</p>
<hr>
<h2 id="블록체인-단점">블록체인 단점</h2>
<h3 id="규제-부족">규제 부족</h3>
<p>블록체인 기술은 규제가 덜하기 때문에 블록체인에 저장한 데이터를 도난당할 경우 손실을 복구하기가 훨씬 더 어렵다. 블록체인 지갑에 암호화폐가 있는데 누군가 지갑에 엑세스한 경우, 해당 화폐를 돌려받을 수 있도록 도와줄 은행이나 중앙 기관이 없다.</p>
<p>지나친 투명성
투명성에는 단점도 있다. 블록체인의 대부분의 데이터는 대부분의 사람들이 접근할 수 있기 때문에 민감한 정보를 저장하기에 좋은 장소가 아니다. 해싱 기술은 데이터 소유자를 숨기지만, 데이터 자체는 여전히 공개되어 있다. 그렇기 때문에 정부 기록 보관이나 의료 데이터에 블록체인을 사용하자는 제안에 회의적인 태도를 취해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Blockchain - 하이퍼레저 패브릭(Hyperledger Fabric)]]></title>
            <link>https://velog.io/@attosisss_/Blockchain-%ED%95%98%EC%9D%B4%ED%8D%BC%EB%A0%88%EC%A0%80-%ED%8C%A8%EB%B8%8C%EB%A6%ADHyperledger-Fabric</link>
            <guid>https://velog.io/@attosisss_/Blockchain-%ED%95%98%EC%9D%B4%ED%8D%BC%EB%A0%88%EC%A0%80-%ED%8C%A8%EB%B8%8C%EB%A6%ADHyperledger-Fabric</guid>
            <pubDate>Tue, 16 Jan 2024 02:00:00 GMT</pubDate>
            <description><![CDATA[<h4 id="1-프라이빗-블록체인">1. 프라이빗 블록체인</h4>
<p>비트코인과 이더리움과 같은 퍼블릭 블록체인(Public Blockchain)은 누구나 네트워크에 참여하여 블록체인을 읽거나 쓸 수 있다. 또한, 탈중앙화 되어 있고 네트워크를 제어하는 독점 참여자가 존재하지 않는다. 퍼블릭 블록체인에 있는 데이터는 노드(참여자 = 컴퓨터 , 폰 등등)들에 의해 검증되고 블록체인에 한번 기록되면 수정하거나 삭제하는 것이 불가능하다.</p>
<p>이러한 퍼블릭 블록체인과 반대되는 개념이 바로 프라이빗 블록체인(Private BlockChain)이다.
프라이빗 블록체인은</p>
<ul>
<li><p>네트워크 내에서 초대된 사용자만이 네트워크에 참여하고, 원장에 접근할 수 있는 허가형(Permissioned) 블록체인을 의미한다.</p>
</li>
<li><p>프라이빗 블록체인에서 중앙기관은 채굴 과정과 합의 알고리즘에 대한 권한을 개발하고 유지하며, 네트워크에 참여할 수 있는 사용자를 결정한다.</p>
</li>
<li><p>채굴이나 트랜잭션 수수료가 매우 적거나 아예 존재하지 않기도 한다.</p>
</li>
<li><p>중앙 기관이 네트워크에 대한 관리 권한을 가지므로 특정 노드만 원장을 쓸 수 있도록 지정할 수 있고, 삭제할 수 도 있다.</p>
</li>
</ul>
<h3 id="하이퍼레저-패브릭hyperledger-fabirc이란">하이퍼레저 패브릭(HyperLedger Fabirc)이란?</h3>
<p>하이퍼레저는 리눅스 재단에서 진행하는 스마트 컨트랙트를 지원하는 프아리빗 블록체인 프로젝트이다.
하이퍼레저의 목표는 산업간 블록체인 기술을 고도화하여 협력사 간 책임성과 투명성, 신뢰가 보장되도록 하는 것이다.</p>
<p>하이퍼레저 패브릭은 분산 원장 솔루션을 제공하는 플랫폼으로 기밀성, 탄력성, 유연성 및 확장성을 제공한다.
하이레저 패브릭의 특징으로는</p>
<ul>
<li><p>프라이버시와 기밀성: 하이퍼레저 패브릭은 채널(Channel)이라고 불리는 제한된 메시지 경로를 제공하며, 이러한 채널은 네트워크의 특정 하위 노드 집합에게 트랜잭션 프라이버시와 기밀성을 보장해준다. 트랜잭션, 노드 그리고 채널 정보와 같은 채널에 있는 모든 데이터는 채널에 대한 접근 권한이 없으면 열람할 수 없다. 이 특징을 통해 서로 다른 이해관계를 가진 기업들이 동일한 허가형 네트워크에서 공존할 수 있도록 해준다.</p>
</li>
<li><p>효율적인 처리: 네트워크에 동시성과 병렬성을 제공하기 위해 트랜잭션 실행은 트랜잭션 정렬 및 커밋과 분리된다. 네트워크의 노드들은 노드 유형에 따라 액션을 수행하는 역할이 정해져 있다. 이러한 동시적 실행은 각 노드의 처리 효율성을 높이고 트랜잭션 전달을 가속화 한다.</p>
</li>
<li><p>체인코드: 체인코드는 이더리움과 같은 분산 플랫폼에 있는 스마트 컨트랙트에 해당한다. 체인코드는 자산(현실 세계의 객체를 표현)과 자산을 변경하기 위한 트랜잭션 명령을 인코딩하는데 사용한다.</p>
</li>
<li><p>모듈식 설계: 하이퍼레저 패브릭은 모듈식 아키텍처를 구현하여 네트워크 설계자에게 기능적 선택권을 제공한다. 에를 들어, 식별, 정렬(합의), 암호화에 대한 특정 알고리즘을 모든 하이퍼레저 패브릭 네트워크에 연결할 수 있다.</p>
</li>
</ul>
<p><strong>하이퍼레저 패브릭 주요 개념</strong></p>
<ul>
<li>피어(Peer): 피어는 블록체인 네트워크의 기본 요소이며, 원장과 스마트 컨트랙트를 호스팅하는 노드이다. 네트워크와의 상호작용의 시작점이며, 어플리케이션의 체인코드(스마트 컨트랙트)를 실행하여 쿼리를 하거나 원장을 업데이트 하도록 한다.
<img src="https://velog.velcdn.com/images/attosisss_/post/3eb1236a-756e-425d-a7c3-d7d6b2ecdf93/image.png" alt=""></li>
</ul>
<p>네트워크의 각 피어에는 디지털 인증서가 할당되며, 디지털 인증서는 피어가 참여하는 채널에 해당 피어를 식발하도록 해준다. 이 방식으로 인해 피어는 각 채널에서 다양한 권한을 가질 수 있다. 모든 피어는 기본적으로 동일하지만, 네트워크에서 여러 역할을 수행할 수 있다.</p>
<ol>
<li><p>커밋 피어(Committing Peer): 채널의 모든 피어 노드는 커밋 피어이다. 생성된 트랜잭션 블록을 수신하고, 블록이 네트워크에 의해 검증되면 추가 작업을 통해 원장의 사본을 커밋한다.</p>
</li>
<li><p>보증 피어(Endorsing Peer): 스마트 컨트랙트를 체결한 모든 피어는 보증 피어가 될 수 있다. 보증 피어가 되기 위해서는 디지털 서명된 트랜잭션 응답을 생성하는 어플리케이션에 의해 실행되어야 한다.</p>
</li>
<li><p>채널(Channel): 하이퍼레저 패브릭 채널은 둘 이상의 특정 네트워크 노드 간의 통신을 위한 프라이빗 서브넷으로, 프라이빗 트랜잭션을 수행하기 위한 목적으로 사용된다. 채널은 멤버(Orgs), 공유원장, 체인코드 어플리케이션 및 정렬 서비스 노드에 의해 정의된다. 네트워크의 모든 트랜잭션은 채널에서 실행되며, 각 당사자는 해당 채널에서 트랜잭션을 수행하기 위해 인증을 거쳐야 한다. 채널에 참여는 각 피어는 MSP(Membership Service Provider)가 제공하는 고유한 식별자를 가지며, 각 피어를 해당 채널과 서비스에 인증한다.</p>
</li>
</ol>
<p><strong>스마트 컨트랙트와 체인코드</strong>
스마트 컨트랙트는 원장에 추가되는 새로운 데이터를 생성하는 실행 가능한 로직을 정의한다. 일반적으로 스마트 컨트랙트와 체인 코드라는 용어는 혼용되지만, 스마트 컨트랙트는 전역 상태에 포함된 비즈니스 객체의 상태를 변경시키는 트랜잭션 로직을 정의한다. 반면, 체인코드는 블록체인 네트워크에 배포하기 위해 패키징된 스마트 컨트랙트의 집합이다. 즉, 스마트 컨트랙트는 트랜잭션을 관리하며, 체인코드는 스마트 컨트랙트를 배포하기 위해 패키징하여 관리한다.</p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/4bbcc403-8f68-44ee-b116-1e6a23ca276b/image.png" alt=""></p>
<p>체인코드를 통해 아키텍트나 개발자가 하나의 블록체인 네트워크 안에 있는 서로 다른 조직 간 공유되어야 하는 데이터와 비즈니스 프로세스를 정의한다.</p>
<ul>
<li><p>보증(Endorsement): 모든 체인코드에는 모든 스마트 컨트랙트에 적용되는 관련 보증 정책이 있다. 이 정책은 트랜잭션의 유효성을 위해 트랜잭션에 서명해야 하는 블록체인 네트워크 내 조직들을 의미한다.</p>
</li>
<li><p>유효한 트랜잭션: 스마트 컨트랙트는 블록체인 네트워크 내 조직이 소유한 피어 노드에서 실행된다. 이 스마트 컨트랙트들은 트랜잭션 제안서라 불리는 입력 파라미터의 집합을 취하여 스마트 컨트랙트의 프로그램 로직과 함께 사용하여 원장을 읽고 쓴다. 트랜잭션과 이를 읽은 상태가 유효할 경우, 전역 상태는 기존 상태에서 새로운 상태값으로 변경되어 저장된다. 다만 전역 상태는 스마트 컨트랙트가 실행될 때 변하지 않고, 트랜잭션이 최종적으로 유효해지면 업데이트된다.</p>
</li>
</ul>
<p>네트워크의 모든 피어 노드에 배포되는 트랜잭션은 두 단계에 거쳐 검증된다. 먼저, 보증 정책에 따라 지정된 조직에서 서명된 거래인지 확인한다. 그리고 전역 상태의 현재 값이 보증 피어 노드에 의해 서명되었을 때 트랜잭션의 읽기 집합과 일치하는지 확인한다. 트랜잭션이 두 과정을 모두 통화하면 유효한 트랜잭션이 된다.</p>
<p>유효한 트랜잭션과 유효하지 않은 트랜잭션은 모두 블록체인 이력에 추가되지만, 유효한 트랜잭션만이 전역 상태를 업데이트 한다.</p>
<h4 id="이더리움과-하이퍼레저-패브릭의-차이점">이더리움과 하이퍼레저 패브릭의 차이점</h4>
<ul>
<li><p>플랫폼의 목적: 이더리움은 EVM 위에서 스마트 컨트랙트를 실행하는 하나의 목적을 위해 만들어졌고, dApp이라는 분산형 어플리케이션을 배포할 수 있다. 반면, 하이퍼레저는 고성능과 신뢰성이 높은 블록체인 개발을 위한 산업 전반의 협업을 가속화 하기 위해 도입되었다. 하이퍼레저 패브릭은 사람들이 다양한 요구에 맞는 개인화된 블록체인을 개발할 수 있게 해준다.</p>
</li>
<li><p>작동 방식: 이더리움은 퍼블릭 블록체인으로, 누구나 블록체인 네트워크에 접근할 수 있고, 허가 받을 필요가 없지만, 하이퍼레저 패브릭은 허가된 사용자만이 네트워크에 참여할수 있다.</p>
</li>
<li><p>피어 역할: 이더리움은 피어마다 역할이 있는데, 트랜잭션이 완료되면 이를 위해 여러 노드가 참여해야 하므로 확장성, 프라이버시, 효율성 등의 문제가 발생할 수 있다. 하이퍼레저는 네트워크 내의 각 피어에게 트랜잭션을 수행하기 위해 정보를 제공할 필요가 없는 DLT(Distributed Ledger Technology)를 제공한다.</p>
</li>
<li><p>합의 메커니즘: 이더리움은 모든 노드가 합의에 도달해야 하는 PoS 합의 알고리즘을 사용한다. 반면, 하이퍼레저 패브릭은 다른 종류의 합의 알고리즘을 사용한다. 하이퍼레저 패브릭에서는 아예 합의하지 않거나, 다양한 합의 프로토콜 중 하나를 선택할 수 있다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift - Combine]]></title>
            <link>https://velog.io/@attosisss_/Swift-Combine</link>
            <guid>https://velog.io/@attosisss_/Swift-Combine</guid>
            <pubDate>Tue, 19 Dec 2023 08:01:47 GMT</pubDate>
            <description><![CDATA[<h2 id="combine">Combine</h2>
<blockquote>
<p>Apple에서 2019년 iOS 13 버전부터 사용 가능한 Combine Framework를 공개했다.</p>
</blockquote>
<h2 id="publisher-와-subscriber">Publisher 와 Subscriber</h2>
<hr>
<p>Combine Framework를 사용하기 위해서는 Publisher와 Subscriber로 데이터를 주고 받는 것에 대한 이해에서 부터 출발한다.</p>
<p>먼저 Publisher는 이벤트(혹은 값)를 전달하는 존재로 생각할 수 있고 , Subscriber는 Publisher가 내보낸 이벤트(혹은 값)를 수신하는 존재로 생각하면 된다 !</p>
<p>WWDC 2019에서 Apple은 Combine Framework의 흐름을 소개할 때 다음과 같이 소개한다~</p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/137c302b-4674-4523-9473-ba1711c8968b/image.png" alt=""></p>
<p>먼저 Subscriber가 Publisher를 subscribe하기 전까지 Publisher는 대기 상태로 존재한다.(여기서 Publisher는 Publisher protocol을 준수하는 인스턴스)</p>
<ol>
<li><p>Pusblisher의 subscribe(_:) 메서드의 전달인자로 Subscriber protocol을 준수하는 인스턴스를 전달하여 Pubulisher와 Subscriber를 연결한다.</p>
</li>
<li><p>Subscriber의 receive(subscription:) 메서드는 subscribe 요청을 인지하고 Publisher로 부터 Subscription 인스턴스를 전달 받는다.(Publisher-Subscriber 연결 완료!)</p>
</li>
<li><p>Subscriber는 전달 받은 Subscription의 reuest( _ : ) 메서드를 통해 Publisher의 값을 전달하라고 요청한다.
그럼 비로소 Publisher는 Subscriber에게 값과 오류 타입을 함께 전달한다.(오류가 발생하지 않으면 Never 오류 타입을 전달한다.)</p>
</li>
<li><p>Subscriber의 receive ( _ : )메서드는 Publisher로 부터 전달 받은 값을 처리한다.</p>
</li>
<li><p>만약 더 이상 Subscriber에게 전달할 값이 없으면 receiver(completion : )메서드를 통해  completion, 혹은 오류를 전달 받는다.</p>
</li>
</ol>
<p>위의 과정에 사용된 메서드들은 Publisher의 protocol과 Subscriber의 protocol에 선언 되어 있다.</p>
<h2 id="publisher-protocol">Publisher Protocol</h2>
<hr>
<pre><code class="language-jsx">
public protocol Publisher&lt;Output, Failure&gt; {

    /// The kind of values published by this publisher.
    associatedtype Output

    /// The kind of errors this publisher might publish.
    ///
    /// Use `Never` if this `Publisher` does not publish errors.
    associatedtype Failure : Error

    /// Attaches the specified subscriber to this publisher.
    ///
    /// Implementations of ``Publisher`` must implement this method.
    ///
    /// The provided implementation of ``Publisher/subscribe(_:)-4u8kn``calls this method.
    ///
    /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values.
    func receive&lt;S&gt;(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
</code></pre>
<blockquote>
<h3 id="swift-문법-짚고-넘어가기-">Swift 문법 짚고 넘어가기 !!</h3>
<p>protocol에는 일반적으로 함수에서 Generic을 선언 하듯이 Generic을 선언할 수 없다.
대신 protocol에서 Generic처럼 동적으로 타입을 지정할 수 있는 associaterdType placeholder가 있다.</p>
</blockquote>
<h4 id="추가-설명">추가 설명</h4>
<ul>
<li>Output 타입은 Publisher가 Subscriber에게 전달하는 값의 타입을 의미한다.</li>
<li>Failure Publisher가 Subscriber에게 전달할 오류 타입이다. Publisher가 Subscriber에게 전달할 때 오류가 발생하지 않으면 Never로 지정한다.</li>
<li>receive(subscriber : ) 메서드는 Subscriber protocol을 준수하는 인스턴스를 전달인자로 전달 받는다.</li>
</ul>
<p>📍 Apple 문서에도 receive(subscriber:) 메서드 대신 위의 이미지에서 설명된 subscribe(_: )메서드를 사용할 것을 권장하고 있다. </p>
<h2 id="subscriber-protocol">Subscriber protocol</h2>
<hr>
<pre><code class="language-jsx">public protocol Subscriber&lt;Input, Failure&gt; : CustomCombineIdentifierConvertible {

    /// The kind of values this subscriber receives.
    associatedtype Input

    /// The kind of errors this subscriber might receive.
    ///
    /// Use `Never` if this `Subscriber` cannot receive errors.
    associatedtype Failure : Error

    /// Tells the subscriber that it has successfully subscribed to the publisher and may request items.
    ///
    /// Use the received ``Subscription`` to request items from the publisher.
    /// - Parameter subscription: A subscription that represents the connection between publisher and subscriber.
    func receive(subscription: Subscription)

    /// Tells the subscriber that the publisher has produced an element.
    ///
    /// - Parameter input: The published element.
    /// - Returns: A `Subscribers.Demand` instance indicating how many more elements the subscriber expects to receive.
    func receive(_ input: Self.Input) -&gt; Subscribers.Demand

    /// Tells the subscriber that the publisher has completed publishing, either normally or with an error.
    ///
    /// - Parameter completion: A ``Subscribers/Completion`` case indicating whether publishing completed normally or with an error.
    func receive(completion: Subscribers.Completion&lt;Self.Failure&gt;)
}
</code></pre>
<h4 id="추가-설명-1">추가 설명</h4>
<ul>
<li>Input은 Publisher로 부터 전달 받은 값의 타입을 의미한다.</li>
<li>Failure는 마찬가지로 오류 타입을 의미, 마찬가지로 오류를 전달 받지 않으면 Never 타입을 사용한다.</li>
<li>receive(subscription : ) 메서드는 Publisher로 부터 Subscription을 전달 받는다.</li>
<li>receive(_ input : )메서드는 Publisher로 부터 값을 전달 받는다. 반환 값은 앞으로 Publisher로 부터 얼마나 요소를 전달 받을 수 있는지를 나타낸다.</li>
<li>receive( completion : _ ) Publisher로 부터 completion 혹은 오류를 전달 받는다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift- Dependency Injection]]></title>
            <link>https://velog.io/@attosisss_/Swift-Dependency-Injection</link>
            <guid>https://velog.io/@attosisss_/Swift-Dependency-Injection</guid>
            <pubDate>Wed, 13 Dec 2023 04:39:38 GMT</pubDate>
            <description><![CDATA[<p>✅ 의존성 주입은 swift에서만 등장하는 용어는 아니다.
거의 모든 객체 지향 프로그래밍 언어에서 찾아볼 수 있지만 , 아래는 Swift 예시다.</p>
<h2 id="dependcy-의존성이란-">Dependcy, 의존성이란 ?</h2>
<blockquote>
<p>객체 지향 프로그래밍에서 Dependency, 의존성은 <strong>서로 다른 객체 사이에 의존 관계가 있다</strong>는 것을 말한다. 즉, <strong>의존하는 객체가 수정되면, 다른 객체도 영향을 받는다</strong>는 것이다.</p>
</blockquote>
<p>예를 들어 아래의 코드를 보자.</p>
<pre><code class="language-jsx">import UIKit

struct Eat {
    func coffee() {
        print(&quot;아메리카노&quot;)
    }

    func meal() {
        print(&quot;피자&quot;)
    }
}

struct Person {
    var todayEat: Eat

    func coffee() {
        todayEat.coffee()
    }

    func meal() {
        todayEat.meal()
    }
}</code></pre>
<p>Person 객체는 Eat 객체를 인스턴스로 사용하고 있으므로, Eat 객체에 의존성이 생긴다.
만약 이때, Eat 객체에 중요한 수정이나 오류가 발생한다면, Person 객체도 영향을 받을 수 있다.</p>
<p>의존성을 가지는 코드가 많아진다면, 재활용성이 떨어지고 매번 의존성을 가지는 객체들을 함께 수정해주어야 한다는 문제가 발생한다.</p>
<p>이러한 의존성을 해결하기 위해 나온 개념이 바로 <strong>Dependency Injection, 의존성 주입</strong>이다!!!!!</p>
<h2 id="injection-주입이란-">Injection, 주입이란 ?</h2>
<hr>
<p>injection, 주입은 외부에서 객체를 생성해서 넣는 것을 의미한다.</p>
<pre><code class="language-jsx">class Eat:Menu {
    var coffee: String
    var meal: String

    init(coffee: String, meal: String) {
        self.coffee = coffee
        self.meal = meal
    }

    func printCoffee() {
        print(&quot;아메리카노&quot;)
    }

    func printMeal() {
        print(&quot;피자&quot;)
    }
}

let menu = Eat(coffee: &quot;아메리카노&quot;, meal: &quot;피자&quot;)</code></pre>
<p>위 코드와 같이 생성자(init = 이니셜라이저)등을 활용해서 외부에서 주입할 수 있다.</p>
<h2 id="dependency-injection-의존성-주입">Dependency Injection, 의존성 주입!</h2>
<hr>
<blockquote>
<p>그렇다면 의존성 주입을 하는 이유는 무엇일까?</p>
</blockquote>
<ul>
<li>Unit Test가 용이해진다.</li>
<li>코드의 재활용성을 높여준다</li>
<li>객체 간의 의존성(종속성)을 줄이거나 없앨 수 있다.</li>
<li>객체 간의 결합도를 낮추면서 유연한 코드를 작성할 수 있다.</li>
</ul>
<p>그렇다면 내부에서 만든 객체를 외부에서 넣어서 의존성을 주입해보자.
하지만 전에 의존 관계 역전 법칙을 알아야한다.</p>
<h2 id="dependency-inversion-principle-의존-관계-역전-법칙">Dependency Inversion Principle, 의존 관계 역전 법칙</h2>
<hr>
<p>DIP, 의존 관계 역전 법칙은 객체 지향 프로그래밍 설계의 다섯가지 기본 원칙(SOLID) 중 하나이다. 추상화 된 것은 구체적인 것에 의존하면 안되고 구체적인 것이 추상화된 것에 의존 해야한다.</p>
<p>즉, 구체적인 객체는 추상회된 객체에 의존 해야한다는 것이 핵심 !!!!이다ㅎㅎ
(이런 자세한 정보는 나도 이번에 공부하면서 알게된 ,,, 멀고도 머네요 ,,,)</p>
<p>Swift에서 추상화된 객체는 <code>Protocol</code>이 있다.
우리는 이 <code>protocol</code>을 활용해서 의존성 주입을 구현해보자</p>
<p>우선 <code>protocol</code>을 활용해서 추상적인 객체를 만들어야한다.
<code>Menu</code>라는 <code>Protocol</code>은 <code>printCoffee()</code> 와 <code>printMeal()</code> 함수를 가지고 있다.</p>
<pre><code class="language-jsx">protocol Menu {
    func printCoffee()
    func printMeal()
}</code></pre>
<p>이후 Eat클래스는 <code>Menu Protocol</code>을 채택한 후, <code>Protocol</code>에 정의한 함수를 실체화 시켜준다.</p>
<pre><code class="language-jsx">class Eat: Menu {
    var coffee: String
    var meal: String

    init(coffee: String, meal: String) {
        self.coffee = coffee
        self.meal = meal
    }

    func printCoffee() {
        print(&quot;아메리카노&quot;)
    }

    func printMeal() {
        print(&quot;피자&quot;)
    }
}</code></pre>
<p>이제부터 중요한 부분이 나온다.
기존의 방식과 다르게 <code>todayEat</code> 변수는 추상적인 객체인 <code>Menu</code> 타입에 의존하게 된다.</p>
<p>여기서 changeMenu 함수를 활용해서 의존성 주입을 시킬 수 있다.</p>
<pre><code class="language-jsx">struct Person {
    var todayEat: Menu

    func printCoffee() {
        todayEat.printCoffee()
    }

    func printMeal() {
        todayEat.printMeal()
    }

    mutating func changeMenu(menu: Menu) {
        self.todayEat = menu
    }
}</code></pre>
<p>이렇게 구현한다면 Eat객체와 Person 객체는 거의 독립적인 객체가 된다.
Eat 객체를 수정하거나 Person을 수정한다고 해서 상대 객체를 함께 수정해야 하는 문제를 방지할 수 있다.</p>
<pre><code class="language-jsx">let menu = Eat(coffee: &quot;아메리카노&quot;, meal: &quot;피자&quot;)
let anotherMenu = Eat(coffee: &quot;라떼&quot;, meal: &quot;햄버거&quot;)

var suhshin = Person(todayEat: menu)

suhshin.printCoffee() // print 아메리카노
suhshin.changeMenu(menu: anotherMenu)
suhshin.printCoffee() // print 라떼</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift- 고차함수]]></title>
            <link>https://velog.io/@attosisss_/Swift-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@attosisss_/Swift-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98</guid>
            <pubDate>Tue, 05 Dec 2023 05:51:46 GMT</pubDate>
            <description><![CDATA[<h2 id="map">Map</h2>
<ul>
<li><code>map</code>은 자신을 호출할 때 매개변수로 전달된 함수를 실행하여 그 결괏값을 다시 반환해주는 함수이다.</li>
<li><code>map</code>을 사용하기 위해서는 Swift의 <code>collection</code>, <code>sequence</code> 프로토콜을 따르면 가능하다. 따라서 <code>Array</code>, <code>Dictionary</code>,<code>Set</code>,<code>optional</code> 등에서 사용이 가능</li>
<li><code>map</code>을 사용하여도 기존의 컨테이너의 값은 변경되지 않고 새로운 컨테이너가 생성되어 map은 기존 데이터를 변형하는데 많이 사용된다.</li>
<li><code>map</code> 은 다른 함수의 형태로 입력을 받는다.</li>
</ul>
<p>또 , 다중 스레드 환경일 때 대상 컨테이너의 값이 스레드에서 변경되는 시점에 다른 스레드에서도 동시에 값이 변경되려고 할 때 예측하지 못한 결과가 발생하는 부작용을 방지한다.</p>
<pre><code class="language-jsx">let item = [&quot;가방&quot;, &quot;책&quot;, &quot;블로그&quot;, &quot;지갑&quot;]

let first = item.map{ (name) in &quot;서근의 &quot; + name }
print(first)

let second = item.map({(name) in &quot;서근의 &quot; + name})
print(second)

let third = item.map {&quot;서근의 &quot; + $0}
print(third)</code></pre>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/281f972c-285c-4b29-8309-91e121ed7372/image.png" alt=""></p>
<p>이것을 클로저로 바꾸면 아래와 같다.</p>
<pre><code class="language-jsx">let item = [&quot;가방&quot;, &quot;책&quot;, &quot;블로그&quot;, &quot;지갑&quot;]

func addName(name: String) -&gt; String {
    return &quot;서근의 &quot; + name
}

item.map(addName)</code></pre>
<hr>
<h3 id="map-메서드와-for-in-구문">map 메서드와 for-in 구문</h3>
<p><code>for-in</code> 구문과 <code>map</code> 메서드 사용을 비교해보자면 아래와 같다.</p>
<pre><code class="language-jsx">let numbers: [Int] = [0, 1, 2, 3, 4]

var doubledNumbers: [Int] = [Int]()
var string: [String] = [String]()

for number in numbers {
    doubledNumbers.append(number * 2)
    string.append(&quot;\(number)&quot;)
}
print(doubledNumbers) //[0, 2, 4, 6, 8]
print(string) //[0, 2, 4, 6, 8]


//map 메서드
doubledNumbers = numbers.map({ (number: Int) -&gt; Int in
    return number * 2 //[0, 2, 4, 6, 8]
})
string = numbers.map({ (number: Int) -&gt; String in
    return &quot;\(number)&quot;  //[&quot;0&quot;, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;]
})</code></pre>
<p><code>map</code> 메서드를 사용하면 <code>for-in</code> 구문을 사용한 것보다 간단하고 편하게 연산을 실행할 수 있다. 또, <code>map</code> 메서드를 사용하면 <code>for-in</code> 구문을 사용하기 위해 빈 배열을 생성할 필요도, <code>append</code> 연산을 실행할 시간도 필요가 없어진다!!</p>
<p>위 코드에서 사용된 <code>map</code> 메서드를 클로저 표현으로 요약할 수 있다.</p>
<pre><code class="language-jsx">let numbers: [Int] = [0, 1, 2, 3, 4]

//map 메서드
doubledNumbers = numbers.map({ (number: Int) -&gt; Int in
    return number * 2 //[0, 2, 4, 6, 8]
})
string = numbers.map({ (number: Int) -&gt; String in
    return &quot;\(number)&quot;  //[&quot;0&quot;, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;]
})

//유형 추론으로 요약 가능
doubledNumbers = numbers.map({ (number) in
    return number * 2 //[0, 2, 4, 6, 8]
})
string = numbers.map({ (number) in
    return &quot;\(number)&quot;  //[&quot;0&quot;, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;]
})

//매개변수 및 반환 타입 생략
doubledNumbers = numbers.map({return $0 * 2})
string = numbers.map({return &quot;\($0)&quot;})
//반환 키워드 생략
doubledNumbers = numbers.map({$0 * 2})
string = numbers.map({&quot;\($0)&quot;})

//후행 클로저로 요약 가능
doubledNumbers = numbers.map { $0 * 2 }
string = numbers.map { &quot;\($0)&quot;}</code></pre>
<p>코드의 재사용 측면에 대해 알아보자면 만약 같은 기능을 여러번 사용해야 한다면 하나의 클로저를 여러 <code>map</code> 메서드에서 사용하는 것이 좋다!</p>
<pre><code class="language-jsx">let evenNumbers: [Int] = [0, 2, 4, 6, 8, 10]
let oddNumbers: [Int] = [0, 1, 3, 5, 7, 9]
let multiplyTwo: (Int) -&gt; Int = { $0 * 2 }

let doubledEvenNumbers = evenNumbers.map(multiplyTwo)
//[0, 4, 8, 12, 16, 20]
let doubledOddNumbers = oddNumbers.map(multiplyTwo)
//[0, 2, 6, 10, 14, 18]</code></pre>
<hr>
<h2 id="filter">Filter</h2>
<ul>
<li><code>filter</code>는 내부 값을 걸러서 추출하는 역할을 한다.</li>
<li><code>map</code>과 동일하게 새로운 컨테이너에 걸러진 값을 담아 반환한다.</li>
<li><code>map</code>은 기존의 요소를 변경한 값을 반환했다면, <code>filter</code>는 기준을 가지고 기준에 맞는 값들을 반환해준다.</li>
<li><code>filter</code> 함수의 매개변수로 전달되는 함수 반환 타입은 <code>Bool</code> 이다.</li>
<li>새로운 컨테이너에 포함될 항목이라고 판단되면<code>true</code>, 그게 아니라면 <code>false</code> 반환</li>
</ul>
<pre><code class="language-jsx">let number = [1, 2, 3, 4, 5]
print(number.filter {$0 &gt; 3}) //4, 5

//필터 조건이 맞다면 map조건을 실행
let filterAndMap = [1, 2, 3, 4, 5].filter{$0 &gt; 3}.map{$0 * 10}
print(filterAndMap) //40, 50</code></pre>
<p>이런 식으로 filter 를 사용하여 필요 없는 요소들을 삭제하고 필요한 요소들만 가지고 연산을 하는 게 가능하다 !!</p>
<pre><code class="language-jsx">let numbers: [Int] = [0, 1, 2, 3, 4, 5]

var evenNumber: [Int] = numbers.filter { (number: Int) -&gt; Bool in
    return number % 2 == 0
}
print(evenNumber) // [0, 2, 4]

let oddNumbers: [Int] = numbers.filter { $0 % 2 == 1 }
print(oddNumbers) // [1, 3, 5]</code></pre>
<hr>
<h2 id="reduce">Reduce</h2>
<ul>
<li>reduce는 줄이다 라는 뜻이지만 결합 기능을 하는 메서드이다.</li>
<li>컨테이너의 내부의 요소들을 하나로 합치는 기능을 하는 고차 함수이다.</li>
<li>배열의 모든 값을 전달 인자로 전달받아 클로저의 연산 결과로 합해주게 된다.</li>
</ul>
<h3 id="swift에서의-reduce-형태두-가지">Swift에서의 reduce 형태(두 가지)</h3>
<ul>
<li><p>첫 번째, 클로저가 각 요소를 전달받아 연산한 후 값을 다음 클로저 실행을 위해 반환하며 컨테이너를 순환하는 형태. <code>initialResult</code> 라는 이름의 매개변수로 전달되는 값을 통해 초깃값을 지정하고, <code>nextPartialResult</code> 매개변수로 클로저를 전달받음. </p>
</li>
<li><p>두 번째 , 컨테이너를 순환하며 클로저가 실행되지만 클로저가 따로 결괏값을 반환하지 않는 형태. 대신 <code>inout</code> 매개변수를 사용하여 초깃값에 직접 연산을 실행함.</p>
</li>
</ul>
<pre><code class="language-jsx">let number = [1, 2, 3, 4, 5]

let sum1 = number.reduce(0) { (result:Int, element: Int) -&gt; Int in return result + element }
print(sum1) //15

//추론으로 생략 가능
let sum2 = number.reduce(0) { (result, element) in result + element }
print(sum2) //15

let sum3 = number.reduce(0) {$0 + $1}
print(sum3) //15

let sum4 = number.reduce(1, +)
print(sum4) //16

/*
reduce 초기값이 0이기 때문에 0 + 1 부터 시작하여 마지막 값을 결괏값으로 보여준다.
0 + 1
1 + 2
3 + 3
6 + 4
10 + 5
결괏값 = 15
*/</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift- Dictionary(딕셔너리)]]></title>
            <link>https://velog.io/@attosisss_/Swift-Dictionary%EB%94%95%EC%85%94%EB%84%88%EB%A6%AC</link>
            <guid>https://velog.io/@attosisss_/Swift-Dictionary%EB%94%95%EC%85%94%EB%84%88%EB%A6%AC</guid>
            <pubDate>Mon, 27 Nov 2023 05:44:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>딕셔너리는 요소들의 순서 없이 키와 값의 쌍으로 구성되는 컬렉션 타입이다. 딕셔너리에 저장되는 값을 항상 키와 쌍을 이루게 되는데, 딕셔너리안에는 키가 하나이거나 여러개일 수 있다. 
배열과 마찬가지로 값의 모음이지만 정수 위치로 항목을 저장하는 대신 원하는 것을 사용하여 접근할 수 있다.</p>
</blockquote>
<p><strong>단, 하나의 딕셔너리안의 키는 같은 이름을 중복해서 사용할 수 없다. 즉, 딕셔너리에 키는 값을 대변하는 유일한 식별자가 되는 것이다.</strong></p>
<p>딕셔너리 데이터를 저장하는 가장 일반적인 방법은 문자열을 사용하는 것이다. 예를 들어 이름을 사용하여 사람의 키를 저장하는 사전을 만들 수 있다.</p>
<ul>
<li>let : 변경 불가능한 딕셔너리(상수)</li>
<li>var : 변수로 선언</li>
<li>isEmpty : 비어있는 딕셔너리인지 확인</li>
<li>count : 딕셔너리의 요소 개수 확인</li>
</ul>
<pre><code class="language-jsx">let heights = [ &quot;하울&quot;: 1.78, &quot;서근&quot;: 1.76 ]</code></pre>
<p>배열과 마찬가지로 딕셔너리는 <code>대괄호</code>를 사용하고 , <code>콜론(:)</code>을 사용해서 값과 식별자를 구분한다. 이 식별자를 <code>key</code>라고 하며, 딕셔너리에서 데이터를 다시 읽을 수 있다!!!!</p>
<pre><code class="language-jsx">heights[&quot;서근&quot;]</code></pre>
<hr>
<h3 id="딕셔너리와-배열의-차이">딕셔너리와 배열의 차이</h3>
<p>딕셔너리와 배열은 서로 다른 방법으로 저장한다.
딕셔너리를 사용하면 추가하려는 항목을 식별하는 &quot;키&quot;를 선택할 수 있지만 , 배열은 각 항목을 순차적으로 추가한다.</p>
<p>예를들어 <code>Array</code> 인덱스[7] 이 사용자의 국가를 의미한다는 것을 기억하려고 하기보다, <code>user[&quot;country&quot;]</code>라고 쓰는 것이 훨씬 편하다.</p>
<p>세트와 마찬가지로 딕셔너리는 특정 순서로 항목을 저장하지 않으므로 <em>빠른 검색</em> 을 위해 항목을 저장하는 방식을 최적화한다.</p>
<p>따라서 <code>user[&quot;country&quot;]</code> 라고 호출을 하면 내부에 있는 1000개의 항목이 있는 딕셔너리가 있어도 해당 키(또는 <code>nill</code> )에 있는 항목을 즉시 반환한다.</p>
<h4 id="예시-">예시 )</h4>
<pre><code class="language-jsx">var roles = [&quot;captain&quot;: &quot;Mal&quot;, &quot;engineer&quot;: &quot;Kaylee&quot;]</code></pre>
<pre><code class="language-jsx">let scores = [&quot;Sophie&quot;: 100]</code></pre>
<hr>
<h3 id="딕셔너리-기본-값">딕셔너리 기본 값</h3>
<p>존재하지 않는 키(nil)을 사용하여 딕셔너리에서 값을 읽으려고 하면 nil을 돌려준다. 누락 된 키를 요청하는 경우 사용할 기본값을 딕셔저너리에 제공할 수 있다.</p>
<pre><code class="language-jsx">let favoriteIceCream = [ &quot;서근&quot;: &quot;초콜릿&quot;, &quot;포뇨&quot;: &quot;바닐라&quot; ]</code></pre>
<p>서근이 가장 좋아하는 아이스크림을 다음과 같이 읽을 수 있다.</p>
<pre><code class="language-jsx">favoriteIceCream[&quot;서근&quot;]</code></pre>
<p>하지만 하울에게 가장 좋아하는 아이스크림을 읽으려고 하면 nil이 반환된다. 이는 Swift에는 해당 키에 대한 값이 없음을 의미한다.</p>
<pre><code class="language-jsx">favoriteIceCream[&quot;하울&quot;]</code></pre>
<p>디셔너리에 &quot;Unknown&quot;의 기본값을 지정하여이 문제를 해결할 수 있다. 이렇게 기본값을 정해주면 &#39;하울&#39;에 대한 아이스크림이 없을 때 nil이 아닌 &quot;Unknown&quot;이 반환된다.</p>
<pre><code class="language-jsx">favoriteIceCream[&quot;하울&quot;, default: &quot;Unknown&quot;]</code></pre>
<hr>
<h3 id="딕셔너리의-선언과-생성">딕셔너리의 선언과 생성</h3>
<pre><code class="language-jsx">// typealias를 사용해 더 단순하게 표현 가능
typealias StringIntDictionary = [String: Int]


// 키는 String, 값은 Int 타입인 빈 딕셔너리 생성
// 모두 같은 표현
var nameAndAge1: [String: Int] = [String: Int]()

var nameAndAge2: Dictionary&lt;String, Int&gt; = Dictionary&lt;String, Int&gt;()

var nameAndAge3: StringIntDictionary = StringIntDictionary()

// 딕셔너리의 키와 값 타입을 정확히 명시해줬다면, [:] 만으로도 빈 딕셔너리 생성 가능
var nameAndAge4: [String: Int] = [:]

var nameAndAge5: [String: Int] = [&quot;서근&quot;: 25, &quot;미진&quot;: 19, &quot;철수&quot;: 12]


print(nameAndAge4.isEmpty) // true
print(nameAndAge5.isEmpty) // false
print(nameAndAge5.count) // 3
print(nameAndAge5) // [&quot;철수&quot;: 12, &quot;미진&quot;: 19, &quot;서근&quot;: 25]</code></pre>
<p>위에서 말했듯이 , 딕셔너리는 각 값에 키로 접근할 수 있다. <code>딕셔너리 내부에서 키는 유일해야하고 , 값은 유일하지 않다</code>.
딕셔너리는 배열과 다르게 딕셔너리 내부에 없는 키로 접근해도 오류가 발생하지 않고 <code>nil</code>을 반환한다.</p>
<p>특정 키에 해당하는 값을 제거하려면 <code>removeValue(forkey:)</code> 메소드를 사용한다. 키에 해당하는 값이 제거된 후 반환된다.(set과 같음)</p>
<pre><code class="language-jsx">var nameAndAge4: [String: Int] = [:]

var nameAndAge5: [String: Int] = [&quot;서근&quot;: 25, &quot;미진&quot;: 19, &quot;철수&quot;: 12]


print(nameAndAge4.isEmpty) // true
print(nameAndAge5.isEmpty) // false
print(nameAndAge5.count) // 3
print(nameAndAge5) // [&quot;철수&quot;: 12, &quot;미진&quot;: 19, &quot;서근&quot;: 25]

print(nameAndAge5[&quot;서근&quot;]) // 25
print(nameAndAge5[&quot;소피아&quot;]) // nil

nameAndAge5[&quot;소피아&quot;] = 20
print(nameAndAge5[&quot;소피아&quot;]) // 20

print(nameAndAge5.removeValue(forKey: &quot;미진&quot;)) //19
print(nameAndAge5) // [&quot;소피아&quot;: 20, &quot;철수&quot;: 12, &quot;서근&quot;: 25]

print(nameAndAge5.removeValue(forKey: &quot;미진&quot;)) // nil
print(nameAndAge5[&quot;미진&quot;, default: 0]) // 미진은 nil이지만 기본값을 0으로 지정해줬기 때문에 0
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift - Set(세트)]]></title>
            <link>https://velog.io/@attosisss_/Swift-Set%EC%84%B8%ED%8A%B8</link>
            <guid>https://velog.io/@attosisss_/Swift-Set%EC%84%B8%ED%8A%B8</guid>
            <pubDate>Mon, 27 Nov 2023 04:13:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>세트는 말 그대로 공통적인 것들을 묶어놓은 것. 배열과 반대로 <strong>순서가 중요하지 않고</strong>, 유일한 값들로 채우려고 할 때 세트가 유용하다. 또 세트는 집합으로 활용하기에 좋다.</p>
</blockquote>
<p>세트(Set)는 아래 두 가지 차이점을 제외하면 배열(array)과 같은 값의 모음이다.</p>
<ul>
<li><strong>항목은 어떤 순서로도 저장되지 않는다.임의의 순서로 저장된다.</strong></li>
<li><strong>한 세트에 항목이 두 번 나타날 수 없다. 즉, 중복이 안된다. 모든 항목은 고유해야한다.</strong></li>
</ul>
<hr>
<p>다음처럼 직접 집합을 만들 수 있다.</p>
<pre><code class="language-jsx">let colors = Set([&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;])
//print = blue, green, red</code></pre>
<p>출력된 주석을 보면 내가 생성한 순서와 일치하지 않는다는 것을 볼 수가 있다. 순서가 지정되지 않는 것이다! 이 처럼 순서가 없기 때문에 <strong>배열처럼 요소의 위치를 사용해서 값을 읽을 수 없다.</strong></p>
<p>세트에 중복 항목을 삽입하려고 하면 중복 항목이 무시가 된다.</p>
<pre><code class="language-jsx">let colors2 = Set([&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;, &quot;red&quot;, &quot;blue&quot;])
//print = green, red, blue</code></pre>
<hr>
<h3 id="세트의-기본-프로퍼티">세트의 기본 프로퍼티</h3>
<p>세트 역시 기본적으로 요소를 추가하거나 , 삭제를 할 수 있도록 프로퍼티를 제공하고 있다.
<img src="https://velog.velcdn.com/images/attosisss_/post/7ee2a474-d7d9-4bdb-9f33-c9745551e6e6/image.png" alt="">
출력 결과!!!!!! 
<img src="https://velog.velcdn.com/images/attosisss_/post/37c58d11-01ea-406a-85b2-890774b3ea9d/image.png" alt=""></p>
<hr>
<h3 id="세트와-배열이-다른-이유">세트와 배열이 다른 이유</h3>
<p>세트와 배열은 모두 데이터 모음이므로 단일 변수 내에 여러 값을 보유한다. 그러나 값을 유지하는 방법이 중요하다.
<strong>세트는 순서가 지정되지 않고 중복을 포함할 수 없는 반면 배열은 순서를 유지하고 중복을 포함할 수 있다.</strong></p>
<p>세트는 추가 한 순서대로 개체를 저장할 필요가 없기 때문에 대신 바른 검색을 위해 최적화하는 임의의 순서로 개체를 저장할 수 있다.</p>
<p>이에 비해 배열은 항목을 제공한 순서대로 저장을 해야한다. 따라서 1000개의 항목을 포함하는 배열에 존재하는지 확인하려면 첫번째 항목에서부터 발견될 때까지 모든 항목을 확인해야한다 ...</p>
<p>즉, 큰 차이점은 &quot;이 항목이 있어?&quot;라고 말하고 싶을 때 <strong>세트가 더 유용</strong>하다. 예를 들어, 단어가 사전에 나타나는지 확인하려면 세트를 사용해야한다. <strong>중복이 없고 빠른 검색</strong>을 할 수 있다.</p>
<pre><code class="language-jsx">var readings = Set([true, false, true, true])</code></pre>
<pre><code class="language-jsx">var ratings = Set([1, 1, 1, 2, 2, 2])
//세트에는 고유한 항목이 포함되어 있어야 하므로 중복된 값들은 삭제됩니다.</code></pre>
<pre><code class="language-jsx">let someStrSet:Set = [&quot;ab&quot;,&quot;bc&quot;,&quot;cd&quot;,&quot;de&quot;,&quot;ab&quot;]
print(someStrSet)  //bc, ab, de, cd</code></pre>
<hr>
<h3 id="세트의-선언과-생성">세트의 선언과 생성</h3>
<pre><code class="language-jsx">// 빈 세트를 생성하는 방법
var names1: Set&lt;String&gt; = Set&lt;String&gt;()
var names2: Set&lt;String&gt; = []

// Array와 마찬가지로 대괄호를 사용한다.
var names3: Set&lt;String&gt; = [&quot;서근&quot;, &quot;미진&quot;, &quot;철수&quot;, &quot;포뇨&quot;, &quot;소피아&quot;]

// 타입 추론을 사용하면 컴파일러가 Set가 아닌 Array로 타입을 지정함
var numbers = [100, 200, 300]
print(type(of: numbers)) // Array&lt;Int&gt;

print(names1.isEmpty) // true
print(names3.isEmpty) // false
print(names3.count) // 5
</code></pre>
<p>만약 타입 추론을 사용하면 컴파일러가 Set가 아닌 Array로 타입을 지정해주기 때문에 만약 <strong>Set를 사용하려면 반드시 Set타입을 지정</strong>해줘야 한다.</p>
<p>또 , 세트에 요소를 추가하고 싶다면 Insert(__ :) 메소드를 , 삭제하고 싶다면 remove( ___:)메소드를 사용하는데 메소드를 사용하면 해당 요소가 삭제된 후 반환된다.</p>
<pre><code class="language-jsx">var names3: Set&lt;String&gt; = [&quot;서근&quot;, &quot;미진&quot;, &quot;철수&quot;, &quot;포뇨&quot;, &quot;소피아&quot;]

names3.insert(&quot;캘시퍼&quot;)
print(names3) // [&quot;소피아&quot;, &quot;철수&quot;, &quot;서근&quot;, &quot;캘시퍼&quot;, &quot;미진&quot;, &quot;포뇨&quot;]

names3.remove(&quot;미진&quot;)
names3.remove(&quot;소피아&quot;)
print(names3) // [&quot;캘시퍼&quot;, &quot;철수&quot;, &quot;서근ㅕ&quot;, &quot;포뇨&quot;]

print(names3.remove(&quot;민지&quot;)) // nil</code></pre>
<hr>
<h3 id="세트의-활용---집합-연산">세트의 활용 - 집합 연산</h3>
<p>세트는 자신 내부의 값들이 모두 유일함을 보장하므로, 집합 관계를 표현하고자 할 때 유용하게 쓰일 수 있으며, 두 세트의 교집합, 합집합 등을 연산하기에 매우 용이하다. 또한 <code>sorted()</code> 메소드를 통해 정렬된 배열을 반환해줄 수 있다.</p>
<pre><code class="language-jsx">let myFavoriteFruits: Set&lt;String&gt; = [&quot;사과&quot;, &quot;배&quot;, &quot;포도&quot;, &quot;메론&quot;, &quot;수박&quot;]
let yourFavoriteFruits: Set&lt;String&gt; = [&quot;메론&quot;, &quot;수박&quot;, &quot;딸기&quot;, &quot;참외&quot;, &quot;자두&quot;]

// 교집합
let intersectSet: Set&lt;String&gt; = myFavoriteFruits.intersection(yourFavoriteFruits)
print(intersectSet)
// [&quot;메론&quot;, &quot;수박&quot;]

// 여집합
let symmetricDiffSet: Set&lt;String&gt; = myFavoriteFruits.symmetricDifference(yourFavoriteFruits)
print(symmetricDiffSet)
// [&quot;사과&quot;, &quot;배&quot;, &quot;포도&quot;, &quot;참외&quot;, &quot;자두&quot;, &quot;딸기&quot;]

// 합집합
let unionSet: Set&lt;String&gt; = myFavoriteFruits.union(yourFavoriteFruits)
print(unionSet)
// [&quot;자두&quot;, &quot;메론&quot;, &quot;사과&quot;, &quot;포도&quot;, &quot;참외&quot;, &quot;수박&quot;, &quot;배&quot;, &quot;딸기&quot;]

// 차집합
let subtracSet: Set&lt;String&gt; = myFavoriteFruits.subtracting(yourFavoriteFruits)
print(subtracSet)
// [&quot;사과&quot;, &quot;배&quot;, &quot;포도&quot;]

//정렬된 배열을 반환해줌
print(unionSet.sorted())
// [&quot;딸기&quot;, &quot;메론&quot;, &quot;배&quot;, &quot;사과&quot;, &quot;수박&quot;, &quot;자두&quot;, &quot;참외&quot;, &quot;포도&quot;]
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[axios interceptors (feat. hisotry)]]></title>
            <link>https://velog.io/@attosisss_/axios-interceptors-feat.-hisotry</link>
            <guid>https://velog.io/@attosisss_/axios-interceptors-feat.-hisotry</guid>
            <pubDate>Wed, 25 Oct 2023 05:06:38 GMT</pubDate>
            <description><![CDATA[<p>프로젝트 진행하면서 middleware의 역할로 공통에러 처리를 위해 axios interceptors를 사용하게 되었다.
사용하면서 애 먹었던 케이스와 해결 방법을 정리하려고 한다 ~!</p>
<hr>
<p>먼저 어떤건지 알고 가자 !</p>
<blockquote>
<p><strong>Axios Interceptor란 ?</strong>
then 또는 catch로 처리되기 전에 요청과 응답을 가로챌수 있도록 하는 것이다.
참고링크 : <a href="https://axios-http.com/kr/docs/interceptors">Axios Interceptor Docs</a></p>
</blockquote>
<ol>
<li><p>Axios Interceptor를 위한 파일을 생성해준다.
(나는 기존에 있던 프로젝트 유지보수 역할인지라 따로 수정은 하지 않고 그대로   <code>src &gt; apis &gt; client.js</code> 파일에서 진행했다.)</p>
</li>
<li><p>Import Axios
원래 각 컴포넌트에서 axios.post()로 진행하던 부분이 있을 것이다. 그 대신에 위 파일(<code>client.js</code>)에서 axios 부분을 처리 해주면 된다.</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/71a3bdfd-9e46-48c6-8e58-fb0674a0e922/image.png" alt=""></p>
<pre><code>Axios를 import 해주고 client 라는 이름으로 선언을 해준다.</code></pre><ol start="3">
<li>URL지정 :  &quot;http// ~&quot; 개발하는 서버 주소를 적어준다 
(default로 사용할 개발 서버를 적어주면 된다.)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/33e9c1c4-3916-4685-9816-e6784e40fc76/image.png" alt=""></p>
<ol start="4">
<li>Request 세팅
<img src="https://velog.velcdn.com/images/attosisss_/post/bb25acf8-a589-40a5-acc2-a65efedd7d91/image.png" alt=""></li>
</ol>
<p>API 통신 요청 전 세팅을 해줄 수 있다.
접근 권한을 위해 모든 요청 전, header에 accessToken 세팅을 해주었다.
(로그인 화면에서 성공하면 토큰을 받아오기 때문에 로그인 요청에서는 예외 처리를 해주어야한다.)</p>
<ol start="5">
<li>Response 세팅</li>
</ol>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/ce5aa736-a9d7-4c4c-9915-154810aa3ec3/image.png" alt=""></p>
<p>response로 200인 경우에는 받아온 데이터를 return 해준다.</p>
<hr>
<p>그리고 아래에 error 부분이다. 여기서 에러가 발생했었다 ...</p>
<p>401인 경우(토큰이 만료됨)에 다시 로그인을 하게끔 라우팅을 해줘야했다.</p>
<h3 id="문제-상황">문제 상황</h3>
<p>URL만 변경이 되고 페이지 이동이 되지 않는다.(새로고침해야 됨)</p>
<ol>
<li>함수 밖이기 때문에 hook을 이용해 라우팅 불가</li>
<li>현재 프로젝트 react-router-dom 버전이 5이기 때문에 navigate 사용 불가</li>
<li>저기에 있는 <code>history.push</code>는 <code>useHistory()</code>가 아닌 <code>createBrowserHistory()</code> 이다.</li>
</ol>
<p>또 이 프로젝트가 리덕스 , 리덕스 사가를 함께 사용중이었다 . 그래서 window.location으로 이동을 하려고 할 때 리덕스 사가와 동기화 되지 않아서 페이지 이동은 되나 다시 로그인할 때 사가가 호출이 되지 않았다.</p>
<p>자세히 찾아보니 
*<em><code>createBrowserHistory</code>를 사용할 경우에는 Route와 연결을 해줘야한다는 것이다. 
*</em></p>
<h3 id="해결-방법">해결 방법</h3>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/3d8d900c-d338-4d2c-b909-4b4900f75d5d/image.png" alt=""></p>
<p>최상단에 있는 App.js에서 Router로 감싸주고 history를 넘겨주었다. </p>
<p>작동이 아주 잘~된다!!!!!!!</p>
<p>.
.
.
.
.</p>
<p>이렇게 보면 사실 되게 간단한 문제이지만.. 하루종일 hisotry를 사용하지 않아야 한다는 생각에 근본적인 사용방법을 생각하지 못한 거 같다.. 화이팅 !</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[redux-persist 사용법]]></title>
            <link>https://velog.io/@attosisss_/redux-persist-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@attosisss_/redux-persist-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 17 Oct 2023 05:32:22 GMT</pubDate>
            <description><![CDATA[<h3 id="사용하는-이유">사용하는 이유</h3>
<blockquote>
<p>redux의 store는 페이지를 새로고침 할 경우 state가 날아간다.
이것에 대한 대응 방안으로 localStorage 또는 session에 저장하고자 하는 reducer state를 저장하여 , 새로고침해도 저장공간에 있는 데이터를 redux에 불러오는 형식으로 이뤄진다.
이것을 위해 redux-persist를 사용한다!</p>
</blockquote>
<hr>
<h3 id="설치">설치</h3>
<blockquote>
<p><code>yarn add redux-persist</code></p>
</blockquote>
<h3 id="1-reducer에-persist-store-정의">1. reducer에 persist store 정의</h3>
<ul>
<li><p>localStorage에 저장
<code>import storage from &#39;redux-persist/lib/storage</code></p>
</li>
<li><p>sessionStorage에 저장
<code>import storageSession from &#39;redux-persist/lib/storage/session</code></p>
</li>
</ul>
<pre><code class="language-jsx">
// reducers/index.js
import { combineReducers } from &quot;redux&quot;;
➊ import { persistReducer } from &quot;redux-persist&quot;;
➋ import storage from &quot;redux-persist/lib/storage&quot;;

import auth from &quot;./auth&quot;;
import board from &quot;./board&quot;;
import studio from &quot;./studio&quot;;

➌ const persistConfig = {
  key: &quot;root&quot;,
  // localStorage에 저장합니다.
  storage,
  // auth, board, studio 3개의 reducer 중에 auth reducer만 localstorage에 저장합니다.
  whitelist: [&quot;auth&quot;]
  // blacklist -&gt; 그것만 제외합니다
};

export const rootReducer = combineReducers({
  auth,
  board,
  studio
});

➍ export default persistReducer(persistConfig, rootReducer);
</code></pre>
<h3 id="2-persist-store-사용">2. persist store 사용</h3>
<pre><code class="language-jsx">
// src/index.js

import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;
import { createStore, applyMiddleware, compose } from &quot;redux&quot;;
import { Provider } from &quot;react-redux&quot;;
➊ import { persistStore } from &quot;redux-persist&quot;;
➋ import { PersistGate } from &quot;redux-persist/integration/react&quot;;
import App from &quot;./App&quot;;
import configureStore from &quot;./store&quot;;
import { rootReducer } from &quot;./reducers&quot;;

const store = createStore(rootReducer);
➌ const persistor = persistStore(store);

const Root = () =&gt; (
  &lt;Provider store={store}&gt;
    ➍ &lt;PersistGate loading={null} persistor={persistor}&gt;
      &lt;App /&gt;
    &lt;/PersistGate&gt;
  &lt;/Provider&gt;
);

ReactDOM.render(&lt;Root /&gt;, document.getElementById(&quot;root&quot;));
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[immer.js]]></title>
            <link>https://velog.io/@attosisss_/immer.js</link>
            <guid>https://velog.io/@attosisss_/immer.js</guid>
            <pubDate>Tue, 17 Oct 2023 05:19:26 GMT</pubDate>
            <description><![CDATA[<h2 id="immerjs란-">immer.js란 ?</h2>
<blockquote>
<p> react에서 불변성을 유지하는 코드를 작성하기 쉽게 해주는 라이브러리</p>
</blockquote>
<h2 id="불변성이란">불변성이란?</h2>
<blockquote>
<p>쉽게 말하면 <code>상태를 변경하지 않는 것</code>이다.
상태를 변경하는데, 상태를 변경하지 않으면서 원하는 상태를 바꾼다는게 모순적인 거 같지만 ...</p>
</blockquote>
<h3 id="react-기본-속성">React 기본 속성</h3>
<blockquote>
<p>react는 기본적으로 부모 컴포넌트가 리렌더링을 하면 자식 컴포넌트도 리렌더링하게 된다. 즉, 얕은 비교를 통해 새로운 값인지 아닌지를 판단한 후 새로운 값인 경우 리렌더링을 하게 된다.
여기서 얕은 비교란 ! 객체 , 배열, 함수와 같은 참조 타입들을 실제 내부 값까지 비교하지 않고 동일 참조인지(동일한 메모리 값을 사용하는지)를 비교하는 것을 뜻합니다.</p>
</blockquote>
<ol>
<li>우리가 컴포넌트를 리렌더링 해야하는 상황이 있다고 가정하면 , 타입이 배열인 state를 바꾼다.</li>
<li>이때 , <code>state.push(1)</code>을 통해 state 배열에 직접 접근하여 요소를 추가한다.</li>
<li>우리는 push 전과 다른 값이라고 생각하지만, 리액트는 state라는 값은 <strong>새로운 참조값이 아니기 때문에</strong> 이전과 같은 값이라고 인식하고 리렌더링 하지 않는다.</li>
</ol>
<ul>
<li>즉, 위 이유로 우리가 state를 바꾸고 돔을 다시 만들려면, 새로운 객체 or 배열을 만들어 새로운 참조값을 만들고, react에게 이 값은 이전과 다른 참조값임을 알려야하는 것이다.
(위 과정은 가상 dom에서만 이뤄지는 렌더링)</li>
</ul>
<hr>
<h2 id="불변성-지키면서-state-바꾸기">불변성 지키면서 state 바꾸기</h2>
<h3 id="배열에-추가">배열에 추가</h3>
<pre><code class="language-jsx">setUsers(state.array.concat(user));</code></pre>
<h3 id="배열에서-삭제">배열에서 삭제</h3>
<pre><code class="language-jsx">const onRemove = id =&gt; {
  // user.id 가 id 인 것을 제거
  setUsers(users.filter(user =&gt; user.id !== id));
};
</code></pre>
<h3 id="배열에서-수정">배열에서 수정</h3>
<pre><code class="language-jsx">const onToggle = id =&gt; {
  setUsers(
    users.map(user =&gt;
      user.id === id ? { ...user, active: !user.active } : user
    )
  );
};
</code></pre>
<h3 id="객체에서-추가">객체에서 추가</h3>
<pre><code class="language-jsx">setState(state =&gt; {...state, key: value})</code></pre>
<h3 id="객체에서-제거">객체에서 제거</h3>
<pre><code class="language-jsx">setState(state =&gt; {..._.omit(state, &#39;deleteKey&#39;)})</code></pre>
<h3 id="객체에서-수정">객체에서 수정</h3>
<pre><code class="language-jsx">setState(state =&gt; {...state, key: newValue})</code></pre>
<ul>
<li>위 처럼 배열 혹은 객체를 바꾸면 불변성을 유지하면서 변경할 수 있다.
그러나 <code>immer.js</code>를 이용한 일반 객체 또는 배열 다루듯 사용하면 <code>immer가 불변성을 지켜준다</code>!</li>
</ul>
<hr>
<h2 id="immer">immer</h2>
<pre><code class="language-jsx">import produce from &quot;immer&quot;;

const baseState = [
  {
    todo: &quot;Learn typescript&quot;,
    done: true
  },
  {
    todo: &quot;Try immer&quot;,
    done: false
  }
];

const nextState = produce(baseState, draftState =&gt; {
  draftState.push({ todo: &quot;Tweet about it&quot; });
  draftState[1].done = true;
});</code></pre>
<p>immer에서 우리가 쓸 함수는 오직 <code>produce</code>만 알면 된다. 2가지의 파람을 가져오고 <strong>첫번째는 수정하고 싶은 객체/배열 , 두번째는 첫번째 파라미터에 할당된 객체/배열을 바꾸는 함수</strong>이다.</p>
<hr>
<h3 id="redux에서-immer-쓰기">redux에서 immer 쓰기</h3>
<pre><code class="language-jsx">const initialState = [{ name: &quot;nkh&quot;, address: { city: &quot;seoul&quot; } }];

export default function auth(state = initialState, action) {
  produce(state, draft =&gt; {
    switch (action.type) {
      case SET_INFO:
        draft[0].name = action.data.name;
        draft[0].address.city = action.data.city;
        break;
      case ADD_INFO:
        draft.push({ name: &quot;hhh&quot;, address: { city: &quot;zzz&quot; } });
      default:
        return draft;
    }
  });
}
</code></pre>
<ul>
<li>공식 예제</li>
</ul>
<pre><code class="language-jsx">const byId = (state, action) =&gt;
  produce(state, draft =&gt; {
    switch (action.type) {
      case RECEIVE_PRODUCTS:
        action.products.forEach(product =&gt; {
          draft[product.id] = product;
        });
        break;
    }
  });

const byId = (state, action) =&gt; {
  switch (action.type) {
    case RECEIVE_PRODUCTS:
      return {
        ...state,
        ...action.products.reduce((obj, product) =&gt; {
          obj[product.id] = product;
          return obj;
        }, {})
      };
    default:
      return state;
  }
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI]@State, @ObservedObject]]></title>
            <link>https://velog.io/@attosisss_/SwiftUIState-ObservedObject</link>
            <guid>https://velog.io/@attosisss_/SwiftUIState-ObservedObject</guid>
            <pubDate>Tue, 26 Sep 2023 05:32:32 GMT</pubDate>
            <description><![CDATA[<h2 id="state">@State</h2>
<h4 id="왜-state는-오직-struct에서만-작동을-할까-">왜 @State는 오직 Struct에서만 작동을 할까 ?</h4>
<p>만약 사용자가 버튼을 누르거나 스크롤을 하거나 텍스트에 상자를 입력했다고 치면, 그 특정 행동은 그 State 즉 상태를 변경한다. 그 이후에 일어날 일은 State가 변경되면 자동으로 변환시켜주는 일을 한다. 
사용자 인터페이스를 업데이트하는 것이다.</p>
<p>그렇다면 어떻게 이렇게 할 수 있을까? 
View를 사용할 때 ContentView가 실제로 View 프로토콜을 준수한다는 것을 기억해야한다. Body속성을 작성한다. 이것이 <code>View 프로토콜의 유일한 요구 사항이다.</code></p>
<p>상태를 변경할 때마다 body속성이 재설정 된다. 뷰 자체가 다시 렌더링 되는 것. 
따라서 State를 변경 할 때마다 항상 새로운 View에서 렌더링 된다는 점을 기억하자! 
그리고 사용자가 그것을 보게 된다. </p>
<p>사용자가 스위치를 켜고 끄는 것처럼 상태를 변경할 때 그 값을 State안에 넣으면 뷰를 렌더링 하게 된다.</p>
<p>이것을 사용하기 위해서는 @State와 Struct를 필수적으로 사용해야 합니다. <code>Strcut 내부의 값이 전체 Struct를 변경할 때마다 전체 구조체가 자동적으로 변경된다.</code></p>
<hr>
<h2 id="observedobject--published">@ObservedObject / @Published</h2>
<h3 id="state-1">@State</h3>
<p>특정 view에서만 사용하는 프로퍼티</p>
<h3 id="observedobject">@ObservedObject</h3>
<p>복잡한 프로퍼티(여러 프로퍼티나 메서드가 있거나 , 여러 view에서 공유할 수 있는 커스텀 타입이 있는 경우)</p>
<blockquote>
</blockquote>
<ul>
<li>String이나 integer 같은 간단한 로컬 프로퍼티 대신 외부 참조 타입을 사용한다는 점을 제외하면 @State와 매우 유사하다</li>
<li>@ObservedObject와 함꼐 사용하는 타입은 ObservedObject 프로토콜을 따라야한다.</li>
<li>@ObservedObject가 데이터가 변경되었음을 view에 알리는 방법은 여러가지가 있지만 가장 쉬운 방법은 @Published 프로퍼티 래퍼를 사용하는 것 - SwiftUI에 view reload를 트리거</li>
</ul>
<pre><code class="language-jsx">
class UserSettings: ObservableObject {
   //@ObervedObjet를 사용하기위해 @Published를 할당
   @Published var score = 0
}

struct ContentView: View {
   //@state를 지우고 @ObervedObject로 바꿔줌
    @ObservedObject var settings = UserSettings()

    var body: some View {
        VStack {
            Text(&quot;나의 점수는 \(settings.score)점 입니다.&quot;)
            Button(action: {
                self.settings.score += 1
            }) {
                Text(&quot;Increase Score&quot;)
            }
        }
    }
}
</code></pre>
<p><strong>score에 @Published가 붙었기 때문에 이 score가 변경되면 view를 reload하게 된다.</strong></p>
<hr>
<h3 id="swiftui에서-class를-그대로-사용하고-싶을-땐-">SwiftUI에서 class를 그대로 사용하고 싶을 땐 ?</h3>
<p>@ObservedObject property wrapper는 ObservableObject프로토콜에 적합한 유형에만 사용할 수 있으며 실제로 해결하기도 쉽다.</p>
<ul>
<li><p>FruitViewModel -&gt;  protocol : <code>ObservableObject</code>
<img src="https://velog.velcdn.com/images/attosisss_/post/ad07338f-639f-4068-ae6a-7e895ba9d13f/image.png" alt=""></p>
</li>
<li><p>FruitBasicView -&gt; <code>@ObservedObject</code></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/9bb895de-b889-4a49-870a-113f042f14a5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/8cd3fd16-e36a-47b2-b35d-c926e73bd867/image.png" alt=""></p>
<p><strong>결과 화면</strong></p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/76bb01a5-7f64-42c3-923d-85fcdd89f17e/image.png" alt=""><img src="https://velog.velcdn.com/images/attosisss_/post/1ee055db-0f80-424c-93cc-4022540579b8/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI]@Published vs @State]]></title>
            <link>https://velog.io/@attosisss_/SwiftUIPublished-vs-State</link>
            <guid>https://velog.io/@attosisss_/SwiftUIPublished-vs-State</guid>
            <pubDate>Fri, 15 Sep 2023 08:53:22 GMT</pubDate>
            <description><![CDATA[<h3 id="published">@Published</h3>
<p>@Published로 표시된 프로퍼티 값이 바뀌면 외부로 바뀐 값을 publish 하는 타입이라고 정의되어 있다.
@Published 속성으로 프로퍼티를 게시하면 , 그 타입에 대한 퍼블리셔가 생성된다
그 퍼블리셔에 접근하려면 <code>$</code> 사인을 붙여야 하고 , 공식 문서 예제는 아래와 같다.</p>
<pre><code class="language-jsx">class Weather {
    @Published var temperature: Double
    init(temperature: Double) {
        self.temperature = temperature
    }
}

let weather = Weather(temperature: 20)
cancellable = weather.$temperature
    .sink() {
        print(&quot;Temperature now: \($0)&quot;)
}
weather.temperature = 25</code></pre>
<p>@Published로 선언된 프로퍼티 값이 변경되면, 그 프로퍼티의 WillSet 블럭에서 퍼블리싱이 발생한다.
-&gt; 그 프로퍼티를 구독하고 잇는 구독자들이 실제로 그 프로퍼티에 새로운 값이 세팅됙 ㅣ전에 새로운 값을 전달 받게 된다는 뜻인거 같다.</p>
<p>위에 코드에서 <code>sink()</code> 는 컴바인 메서드인데 @Published로 선언된 temperature 값이 바뀔 때마다 호출된다고 한다</p>
<p>그래서 맨 처음에 Weather가 초기화되면서 temperature가 20으로 세팅되었을 때 20이 프린트되고, 두번째로 weather.temperature를 25로 바꿨을 때 또 25가 프린트 된다.</p>
<pre><code class="language-jsx">// Prints:
// Temperature now: 20.0
// Temperature now: 25.0</code></pre>
<p>그리고 중요한 것은,</p>
<blockquote>
<p>The @Published attribute is class constrained. Use it with properties of classes, not with non-class types like structures.</p>
</blockquote>
<p> <strong>-&gt; @Published는 클래스 프로퍼티에서만 사용할 수 있고, structure 같은 non-class 타입에서는 사용이 제한된다고 한다.</strong></p>
<p>프로젝트를 진행할 때는 ViewModel이 observing하고 , 값이 바뀜에 따라 뷰가 다시 그려지도록 하기 위해서 viewModel을 다 @ObservableObject를 채택하도록 해주어야할 거 같다.
그런데 이 @ObservableObject는 AnyObject를 채택하는 프로토콜이기 때문에 viewModel을 class로 선언해주고, 내부 프로퍼티들을 @Published로 선언해줘야겠다.</p>
<hr>
<h3 id="state">@State</h3>
<p>@State는 SwiftUI에 의해 관리되는 값을 읽거나 쓸 수 있는 property wrapper 타입이다.</p>
<p>SwiftUI는 <code>@State</code> 로 선언된 프로퍼티의 저장소를 관리한다고 한다.(저장소 역할 인 것은 react랑 비슷한거 같다.)
그 프로퍼티의 값이 바뀌면, SwiftUI는 view hierarchy에서 그 프로퍼티 값에 의존하는 뷰들을 업데이트한다.</p>
<p>그리고</p>
<blockquote>
<p>Use state as th <strong>single source of truth</strong> for a given value stored in a view hirearchy.</p>
</blockquote>
<p>라고 되어있는데 , <strong>single source of truth</strong>는 <em>뷰에서 사용하는 데이터는 하나의 원천을 갖는다 또는 데이터가 여러 곳에서 존재하지 않고 오직 한 곳에만 존재한다 라는 뜻으로 볼 수 있다고 한다.</em></p>
<p>그래서 view hierarchy 중 여러 view에서 state로 선언된 프로퍼티들이 쓰이더라도 , <strong>데이터의 원천은 하나라고 생각하면 될 거 같다.</strong></p>
<p>만약 부모 view가 자식view에게 state 프로퍼티를 전달하면, SwiftUI는 부모뷰의 state 프로퍼티 값이 바뀔 때마다 자식view를 업데이트하겠지만 , <em>자식 view는 그 값을 수정할 수는 없다.</em>
자식 view가 값을 변경하도록 하기 위해서는 , Binding을 전달해줘야한다.</p>
<pre><code class="language-jsx">struct PlayerView: View {
    @State private var isPlaying: Bool = false

    var body: some View {
        PlayButton(isPlaying: $isPlaying) // 자식뷰에게 binding 전달
    }
}

struct PlayButton: View {
    @Binding var isPlaying: Bool // 부모뷰에게서 binding 값 받음

    var body: some View {
        Button(isPlaying ? &quot;Pause&quot; : &quot;Play&quot;) {
            isPlaying.toggle() // 부모뷰에게서 받은 값 수정 가능
        }
    }
}</code></pre>
<p>그런데 위 상황에서 자식 view가 초기화 될 때 <code>isPlaying</code> 도 초기화 해버리면 SwiftUI의 storage management에 충돌이 일어난다(다른 view더라도 <code>isPlaying</code> 은 같은 값을 써야하기 때문인 듯)</p>
<p><strong>그래서 부모 view가 직접 자식view에게 state 프로퍼티를 공유하는 경우 이외에 다른 view에서 state 값에 접근하지 못하도록 state는 항상 private으로 선언하고, 그 값에 접근해야하는 view들 중에 가장 상위의 view에 위치 시켜야한다.</strong></p>
<p>그리고 그 값에 접근해야 하는 자식 view들은 그 값을 읽기 전용(binding X) 또는 읽기쓰기 전용(binding O)으로 공유 받아서 사용해야 한다.
그러면 state 프로퍼티들은 어떤 thread에서도 안전하게 값을 바꿀 수 있다고 한다.</p>
<p><strong>우선 @Published는 class에서만 사용되고, @State는 struct 내의 프로퍼티의 값을 바꿀 때 사용한다 (그리고 @State는 private으로 선언해야함)
그리고 @Published는 class에서만 사용되기 때문에 struct 타입인 뷰에서는 사용하기 어려울 것 같고, @State는 SwiftUI에 정의되어 있기 때문에 뷰에서 사용하는 것이 적절해보인다!!!!!!</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift-TrailingClosure]]></title>
            <link>https://velog.io/@attosisss_/Swift-TrailingClosure</link>
            <guid>https://velog.io/@attosisss_/Swift-TrailingClosure</guid>
            <pubDate>Thu, 31 Aug 2023 06:53:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>함수의 <strong>마지막 파라미터가 클로저일</strong> 때 , 이름 파라미터 값 형식이 아닌 함수 뒤에 붙여 작성하는 문법! 이때 , <strong>Argument Label은 생략</strong>된다.
(클로저를 좀 더 보기 편하게 문법을 변형하는 것 - 경량 문법 중 하나)</p>
</blockquote>
<h3 id="1-파라미터가-클로저-하나인-함수">1. 파라미터가 클로저 하나인 함수</h3>
<p>다음과 같이 <strong>클로저 하나만 파라미터로 받는 함수</strong>가 있을 때</p>
<pre><code class="language-jsx">
func doSomething(closure: () -&gt; ()) {
    closure()
}</code></pre>
<p>이 함수를 호출하려고 하면 어떻게 해야 했냐면</p>
<pre><code class="language-jsx">
doSomething(closure: { () -&gt; () in
    print(&quot;Hello!&quot;)
})
</code></pre>
<p>이렇게 해야했다 .. 
이렇게 클로저가 <strong>파라미터의 값 형식</strong>으로 <code>함수 호출 구문 ()</code> 안에 작성되어 있는 것을 <code>Inline Closure</code> 라고 부른당 ~</p>
<p>근데 이런 식으로 작성하면 헷갈리기 쉽다.
따라서 이 클로저를 파라미터 값 형식으로 보내는 것이 아닌, <strong>함수의 가장 마지막에 클로저를 꼬리처럼 덧붙여서</strong> 쓸 수 있는데 </p>
<pre><code class="language-jsx">
doSomething () { () -&gt; () in
    print(&quot;Hello!&quot;)
}</code></pre>
<p>위와 같은 방식으로 쓰는 것이 바로 <em><strong>Trailing Closure</strong></em> 이다!!!!!!!!</p>
<p>여기서 핵심은 두가지가 있당
<strong>1. 파라미터 클로저 하나일 경우, 이 클로저는 첫 파라미터이자 마지막 파라미터이므로 트레일링 클로저가 가능</strong>
<strong>2. Closure 라는 ArgumentLabel은 트레일링 클로저에선 생략</strong></p>
<p>근데 여기서 더 줄여보자면 <strong>파라미터가 클로저 하나일 경우</strong> 엔 <strong>호출구문인()도 생략 가능</strong></p>
<pre><code class="language-jsx">
doSomething { () -&gt; () in
    print(&quot;Hello!&quot;)
}
</code></pre>
<p>이렇게 가능합니다 <del>~</del>!!</p>
<hr>
<h3 id="2-파라미터가-여러-개인-함수">2. 파라미터가 여러 개인 함수</h3>
<p>다음과 같이 첫 번째 파라미터로 <code>success</code>라는 클로저를 받고 , 두 번째 파라미터로 <code>fail</code>이라는 클로저를 받는 함수가 있다.</p>
<pre><code class="language-jsx">func fetchData(success: () -&gt; (), fail: () -&gt; ()) {
    //do something...
}</code></pre>
<p>이런 함수가 있을 때 , <code>Inline Closure</code>의 경우</p>
<pre><code class="language-jsx">
fetchData(success: { () -&gt; () in
    print(&quot;Success!&quot;)
}, fail: { () -&gt; () in
    print(&quot;Fail!&quot;)
})</code></pre>
<p>이런식으로 호출을 하게 된다 !!
하지만 <strong>Trailing Closure</strong>의 경우에는 <strong>마지막 파라미터의 클로저는 함수 뒤에 붙여 쓸 수 있다</strong>.</p>
<pre><code class="language-jsx">
fetchData(success:  { () -&gt; () in
    print(&quot;Success!&quot;)
}) { () -&gt; () in
    print(&quot;Fail!&quot;)
}
</code></pre>
<p>따라서 이런 형태로 사용이 가능하당.
<strong>파라미터가 여러 개일 경우, 함수 호출 구문()을 마음대로 생략하면 안된다</strong> 
success란 파라미터는 파라미터 값 타입으로 넘겨주어야 하니까!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift-convenience init]]></title>
            <link>https://velog.io/@attosisss_/Swift-convenience-init</link>
            <guid>https://velog.io/@attosisss_/Swift-convenience-init</guid>
            <pubDate>Thu, 31 Aug 2023 02:33:58 GMT</pubDate>
            <description><![CDATA[<p>오늘은 init과 비슷한 convenience init에 대해서 적어보려고 한다!</p>
<p>struct와 달리 class 생성시 init을 통해서 모든 프로퍼티를 필수로 초기화를 해줘야한다.
그렇다면 convenience init은 뭘까?..</p>
<p>init과의 차이점을 상황을 예시로 이해해보자!</p>
<p>상황)
레스토랑을 운영하고 있는 나!
메뉴의 이름과 가격을 정하려고 한다.
하지만, 이름을 정하지 않았더라도 가격만 넣어놓을 수 있게 하고싶다.</p>
<p>원래 - init은 다음과 같이 모든 프로퍼티를 초기화 해줘야한다.</p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/88be3e9b-28ca-4e94-88f2-fa947e22e253/image.png" alt=""></p>
<ul>
<li>만약 하나라도 적어주지 않는다면 , 에러가 발생한다 ..</li>
</ul>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/7caea7d3-d0c3-4c27-b336-60cdbdce94a4/image.png" alt=""></p>
<p>위에서 말했듯이, 가격만 정해도 사용할 수 있게 만들고 싶다! 라고 할 경우를 위해
<strong>convenience init</strong>을 이용해서 가격만 넣어도, 자동으로 <code>dishName</code>은 &quot;미정&quot;으로 인스턴스를 생성할 수 있게 만들었당 ~!</p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/055aa776-7634-4752-b7b9-ff3162a3fb59/image.png" alt=""></p>
<p>다음과 같이 이름을 지정하지 않은 <code>secondMenu</code>의 이름은 <code>미정</code>이라고 출력된걸 볼 수 있다 ㅎㅎ</p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/812b5f0b-240c-4617-921a-2aa497338fbc/image.png" alt=""></p>
<h4 id="정리">정리!!!!!!!!!!!</h4>
<p>convenience init은 init이 모든 프로퍼티를 전달받지 않았을 때 ? 일부 프로퍼티만 있을 때 ? 의 상황을 대비하여 에러가 발생하지 않도록 미리 예약해놓는 느낌의 <strong>보조 이니셜라이저</strong>이다.
_
*여기서 유의할 점 _
init의 보조이기 때문에 위에서 선언한 init을 활용해서 사용해야 한다.
<img src="https://velog.velcdn.com/images/attosisss_/post/095794a4-2ef2-45ae-99f3-ff17a48a3048/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift- 후행 클로저에 매개변수]]></title>
            <link>https://velog.io/@attosisss_/Swift-%ED%9B%84%ED%96%89-%ED%81%B4%EB%A1%9C%EC%A0%80%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@attosisss_/Swift-%ED%9B%84%ED%96%89-%ED%81%B4%EB%A1%9C%EC%A0%80%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98</guid>
            <pubDate>Wed, 23 Aug 2023 05:18:14 GMT</pubDate>
            <description><![CDATA[<h3 id="매개변수를-받을-때--클로저를-매개변수로-사용">매개변수를 받을 때 , 클로저를 매개변수로 사용</h3>
<p>여태까지 우리는 <code>() -&gt; Void</code>를 &quot;매개변수가 없고 , 아무것도 반환하지 않는다&quot; 라는 의미로 사용해왔지만 , 클로저에서 허용되는 모든 매개변수 유형으로 <code>()</code>에 채울 수 있다.</p>
<p><strong>1. 반환형 없이 이벤트만 전달하는 경우 <code>() -&gt; Void</code></strong>
<img src="https://velog.velcdn.com/images/attosisss_/post/a2e8168f-9f0a-4a78-8f67-b43431b7da12/image.png" alt=""></p>
<p><strong>2. 매개변수로서 데이터를 반환하는 클로저 <code>(String) -&gt; Void</code></strong></p>
<p>Swift는 인라인 클로저에 자동으로 축약 인자 이름을 제공한다.</p>
<p>이 인자를 사용하면 인자 값을 순서대로 <code>$0</code> , <code>$1</code> , <code>$2</code> 등으로 사용할 수 있다. 축약 인자 이름을 사용하면 인자 값과 그 인자로 처리할 때 사용하는 인자가 같다는 것을 알기 때문에 인자를 입력받는 부분과 in 키워드 부분을 생략 할 수 있다.</p>
<p>그럼 아래와 같이 점점 단축해서 쓸 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/07f751aa-155c-4814-9d1c-bfa7d5b6470d/image.png" alt=""></p>
<p><code>place in</code> 을 작성하는 대신 Swift가 클로저의 매개변수에 자동 이름을 제공하도록 할 수 있다. 이것들은 달러기호<code>$</code>로 명명된 다음 <code>0부터</code> 세는 숫자로 지정된다.</p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/d0d5b5f8-f034-4f15-8b2f-4a38cf05b8d1/image.png" alt=""></p>
<p><strong>3. 매개변수로서 데이터 여러개를 반환하는 클로저 <code>(String,String) -&gt; Void</code></strong></p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/5908cb54-8278-4cc1-a45e-cad678313a49/image.png" alt=""></p>
<p>*<em>3. Completion 이벤트를 받고싶지 않은 경우 *</em></p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/4dd89eec-aa47-40b1-acf6-02f59a5cfedc/image.png" alt=""></p>
<hr>
<p>** 연습 ) **
<img src="https://velog.velcdn.com/images/attosisss_/post/f61172e5-ac8a-4b35-8621-17e347ccf036/image.png" alt=""></p>
<pre><code>갑자기 느낀점 : 
후행 클로저애 매개변수 사용하는 것이 익숙치 않아 노트에 적어보고 playground에서 연습하고 테스트해보니 훨씬 수월해졌다!</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Swift-Closure(클로저)]]></title>
            <link>https://velog.io/@attosisss_/Swift-Closure%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@attosisss_/Swift-Closure%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Tue, 22 Aug 2023 08:14:47 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>클로저는 일정 기능을 하는 코드를 하나의 블록으로 모아놓은 것을 말한다!</p>
</blockquote>
<p>함수를 만들어 변수에 할당하고, 해당 변수를 사용하여 해당 함수를 호출하고, 해당 함수를 다른 함수에 매게 변수로 전달 할 수도 있다. 이러한 방식으로 사용되는 함수를 클로저라고 하며 함수처럼 작동하지만 약간 다르다..</p>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/00d7e66c-e3dc-441f-9322-c413d64d435f/image.png" alt=""></p>
<p>여기서 질문 ..!!!</p>
<h4 id="✅-클로저는-무엇이며-swift에서-왜-이렇게-많이-사용될까-">✅ <em>클로저는 무엇이며 Swift에서 왜 이렇게 많이 사용될까 ?</em></h4>
<p>클로저를 사용하는 가장 일반적인 이유 중 하나는 <strong>기능을 저장</strong>하는 것이다.</p>
<ol>
<li>딜레이 후 일부 코드를 실행한다.</li>
<li>애니메이션이 완료된 후 일부 코드를 실행한다.</li>
<li>다운로드가 완료되면 일부 코드를 실행한다.</li>
<li>사용자가 메뉴에서 옵션을 선택한 경우 일부 코드를 실행한다.</li>
</ol>
<hr>
<h3 id="1-클로저에서-매개변수-사용">1. 클로저에서 매개변수 사용</h3>
<p>클로저에서 매개변수는 <code>{}</code> 안에 나열된다. 클로저가 매개변수를 받아들이도록 하려면 <code>{}</code> 바로 뒤 <code>()</code> 안에 나열한 다음 <code>in</code> 을 작성하여 클로저가 시작된다는 것을 알 수 있도록 작성해야한다.</p>
<p>예시로 다음과 같이 장소 이름 문자열을 유일한 매개변수로 받아들이는 클로저를 만들 수 있다.</p>
<pre><code class="language-jsx">let driving = { (place: String) in
    print(&quot;저는 차를타고 \(place)에 가고있습니다.&quot;)
}</code></pre>
<p>여기서 함수와 클로저의 차이점 중 하나는 클로저를 사용할 때는 <code>변수레이블을 사용하지 않는다</code>는 점이다. </p>
<p>아래와 같이 호출할 수 있당!!</p>
<pre><code class="language-jsx">driving(&quot;병원&quot;)
//저는 차를타고 병원에 가고있습니다.</code></pre>
<hr>
<h3 id="2-클로저와-함수의-매개-변수-갖는-방식의-차이">2. 클로저와 함수의 매개 변수 갖는 방식의 차이</h3>
<h4 id="함수">함수</h4>
<pre><code class="language-jsx">func pay(user: String, amount: Int) {
    // code
}</code></pre>
<h4 id="클로저">클로저</h4>
<pre><code class="language-jsx">let payment = { (user: String, amount: Int) in
    // code
}</code></pre>
<p><em>함수의 매개변수 괄호가 중괄호 안으로 들어왔다고 생각하면 편할 거 같다 ㅎㅎ.</em> <code>in</code> 키워드는 매개변수 끝에 있고 , 클로저의 본문이 시작한다고 표시하고 있다.</p>
<p>매개변수와 클로저 본문은 모두 동일한 코드 블록의 일부이며 변수이다. 
<strong>만약 <code>in</code>가 없으면 클로저 본문이 시작되는 위치를 알기 어렵기 때문에 아주 중요하다구 한다 ~</strong></p>
<hr>
<h3 id="3-클로저에서-값-반환">3. 클로저에서 값 반환</h3>
<p>클로저는 값을 <code>return</code> 할 수 있으며 방법은, 클로저 내부 <code>in</code> 키워드 바로 앞에 작성하면 된다.</p>
<pre><code class="language-jsx">let driving = { (place: String) in
    print(&quot;저는 차를타고 \(place)에 가고있습니다.&quot;)
}</code></pre>
<p>위의 코드는 단순히 <code>driving()</code> 클로저를 가져와서 출력하고 있다.
대신 아래처럼 문자열을 <code>return</code> 할 수도 있다.</p>
<pre><code class="language-jsx">let drivingWithReturn = { (place: String) -&gt; String in
    return &quot;저는 차를타고 \(place)에 가고있습니다.&quot;
}
// 클로저 실행 후 return 값 출력
let message = drivingWithReturn(&quot;병원&quot;)
print(message)
</code></pre>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/eb7dacf1-b766-40ad-bc7f-648684077f67/image.png" alt=""></p>
<h4 id="그렇다면-매개변수가-없는-클로저에서-값을-반환하는-방법은-">그렇다면 매개변수가 없는 클로저에서 값을 반환하는 방법은 ??</h4>
<ol>
<li>하나의 매개변수를 받아들이고 아무것도 return 하지 않는 클로저</li>
</ol>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/6ddad8f8-8618-4ff7-abea-156597b52dd2/image.png" alt=""></p>
<ol start="2">
<li>하나의 매개변수를 받아들이고 Bool을 return 하는 클로저</li>
</ol>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/8120ba06-5b74-48b7-a54c-ebaa01010a04/image.png" alt=""></p>
<ol start="3">
<li>매개변수를 받아들이지 않고 값을 반환하는 것은 불가능하기 때문에 , 대신 다음과 같이 매개변수 목록에 빈 () 를 사용해야한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/attosisss_/post/952a086c-4e87-40e3-8723-e9e11630fad4/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>