<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>tera_geniel.log</title>
        <link>https://velog.io/</link>
        <description>이것저것 개발하는 것 좋아하지만 서버 개발이 제일 좋더라구요..</description>
        <lastBuildDate>Tue, 30 Jan 2024 15:08:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>tera_geniel.log</title>
            <url>https://velog.velcdn.com/images/tera_geniel/profile/1721e11a-426b-452c-a71b-ff40d9b10c44/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. tera_geniel.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/tera_geniel" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[코어 자바스크립트 요약] 02. 실행 컨텍스트]]></title>
            <link>https://velog.io/@tera_geniel/%EC%BD%94%EC%96%B4-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9A%94%EC%95%BD-02.-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@tera_geniel/%EC%BD%94%EC%96%B4-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9A%94%EC%95%BD-02.-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Tue, 30 Jan 2024 15:08:47 GMT</pubDate>
            <description><![CDATA[<h1 id="02-실행-컨텍스트">02. 실행 컨텍스트</h1>
<aside>
⚠️ 실행 컨텍스트를 정확히 이해하자.

</aside>

<h1 id="01-실행-컨텍스트란">01. 실행 컨텍스트란?</h1>
<ul>
<li><p>의미: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체.</p>
</li>
<li><p>JS의 동작</p>
<p>  [1] 어떤 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올림(hoisting)
  [2] 외부 환경 정보를 구성
  [3] this 값을 설정</p>
</li>
<li><p>역할: 전체 코드의 환경과 순서를 보장</p>
<p>  <em>동일한 환경</em>에 있는 코드들을 실행할 때 필요 환경 정보들을 모아 컨텍스트를 구성, 콜 스택 call stack에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스와 관련 있는 코드들을 실행하는 식</p>
<p>  <em>동일한 환경*</em>: 하나의 실행 컨텍스트를 구성할 수 있는 방법 - 전역공간, eval 함수, 함수</p>
</li>
<li><p>실행 컨텍스트 객체: JS 엔진이 어떤 실행 컨텍스트가 활성화될 때 그 관련된 코드들을 실행하는 데 필요한 환경 정보들을 수집</p>
<ul>
<li>VariableEnvironment: 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언 시점의 LexicalEnvironment의 스냅샷 snapshot, 변경 사항은 반영되지 않음</li>
<li>LexicalEnvironment: 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영</li>
<li>ThisBinding: this 식별자가 바라봐야 할 대상 객체</li>
</ul>
</li>
</ul>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/a12d49c7-5ff6-4a54-8da2-56ffdebd2e1e/Untitled.png" alt="Untitled"></p>
<h1 id="02-variableenvironment">02. VariableEnvironment</h1>
<ul>
<li>차이: LexicalEnvironment와 같지만 최초 실행 시의 스냅샷을 유지한다는 점이 다름.</li>
<li>00실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 먼저 담고, LexicalEnvironment를 만들고, 이후에는 LexicalEnvironment를 주로 활용.</li>
<li>내부 구성(공통): VariableEnvironment와 LexicalEnvironment 둘 모두 내부 구성은 아래와 같이 동일하다.<ul>
<li>environmentRecord</li>
<li>outer-EnvironmentReference</li>
</ul>
</li>
</ul>
<h1 id="03-lexicalenvironment">03. LexicalEnvironment</h1>
<h2 id="2-3-1-environmentrecord와-호이스팅">2-3-1 environmentRecord와 호이스팅</h2>
<ul>
<li><p>environmentRecord: 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장.</p>
<ul>
<li><p>식별자의 종류</p>
<p>  [1] 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자
  [2] 선언한 함수 자체
  [3] var로 선언된 변수의 식별자</p>
</li>
<li><p>참고</p>
<p>  전역 실행 컨텍스트는 <del>변수 객체</del>가 아닌, JS 구동 환경이 별도로 제공하는 <em>전역 객체</em>를 활용</p>
<p>  <em>전역 객체*</em>: 브라우저의 window, Node.js의 global 객체 등이 있음
  이들은 JS 내장 객체(native object)가 아닌 호스트 객체(host object)로 분류됨</p>
</li>
</ul>
</li>
<li><p>hoisting: 컨텍스트 내부 전체를 처음부터 끝까지 훑어 나가며 순서대로 수집</p>
<ul>
<li>BUT 코드 실행 전에 수집</li>
<li>실제로 끌어올리지는 않지만, 편의상 JS 엔진은 식별자들을 최상단으로 끌어올린 후 실제 코드를 실행한다고 생각 가능</li>
</ul>
</li>
</ul>
<h3 id="hoisting-규칙">hoisting 규칙</h3>
<ul>
<li>변수명과 함수 선언의 정보를 수집<ul>
<li>변수: 선언부와 할당부를 나누어 선언부만 수집</li>
<li>함수 선언: 함수 전체를 수집</li>
<li>함수 표현식: 변수 선언부만 호이스팅</li>
</ul>
</li>
<li>hoisting 규칙에 의해 전역에서 호출 가능한 <del>함수 선언</del>보다는 선언 이후 실행 가능한 함수 표현식 사용하는 게 best!<ul>
<li>EX) 전역공간에 동명의 함수가 여럿 존재하는 상황에서의 디버깅!</li>
</ul>
</li>
</ul>
<p><strong>@매개변수와 변수에 대한 호이스팅</strong></p>
<pre><code class="language-jsx">// 매개변수와 변수에 대한 호이스팅 - 원본 코드
function a (x) {  // 수집 대상 1 - 매개 변수 선언
  console.log(x); 
    var x;          // 수집 대상 2 - 변수 선언
    console.log(x);
    var x = 2;      // 수집 대상 3 - 변수 선언
    console.log(x);
}
a(1);

// 매개변수와 변수에 대한 호이스팅 - 호이스팅을 마친 상태
⚠️ 주의: 실제 JS 엔진은 이러한 변환 과정을 거치지 않음. 사람 입장에서의 코드로 변환한 것!

function a (){
    var x;         // 수집 대상 1 - 매개 변수 선언
    ~~var x;         // 수집 대상 2 - 변수 선언
    var x;         // 수집 대상 3 - 변수 선언~~

    x = 1;         // 수집 대상 1의 할당 부분
    console.log(x);
    console.log(x);
    x = 2;         // 수집 대상 3의 할당 부분
    console.log(x);
}
a(1);</code></pre>
<p>실제 코드 실행</p>
<p>[1] 변수 x 선언(메모리 공간 미리 확보, 주솟값 변수 x에 연결)
[2] 다시 변수 x 선언, 이미 선언된 변수 x가 있으므로 무시
[3] x에 1 할당
[4] 각 x 출력, 모두 1 출력됨
[5] x에 2 할당(원래 1을 가리키는 주솟값 → 2의 주솟값으로 대치)
[6] x 출력, 2 출력됨
[7] 함수 내부의 모든 코드가 실행됐으므로 실행 컨텍스트가 콜 스택에서 제거됨</p>
<p><strong>@함수 선언의 호이스팅</strong></p>
<pre><code class="language-jsx">// 원본 코드
function a () {
    console.log(b);
    var b = &#39;bb&#39;;
    console.log(b);
    function b(){}
    console.log(b);
}
a();

// 호이스팅을 마친 상태
function a(){
    var b;
    function b (){}

    console.log(b);
    b = &quot;bbb&quot;;
    console.log(b);
    console.log(b);
}
a();

// 함수 선언문을 함수 표현식으로 바꾼 코드
function a(){
    var b;                        // [1] 
    var b = function b (){};      // [2]

    console.log(b);               // [3]  함수 b 출력
    b = &quot;bbb&quot;;                    // [4]
    console.log(b);               // [5]  &quot;bbb&quot; 출력
    console.log(b);
}
a();</code></pre>
<p>실행 시</p>
<p>[1] 변수 b 선언
[2] 함수는 별도의 메모리, 그 함수가 저장된 주솟값을 b와 연결된 공간에 저장. 이제 변수 b는 함수를 가리킴
[3] 변수 b에 할당된 함수 b가 출력
[4] 변수 b에 “bbb”를 할당
[5] “bbb” 출력 후 실행 컨텍스트는 콜 스택에서 제거(모든 코드 실행됐으므로)</p>
<h3 id="함수-선언문과-함수-표현식">함수 선언문과 함수 표현식</h3>
<ul>
<li><p>함수 선언문: function 정의부만 존재하고 별도의 할당 명령이 없는 경우</p>
</li>
<li><p>함수 표현식: 정의한 function을 별도의 변수에 할당하는 것을 말함</p>
<ul>
<li>기명 함수 표현식: 함수명을 정의한 함수 표현식</li>
<li>익명 함수 표현식: 함수명을 정의하지 않은 함수 표현식</li>
</ul>
</li>
<li><p>함수 정의하는 3가지 방식</p>
<pre><code class="language-jsx">  // 함수를 정의하는 3가지 방식
  function a(){ ... }
  a();  // 실행 OK!

  var b = function(){ ... }
  b();  // 실행 OK!

  var c = function d(){ ... }
  c();  // 실행 OK!
  d();  // 에러!</code></pre>
<ul>
<li><p>⚠️ 기명 함수 표현식 주의</p>
<p>  외부에서는 함수명으로 함수를 호출할 수 없음.
  오직 내부에서 함수명으로 함수 호출 가능!
  과거) 기명 함수 표현식 - 디버깅 시 어떤 함수인 지 추적하기에 익명 함수 표현식보다 유리한 측면이 있었음
  현재) 모든 브라우저 - 익명 함수 표현식의 변수명을 함수의 name 프로퍼티에 할당</p>
</li>
</ul>
</li>
</ul>
<p><strong>@함수 선언문과 함수 표현식</strong></p>
<pre><code class="language-jsx">// 원본 코드
console.log(sum(1,2));
console.log(multipy(3,4));

function sum(a,b){  // 함수 선언문 sum
    return a+b;
}

var multiply = function(a,b){   // 함수 표현식 multiply
    return a*b;
}

// 호이스팅을 마친 상태
var sum = function sum(a,b){  // 함수 선언문 전체를 호이스팅
    return a+b;
}
var multiply;                 // 변수는 선언부만 수집

console.log(sum(1,2));
console.log(multipy(3,4));

multifly = function(a,b){     // 변수의 할당부는 원래 자리에 남겨둠
    return a*b;
}</code></pre>
<p>실행 시</p>
<p>[1] sum ← 메모리 공간 확보 후 연결
[2] multiply ← 메모리 공간 확보 후 연결
[3] sum 함수 → 또 다른 메모리 공간에 저장, 그 주솟값을 변수 sum의 공간에 할당.
[4] sum 실행
[5] multiply is not a function 이라는 에러 메시지가 출력됨
← 현재 multiply에는 값이 할당되어 있지 않음. 비어있는 대상을 함수로 여겨 출력하라는 명령이었음.</p>
<h2 id="2-3-2-스코프-스코프-체인-outerenvironmentreference">2-3-2 스코프, 스코프 체인, outerEnvironmentReference</h2>
<ul>
<li>scope: 식별자에 대한 유효범위<ul>
<li>ES5까지의 JS는 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성</li>
</ul>
</li>
<li>스코프 체인 scope chain: 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것<ul>
<li>LexicalEnvironment의 outerEnvironmentReference 활용</li>
</ul>
</li>
</ul>
<h3 id="스코프-체인">스코프 체인</h3>
<ul>
<li><p>outerEnvironmentReference: 현재 호출된 함수가 선언될 당시*의 LexicalEnvironment를 참조</p>
<p>  선언될 당시* : A 함수 내부에 B 함수를 선언, B 함수 내부에 C 함수를 선언
  함수 C의 outerEnvironmentReference → 함수 B의 LexicalEnvironment를 참조
  (함수 C가 선언될 당시)
  함수 B의 outerEnvironmentReference → 함수 A의 LexicalEnvironment를 참조
  (함수 B가 선언될 당시)</p>
</li>
<li><p>outerEnvironmentReference는 연결 리스트의 형태</p>
<ul>
<li>선언 시점의 LexicalEnv를 계속 찾아 올라가면 마지막엔 전역 컨텍스트의 LexicalEnv가 있을 것</li>
</ul>
</li>
<li><p>여러 스코프에서 동일한 식별자를 선언한 경우 - 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근이 가능</p>
<ul>
<li>가장 먼저* : 찾아 ^올라가는^ 형태</li>
</ul>
</li>
</ul>
<p><strong>@예제</strong></p>
<pre><code class="language-jsx">var a = 1;
var outer = function() {
    var inner = function () {
        console.log(a);
        var a = 3;
    };

    inner();
    console.log(a);
};
outer();
console.log(a);</code></pre>
<ul>
<li><p>설명</p>
<p>  [1] 가장 처음
  전역 컨텍스트 활성화
  전역 컨텍스트 → environmentRecord = {a, outer} 식별자를 저장
  전역 컨텍스트 → outerEnv-Ref = 아무것도 안 담김</p>
<p>  [2] 전역 스코프에서 호이스팅
  전역 스코프에 있는 변수 a ← 1,
  outer ← 함수 할당</p>
<p>  [3] <code>outer();</code>
  outer 함수 호출 전역 컨텍스트의 코드는 임시중단,
  outer 실행 컨텍스트가 활성화, 
  2번째 줄로 이동</p>
<p>  [4] <code>var outer = function () {</code>
  outer 실행 컨텍스트 → environmentRecord = {inner} 식별자 저장
  outer 실행 컨텍스트 → outerEnv-Ref = [GLOBAL, {a, outer}] 
  [GLOBAL, {a, outer}]* : 전역 컨텍스트 → LexicalEnvironment를 참조복사한 것을 편의상 표기.</p>
<pre><code>1번째 인자 = 실행 컨텍스트의 이름, 2번째 인자 = environmentRecord 객체</code></pre><p>  [5] <code>var inner = function () {</code>
  outer 스코프에 있는 변수 inner에 함수 할당</p>
<p>  [6] <code>inner();</code></p>
<p>  inner 함수 호출. outer 실행 컨텍스트 임시중단, inner 실행 컨텍스트 활성화</p>
<p>  [7] <code>var inner = function () {</code>
  inner 실행 컨텍스트 → environmentRecord = {a} 식별자 저장
  inner 실행 컨텍스트 → outerEnv-Ref = [outer, {inner}]</p>
<p>  [8] <code>console.log(a);</code>
  식별자 a에 접근, inner 컨텍스트의 envRec에서 a를 검색
  undefined 출력</p>
<p>  [9] <code>var a = 3;</code>
  inner 스코프에 변수 a에 3 할당</p>
<p>  [10] <code>};</code>
  inner 함수 실행이 종료, inner 실행 컨텍스트가 콜 스택에서 제거, outer 실행 컨텍스트가 다시 활성화</p>
<p>  [11] <code>console.log(a);</code>
  식별자 a 에 접근.
  활성화된 실행 컨텍스트의 LexicalEnv에 접근, 매 요소마다 envRec에 a 있는 지 찾아보고, 없으면 outerEnvRef에서 environmentRecord에서 a 찾는 식으로 계속해서 검색</p>
<p>  [12] <code>};</code>
  outer 함수 실행 종료
  outer 실행 컨텍스트가 콜 스택에서 제거, 바로 아래의 전역 컨텍스트가 다시 활성화</p>
<p>  [13] <code>console.log(a);</code>
  식별자 a에 접근
  현 활성화된 전역 컨텍스트의 environmentRecord에서 a를 검색
  전역 컨텍스트가 콜 스택에서 제거되고 종료</p>
</li>
</ul>
<p><strong>@ 간략 요약 표</strong></p>
<ul>
<li>LE = LexicalEnvironment, e = environmentRecord, o = outerEnvironmentReference</li>
<li>변수 은닉화<ul>
<li>스코프 체인 상에 있는 변수라고 해서 무조건 접근 가능한 것은 아니며, 가장 가까운 실행 컨텍스트에서 어떤 변수가 이미 검색되는 경우, 스코프 체인 검색을 더 이상 진행하지 않음. 즉, 접근할 수 없는 것과 마찬가지.</li>
</ul>
</li>
</ul>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/23221020-521d-4f97-bd69-7d98fb49c759/Untitled.png" alt="Untitled"></p>
<h3 id="전역변수와-지역변수">전역변수와 지역변수</h3>
<ul>
<li>전역변수: 전역 공간에서 선언한 변수</li>
<li>지역변수: 함수 내부에서 선언한 변수</li>
<li>코드의 안전성을 위해 가급적 전역변수 사용을 최소화하고자 노력하는 것이 좋음.</li>
</ul>
<h1 id="04-this">04. this</h1>
<p>실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장</p>
<p>실행 컨텍스트 활성화 당시, this가 지정되지 않는 경우: this ← 전역 객체가 저장</p>
<h1 id="05-정리">05. 정리</h1>
<ul>
<li>실행 컨텍스트<ul>
<li>실행할 코드에 제공할 환경 정보들을 모아놓은 객체</li>
<li>전역 공간에서 자동으로 생성되는 전역 컨텍스트와 eval 및 함수 실행에 의한 컨텍스트 등</li>
<li>활성화 시점에 VariableEnv, LexicalEnv, ThisBinding 정보를 수집</li>
</ul>
</li>
<li>VariableEnv, LexicalEnv<ul>
<li>공통: 실행 컨텍스트 생성 당시 동일 내용</li>
<li>차이<ul>
<li>LexicalEnv: 함수 실행 도중에 변경되는 사항이 즉시 반영</li>
<li>VariableEnv: 초기 상태를 유지</li>
</ul>
</li>
<li>구성<ul>
<li>environmentRecord: 매개변수명, 변수의 식별자, 선언한 함수의 함수명 수집</li>
<li>outerEnvironment-Reference: 바로 직전 컨텍스트의 LexicalEnvironment 정보를 참조하는 outerEnvironmentReference로 구성</li>
</ul>
</li>
</ul>
</li>
<li>호이스팅<ul>
<li>코드 해석을 수월하게 해 environmentRecord의 수집 과정을 추상화<ul>
<li>실행 컨텍스트가 관여하는 코드 집단의 최상단으로 이들을 끌어올린다고 해석하는 것</li>
</ul>
</li>
<li>변수 선언과 값 할당이 동시에 이뤄진 문장: 선언부만 호이스팅, 할당 과정(호출부)는 원래 자리에.<ul>
<li>함수 선언문 vs 함수 표현식의 차이 발생</li>
</ul>
</li>
</ul>
</li>
<li>스코프<ul>
<li>변수의 유효범위</li>
<li>outerEnvironmentReference: 해당 함수가 선언된 위치의 LexicalEnvironment를 참조</li>
<li>코드 상에서 어떤 변수에 접근하는 경우 현재 컨텍스트의 LexicalEnvironment를 탐색해서<ul>
<li>발견 : 그 값을 반환</li>
<li>발견 못하면 다시 outerEnvRef에 담긴 LexicalEnv를 탐색하는 과정을 거침</li>
<li>전역 컨텍스트까지 탐색해도 해당 변수를 찾지 못하면 undefined를 반환</li>
</ul>
</li>
</ul>
</li>
<li>전역변수와 지역변수<ul>
<li>전역변수: 전역 컨텍스트의 LexicalEnv에 담긴 모든 변수</li>
<li>그 밖의 함수에 의해 생성된 실행 컨텍스트의 변수들</li>
<li>안전한 코드 구성을 위해 전역변수 사용 최소화</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코어 자바스크립트 요약] 01. 데이터 타입]]></title>
            <link>https://velog.io/@tera_geniel/%EC%BD%94%EC%96%B4-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9A%94%EC%95%BD-01.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@tera_geniel/%EC%BD%94%EC%96%B4-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9A%94%EC%95%BD-01.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85</guid>
            <pubDate>Tue, 30 Jan 2024 15:07:28 GMT</pubDate>
            <description><![CDATA[<aside>
⚠️ [1] 기본형 타입과 참조형 타입이 서로 다르게 동작하는 이유를 이해하며, 적절히 활용한다.
[2] 데이터 타입과 관련된 개념에 대해 이해한다.

</aside>

<h1 id="01-데이터-타입의-종류">01. 데이터 타입의 종류</h1>
<h2 id="기본형-primitive-type">기본형 primitive type</h2>
<blockquote>
<p>숫자 number, 문자열 string, 불리언 boolean, null, undefined, 심볼 symbol (ES6)</p>
</blockquote>
<ul>
<li>할당이나 연산 시 값이 담긴 주솟값을 바로 복제함.</li>
<li>불변성(immutuability)</li>
</ul>
<h2 id="참조형-reference-type">참조형 reference type</h2>
<blockquote>
<p>객체 object, 배열 array, 함수 function, 날짜 date, 정규표현식 RegExp</p>
</blockquote>
<p>할당이나 연산 시 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주소값을 복제함.</p>
<h1 id="02-데이터-타입에-관한-배경지식">02. 데이터 타입에 관한 배경지식</h1>
<h3 id="1-2-1-메모리와-데이터">1-2-1 메모리와 데이터</h3>
<ul>
<li>비트: 0 또는 1만 표현할 수 있는 하나의 메모리 조각</li>
<li>바이트(byte): 8비트, 8개의 비트로 구성</li>
</ul>
<p>C/C++ 혹은 JAVA는 2바이트(short), 4바이트(int)</p>
<p>Javascript는 숫자의 경우 8바이트를 확보(정수형과 부동소수형 구분을 하지 않음)</p>
<p>모든 데이터는 바이트 단위의 식별자, 더 정확히는 메모리 주솟값을 통해 서로 구분 및 연결이 가능</p>
<h3 id="1-2-2-식별자와-변수">1-2-2 식별자와 변수</h3>
<ul>
<li>변수: 변할 수 있는 데이터</li>
<li>식별자: 변수명</li>
</ul>
<h1 id="03-변수-선언과-데이터-할당">03. 변수 선언과 데이터 할당</h1>
<h3 id="1-3-1-변수-선언">1-3-1 변수 선언</h3>
<p>변수: 변경 가능한 데이터가 담길 수 있는 공간 또는 그릇</p>
<pre><code class="language-jsx">var a;</code></pre>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/4d9dcfdf-8d25-49ea-a207-9e1c2c93dab6/Untitled.png" alt="Untitled"></p>
<p>[1] 변수 선언: 컴퓨터는 메모리에서 비어있는 공간 하나를 확보, 임의로 1003번으로 정함.
이 공간의 이름(식별자)을 a라고 지정.</p>
<p>[2] a에 접근: 컴퓨터는 메모리에서 a라는 이름을 가진 주소를 검색해 해당 공간에 담긴 데이터를 반환</p>
<h3 id="1-3-2-데이터-할당">1-3-2 데이터 할당</h3>
<pre><code class="language-jsx">a = &#39;abc&#39;; // 할당</code></pre>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/bec7ccbb-344f-4962-8771-c1b954fbc5db/Untitled.png" alt="Untitled"></p>
<p>[1] 데이터 영역의 빈공간(@5004)에 문자열 ‘abc’를 저장</p>
<p>[2] 변수 영역에서 a라는 식별자를 검색(@1003)</p>
<p>[3] 앞서 저장한 문자열의 주소 @5004를 @1003의 공간에 대입</p>
<ul>
<li><p><strong>@왜 변수 영역에 값을 직접 대입하지 않고 굳이 번거롭게 단계를 더 거치는가?</strong></p>
<p>  → 데이터 변환을 자유롭게 + 메모리를 더욱 효율적으로 관리하기 위한 고민의 결과</p>
</li>
<li><p><strong>JS에서 문자열은 특별히 정해진 규격이 없다. 문자열 변수는 가변적으로 데이터 영역을 차지한다.</strong></p>
</li>
<li><p><strong>@미리 확보한 공간 내에서만 데이터 변환을 할 수 있다면?</strong></p>
<p>  → 확보된 공간을 변환된 데이터 크기에 맞게 늘리는 작업이 선행되어야 할 것</p>
<p>  → 따라서 변수 영역, 데이터 영역을 분리하는 게 효율적</p>
</li>
</ul>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/b5905188-1a50-41ee-814c-e78940b09d74/Untitled.png" alt="Untitled"></p>
<p><strong>@문자열 ‘abc’의 마지막의 ‘def’를 추가하자</strong></p>
<p>[1] ‘abcdef’라는 문자열을 새로 만들어 별도의 공간(@1003)에 저장</p>
<p>[2] 변수 영역에서 a라는 식별자를 검색(@1003)</p>
<p>[3] 앞서 저장한 문자열의 주소 @5005를 @1003의 공간에 대입</p>
<p>[4]@5004는 (자신의 주소를 저장하는 변수가 하나도 없다면) 가비지 콜렉터가 수거</p>
<p><strong>@500개의 변수를 생성해 모든 변수에 숫자 5를 할당</strong></p>
<p>→ 데이터 영역이 없다면, 주소 공간 2바이트<em>500개 + 데이터 공간 8바이트</em>500개</p>
<p>→ 데이터 영역이 있으므로 중복 참조해 주소 공간 2바이트<em>500개 + 데이터 공간 8바이트</em>1개</p>
<p>→ IF 변수 중 하나를 다른 숫자로 할당?</p>
<p>⇒ 다른 숫자용으로 데이터 영역 하나 차지, 주소 공간에 주소만 대입</p>
<p>→ → 따라서 변수 영역, 데이터 영역을 분리하면 중복된 데이터에 대한 처리 효율도 높아짐.</p>
<h1 id="04-기본형-데이터와-참조형-데이터">04. 기본형 데이터와 참조형 데이터</h1>
<h2 id="1-4-1-불변값">1-4-1 불변값</h2>
<ul>
<li>변수: 변수 영역 메모리 변경 가능</li>
<li>상수: 변수 영역 메모리 변경 불가</li>
<li>불변값: 데이터 영역 메모리 변경 불가. 변경은 새로 만드는 동작을 통해서만 이뤄짐.</li>
</ul>
<p><strong>@기본형 데이터는 모두 불변값.</strong></p>
<p>기본형 데이터 ← 숫자, 문자열, boolean, null, undefined, Symbol</p>
<pre><code class="language-jsx">var a = &quot;abc&quot;; // [1]
a = a + &quot;def&quot;; // [1]

var b = 5;     // [2]
var c = 5;     // [3]
b = 7;         // [4]</code></pre>
<p>[1] 변수 a에 문자열 ‘abc’를 할당 뒤에 ‘def’를 추가하면 새로운 ‘abcdef’를 만들어 그 주소를 변수 a에 저장</p>
<p>[2] 데이터 영역에서 5를 찾고, 없으면 그제서야 데이터 공간을 하나 만들어 저장, 해당 데이터 공간의 주소를 b에 저장</p>
<p>[3] 이미 만들어둔 데이터 공간의 주소를 c에 저장</p>
<p>[4] 기존에 저장해둔 7의 데이터 공간의 주소를 찾아서 있으면 재활용, 없으면 새로 만들어서 b에 저장.</p>
<h2 id="1-4-2-가변값">1-4-2 가변값</h2>
<ul>
<li>참조형 데이터의 기본적인 성질은 가변값.<ul>
<li>이때 가변은 참조형 데이터 자체를 변경할 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립</li>
</ul>
</li>
<li>객체의 변수 영역이 별도로 존재</li>
<li>설정에 따라 변경 불가능하게 할 수 있고(Object.defineProperty 등), 불변값으로 활용 가능</li>
</ul>
<pre><code class="language-jsx">var obj1 = {
  a: 1,
  b: &#39;bbb&#39;
};</code></pre>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/d6a743ba-e1b7-4d72-8334-9ef132491af3/Untitled.png" alt="Untitled"></p>
<p>[1] 변수 영역 빈 공간 @1002를 확보, 그 주소의 이름을 obj로 지정</p>
<p>[2] 데이터 → 여러 개의 프로퍼티로 이뤄진 데이터 그룹
해당 그룹 내의 프로퍼티들을 저장 위해 별도의 변수 영역을 마련, 그 영역의 주소를 @5001에 저장.</p>
<p>[3] @7103 및 @7104에 각각 a와 b라는 프로퍼티 이름을 지정</p>
<p>[4] 데이터 영역에서 숫자 1 검색, 검색 결과가 없으므로 임의로 @5003에 저장, 이 주소를 @7103에 저장. 문자열 ‘bbb’ 역시 임의로 @5004에 저장, 이 주소를 @7104에 저장</p>
<p><strong>@참조형 데이터의 프로퍼티 재할당</strong></p>
<pre><code class="language-jsx">var obj1 = {
    a: 1,
    b: &#39;bbb&#39;,
};

obj1.a = 2;</code></pre>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/8e71900c-c1ce-40b7-8fb7-d795f5495b76/Untitled.png" alt="Untitled"></p>
<p>[1] 데이터 영역에서 숫자 2 검색</p>
<p>[2] 검색 결과가 없음, 빈 공간인 @5005에 저장, 이 주소를 @7103에 저장</p>
<p>[3] 이 주소를 @7103에 저장. @7104에 이름 arr를 지정
→ 즉 새로운 객체가 아닌 기존의 객체 내부의 값만 바뀐 것!</p>
<p><strong>@중첩된 참조현 데이터(객체)의 프로퍼티 할당</strong></p>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/677fbe23-7d5a-496c-bc69-e6cae925315c/Untitled.png" alt="Untitled"></p>
<p>@1002 → @5001 → (@7103 ~ ?) → @7104 → @5003 → (@8104 ~ ?) → @8105 → @5004 → 4 반환</p>
<p><strong>@여기서 재할당 명령을 아래와 같이 내리면?</strong></p>
<pre><code class="language-jsx">obj.arr = &#39;str&#39;;</code></pre>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/5aee889a-71bd-48de-8a3d-4a32b0a07b43/Untitled.png" alt="Untitled"></p>
<p>@5003은 더 이상 자신의 주소를 참조하는 변수가 하나도 없게 됨
→ GC 수거 대상이 됨.</p>
<ul>
<li>참조 카운트: 어떤 데이터에 대해 자신의 주소를 참조하는 변수의 개수</li>
<li>가비지 컬렉터: 런타임 환경에 따라 특정 시점이나 메모리 사용량이 포화 상태에 임박할 때마다 자동으로 수거 대상들을 수거(collecting)<ul>
<li>수거된 메모리는 다시 새로운 값을 할당할 수 있는 빈 공간이 됨</li>
</ul>
</li>
<li>참조 카운트가 0인 메모리 주소는 가비지 컬렉터의 수거 대상이 됨.</li>
</ul>
<h2 id="1-4-3-변수-복사-비교">1-4-3 변수 복사 비교</h2>
<p>기본형과 참조형의 복사 과정은 동일, 데이터 할당 과정에서 이미 차이 발생, 변수 복사 이후의 동작에도 큰 차이 발생</p>
<pre><code class="language-jsx">var a = 10;
var b = a;

var obj1 = {c:10, d:&#39;ddd&#39;};
var obj2 = &#39;obj1&#39;;</code></pre>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/2bd214b8-c6d3-48c3-9d70-7e9fcb79814a/Untitled.png" alt="Untitled"></p>
<p><strong>@기본형 데이터</strong></p>
<p>@1002 확보, 식별자=b로 지정</p>
<p>식별자 a를 검색해서 @1001에 저장된 값인 @5001을 들고 좀 전에 확보해둔 @1002에 값으로 대입</p>
<p><strong>@참조형 데이터</strong></p>
<p>@1004 확보, 식별자=obj2로 지정</p>
<p>식별자 obj1을 검색해(@1003) 그 값인 @5002를 들고 @1004에 값으로 대입</p>
<p><strong>@변수 복사 이후 값 변경 결과 비교 - 객체의 프로퍼티 변경 시</strong></p>
<pre><code class="language-jsx">var a = 10;
var b = a;
var obj1 = {c:10, d:&#39;ddd&#39;};
var obj2 = obj1;

b = 15;
obj2.c = 20;</code></pre>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/d0997538-ec9c-4c20-a593-6ac4dfd30e38/Untitled.png" alt="Untitled"></p>
<ul>
<li><p>기본형: 변수 a와 b는 서로 다른 주소를 바라보게 됨</p>
</li>
<li><p>참조형: 변수 obj1와 obj2는 여전히 같은 객체를 바라보고 있는 상태.</p>
</li>
<li><p>결국 코드로 표현하면,</p>
<pre><code class="language-jsx">  a !== b
  obj1 === obj2</code></pre>
</li>
</ul>
<p><strong>@변수 복사 이후 값 변경 결과 비교 (2) - 객체 자체를 변경했을 때</strong></p>
<pre><code class="language-jsx">var a = 10;
var b = a;
var obj1 = { c:10, d:&quot;ddd&quot;};
var obj2 = obj1;

b = 15;
obj2 = { c:20, d:&quot;ddd&quot;};</code></pre>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/cc84f5b1-9134-42d8-aaf8-1bbf72de5140/3e9339f0-6f85-402b-aa04-0e8888dbd0ac/Untitled.png" alt="Untitled"></p>
<ul>
<li>obj2에도 새로운 객체를 할당해서 값을 직접 변경.</li>
<li>참조형 데이터가 가변값이라고 설명할 때의 가변은 참조형 데이터 자체를 변경할 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립.</li>
</ul>
<h1 id="05-불변-객체">05. 불변 객체</h1>
<h3 id="1-5-1-불변-객체를-만드는-간단한-방법">1-5-1 불변 객체를 만드는 간단한 방법</h3>
<p>불변 객체 immutable object</p>
<ul>
<li>데이터 자체를 변경하고자 하면 기본형 데이터와 마찬가지로 기존 데이터는 변하지 않음</li>
<li>내부 프로퍼티를 변경할 필요가 있을 때마다 매번 새로운 객체를 만들어 재할당하기로 규칙을 정하거나 자동으로 새로운 객체를 만드는 도구를 활용</li>
</ul>
<p><strong>@불변 객체가 필요한 경우</strong></p>
<p>아래 예시에서 정보가 바뀐 시점에 알림을 보내야 함</p>
<p>바뀌기 전의 정보와 후의 정보의 차이를 가시적으로 보여줘야 하는 등의 기능을 구현해야 함</p>
<p>→ 변경 전, 후에 서로 다른 객체를 바라보게 만들어야 함.</p>
<pre><code class="language-jsx">var user = {
    name: &quot;Jaenam&quot;,
    gender: &#39;male&#39;,
};

var changeName = function (user, newName) {
    var newUser = user;
    newUser.name = newName;
    return newUser;
};

var user2 = changeName(user, &#39;Jung&#39;);

if (user !== user2){
    console.log(&quot;유저 정보가 변경되었습니다.&quot;);
}

console.log(user.name, user2.name); // Jung Jung
console.log(user === user2);  // true</code></pre>
<p>→ 위 코드를 고친 코드는 아래.</p>
<pre><code class="language-jsx">var user = {
    name: &quot;Jaenam&quot;,
    gender: &#39;male&#39;,
};

var changeName = function (user, newName) {
    return {
        name: newName,
        gender: user.gender
    };
};

var user2 = changeName(user, &#39;Jung&#39;);

if (user !== user2){
    console.log(&quot;유저 정보가 변경되었습니다.&quot;);
}

console.log(user.name, user2.name); // Jaenam Jung
console.log(user === user2);  // false</code></pre>
<p>→ 보다 효율적으로 바꾼 코드</p>
<pre><code class="language-jsx">var copyObject = function (target) {
    var result = {};
    for (var prop in target) {
        result[prop] = target[prop];
    }
    return result;
};

var user = {
    name: &quot;Jaenam&quot;,
    gender: &#39;male&#39;,
};

var user2 = copyObject(user);
user2.name = &quot;Jung&quot;

if (user !== user2){
    console.log(&quot;유저 정보가 변경되었습니다.&quot;);
}

console.log(user.name, user2.name); // Jaenam Jung
console.log(user === user2);  // false</code></pre>
<p>모두가 규칙을 따르지 않고는 프로퍼티 변경을 할 수 없게끔 시스템적으로 제약을 거는 편이 안전</p>
<p>→ 실제: immutable.js, baobab.js 등의 라이브러리 활용</p>
<h3 id="1-5-2-얕은-복사와-깊은-복사">1-5-2 얕은 복사와 깊은 복사</h3>
<ul>
<li>얕은 복사 = shallow copy<ul>
<li>바로 아래 단계의 값만 복사</li>
</ul>
</li>
<li>깊은 복사 = deep copy<ul>
<li>내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법</li>
</ul>
</li>
</ul>
<p><strong>@객체의 깊은 복사를 수행하는 범용 함수</strong></p>
<pre><code class="language-jsx">var copyObjectDeep = function(target) {
    var result = {};
    if (typeof target === &quot;object&quot; &amp;&amp; target !== null) {
        for (var prop in target) {
            result[prop] = copyObjectDeep(target[prop]);
        }
    } else {
        result = target;
    }

    return result;
};</code></pre>
<p><strong>@다른 방법</strong></p>
<ul>
<li>hasOwnProperty 메서드를 활용해 프로토타입 체이닝, 상속된 프로퍼티를 복사하지 않게끔 할 수도 있음</li>
<li>JSON 활용<ul>
<li>JSON 문법으로 표현된 문자열로 전환, 다시 JSON 객체로 바꿈</li>
<li>CF) 메서드(함수)나 숨겨진 프로퍼티(proto, getter/setter 등) JSON으로 변경 불가한 프로퍼티들은 모두 무시</li>
<li>httpRequest로 받은 데이터를 저장한 객체를 복사할 때 등 순수한 정보만 다룰 때 활용하기 좋은 방법</li>
</ul>
</li>
</ul>
<h1 id="06-undefined와-null">06. undefined와 null</h1>
<h3 id="undefined">undefined</h3>
<ul>
<li><p>사용자가 명시적으로 지정 가능</p>
<p>  접근 시 
  → 이때의 프로퍼티나 배열의 요소는 고유의 키값(프로퍼티 이름)이 실존, 따라서 순회의 대상이 될 수 있음.</p>
</li>
<li><p>값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여하기도 함</p>
<p>  접근 시 
  → 이때의 해당 프로퍼티 내지 배열의 키값 자체가 존재하지 않음</p>
</li>
</ul>
<p>[1] 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때</p>
<p>[2] 객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때</p>
<p>[3] return 문이 없거나 호출되지 않는 함수의 실행 결과</p>
<pre><code class="language-jsx">// 자동으로 undefined를 부여하는 경우

var a;
console.log(a); // undefined, 1 - 값 대입하지 않은 변수

var obj = {a:1};
console.log(obj.a); // 1
console.log(obj.b); // undefined, 2 - 존재하지 않는 프로퍼티에 접근
console.log(b);     // c.f) Reference error - b is not defined

var func = function(){};
var c = func();     // undefined, 3 - return 값이 없으면 undefined를 반환한 것으로 간주
console.log(c);     // undefined

// undefined와 배열
var arr1 = [];
arr1.length = 3;
console.log(arr1); // [empty * 3]

var arr2 = new Array(3);
console.log(arr2); // [empty * 3]

var arr3 = [undefined, undefined, undefined];
console.log(arr3); // [undefined * 3]

// 빈 요소와 배열의 순회

var arr1 = [undefined, 1];
var arr2 = [];
arr2[1] = 1;

arr1.forEach(function(v, i){console.log(v,i);}); // undefined 0 / 1 1 
arr2.forEach(function(v, i){console.log(v,i);}); // 1 1 

arr1.map(function(v, i){return v+i;}); // [NaN, 2]
arr2.map(function(v, i){return v+i;}); // [empty, 2]

arr1.filter(function(v, i){return !v;}); // [undefined]
arr2.filter(function(v, i){return !v;}); // []

arr1.reduce(function(p, c, i){return p + c + i;}); // undefined011
arr2.reduce(function(p, c, i){return p + c + i;}); // 11</code></pre>
<p>arr1은 배열의 모든 요소를 순회해 결과를 출력</p>
<p>arr2은 각 메서드들이 비어 있는 요소에 대해서는 어떠한 처리도 하지 않고 건너뜀</p>
<p><strong>@배열도 객체</strong></p>
<p>→ 존재하지 않는 프로퍼티에 대해서는 순회할 수 없음</p>
<p>→ 객체와 마찬가지로 특정 인덱스에 값을 지정할 때 비로소 빈 공간을 확보, 인덱스를 이름으로 지정하고 데이터의 주솟값을 저장하는 등의 동작을 함.</p>
<p>→ 사용자가 undefined를 명시적으로 부여한 경우 vs 비어있는 요소에 접근할 때 반환되는 경우 차이를 알 수 있음.</p>
<h2 id="null">null</h2>
<ul>
<li><p>사용 이유: undefined의 혼동을 피하기 위해, “비어있음”을 명시적으로 나타내고 싶을 때 사용</p>
<p>  → undefined의 의미 = 값을 대입하지 않은 변수에 접근하고자 할 때 자바스크립트 엔진이 반환해주는 값으로만 존재할 수 있음</p>
</li>
<li><p>주의: typeof null == object</p>
</li>
</ul>
<pre><code class="language-jsx">// undefined vs null 비교
var n = null;
console.log(typeof n);       // object

console.log(n == undefined); // true
console.log(n == null);      // true

console.log(n === undefined); // false
console.log(n === null)       // true</code></pre>
<ul>
<li><p>동등 연산자(equality operator) vs 일치 연산자(identity operator)</p>
<p>  <strong>동등 연산자</strong>(==)는 두 피연산자의 값이 서로 같으면 참(true)을 반환합니다. 이때 두 피연산자의 타입이 서로 다르면, 비교를 위해 강제로 타입을 같게 변환합니다. 
  하지만 <strong>일치 연산자</strong>(===)는 타입의 변환 없이 두 피연산자의 값이 같고, 타입도 같아야만 참(true)을 반환합니다.</p>
<p>  &lt;예시&gt;</p>
<pre><code class="language-jsx">  1 == true     =&gt; true
  true == true  =&gt; true
  1 === true    =&gt; false
  true === true =&gt; true</code></pre>
</li>
</ul>
<h1 id="07-정리">07. 정리</h1>
<ul>
<li>기본형 = 불변값</li>
<li>참조형 = 가변값</li>
<li>변수 = 변경 가능한 데이터가 담길 수 있는 공간</li>
<li>식별자 = 그 변수의 이름</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 맛보기 😋]]></title>
            <link>https://velog.io/@tera_geniel/React-%EB%A7%9B%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@tera_geniel/React-%EB%A7%9B%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 21 Nov 2023 15:04:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/tera_geniel/post/b72e591f-45fd-44ae-a409-d2779ae2b94a/image.png" alt=""></p>
<h1 id="목표">목표</h1>
<p>(0) 환경 설정 및 base code 짜기</p>
<p>(1) hook 사용해보기(useState)</p>
<p>(2) components 사이에 data sharing하는 작업</p>
<p>을 해본다.</p>
<p>각 (0), (1), (2) 목표는 각 실습에 따라 이뤄진다.</p>
<h1 id="react-환경-설정하기">React 환경 설정하기</h1>
<p><a href="https://ffoorreeuunn.tistory.com/199">Nodejs 설치하기 &amp; npm 설치하기 &amp; npx 설치하기</a></p>
<h2 id="nodejs-다운로드">node.js 다운로드</h2>
<p><a href="https://nodejs.org/en">https://nodejs.org/en</a> 에서 LTS 버전 다운로드 후 설치(디폴트 설정)</p>
<h4 id="cmd-창에서-node--v-입력-후-버전-확인">cmd 창에서 <code>node -v</code> 입력 후 버전 확인</h4>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/46ce7c01-852b-4423-aa94-fe4d703fe8b3/image.png" alt=""></p>
<h4 id="cmd-창에서-npm--v-입력-후-버전-확인">cmd 창에서 <code>npm -v</code> 입력 후 버전 확인</h4>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/e0557089-3f89-40fd-9495-7a59f2fce79a/image.png" alt=""></p>
<h2 id="npx-install">npx install</h2>
<p>cmd 창에서 아래 입력</p>
<pre><code>npm install -g create-react-app</code></pre><h2 id="react-프로젝트-만들기">react 프로젝트 만들기</h2>
<pre><code>npx create-react-app yum</code></pre><p>or <a href="http://playcode.io/react">playcode.io/react</a> 에서 수정하기</p>
<h1 id="base-code-복붙">BASE CODE 복붙</h1>
<p>App.js</p>
<pre><code class="language-jsx">import logo from &quot;./logo.svg&quot;;
import &quot;./App.css&quot;;
import React, { useState } from &quot;react&quot;;

function Counter() {
  const counter = 0;
  return (
    &lt;div className=&quot;Counter-Wrapper&quot;&gt;
      &lt;div&gt;COUNTER: {counter}&lt;/div&gt;
      &lt;button&gt;+1&lt;/button&gt;
      &lt;button&gt;-1&lt;/button&gt;
    &lt;/div&gt;
  );
}

function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;Counter /&gt;
    &lt;/div&gt;
  );
}

export default App;</code></pre>
<p>App.css</p>
<pre><code class="language-css">.App {
  text-align: center;
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.Counter-Wrapper {
  margin-bottom: 15px;
}

.Counter-Wrapper &gt; div {
  border: 1px solid white;
  padding: 5px;
  border-radius: 5px;
}

.Counter-Wrapper &gt; button {
  margin-top: 15px;
  margin-left: 10px;
  min-height: calc(10px + 3vmin);
  min-width: calc(10px + 5vmin);
  font-size: calc(5px + 2vmin);
}</code></pre>
<h1 id="1-hook-사용해보기usestate-useeffect">(1) hook 사용해보기(useState, useEffect)</h1>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/c9a53002-1f93-4b6c-8dca-8d64b72fa599/image.png" alt=""></p>
<p>COUNTER가 증가하고, 감소하도록 함수 Counter를 작성해 준다.</p>
<h2 id="react-이론-설명">React 이론 설명</h2>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/54c7d548-b7cc-4aa5-9b89-c119a7bb3aae/image.png" alt="">
⏲️ 리액트에서 데이터가 변해 웹 브라우저에서 실제 DOM에 업데이트할 때 밟는 3가지 절차 ⏲️</p>
<ol>
<li>데이터를 업데이트하면 전체 UI에서 Virtual DOM에 re-render</li>
<li>이전 Virtual DOM에 있던 내용과 현재 내용을 비교</li>
<li>바뀐 부분만 실제 DOM에 적용함</li>
</ol>
<p>그래서 아까 [그림 1-6]의 바뀐 DOM 트리가 그 VIrtual DOM 트리로 확인하는 것임.</p>
<ul>
<li><p><code>useState</code>: state와 state을 변경할 수 있는 함수를 제공해준다. state가 있어야 컴포넌트에 변경된 값이 감지되고, 리렌더됨(reconciliation 과정).</p>
<ul>
<li><p>예시 코드</p>
<pre><code class="language-jsx">  // 예시
  import React, { useState } from &quot;react&quot;;

  function App() {
      const [counter, setCounter] = useState(0);
    return (
      &lt;div className=&quot;App&quot;&gt;
              {counter}
      &lt;/div&gt;
    );
  }</code></pre>
</li>
</ul>
</li>
<li><p><code>onClick</code>: button 과 같은 페이지 요소에서 event 발생 시 호출되는 함수 중 하나. 한마디로, button 클릭 시 호출되는 이벤트 함수다.</p>
<ul>
<li><p>예시 코드</p>
<pre><code class="language-jsx">  &lt;button onClick={()=&gt;{console.log(&quot;+1 clicked&quot;)}&gt;+1&lt;/button&gt;
  // 여기서 ()=&gt;{}는 화살표 함수(JS 문법)다. onClick 이벤트 발생 시 호출되는 함수.</code></pre>
</li>
</ul>
</li>
</ul>
<h1 id="2-components-사이에-data-sharing하는-작업을-해본다">(2) components 사이에 data sharing하는 작업을 해본다.</h1>
<p>위 사진과 같이 +1 혹은 -1 버튼 클릭 시, 해당 결과값만큼 특정 문자열이 반복되도록 한다.</p>
<ul>
<li><p>자식 컴포넌트에서 데이터 변경 작업을 해도 다른 자식 컴포넌트가 해당 사실을 알도록 해야 한다.</p>
</li>
<li><p><code>props</code> 전달: 부모 컴포넌트에서 자식 컴포넌트로 전달할 때 인자로 전달한다. 이때 인자를 의미. 그런데 인자로 함수도 전달할 수 있다!</p>
<ul>
<li><p>예시 코드</p>
<pre><code class="language-jsx">  function Counter({ counter, setCounter }) {
      // ...
  }

  // 혹은
  function Counter(props) {
      const counter = props.counter;
      const setCounter = props.setCounter;
      // ...
  }

  function App() {
    const [counter, setCounter] = useState(0);
    return (
      &lt;div className=&quot;App&quot;&gt;
        &lt;Counter counter={counter} setCounter={setCounter} /&gt;
      &lt;/div&gt;
    );
  }</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="react-설명">React 설명</h2>
<p>Often you’ll need components to share data and always update together.</p>
<p>To make both MyButton components display the same count and update together, you need to move the state from the individual buttons “upwards” to the closest component containing all of them.
<img src="https://velog.velcdn.com/images/tera_geniel/post/4850a742-bdc3-4277-aaff-24e4c41011a2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[임베디드 시스템 하드웨어 개요]]></title>
            <link>https://velog.io/@tera_geniel/%EC%9E%84%EB%B2%A0%EB%94%94%EB%93%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@tera_geniel/%EC%9E%84%EB%B2%A0%EB%94%94%EB%93%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Wed, 26 Apr 2023 10:03:34 GMT</pubDate>
            <description><![CDATA[<h1 id="01-임베디드-시스템의-구성">01. 임베디드 시스템의 구성</h1>
<ul>
<li><p>PC
=&gt; 애플리케이션에 따라 다양한 일을 함</p>
</li>
<li><p>일반적인 임베디드 시스템 
=&gt; 기능이 정해진 목적에 따라 구성되어 전용 동작을 함 -&gt; 동작 중에 기능이 변화하는 일이 없음.
=&gt; <strong>실시간성</strong>(시간 제약)이 있음 </p>
</li>
<li><blockquote>
<p>동작 환경, 제어 대상에 맞추어 동작하도록 설계해야 함</p>
</blockquote>
</li>
<li><blockquote>
<p>하드 리얼타임: 시간 제약을 만족시키지 않는 경우 중대한 사고</p>
</blockquote>
</li>
<li><blockquote>
<p>소프트 리얼타임: 소프트웨어 업데이트로 처리 속도가 향상되는 때도 있는 실시간성</p>
</blockquote>
</li>
</ul>
<h1 id="02-임베디드-마이크로컴퓨터의-구성">02. 임베디드 마이크로컴퓨터의 구성</h1>
<h2 id="01-하드웨어의-종류">01. 하드웨어의 종류</h2>
<ul>
<li>CPU : 인간의 두뇌에 해당</li>
<li>메모리: 기억하기 위한 장치(ROM, RAM)</li>
<li>주변장치(Peripheral): 입출력을 담당하는 장치
(Timer, DMA Controller, Interrupt Controller, Serial Controller, A/D Controller, PWM Controller)</li>
</ul>
<h2 id="02-cpu와-마이크로컴퓨터">02. CPU와 마이크로컴퓨터</h2>
<p>CPU: 계산에 필요한 부품을 하나의 칩으로 합쳐 놓은 것
마이크로컴퓨터: CPU에서 다양한 기능을 탑재한 것.</p>
<p>주변장치: 마이크로 컴퓨터로 실현할 수 있는 기능을 늘리기 위해 추가됨.
즉, 주변에서 접속되는 하드웨어. 마이크로컴퓨터의 CPU로부터 제어됨.</p>
<p>버스: CPU &lt;-&gt; 주변장치는 버스(신호선)에 접속됨.
CPU로부터의 지시는 버스를 경유해 전달, 주변장치로부터 출력, 주변장치로부터의 입력은 버스를 경유해 CPU에 전달.</p>
<blockquote>
<p>마이크로컴퓨터: 마이크로프로세서 중 1개의 칩 내에 CPU, 일정 용량의 메모리(ROM, RAM 등)와 입출력 제어 인터페이스 회로까지 내장한 것</p>
</blockquote>
<h2 id="03-메모리">03. 메모리</h2>
<p>LED ON/OFF는 주변장치만으로 구현 가능
BUT 100ms마다 LED ON/OFF 되는 장치는 CPU 없이 구현 불가
BUT 절차로 기술한 &#39;프로그램&#39;은 CPU에 전달해야 하고, 이때 &#39;보관&#39;에 메모리가 필요</p>
<blockquote>
<p>ROM: 프로그램 자체를 보관하여 CPU에 절차를 전달하기 위해 사용(읽기 전용 메모리)
RAM: CPU가 ROM으로부터 읽어 들인 절차를 실행할 때 이용</p>
</blockquote>
<h3 id="rom">ROM</h3>
<p>읽기 전용 메모리</p>
<p>프로그램을 보관해서 CPU에서 읽기 요구가 있을 때 프로그램을 읽고 CPU에 프로그램 전달하는 역할을 담당.</p>
<p>사람이 기록한 절차를 기억해서 그 절차에 따른 기능을 CPU에 실행시킴</p>
<h3 id="ram">RAM</h3>
<p>프로그램 실행 때 일시적으로 데이터의 보관이 필요할 경우에 사용
ROM은 읽기만이 가능한 메모리이므로 일시적인 보관 장소로는 이용할 수 없기 때문</p>
<p>시리얼 콘솔에 문자를 출력할 때 문자나 숫자는 CPU에서 생성되고, 이것들의 값을 시리얼로 전달하기 위해 RAM에 일시적으로 보관해둔다.</p>
<h2 id="04-메모리의-종류">04. 메모리의 종류</h2>
<p>ROM: 비휘발성 메모리(non-volatile memory), 전원이 꺼져도 내용이 지워지지 않음
MASK ROM, PROGRAMMABLE ROM</p>
<p>RAM: 휘발성 메모리(volatile memory), 전원이 꺼지면 내용도 지워진다.
DRAM(dynamic ram), SRAM(static ram)</p>
<h2 id="05-버스의-구성">05. 버스의 구성</h2>
<p>메인 버스 -&gt; 고속 주변 장치(메모리)는 CPU로부터 고속으로 제어하길 바람, 메인 버스로 접속
로컬 버스 -&gt; 저속으로 동작하는 주변장치는 브리지를 경유해서 로컬 버스로 접속</p>
<h2 id="06-메인-버스의-용도">06. 메인 버스의 용도</h2>
<p>CPU와 메모리, 또는 주변장치에 접속하기 위한 신호선</p>
<ul>
<li><strong>주소 버스</strong>: 메모리나 주변장치에 접근할 특정 위치를 나타내기 위해 이용되는 신호선</li>
<li><strong>데이터 버스</strong>: 메모리나 주변장치로부터 데이터를 읽어 들이기 위한 신호선. 데이터 버스는 쌍방향으로 되어 있으며 CPU로부터의 출력, CPU로의 입력이 가능하도록 되어 있음.</li>
<li><strong>컨트롤 버스</strong>: 메모리나 주변장치를 제어하기 위한 신호선. 데이터를 r/w 타이밍, 주변장치와 CPU 간의 제어에 필요한 신호를 전달 위해 사용함</li>
</ul>
<p>버스 신호는 클럭(타이밍 신호)에 동기화되어 CPU로부터의 지시 대상이 되는 메모리, 주변장치에 지시를 전달하는 데 사용됨.</p>
<h3 id="예시데이터-rw">예시(데이터 R/W)</h3>
<p>버스는 CPU에서 지정해서 동작됨.</p>
<h4 id="ram에서-데이터-읽어-낼-때">RAM에서 데이터 읽어 낼 때</h4>
<p>RAM을 선택하는 컨트롤 버스의 칩 셀렉트 신호, 읽기 가능 신호, 주소 버스에 읽어 낼 주소
-&gt; 클럭이 시작할 타이밍에 지정</p>
<p>RAM 해당 번지의 데이터가 데이터 버스에 출력됨.
이 출력된 데이터를 CPU가 읽어서 레지스터에 보관함.</p>
<h4 id="ram에-데이터를-기록할-때">RAM에 데이터를 기록할 때</h4>
<p>컨트롤 버스의 칩 셀렉트 신호, 쓰기 가능 신호, 주소 버스에 기록하고자 하는 번지, 데이터 버스에 기록할 데이터
-&gt; 클럭이 시작할 타이밍에 지정</p>
<p>쓰기 가능한 동안 RAM의 해당 번지에 기록함.</p>
<h2 id="07-로컬-버스">07. 로컬 버스</h2>
<p>메인 버스와 달리 메인 버스의 클럭 속도보다도 저속으로 동작하는 주변 장치를 제어하는 신호선으로 되어 있음.
로컬 버스를 사용해서 많은 주변장치에 접속 가능.</p>
<h3 id="브리지">브리지</h3>
<p>메인 버스와 로컬 버스를 연결하는 컨트롤러
고속으로 동작하는 메인 버스와 저속으로 동작하는 로컬 버스와의 속도차를 통제해 주는 하드웨어.</p>
<p>FIFO(First In First Out)을 구현한 하드웨어 등에서 저속의 로컬 버스로부터의 데이터를 관리하고, 고속의 메인 버스 타이밍에 맞춰 데이터를 송수신해 주는 하드웨어.</p>
<h3 id="uart">UART</h3>
<p>Universal Asynchronous Receiver/Transmitter</p>
<p>동기식 직렬 신호를 병렬 신호로, 역으로 병렬을 직렬로 변환하는 하드웨어, 로컬 버스에 접속됨.</p>
<h4 id="cpu--uart">CPU-&gt; UART</h4>
<p>CPU에서 8-16비트의 폭으로 데이터가 병렬 전송됨. 
복수의 데이터 신호 -&gt; 직렬의 데이터 신호로 변환해 직렬 신호로 송신</p>
<h4 id="uart---cpu">UART -&gt; CPU</h4>
<p>직렬 신호를 수신받으면 복수의 데이터 신호가 될 때까지 데이터를 담아 놓고,
CPU로 데이터를 보내기 위해 병렬로 데이터를 변환 후에 송신</p>
<h4 id="uart---uart">UART &lt;-&gt; UART</h4>
<p>UART끼리 통신할 때: 비동기 직렬 통신.</p>
<blockquote>
<p>비동기 방식 = 지금부터 데이터를 보낸다, 이것으로 데이터가 끝났다라는 신호를 실제 데이터 사이에 보내서 데이터를 송수신하는 장치끼리 상호 인식을 하며 통신하는 방식.</p>
</blockquote>
<h3 id="i2c">I2C</h3>
<p>시리얼 클록(Serial Clock, SCL)과 양방향 시리얼 데이터(Serial DAta. SDA)의 2개의 신호선을 사용해 통신하는 동기식 직렬 통신.</p>
<p>마스터 장치 &lt;-&gt; 슬레이브 장치</p>
<p>마스터 장치는 개별로 정해진 슬레이브의 주소를 지정해 복수의 슬레이브 장치에 접속할 수 있음.</p>
<h3 id="spi">SPI</h3>
<p>시리얼로 제어하는 버스
SCK = 시리얼 클록
SDI = 시리얼 데이터 인
SDO = 시리얼 데이터 아웃</p>
<p>세가지 신호선으로 통신하는 동기식 직렬 통신</p>
<p>버스에서 제어 버스인 슬레이브 셀렉트(SS)를 이용해 마스터에서 (복수의) 슬레이브 디바이스를 선택해 통신할 수 있다.</p>
<p>I2C보다 고속으로 통신이 가능하다. BUT 많은 신호선이 필요함.</p>
<p>SPI는 플래시 메모리 같은 스토리지 디바이스, CPU 간의 통신 등에 많이 사용됨.</p>
<h2 id="08-주변-장치">08. 주변 장치</h2>
<h3 id="dmadirect-memory-access-컨트롤러">DMA(Direct Memory Access) 컨트롤러</h3>
<p>직접 메모리에 엑세스하는 주변장치.
CPU가 메모리의 데이터 읽쓰를 실행.</p>
<p>-&gt; PIO(Programmed I/O)라고 부름</p>
<p>PIO 방식으로 대량의 데이터를 읽고 쓰면 CPU는 다른 처리를 할 수 없게 됨.
DMA -&gt; PIO와 다르게, CPU를 사용X, 메모리의 데이터를 읽쓰 주변장치.
-&gt; 즉, 메모리를 읽쓰 도중에 다른 처리를 할 수 있음.</p>
<h4 id="버스-아비터">버스 아비터</h4>
<p>bus arbiter는 버스를 사용하기 위한 버스 중재를 실시(각 주변장치 사이, 혹은 CPU로부터의 데이터가 충돌 안 나게 함).
DMA 실행 시에는 버스 아비터가 버스의 중재를 실시해 데이터가 충돌하지 않도록 제어함</p>
<p>데이터를 메모리에 전송하는 동안 버스를 점유, 메모리 액세스가 느려지는 일도 있음
DMA 컨트롤러의 전송 모드에 메모리 액세스에 영향이 발생하지 않도록 하는 제어 방법도 있으므로 설정을 파악해 두어야 한다.</p>
<h3 id="타이머">타이머</h3>
<p>임베디드 시스템에서 반드시 이용하는 주변 장치
프로그램에서 주변장치를 주기적으로 감시, 주기적으로 데이터 출력 등 시간 관련 처리를 위해서 필수적인 주변장치</p>
<p>카운터라고 부르는 레지스터에 주기 시간을 설정함.
주기 시간 경과 이후 처리 중간에 끼어들어 그 사실을 CPU에 통지.
CPU는 인터럽트를 받아들여 해당 프로그램을 동작시키면서 주기적인 처리를 실현</p>
<h3 id="rtcreal-time-clock">RTC(Real Time Clock)</h3>
<p>시간을 관리하기 위한 주변장치
매초 시간이 갱신됨</p>
<p>CPU가 재개한 때에 정확한 시간을 알 수 있도록 RTC로 시간을 관리</p>
<h3 id="gpiogeneral-purpose-inputoutput">GPIO(General Purpose Input/Output)</h3>
<p>CPU가 외부로부터의 입력 및 출력을 범용으로 입출력할 수 있는 포트
실제로 CPU에 연결되어 있는 포트.</p>
<p>CPU의 설정을 통해 활성화/비활성화 가능</p>
<p>외부의 주변장치로부터의 인터럽트 신호에 사용하는 등 범용적으로 입력/출력에 사용 가능</p>
<h2 id="09-주변장치의-제어-방식">09. 주변장치의 제어 방식</h2>
<p>레지스터(제어용 메모리)를 사용해 CPU로부터 제어
각 주변장치에 따라 비트 위치, 비트 폭 등의 구성이 다름.
&lt;공통&gt; CPU가 제어할 때 레지스터로의 쓰기/읽기를 통해 제어
&lt;차이&gt; 각 주변장치에 따라 비트위치, 비트 폭 등의 구성이 다름</p>
<h3 id="메모리-맵드-io">메모리 맵드 I/O</h3>
<p>ROM, RAM과 마찬가지로 주변장치의 레지스터도 메모리로 취급
CPU는 파일을 메모리에서 접근 가능하게 된다. 이를 통해 CPU는 read() 혹은 write()등의 System call을 사용하지 않고 메모리에 data를 읽고 쓰는 것처럼 사용 가능하다.</p>
<h3 id="io-맵드-io">I/O 맵드 I/O</h3>
<p>ROM, RAM은 메모리 공간으로 취급
메모리와 입출력의 주소 공간을 분리한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[6️⃣ SQL PS - 문제만]]></title>
            <link>https://velog.io/@tera_geniel/6-SQL-PS-%EB%AC%B8%EC%A0%9C%EB%A7%8C</link>
            <guid>https://velog.io/@tera_geniel/6-SQL-PS-%EB%AC%B8%EC%A0%9C%EB%A7%8C</guid>
            <pubDate>Sun, 12 Mar 2023 14:31:09 GMT</pubDate>
            <description><![CDATA[<h1 id="select">SELECT</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/133025">과일로 만든 아이스크림 고르기(lv 1)</a></p>
<h1 id="group-by">GROUP BY</h1>
<h2 id="예제-쿼리">예제 쿼리</h2>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/e699cab4-2a00-40d4-b046-a8d9b2df1983/image.png" alt=""></p>
<h3 id="예제-01">예제 01</h3>
<p>type 그룹화하여 name 갯수 조회 (컬럼 그룹화)</p>
<p><strong>결과</strong></p>
<table>
<thead>
<tr>
<th>type</th>
<th>cnt</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>2</td>
</tr>
<tr>
<td>4</td>
<td>1</td>
</tr>
</tbody></table>
<h3 id="예제-02">예제 02</h3>
<p>type 1 초과인, type 그룹화하여 name 갯수 조회</p>
<p><strong>결과</strong></p>
<table>
<thead>
<tr>
<th>type</th>
<th>cnt</th>
</tr>
</thead>
<tbody><tr>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>2</td>
</tr>
<tr>
<td>4</td>
<td>1</td>
</tr>
</tbody></table>
<h3 id="예제-03">예제 03</h3>
<p>type 그룹화하여 name 갯수를 가져온 후, 그 중에 갯수가 2개 이상인 데이터 조회 (조건 처리 후에 컬럼 그룹화 후에 조건 처리)</p>
<p><strong>결과</strong></p>
<table>
<thead>
<tr>
<th>type</th>
<th>cnt</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>2</td>
</tr>
</tbody></table>
<h3 id="예제-04">예제 04</h3>
<p>type 1 초과인, type 그룹화하여 name 갯수를 가져온 후, 그 중에 갯수가 2개 이상인 데이터를 type 내림차순 정렬로 조회 (내림차순 정렬)</p>
<p><strong>결과</strong></p>
<table>
<thead>
<tr>
<th>type</th>
<th>cnt</th>
</tr>
</thead>
<tbody><tr>
<td>3</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>2</td>
</tr>
</tbody></table>
<h2 id="문제">문제</h2>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/151137">자동차 종류 별 특정 옵션이 포함된 자동차 수 구하기</a></p>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/131116">식품분류별 가장 비싼 식품의 정보 조회하기</a></p>
<h1 id="join">JOIN</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/59045">보호소에서 중성화한 동물</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[5️⃣ SQL HONEY TIP]]></title>
            <link>https://velog.io/@tera_geniel/m8lgnmic</link>
            <guid>https://velog.io/@tera_geniel/m8lgnmic</guid>
            <pubDate>Mon, 20 Feb 2023 02:08:56 GMT</pubDate>
            <description><![CDATA[<h2 id="비교">비교</h2>
<p>=, <strong>&lt;&gt;</strong></p>
<h2 id="집계-함수">집계 함수</h2>
<p>SUM(price), AVG(price), COUNT(*), MAX(price), MIN(price)</p>
<h2 id="반올림">반올림</h2>
<p>round(price, 2)</p>
<h2 id="컬럼명-치환">컬럼명 치환</h2>
<p>select price as p</p>
<h2 id="중복-제거">중복 제거</h2>
<p>select distinct price</p>
<h2 id="정렬">정렬</h2>
<p>order by price asc</p>
<p>order by price desc</p>
<p>우선순위에 따라 ,로 배치</p>
<h2 id="와일드-카드">와일드 카드</h2>
<pre><code class="language-json">SELECT * FROM tb_product WHERE product_name LIKE CONCAT(&#39;%&#39;,&#39;a&#39;,&#39;%&#39;);</code></pre>
<h2 id="날짜-시간-문자열">날짜, 시간, 문자열</h2>
<p><a href="https://velog.io/@syh0397/SQL-%EB%82%A0%EC%A7%9C%EC%99%80-%EC%8B%9C%EA%B0%84-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%8B%A4%EB%A3%A8%EA%B8%B0">https://velog.io/@syh0397/SQL-날짜와-시간-문자열-다루기</a></p>
<pre><code class="language-sql">2. 예문)
   1.DATE_FORMAT(now(), &#39;%Y-%m-%d&#39;) // 2011-06-14
   2.DATE_FORMAT(now(), &#39;%Y-%M-%D&#39;) // 2011-June-14th
   3.DATE_FORMAT(now(), &#39;%H:%i:%s&#39;)  // 22:26:11  ( 24시간 표현 )
   4.DATE_FORMAT(now(), &#39;%h:%i:%s&#39;)  // 10:26:11 ( 12시간 표현 )</code></pre>
<h2 id="집합연산">집합연산</h2>
<p>합집합 - UNION</p>
<pre><code class="language-sql"># NULL 값 주는 경우
select id, NULL date
from T1
union
select id, data
from T2;</code></pre>
<p>차집합 - EXCEPT</p>
<p>교집합 - INTERSECT</p>
<h2 id="조인">조인</h2>
<h3 id="조건없이-조인카티전-프로덕트">조건없이 조인(카티전 프로덕트)</h3>
<pre><code class="language-sql">SELECT *
FROM CUSTOMER, ORDERS;

또는

SELECT 조회할컬럼
FROM 테이블1
JOIN 테이블2

또는

SELECT 조회할컬럼
FROM 테이블1
CROSS JOIN 테이블2</code></pre>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/5ddd8a14-1ce8-4b01-9c14-813702eaadb8/image.png" alt=""></p>
<h3 id="동등-조인">동등 조인</h3>
<h3 id="inner-조인">INNER 조인</h3>
<pre><code class="language-sql">SELECT Sales.*, Countries.Country 
FROM Sales 
JOIN Countries 
ON Sales.CountryID = Countries.ID</code></pre>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/f89f4ac4-d5de-44d4-9057-68ab7bb9efc1/image.png" alt=""></p>
<h3 id="외부-조인">외부 조인</h3>
<p>[FULL OUTER JOIN]</p>
<pre><code class="language-sql">SELECT *
FROM instructor
FULL OUTER JOIN teaches 
ON instructor.id = teaches.id</code></pre>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/b02a3d1d-0831-4362-8148-5c30198ae9d4/image.png" alt=""></p>
<p>[LEFT OUTER JOIN]</p>
<pre><code class="language-sql">SELECT *
FROM instructor
LEFT OUTER JOIN teaches 
ON instructor.id = teaches.id</code></pre>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/3ae7309e-6a8d-413c-b830-eff1c1160125/image.png" alt=""></p>
<p>[RIGHT OUTER JOIN]</p>
<pre><code class="language-sql">SELECT *
FROM instructor
RIGHT OUTER JOIN teaches 
ON instructor.id = teaches.id</code></pre>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/ad184bef-2ddb-4c9d-81d8-be0da03947a5/image.png" alt=""></p>
<h2 id="부속질의">부속질의</h2>
<p>IN, &gt; ALL, &lt; SOME, EXISTS</p>
<h2 id="상위-n개-행-반환">상위 n개 행 반환</h2>
<p>select top 2 price</p>
<p>from products;</p>
<p>&lt;예시&gt;</p>
<pre><code class="language-jsx">SELECT userID, addr, mDate 
FROM userTb1
WHERE addr LIKE &#39;%산&#39;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[4️⃣ SQL QUERY PS]]></title>
            <link>https://velog.io/@tera_geniel/4-SQL-QUERY-PS</link>
            <guid>https://velog.io/@tera_geniel/4-SQL-QUERY-PS</guid>
            <pubDate>Wed, 15 Feb 2023 01:17:47 GMT</pubDate>
            <description><![CDATA[<h1 id="select">SELECT</h1>
<p>과일로 만든 아이스크림 고르기(lv 1) -&gt; <a href="https://school.programmers.co.kr/learn/courses/30/lessons/133025">링크</a></p>
<pre><code class="language-sql">SELECT ROUND(AVG(DAILY_FEE), 0) AS AVERAGE_FEE
FROM CAR_RENTAL_COMPANY_CAR
WHERE CAR_TYPE=&#39;SUV&#39;;</code></pre>
<p>재구매가 일어난 상품과 회원 리스트 구하기 (lv 2) -&gt; <a href="https://school.programmers.co.kr/learn/courses/30/lessons/131536">링크</a></p>
<pre><code class="language-sql">SELECT DISTINCT O1.USER_ID AS USER_ID, O1.PRODUCT_ID AS PRODUCT_ID
FROM ONLINE_SALE O1, ONLINE_SALE O2
WHERE O1.USER_ID = O2.USER_ID AND O1.PRODUCT_ID = O2.PRODUCT_ID AND O1.SALES_DATE &lt;&gt; O2.SALES_DATE
    ORDER BY USER_ID ASC, PRODUCT_ID DESC;</code></pre>
<p>오프라인/온라인 판매 데이터 통합하기(lv 4) -&gt; <a href="https://school.programmers.co.kr/learn/courses/30/lessons/131537">링크</a></p>
<h1 id="group-by">GROUP BY</h1>
<p>조건에 맞는 도서와 저자 리스트 출력하기(lv 2) -&gt; <a href="https://school.programmers.co.kr/learn/courses/30/lessons/144854">링크</a>
있었는데요 없었습니다(lv 3) -&gt; <a href="https://school.programmers.co.kr/learn/courses/30/lessons/59043">링크</a>
보호소에서 중성화한 동물(lv 4) -&gt; <a href="https://school.programmers.co.kr/learn/courses/30/lessons/59045">링크</a></p>
<h1 id="join">JOIN</h1>
<p>조건에 맞는 도서와 저자 리스트 출력하기(lv 2) -&gt; <a href="https://school.programmers.co.kr/learn/courses/30/lessons/144854">링크</a>
있었는데요 없었습니다(lv 3) -&gt; <a href="https://school.programmers.co.kr/learn/courses/30/lessons/59043">링크</a>
보호소에서 중성화한 동물(lv 4) -&gt; <a href="https://school.programmers.co.kr/learn/courses/30/lessons/59045">링크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3️⃣ 조인의 종류]]></title>
            <link>https://velog.io/@tera_geniel/3-%EC%A1%B0%EC%9D%B8%EC%9D%98-%EC%A2%85%EB%A5%98</link>
            <guid>https://velog.io/@tera_geniel/3-%EC%A1%B0%EC%9D%B8%EC%9D%98-%EC%A2%85%EB%A5%98</guid>
            <pubDate>Wed, 15 Feb 2023 01:13:07 GMT</pubDate>
            <description><![CDATA[<p>조인: 하나의 테이블이 아닌 두개 이상의 테이블을 묶어서 하나의 결과물을 만드는 것</p>
<p>👌 참고: MongoDB에서는 조인(lookup 연산)을 되도록 사용하지 말아야 함. 성능이 떨어짐</p>
<h2 id="조인의-종류">조인의 종류</h2>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/25c1dee3-4a3e-4678-ac40-eba6fc9d94da/image.png" alt=""></p>
<h3 id="내부-조인inner-join">내부 조인(inner join)</h3>
<p>왼쪽 테이블과 오른쪽 테이블의 두 행이 모두 일치하는 행이 있는 부분만 표기</p>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/c4f377c0-a2f1-4075-a97e-114f39844e4a/image.png" alt=""></p>
<h3 id="왼쪽-조인left-outer-join">왼쪽 조인(left outer join)</h3>
<p>왼쪽 테이블의 모든 행이 결과 테이블에 표기</p>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/c1d5f52d-eb8c-41ec-82fa-e60d3508afb7/image.png" alt=""></p>
<h3 id="오른쪽-조인right-outer-join">오른쪽 조인(right outer join)</h3>
<p>오른쪽 테이블의 모든 행이 결과 테이블에 표기</p>
<h3 id="합집합-조인full-outer-join">합집합 조인(full outer join)</h3>
<p>두 개의 테이블을 기반으로 조인 조건에 만족하지 않는 행까지 모두 표기</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2️⃣ 데이터베이스의 종류]]></title>
            <link>https://velog.io/@tera_geniel/2-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%A2%85%EB%A5%98</link>
            <guid>https://velog.io/@tera_geniel/2-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%A2%85%EB%A5%98</guid>
            <pubDate>Wed, 15 Feb 2023 01:09:32 GMT</pubDate>
            <description><![CDATA[<h1 id="01-관계형-데이터베이스">01. 관계형 데이터베이스</h1>
<p>행과 열을 가지는 표 형식 데이터를 저장하는 형태의 데이터베이스</p>
<p>SQL이라는 언어를 써서 조작</p>
<p>→ 각각의 제품에 특화시킨 SQL을 사용(오라클 - PL/SQL, MySQL - SQL)</p>
<h3 id="01-mysql">01. MySQL</h3>
<p>현재 가장 많이 사용하는 데이터베이스</p>
<p>특징 참고: <a href="https://jeong-pro.tistory.com/239">https://jeong-pro.tistory.com/239</a></p>
<h3 id="02-postgresql">02. PostgreSQL</h3>
<p>MySQL 다음으로 개발자들이 선호하는 데이터베이스 기술</p>
<p>디스크 조각이 차지하는 영역을 회수할 수 있는 장치 = <code>VACUUM</code>이 특징</p>
<h1 id="02-nosql-데이터베이스">02. NoSQL 데이터베이스</h1>
<p>SQL을 사용하지 않는 데이터베이스.</p>
<p>주로 사용자들이 대규모로 확장되는 경우 - redis, graphdb 등으로 교체 혹은 끼워서 서비스를 확장한다.</p>
<h3 id="01-mongodb">01. MongoDB</h3>
<p>JSON을 통해 데이터를 접근 가능</p>
<p>Binary JSON 형태로 데이터가 저장 → 키-값 데이터 모델에서 확장된 도큐먼트 기반의 데이터베이스</p>
<p>ObjectID: 도큐먼트 생성 때마다 다른 컬렉션에서 중복된 값을 지니기 힘든 유니크한 기본키를 생성</p>
<h3 id="02-redis">02. redis</h3>
<p>인메모리 데이터베이스. 키-값 데이터 모델 기반의 데이터베이스</p>
<h1 id="03-nosql과-관계형-데이터베이스의-차이">03. NoSQL과 관계형 데이터베이스의 차이</h1>
<p><strong>[RDB]</strong></p>
<ul>
<li>상호 관련성을 가진 테이블 집합으로 구성</li>
<li>테이블 사이의 관계는 외래키로 표현</li>
<li>테이블은 행과 열로 구성</li>
<li>스키마 변경이 어렵다</li>
<li>scale up 가능, scale out 어려움</li>
<li>ACID 성질을 가짐(원자성, 일관성, 고립성, 지속성)</li>
</ul>
<p><strong>[NoSQL]</strong></p>
<ul>
<li>다양한 방식으로 데이터를 표현</li>
<li>테이블 사이에 특별히 명시된 제약이나 규칙이 없다</li>
<li>스키마가 고정적이지 않고, 매우 유연</li>
<li>Scale Out 용이</li>
<li>연산이 빠르고 빅데이터 &amp; 실시간 연산에 적합</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[1️⃣ 데이터베이스의 기본]]></title>
            <link>https://velog.io/@tera_geniel/1-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@tera_geniel/1-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Wed, 15 Feb 2023 01:07:42 GMT</pubDate>
            <description><![CDATA[<aside>
💡 해당 게시글은 책 [면접을 위한 CS 전공지식 노트] 기반으로 작성하였습니다.

</aside>

<h1 id="00-데이터베이스란">00. 데이터베이스란?</h1>
<p><code>데이터베이스</code>: 일정한 규칙, 혹은 규약을 통해 구조화되어 저장되어 있는 데이터의 모음.</p>
<ul>
<li>데이터베이스 안의 테이터들은 특정 DBMS마다 정의된 쿼리 언어를 통해 삽입, 삭제, 수정, 조회 등을 수행</li>
<li>실시간 접근과 동시 공유가 가능</li>
</ul>
<p><code>DBMS</code>(DataBase Management System): 해당 데이터베이스를 제어, 관리하는 통합 시스템</p>
<h1 id="01-엔티티">01. 엔티티</h1>
<p><code>엔티티</code>: 사람, 장소, 물건, 사건, 개념 등 “여러 개의 속성을 지닌 명사”</p>
<ul>
<li>예) 회원(엔티티) ← 이름, 아이디, 주소, 전화번호 (속성)</li>
</ul>
<h2 id="약한-엔티티와-강한-엔티티">약한 엔티티와 강한 엔티티</h2>
<p>약한 엔티티: 혼자 존재하지 못하고, 강한 엔티티의 존재 여부에 따라 종속적</p>
<ul>
<li>예) 방 = 약한 엔티티, 건물 = 강한 엔티티</li>
</ul>
<h1 id="02-릴레이션">02. 릴레이션</h1>
<p><code>릴레이션</code>: 데이터베이스에서 정보를 구분해 저장하는 기본 단위</p>
<p>→ 엔티티에 관한 데이터를 릴레이션 하나에 담아 저장함.</p>
<ul>
<li>관계형 데이터베이스: 릴레이션 == <code>테이블</code>, 레코드 - 테이블 - 데이터베이스</li>
<li>NoSQL 데이터베이스: 릴레이션 == <code>컬렉션</code>, 도큐먼트 - 컬렉션 - 데이터베이스</li>
</ul>
<h1 id="03-속성">03. 속성</h1>
<p><code>속성</code>: 릴레이션에서 관리하는 구체적이며 고유한 이름을 갖는 정보</p>
<ul>
<li>예) 차 → 고유넘버, 바퀴수, 차색깔, 차종…</li>
</ul>
<h1 id="04-도메인">04. 도메인</h1>
<p><code>도메인</code>: 릴레이션에 포함된 각각의 속성들이 가질 수 있는 값의 집합</p>
<ul>
<li>예) 성별(속성) → 여/남</li>
</ul>
<h1 id="05-필드와-레코드">05. 필드와 레코드</h1>
<table>
<thead>
<tr>
<th>name</th>
<th>id</th>
<th>phone_number</th>
</tr>
</thead>
<tbody><tr>
<td>이진</td>
<td>jinjin</td>
<td>01000000000</td>
</tr>
<tr>
<td>최소은</td>
<td>coso</td>
<td>01022222222</td>
</tr>
<tr>
<td>박은수</td>
<td>eunddu</td>
<td>01011111111</td>
</tr>
<tr>
<td>- 회원이란 엔티티는 member라는 테이블로 속성인 이름, 아이디, 등을 가지고 있음</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- name, ID, address 등의 필드를 가짐.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p><code>레코드</code>: 테이블에 쌓이는 행 단위의 데이터, 튜플이라고도 함.</p>
<h2 id="필드-타입">필드 타입</h2>
<h3 id="01-숫자-타입">01. 숫자 타입</h3>
<p>TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT</p>
<h3 id="02-날짜-타입">02. 날짜 타입</h3>
<p>DATE(3), DATETIME(8), TIMESTAMP(4)</p>
<h3 id="03-문자-타입">03. 문자 타입</h3>
<p>CHAR, VARCHAR, TEXT, BLOB, ENUM, SET</p>
<ul>
<li><p><code>CHAR</code>: 테이블을 생성할 때 선언한 길이로 고정되어 저장됨.</p>
</li>
<li><p><code>VARCHAR</code>: 가변 길이 문자열. 10글자의 이메일이면 10글자 바이트 + 길이기록용 1바이트로 저장.</p>
<p>  → VARCHAR(1000)으로 선언해도 마찬가지.</p>
</li>
<li><p><code>TEXT</code>: 큰 문자열 저장. 게시판의 본문을 저장할 때 사용</p>
</li>
<li><p><code>BLOB</code>: 이미지, 동영상 등 큰 데이터 저장에 씀. BUT 보통은 아마존 이미지 호스팅 서비스 = S3를 사용</p>
</li>
<li><p><code>ENUM</code>: 문자열을 열거한 타입. 메모리를 적게 사용하는 이점. 약 6만 5천개의 요소 넣을 수 있음.</p>
</li>
<li><p><code>SET</code>: ENUM과 비슷, 여러 개의 데이터를 선택 가능, 비트 단위의 연산 가능, 최대 64개의 요소</p>
</li>
</ul>
<h1 id="06-관계">06. 관계</h1>
<p>테이블은 서로의 관계가 정의되어 있고, 이를 관계화살표로 나타낸다.</p>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/f5c4c745-5092-41bf-b8c1-aecd7a45e3f4/image.png" alt=""></p>
<h2 id="01-11-관계">01. 1:1 관계</h2>
<p>유저당 장바구니는 1개씩. 장바구니도 1개의 유저만 담는다.</p>
<p>→ 테이블이 2개!</p>
<h2 id="02-1n-관계">02. 1:N 관계</h2>
<p>장바구니당 여러 개의 상품을 담을 수 있다.</p>
<p>하나도 넣지 않는 0개의 경우도 있으니 0도 포함되는 화살표를 통해 표현해야 한다.</p>
<h2 id="03-nm-관계">03. N:M 관계</h2>
<p>학생과 강의의 관계.</p>
<p>강의는 여러 명의 학생이 참여하기도 하고, 학생은 여러 개의 강의를 수강할 수 있다.</p>
<p>학생_강의 라는 테이블을 만들어서 각각 1:N, 1:M이라는 관계를 갖는 테이블 두개로 나누어서 설정.</p>
<h1 id="07-키">07. 키</h1>
<p>테이블 자체의 인덱스를 위해 설정된 장치 → 기본키, 외래키, 후보키, 슈퍼키, 대체키가 있음</p>
<ul>
<li><code>유일성</code>: 중복되는 값이 없음.</li>
<li><code>최소성</code>: 필드를 조합하지 않고 최소 필드만 써서 키를 형성할 수 있는 것.</li>
</ul>
<h3 id="01-기본키-primary-key-⭐">01. 기본키 PRIMARY KEY ⭐</h3>
<p>유일성과 최소성을 만족하는 키.</p>
<p>자연키와 인조키 중 택(보통 인조키 택)</p>
<ul>
<li><code>자연키</code>: 중복된 값들을 제외하며 중복되지 않는 것을 자연스레 뽑다가 나오는 키. 가변성<ul>
<li>예) 주민등록번호</li>
</ul>
</li>
<li><code>인조키</code>: 인위적으로 생성한 키. 변하지 않음.<ul>
<li>예) 인위적으로 부여한 고유 식별자(auto increment, sequence 등으로 설정)</li>
</ul>
</li>
</ul>
<h3 id="02-외래키-⭐">02. 외래키 ⭐</h3>
<p>다른 테이블의 기본키를 그대로 참조하는 값으로 엔티티들간의 관계를 식별하는 데 사용</p>
<p>중복 가능.</p>
<h3 id="03-후보키">03. 후보키</h3>
<p>기본키가 될 수 있는 후보들</p>
<p>유일성, 최소성을 동시에 만족하는 키</p>
<h3 id="04-대체키">04. 대체키</h3>
<p>후보키가 두 개 이상인 경우 → 어느 한개를 기본키로 지정하고 “남은 후보키”들</p>
<h3 id="05-슈퍼키">05. 슈퍼키</h3>
<p>각 레코드를 유일하게 식별할 수 있는 유일성을 갖춘 키</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 쓰레드 -4]]></title>
            <link>https://velog.io/@tera_geniel/C-%EC%93%B0%EB%A0%88%EB%93%9C-4</link>
            <guid>https://velog.io/@tera_geniel/C-%EC%93%B0%EB%A0%88%EB%93%9C-4</guid>
            <pubDate>Tue, 12 Jul 2022 09:41:55 GMT</pubDate>
            <description><![CDATA[<h1 id="비동기-연산을-위한-도구들">비동기 연산을 위한 도구들</h1>
<h2 id="동기synchronous와-비동기asynchronous-실행">동기(synchronous)와 비동기(asynchronous) 실행</h2>
<p>하드 디스크에서 파일을 읽는다고 생각해 보자.
SSD가 아니라 하드 디스크를 사용하면 임의의 위치에 쓰여져 있는 파일을 읽는데 시간이 오래 걸림. -&gt; 램에서 데이터를 읽어내는 데 50나노초가 걸리는데, 그에 비해 약 8만배 느림.</p>
<h3 id="동기적인-작업">동기적인 작업</h3>
<pre><code class="language-cpp">string txt = read(&quot;a.txt&quot;); // 5ms
string result = do_something_with_txt(txt); // 5ms
do_other_computation(); // 5ms 걸림 (CPU 로 연산을 수행함)</code></pre>
<p>위 코드가 순차적으로 실행된다고 하자.</p>
<p>1번에 1개씩 순차적으로 실행되는 작업을 동기적(synchronous)으로 실행된다고 부름
동기적인 작업들은 한 작업이 끝날 때까지 다음 작업으로 이동하지 않기 때문.</p>
<h3 id="비동기적인-작업">비동기적인 작업</h3>
<blockquote>
<p>프로그램의 실행이 한 갈래가 아니라 여러 갈래로 갈라져서 동시에 진행되는 것</p>
</blockquote>
<pre><code class="language-cpp">void file_read(string* result) {
  string txt = read(&quot;a.txt&quot;); // (1)
  *result = do_something_with_txt(txt);
}

int main() {
  string result;
  thread t(file_read, &amp;result);
  do_other_computation(); // (2)
  t.join();
}</code></pre>
<ol>
<li>(1) 파일 입출력을 맡겨 두다가, </li>
<li>CPU 놀지 않고 (2) 먼저 하다가 스레드 끝남.</li>
<li>이후 join 수행으로 다시 file_read 스레드를 실행, </li>
<li>하드 디스크에서 a.txt 파일의 내용이 도착해 있으므로, do_something_with_txt를 바로 실행함.</li>
</ol>
<p>JS와는 달리 명시적으로 쓰레드를 생성해서 적절히 수행해야 했음
-&gt; C++11 도입 이후 비동기적 실행을 할 수 있게 해주는 도구를 제공해 줌.</p>
<h2 id="stdpromise-와-stdfuture">std::promise 와 std::future</h2>
<blockquote>
<p>어떤 데이터를 다른 스레드를 통해 처리해서 받아내는 것.</p>
</blockquote>
<p>어떤 스레드 T를 사용해서, 비동기적으로 값을 받아내겠다
: 미래에(future) 쓰레드 T가 원하는 데이터를 돌려 주겠다 라는 약속(promise)라고 볼 수 있음.</p>
<pre><code class="language-cpp">#include &lt;future&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;thread&gt;

using std::string;

void worker(std::promise&lt;string&gt;* p) {
  // 약속을 이행하는 모습. 해당 결과는 future 에 들어간다.
  p-&gt;set_value(&quot;some data&quot;);
}

int main() {
  std::promise&lt;string&gt; p;

  // 미래에 string 데이터를 돌려 주겠다는 약속.
  std::future&lt;string&gt; data = p.get_future();
  std::thread t(worker, &amp;p);

  // 미래에 약속된 데이터를 받을 때 까지 기다린다.
  data.wait();

  // wait 이 리턴했다는 뜻이 future 에 데이터가 준비되었다는 의미.
  // 참고로 wait 없이 그냥 get 해도 wait 한 것과 같다.
  std::cout &lt;&lt; &quot;받은 데이터 : &quot; &lt;&lt; data.get() &lt;&lt; std::endl;
  t.join();
}
</code></pre>
<p>promise 객체를 정의할 때, 연산을 수행하고 돌려줄 객체의 타입을 템플릿 인자로 받음.
우리의 경우 string 객체를 돌려줄 예정이므로 string을 전달함.</p>
<blockquote>
<p>❗ future에서 get 함수 호출하면 설정된 객체가 이동해 절대로 get을 2번 호출하면 안됨.</p>
</blockquote>
<p>promise: 생산자 - 소비자 패턴에서 마치 생산자(producer)의 역할을 수행하고, future는 소비자(consumer)의 역할을 수행한다고 볼 수 있음.</p>
<h2 id="shared_future">shared_future</h2>
<p>여러 개의 스레드에서 future를 get하고 싶을 때 사용</p>
<h2 id="packaged_task">packaged_task</h2>
<p>promise-future 패턴을 비동기적 함수(Callable)의 리턴값에 간단히 적용할 수 있는 packaged_task 라는 것을 지원.</p>
<p>future는 packaged_task가 리턴하는 future에서 접근할 수 있음.</p>
<pre><code class="language-cpp">int some_task(int x) { return 10 + x; }
int main() {
  // int(int) : int 를 리턴하고 인자로 int 를 받는 함수. (std::function 참조)
  std::packaged_task&lt;int(int)&gt; task(some_task);
  std::future&lt;int&gt; start = task.get_future();

  std::thread t(std::move(task), 5);
  std::cout &lt;&lt; &quot;결과값 : &quot; &lt;&lt; start.get() &lt;&lt; std::endl;
  t.join();
}</code></pre>
<p>packaged_task는 비동기적으로 수행할 함수 자체를 생성자의 인자로 받음.
템플릿 인자로 해당 함수의 타입을 명시해야 함.</p>
<blockquote>
<p>packaged_task는 전달된 함수를 실행해서, 그 함수의 리턴값을 promise에 설정함.</p>
</blockquote>
<p>이때 packaged_task는 복사 생성이 불가능하므로, 명시적으로 move해 줘야 함.
쓰레드에 굳이 promise를 전달하지 않아도 알아서 packaged_task가 함수의 리턴값을 처리해줘서 매우 편리함.</p>
<h2 id="stdasync">std::async</h2>
<blockquote>
<p>std::async 에 어떤 함수를 전달한다면, 아예 쓰레드를 알아서 만들어서 해당 함수를 비동기적으로 실행하고, 그 결과값을 future 에 전달함.</p>
</blockquote>
<pre><code class="language-cpp">std::future&lt;int&gt; lower_half_future =
std::async(std::launch::async, sum, cref(v), 0, v.size() / 2);</code></pre>
<p>async 함수는 인자로 받은 함수를 비동기적으로 실행한 후, 해당 결과값을 보관할 future를 리턴함.</p>
<p>첫번째 인자로 아래 두가지 경우가 가능</p>
<ul>
<li><code>std::launch::async</code> : 바로 쓰레드를 생성해서 인자로 전달된 함수를 실행한다.</li>
<li><code>std::launch::deferred</code> : future 의 get 함수가 호출되었을 때 실행한다. (새로운 쓰레드를 생성하지 않음), 즉 동기적으로 실행됨</li>
</ul>
<pre><code class="language-cpp">#include &lt;future&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;

int do_work(int x) {
  // x 를 가지고 무슨 일을 한다.
  std::this_thread::sleep_for(std::chrono::seconds(3));
  return x;
}

void do_work_parallel() {
  auto f1 = std::async([]() { do_work(3); });
  auto f2 = std::async([]() { do_work(3); });
  do_work(3);
  f1.get();
  f2.get();
}

void do_work_sequential() {
  do_work(3);
  do_work(3);
  do_work(3);
}

int main() { do_work_parallel(); }</code></pre>
<p>3 개의 do_work 함수를 동시에 각기 다른 쓰레드에서 실행한 덕분에 3 초 만에 끝난다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 쓰레드 -3]]></title>
            <link>https://velog.io/@tera_geniel/C-%EC%93%B0%EB%A0%88%EB%93%9C-3</link>
            <guid>https://velog.io/@tera_geniel/C-%EC%93%B0%EB%A0%88%EB%93%9C-3</guid>
            <pubDate>Sun, 10 Jul 2022 14:55:34 GMT</pubDate>
            <description><![CDATA[<h1 id="atomic-객체와-명령어-재배치">atomic 객체와 명령어 재배치</h1>
<p>CPU와 컴퓨터 메모리(RAM)은 물리적으로 떨어져 있음.
따라서 CPU에서 메모리를 읽는데 걸리는 시간: 약 42사이클(add 한번 하는데 걸리는 시간: 1사이클)</p>
<h2 id="캐시">캐시</h2>
<p>CPU 칩 안에 있는 조그마한 메모리.
램과는 달리 CPU에서 연산을 수행하는 부분이랑 거의 붙어 있어, 읽기/쓰기 속도가 매우 빠름</p>
<p>CPU에서 차례로 가장 많이 접근하는 메모리 영역: L1 &gt; L2 &gt; L3 캐시 순</p>
<p>CPU가 특정 주소에 있는 데이터에 접근하려 하면</p>
<ol>
<li>일단 캐시에 있는 지 확인</li>
<li>캐시에 있다면 해당 값을 읽음(<code>Cache hit</code>)</li>
<li>없으면 메모리까지 갔다가 옴(<code>Cache miss</code>)</li>
</ol>
<p>CPU가 어떻게 어느 영역의 메모리에 자주 접근할 지 어떻게 아는가?
-&gt; 알 수 없다.</p>
<p>CPU에서 캐시가 작동하는 방식은</p>
<ul>
<li>메모리를 읽으면 일단 캐시에 저장한다.</li>
<li>만일 캐시가 다 차면 <code>특정 방식</code>에 따라 처리한다.</li>
</ul>
<p>이때 <code>특정 방식</code>에는 대표적으로 LRU 방식이 있음(Least Recently Used).
최근에 접근한 데이터를 자주 반복해서 접근하면 매우 유리함.</p>
<h2 id="컴퓨터-컴파일-실행-순서">컴퓨터 컴파일 실행 순서</h2>
<pre><code class="language-cpp">int a = 0;
int b = 0;

void foo() { 
  a = b + 1; 
  b = 1;
}</code></pre>
<p>그런데 이때 a = b + 1 부분의 실행이 끝나기 전에 b = 1이 먼저 실행이 끝남.
-&gt; 만약 다른 쓰레드가 있어 a와 b의 값을 확인했을 때, 코드가 순서대로 실행되었으면 b =1이면 a = 1이어야 하지만, a=0인데 b=1일 수 있다.</p>
<h2 id="cpu-파이프라이닝">CPU 파이프라이닝</h2>
<blockquote>
<p>한 작업이 끝나기 전에, 다음 작업을 시작하는 방식으로 동시에 여러 개의 작업을 동시에 실행하는 것</p>
</blockquote>
<p>실제 CPU에서 명령어를 실행할 때: 
<code>fetch</code>(명령어 읽음), <code>decode</code>(읽은 명령어가 뭔지 해석), <code>execute</code>(해석된 명령어를 실행), <code>write</code>(결과를 씀)의 과정을 거침.</p>
<p>그런데 예컨데, 세탁이나 빨래 개기는 30분인데 건조가 3시간이나 걸려서 건조기 기다리느라 빨래를 계속할 수 없다면, 비효율적일 수 있다.</p>
<p>따라서 컴파일러는 CPU의 파이프라인을 효율적으로 활용할 수 있도록 명령어를 재배치함.
또한 컴파일러가 아니더라도, 캐시 상 더 빨리 처리할 수 있는 걸 CPU에서 먼저 실행할 수도 있음.</p>
<h3 id="수정-순서-modification-order">수정 순서 modification order</h3>
<blockquote>
<p>수정 순서: 만약 어떤 객체의 값을 실시간으로 확인할 수 있는 전지전능한 무언가가 있다고 했을 때, 해당 객체의 값의 변화를 기록한 것</p>
</blockquote>
<p>C++의 모든 객체들은 수정 순서(modification order)라는 것을 정의할 수 있음.
즉, 원자적 연산을 할 경우 모든 쓰레드에서 같은 객체에 대해 동일한 <code>수정 순서</code>를 관찰할 수 있다.</p>
<p>💡 BUT 같은 시간에 변수 a의 값을 관찰했다고 해서 굳이 모든 쓰레드들이 동일한 값을 관찰할 필요는 없다.</p>
<p>-&gt; 쓰레드 간에 같은 시간에 변수의 값을 읽었을 때 다른 값을 리턴할 수 있음.
-&gt; CPU 캐시가 각 코어별로 존재하기 때문.
-&gt; 각 코어가 각각 자신들의 L1, L2 캐시들을 가지고 있음(동기화 작업이 필요하나, 시간을 꽤나 잡아먹음)</p>
<h2 id="원자성">원자성</h2>
<p>원자적인 연산이 아닌 경우: 모든 스레드에서 같은 수정 순서를 관찰할 수 있음이 보장되지 않음. 직접 적정한 동기화 방법을 통해 처리해야 함.</p>
<blockquote>
<p>원자적: CPU가 명령어 1개로 처리하는 명령. 중간에 다른 스레드가 끼어들 여지가 전혀 없는 연산.</p>
</blockquote>
<pre><code class="language-cpp">#include &lt;atomic&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

void worker(std::atomic&lt;int&gt;&amp; counter) {
  for (int i = 0; i &lt; 10000; i++) {
    counter++;
  }
}

int main() {
  // ❗ 이레와 같은 아토믹 객체를 만들 수 있음.
  std::atomic&lt;int&gt; counter(0);
  std::vector&lt;std::thread&gt; workers;
  for (int i = 0; i &lt; 4; i++) {
    workers.push_back(std::thread(worker, ref(counter)));
  }
  for (int i = 0; i &lt; 4; i++) {
    workers[i].join();
  }
  std::cout &lt;&lt; &quot;Counter 최종 값 : &quot; &lt;&lt; counter &lt;&lt; std::endl;
}
</code></pre>
<p>atomic의 템플릿 인자로 원자적으로 만들고 싶은 타입을 전달할 수 있음.</p>
<p>그런데 CPU에 따라 어느 CPU에서 실행할 지 컴파일러가 알고 있음
-&gt; CPU 특이적인 명령어를 제공할 수 있다는 걸 <code>is_lock_free</code> 함수로 알 수 있음.</p>
<h2 id="memory-order">memory order</h2>
<p>atomic 객체들은 원자적 연산 시, 메모리 접근 방식을 지정 가능.</p>
<h3 id="memory_order_relaxed">memory_order_relaxed</h3>
<p>가장 느슨한 조건. 
memory_order_relaxed 방식으로 메모리에서 읽거나 쓸 경우,
<strong>주위의 다른 메모리 접근들과 순서가 바뀌어도 무방</strong>함.</p>
<pre><code class="language-cpp">#include &lt;atomic&gt;
#include &lt;cstdio&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

using std::memory_order_relaxed;

void t1(std::atomic&lt;int&gt;* a, std::atomic&lt;int&gt;* b) {
  b-&gt;store(1, memory_order_relaxed); // b = 1 (쓰기)
  int x = a-&gt;load(memory_order_relaxed); // x = a (읽기)
  printf(&quot;x : %d \n&quot;, x);
}

void t2(std::atomic&lt;int&gt;* a, std::atomic&lt;int&gt;* b) {
  a-&gt;store(1, memory_order_relaxed); // a = 1 (쓰기)
  int y = b-&gt;load(memory_order_relaxed); // y = b (읽기)
  printf(&quot;y : %d \n&quot;, y);
}

int main() {
  std::vector&lt;std::thread&gt; threads;
  std::atomic&lt;int&gt; a(0);
  std::atomic&lt;int&gt; b(0);

  threads.push_back(std::thread(t1, &amp;a, &amp;b));
  threads.push_back(std::thread(t2, &amp;a, &amp;b));
  for (int i = 0; i &lt; 2; i++) {
    threads[i].join();
  }
}</code></pre>
<p>store과 load는 atomic 객체들에 대해 원자적으로 쓰기, 읽기를 지원해 주는 함수.</p>
<h4 id="장점">장점</h4>
<p>CPU에서 메모리 연산 순서에 관련해서 무한한 자유를 줌.
-&gt; CPU에서 매우 빠른 속도로 실행함.</p>
<p>즉, 위 경우가 아닌 <code>counter++</code> 10000번씩, 4개의 쓰레드로 ++하는 경우 더 유용함.
비록 다른 메모리연산들 보다 <code>counter++</code> 이 늦게 된다고 하더라도 결과적으로 증가되기만 하면 문제 될게 없기 때문(순서가 중요하지 않은 경우에 더 유용!)</p>
<h4 id="단점">단점</h4>
<p>위 코드에서 결과로 
x : 0
y : 0
가 나올 수도 있음.</p>
<p>즉, 아래와 같이 CPU가 코드 순서를 재배치해서 실행할 수도 있음.</p>
<pre><code class="language-cpp">int x = a-&gt;load(memory_order_relaxed); // x = a (읽기)
b-&gt;store(1, memory_order_relaxed); // b = 1 (쓰기)</code></pre>
<h3 id="동기화-memory_order_acquire-과-memory_order_release">동기화: memory_order_acquire 과 memory_order_release</h3>
<pre><code class="language-cpp">#include &lt;atomic&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

using std::memory_order_relaxed;

void producer(std::atomic&lt;bool&gt;* is_ready, int* data) {
  *data = 10;
  is_ready-&gt;store(true, memory_order_relaxed);
  // ❗ *data = 10과 순서가 바뀌어서 실행되면 is_ready가 true라도 *data = 10이 실행이 끝나지 않을 수 있음.
}

void consumer(std::atomic&lt;bool&gt;* is_ready, int* data) {
  // data 가 준비될 때 까지 기다린다.
  while (!is_ready-&gt;load(memory_order_relaxed)) {
  }
  std::cout &lt;&lt; &quot;Data : &quot; &lt;&lt; *data &lt;&lt; std::endl;
}

int main() {
  std::vector&lt;std::thread&gt; threads;
  std::atomic&lt;bool&gt; is_ready(false);
  int data = 0;

  threads.push_back(std::thread(producer, &amp;is_ready, &amp;data));
  threads.push_back(std::thread(consumer, &amp;is_ready, &amp;data));

  for (int i = 0; i &lt; 2; i++) {
    threads[i].join();
  }
}</code></pre>
<p><code>*data = 10</code>과 순서가 바뀌어서 실행되면 is_ready가 true라도 <code>*data = 10</code>이 실행이 끝나지 않을 수 있기 때문에, 
consumer 스레드에서 is_ready가 true가 되어도 제대로 된 data를 읽을 수 없음.</p>
<p>이건 consumer 스레드에서도 마찬가지임.</p>
<pre><code class="language-cpp">while (!is_ready-&gt;load(memory_order_relaxed)) {
}
std::cout &lt;&lt; &quot;Data : &quot; &lt;&lt; *data &lt;&lt; std::endl;</code></pre>
<p>위 코드에서 data를 읽는 부분과 is_ready에서 읽는 부분 순서가 바뀌면, is_ready가 true가 되기 전의 data값을 읽을 수 있음.</p>
<p>-&gt; memory_order_relaxed를 사용할 수 없음.</p>
<pre><code class="language-cpp">#include &lt;atomic&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

void producer(std::atomic&lt;bool&gt;* is_ready, int* data) {
  *data = 10;
  is_ready-&gt;store(true, std::memory_order_release);
      // ❗ 1. memory_order_release: 해당 명령 이전의 모든 메모리 명령들이 해당 명령 이후로 재배치되는 것을 금지함.
}

void consumer(std::atomic&lt;bool&gt;* is_ready, int* data) {
  // data 가 준비될 때 까지 기다린다.
  while (!is_ready-&gt;load(std::memory_order_acquire)) {
  }
  // ❗ 2. 같은 변수를 memory_order_acquire으로 읽는 쓰레드가 있으면, memory_order_release 이전에 오는 모든 메모리 명령들이 해당 쓰레드에 의해서 관찰될 수 있어야 함.
  std::cout &lt;&lt; &quot;Data : &quot; &lt;&lt; *data &lt;&lt; std::endl;
}

int main() {
  std::vector&lt;std::thread&gt; threads;
  std::atomic&lt;bool&gt; is_ready(false);
  int data = 0;

  threads.push_back(std::thread(producer, &amp;is_ready, &amp;data));
  threads.push_back(std::thread(consumer, &amp;is_ready, &amp;data));

  for (int i = 0; i &lt; 2; i++) {
    threads[i].join();
  }
}</code></pre>
<ol>
<li><code>memory_order_release</code>: 해당 명령 이전의 모든 메모리 명령들이 해당 명령 이후로 재배치되는 것을 금지함.</li>
<li>같은 변수를 <code>memory_order_acquire</code>으로 읽는 쓰레드가 있으면, <code>memory_order_release</code> 이전에 오는 모든 메모리 명령들이 해당 쓰레드에 의해서 관찰될 수 있어야 함. (❓)
즉, 해당 명령 뒤에 오는 모든 메모리 명령들이 해당 명령 위로 재배치되는 것을 금지함.</li>
</ol>
<p>따라서 이 두 개의 다른 스레드들이 같은 변수의 release, acquire 를 통해 <strong>동기화</strong>(synchronize)를 수행하게 됨.</p>
<h3 id="memory_order_acq_rel">memory_order_acq_rel</h3>
<p>acquire와 release를 모두 수행하는 것.
읽기, 쓰기를 모두 수행하는 명령들 예를 들어 fetch_add와 같은 함수 속에서 사용될 수 있음.</p>
<h3 id="memory_order_seq_cst">memory_order_seq_cst</h3>
<p>메모리 명령의 순차적 일관성을 보장.</p>
<h4 id="순차적-일관성">순차적 일관성</h4>
<p>메모리 명령 재배치도 없고, 모든 쓰레드에서 모든 시점에 동일한 값을 관찰할 수 있는 방식</p>
<h4 id="특징">특징</h4>
<ul>
<li>멀티 코어 시스템에서 memory_order_seq_cst가 꽤나 비싼 연산임.</li>
<li>인텔, AMD x86 CPU는 사실 거의 순차적 일관성이 보장됨. memory_order_seq_cst를 강제해도 그 차이가 그렇게 크지 않음.</li>
<li>ARM 계열의 CPU와 같은 경우 순차적 일관성을 보장하기 위해 CPU의 동기화 비용이 매우 큼.</li>
<li><blockquote>
<p>따라서 정말 꼭 필요할 때만 사용해야 함.</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 쓰레드 -2]]></title>
            <link>https://velog.io/@tera_geniel/C-%EC%93%B0%EB%A0%88%EB%93%9C-2</link>
            <guid>https://velog.io/@tera_geniel/C-%EC%93%B0%EB%A0%88%EB%93%9C-2</guid>
            <pubDate>Tue, 05 Jul 2022 06:11:41 GMT</pubDate>
            <description><![CDATA[<h1 id="뮤텍스와-조건변수">뮤텍스와 조건변수</h1>
<h4 id="경쟁-상태race-condition">경쟁 상태(race condition)</h4>
<p>서로 다른 쓰레드들이 동일한 자원을 사용할 때 발생하는 문제</p>
<h2 id="race-condition">Race condition</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

void worker(int&amp; counter) {
  for (int i = 0; i &lt; 10000; i++) {
  // ❗ 문제 발생
    counter += 1;
  }
}

int main() {
  int counter = 0;
  std::vector&lt;std::thread&gt; workers;

  for (int i = 0; i &lt; 4; i++) {
  // 레퍼런스로 전달하려면 ref 함수로 감싸야 한다 (지난 강좌 bind 함수 참조)
    workers.push_back(std::thread(worker, std::ref(counter)));
  }

  for (int i = 0; i &lt; 4; i++) {
    workers[i].join();
  }

  std::cout &lt;&lt; &quot;Counter 최종 값 : &quot; &lt;&lt; counter &lt;&lt; std::endl;
}</code></pre>
<h3 id="cpu-간단-소개">CPU 간단 소개</h3>
<p>CPU: 컴퓨터의 모든 연산이 발생하는 두뇌</p>
<p>CPU의 연산: CPU의 레지스터라는 곳에 데이터를 기록한 다음에 연산을 수행해야 함.</p>
<p>모든 데이터들 = 메모리에 저장,
연산할 때: 메모리 -&gt; 레지스터로 값을 가져오고 빠르게 연산을 하고, 다시 메모리에 가져다 놓는 식으로 작동</p>
<h2 id="뮤텍스mutex">뮤텍스(mutex)</h2>
<p>위 코드의 결과로 이상한 게 나올 수 있다.
&lt;- counter += 1을 두번 했는데 결과는 한번만 한것과 같은 효과를 가질 수 있기 때문.
(쓰레드가 counter에 서로 쓰려고 해서 생기는 문제)</p>
<p>쓰레드를 어떻게 스케둘링할 지는 운영체제가 마음대로 결정하는 것이기 때문.
멀티쓰레딩을 했을 때의 문제점이다.</p>
<blockquote>
<p>뮤텍스: 한번에 한 쓰레드에서만 위 코드를 실행시킬 수 있다.(경찰관 역할)</p>
</blockquote>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;mutex&gt; // mutex 를 사용하기 위해 필요
#include &lt;thread&gt;
#include &lt;vector&gt;

void worker(int&amp; result, std::mutex&amp; m) {
  for (int i = 0; i &lt; 10000; i++) {
    m.lock();
    result += 1;
    m.unlock();
  }
}

int main() {
  int counter = 0;
  std::mutex m; // 우리의 mutex 객체
  std::vector&lt;std::thread&gt; workers;

  for (int i = 0; i &lt; 4; i++) {
    workers.push_back(std::thread(worker, std::ref(counter), std::ref(m)));
  }

  for (int i = 0; i &lt; 4; i++) {
    workers[i].join();
  }

  std::cout &lt;&lt; &quot;Counter 최종 값 : &quot; &lt;&lt; counter &lt;&lt; std::endl;
}</code></pre>
<blockquote>
<p>mutex: 상호 배제(mutual exclusion)</p>
</blockquote>
<p>m.lock() -&gt; 뮤텍스 m을 나만 쓴다.(1번에 1 스레드에서만 m의 사용 권한)
다른 스레드에서 m.lock()을 하면 m을 소유한 스레드가 m.unlock()을 통해 m을 반환 때까지 무한정 기다림.</p>
<blockquote>
<p>result += 1은 결국 한 스레드만이 유일하게 실행할 수 있음.</p>
</blockquote>
<h3 id="임계영역critical-section">임계영역(critical section)</h3>
<p>스레드만이 유일하게 실행할 수 있는 코드 부분</p>
<h3 id="데드락">데드락</h3>
<p>m.unlock() 코드를 지우면, 뮤텍스를 취득한 스레드가 unlock을 하지 않으므로, 다른 모든 스레드들이 기다리게 됨. 본인도 마찬가지로 m.lock()을 다시 호출, unlock을 하지 않았으므로 본인 역시 기다림.</p>
<p>-&gt; 취득한 뮤텍스는 사용이 끝나면 반드시 반환을 해야 함.</p>
<h3 id="lock-guard">lock guard</h3>
<pre><code class="language-cpp">std::lock_guard&lt;std::mutex&gt; lock(m);</code></pre>
<p>lock guard 객체는 뮤텍스를 인자로 받아서 생성, 이때 생성자에서 뮤텍스를 lock하게 됨.
lock guard가 소멸될 때 알아서 lock했던 뮤텍스를 unlock하게 됨.</p>
<p>사용자가 따로 unlock을 신경 쓰지 않아도 되서 매우 편리.</p>
<h2 id="데드락-deadlock">데드락 deadlock</h2>
<p>데드락이 발생하는 조건은 다음과 같이 worker1, worker2를 실행했을 때</p>
<pre><code class="language-cpp">// worker1
std::lock_guard&lt;std::mutex&gt; lock1(m1);
std::lock_guard&lt;std::mutex&gt; lock2(m2);

// worker2
std::lock_guard&lt;std::mutex&gt; lock2(m2);
std::lock_guard&lt;std::mutex&gt; lock1(m1);</code></pre>
<p>서로가 필요한 뮤텍스가 서로에게 있기 떄문에 모두 이러지도 저러지도 못함.</p>
<h3 id="데드락-극복하기">데드락 극복하기</h3>
<blockquote>
<p>한 스레드에게 우선권을 주는 방법이 있다.</p>
</blockquote>
<p>쓰레드로 비유하면, 한 스레드가 다른 스레드에 비해 우위를 갖게 된다면, 한 스레드만 열심히 일하고 다른 스레드는 일할 수 없는 기아 상태(starvation)가 발생할 수 있음.</p>
<pre><code class="language-cpp">void worker2(std::mutex&amp; m1, std::mutex&amp; m2) {
  for (int i = 0; i &lt; 10; i++) {
    while (true) {
      m2.lock();
      // m1 이 이미 lock 되어 있다면 &quot;야 차 빼&quot; 를 💥스스로💥 수행하게 된다.
      if (!m1.try_lock()) {
        m2.unlock();
        continue;
      }
      std::cout &lt;&lt; &quot;Worker2 Hi! &quot; &lt;&lt; i &lt;&lt; std::endl;
      m1.unlock();
      m2.unlock();
      break;
    }
  }
}</code></pre>
<p>일단 m2는 아무 문제 없이 lock이 가능.
m1을 lock할 때: worker1이 m1을 lock하고 있는 경우 -&gt; 원래는 이도저도 못했음.</p>
<p>try_lock: lock할 수 있으면 lock하고 true 리턴, 아니면 기다리지 않고 false 리턴</p>
<p>m1.try_lock()이 </p>
<ul>
<li>true를 리턴했으면 worker2가 m1, m2를 성공적으로 lock한 상황, 그대로 처리하면 됨.</li>
<li>false를 리턴했으면 worker1이 이미 m1을 lock했다는 의미. worker1에게 우선권을 줘야 하기 때문에 이미 얻은 m2 역시 worker1에게 제공해 줌.</li>
</ul>
<p>참고: C++ Concurrency In Action, 데드락 상황을 피하기 위해 가이드라인을 제공.</p>
<h4 id="중첩된-lock을-사용하는-것을-피해라">중첩된 Lock을 사용하는 것을 피해라.</h4>
<p>모든 스레드들이 최대 1개의 lock만을 소유하도록 하자.</p>
<h4 id="lock을-소유하고-있을-때-유저-코드를-호출하는-것을-피해라">Lock을 소유하고 있을 때 유저 코드를 호출하는 것을 피해라.</h4>
<p>유저 코드에서 Lock을 소유할 수도 있기 때문에 중첩된 lock을 얻는 걸 피하려면 lock 소유 시 유저 코드를 호출하는 것을 지양해야 함.</p>
<h4 id="lock들을-언제나-정해진-순서로-획득해라">Lock들을 언제나 정해진 순서로 획득해라.</h4>
<p>반드시 정해진 순서로 획득하자.
앞서도 worker1이 m1, m2 순으로, worker2가 m2, m1 순으로 lock했기 때문에 발생한 문제였다.</p>
<h2 id="생산자와-소비자의-패턴">생산자와 소비자의 패턴</h2>
<ul>
<li><strong>생성자</strong>: 무언가 처리할 일을 받아오는 쓰레드</li>
<li><strong>소비자</strong>: 받은 일을 처리하는 쓰레드</li>
</ul>
<pre><code class="language-cpp">#include &lt;chrono&gt; // std::chrono::miliseconds
#include &lt;iostream&gt;
#include &lt;mutex&gt;
#include &lt;queue&gt;
#include &lt;string&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;

void producer(std::queue&lt;std::string&gt;* downloaded_pages, std::mutex* m,
int index) {
  for (int i = 0; i &lt; 5; i++) {
    // 웹사이트를 다운로드 하는데 걸리는 시간이라 생각하면 된다.
    // 각 쓰레드 별로 다운로드 하는데 걸리는 시간이 다르다.
    std::this_thread::sleep_for(std::chrono::milliseconds(100 * index));
    std::string content = &quot;웹사이트 : &quot; + std::to_string(i) + &quot; from thread(&quot; + std::to_string(index) + &quot;)\n&quot;;
    // data 는 쓰레드 사이에서 공유되므로 critical section 에 넣어야 한다.
    m-&gt;lock();
    downloaded_pages-&gt;push(content);
    m-&gt;unlock();
  }
}

void consumer(std::queue&lt;std::string&gt;* downloaded_pages, std::mutex* m,
int* num_processed) {
  // 전체 처리하는 페이지 개수가 5 * 5 = 25 개.
  while (*num_processed &lt; 25) {
    m-&gt;lock();
    // 만일 현재 다운로드한 페이지가 없다면 다시 대기.
    // 다른 스레드에게 기회를 줌!
    if (downloaded_pages-&gt;empty()) {
      m-&gt;unlock(); // (Quiz) 여기서 unlock 을 안한다면 어떻게 될까요?
      // 10 밀리초 뒤에 다시 확인한다.
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
      continue;
    }

    // 맨 앞의 페이지를 읽고 대기 목록에서 제거한다.
    std::string content = downloaded_pages-&gt;front();
    downloaded_pages-&gt;pop();
    (*num_processed)++;
    m-&gt;unlock();

    // content 를 처리한다.
    std::cout &lt;&lt; content;
    std::this_thread::sleep_for(std::chrono::milliseconds(80));
  }
}

int main() {
  // 현재 다운로드한 페이지들 리스트로, 아직 처리되지 않은 것들이다.
  std::queue&lt;std::string&gt; downloaded_pages;
  std::mutex m;
  std::vector&lt;std::thread&gt; producers;

  for (int i = 0; i &lt; 5; i++) {
    producers.push_back(std::thread(producer, &amp;downloaded_pages, &amp;m, i + 1));
  }

  int num_processed = 0;
  std::vector&lt;std::thread&gt; consumers;

  for (int i = 0; i &lt; 3; i++) {
    consumers.push_back(
    std::thread(consumer, &amp;downloaded_pages, &amp;m, &amp;num_processed));
  }

  for (int i = 0; i &lt; 5; i++) {
    producers[i].join();
  }

  for (int i = 0; i &lt; 3; i++) {
    consumers[i].join();
  }
}</code></pre>
<p>producer: 인터넷 페이지를 다운로드 받음(큐에 집어넣음)
consumer: 다운로드받은 인터넷 페이지를 처리함</p>
<p>여기에서 consumer 스레드가 10ms마다 downloaded_pages에 할 일이 있는 지 확인하고 없으면 다시 기다리는 형태를 취하고 있음. -&gt; 비효율적임!</p>
<p>-&gt; producer에서 데이터가 뜸하게 오는 것을 알면 그냥 consumer는 아예 재워놓고, producer에서 데이터가 오면 consumer를 깨우는 방식이 나을 수 있음. 쓰레드를 재워놓으면 다른 쓰레드들이 일을 할 수 있기 때문(CPU 효율적으로 사용)</p>
<h3 id="condition_variable">condition_variable</h3>
<p>downloaded_pages 가 empty() 가 참이 아닐 때 까지 자라 라는 명령을 내릴 수 있음.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 쓰레드 -1]]></title>
            <link>https://velog.io/@tera_geniel/C-%EC%93%B0%EB%A0%88%EB%93%9C-1</link>
            <guid>https://velog.io/@tera_geniel/C-%EC%93%B0%EB%A0%88%EB%93%9C-1</guid>
            <pubDate>Tue, 05 Jul 2022 04:53:45 GMT</pubDate>
            <description><![CDATA[<h1 id="c-쓰레드">C++ 쓰레드</h1>
<p>작업 관리자를 실행하면, 막대한 양의 프로세스가 나오는 것을 볼 수 있음</p>
<blockquote>
<p>프로세스: 운영체제에서 실행되는 프로그램의 최소 단위, CPU의 코어에서 실행됨.</p>
</blockquote>
<p>1개의 프로그램을 가리킬 때 보통 1개의 프로세스를 의미함.</p>
<h3 id="컨텍스트-스위칭">컨텍스트 스위칭</h3>
<p>CPU는 한 프로그램을 통째로 쭉 실행시키는 게 아니라, 이 프로그램 조금, 저 프로그램 조금씩 골라서 차례를 돌며 실행시킨다.</p>
<blockquote>
<p><strong>운영체제의 스케줄러</strong>가 프로그램에서 프로그램으로 스위치할 때 &quot;어느 프로그램으로&quot; 스위치할 지 결정하는 역할을 한다.</p>
</blockquote>
<h3 id="쓰레드">쓰레드</h3>
<ul>
<li><strong>쓰레드</strong>: CPU 코어에서 돌아가는 프로그램 단위</li>
<li>CPU 코어 하나에 한번에 한 개의 쓰레드의 명령을 실행</li>
<li>1개의 프로세스는 최소 1개~ 여러 개의 쓰레드로 이루어짐
-&gt; 멀티 쓰레드(multi thread) 프로그램 : 여러개의 쓰레드로 구성된 프로그램</li>
</ul>
<h4 id="쓰레드와-프로세스의-차이점">쓰레드와 프로세스의 차이점</h4>
<p>프로세스들은 서로 메모리를 공유하지 않음.
프로세스 1, 프로세스 2 중 프로세스1은 프로세스2의 메모리에 접근 불가,
프로세스2도 프로세스 1의 메모리에 접근 불가.</p>
<p>프로세스는 서로의 메모리를 접근 불가, 같은 <strong>프로세스 내 쓰레드끼리는 메모리를 공유함.</strong>
한 프로세스 내에 쓰레드 1, 쓰레드 2가 있다면 서로 같은 메모리를 공유함.</p>
<h2 id="왜-멀티쓰레드인가">왜 멀티쓰레드인가?</h2>
<h4 id="병렬-가능한parallelizable-작업들">병렬 가능한(Parallelizable) 작업들</h4>
<p>프로그램 논리 구조 상 연산들 간의 의존 관계가 많을수록 병렬화가 어려워지고,
반대로 <strong>다른 연산의 결과와 관계없이 독립적으로 수행할 수 있는 구조가 많을수록 병렬화가 매우 쉬워짐</strong>.</p>
<h4 id="대기시간이-긴-작업들">대기시간이 긴 작업들</h4>
<p>예를 들어, 웹사이트를 다운받는 작업을 한다고 치면, 아래와 같이 쓰레드 1개만을 사용할 때와 달리 쓰레드 여러개를 사용할 때가 더 효율적으로 작업을 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/ec280733-fdd8-45ae-ad5f-df84c1b66a80/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/a90e898c-4371-4125-bbdc-47347fce9c05/image.png" alt=""></p>
<h1 id="c에서-쓰레드-생성하기">C++에서 쓰레드 생성하기</h1>
<p>C++11에서부터 표준에 쓰레드가 추가되면서, 쓰레드 사용이 매우 편리해졌음.</p>
<p>멀티 쓰레드 프로그램을 만들어보자.</p>
<pre><code class="language-cpp">// 내 생에 첫 쓰레드
#include &lt;iostream&gt;
#include &lt;thread&gt;

using std::thread;

void func1() {
  for (int i = 0; i &lt; 10; i++) {
    std::cout &lt;&lt; &quot;쓰레드 1 작동중! \n&quot;;
  }
}
void func2() {
  for (int i = 0; i &lt; 10; i++) {
    std::cout &lt;&lt; &quot;쓰레드 2 작동중! \n&quot;;
  }
}
void func3() {
  for (int i = 0; i &lt; 10; i++) {
    std::cout &lt;&lt; &quot;쓰레드 3 작동중! \n&quot;;
  }
}

int main() {
  // 아래와 같이 thread 객체를 생성
  thread t1(func1);
  thread t2(func2);
  thread t3(func3);
  t1.join();
  t2.join();
  t3.join();
}
</code></pre>
<p>이때, 결과는 늘 그때마다 다르다는 것을 확인할 수 있다.
프로그램을 실행할 때마다 그 결과가 달라진다는 게 가장 재밌는 점이다.
운영체제가 쓰레드들을 어떤 코어에 할당하고, 또 어떤 순서로 스케줄할 지는 그때그때마다 상황에 맞게 바뀌기 때문에 그 결과를 정확히 예측할 수 없다(주의).</p>
<blockquote>
<p>join: 해당하는 쓰레드들이 실행을 종료하면 리턴하는 함수</p>
</blockquote>
<h3 id="join과-detach">join과 detach</h3>
<p>join되거나 detach되지 않는 쓰레드들의 소멸자가 호출하면 예외를 발생시킴
(main 함수 종료되면 쓰레드 객체들의 소멸자가 호출, 스레드 부르고 join 호출 안하면 예외 발생)</p>
<h4 id="detach">detach</h4>
<p>해당 스레드를 실행시킨 후, 잊어버리는 것
(메인함수 종료 후에도 백그라운드에서 실행됨)</p>
<h2 id="쓰레드에-인자-전달하기">쓰레드에 인자 전달하기</h2>
<p>쓰레드는 리턴값이란 게 없기 때문에 만약 어떤 결과를 반환하고 싶으면 포인터의 형태로 전달하면 됨.</p>
<p>이때, 컨텍스트 스위치가 되는 그 변수를 출력하고 싶으면 cout 이 아닌 printf 를 사용해야 함
&lt;- 컨텍스트 스위치가 되더라도 다른 쓰레드들이 그 사이에 메세지를 집어넣지 못하게 막기 때문</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수 객체]]></title>
            <link>https://velog.io/@tera_geniel/%ED%95%A8%EC%88%98-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@tera_geniel/%ED%95%A8%EC%88%98-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Tue, 28 Jun 2022 07:48:14 GMT</pubDate>
            <description><![CDATA[<h1 id="callable">Callable</h1>
<blockquote>
<p><strong>Callable</strong>: 호출할 수 있는 모든 것</p>
</blockquote>
<p>람다 함수, 함수 객체 등 역시 callable이라고 할 수 있음.</p>
<h2 id="stdfunction">std::function</h2>
<p>callable들을 객체의 형태로 보관할 수 있는 std::function이라는 클래스를 제공함.</p>
<pre><code class="language-cpp">#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;

int some_func1(const std::string&amp; a) { 
  std::cout &lt;&lt; &quot;Func1 호출! &quot; &lt;&lt; a &lt;&lt; std::endl; 
  return 0;
}

struct S {
void operator()(char c) { std::cout &lt;&lt; &quot;Func2 호출! &quot; &lt;&lt; c &lt;&lt; std::endl; }
};

int main() {
  std::function&lt;int(const std::string&amp;)&gt; f1 = some_func1;
  // std::function&lt;리턴타입(인자타입)&gt; 함수이름 = 진짜함수이름;
  std::function&lt;void(char)&gt; f2 = S();
  std::function&lt;void()&gt; f3 = []() { std::cout &lt;&lt; &quot;Func3 호출! &quot; &lt;&lt; std::endl; };
  f1(&quot;hello&quot;);
  f2(&#39;c&#39;);
  f3();
}</code></pre>
<h3 id="멤버-함수를-가지는-stdfunction">멤버 함수를 가지는 std::function</h3>
<pre><code class="language-cpp">#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;

class A { 
  int c;
public:
  A(int c) : c(c) {}
  int some_func() {
    std::cout &lt;&lt; &quot;비상수 함수: &quot; &lt;&lt; ++c &lt;&lt; std::endl;
    return c;
  }
  int some_const_function() const {
    std::cout &lt;&lt; &quot;상수 함수: &quot; &lt;&lt; c &lt;&lt; std::endl; return c;
  }
  static void st() {} 
};

int main() { 
  A a(5);
  // 원래 인자에 추가적으로 개게를 받는 인자를 전달
  // 상수 함수: 상수 형태로 인자를 받아야 함, 상수 함수가 아니면 단순히 A&amp;로 받으면 됨.
  std::function&lt;int(A&amp;)&gt; f1 = &amp;A::some_func;
  std::function&lt;int(const A&amp;)&gt; f2 = &amp;A::some_const_function;
  f1(a);
  f2(a); 
}</code></pre>
<p>이때 const 함수는 이 함수 안에서는 어떤 변수도 바꿀 수 없음(mutable은 예외)를 뜻한다.
멤버 함수들의 경우 암시적 변환이 발생하지 않으므로 &amp; 연산자를 통해 명시적으로 주소값을 전달해줘야 함..</p>
<h3 id="멤버-함수들을-함수-객체로---mem_fn">멤버 함수들을 함수 객체로 - mem_fn</h3>
<p>vector들을 가지는 vector가 있을 때, 각각의 vector들의 크기들을 벡터로 만들어주는 코드를 생각해 보자.</p>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;functional&gt; 
#include &lt;iostream&gt; 
#include &lt;vector&gt; 

using std::vector;

int main() { 
  vector&lt;int&gt; a(1); 
  vector&lt;int&gt; b(2); 
  vector&lt;int&gt; c(3); 
  vector&lt;int&gt; d(4);

  vector&lt;vector&lt;int&gt;&gt; container; container.push_back(b);
  container.push_back(d);
  container.push_back(a);
  container.push_back(c);

  vector&lt;int&gt; size_vec(4);

  //std::transform(container.begin(), container.end(), size_vec.begin(),
  &amp;vector&lt;int&gt;::size); // 에러 코드
  // std::function&lt;size_t(const vector&lt;int&gt;&amp;)&gt; sz_func = &amp;vector&lt;int&gt;::size;

  transform(container.begin(), container.end(), size_vec.begin(),
std::mem_fn(&amp;vector&lt;int&gt;::size)); // mem_fn으로 사용 가능

  for (auto itr = size_vec.begin(); itr != size_vec.end(); ++itr) {
    std::cout &lt;&lt; &quot;벡터 크기 :: &quot; &lt;&lt; *itr &lt;&lt; std::endl; 
  }
}</code></pre>
<p>function 객체를 리턴해버리는 함수를 추가함
mem_fn은 이름 그대로, 전달된 멤버 함수를 function 객체로 만들어서 리턴해줌.</p>
<h2 id="stdbind">std::bind</h2>
<p>함수 객체 생성 시에 인자를 특정한 것으로 지정할 수 있음.</p>
<blockquote>
<p>bind 함수는 원래 함수에 특정 인자를 bind해(붙여) 줌.</p>
</blockquote>
<pre><code class="language-cpp">#include &lt;functional&gt;
#include &lt;iostream&gt;

void add(int x, int y) {
  std::cout &lt;&lt; x &lt;&lt; &quot; + &quot; &lt;&lt; y &lt;&lt; &quot; = &quot; &lt;&lt; x + y &lt;&lt; std::endl;
}
void subtract(int x, int y) {
  std::cout &lt;&lt; x &lt;&lt; &quot; - &quot; &lt;&lt; y &lt;&lt; &quot; = &quot; &lt;&lt; x - y &lt;&lt; std::endl;
}

int main() {
  auto add_with_2 = std::bind(add, 2, std::placeholders::_1); 
  add_with_2(3);
  // 두 번째 인자는 무시된다. 
  add_with_2(3, 4);

  auto subtract_from_2 = std::bind(subtract, std::placeholders::_1, 2); 
  auto negate =
       std::bind(subtract, std::placeholders::_2, std::placeholders::_1);

  subtract_from_2(3); // 3 - 2 를 계산한다.
  negate(4, 2); // 2 - 4 를 계산한다 
}</code></pre>
<p>이때 bind 인자로 특정 객체를 지정하고 싶다면, 아래와 같이 직접 레퍼런스를 명시적으로 전달해줘야 함.
(만약 명시적으로 레퍼런스를 전달하지 않는다면, 복사 생성자가 호출됨)</p>
<pre><code class="language-cpp">#include &lt;functional&gt;
#include &lt;iostream&gt;

struct S {
  int data;
  S(int data) : data(data) { std::cout &lt;&lt; &quot;일반 생성자 호출!&quot; &lt;&lt; std::endl; } 
  S(const S&amp; s) {
    std::cout &lt;&lt; &quot;복사 생성자 호출!&quot; &lt;&lt; std::endl; data = s.data;
  }
  S(S&amp;&amp; s) {
    std::cout &lt;&lt; &quot;이동 생성자 호출!&quot; &lt;&lt; std::endl; data = s.data;
  } 
};

void do_something(S&amp; s1, const S&amp; s2) { 
  s1.data = s2.data + 3; 
} 

int main() {
   S s1(1), s2(2);
   std::cout &lt;&lt; &quot;Before : &quot; &lt;&lt; s1.data &lt;&lt; std::endl;
  // s1 이그대로전달된것이아니라s1 의복사본이전달됨! 
   auto do_something_with_s1 =
       std::bind(do_something, std::ref(s1), std::placeholders::_1);
   do_something_with_s1(s2);
   std::cout &lt;&lt; &quot;After :: &quot; &lt;&lt; s1.data &lt;&lt; std::endl;
}</code></pre>
<p>ref 함수는 전달받은 인자를 복사 가능한 레퍼런스로 변환해 주어 s1의 레퍼런스가 잘 전달될 수 있게 함(bind 함수 안으로).</p>
<p>const 레퍼런스의 경우 cref 함수를 호출하면 됨.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스마트 포인터]]></title>
            <link>https://velog.io/@tera_geniel/%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0</link>
            <guid>https://velog.io/@tera_geniel/%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0</guid>
            <pubDate>Mon, 27 Jun 2022 04:18:44 GMT</pubDate>
            <description><![CDATA[<p>사용이 끝난 자원은 반드시 반환을 해서 다른 작업 때 사용할 수 있도록 해야 합니다. 메모리를 할당만 하고 해제를 하지 않는다면, 결국 메모리 부족으로 프로그램이 crash 될 수도 있음.</p>
<p>C++은 가비지 콜렉터가 없음 -&gt; 한번 획득한 자원은 직접 해제해주지 않는 이상 프로그램이 종료되기 전까지 영원히 남아있게 됨.</p>
<p>예를 들어,</p>
<pre><code class="language-cpp">#include &lt;iostream&gt; class A {
  int *data; 
public:
  A() {
    data = new int[100];
    std::cout &lt;&lt; &quot;자원을 획득함!&quot; &lt;&lt; std::endl;
  }
  ~A() {
    std::cout &lt;&lt; &quot;소멸자 호출!&quot; &lt;&lt; std::endl; 
    delete[] data;
  } 
};
void do_something() {
  A *pa = new A();
} 

int main() {
   do_something();
  // 할당된 객체가 소멸되지 않음!
  // 즉, 400 바이트(4 * 100) 만큼의메모리누수발생 
}

/* [출력]
자원을 획득함!
*/
</code></pre>
<p>자원을 획득만 하고, 소멸자는 호출되지 않음.
-&gt; <code>delete *pa</code>를 안해서 생성된 객체의 주소값을 가지는 포인터는 메모리 상에 존재하지 않게 됨.</p>
<p>또한, 아래와 같이 thrower()로 발생된 예외로 delete pa가 실행되지 않고 넘어가면 메모리 누수가 생기게 됨.</p>
<pre><code class="language-cpp">void thrower() { 
  // 예외를 발생시킴! 
  throw 1;
}
void do_something() { 
  A *pa = new A(); thrower();
  // 발생된 예외로 인해 delete pa 가 호출되지 않는다!
  delete pa; 
}</code></pre>
<h3 id="resource-acquisition-is-initialization---raii">Resource Acquisition Is Initialization - RAII</h3>
<p>RAII 패턴: 자원의 획득은 초기화다 - * Resource Acquisition Is Initialization *</p>
<blockquote>
<p>자원의 관리를 스택에 할당한 객체를 통해 수행하는 것</p>
</blockquote>
<p><strong>stack unwinding</strong> : 예외가 발생해서 함수를 빠져나가도, 그 함수의 스택에 정의되어 있는 모든 객체들은 빠짐없이 소멸자가 호출된다. -&gt; 예외가 발생하지 않으면 함수가 종료될 때 당연히 소멸자들이 호출됨.</p>
<p>이 개념을 이용해서, <strong>포인터 객체</strong>라는 걸 도입하면, 함수가 종료되며 쌓인 객체 스택에서 소멸자들이 차례로 불리면서 포인터 자체와 포인터가 가리키는 객체 자체도 소멸될 수 있다(포인터 객체 내 소멸자에 가리키는 객체를 소멸하는 명령어 존재).</p>
<h1 id="객체의-유일한-소유권---unique_ptr">객체의 유일한 소유권 - unique_ptr</h1>
<p>C++에서 메모리를 잘못된 방식으로 관리하는 경우 다음 두가지 종류의 문제점이 발생함</p>
<ul>
<li>메모리 사용 후 해제하지 않은 경우: 메모리 누수(memory leak)
-&gt; 시스템 메로리가 부족해져서 서버가 죽어버리는 상황이 발생 가능
=&gt; RAII 패턴을 사용하면 해결 가능</li>
<li>이미 해제된 메모리를 다시 참조하는 경우
-&gt; double free bug: 이미 소멸된 객체를 다시 소멸시켜서 발생하는 버그</li>
</ul>
<p>2번째 문제점인 double free bug 는 어떤 포인터에 객체의 소유권을 유일하게 부여해서 해결 가능.
-&gt; <code>unique_ptr</code>: 특정 객체에 유일한 소유권을 부여하는 포인터 객체</p>
<p>++ unique_ptr로 pa를 스택에 정의된 객체로 만들기 때문에, RAII 패턴 역시 자동 사용 가능.</p>
<pre><code class="language-cpp">std::unique_ptr&lt;A&gt; pa(new A());
pa-&gt;some();

// 아래와 동일
A* pa = new A();</code></pre>
<p>unique_ptr은 -&gt; 을 오버로드해서 마치 포인터를 다루는 것과 같이 사용할 수 있게 함.</p>
<h3 id="삭제된-함수">삭제된 함수</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
class A { 
public:
  A(int a){};
  A(const A&amp; a) = delete; 
  // 복사 생성자를 호출하는 부분에서 오류가 발생함
};

int main() {
  A a(3); // 가능
  A b(a); // 불가능 (복사 생성자는 삭제됨)
}</code></pre>
<p>컴파일하게 되면 복사 생성자를 호출하는 부분에서 오류가 발생함.
-&gt; 복사 생성자를 명시적으로 삭제했기 때문</p>
<p>이와 같이, unique_ptr도 복사 생성자가 명시적으로 삭제됨.
-&gt; unique_ptr는 어떠한 객체를 유일하게 소유해야 하기 때문!</p>
<p>unique_ptr를 복사 생성할 수 있다면, 특정 객체를 여러 unique_ptr들이 소유하게 되는 문제가 발생함.
각각의 unique_ptr들이 소멸될 때 전부 객체를 delete하려고 하기 때문에 double free bug가 발생함.</p>
<h2 id="unique_ptr-소유권-이전하기">unique_ptr 소유권 이전하기</h2>
<p>소유권은 이전이 가능: 이동 생성자 사용</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;
class A { 
  int *data;
public: 
  A() {
    std::cout &lt;&lt; &quot;자원을 획득함!&quot; &lt;&lt; std::endl;
    data = new int[100]; 
  }
  void some() { std::cout &lt;&lt; &quot;일반 포인터와 동일하게 사용가능!&quot; &lt;&lt; std::endl; }
  ~A() {
    std::cout &lt;&lt; &quot;자원을 해제함!&quot; &lt;&lt; std::endl; delete[] data;
  } 
};

void do_something() {
std::unique_ptr&lt;A&gt; pa(new A());
std::cout &lt;&lt; &quot;pa : &quot;; 
pa-&gt;some();

// pb 에 소유권을 이전.
std::unique_ptr&lt;A&gt; pb = std::move(pa);
std::cout &lt;&lt; &quot;pb : &quot;;
pb-&gt;some();

}

int main() { do_something(); }
</code></pre>
<p>unique_ptr은 복사 생성자는 정의되어 있지 않지만, 이동 생성자는 가능하다.
마치 소유권을 이동시킨다라는 개념으로 생각하면 됨.</p>
<blockquote>
<p>소유권 이동 이후 기존 unique_ptr을 접근하지 않도록 조심해야 함.</p>
</blockquote>
<h2 id="unique_ptr를-함수-인자로-전달하기">unique_ptr를 함수 인자로 전달하기</h2>
<p>unique_ptr를 함수 인자로 전달하고 싶다면: unique_ptr는 복사 생성자가 없음.
함수에 레퍼런스로 전달하면?</p>
<p>-&gt; 소유권이라는 의미가 사라지게 됨...</p>
<h3 id="get-함수">get 함수</h3>
<pre><code class="language-cpp">void do_something(A* ptr) { ptr-&gt;do_sth(3); }

int main() {
  std::unique_ptr&lt;A&gt; pa(new A()); 
  do_something(pa.get());
  // 실제 객체의 주소값을 리턴해 줌: 일반적인 포인터를 받을 수 있음.
}
</code></pre>
<p>소유권이라는 의미는 버린 채, do_something 함수 내부에서 객체로 접근 권한을 주는 것.</p>
<h2 id="unique_ptr을-쉽게-생성하기">unique_ptr을 쉽게 생성하기</h2>
<pre><code class="language-cpp">std::unique_ptr&lt;Foo&gt; ptr(new Foo(3, 5));</code></pre>
<p>unique_ptr로 완벽한 전달을 수행함.</p>
<h3 id="unique_ptr를-원소로-가지는-컨테이너">unique_ptr를 원소로 가지는 컨테이너</h3>
<p>기본적으로 vector의 push_back 함수는 전달된 인자를 <strong>복사해서</strong> 집어넣기 때문에 컴파일 때 에러가 발생하게 됨.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;vector&gt;

class A { 
  int *data;

public: 
  A(int i) {
    std::cout &lt;&lt; &quot;자원을 획득함!&quot; &lt;&lt; std::endl; data = new int[100];
    data[0] = i;
  }

  void some() { 
    std::cout &lt;&lt; &quot;일반 포인터와 동일하게 사용가능!&quot; &lt;&lt; std::endl; 
  }
  ~A() {
    std::cout &lt;&lt; &quot;자원을 해제함!&quot; &lt;&lt; std::endl; 
    delete[] data;
  } 
};

int main() { 
  std::vector&lt;std::unique_ptr&lt;A&gt;&gt; vec; 
  std::unique_ptr&lt;A&gt; pa(new A(1));
  vec.push_back(pa); // ?? 
}</code></pre>
<p>따라서 아래와 같이 push_back을 사용할 때 move로 명시적으로 이동 생성자 이용!</p>
<pre><code class="language-cpp">int main() { 
  std::vector&lt;std::unique_ptr&lt;A&gt;&gt; vec;
  std::unique_ptr&lt;A&gt; pa(new A(1));
  vec.push_back(std::move(pa)); // 잘 실행됨 
}
</code></pre>
<p>emplace_back 함수를 이용하면 vector 안에 unique_ptr를 직접 생성해서 넣을 수 있음.</p>
<h1 id="여러-객체가-소유할-수-있는-포인터">여러 객체가 소유할 수 있는 포인터</h1>
<p>특정 자원을 몇 개의 객체에서 가리키는지를 추적한 다음에, 그 수가 0 이 되야만 비로소 해제를 시켜주는 방식의 포인터</p>
<h2 id="shared_ptr">shared_ptr</h2>
<p>객체를 소유하는 unique_ptr와는 다르게, shared_ptr로 객체로 가리킬 경우, 다른 shared_ptr 역시 그 객체를 가리킬 수 있음.</p>
<p>참조 개수가 0이 되어야 가리키고 있는 객체를 해제할 수 있음.</p>
<p>현재 shared_ptr 의 참조 개수가 몇 개 인지는 use_count 함수를 통해 알 수 있음.</p>
<pre><code class="language-cpp">std::shared_ptr&lt;A&gt; p1(new A());
std::shared_ptr&lt;A&gt; p2(p1); // p2 역시 생성된 객체 A 를 가리킨다.

std::cout &lt;&lt; p1.use_count(); // 2 
std::cout &lt;&lt; p2.use_count(); // 2</code></pre>
<p>개개의 shared_ptr들은 참조 개수가 몇개인 지 알고 있어야 함.</p>
<p>그런데 아래와 같은 경우 p2의 참조 개수는 증가시켜도 p1에 저장된 참조 개수는 건드릴 수 없음.</p>
<pre><code class="language-cpp"> std::shared_ptr&lt;A&gt; p3(p2);</code></pre>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/c393d03a-5883-4eb5-ae9e-0f20ad522fbc/image.png" alt=""></p>
<p>-&gt; 이런 문제점 때문에</p>
<blockquote>
<p>실제 객체를 가리키는 shared_ptr가 제어 블록(control block)을 동적으로 할당하고 shared_ptr들이 제어 블록에 필요한 정보를 공유하는 방식으로 구현됨.</p>
</blockquote>
<p>shared_ptr는 복사 생성할 때마다 해당 제어 블록의 위치만 공유하고, shared_ptr가 소멸할 때마다 제어 블록의 참조 개수를 하나 줄이고, 생성 때마다 하나 늘리는 방식으로 작동함.</p>
<h3 id="생성">생성</h3>
<pre><code class="language-cpp">std::shared_ptr&lt;A&gt; p1(new A());</code></pre>
<p>위 생성 방법으로는 동적 할당이 2번 발생</p>
<ul>
<li>A를 생성하기 위해 동적 할당이 1번 일어나야 함</li>
<li>제어 블록을 동적으로 할당함</li>
</ul>
<p>동적 할당은 아예 두개 합친 크기로 한번 할당하는 게 훨씬 빠름 -&gt; 아래 참고</p>
<pre><code class="language-cpp">std::shared_ptr&lt;A&gt; p1 = std::make_shared&lt;A&gt;();</code></pre>
<h3 id="shared_ptr-생성-시-주의할-점">shared_ptr 생성 시 주의할 점</h3>
<p>shared_ptr은 <strong>인자로 주소값이 전달되면,</strong> 
마치 본인이 해당 객체를 첫번째로 소유하는 shared_ptr인 것처럼 행동함.</p>
<pre><code class="language-cpp">A* a = new A(); 
std::shared_ptr&lt;A&gt; pa1(a); 
std::shared_ptr&lt;A&gt; pa2(a);</code></pre>
<p>따라서 shared_ptr를 주소값을 통해 생성하는 걸 지양해야 함.
(이미 해제한 메모리를 또 해제한다는 오류가 날 수 있음)</p>
<p>또한, 순환 참조 문제가 발생할 수 있음(락).</p>
<h2 id="weak_ptr">weak_ptr</h2>
<p>위에서 말한 순환 참조 문제를 해결하기 위해 weak_ptr를 사용</p>
<ul>
<li>제어블록에는 참조 개수, 약한 참조 개수(weak count)를 기록함</li>
<li>weak_ptr 활용할 때 lock() 함수를 통해 수행(shared_ptr로 변환해야 함)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[우측값과 이동 연산]]></title>
            <link>https://velog.io/@tera_geniel/%EC%9A%B0%EC%B8%A1%EA%B0%92%EA%B3%BC-%EC%9D%B4%EB%8F%99-%EC%97%B0%EC%82%B0</link>
            <guid>https://velog.io/@tera_geniel/%EC%9A%B0%EC%B8%A1%EA%B0%92%EA%B3%BC-%EC%9D%B4%EB%8F%99-%EC%97%B0%EC%82%B0</guid>
            <pubDate>Mon, 20 Jun 2022 05:31:13 GMT</pubDate>
            <description><![CDATA[<h1 id="우측값-레퍼런스와-이동-생성자">우측값 레퍼런스와 이동 생성자</h1>
<h2 id="복사-생략copy-elision">복사 생략(Copy Elision)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;

class A {
  int data_;
public:
  A(int data) : data_(data) { std::cout &lt;&lt; &quot;일반 생성자 호출!&quot; &lt;&lt; std::endl; }
  A(const A&amp; a) : data_(a.data_) {
    std::cout &lt;&lt; &quot;복사 생성자 호출!&quot; &lt;&lt; std::endl;
  }
};
int main() {
  A a(1); // 일반 생성자 호출
  A b(a); // 복사 생성자 호출
  A c(A(2)); // ❓ 일반 생성자 호출
}</code></pre>
<p>위 코드에서, <code>A c(A(2)); // ❓ 일반 생성자 호출</code> 이 부분의
cpp에서는 복사 생성을 굳이 수행하지 않고, 임시로 만들어진 A(2)를 그냥 2로 취급해 일반 생성자만 호출하게 된다.</p>
<h2 id="좌측값과-우측값">좌측값과 우측값</h2>
<ul>
<li><code>좌측값</code>(lvalue): 주소값을 취할 수 있는 값(식의 왼오 모두 올 수 있음)</li>
<li><code>우측값</code>(rvalue): 주소값을 취할 수 없는 값(실체가 없는 값)</li>
</ul>
<h3 id="좌측값-레퍼런스lavlue-reference">좌측값 레퍼런스(lavlue reference)</h3>
<p>&amp; 하나를 이용해서 정의하는 레퍼런스
좌측값 레퍼런스 자체도 좌측값이 됨</p>
<pre><code class="language-cpp">int&amp; func1(int&amp; a) { return a; }
int func2(int b) { return b; }

int main() {
  int a = 3;
  func1(a) = 4;
  std::cout &lt;&lt; &amp;func1(a) &lt;&lt; std::endl;
  int b = 2;
  a = func2(b); // 가능
  func2(b) = 5; // 오류 1
  std::cout &lt;&lt; &amp;func2(b) &lt;&lt; std::endl; // 오류 2
}
</code></pre>
<p>func2(b)는 우측값, func1(a)는 좌측값에 해당하게 됨.</p>
<h2 id="우측값-레퍼런스-이용-이동-생성자">우측값 레퍼런스 이용: 이동 생성자</h2>
<p>이동 생성자를 이용하면 기존의 메모리에 원소들이 모두 이동됨.
따라서 똑같은 내용으로 메모리를 한번만 차지함(복사 생성자는 두번 차지).</p>
<p>우측값 레퍼런스를 사용한 이동 생성자의 정의 부분은 다음과 같다.</p>
<pre><code class="language-cpp">MyString::MyString(MyString&amp;&amp; str) {
  std::cout &lt;&lt; &quot;이동 생성자 호출 !&quot; &lt;&lt; std::endl;
  string_length = str.string_length;
  string_content = str.string_content;
  memory_capacity = str.memory_capacity;
  // 임시 객체 소멸 시에 메모리를 해제하지
  // 못하게 한다.
  str.string_content = nullptr;
}</code></pre>
<p><strong>우측값의 레퍼런스를 정의하기 위해서 좌측값과는 달리 &amp;를 2개 사용해서 정의해야 함.</strong>
여기서 위 생성자의 경우 MyString 타입의 우측값을 인자로 받음.
str은 &quot;타입이 &lt;MyString의 우측값 레퍼런스&gt;인 <em>좌측값</em>&quot;이라고 보면 됨.
-&gt; 표현식의 좌측에 올 수도 있음.</p>
<h3 id="null-ptr-메모리-해제">null ptr 메모리 해제?</h3>
<h3 id="우측값-레퍼런스의-예시">우측값 레퍼런스의 예시</h3>
<pre><code class="language-cpp">int a;
int&amp; l_a = a;
int&amp; ll_a = 3; // 불가능
int&amp;&amp; r_b = 3;
int&amp;&amp; rr_b = a; // 불가능</code></pre>
<h3 id="이동-생성자-작성-시-주의할-점-noexcept">이동 생성자 작성 시 주의할 점, noexcept</h3>
<p>이동 생성 과정에서 예외가 발생하는 경우, 기존의 메모리에 원소들이 모두 이동되어 사라져서 새로 할당한 메모리를 섯불리 해제할 수 없음.</p>
<p>-&gt; vector 및 cpp의 다른 컨테이너들은 이동 생성자가 <code>noexcept</code>가 아닌 이상 이동 생성자를 사용하지 않음.</p>
<p>=&gt; <code>noexcept</code> 추가하면 이동 생성자 사용.</p>
<pre><code class="language-cpp">MyString::MyString(MyString &amp;&amp;str) noexcept {
  std::cout &lt;&lt; &quot;이동 생성자 호출 !&quot; &lt;&lt; std::endl;
  string_length = str.string_length;
  string_content = str.string_content;
  memory_capacity = str.memory_capacity;
  // 임시 객체 소멸 시에 메모리를 해제하지
  // 못하게 한다.
  str.string_content = nullptr;
}</code></pre>
<h2 id="move-문법과-완벽한-전달">move 문법과 완벽한 전달</h2>
<p>좌측값이 우측값으로 취급될 수 있게 바꿔주는 함수: move 함수</p>
<h3 id="move-semantics">move semantics</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;utility&gt;

class A {
public:
  A() { std::cout &lt;&lt; &quot;일반 생성자 호출!&quot; &lt;&lt; std::endl; }
  A(const A&amp; a) { std::cout &lt;&lt; &quot;복사 생성자 호출!&quot; &lt;&lt; std::endl; }
  A(A&amp;&amp; a) { std::cout &lt;&lt; &quot;이동 생성자 호출!&quot; &lt;&lt; std::endl; }
};

int main() {
  A a;  // 일반
  std::cout &lt;&lt; &quot;---------&quot; &lt;&lt; std::endl;
  A b(a); // 복사
  std::cout &lt;&lt; &quot;---------&quot; &lt;&lt; std::endl;
  A c(std::move(a)); // 이동
}</code></pre>
<p>std::move 함수가 인자로 받은 객체를 우측값으로 변환해서 리턴해 줌.
실제로는 단순한 타입 변환만 수행함(즉 우측값을 받는 함수들이 오버로딩되며 수행되는 것).</p>
<blockquote>
<p>std::move는 이동은 수행하지 않음!</p>
</blockquote>
<h2 id="완벽한-전달perfect-forwarding">완벽한 전달(perfect forwarding)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

template &lt;typename T&gt;
void wrapper(T u) {
  g(u);
}
class A {};

void g(A&amp; a) { std::cout &lt;&lt; &quot;좌측값 레퍼런스 호출&quot; &lt;&lt; std::endl; }

void g(const A&amp; a) { std::cout &lt;&lt; &quot;좌측값 상수 레퍼런스 호출&quot; &lt;&lt; std::endl; }

void g(A&amp;&amp; a) { std::cout &lt;&lt; &quot;우측값 레퍼런스 호출&quot; &lt;&lt; std::endl; }

int main() {
  A a;
  const A ca;
  std::cout &lt;&lt; &quot;원본 --------&quot; &lt;&lt; std::endl;
  g(a);
  g(ca);
  g(A());
  std::cout &lt;&lt; &quot;Wrapper -----&quot; &lt;&lt; std::endl;
  wrapper(a);
  wrapper(ca);
  wrapper(A());
}

/*
[출력]
원본 --------
좌측값 레퍼런스 호출
좌측값 상수 레퍼런스 호출
우측값 레퍼런스 호출
Wrapper -----
좌측값 레퍼런스 호출
좌측값 레퍼런스 호출
좌측값 레퍼런스 호출
*/</code></pre>
<p>위와 같이 출력된 이유는 C++ 컴파일러가 템플릿 타입을 추론할 때, 템플릿 인자 R가 레퍼런스가 아닌 일반적인 타입이라면 const를 무시하기 때문이다.</p>
<p>-&gt; 즉, T가 전부 다 class A로 추론됨</p>
<p>따라서 아래와 같이 모든 조합의 템플릿 함수들을 정의해 주면 된다.</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void wrapper(T&amp; u, T&amp; v) {
  g(u, v);
}

template &lt;typename T&gt;
void wrapper(const T&amp; u, T&amp; v) {
  g(u, v);
}

template &lt;typename T&gt;
void wrapper(T&amp; u, const T&amp; v) {
  g(u, v);
}

template &lt;typename T&gt;
void wrapper(const T&amp; u, const T&amp; v) {
  g(u, v);
}</code></pre>
<h3 id="보편적-레퍼런스universal-reference">보편적 레퍼런스(Universal reference)</h3>
<p>forward 키워드를 사용하면 위에서 거쳤던 복잡한 오버로딩(모든 조합에 대한 오버로딩)을 생략할 수 있음.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

template &lt;typename T&gt;
void wrapper(T&amp;&amp; u) {
  g(std::forward&lt;T&gt;(u));
}

class A {};

void g(A&amp; a) { std::cout &lt;&lt; &quot;좌측값 레퍼런스 호출&quot; &lt;&lt; std::endl; }
void g(const A&amp; a) { std::cout &lt;&lt; &quot;좌측값 상수 레퍼런스 호출&quot; &lt;&lt; std::endl; }
void g(A&amp;&amp; a) { std::cout &lt;&lt; &quot;우측값 레퍼런스 호출&quot; &lt;&lt; std::endl; }

int main() {
  A a;
  const A ca;
  std::cout &lt;&lt; &quot;원본 --------&quot; &lt;&lt; std::endl;
  g(a);
  g(ca);
  g(A());
  std::cout &lt;&lt; &quot;Wrapper -----&quot; &lt;&lt; std::endl;
  wrapper(a);
  wrapper(ca);
  wrapper(A());
}</code></pre>
<h4 id="레퍼런스-겹침-규칙">레퍼런스 겹침 규칙</h4>
<pre><code class="language-cpp">#include &lt;iostream&gt;

void show_value(int&amp;&amp; t) { 
    std::cout &lt;&lt; &quot;우측값 : &quot; &lt;&lt; t &lt;&lt; std::endl;
}

int main() {
    show_value(5); // 우측값 ok!
    int x = 3;
    show_value(x); // 애러 
}</code></pre>
<p>이때 <code>int&amp;&amp; t</code>는 우측값 외에는 못 받음.</p>
<blockquote>
<p>보편적 레퍼런스(Universal reference): 템플릿 인자 T 에 대해서, 우측값 레퍼런스를 받는 형태</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++에서의 예외 처리]]></title>
            <link>https://velog.io/@tera_geniel/C%EC%97%90%EC%84%9C%EC%9D%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@tera_geniel/C%EC%97%90%EC%84%9C%EC%9D%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 20 Jun 2022 02:30:08 GMT</pubDate>
            <description><![CDATA[<h1 id="예외-처리">예외 처리</h1>
<h2 id="기존의-예외-처리-방식c">기존의 예외 처리 방식(C)</h2>
<p>기존에는 아래와 같이 if else문으로 오류 처리를 진행</p>
<pre><code class="language-cpp">char *c = (char *)malloc(1000000000);
if (c== null){
  printf(&quot;메모리 할당 오류!&quot;);
  return;
}</code></pre>
<h3 id="문제점">문제점</h3>
<p>함수가 쌓이고 딥하게 들어간 함수에서(함수1() -&gt; 함수2() -&gt; 함수3()인데 함수3에서) 예외 처리하는 경우, 함수가 복잡하게 쌓이고, 중간에 다른 요인으로 예외 처리를 하고 싶을 때 그걸 할 수 없다.</p>
<h2 id="throw로-예외-발생시키기">throw로 예외 발생시키기</h2>
<pre><code class="language-cpp">template &lt;typename T&gt;
class Vector {
public:
  Vector(size_t size) : size_(size) {
    data_ = new T[size_];
    for (int i = 0; i &lt; size_; i++) {
      data_[i] = 3;
    }
  }
  const T&amp; at(size_t index) const {
      // 💥여기 주목💥
    if (index &gt;= size_) {
      throw out_of_range(&quot;vector 의 index 가 범위를 초과하였습니다.&quot;);
    }
    return data_[index];
  }

  ~Vector() { delete[] data_; }
private:
  T* data_;
  size_t size_;

};</code></pre>
<p>throw로 &lt;예외로 전달하고 싶은 객체&gt;를 써주면 된다.</p>
<blockquote>
<p>throw한 위치에서 즉시 함수가 종료되고, 예외 처리하는 부분까지 점프함.
throw밑 문장은 모두 실행되지 앟음. 함수를 빠져나가며, stack에 생성된 객체들을 빠짐없이 소멸시켜 준다(소멸자만 제대로 작성하면).</p>
</blockquote>
<h2 id="예외-처리하기---try-catch">예외 처리하기 - try, catch</h2>
<pre><code class="language-cpp">int main() {
  Vector&lt;int&gt; vec(3);
  int index, data = 0;
  std::cin &gt;&gt; index;
  try {
    data = vec.at(index);
  } catch (std::out_of_range&amp; e) {
    std::cout &lt;&lt; &quot;예외 발생 ! &quot; &lt;&lt; e.what() &lt;&lt; std::endl;
  }
// 예외가 발생하지 않았다면 3을 이 출력되고, 예외가 발생하였다면 원래 data 에
// 들어가 있던 0 이 출력된다.
  std::cout &lt;&lt; &quot;읽은 데이터 : &quot; &lt;&lt; data &lt;&lt; std::endl;
}</code></pre>
<p>이때, out_of_range 클래스는 문자열 필드 하나 달랑 있고 이 역시 what() 함수로 그 값을 들여다 볼 수 있음.
위 경우 vector의 index가 범위를 초과했습니다가 나오게 됨</p>
<h2 id="스택-풀기stack-unwinding">스택 풀기(stack unwinding)</h2>
<blockquote>
<p>스택 풀기: catch로 점프하면서 스택 상에서 정의된 객체들을 소멸시키는 과정</p>
</blockquote>
<p>throw를 하게 되면 가장 가까운 catch로 점프한다</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;stdexcept&gt;

class Resource {
public:
  Resource(int id) : id_(id) {}
  ~Resource() { std::cout &lt;&lt; &quot;리소스 해제 : &quot; &lt;&lt; id_ &lt;&lt; std::endl; }
private:
  int id_;
};
int func3() {
  Resource r(3);
  throw std::runtime_error(&quot;Exception from 3!\n&quot;);
}
int func2() {
  Resource r(2);
  func3();
  std::cout &lt;&lt; &quot;실행 안됨!&quot; &lt;&lt; std::endl;
  return 0;
}
int func1() {
  Resource r(1);
  func2();
  std::cout &lt;&lt; &quot;실행 안됨!&quot; &lt;&lt; std::endl;
  return 0;
}
int main() {
  try {
    func1();
  } catch (std::exception&amp; e) {
    std::cout &lt;&lt; &quot;Exception : &quot; &lt;&lt; e.what();
  }
}
</code></pre>
<p><img src="https://velog.velcdn.com/images/tera_geniel/post/1e8263e7-9af7-4089-b964-48420f8cfb16/image.png" alt=""></p>
<p>생성자에서 예외가 발생할 때 소멸자가 호출되지 않음.</p>
<h2 id="여러-종류의-예외-받기">여러 종류의 예외 받기</h2>
<h3 id="여러-자료형으로-예외-받기">여러 자료형으로 예외 받기</h3>
<p>catch문은 아래와 같이 여러 종류의 자료형으로 받을 수 있음.</p>
<pre><code class="language-cpp">catch (char x) {
  std::cout &lt;&lt; &quot;Char : &quot; &lt;&lt; x &lt;&lt; std::endl;
}
catch (int x) {
  std::cout &lt;&lt; &quot;Int : &quot; &lt;&lt; x &lt;&lt; std::endl;
}
catch (std::string&amp; s) {
  std::cout &lt;&lt; &quot;String : &quot; &lt;&lt; s &lt;&lt; std::endl;
}
catch (const char* s) {
  std::cout &lt;&lt; &quot;String Literal : &quot; &lt;&lt; s &lt;&lt; std::endl;
}</code></pre>
<h3 id="기반-파생-클래스로-예외-받기">기반, 파생 클래스로 예외 받기</h3>
<p>예외는 순서대로 들어가므로
func(c)에서 예외로 Child가 발생해도 Patent&amp; p로 예외 처리가 진행된다(첫번쨰 catch문).</p>
<pre><code class="language-cpp">#include &lt;exception&gt;
#include &lt;iostream&gt;
class Parent : public std::exception {
public:
  virtual const char* what() const noexcept override { return &quot;Parent!\n&quot;; }
};
class Child : public Parent {
public:
  const char* what() const noexcept override { return &quot;Child!\n&quot;; }
};
int func(int c) {
  if (c == 1) {
    throw Parent();
  } else if (c == 2) {
    throw Child();
  }
  return 0;
}
int main() {
  int c;
  std::cin &gt;&gt; c;
  try {
    func(c);
  } catch (Parent&amp; p) {
    std::cout &lt;&lt; &quot;Parent Catch!&quot; &lt;&lt; std::endl;
    std::cout &lt;&lt; p.what();
  } catch (Child&amp; c) {
    std::cout &lt;&lt; &quot;Child Catch!&quot; &lt;&lt; std::endl;
    std::cout &lt;&lt; c.what();
  }
}
</code></pre>
<h2 id="모든-예외-받기">모든 예외 받기</h2>
<p>어떤 예외를 throw했는데, 이를 받는 catch가 없으면
<code>catch(...)</code> 쓰면 된다.</p>
<pre><code class="language-cpp">try {
  func(c);
} catch (int e) {
  std::cout &lt;&lt; &quot;Catch int : &quot; &lt;&lt; e &lt;&lt; std::endl;
} catch (...) {
// 여기서 try 안에서 발생한 모든 예외들을 받음. 어떤 예외도 다 받을 수 있어서 특정 타입을 찝어 객체에 대입시킬 수는 없음.
  std::cout &lt;&lt; &quot;Default Catch!&quot; &lt;&lt; std::endl;
}</code></pre>
<h3 id="예외를-발생시키지-않는-함수---noexcept">예외를 발생시키지 않는 함수 - noexcept</h3>
<p>어떤 함수가 예외를 안 발생시키면 noexcept를 통해 명시할 수 있음.</p>
<p><code>int foo() noexcept{}</code></p>
<p>foo 함수의 경우 예외를 발생시키지 않아 noexcept를 넣어서 나타낼 수 있음.
❗함수에 noexcept 키워드를 붙였다고 해서 함수가 예외를 절대로 던지지 않는 건 아님.</p>
<blockquote>
<p>noexcept 붙이는 이유: 컴파일러가 추가적인 최적화를 수행할 수 있음</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 표준 라이브러리(컨테이너와 알고리즘) -3]]></title>
            <link>https://velog.io/@tera_geniel/C-%ED%91%9C%EC%A4%80-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-3</link>
            <guid>https://velog.io/@tera_geniel/C-%ED%91%9C%EC%A4%80-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-3</guid>
            <pubDate>Mon, 13 Jun 2022 05:37:29 GMT</pubDate>
            <description><![CDATA[<h1 id="c-표준-알고리즘-라이브러리">C++ 표준 알고리즘 라이브러리</h1>
<p>알고리즘 라이브러리는 컨테이너에 반복자들을 가지고 이런 저런 작업을 쉽게 수행할 수 있도록 도와주는 라이브러리</p>
<p>크게 아래 두 형태를 띄고 있음</p>
<p><code>template &lt;typeName Iter&gt; void do_something(Iter begin, Iter end);</code>
<code>template &lt;typeName Iter, typeName Pred&gt; void do_something(Iter begin, Iter end, Pred pred);</code></p>
<p>전자: 알고리즘을 수행할 반복자의 시작점과 끝점 바로 뒤를 받음
후자: 반복자는 동일하게 받되, 특정 조건을 추가 인자로 받음(특정 조건: 서술자-Predicate).</p>
<blockquote>
<p>Predicate 자리에는 보통 bool 값을 리턴하는 함수 객체(Functor)가 오게 됨.</p>
</blockquote>
<h2 id="정렬sort-stable_sort-partial_sort">정렬(sort, stable_sort, partial_sort)</h2>
<ul>
<li><code>sort</code></li>
<li><code>stable_sort</code>
-&gt; 순서를 지킨 정렬. [a,b]순일 때, a==b인 경우 [a,b]로 순서 지킴
-&gt; sort보다 느림</li>
<li><code>partial_sort</code>
-&gt; 배열의 일부만 정렬</li>
</ul>
<h3 id="sort">sort</h3>
<p>sort는 기본적으로 오름차순으로 정렬해줌.
내림차순으로 정렬하고 싶으면 아래와 같이 functor를 사용해 비교를 어떻게 수행할 지 알려주면 됨.</p>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;iostream&gt;
#include &lt;vector&gt;

template &lt;typename Iter&gt;
void print(Iter begin, Iter end) {
  while (begin != end) {
    std::cout &lt;&lt; *begin &lt;&lt; &quot; &quot;; begin++;
  }
  std::cout &lt;&lt; std::endl;
}

struct int_compare {
  bool operator()(const int&amp; a, const int&amp; b) const { return a &gt; b; }
};

int main() {
  std::vector&lt;int&gt; vec;
  vec.push_back(5);
  vec.push_back(3);
  vec.push_back(1);
  vec.push_back(6);
  vec.push_back(4);
  vec.push_back(7);
  vec.push_back(2);

  std::cout &lt;&lt; &quot;정렬 전 ----&quot; &lt;&lt; std::endl;
  print(vec.begin(), vec.end());
  std::sort(vec.begin(), vec.end(), int_compare());
  std::cout &lt;&lt; &quot;정렬 후 ----&quot; &lt;&lt; std::endl;
  print(vec.begin(), vec.end());
}</code></pre>
<p>참고: functional 헤더의 <code>greater&lt;int&gt;</code> 참고</p>
<h3 id="partial_sort">partial_sort</h3>
<p>100명의 학생 중 상위 10명 학생의 성적순을 보고 싶다
-&gt; partial sort 사용</p>
<h4 id="시간적-복잡도-onlogm">시간적 복잡도: O(NlogM)</h4>
<p>이때 N개의 원소 중 M개만 순서대로 정렬하고 싶을 때,  NlogM 만큼의 시간만큼으로 수행됨.
따라서 모두 다 정렬하는 일반 sort(NlogN) &gt; partial_sort(NlogM) 임</p>
<h3 id="stable_sort">stable_sort</h3>
<p>stable_sort가 그냥 sort보다 시간이 더 걸림
최악의 경우 O(n(logn)^2)으로 작동함</p>
<h2 id="원소-제거">원소 제거</h2>
<h3 id="remove">remove</h3>
<p>원소를 제거하는 함수 remove.</p>
<p>remove 함수는 해당 값 v만을 삭제하도록 <strong>돕는</strong> 함수로,
원소의 이동만을 수행하지 실제로 원소를 삭제하는 연산을 수행하지는 않음.</p>
<blockquote>
<p>remove 함수가 끝나면 해당 값 v들을 뒤로 밀어버리고 밀어진 첫 v값 위치를 가리키는 반복자를 리턴함.</p>
</blockquote>
<pre><code class="language-cpp">// erase 쓰는 방법
Iterator erase(Iterator first, Iterator last);
// remove 사용해서 erase 쓰기
vec.erase(remove(vec.begin(), vec.end(), 3), vec.end());</code></pre>
<h3 id="remove_if">remove_if</h3>
<p>세번째 인자로 조건을 설명할 함수 객체를 전달받음.</p>
<pre><code class="language-cpp">template &lt;typename Iter, typename Pred&gt;
remove_if(Iter first, Iter last, Pred pred)</code></pre>
<p>함수 객체 대신 실제 함수를 전달할 수도 있음 - 아래 참고.</p>
<pre><code class="language-cpp">bool odd(const int&amp; i) {
  return i % 2 == 1; 
} 

int main() {
  std::vector&lt;int&gt; vec; 
  vec.push_back(5); 
  vec.push_back(3); 
  vec.push_back(1); 
  vec.push_back(2); 
  vec.push_back(3); 
  vec.push_back(4);
  std::cout &lt;&lt; &quot;처음 vec 상태 ------&quot; &lt;&lt; std::endl;

  print(vec.begin(), vec.end());
  std::cout &lt;&lt; &quot;벡터에서 홀수 인 원소 제거 ---&quot; &lt;&lt; std::endl;
  vec.erase(std::remove_if(vec.begin(), vec.end(), odd), vec.end()); 
  print(vec.begin(), vec.end());
}</code></pre>
<h4 id="remove_if에-조건-추가하기">remove_if에 조건 추가하기</h4>
<p>C++ 표준에 따르면, 함수 객체 안에 인스턴스 변수를 넣으면 안됨.
&lt;- 해당 함수 객체가 여러번 복사될 수 있기 때문</p>
<p>그렇다면 어떻게 하면 상태가 필요한 조건을 성공적으로 추가할 수 있을까?
-&gt; 외부 변수로 빼면 됨: 포인터!</p>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

template &lt;typename Iter&gt;
void print(Iter begin, Iter end) {
  while (begin != end) {
    std::cout &lt;&lt; &quot;[&quot; &lt;&lt; *begin &lt;&lt; &quot;] &quot;; begin++;
  }
  std::cout &lt;&lt; std::endl;
 }

struct is_odd { 
  int* num_delete;
  // **포인터로 넘김**
  is_odd(int* num_delete) : num_delete(num_delete) {}
  bool operator()(const int&amp; i) {
    if (*num_delete &gt;= 2) return false; 
    if (i % 2 == 1) {
      (*num_delete)++;
      return true; 
    }
    return false;
  }
};

int main() {
  std::vector&lt;int&gt; vec; 
  vec.push_back(5); 
  vec.push_back(3); 
  vec.push_back(1); 
  vec.push_back(2); 
  vec.push_back(3); 
  vec.push_back(4);
  std::cout &lt;&lt; &quot;처음 vec 상태 ------&quot; &lt;&lt; std::endl; 
  print(vec.begin(), vec.end());
  std::cout &lt;&lt; &quot;벡터에서 홀수인 원소 앞의 2개 제거 ---&quot; &lt;&lt; std::endl;
  int num_delete = 0;
  vec.erase(std::remove_if(vec.begin(), vec.end(), is_odd(&amp;num_delete)),
               vec.end());
  print(vec.begin(), vec.end());
}</code></pre>
<h2 id="람다-함수">람다 함수</h2>
<blockquote>
<p>람다 함수: 이름이 없는 함수 객체</p>
</blockquote>
<pre><code class="language-cpp">[capture list] (받는 인자) -&gt; 리턴 타입 { 함수 본체 }

// 리턴 타입을 생략할 경우
[capture list] ( 받는 인자) {함수 본체}</code></pre>
<p>문제는 람다 함수 외부에서 정의된 변수들 사용 불가
(나름 함수라서 자기 자신만의 스코프를 가지기 때문)</p>
<p>=&gt; <strong>캡쳐 목록</strong>을 사용하면 됨</p>
<h3 id="캡처-목록">캡처 목록</h3>
<p>어떤 변수를 캡쳐할 지 써주면 됨.
람다 함수 내에서 num_erased를 마치 같은 스코프 안에 있는 것처럼 사용할 수 있게 됨.</p>
<pre><code class="language-cpp">int main() { 
  std::vector&lt;int&gt; vec; 

  std::cout &lt;&lt; &quot;처음 vec 상태 ------&quot; &lt;&lt; std::endl; 
  print(vec.begin(), vec.end());
  std::cout &lt;&lt; &quot;벡터에서 홀수인 원소 2개 삭제---&quot; &lt;&lt; std::endl; 
  int num_erased = 0; 

  vec.erase(
    std::remove_if(
      vec.begin(),
      vec.end(),
      [&amp;num_erased](int i) {
      // 레퍼런스를 캡처해 와서 람다 함수 스코프 내에서도 변수 내용 바꿀 수 있게 함
        if (num_erased &gt;= 2)
          return false;
        else if (i % 2 == 1) {
          num_erased++;
          return true; 
        }
        return false; 
      }
    ),
    vec.end());
  print(vec.begin(), vec.end());
}</code></pre>
<h4 id="캡쳐-목록-사용법">캡쳐 목록 사용법</h4>
<ul>
<li><code>[this]</code>: 클래스에서 멤버 함수에서는: this를 캡처 리스트로 전달하는 게 가장 이상적</li>
<li><code>[]</code>: 아무것도 캡처 안함</li>
<li><code>[&amp;a, b]</code>: a는 레퍼런스로 캡처, b는 변경 불가한 복사본으로 캡처</li>
<li><code>[&amp;]</code>: 외부 모든 변수들을 레퍼런스로 캡처</li>
<li><code>[=]</code>: 외부 모든 변수들을 복사본으로 캡처</li>
</ul>
<h2 id="원소-수정하기transform">원소 수정하기(transform)</h2>
<blockquote>
<p>transform (시작 반복자, 끝 반복자, 결과를 저장할 컨테이너의 시작 반복자, Pred)</p>
</blockquote>
<pre><code class="language-cpp">std::cout &lt;&lt; &quot;벡터 전체에 1 을 더한다&quot; &lt;&lt; std::endl; 
std::transform(vec.begin(), vec.end(), vec.begin(), [](int i) { return i + 1; });</code></pre>
<h3 id="주의">주의</h3>
<p>값을 저장하는 컨테이너 크기 &gt;= 원래의 컨테이너</p>
<h2 id="그-외-함수들">그 외 함수들</h2>
<p>find, find_if, any_of, all_of</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 표준 라이브러리(컨테이너와 알고리즘) -2]]></title>
            <link>https://velog.io/@tera_geniel/C-%ED%91%9C%EC%A4%80-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2</link>
            <guid>https://velog.io/@tera_geniel/C-%ED%91%9C%EC%A4%80-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2</guid>
            <pubDate>Sun, 12 Jun 2022 17:10:45 GMT</pubDate>
            <description><![CDATA[<h1 id="c의-표준-연관-컨테이너들associative-container">C++의 표준 연관 컨테이너들(associative container)</h1>
<p>연관 컨테이너는 키-값 구조를 가짐</p>
<p>키의 존재 유무만 궁금 -&gt; 셋, 멀티셋
키에 대응하는 값이 궁금 -&gt; 맵, 멀티맵</p>
<h2 id="셋">셋</h2>
<p>내부적으로 이진 검색 트리 구조로 구성되어 있음.
실제로 셋 내부적으로 원소들이 정렬된 상태를 유지하기 때문!</p>
<ul>
<li>삽입, 삭제: O(logN)</li>
<li>검색: O(logN)</li>
</ul>
<p>즉, 원소를 검색하는데 필요한 횟수는 트리의 높이와 일치.
셋 안에는 중복된 원소들이 없다는 게 또 하나의 특징.</p>
<p>균형 잡힌 트리 구조를 지향
(이진 검색 트리에서 균형잡히지 않은, 편향된 트리 구조를 지양하기 위해 여러 다른 규칙 적용)</p>
<h3 id="커스텀-객체를-셋에-삽입해-보자">커스텀 객체를 셋에 삽입해 보자!</h3>
<p>커스텀 객체를 셋에 삽입할 때, &lt; 연산자에 대한 메소드를 명시해야 한다.
셋 내부에서 비교 연산자로 셋에 삽입하기 때문이다.</p>
<h4 id="strict-weak-ordering">strict weak ordering</h4>
<p>operator &lt;를 설계할 때</p>
<ol>
<li>A &lt; B , B &lt; A 중 하나는 반드시 True여야 함
-&gt; 매몰되기 쉬운 실수!</li>
<li>A &lt; A 는 거짓</li>
<li>A &lt; B 이고 B &lt; C 이면 A &lt; C</li>
<li>A == B 이면 A &lt; B 와 B &lt; A 둘 다 거짓 </li>
<li>A == B 이고 B == C 이면 A == C</li>
</ol>
<h2 id="맵">맵</h2>
<p>맵의 경우 키에 대응되는 값까지도 같이 보관함</p>
<h3 id="-연산자-주의할-점">[] 연산자 주의할 점</h3>
<p>디폴트 생성자로 0을 만들어서 초기화할 수 있음.
따라서 find()로 있는 지 먼저 확인 후 초기화하는 게 베스트</p>
<h2 id="멀티셋과-멀티맵">멀티셋과 멀티맵</h2>
<p>멀티셋과 멀티맵은 중복된 원소를 허락
(셋, 맵은 중복 원소 불허)</p>
<h3 id="키로-값-부르기중복되는-경우">키로 값 부르기(중복되는 경우)</h3>
<pre><code class="language-cpp">auto range = m.equal_range(1);</code></pre>
<p>위의 경우 키가 1인 값들을 range에 받아 begin, end를 std::pair로 만들어서 세트로 리턴함.</p>
<h2 id="정렬되지-않은-셋과-맵unordered_set-unordered_map">정렬되지 않은 셋과 맵(unordered_set, unordered_map)</h2>
<p>말 그대로 원소들이 정렬되지 않아 있음.
따라서 반복자로 원소들을 하나씩 출력하면 거의 랜덤한 순서로 나옴</p>
<p>실제 unordered_set의 모든 원소들을 반복자로 출력하면 딱히 순서대로 나오지는 않음.</p>
<h3 id="장점">장점</h3>
<p>insert, erase, find 모두 O(1)에 수행</p>
<p>-&gt; 상수 시간에 원소 삽입, 검색이 가능</p>
<h3 id="구현-방법---해시-함수">구현 방법 - 해시 함수</h3>
<p>해시함수는 1부터 D까지의 값을 반환, 그 해시값을 원소를 저장할 상자의 번호로 삼음.
해시 함수는 구조상 1 ~ D까지 고른 값을 반환하도록 설계함.</p>
<p>같은 원소를 해시 함수에 전달하면 같은 해시값을 리턴.
-&gt; 원소의 탐색을 빠르게 수행</p>
<p>=&gt; 그러나, 해시 충돌의 경우와 rehash의 경우, 최악의 경우 O(n)이 될 수 있음.</p>
<blockquote>
<p>보통의 경우 안전하게 맵, 셋을 이용
만약 최적화가 매우 필요한 작업일 경우: 해시 함수, 상자 개수를 잘 설계해서 <code>unordered_set</code>, <code>unordered_map</code>을 사용하는 게 좋음.</p>
</blockquote>
<h4 id="커스텀-객체에-해시-함수-반영">커스텀 객체에 해시 함수 반영</h4>
<p>functor를 사용해보자(객체이지만 함수인 척을 하는 객체).</p>
<pre><code class="language-cpp">class Todo {
  int priority; // 중요도. 높을 수록 급한것! std::string job_desc;
public:
  Todo(int priority, std::string job_desc)
        : priority(priority), job_desc(job_desc) {}
  bool operator==(const Todo&amp; t) const {
      // **!! 해시 충돌 발생 시 상자 안 원소들과 비교해야 하기 때문**
    if (priority == t.priority &amp;&amp; job_desc == t.job_desc)
      return true; return false;
  }
  friend std::ostream&amp; operator&lt;&lt;(std::ostream&amp; o, const Todo&amp; t);
  friend struct std::hash&lt;Todo&gt;; 
};

// Todo 해시 함수를 위한 함수객체(Functor) // 를 만들어줍니다!
namespace std {
  template &lt;&gt;
  struct hash&lt;Todo&gt; {
    size_t operator()(const Todo&amp; t) const {
      hash&lt;string&gt; hash_func;
      return t.priority ^ (hash_func(t.job_desc));
    } 
  };
} // namespace std
</code></pre>
<p><code>return</code>문 -&gt; <code>priority</code>는 int값(해시값 그 자체로 쓰기로 함), <code>string</code>의 해시값은 hash_func 객체로 이용해서 계산, 이를 XOR시킴.</p>
<p><code>namespace</code>문 -&gt; 특정 namespace 안에 새로운 클래스/함수 추가 위해서는 위처럼 명시적으로 namespace를 써줘야 하기 때문(<a href="https://stackoverflow.com/questions/2282349/specialization-of-templateclass-tp-struct-stdless-in-different-namespace">여기</a> 참고)</p>
]]></description>
        </item>
    </channel>
</rss>