<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hyuntae_.log</title>
        <link>https://velog.io/</link>
        <description>일상이 개발이다</description>
        <lastBuildDate>Thu, 31 Aug 2023 15:50:04 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. hyuntae_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hyuntae_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[db event scheduler]]></title>
            <link>https://velog.io/@hyuntae_/db-event-scheduler</link>
            <guid>https://velog.io/@hyuntae_/db-event-scheduler</guid>
            <pubDate>Thu, 31 Aug 2023 15:50:04 GMT</pubDate>
            <description><![CDATA[<p>DELIMITER//
create event if not exist ptr_pmp
on schedule every 1 day
start &#39;2023-03-25 12:22:32&#39;
on completion not preserve
enable
do
begin
    delet from save_ptr_pmp
    where data &lt; date_add(now(), interval -30 day)
end//
DELIMITER</p>
<p>이벤트 스케쥴러 생성 
save_ptr_pmp테이블에서 30일 전 테이터는 날려버려
그리고 2023-03-25 12:22:32 부터 시작하고 하루마다 실행시켜줘
끝나면 이 테이블은 없애줘</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[model fit]]></title>
            <link>https://velog.io/@hyuntae_/model-fit</link>
            <guid>https://velog.io/@hyuntae_/model-fit</guid>
            <pubDate>Thu, 31 Aug 2023 15:35:43 GMT</pubDate>
            <description><![CDATA[<p>매개변수 desc</p>
<ul>
<li>x: 데이터들</li>
<li>y: 실제값(들)</li>
<li>batch_size: 모델 한번 돌 때 사용할 샘플 수</li>
<li>epochs: 학습횟수</li>
<li>verbose: 출력정보 컨트롤
0: 출력 X
1: 진행 바(Progress bar) 표시
2: 에포크마다 한 줄의 로그 표시</li>
<li>callbacks: 학습 중에 호출될 콜백 목록, 예를 들면, 모델 저장이나 Early Stopping 등의 기능을 제공하는 객체들</li>
<li>validation_split: 0과 1 사이의 부동소수점 값. 학습 데이터의 일부를 검증 데이터로 자동 분할하기 위해 사용</li>
<li>validation_data: 검증을 위한 데이터(tuple 또는 tf.data 데이터셋)</li>
<li>shuffle: 각 에포크마다 데이터를 섞을지 여부를 지정</li>
<li>class_weight: 클래스별 가중치를 설정할 딕셔너리</li>
<li>sample_weight: 샘플별 가중치를 포함하는 numpy 배열</li>
<li>initial_epoch: 학습 시작 시 시작할 에포크 번호, 이전에 중단된 학습을 재개할 때 유용</li>
<li>steps_per_epoch: 한 에포크에 사용될 학습 단계 수, 주로 tf.data 데이터셋을 사용할 때 설정</li>
<li>validation_steps: 검증을 실행할 때 사용될 단계의 수</li>
<li>validation_batch_size: 검증할 때 &quot;한 번에&quot; 사용할 샘플 수</li>
<li>validation_freq: 검증을 몇 에포크마다 수행할지 결정</li>
<li>max_queue_size: 생성자(generator)를 사용할 때 최대 큐 크기</li>
<li>workers: 생성자를 사용할 때 동시에 실행할 작업자(worker)의 수</li>
<li>use_multiprocessing: 생성자를 사용할 때 멀티 프로세싱을 사용할지 여부를 지정</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스 뿌시기]]></title>
            <link>https://velog.io/@hyuntae_/%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%BF%8C%EC%8B%9C%EA%B8%B0</link>
            <guid>https://velog.io/@hyuntae_/%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%BF%8C%EC%8B%9C%EA%B8%B0</guid>
            <pubDate>Wed, 30 Aug 2023 13:14:19 GMT</pubDate>
            <description><![CDATA[<h2 id="a--b">a &gt; b</h2>
<h3 id="b에-a를-덮어씌우기">b에 a를 덮어씌우기</h3>
<hr>
<h2 id="a--b-1">a &gt;&gt; b</h2>
<h3 id="b에-a를-이어붙이기">b에 a를 이어붙이기</h3>
<hr>
<h2 id="tac-a">tac a</h2>
<h3 id="a파일-뒤집어서-보기">a파일 뒤집어서 보기</h3>
<hr>
<h2 id="cat--새로운-파일이름">cat &gt; 새로운 파일이름</h2>
<h3 id="간단하게-내용작성할-때-vi-대신-쓰면-유용">간단하게 내용작성할 때 vi 대신 쓰면 유용</h3>
<h3 id="ctr--d-저장">ctr + d 저장</h3>
<hr>
<h2 id="cal--tee--a-a5">cal | tee -a a5</h2>
<h3 id="cal-실행결과를-보여주고-a5파일에-이어붙이기">cal 실행결과를 보여주고 a5파일에 이어붙이기</h3>
<hr>
<h2 id="nl-a">nl a</h2>
<h3 id="a파일-넘버링">a파일 넘버링</h3>
<h4 id="nl--w10-a-너비-10">nl -w10 a (너비 10)</h4>
<h4 id="nl--i2-a-행번호-2씩-증가">nl -i2 a (행번호 2씩 증가)</h4>
<h4 id="nl--i10--v10-a-시작번호-10-행번호-10씩-증가">nl -i10 -v10 a (시작번호 10, 행번호 10씩 증가)</h4>
<hr>
<h2 id="head--n-5-파일명">head -n (+)5 파일명</h2>
<h3 id="첫줄부터-5개-가져와">첫줄부터 5개 가져와</h3>
<hr>
<h2 id="head--n--5-파일명">head -n -5 파일명</h2>
<h3 id="맨뒤에서-5개-잘라내고-나머지-가져와">맨뒤에서 5개 잘라내고 나머지 가져와</h3>
<hr>
<h2 id="tail--n--5-파일명">tail -n (-)5 파일명</h2>
<h3 id="뒤에서부터-5개">뒤에서부터 5개</h3>
<hr>
<h2 id="tail--n-5-파일명">tail -n +5 파일명</h2>
<h3 id="앞에서-5개-잘라내고-가져와">앞에서 5개 잘라내고 가져와</h3>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS Object 메서드2]]></title>
            <link>https://velog.io/@hyuntae_/JS-Object-%EB%A9%94%EC%84%9C%EB%93%9C2</link>
            <guid>https://velog.io/@hyuntae_/JS-Object-%EB%A9%94%EC%84%9C%EB%93%9C2</guid>
            <pubDate>Fri, 26 May 2023 07:51:43 GMT</pubDate>
            <description><![CDATA[<h1 id="objectgetownpropertydescriptor">Object.getOwnPropertyDescriptor()</h1>
<h2 id="구문">구문</h2>
<p><code>Object.getOwnPropertyDescriptor(obj, prop)</code></p>
<h2 id="설명">설명</h2>
<p>obj객체의 prop 속성에 대한 설명자를 반환해요</p>
<h2 id="예제">예제</h2>
<pre><code>const case1 = Object.defineProperties({},{
    &#39;name&#39; : {
                value : &#39;홍길동&#39;,
                writable : true,
                enumerable : true,
             },
    &#39;age&#39; : {
                value : 100,
                writable : false,
                enumerable : false
            }
})

//case1 객체의 name 속성을 찍어보자
console.log(Object.getOwnPropertyDescriptor(case1, &#39;name&#39;))
configurable : false 
enumerable : true
value : &quot;홍길동&quot;
writable : true

//case1 객체의 age 속성을 찍어보자
console.log(Object.getOwnPropertyDescriptor(case1, &#39;age&#39;))
configurable : false
enumerable : false
value : 100
writable : false
</code></pre><p>참고로 defineProperties()메서드를 사용하여 속성을 정의할 경우
configurable, enumerable, writable 기본값은 false입니다.</p>
<h1 id="objectgetownpropertydescriptors">Object.getOwnPropertyDescriptors()</h1>
<h2 id="구문-1">구문</h2>
<p><code>Object.getOwnPropertyDescriptors(obj)</code></p>
<h2 id="설명-1">설명</h2>
<p>매개변수로 들어온 obj객체의 모든 속성들의 설명자를 반환해요</p>
<h2 id="예제-1">예제</h2>
<pre><code>const case1 = Object.defineProperties({},{
    &#39;name&#39; : {
                value : &#39;홍길동&#39;,
                writable : true,
                enumerable : true,
             },
    &#39;age&#39; : {
                value : 100,
                writable : false,
                enumerable : false
            }
})

// case1 객체의 설명자가 궁금해요
console.log(Object.getOwnPropertyDescriptors(case1))

age : 
    configurable : false
    enumerable : false
    value : 100
    writable : false

name : 
    configurable : false
    enumerable : true
    value : &quot;홍길동&quot;
    writable : true
</code></pre><h1 id="objectgetownpropertynames">Object.getOwnPropertyNames()</h1>
<h2 id="구문-2">구문</h2>
<p><code>Object.getOwnPropertyNames(obj)</code></p>
<h2 id="설명-2">설명</h2>
<p>매개변수의 속성들을 배열에 담아 반환시켜줍니다.</p>
<h2 id="예제-2">예제</h2>
<pre><code>//case1를 만들어볼께요
const case1 = { a : 1, b : 2, c : 3}
//객체에 어떤 속성이 있는지 궁금해요
console.log(Object.getOwnPropertyNames(case1))

결과값 =&gt; [ &#39;a&#39;, &#39;b&#39;, &#39;c&#39; ]

//case2를 만들어 볼께요
const case2 = { name : &#39;홍길동&#39;, age : 100 }
//콘솔찍어 볼께요
console.log(Object.getOwnPropertyNames(case2))

결과값 =&gt; [ &#39;name&#39;, &#39;age&#39; ]
</code></pre><h1 id="objectgetprototypeof">Object.getPrototypeOf()</h1>
<h2 id="구문-3">구문</h2>
<p><code>Object.getPrototypeOf(obj)</code></p>
<h2 id="설명-3">설명</h2>
<p>매개변수의 프로토타입(상속받은 객체)을 반환해요.</p>
<h2 id="예제-3">예제</h2>
<pre><code>//객체를 하나 만들어 볼께요
let obj1 = { name : &#39;홀깅돌&#39; , age : 100 };

//obj1을 상속받는 객체를 하나 만들어 볼께요
let obj2 = Object.create(obj)

// Object.getPrototypeOf(obj2) === obj1

결과값 =&gt; true</code></pre><h1 id="objectkeys">Object.keys()</h1>
<h2 id="구문-4">구문</h2>
<p><code>Object.keys(obj)</code></p>
<h2 id="설명-4">설명</h2>
<p>주어진 객체의 속성 이름들을 배열에 담아 반환해줘요</p>
<h2 id="예제-4">예제</h2>
<pre><code class="language-js">
//객체를 하나 만들어 볼께요
const object1 = {
  a: &#39;something&#39;,
  b: 100,
  c: &#39;kim&#39;
};
//Object.keys()를 사용해볼께요
console.log(Object.keys(object1))// [ &#39;a&#39;, &#39;b&#39;, &#39;c&#39; ]


//이번엔 defineProperties()메서드를 사용해서 원하는 key들만 반환시켜볼께요
const object2 = Object.defineProperties({},{
    a : {
             value : &#39;somestring&#39;,
              enumerable : true
        },
      b : {
              value : 42,
              enumerable : false
          },
      c : {
            value : &#39;kim&#39;,
              enumerable : true
          }
})

// b만 enumerable(순회할 때 출력을 결정할 수 있는 옵션)을 false로 설정했어요
// 어떻게 나올지 콘솔찍어볼께요

console.log(Object.keys(object2));//[ &#39;a&#39; , &#39;c&#39; ] 
// b 키만 출력되지 않았어요
</code></pre>
<h1 id="objectseal-objectpreventextensions">Object.seal(), Object.preventExtensions(),</h1>
<h2 id="구문-5">구문</h2>
<p><code>Object.preventExtensions(obj)</code>
<code>Object.seal(obj)</code></p>
<h2 id="설명-5">설명</h2>
<p>매개변수로 받아온 객체를 밀봉하여 객체의 확장을 막아요(새 속성 추가 X)
하지만 기존 속성의 값을 변경할 수있다는 점에서 Object.freeze()메서드와 차이점이 존재해요.</p>
<h2 id="예제-5">예제</h2>
<pre><code class="language-js">
//age속성을 갖는 객체를 하나 만들어볼께요.
const object1 = {
  age : 100,
};
//만들객체를 밀봉시켜볼께요
Object.seal(object1);

//1. name 속성을 추가 시켜볼께요
object1.name = &#39;홍길동&#39;;
//콘솔을 찍어봐요
console.log(object1.name);
// 결과값 =&gt; undefined

//2. 기존에 있던 age 속성을 지워볼께요
&#39;use strict&#39;
delete object1.age
//Uncaught TypeError: Cannot delete property &#39;age&#39;

//3. age값을 변경시켜볼께요
object1.age = 200;
//콘솔찍어봐요
console.log(object1.age);
//결과값 =&gt; 200
</code></pre>
<blockquote>
<p><code>Object.preventExtensions()</code> 메서드도 동일하게 작동하기 때문에 예제에 포함시키지 않았고 만약 속성의 값을 변경시켜 주기 싫다 하시면 <code>Object.freeze()</code> 또는 Object.defineProperties() 사용할 수 있어요</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS Object 메서드]]></title>
            <link>https://velog.io/@hyuntae_/JS-Object-%EB%A9%94%EC%84%9C%EB%93%9C</link>
            <guid>https://velog.io/@hyuntae_/JS-Object-%EB%A9%94%EC%84%9C%EB%93%9C</guid>
            <pubDate>Wed, 24 May 2023 09:06:17 GMT</pubDate>
            <description><![CDATA[<h1 id="object-메서드-종류">Object 메서드 종류</h1>
<blockquote>
<p>mdn 문서를 참고하여 정리한 내용이며, 부가적인 옵션은 추후에 업데이트 할 예정입니다.</p>
</blockquote>
<h2 id="objectassigntarget-source">Object.assign(target, ...source)</h2>
<p>짧은 설명 : source 객체들을 복사해 target에 붙여넣는다.</p>
<pre><code class="language-js">const target = { a : 1, b : 2 }
const source = { b : 3, c : 4 }

const returnTarget = Object.assign(tartget, source);

console.log(returnTarget)//{ a : 1, b : 3, c : 4 }
console.log(returnTarget === target)// true;

assign 메서드는 불변성을 가지고 있지 않아 target객체와 returnTarget객체는 모두 같은 주소값을 가졌으며 같은 객체를 바라보고 있다.
</code></pre>
<h2 id="objectdefineproperties">Object.defineProperties()</h2>
<p>짧은 설명 : 객체에 새로운 속성을 부여하거나 기존의 속성을 수정하고 그 객체를 반환</p>
<pre><code class="language-js">//객체 생성
const obj = {};

//
Object.defineProperties(obj, {
  //name 속성을 부여하였으며 값은 10 할당, 수정 불가능
  name: {
    value : &#39;kim&#39;,
    writable: false
  },

  //age 속성을 부여하였으며 수정가능
  age : {
      value : 20 ,
    writable : true
  }
});

console.log(obj.name);//kim
console.log(obj.age);//20

//obj의 name 속성을 park으로, age 속성을 30으로 수정
obj.name = &#39;park&#39;;
obj.age = 30;

console.log(obj.name);//kim
console.log(obj.age);//30

//name속성은 수정이 안되는것을 확인할 수 있다. writable을 false로 사용하여 객체 내부의 값을 함부로 수정하지 못하게 막았기 때문이다. 
</code></pre>
<h2 id="objectentries">Object.entries()</h2>
<p>짧은 설명 : 매개변수로 받은 obj의 enumerable속성을  <code>[key, value]</code> 쌍을 배열로 반환합니다.</p>
<pre><code class="language-js">
const case1 = { a : 1, b : 2, c : 3 }
console.log(Object.entries(case1)) // [ [&#39;a&#39;,1], [&#39;b&#39;,2], [&#39;c&#39;,3] ]

const case2 = { 100: &#39;a&#39;, 2: &#39;b&#39;, 7: &#39;c&#39; };
console.log(Object.entries(case2)); // [ [&#39;2&#39;, &#39;b&#39;], [&#39;7&#39;, &#39;c&#39;], [&#39;100&#39;, &#39;a&#39;] ]

</code></pre>
<h2 id="objectfromentries">Object.fromEntries()</h2>
<p>짧은 설명 : 매개변수로 전달받은 키-값쌍 목록을 객체로 반환해준다.
쉽게말해 Object.entries()의 반대되는 메서드임.</p>
<pre><code class="language-js">
const case1 = [ [&#39;a&#39;,1], [&#39;b&#39;,2], [&#39;c&#39;,3] ];
console.log(Object.fromEntries(case1))//{a: 1, b: 2, c: 3}

//배열을 객체로 만들어서 반환


const case2 = [ [&#39;2&#39;, &#39;b&#39;], [&#39;7&#39;, &#39;c&#39;], [&#39;100&#39;, &#39;a&#39;] ];
console.log(Object.fromEntries(case2))//{2: &#39;b&#39;, 7: &#39;c&#39;, 100: &#39;a&#39;}

//배열을 객체로 만들어서 반환
</code></pre>
<h2 id="objectfreeze">Object.freeze()</h2>
<p>짧은 설명 : 객체를 동결시켜 속성값을 추가하거나 변경하거나 제거를 방지함</p>
<pre><code class="language-js">//배열생성
const case1 = [1,2,3];
//case1를 동결시켜볼께요
Object.freeze(case1)
//배열에 2를 추가해볼께요
case1.push(1);//error
case1.pop()//error
case1[0] = 10;//error를 발생시키지 않고 조용히 넘어감 대신 변경은 안됨!!
console.log(case1[0])// 1
//case1이 동결인지 아닌지 확인해볼께요
Object.isFrozen(case1);//true

//객체생성
const case2 = { name : &#39;kim&#39; }

//동결시켜보자
Object.freeze(case2)

//age속성을 추가시킨후에 case2를 출력시켜보면
case2.age = 100
console.log(case2)//{name: &#39;kim&#39;}

//name속성을 삭제시킨후 콘솔을 찍어보면
delete case2.name
console.log(case2)//{name: &#39;kim&#39;}

//name속성 값을 변경시킨 후 콘솔을 찍어보면
case2.name = &#39;park&#39;
console.log(case2)//{name: &#39;kim&#39;}


Object.defineProperties()메서드는 각 속성을 수정할지 안할지 결정할 수 있었지만 
Ojbect.freeze()메서드는 모든 것을 막는다.</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 Array.prototype 메서드 추가]]></title>
            <link>https://velog.io/@hyuntae_/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Array.prototype-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@hyuntae_/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Array.prototype-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Mon, 22 May 2023 14:11:23 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트의 메서드중에 불변성을 유지하지 못하는 메서드들이 많은데 이번에 추가로 불변성을 유지시켜주는 메서드가 추가되서 정리한번 해보았다.</p>
<ol>
<li>toSorted<pre><code class="language-js">const originSorted = [1,2,3,4,5];
const newSorted = originSorted.sort((a,b)=&gt; b-a);
console.log(originSorted);//[5,4,3,2,1]
console.log(newSorted);//[5,4,3,2,1]
console.log(originSorted === newSorted);//true</code></pre>
기존의 sort메서드는 원본을 참조하여 변수에 원본 주소값을 넘겨주기에 불변성을 유지하지 못하였지만</li>
</ol>
<pre><code class="language-js">const originToSorted = [1,2,3,4,5];
const newToSorted = originToSorted.toSorted((a,b)=&gt; b-a);
console.log(originToSorted);//[5,4,3,2,1]
console.log(newToSorted);//[5,4,3,2,1]
console.log(originToSorted === newToSorted);//true</code></pre>
<p>toSorted를 사용해주면 원본은 그대로 유지하되 newToSorted 변수에 새로운 배열을 반환해준다.</p>
<hr>
<ol start="2">
<li>toReverse()</li>
</ol>
<pre><code class="language-js">const originReverse = [1,2,3,4,5];
const newReverse = originReverse.reverse();
console.log(originReverse);//[5,4,3,2,1];
console.log(newReverse);//[5,4,3,2,1]
console.log(originReverse === newReverse);//true</code></pre>
<pre><code class="language-js">const originReverse = [1,2,3,4,5];
const newReverse = originReverse.toReverse();
console.log(originReverse);//[1,2,3,4,5]
console.log(newReverse);//[5,4,3,2,1]
console.log(originReverse === newReverse);//false</code></pre>
<p>reverse()를 사용하면 원본 배열의 주소값을 반환하였지만 toReverse()메서드는 새로운 배열을 만들어 반환시켜주기에 기존 배열의 불변성을 유지할 수 있다.</p>
<hr>
<ol start="3">
<li>with(index, value)</li>
</ol>
<pre><code class="language-js">const originWith = [1,2,3,4,5];
const newWith = originWith.with(3,100);
console.log(originWith);//[1,2,3,4,5]
console.log(newWith);//[1,2,3,100,5]
console.log(originWith === newWith);//false</code></pre>
<p>with()메서드는 첫번째 인자값으로 인덱스를 받고 두번째 인자값으로 대체할 값을 받는다.
with(3,10)을 해석하자면 배열의 3번째 인덱스 값을 100으로 바꿔줘! 라는 뜻이다.
기존에는 slice를 사용하여 코드가독성이 떨어지는 측면이 있었는데 이를 해결해 준 느낌이다.</p>
<p>ex) <code>[...array.slice(0,3), 바꿔줄 값, ...array.slice(4)]</code></p>
<hr>
<ol start="4">
<li>findLast()</li>
</ol>
<pre><code class="language-js">const find = [1,2,3,4,5];
find.find((el) =&gt; console.log(el));//1,2,3,4,5

const findLast = [1,2,3,4,5];
findLast.findLast((el) =&gt; console.log(el))//5,4,3,2,1</code></pre>
<p>기존의 find()는 배열 탐색 시 첫번째 인자부터 검색했지만 findLast()는 가장 마지막 인자부터 탐색을 시작한다. 비록 시간복잡도 측면에서는 비슷하지만 맨 뒤부터 검색해보고 싶다면 위 메서드를 사용할 수 있을것같다.</p>
<hr>
<ol start="5">
<li>findLastIndx()</li>
</ol>
<pre><code class="language-js">const findIndex = [1,2,3,4,5];
findIndex.findIndex((_,idx) =&gt; console.log(idx))//0,1,2,3,4

const findLastIndex = [1,2,3,4,5];
findLastIndex.findLastIndex((_,idx) =&gt; console.log(idx))//4,3,2,1,0</code></pre>
<p>findIndex()메서드는 배열 첫번째 인덱스부터 탐색을 시작하고, findLastIndex()는 배열 마지막 인덱스부터 탐색을 시작한다. 이 메서드도 배열 가장 마지막 인덱스부터 탐색을 하고 싶다면 사용해보길 바란다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[지도에 로드뷰 설정하기]]></title>
            <link>https://velog.io/@hyuntae_/%EC%A7%80%EB%8F%84%EC%97%90-%EB%A1%9C%EB%93%9C%EB%B7%B0-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyuntae_/%EC%A7%80%EB%8F%84%EC%97%90-%EB%A1%9C%EB%93%9C%EB%B7%B0-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 02 May 2023 11:18:44 GMT</pubDate>
            <description><![CDATA[<p>찾고싶은 매물의 실제 모습이나 주변환경을 눈으로 직접 확인해야 아무래도 믿을 수 있기때문에 로드뷰 기능을 추가하기로 결정했다.</p>
<p>문제발생!!
로드뷰 버튼 만들어서 클릭하면 현재 지도 중심의 위도, 경도를 구해와 바로 화면에 보여준다는 점
사용자가 원하는 위치를 클릭하여 로드뷰를 볼 수 있도록 구현해줘야 사용자입장에서 훨씬 편하다.</p>
<p>수정 후 로드뷰 누르면 그림처럼 내가 원하는 위치의 로드뷰를 설정하여 볼 수 있다.
![]
(<a href="https://velog.velcdn.com/images/hyuntae_/post/2b4d4e79-fa07-461d-9106-6427fc56ace1/image.png">https://velog.velcdn.com/images/hyuntae_/post/2b4d4e79-fa07-461d-9106-6427fc56ace1/image.png</a>)</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/3199a703-ae1d-411d-94e3-ef7d6b00f997/image.png" alt=""></p>
<p>휴..
사이트 주소 : <a href="https://happyhouse.vercel.app/apart">https://happyhouse.vercel.app/apart</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next 왜 사용할까?]]></title>
            <link>https://velog.io/@hyuntae_/Next-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@hyuntae_/Next-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Wed, 05 Apr 2023 06:38:44 GMT</pubDate>
            <description><![CDATA[<p>react의 프레임워크로 Next를 사용하는 개발자들이 점점 늘어나는 추세다.</p>
<blockquote>
<p>react도 충분히 좋은 도구이지만 왜 next를 사용할까?</p>
</blockquote>
<h1 id="react-단점이-뭐지">react... 단점이 뭐지</h1>
<p>바로 SEO(검색엔진)과 Pre-render이라고 생각한다.
인터넷 사용자들은 무언가 찾고싶을 때 네이버나 구글 혹은 다른 브라우저를 이용하여 검색창에 검색을 하고 원하는 데이터를 얻는다. 이 과정에서 브라우저검색엔진이 작동을 하고 사용자들에게 원하는 데이터를 찾아서 보여줍니다.</p>
<p>여기서 검색엔진의 중요성이 나오는데요, </p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/7a437559-4d5d-417d-800e-1194c03100c1/image.png" alt=""></p>
<p>react를 생성하고 index.html보시면 아무것도 없고 body에 달랑 div태그 하나만 있습니다. 브라우저 검색엔진은 html 파일을 뒤져서 필요한 내용이 있다면 가져오는 방식으로 작동을 하는데 이때 react는 아무것도 제공해주질 못해서 검색엔진에 약점이 존재하기에 이를 보완하기 위해서 Next.js의 Pre-rendering을 사용합니다.</p>
<h1 id="pre-rendering">Pre-rendering?</h1>
<blockquote>
<p>Pre-rendering이란 사용자가 페이지를 보기 전에 서버에서 미리 html파일을 생성하여 요청이 오면 보내주고 사용자는 즉각 화면에 어떠한 데이터를 볼 수 있습니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/f35a6d7a-8d71-440a-8cb7-f4c4ba986f95/image.png" alt=""></p>
<p>위 그림을 보시면 사용자가 사이트에 접속을 하면 미리 렌더링된 HTML을 볼 수가 있고 그리고 나서 동작기능을 사용하기 위해서 js파일을 요청하고 html과 js가 하나로 합쳐지는 hydration 과정이 끝난 후에 사용자는 모든 기능을 사용할 수 있습니다.</p>
<h1 id="ssg와-ssr">SSG와 SSR</h1>
<p>그렇다면 Pre-rendering의 종류에는 어떠한 것이 있는지 알아봅시다</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/72e2c5b5-c997-4909-8d68-d047405cc179/image.png" alt=""></p>
<p>먼저 SSG(이하 ssg)입니다. static site generation의 약자로 정적페이지생성을 뜻합니다. 말 그대로 정적, 데이터가 변하지 않고 항상 똑같은 데이터를 가지고 똑같은 화면을 보여주는 경우에 사용할 수 있습니다. 그렇기에 next공식문서에도 추천하고 있습니다✅
ssg는 프로젝트 빌드시 한번 실행되어 html파일로 만들어지고 CDN에 캐싱되어 다음 요청시부터 재사용가능합니다.</p>
<p>어? 저는 데이터가 그때그때 마다 달라서 ssg를 사용하면 안되는데요? </p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/54625b48-1a25-4784-8d61-3e510395f0f2/image.png" alt=""></p>
<p>이를 위해서 SSR이 존재합니다. SSR(이하 ssr)은 Server-side Rendering의 약자로 ssg와의 가장 큰 차이점이자 장점은 항상 최신내용을 가지고 있다는 것입니다. 물론 재사용/재활용을 하지 않기에 ssg와 ssr을 잘 생각해서 필요한 곳에 적절히 써야됩니다.</p>
<h1 id="결론">결론</h1>
<blockquote>
<p>ssr과 ssg를 사용하여 미리 렌더링할 수 있어 검색엔진에 있어서 장점이 있지만, 유저보드처럼 seo의 필요성이 적다면 굳이 사용하지 않아도 된다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[표 병합]]></title>
            <link>https://velog.io/@hyuntae_/%ED%91%9C-%EB%B3%91%ED%95%A9</link>
            <guid>https://velog.io/@hyuntae_/%ED%91%9C-%EB%B3%91%ED%95%A9</guid>
            <pubDate>Wed, 29 Mar 2023 18:08:39 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-설명">문제 설명</h1>
<p>당신은 표 편집 프로그램을 작성하고 있습니다.
표의 크기는 50 × 50으로 고정되어있고 초기에 모든 셀은 비어 있습니다.
각 셀은 문자열 값을 가질 수 있고, 다른 셀과 병합될 수 있습니다.</p>
<p>위에서 r번째, 왼쪽에서 c번째 위치를 (r, c)라고 표현할 때, 당신은 다음 명령어들에 대한 기능을 구현하려고 합니다.</p>
<blockquote>
<ol>
<li>&quot;UPDATE r c value&quot;
(r, c) 위치의 셀을 선택합니다.
선택한 셀의 값을 value로 바꿉니다.</li>
</ol>
</blockquote>
<blockquote>
<ol start="2">
<li>&quot;UPDATE value1 value2&quot;
value1을 값으로 가지고 있는 모든 셀을 선택합니다.
선택한 셀의 값을 value2로 바꿉니다.</li>
</ol>
</blockquote>
<blockquote>
<ol start="3">
<li>&quot;MERGE r1 c1 r2 c2&quot;
(r1, c1) 위치의 셀과 (r2, c2) 위치의 셀을 선택하여 병합합니다.
선택한 두 위치의 셀이 같은 셀일 경우 무시합니다.
선택한 두 셀은 서로 인접하지 않을 수도 있습니다. 이 경우 (r1, c1) 위치의 셀과 (r2, c2) 위치의 셀만 영향을 받으며, 그 사이에 위치한 셀들은 영향을 받지 않습니다.
두 셀 중 한 셀이 값을 가지고 있을 경우 병합된 셀은 그 값을 가지게 됩니다.
두 셀 모두 값을 가지고 있을 경우 병합된 셀은 (r1, c1) 위치의 셀 값을 가지게 됩니다.
이후 (r1, c1) 와 (r2, c2) 중 어느 위치를 선택하여도 병합된 셀로 접근합니다.</li>
</ol>
</blockquote>
<blockquote>
<ol start="4">
<li>&quot;UNMERGE r c&quot;
(r, c) 위치의 셀을 선택하여 해당 셀의 모든 병합을 해제합니다.
선택한 셀이 포함하고 있던 모든 셀은 프로그램 실행 초기의 상태로 돌아갑니다.
병합을 해제하기 전 셀이 값을 가지고 있었을 경우 (r, c) 위치의 셀이 그 값을 가지게 됩니다.</li>
</ol>
</blockquote>
<blockquote>
<ol start="5">
<li>&quot;PRINT r c&quot;
(r, c) 위치의 셀을 선택하여 셀의 값을 출력합니다.
선택한 셀이 비어있을 경우 &quot;EMPTY&quot;를 출력합니다.</li>
</ol>
</blockquote>
<p>아래는 UPDATE 명령어를 실행하여 빈 셀에 값을 입력하는 예시입니다.</p>
<p>UPDATE 1 1 menu    (1,1)에 &quot;menu&quot; 입력
UPDATE 1 2 category    (1,2)에 &quot;category&quot; 입력
UPDATE 2 1 bibimbap    (2,1)에 &quot;bibimbap&quot; 입력
UPDATE 2 2 korean    (2,2)에 &quot;korean&quot; 입력
UPDATE 2 3 rice    (2,3)에 &quot;rice&quot; 입력
UPDATE 3 1 ramyeon    (3,1)에 &quot;ramyeon&quot; 입력
UPDATE 3 2 korean    (3,2)에 &quot;korean&quot; 입력
UPDATE 3 3 noodle    (3,3)에 &quot;noodle&quot; 입력
UPDATE 3 4 instant    (3,4)에 &quot;instant&quot; 입력
UPDATE 4 1 pasta    (4,1)에 &quot;pasta&quot; 입력
UPDATE 4 2 italian    (4,2)에 &quot;italian&quot; 입력
UPDATE 4 3 noodle    (4,3)에 &quot;noodle&quot; 입력
위 명령어를 실행하면 아래 그림과 같은 상태가 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/bd611da0-8e6c-471e-9eb6-4fa9b377fc42/image.png" alt=""></p>
<p>아래는 MERGE 명령어를 실행하여 셀을 병합하는 예시입니다.</p>
<p>MERGE 1 2 1 3    (1,2)와 (1,3) 병합
MERGE 1 3 1 4    (1,3)과 (1,4) 병합
위 명령어를 실행하면 아래와 같은 상태가 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/f5ad1652-ef55-4a45-97e0-1016ea95c8be/image.png" alt=""></p>
<p>병합한 셀은 &quot;category&quot; 값을 가지게 되며 (1,2), (1,3), (1,4) 중 어느 위치를 선택하더라도 접근할 수 있습니다.</p>
<p>아래는 UPDATE 명령어를 실행하여 셀의 값을 변경하는 예시입니다.</p>
<p>UPDATE korean hansik    &quot;korean&quot;을 &quot;hansik&quot;으로 변경
UPDATE 1 3 group    (1,3) 위치의 셀 값을 &quot;group&quot;으로 변경
위 명령어를 실행하면 아래와 같은 상태가 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/a41ba545-5373-45e6-802c-760f89633943/image.png" alt=""></p>
<p>아래는 UNMERGE 명령어를 실행하여 셀의 병합을 해제하는 예시입니다.</p>
<p>UNMERGE 1 4    셀 병합 해제 후 원래 값은 (1,4)가 가짐
위 명령어를 실행하면 아래와 같은 상태가 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/274597ae-2d07-4201-bdd9-11a4d9184409/image.png" alt=""></p>
<p>실행할 명령어들이 담긴 1차원 문자열 배열 commands가 매개변수로 주어집니다. commands의 명령어들을 순서대로 실행하였을 때, &quot;PRINT r c&quot; 명령어에 대한 실행결과를 순서대로 1차원 문자열 배열에 담아 return 하도록 solution 함수를 완성해주세요.</p>
<h1 id="제한사항">제한사항</h1>
<p>1 ≤ commands의 길이 ≤ 1,000
commands의 각 원소는 아래 5가지 형태 중 하나입니다.</p>
<blockquote>
<p>&quot;UPDATE r c value&quot;
r, c는 선택할 셀의 위치를 나타내며, 1<del>50 사이의 정수입니다.
value는 셀에 입력할 내용을 나타내며, 알파벳 소문자와 숫자로 구성된 길이 1</del>10 사이인 문자열입니다.</p>
</blockquote>
<blockquote>
<p>&quot;UPDATE value1 value2&quot;
value1은 선택할 셀의 값, value2는 셀에 입력할 내용을 나타내며, 알파벳 소문자와 숫자로 구성된 길이 1~10 사이인 문자열입니다.</p>
</blockquote>
<blockquote>
<p>&quot;MERGE r1 c1 r2 c2&quot;
r1, c1, r2, c2는 선택할 셀의 위치를 나타내며, 1~50 사이의 정수입니다.</p>
</blockquote>
<blockquote>
<p>&quot;UNMERGE r c&quot;
r, c는 선택할 셀의 위치를 나타내며, 1~50 사이의 정수입니다.</p>
</blockquote>
<blockquote>
<p>&quot;PRINT r c&quot;
r, c는 선택할 셀의 위치를 나타내며, 1~50 사이의 정수입니다.
commands는 1개 이상의 &quot;PRINT r c&quot; 명령어를 포함하고 있습니다.</p>
</blockquote>
<h1 id="입출력-예">입출력 예</h1>
<h2 id="입력1">입력1</h2>
<blockquote>
<p>&quot;UPDATE 1 1 menu&quot;, &quot;UPDATE 1 2 category&quot;, &quot;UPDATE 2 1 bibimbap&quot;, &quot;UPDATE 2 2 korean&quot;, &quot;UPDATE 2 3 rice&quot;, &quot;UPDATE 3 1 ramyeon&quot;, &quot;UPDATE 3 2 korean&quot;, &quot;UPDATE 3 3 noodle&quot;, &quot;UPDATE 3 4 instant&quot;, &quot;UPDATE 4 1 pasta&quot;, &quot;UPDATE 4 2 italian&quot;, &quot;UPDATE 4 3 noodle&quot;, &quot;MERGE 1 2 1 3&quot;, &quot;MERGE 1 3 1 4&quot;, &quot;UPDATE korean hansik&quot;, &quot;UPDATE 1 3 group&quot;, &quot;UNMERGE 1 4&quot;, &quot;PRINT 1 3&quot;, &quot;PRINT 1 4&quot;</p>
</blockquote>
<h2 id="출력1">출력1</h2>
<blockquote>
<p>(1,3) 위치의 셀은 비어있고 (1,4) 위치의 셀 값은 &quot;group&quot;입니다. 따라서 [&quot;EMPTY&quot;, &quot;group&quot;]을 return 해야 합니다.</p>
</blockquote>
<h1 id="풀이">풀이</h1>
<p>문제를 처음 읽고 든 생각은 각 명령어 마다 함수를 생성해주고 각각 요구사항에 맞게 풀면 되겠다 생각했는지만 호락호락하지 않았다(역시 카카오 ㄷㄷ😭)
핵심구현이라고 생각한것은 MERGE 명령어였다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/5541b12c-9460-4aa5-88e0-c7f5021b9872/image.png" alt=""></p>
<p>예들 들어 위 그림과 같이 두 집합 A,B가 있다고 가정해보자(초기화된 상태보다는 병합된 상태가 핵심이기에 미리 병합된 집합을 예시로 들었어요)</p>
<blockquote>
<p>MERGE 1 1 2 2</p>
</blockquote>
<p>위 command가 있다고 하면 1,1은 A집합 2,2는 B집합에 속해있는 상태다. 여기서 MERGE를 하면 또 다른 집합 C가 생성되고 이 집합의 인자는 (1,1),(1,2),(2,2),(2,3) 총 4개를 가진다. 이 과정에 탐색과 집합 A,B 인자의 모든 위치에 새로운 배열 C를 넣어줘야하기에 메모리 초과가 발생하여 에러를 일으킨다. 그림으로 표현하면 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/072e7a82-f44d-4e6b-99c3-2f8c42cdc363/image.png" alt=""></p>
<p>이를 해결하기 위해 union 알고리즘을 사용하였다. union알고리즘은 두 집합을 합칠 때 공통인자를 가진 값을 넣어 두 집합이 하나라는 표시를 해주는 것이다. 그림을 살펴보자</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/cddfee9a-52f5-4906-9db0-8fdcbf1863d7/image.png" alt=""></p>
<p>위 그림처럼 각각의 좌표에는 고유의 값이 존재한다. 다음으로 MERGE 1 1 1 2 와 MERGE 2 2 2 3 을 실행해 보면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/7d066ce3-d80c-4dbb-bc33-c5468845b521/image.png" alt=""></p>
<p>(1,1),(1,2)의 값을 동일하므로 같은 집합, 마찬가지로 (2,2),(2,3)의 값이 같으므로 같은 집합으로 생각하면 되요. 다음으로 MERGE 1 1 2 2를 실행한 결과를 보면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/806cb401-dc26-430f-a3d9-4f89674d60af/image.png" alt=""></p>
<p>(1,1)의 고유값은 1이고, (2,2)의 고유값은 7입니다. 여기서 로직은 7값을 가진 모든 좌표를 찾아 1로 바꿔주면 되고 시간복잡도는 50 x 50= 2500번입니다.</p>
<p>지금까지 MERGE를 살펴보았고 제가 생각하기로 MERGE를 잘 구현할 수 있다면 위 문제를 스무스?하게 풀 수 있을거라 생각합니다.</p>
<pre><code class="language-js">const map = Array.from({ length : 51} , () =&gt; new Array(51).fill(null));
const parents = Array.from({ length : 51 } , () =&gt; new Array(51).fill(0));

for(let i=1; i&lt;=50; i++){
    for(let j=1; j&lt;=50; j++){
        parents[i][j] = i * 50 + j;
    }
}

const update1 = (r,c,value) =&gt; {
    //todo
    //r,c와 병합된 다른 셀들의 값도 바꿔줘야함. 
    const p = parents[r][c];

    for(let i=1; i&lt;=50; i++){
        for(let j=1; j&lt;=50; j++){
            if (parents[i][j] === p){
                map[i][j] = value;
            }
        }
    }
}

const update2 = (value1, value2) =&gt; {
    for(let i=1; i&lt;=50; i++){
        for(let j=1; j&lt;=50; j++){
            if (map[i][j] === value1) map[i][j] = value2;
        }
    }
}

const merge = (r1, c1, r2, c2) =&gt; {
    if (r1 === r2 &amp;&amp; c1 === c2) return;
    //두 셀의 최상위 조상의 셀이 같은지 확인
    const aP = parents[r1][c1];
    const bP = parents[r2][c2];
    //최상위 조상이 같을 경우 패스
    if (aP === bP) return;
    //최상위 조상이 다를 경우
    if ((map[r1][c1] !== null &amp;&amp; map[r2][c2] !== null) || (map[r1][c1] !== null &amp;&amp; map[r2][c2] === null)){

        //(r2,c2)의  조상노드를 가지고 있는 모든 노드를 찾아 (r1,c1)값으로 변환
        for(let i=1; i&lt;=50; i++){
            for(let j=1; j&lt;=50; j++){
                if (parents[i][j] === bP){
                    parents[i][j] = aP;
                    map[i][j] = map[r1][c1];
                }
            }
        }
    } else if (map[r1][c1] === null &amp;&amp; map[r2][c2] !== null){
        //(r1,c1)의 조상노드를 가지고 있는 모든 노드를 찾아 (r2,c2)값으로 변환
        for(let i=1; i&lt;=50; i++){
            for(let j=1; j&lt;=50; j++){
                if (parents[i][j] === aP){
                    parents[i][j] = bP;
                    map[i][j] = map[r2][c2];
                }
            }
        }
    } else if (map[r1][c1] === null &amp;&amp; map[r2][c2] === null){
        for(let i=1; i&lt;=50; i++){
            for(let j=1; j&lt;=50; j++){
                if (parents[i][j] === aP){
                    parents[i][j] = bP;
                }
            }
        }
    }
}

const unmerge = (r,c) =&gt; {
    //todo
    const data = map[r][c];
    //(r,c)의 최상위 조상을 찾는다 
    const p = parents[r][c];
    //이 조상과 같은 모든 노드들의 조상값을 초기화시키고 null값으로 초기화
    for(let i=1; i&lt;=50; i++){
        for(let j=1; j&lt;=50; j++){
            if (parents[i][j] === p){
                parents[i][j] = 50 * i + j;
                map[i][j] = null;
            }
        }
    }
    //(r,c)만 해체 전 셀 값을 가지고 있음
    map[r][c] = data;
}

const print = (r,c) =&gt; {    
    if (map[r][c] === null) return &quot;EMPTY&quot;;
    return map[r][c];
}

function solution(commands) {

    const answer = [];
    for(let command of commands){
        const splitCommand = command.split(&#39; &#39;);
        const order = splitCommand[0];

        if (order === &#39;UPDATE&#39;){
            const len = splitCommand.length;
            //1. UPDATE r c value  =&gt; update1
            if (len === 4){
                update1(Number(splitCommand[1]), Number(splitCommand[2]), splitCommand[3]);
            } 
            //2. UPDATE value1 value2 =&gt; update2
            else if (len === 3){
                update2(splitCommand[1],splitCommand[2]);
            }
        } 
        //3. MERGE r1 c1 r2 c2
        else if(order === &#39;MERGE&#39;){
            const r1 = Number(splitCommand[1]);
            const c1 = Number(splitCommand[2]);
            const r2 = Number(splitCommand[3]);
            const c2 = Number(splitCommand[4]);
            merge(r1,c1,r2,c2);
        } 
        //4. UNMERGE r c
        else if (order === &#39;UNMERGE&#39;){
            const r = Number(splitCommand[1]);
            const c = Number(splitCommand[2]);
            unmerge(r,c);
        }
        //5. PRINT r c 
        else if (order === &#39;PRINT&#39;){
            const r = Number(splitCommand[1]);
            const c = Number(splitCommand[2]);
            answer.push(print(r,c));
        }
    }

    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[6087 레이저 통신]]></title>
            <link>https://velog.io/@hyuntae_/6087-%EB%A0%88%EC%9D%B4%EC%A0%80-%ED%86%B5%EC%8B%A0</link>
            <guid>https://velog.io/@hyuntae_/6087-%EB%A0%88%EC%9D%B4%EC%A0%80-%ED%86%B5%EC%8B%A0</guid>
            <pubDate>Tue, 28 Mar 2023 02:02:15 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>크기가 1×1인 정사각형으로 나누어진 W×H 크기의 지도가 있다. 지도의 각 칸은 빈 칸이거나 벽이며, 두 칸은 &#39;C&#39;로 표시되어 있는 칸이다.</p>
<p>&#39;C&#39;로 표시되어 있는 두 칸을 레이저로 통신하기 위해서 설치해야 하는 거울 개수의 최솟값을 구하는 프로그램을 작성하시오. 레이저로 통신한다는 것은 두 칸을 레이저로 연결할 수 있음을 의미한다.</p>
<p>레이저는 C에서만 발사할 수 있고, 빈 칸에 거울(&#39;/&#39;, &#39;&#39;)을 설치해서 방향을 90도 회전시킬 수 있다.</p>
<p>아래 그림은 H = 8, W = 7인 경우이고, 빈 칸은 &#39;.&#39;, 벽은 &#39;*&#39;로 나타냈다. 왼쪽은 초기 상태, 오른쪽은 최소 개수의 거울을 사용해서 두 &#39;C&#39;를 연결한 것이다.</p>
<p>7 . . . . . . .         7 . . . . . . .
6 . . . . . . C         6 . . . . . /-C
5 . . . . . . *         5 . . . . . | *
4 * * * * * . *         4 * * * * * | *
3 . . . . * . .         3 . . . . * | .
2 . . . . * . .         2 . . . . * | .
1 . C . . * . .         1 . C . . * | .
0 . . . . . . .         0 . -------/ .
  0 1 2 3 4 5 6           0 1 2 3 4 5 6</p>
<h1 id="입력">입력</h1>
<p>첫째 줄에 W와 H가 주어진다. (1 ≤ W, H ≤ 100)</p>
<p>둘째 줄부터 H개의 줄에 지도가 주어진다. 지도의 각 문자가 의미하는 것은 다음과 같다.</p>
<p>.: 빈 칸
*: 벽
C: 레이저로 연결해야 하는 칸
&#39;C&#39;는 항상 두 개이고, 레이저로 연결할 수 있는 입력만 주어진다.</p>
<h1 id="출력">출력</h1>
<p>첫째 줄에 C를 연결하기 위해 설치해야 하는 거울 개수의 최솟값을 출력한다.</p>
<h1 id="예제">예제</h1>
<h2 id="예제-입력-1">예제 입력 1</h2>
<p>7 8</p>
<pre><code class="language-js">.......
......C
......*
*****.*
....*..
....*..
.C..*..
.......</code></pre>
<h2 id="예제-출력-1">예제 출력 1</h2>
<p>3</p>
<hr>
<h1 id="풀이">풀이</h1>
<p>bfs문제이지만 고려해야할 조건이 있다. 흔히 bfs는 어떤 두 점 사이의 최소거리 값을 구하는 문제이지만 이번의 경우에는 조금 다르게 두 &#39;C&#39; 사이의 거리에 놓인 <strong>거울</strong>개수의 최소값을 구하는 것이다. 즉, &#39;C&#39;에서 다른 &#39;C&#39;로 이동하여 갈 때 이미 방문했는지 안했는지가 중요한게 아니라 거울개수가 최소인지가 중요한것이다. 또한 어느 한 점으로 들어오는 빛은 방향성을 가지기 때문에 동,서,남,북으로 진행하는 빛은 본인이 진행하는 방향의 빛을 제외하고는 거울이 필요하다는 것이다.</p>
<p>풀이 로직</p>
<ol>
<li>큐를 먼저 구현한다.</li>
<li>시작점 &#39;C&#39;에서 출발하는 네 가지 방향의 빛을 초기값을 세팅한다.</li>
<li>큐에서 하나 꺼낸다.
3-1. 진행중인 빛의 방향과 다르다면 거울의 개수를 +1
3-2. 진행중인 빛의 방향과 같다면 거울의 개수는 그대로 둔다.</li>
<li>다음 점의 거울개수보다 작거나 같다면 큐에 삽입한다.</li>
<li>큐에 남은게 없을 때까지 3~4를 반복한다.</li>
</ol>
<pre><code class="language-js">const [[W, H], ...map] = require(&#39;fs&#39;)
  .readFileSync(&#39;./dev/stdin&#39;)
  .toString()
  .trim()
  .split(&quot;\n&quot;)
  .map((el, idx) =&gt; (idx ? el.split(&quot;&quot;) : el.split(&quot; &quot;).map(Number)));

class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class Queue {
  constructor() {
    this.head = null;
    this.rear = null;
    this.length = 0;
  }

  isEmpty() {
    if (this.length === 0) return true;
    return false;
  }

  enqueue(data) {
    const node = new Node(data);

    if (!this.head) {
      this.head = node;
    } else {
      this.rear.next = node;
    }
    this.rear = node;
    this.length++;
  }

  dequeue() {
    if (!this.head) return false;

    const answer = this.head.data;
    this.head = this.head.next;
    this.length--;

    return answer;
  }
}

const visited = Array.from({ length: H }, () =&gt; new Array(W).fill(Infinity));
const posC = [];

for (let i = 0; i &lt; H; i++) {
  for (let j = 0; j &lt; W; j++) {
    if (map[i][j] === &quot;C&quot;) posC.push([i, j]);
  }
}
const [startR, startC] = posC[0];
const [endR, endC] = posC[1];
visited[startR][startC] = 0;

const dr = [-1, 0, 1, 0];
const dc = [0, 1, 0, -1];

const queue = new Queue();
for (let i = 0; i &lt; 4; i++) {
  queue.enqueue([startR, startC, i, 0]);
}

while (!queue.isEmpty()) {
  const [curR, curC, dir, mir] = queue.dequeue();

  for (let i = 0; i &lt; 4; i++) {
    const [nextR, nextC] = [curR + dr[i], curC + dc[i]];
    let nextMir = mir;
    // 주어진 맵 범위를 초과한 경우는 패스
    if (nextR &lt; 0 || nextC &lt; 0 || nextR &gt;= H || nextC &gt;= W) continue;
    // 벽인 경우는 패스
    if (map[nextR][nextC] === &quot;*&quot;) continue;
    // 현재 진행중인 방향과 다르다면 거울 +1
    if (dir !== i) nextMir++;
    if (visited[nextR][nextC] &gt;= nextMir) {
      queue.enqueue([nextR, nextC, i, nextMir]);
      visited[nextR][nextC] = nextMir;
    }
  }
}

console.log(visited[endR][endC]);
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[14442 벽 부수고 이동하기 2]]></title>
            <link>https://velog.io/@hyuntae_/14442-%EB%B2%BD-%EB%B6%80%EC%88%98%EA%B3%A0-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-2</link>
            <guid>https://velog.io/@hyuntae_/14442-%EB%B2%BD-%EB%B6%80%EC%88%98%EA%B3%A0-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-2</guid>
            <pubDate>Sat, 18 Mar 2023 09:21:15 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다.</p>
<p>만약에 이동하는 도중에 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 K개 까지 부수고 이동하여도 된다.</p>
<p>한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.</p>
<p>맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.</p>
<h1 id="입력">입력</h1>
<p>첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000), K(1 ≤ K ≤ 10)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.</p>
<h1 id="출력">출력</h1>
<p>첫째 줄에 최단 거리를 출력한다. 불가능할 때는 -1을 출력한다.</p>
<h1 id="예제">예제</h1>
<h2 id="예제-입력-1">예제 입력 1</h2>
<p>6 4 1
0100
1110
1000
0000
0111
0000</p>
<h2 id="예제-출력-1">예제 출력 1</h2>
<p>15</p>
<h2 id="예제-입력-2">예제 입력 2</h2>
<p>6 4 2
0100
1110
1000
0000
0111
0000</p>
<h2 id="예제-출력-2">예제 출력 2</h2>
<p>9</p>
<h2 id="예제-입력-3">예제 입력 3</h2>
<p>4 4 3
0111
1111
1111
1110</p>
<h2 id="예제-출력-3">예제 출력 3</h2>
<p>-1</p>
<h1 id="해결방법">해결방법</h1>
<p>기존의 최단거리 문제는 해당위치에서 동서남북 방향으로 아직 방문하지 않았다면 해당 노드를 방문하고 큐에 넣어 진행하는 방식이지만 이 문제에서는 벽을 깨부수는 경우가 추가되었다.</p>
<p>이 문제를 해결하기 위한 핵심포인트는 벽을 깨부순 상태를 각각 달리하는 것이다.
예를 들어 벽을 0개 부순 상태, 벽을 1개 부순 상태, 벽을 2개 부순 상태는 별개의 인격체를 가진다는 것이다. 즉, 벽의 개수마다 방문처리를 해줘야한다.</p>
<p>그래서 방문처리배열을 기존의 2차원 배열에서 3차원 배열로 변경시켜줘야한다.</p>
<p>하지만 한가지 더 문제가 생겼다. 큐에 다음 노드를 삽입하고 반복문을 돌다보면 메모리초과가 발생한다. 그 이유는 자바스크립트에는 큐가 내장되어있지 않아 일반 배열을 큐 처럼 사용하는데 이때 배열크기를 초과하기때문에 메모리가 초과가 발생했다. 이를 해결해 주기 위해서 클래스를 사용해 직접 큐를 구현해주었다.</p>
<pre><code class="language-js">const [[N, M, K], ...map] = `4 4 3
0111
1111
1111
1110`
  .toString()
  .trim()
  .split(&quot;\n&quot;)
  .map((el, idx) =&gt;
    idx ? el.split(&quot;&quot;).map(Number) : el.split(&quot; &quot;).map(Number)
  );
//큐 규현
class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}
class Queue {
  constructor() {
    this.head = null;
    this.rear = null;
    this.length = 0;
  }

  isEmpty() {
    if (this.length === 0) return true;
    return false;
  }

  enqueue(data) {
    const node = new Node(data);

    if (!this.head) {
      this.head = node;
    } else {
      this.rear.next = node;
    }
    this.rear = node;
    this.length++;
  }

  dequeue() {
    if (!this.head) return false;

    const data = this.head.data;
    this.head = this.head.next;
    this.length--;

    return data;
  }
}

const visited = Array.from({ length: N }, () =&gt;
  Array.from({ length: M }, () =&gt; new Array(K + 1).fill(0))
);

visited[0][0][0] = 1;
//큐 생성
const queue = new Queue();
queue.enqueue([0, 0, 0, 1]);
let idx = 0;
const dr = [-1, 0, 1, 0];
const dc = [0, 1, 0, -1];

while (!queue.isEmpty()) {
  const [curRow, curCol, wall, step] = queue.dequeue();

  for (let i = 0; i &lt; 4; i++) {
    const [nextRow, nextCol] = [curRow + dr[i], curCol + dc[i]];

    if (nextRow &lt; 0 || nextRow &gt;= N || nextCol &lt; 0 || nextCol &gt;= M) continue;

    //벽이고 다음 벽까지 합쳐서 K개 이하이고 방문한 적 없음
    if (
      map[nextRow][nextCol] === 1 &amp;&amp;
      wall + 1 &lt;= K &amp;&amp;
      visited[nextRow][nextCol][wall + 1] === 0
    ) {
      visited[nextRow][nextCol][wall + 1] = step + 1;
      queue.enqueue([nextRow, nextCol, wall + 1, step + 1]);
    }

    //벽이 아니고 방문한 적 없음
    if (map[nextRow][nextCol] === 0 &amp;&amp; visited[nextRow][nextCol][wall] === 0) {
      visited[nextRow][nextCol][wall] = step + 1;
      queue.enqueue([nextRow, nextCol, wall, step + 1]);
    }
  }
}

//최소값 반환
let min = Number.MAX_SAFE_INTEGER;
for (let i = 0; i &lt;= K; i++) {
  const step = visited[N - 1][M - 1][i];
  if (step === 0) continue;
  min = Math.min(min, step);
}
min === Number.MAX_SAFE_INTEGER ? console.log(-1) : console.log(min);
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[성능최적화]]></title>
            <link>https://velog.io/@hyuntae_/%EC%84%B1%EB%8A%A5%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@hyuntae_/%EC%84%B1%EB%8A%A5%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Mon, 13 Mar 2023 16:09:51 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트를 사용하면서 성능최적화에 대해 궁금해졌고 이를 위해 어떠한 Hook이 있는지 찾아봤다. 대표적으로 useMemo와 useCallback Hook이 존재했고 이에 대해 학습한 내용을 정리해본다.</p>
</blockquote>
<h1 id="usememo">useMemo</h1>
<p>useMemo는 렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고, 원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용하는 방식이다. 바로 실습을 통해 알아보자</p>
<pre><code class="language-jsx">import { useState } from &#39;react&#39;

const getAverage = (numbers) =&gt; {
  console.log(&quot;평균값 계산중 ...&quot;);
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) =&gt; a + b);
  return sum / numbers.length;
};

const Average = () =&gt; {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState(&quot;&quot;);

  const onChange = (e) =&gt; {
    setNumber(e.target.value);
  }

  const onInsert = () =&gt; {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber(&quot;&quot;);
  }

  return (
    &lt;div&gt;
      &lt;input value={number} onChange={onChange} /&gt;
      &lt;button onClick={onInsert}&gt;등록&lt;/button&gt;
      &lt;ul&gt;
        {list.map((value, index) =&gt; (
          &lt;li key={index}&gt;{value}&lt;/li&gt;
        ))}
      &lt;/ul&gt;
      &lt;div&gt;
        &lt;b&gt;평균값 : &lt;/b&gt; {getAverage(list)}
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Average;
</code></pre>
<p>위 코드를 복사하여 실행해보면 다음과 같이 실행된다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/6763e9df-dde8-43dc-9f6f-170c16f6c3eb/image.gif" alt=""></p>
<p>input에 값을 생성할 때마다 평균값이 구해지는게 보인다. 하지만 우리는 렌더링마다 평균값 구하기를 원하지 않는다. 즉, 값을 입력하고 등록을 눌렀을 경우메나 평균값 함수를 실행시키고 싶다. 이를 해결하기 위해서 useMemo를 사용한다.</p>
<p>useMemo를 추가한 코드를 살펴보자</p>
<pre><code class="language-jsx">import { useMemo, useState } from &quot;react&quot;;
const getAverage = (numbers) =&gt; {
  console.log(&quot;평균값 계산중 ...&quot;);
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) =&gt; a + b);
  return sum / numbers.length;
};

const Average = () =&gt; {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState(&quot;&quot;);

  const onChange = (e) =&gt; {
    setNumber(e.target.value);
  };

  const onInsert = () =&gt; {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber(&quot;&quot;);
  };

  // useMemo 생성
  const avg = useMemo(() =&gt; getAverage(list), [list]);

  return (
    &lt;div&gt;
      &lt;input value={number} onChange={onChange} /&gt;
      &lt;button onClick={onInsert}&gt;등록&lt;/button&gt;
      &lt;ul&gt;
        {list.map((value, index) =&gt; (
          &lt;li key={index}&gt;{value}&lt;/li&gt;
        ))}
      &lt;/ul&gt;
      &lt;div&gt;
        &lt;b&gt;평균값 : &lt;/b&gt; {avg}
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Average;
</code></pre>
<p><code>useMemo(()=&gt; getAverage(list), [list]);</code> 이와 같은 코드를 추가해줬다. 코드를 해석해보면 <strong style="color:slateblue">두번째 인자로 넘겨준 값이 변할 때만 getAverage함수를 실행시켜줘!!</strong>라고 이해하면 된다. 실행을 하면 값을 등록했을 때만 평균값을 구하는 함수가 실행된것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/d7afa565-1a81-409f-9c6c-a55314268923/image.gif" alt=""></p>
<h2 id="import-react-from-react를-사용안해도-되나요">import React from &#39;react&#39;를 사용안해도 되나요??</h2>
<p>React v17버젼부터는 React내부적으로 JSX transformer가 JSX를 React 요소로 변환하는  작업을 거치기 때문에 모든 컴포넌트에 <code>import React from &#39;react&#39;</code>를 작성하지 않아도 된다.
<img src="https://velog.velcdn.com/images/hyuntae_/post/3a6b303d-6cdd-4b97-a4bd-0f62fdf2ed9b/image.png" alt=""></p>
<p>위 코드를 아래 코드처럼 작성하여도 컴파일단계에서 에러가 발생하지 않는다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/c85aef46-f0c2-44c3-9551-6fb32ced4572/image.png" alt=""></p>
<p>또한 <strong style='color:skyblue'>React v18부터는 ReactDOM.render() 대신에 ReactDOM.createRoot().render() 사용해야 한다.!!</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[트리]]></title>
            <link>https://velog.io/@hyuntae_/%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@hyuntae_/%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Wed, 08 Mar 2023 04:29:40 GMT</pubDate>
            <description><![CDATA[<h1 id="트리란">트리란?</h1>
<p>Tree는 graph의 특수한 경우로서 다음과 같은 특징을 가진다.</p>
<p>트리는 각 노드사이에는 오직 한 개의 간선으로 이루어져 있으며(연결성), 각 노드를 연결했는 때 순환하는 경로가 존재해서는 안된다.(비순환성)</p>
<p>Tree의 용어를 살펴보면 다음과 같다.</p>
<ul>
<li>Root : 모든 노드의 조상이자 트리내에서 가장 최상단에 위치하며 오직 1개 존재한다.</li>
<li>비단말 : 자식이 존재하는 노드</li>
<li>단말 : 자식이 없고 마지막 비단말 노드 아래에 위치한 노드</li>
</ul>
<p>뿐만아니라 K-ary tree, 즉 K진 트리라고 불리우는 모든 node의 자식이 k개 이하인 tree가 있다.</p>
<ul>
<li>k=2 =&gt; 이진트리</li>
<li>k=4 =&gt; 4진트리</li>
<li>k=8 =&gt; 8진트리</li>
</ul>
<h1 id="이진트리의-종류">이진트리의 종류</h1>
<h2 id="포화-이진트리">포화 이진트리</h2>
<ul>
<li>말 그래도 포화다.</li>
<li>즉, 모든 비단말 노드(단말제외)가 2개의 노드를 가진다.</li>
<li>모든 단말노드는 동일한 level 즉, 같은 높이에 있다.</li>
</ul>
<pre><code>total of nodes N = 2^0 + 2^1 + 2^2 + ... + 2^h = 2^(h+1) - 1
위의 식을 계산하면 아래와 같다.
h = log(N+1) - 1 = log N</code></pre><h2 id="완전-이진트리">완전 이진트리</h2>
<ul>
<li>트리의 높이를 h라 가정하였을 때 h-1높이까지는 포화 이진트리이다.</li>
<li>즉, h-1높이까지는 모든 노드들이 꽉 채워진다.</li>
<li>포화 이진트리와 차이점은 마지막 h높이에는 모든 노드들이 꽉 차있는 것은 아니다.</li>
<li>그래서 마지막 레벨이 가질 수 있는 노드의 개수는 1개 이상 2^h - 1개이다.</li>
</ul>
<pre><code>2^h - 1 &lt; N &lt;= 2^(h+1) - 1

2^h &lt;= N &lt; 2^(h+1)
h &lt;= log N &lt; h+1
h = Math.floor(log N) = log N

</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[9205 맥주 마시면서 걸어가기]]></title>
            <link>https://velog.io/@hyuntae_/9205-%EB%A7%A5%EC%A3%BC-%EB%A7%88%EC%8B%9C%EB%A9%B4%EC%84%9C-%EA%B1%B8%EC%96%B4%EA%B0%80%EA%B8%B0</link>
            <guid>https://velog.io/@hyuntae_/9205-%EB%A7%A5%EC%A3%BC-%EB%A7%88%EC%8B%9C%EB%A9%B4%EC%84%9C-%EA%B1%B8%EC%96%B4%EA%B0%80%EA%B8%B0</guid>
            <pubDate>Wed, 08 Mar 2023 02:28:02 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>송도에 사는 상근이와 친구들은 송도에서 열리는 펜타포트 락 페스티벌에 가려고 한다. 올해는 맥주를 마시면서 걸어가기로 했다. 출발은 상근이네 집에서 하고, 맥주 한 박스를 들고 출발한다. 맥주 한 박스에는 맥주가 20개 들어있다. 목이 마르면 안되기 때문에 50미터에 한 병씩 마시려고 한다. 즉, 50미터를 가려면 그 직전에 맥주 한 병을 마셔야 한다.</p>
<p>상근이의 집에서 페스티벌이 열리는 곳은 매우 먼 거리이다. 따라서, 맥주를 더 구매해야 할 수도 있다. 미리 인터넷으로 조사를 해보니 다행히도 맥주를 파는 편의점이 있다. 편의점에 들렸을 때, 빈 병은 버리고 새 맥주 병을 살 수 있다. 하지만, 박스에 들어있는 맥주는 20병을 넘을 수 없다. 편의점을 나선 직후에도 50미터를 가기 전에 맥주 한 병을 마셔야 한다.</p>
<p>편의점, 상근이네 집, 펜타포트 락 페스티벌의 좌표가 주어진다. 상근이와 친구들이 행복하게 페스티벌에 도착할 수 있는지 구하는 프로그램을 작성하시오.</p>
<h1 id="입력">입력</h1>
<p>첫째 줄에 테스트 케이스의 개수 t가 주어진다. (t ≤ 50)</p>
<p>각 테스트 케이스의 첫째 줄에는 맥주를 파는 편의점의 개수 n이 주어진다. (0 ≤ n ≤ 100).</p>
<p>다음 n+2개 줄에는 상근이네 집, 편의점, 펜타포트 락 페스티벌 좌표가 주어진다. 각 좌표는 두 정수 x와 y로 이루어져 있다. (두 값 모두 미터, -32768 ≤ x, y ≤ 32767)</p>
<p>송도는 직사각형 모양으로 생긴 도시이다. 두 좌표 사이의 거리는 x 좌표의 차이 + y 좌표의 차이 이다. (맨해튼 거리)</p>
<h1 id="출력">출력</h1>
<p>각 테스트 케이스에 대해서 상근이와 친구들이 행복하게 페스티벌에 갈 수 있으면 &quot;happy&quot;, 중간에 맥주가 바닥나서 더 이동할 수 없으면 &quot;sad&quot;를 출력한다. </p>
<h1 id="예제">예제</h1>
<h2 id="예제-입력-1">예제 입력 1</h2>
<p>2
2
0 0
1000 0
1000 1000
2000 1000
2
0 0
1000 0
2000 1000
2000 2000</p>
<h2 id="예제-출력-1">예제 출력 1</h2>
<p>happy
sad</p>
<h1 id="해결방안">해결방안</h1>
<p>출발지점에서 도착지점까지 갈 수 있는지 없는지를 구해야 하는데 중요한 것은 도착지점까지 가는 도중에 편의점에 들릴지 말지 결정하는 부분이였다고 생각한다. 그래서 나는 다음과 같은 로직을 토대로 구현해봤다.</p>
<ol>
<li>처음 시작지점에서 도착지점까지 거리가 1000이하 이면 종료(happy출력)</li>
<li>시작지점에서 각각의 편의점까지 거리가 1000인 편의점을 방문처리하고 큐에 넣는다.</li>
<li>큐에서 편의점을 하나 꺼내고 이 편의점에서 갈 수 있는 다음 편의점이 있다면 방문처리 후 큐에 넣는다.
3-1. 도착지점까지 거리가 1000이하라면 반복문 종료(happy출력)</li>
<li>3번 반복</li>
<li>큐를 탐색하는 동안 도착지점에 도착하지 못했다면 sad출력</li>
</ol>
<h1 id="배운점">배운점</h1>
<p>방문처리하는 부분에 대해서 Map 자료구조를 사용했는데 그 이유는 키의 자료형은 string뿐만 다른 자료형도 들어갈 수 있다고 생각해서였다. 하지만 원시타입만 가능할 뿐 참조타입은 안된다는것을 몰랐기에 Map의 키값으로 배열을 넣고 방문처리를 구현할 수 없었다.
[1,2] === [1,2] =&gt; false</p>
<h1 id="코드">코드</h1>
<pre><code class="language-js">const [TC, ...input] = `4
2
0 0
1000 0
1000 1000
2000 1000
2
0 0
1000 0
2000 1000
2000 2000
0
0 0
500 501
0
0 0
500 499`
  .toString()
  .trim()
  .split(&quot;\n&quot;);
// console.log(TC, input);
const answer = [];

const distanceBetweenTwoPoints = (x1, y1, x2, y2) =&gt; {
  return Math.abs(x1 - x2) + Math.abs(y1 - y2);
};

for (let i = 0; i &lt; input.length; i++) {
  const storeCnt = +input[i++];
  const start = input[i++].split(&quot; &quot;).map(Number);
  const store = [];
  let cnt = 0;
  for (let j = i; j &lt; i + storeCnt; j++) {
    const [x, y] = input[j].split(&quot; &quot;).map(Number);
    store.push([x, y, cnt++]);
  }
  i += storeCnt;
  const end = input[i].split(&quot; &quot;).map(Number);

  //   console.log(&quot;편의점 개수 : &quot;, storeCnt);
  //   console.log(&quot;출발지점 : &quot;, start);
  //   console.log(&quot;편의점 모음 : &quot;, store);
  //   console.log(&quot;도착지점 : &quot;, end);

  if (distanceBetweenTwoPoints(start[0], start[1], end[0], end[1]) &lt;= 1000) {
    answer.push(&quot;happy&quot;);
    continue;
  }

  const visited = new Array(store.length).fill(0);
  const queue = [];
  for (let k = 0; k &lt; store.length; k++) {
    const [storeX, storeY, index] = store[k];

    if (distanceBetweenTwoPoints(start[0], start[1], storeX, storeY) &lt;= 1000) {
      queue.push([storeX, storeY, index]);
      visited[index] = 1;
    }
  }
  //   console.log(queue);
  let idx = 0;
  let flag = 0;
  while (idx &lt; queue.length) {
    const [curR, curC, index] = queue[idx++];

    if (distanceBetweenTwoPoints(curR, curC, end[0], end[1]) &lt;= 1000) {
      answer.push(&quot;happy&quot;);
      flag = 1;
      break;
    }
    for (let i = 0; i &lt; store.length; i++) {
      const [storeX, storeY, idx] = store[i];

      if (
        distanceBetweenTwoPoints(curR, curC, storeX, storeY) &lt;= 1000 &amp;&amp;
        !visited[idx]
      ) {
        queue.push([storeX, storeY, idx]);
        visited[idx] = 1;
      }
    }
  }
  if (flag) continue;
  answer.push(&quot;sad&quot;);
}
console.log(answer.join(&quot;\n&quot;));
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2573 빙산]]></title>
            <link>https://velog.io/@hyuntae_/2573-%EB%B9%99%EC%82%B0</link>
            <guid>https://velog.io/@hyuntae_/2573-%EB%B9%99%EC%82%B0</guid>
            <pubDate>Tue, 07 Mar 2023 05:34:19 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>지구 온난화로 인하여 북극의 빙산이 녹고 있다. 빙산을 그림 1과 같이 2차원 배열에 표시한다고 하자. 빙산의 각 부분별 높이 정보는 배열의 각 칸에 양의 정수로 저장된다. 빙산 이외의 바다에 해당되는 칸에는 0이 저장된다. 그림 1에서 빈칸은 모두 0으로 채워져 있다고 생각한다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/af192bcd-761a-4662-bebd-093e4996b314/image.png" alt=""></p>
<p>그림 1. 행의 개수가 5이고 열의 개수가 7인 2차원 배열에 저장된 빙산의 높이 정보</p>
<p>빙산의 높이는 바닷물에 많이 접해있는 부분에서 더 빨리 줄어들기 때문에, 배열에서 빙산의 각 부분에 해당되는 칸에 있는 높이는 일년마다 그 칸에 동서남북 네 방향으로 붙어있는 0이 저장된 칸의 개수만큼 줄어든다. 단, 각 칸에 저장된 높이는 0보다 더 줄어들지 않는다. 바닷물은 호수처럼 빙산에 둘러싸여 있을 수도 있다. 따라서 그림 1의 빙산은 일년후에 그림 2와 같이 변형된다.</p>
<p>그림 3은 그림 1의 빙산이 2년 후에 변한 모습을 보여준다. 2차원 배열에서 동서남북 방향으로 붙어있는 칸들은 서로 연결되어 있다고 말한다. 따라서 그림 2의 빙산은 한 덩어리이지만, 그림 3의 빙산은 세 덩어리로 분리되어 있다.</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/a300d527-34b6-4a36-ba49-9acde8bc4560/image.png" alt=""><br>그림 2</p>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/a02c7d8c-e1b8-41cb-8cd7-2fbce7358a2c/image.png" alt=""></p>
<p>그림 3</p>
<p>한 덩어리의 빙산이 주어질 때, 이 빙산이 두 덩어리 이상으로 분리되는 최초의 시간(년)을 구하는 프로그램을 작성하시오. 그림 1의 빙산에 대해서는 2가 답이다. 만일 전부 다 녹을 때까지 두 덩어리 이상으로 분리되지 않으면 프로그램은 0을 출력한다.</p>
<h1 id="입력">입력</h1>
<p>첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을 나타내는 M개의 정수가 한 개의 빈 칸을 사이에 두고 주어진다. 각 칸에 들어가는 값은 0 이상 10 이하이다. 배열에서 빙산이 차지하는 칸의 개수, 즉, 1 이상의 정수가 들어가는 칸의 개수는 10,000 개 이하이다. 배열의 첫 번째 행과 열, 마지막 행과 열에는 항상 0으로 채워진다.</p>
<h1 id="출력">출력</h1>
<p>첫 줄에 빙산이 분리되는 최초의 시간(년)을 출력한다. 만일 빙산이 다 녹을 때까지 분리되지 않으면 0을 출력한다.</p>
<h1 id="예제">예제</h1>
<h2 id="예제-입력-1">예제 입력 1</h2>
<p>5 7
0 0 0 0 0 0 0
0 2 4 5 3 0 0
0 3 0 2 5 2 0
0 7 6 2 4 0 0
0 0 0 0 0 0 0</p>
<h2 id="예제-출력-1">예제 출력 1</h2>
<p>2</p>
<hr>
<p>위 문제는 bfs 또는 dfs로 풀이가 가능한 문제이다. 즉 탐색문제라는 것이다. 그 이유는 1년 마다 모든 빙산의 위,아래,좌,우를 탐색하여 인접 칸이 0이라면 인접 칸의 개수만큼 빙산의 높이를 감소시키고 그 이후에 다시 모든 배열을 탐색하여 빙산의 구역이 2개 이상으로 나눠졌으면 그 때까지 몇 년이 걸리는지 알아보기 때문이다.</p>
<p>그래서 나는 크게 2가지의 로직으로 구현해봤다.(물론 다르게 생각하신 분도 계실거라고 생각하기때문에 참고만해주시면 감사하겠습니다!!)</p>
<ol>
<li>모든 빙산이 1년 마다 감소되는 높이를 측정하여 큐에 넣어준다.</li>
<li>N년이 지나고 N+1년이 되는 해가 시작되는 지점이 왔을 때 모든 배열이 탐색하여 빙산의 구역이 몇 구역으로 나눠지는지 파악한다.</li>
</ol>
<pre><code class="language-js">let [[N, M], ...map] = require(&#39;fs&#39;)
  .readFileSync(&#39;./dev/stdin&#39;)
  .toString()
  .trim()
  .split(&quot;\n&quot;)
  .map((el) =&gt; el.split(&quot; &quot;).map(Number));

let copyMap = JSON.parse(JSON.stringify(map));
const dr = [-1, 0, 1, 0];
const dc = [0, 1, 0, -1];
const queue = [];

for (let i = 0; i &lt; N; i++) {
  for (let j = 0; j &lt; M; j++) {
    if (map[i][j] !== 0) queue.push([i, j, map[i][j], 0]);
  }
}
&lt;!-- 빙산이 총 몇 구역인지 탐색&gt;
const checkMap = (param) =&gt; {
  const visited = Array.from({ length: N }, () =&gt; new Array(M).fill(0));

  const bfs = (row, col) =&gt; {
    visited[row][col] = 1;

    const queue = [[row, col]];
    let idx = 0;

    while (idx &lt; queue.length) {
      const [curR, curC] = queue[idx++];

      for (let i = 0; i &lt; 4; i++) {
        const [nextR, nextC] = [curR + dr[i], curC + dc[i]];

        if (
          nextR &gt;= 0 &amp;&amp;
          nextC &gt;= 0 &amp;&amp;
          nextR &lt; N &amp;&amp;
          nextC &lt; M &amp;&amp;
          param[nextR][nextC] !== 0 &amp;&amp;
          !visited[nextR][nextC]
        ) {
          visited[nextR][nextC] = 1;
          queue.push([nextR, nextC]);
        }
      }
    }
  };
  let count = 0;
  for (let i = 0; i &lt; N; i++) {
    for (let j = 0; j &lt; M; j++) {
      if (param[i][j] !== 0 &amp;&amp; !visited[i][j]) {
        count += 1;
        if (count &gt;= 2) return true;
        bfs(i, j);
      }
    }
  }
  return false;
};

let idx = 0;
let year = 0;
while (queue.length) {
  const [curRow, curCol, level, curYear] = queue.shift();
  &lt;!-- 새롭게 해가 바뀌는 구간&gt;
  if (year !== curYear) {
    year = curYear;
    map = JSON.parse(JSON.stringify(copyMap));
    if (checkMap(map)) {
      console.log(year);
      return;
    }
  }
&lt;!-- 모든 빙산의 위,아래,왼쪽,오른쪽을 탐색하여 높이 감소&gt;
  let count = 0;
  for (let i = 0; i &lt; 4; i++) {
    const [nextRow, nextCol] = [curRow + dr[i], curCol + dc[i]];

    if (
      nextRow &gt;= 0 &amp;&amp;
      nextCol &gt;= 0 &amp;&amp;
      nextRow &lt; N &amp;&amp;
      nextCol &lt; M &amp;&amp;
      map[nextRow][nextCol] === 0
    ) {
      count += 1;
    }
  }
  const nextLevel = level - count;
  nextLevel &gt; 0
    ? (copyMap[curRow][curCol] = nextLevel)
    : (copyMap[curRow][curCol] = 0);
  if (copyMap[curRow][curCol] !== 0)
    queue.push([curRow, curCol, copyMap[curRow][curCol], curYear + 1]);
}
console.log(0);
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[메모리]]></title>
            <link>https://velog.io/@hyuntae_/%EB%A9%94%EB%AA%A8%EB%A6%AC</link>
            <guid>https://velog.io/@hyuntae_/%EB%A9%94%EB%AA%A8%EB%A6%AC</guid>
            <pubDate>Sat, 04 Mar 2023 02:18:47 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hyuntae_/post/f49c1758-eb72-4a84-859b-09eef61aff66/image.png" alt=""></p>
<p>CPU관점에서 간략하게 살펴보자</p>
<ol>
<li>CPU는 메모리에 프로그램이 올라와야 읽고 실행 할 수 있다!</li>
<li>메모리에 올라온 프로그램 -&gt; 프로세스!</li>
</ol>
<ul>
<li>OS는 프로세스를 어떻게 관리하는가? PCB</li>
<li>프로세스의 라이프 사이클, 쓰레드, 스케쥴링</li>
<li>IPC, 그리고 공유자원 문제, 교착상태</li>
</ul>
<h1 id="메모리-관리">메모리 관리</h1>
<h2 id="운영체제에-의해-메모리-관리가-필요한-이유">운영체제에 의해 메모리 관리가 필요한 이유</h2>
<h3 id="메모리는-공유자원">메모리는 공유자원</h3>
<ul>
<li>여러 프로세스 사이에 메모리 공유</li>
</ul>
<h3 id="메모리-보호">메모리 보호</h3>
<ul>
<li>프로세스의 독립된 메모리 공간 보장</li>
<li>다른 프로세스로부터 보호</li>
</ul>
<h3 id="메모리-용량-한계-극복">메모리 용량 한계 극복</h3>
<ul>
<li>여러 프로세스의 메모리 합이 설치된 물리 메모리보다 큰 경우 필요</li>
</ul>
<h3 id="메모리-효율성-증대">메모리 효율성 증대</h3>
<ul>
<li>가능하면 많은 개수의 프로세스를 실행시키기 위해서</li>
<li>프로세스당 최소한의 메모리 할당</li>
</ul>
<h3 id="메모리는-컴퓨터-시스템-곳곳에-계층적으로-존재">메모리는 컴퓨터 시스템 곳곳에 계층적으로 존재</h3>
<ul>
<li>CPU레지스터 - CPU캐시 - 메인메모리 - 보조기억장치</li>
<li>CPU레지스터에서 보조기억장치로 갈수록<ul>
<li>용량 증가, 가격 저렴, 속도 저하</li>
</ul>
</li>
<li>메모리 계층 구조의 중심은 메인 메모리</li>
</ul>
<blockquote>
<p>그렇다면 왜 메모리를 계층화 했을까?</p>
</blockquote>
<p>바로 CPU가 메모리에 접근하는 시간은 최대한 줄여 프로그램의 실행 속도를 올리기 위해서이다.
<img src="https://velog.velcdn.com/images/hyuntae_/post/00d65341-abfb-4f1f-baf7-0f02cc7159b1/image.png" alt=""></p>
<h2 id="메모리-관리는-왜-어려운가">메모리 관리는 왜 어려운가?</h2>
<ol>
<li>폰노이만 구조 -&gt; 메모리는 컴퓨터의 유일한 작업공간이라 모든 프로그램은 메모리에 올라온다.</li>
</ol>
<ul>
<li>일괄처리 시스템 : 한번에 하나의 프로그램만 실행시킴으로 메모리 관리가 단순하다.</li>
<li>다중 프로그래밍 : 운영체제를 포함한 모든 응용 프로그램이 메모리에 올라와야 한다.</li>
</ul>
<ol start="2">
<li>유일한 작업공간을 같이 쓰다보니..</li>
</ol>
<ul>
<li>여러 프로세스간 침범이 안되게 하면서</li>
<li>그러면서 필요한 공간은 충분히 할당해줘야 하고</li>
<li>가능한 많은 프로세스를 동작시켜야 하는데, 메모리는 부족하고...</li>
</ul>
<h2 id="메모리-관리의-이중성">메모리 관리의 이중성</h2>
<ul>
<li>프로세스 : 가능한 많은 메모리를 쓰고 싶어함(메모리 독점)</li>
<li>OS/관리자 : 가능한 메모리를 적게 주고 싶어함. 가능한 효율적으로..</li>
</ul>
<h2 id="컴퓨터의-메모리를-어떻게-관리-할-것인가">컴퓨터의 메모리를 어떻게 관리 할 것인가?</h2>
<p>-&gt; 메모리 관리자의 작업/정책</p>
<ol>
<li>적재 정책(Fetch policy)</li>
</ol>
<ul>
<li>프로세스가 필요로 하는 데이터를 언제 메모리로 가져올지 결정하는 정책</li>
<li>요구반입 vs 예상반입</li>
</ul>
<ol start="2">
<li>배치 정책(Placement policy)</li>
</ol>
<ul>
<li>가져온 프로세스를 메모리의 어떤위치에 올려놓을지 결정하는 정책</li>
<li>최초 적합 vs 최적 적합 vs 최악 적합</li>
</ul>
<ol start="3">
<li>재배치(교체) 정책(Replacement policy)</li>
</ol>
<ul>
<li>메모리가 꽉 찼을 때 메모리 내에 있는 어떤 프로세스를 내보낼지 결정하는 정책</li>
<li>최적 교체, FIFO, LRU(Least Recently Used), LFU(Least Frequently Used)</li>
</ul>
<h2 id="mmu">MMU</h2>
<blockquote>
<p>Memory Management Unit의 약자로 일반적으로 MMU가 메모리 관리자로 불린다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/d86a7675-2da9-4c14-ac33-512e1589f4df/image.png" alt=""></p>
<p>위 그림에서 빨간색 점선을 따라가보자</p>
<ol>
<li>논리주소를 물리주소로 바꾸는 하드웨어 장치</li>
</ol>
<ul>
<li>CPU가 발생시킨 논리 주소는 MMU에 의해 물리 주소로 바뀌어 메모리에 도달한다.</li>
</ul>
<ol start="2">
<li>오늘날 MMU는 CPU 안에 내장</li>
</ol>
<ul>
<li>MMU 덕분에 여러 프로세스가 하나의 메모리에서 실행될 수 있다.</li>
</ul>
<h1 id="메모리-오버레이">메모리 오버레이</h1>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/58f64e4f-4262-4bc8-8203-4d665c5092f2/image.png" alt=""></p>
<blockquote>
<p>프로그램의 크기가 실제 메모리(물리 메모리)보다 클 때 전체 프로그램을 메모리에 가져오는 대신 적당한 크기로 잘라서 가져오는 기법 즉, 프로그램이 실행되면 현재 필요한 부분만 메모리에 올라와 실행 -&gt; 바꿔가면서(swap)</p>
</blockquote>
<h2 id="가상메모리-공간">가상메모리 공간</h2>
<ul>
<li>각 프로세스는 자신이 <code>모든</code> 메모리를 점유하고 있는 것처럼 생각한다.</li>
</ul>
<h2 id="swap">swap</h2>
<ul>
<li>메모리가 모자라서 쫓겨난 프로세스를 저장장치의 특별한 공간에 모아두는 영역</li>
<li>메모리에서 쫓겨났다가 다시 돌아가는 데이터가 머무는 곳이기에 저장장치는 장소만 빌려주고 메모리 관리자가 관리</li>
<li>사용자는 실제 메모리의 크기와 스왑 영역의 크기를 합쳐서 전체 메모리로 인식하고 사용</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Network 구성 및 링크 계층(1)]]></title>
            <link>https://velog.io/@hyuntae_/Network-%EA%B5%AC%EC%84%B1-%EB%B0%8F-%EB%A7%81%ED%81%AC-%EA%B3%84%EC%B8%B51</link>
            <guid>https://velog.io/@hyuntae_/Network-%EA%B5%AC%EC%84%B1-%EB%B0%8F-%EB%A7%81%ED%81%AC-%EA%B3%84%EC%B8%B51</guid>
            <pubDate>Thu, 02 Mar 2023 13:39:37 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hyuntae_/post/2f6aea82-59fa-42cb-9a59-73554e38fdc3/image.png" alt=""></p>
<p>노트북, 스마트폰에서 네이버에 접속하는 과정</p>
<ol>
<li>Network Edge인 스마트폰이나 노트북이</li>
<li>Access Network인 와이파이에 접속하여</li>
<li>Network core의 기능을 통해 네이버 서버에 패킷을 전송하거나 받아온다.</li>
</ol>
<p>정리</p>
<ol>
<li>Network edge는 end system 즉, 사용자들의 스마트폰이나 PC, 회사 서버 등 클라이언트나 서버</li>
<li>Access Network는 end system들이 인터넷을 사용할 수 있도록 길을 열어주는 것.</li>
<li>Network core는 forwarding과 routing을 통해 패킷을 전송</li>
</ol>
<ul>
<li>forwarding이란? 라우터의 <code>Input</code>으로 들어오는 패킷을 올바른 <code>Output</code>으로 보내는 기능(forwarding table을 읽어서)</li>
<li>routing이란? 패킷이 어디로 가야하는지 판단하는 과정 즉, forwarding table을 만드는 과정.</li>
</ul>
<h1 id="알고싶은-것들">알고싶은 것들</h1>
<ul>
<li>단일 링크로 전송하기 위해서 네트워크 계층 데이터그램을 어떻게 링크 계층 프레임으로 캡슐화하는지</li>
<li>통신 경로상에서 링크마다 상이한 링크 계층 프로토콜이 사용되는지</li>
<li>브로드캐스트 링크에서의 전송 충돌을 어떻게 해결하는지</li>
<li>링크 계층에서 주소체계가 있는지, 있다면 링크 계층 주소체계가 네트워크 계층 주소체계와 어떻게 상호 동작하는지</li>
<li>스위치와 라우터의 차이가 정확하게 무엇인지</li>
<li>현재 가장 많이 사용하는 유선 랜 기술인 이더넷을 포함한 스위치 랜(switched LAN)과 다중 접속망에 대해..</li>
<li>가상(virtual) 랜, 데이터 센터 네트워크(data center network)</li>
</ul>
<h1 id="링크-계층의-두-가지-종류">링크 계층의 두 가지 종류</h1>
<h2 id="브로드-캐스트-채널">브로드 캐스트 채널</h2>
<ul>
<li>일반적으로 무선 랜(LAN), 위성 네트워크, HFC(hybrid fiber-coaxial cable) 접속망에서 <code>다수의 호스트</code>를 연결해 준다.</li>
<li>다수의 호스트가 연결되기 때문에 <code>프레임 전송을 조정히기 위해</code> 매체 접속 프로토콜(media access protocol)이 있어야 한다.</li>
<li>경우에 따라서는 <code>중앙 제어기</code>를 사용해서 전송을 조정할 수도 있다.</li>
</ul>
<h2 id="점대점point-to-point통신">점대점(Point-to-Point)통신</h2>
<ul>
<li>원거리 링크에 의해 연결된 <code>두 라우터</code> 또는 사용자의 사무실 컴퓨터와 이 컴퓨터에 연결된 근처 <code>이더넷 스위치 사이</code>에서 사용될 수 있다.</li>
</ul>
<p>다음 시간에는 오류검출 및 정정기술, 다중 접속 링크와 프로토콜에 대해서 알아보자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[고정소수점, 부동소수점]]></title>
            <link>https://velog.io/@hyuntae_/%EA%B3%A0%EC%A0%95%EC%86%8C%EC%88%98%EC%A0%90-%EB%B6%80%EB%8F%99%EC%86%8C%EC%88%98%EC%A0%90</link>
            <guid>https://velog.io/@hyuntae_/%EA%B3%A0%EC%A0%95%EC%86%8C%EC%88%98%EC%A0%90-%EB%B6%80%EB%8F%99%EC%86%8C%EC%88%98%EC%A0%90</guid>
            <pubDate>Wed, 01 Mar 2023 17:21:13 GMT</pubDate>
            <description><![CDATA[<h1 id="고정소수점이란">고정소수점이란?</h1>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/fe7e4888-5941-4e62-9aa7-d8742acfd563/image.jpeg" alt=""></p>
<p>정수를 표현하는 비트수와 소수를 표현하는 비트수를 미리 정해놓고 해당 비트만큼만 사용해서 숫자를 표현하는 방식이다. 저는 이론을 이해하고 학습하는 것보다 바로 실전을 통해 이론을 학습하는 편이라서 먼저 예시를 통해 빠르게 알아보겠습니다. 예시를 들기위해 임의의 숫자인 263.3을 선택하였고 32비트를 사용하여 부호, 정수부, 소수부를 표현해보겠습니다.</p>
<p>부호의 경우 1비트를 차지하며 양수의 경우에는 0을, 음수의 경우에는 1로 표현합니다.</p>
<p>다음으로 263.3의 정수부를 구하는 과정을 가져보겠습니다.
2진수이기에 2로 나눠주면 다음 과정에서는 몫을 가지고 계속 실행시켜줍니다.</p>
<pre><code>263 % 2 = 1
131 % 2 = 1
65 % 2 = 1
32 % 2 = 0
16 % 2 = 0
8 % 2 = 0
4 % 2 = 0
2 % 2 = 0
1 % 2 = 1
</code></pre><p>아래에서 위로 올라가면 읽어줍니다
100000111(2) -&gt; 9비트
15비트 - 9비트 = 6비트
6비트를 모두 0으로 채워 정수부를 만들어 주면 000000+100000111 = 000000100000111(2) 이게 정수부입니다.</p>
<p>다음으로 소수부를 구해보는 과정을 해보겠습니다.</p>
<pre><code>0.3 * 2 | 0.6 | 0
0.6 * 2 | 1.2 | 1
0.2 * 2 | 0.4 | 0
0.4 * 2 | 0.8 | 0
0.8 * 2 | 1.6 | 1
0.6 * 2 | 1.2 | 1
0.2 * 2 | 0.4 | 0
0.4 * 2 | 0.8 | 0
        .
        .
        .</code></pre><p>0(1001)(1001)(1001)...
처음에 0뒤로는 계속 1001이 반복되는 과정을 거치지만 소수부의 비트는 최대 16비트이므로 16비트만 사용해줍니다.0100110011001100(2)으로 표현할 수 있습니다.</p>
<p>위 결과를 토대로 263.3을 고정소수점으로 나타내면 (0)(000000100000111)(0100110011001100)입니다.</p>
<blockquote>
<p>하지만 고정소수점에는 단점이 존재합니다. 정수부의 bit를 늘려 큰 숫자를 표현할 수 있지만 정밀한 숫자를 표현하기 힘들고, 반대로 소수부의 bit를 늘려 정밀한 숫자를 표현할 수 있지만 큰 숫자를 표현하기 힘들다는 단점이 존재합니다. 이를 보완하기 위해서 부동소수점이 나왔습니다.</p>
</blockquote>
<h1 id="부동소수점이란">부동소수점이란?</h1>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/0666cc6c-e4ef-44d0-8529-5241f4b3d6cc/image.png" alt=""></p>
<p>부동은 영어로 float, 뜻은 떠다니다,유동적인이라는 의미를 나타내고 있습니다.
이는 소수점이 고정되어 있지 않고 좌우로 움직일 수 있다는 뜻입니다.</p>
<p>소수점이 자유롭게 움직인 다는 것은 표현할 수 있는 수의 범위가 매우 넓어진다는 장점을 가지고 있습니다.
바로 263.3을 부동소수점으로 구하는 과정을 구해보자.</p>
<p>고정소수점에서 구한 263.3의 정수부는 100000111(2), 소수부는 0100110011001100였다.
합치면 100000111.0100110011001100이다. 하지만 부동소수점에서는 지수부와 가수부를 사용하기때문에 표준 표기법으로 바꿔준다면 1.000001110100110011001100 * 2^8이 된다.
지수부인 8을 이진법으로 나타내면 100(2)으로 표현할 수 있다. 하지만 여기서 한가지 중요한 것이 있는데 바로 지수부는 양수와 음수를 둘 다 표현하기 위해서 8비트로 표현할 수 있는 숫자 영역을 반으로 나누어 양수와 음수를 표현하는 한다는 점이다.</p>
<pre><code>00000000 ~ 01111110  =&gt; -127부터 -1까지인 음수를 표현
01111110 =&gt; 0을 표현
10000000 ~ 11111111 =&gt; 1부터 128까지를 표현</code></pre><p>따라서 8을 표현하기위해서는 00001000에 127(01111111)을 더해야한다.
00001000 + 01111111 =&gt; 10000111이 바로 지수부이다.</p>
<p>다시 역으로 정리하면 <strong>0</strong> <strong>10000111</strong> <strong>000001110100110011001100</strong>에서 가수부는 000001110100110011001100이니 맨앞에 1하고 소수점을 추가한 1.000001110100110011001100이고
지수부는 8이니 가수부에 적용시키면 100000111.0100110011001100이다.
이를 10진수로 표현하면 263.3이 되는것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1753 최단경로]]></title>
            <link>https://velog.io/@hyuntae_/1753-%EC%B5%9C%EB%8B%A8%EA%B2%BD%EB%A1%9C</link>
            <guid>https://velog.io/@hyuntae_/1753-%EC%B5%9C%EB%8B%A8%EA%B2%BD%EB%A1%9C</guid>
            <pubDate>Tue, 28 Feb 2023 10:35:00 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>방향그래프가 주어지면 주어진 시작점에서 다른 모든 정점으로의 최단 경로를 구하는 프로그램을 작성하시오. 단, 모든 간선의 가중치는 10 이하의 자연수이다.</p>
<h1 id="입력">입력</h1>
<p>첫째 줄에 정점의 개수 V와 간선의 개수 E가 주어진다. (1 ≤ V ≤ 20,000, 1 ≤ E ≤ 300,000) 모든 정점에는 1부터 V까지 번호가 매겨져 있다고 가정한다. 둘째 줄에는 시작 정점의 번호 K(1 ≤ K ≤ V)가 주어진다. 셋째 줄부터 E개의 줄에 걸쳐 각 간선을 나타내는 세 개의 정수 (u, v, w)가 순서대로 주어진다. 이는 u에서 v로 가는 가중치 w인 간선이 존재한다는 뜻이다. u와 v는 서로 다르며 w는 10 이하의 자연수이다. 서로 다른 두 정점 사이에 여러 개의 간선이 존재할 수도 있음에 유의한다.</p>
<h1 id="출력">출력</h1>
<p>첫째 줄부터 V개의 줄에 걸쳐, i번째 줄에 i번 정점으로의 최단 경로의 경로값을 출력한다. 시작점 자신은 0으로 출력하고, 경로가 존재하지 않는 경우에는 INF를 출력하면 된다.</p>
<hr>
<h1 id="예제">예제</h1>
<h2 id="예제-입력-1">예제 입력 1</h2>
<p>5 6
1
5 1 1
1 2 2
1 3 3
2 3 4
2 4 5
3 4 6</p>
<h2 id="예제-출력-1">예제 출력 1</h2>
<p>0
2
3
7
INF</p>
<hr>
<h1 id="해결">해결</h1>
<p>전형적인 다익스트라 알고리즘 문제로 보인다. 내가 참고한 글은 <a href="https://yabmoons.tistory.com/364">https://yabmoons.tistory.com/364</a> 이다. 다른 글도 많지만 여기 블로그가 그림으로 잘 설명되있다.</p>
<p>위 글을 읽고 이해한 내용을 간략하게 설명해보고자 한다.</p>
<p>다익스트라는 방향과 가중치가 존재하는 그래프로 어느 한 노드에서 다른 노드로 갈 때 최단거리를 찾는 알고리즘이다. 다익스트라를 해결하기 위한 핵심은 그리디 알고리즘과 비슷하다고 느꼈다. 왜냐하면 현재의 노드에서 인접한 노드들이 여러개가 존재하는 상황에서 지금 당장 가야만 하는 노드는 최단거리의 노드라는 점이기 때문이다. 그렇기에 현재노드에서 갈 수 있는 노드의 조건은 다음과 같다.</p>
<ol>
<li>아직 최단거리를 찾지 못한 노드(= 아직 방문하지 못한 노드)</li>
<li>갈 수 있는 노드들 중에서 가장 최단거리인 노드
위 와 같은 조건을 만족하는 노드를 찾아 그래프를 탐색하여 모든 노드까지의 최단거리를 구할 수 있다.</li>
</ol>
<pre><code class="language-js">const distance = new Array(V + 1).fill(Infinity);
const visited = new Array(V + 1).fill(0);
const adjArr = Array.from({ length: V + 1 }, () =&gt; new Array());

for (let i = 0; i &lt; E; i++) {
  const [from, to, weight] = input[i];
  adjArr[from].push([to, weight]);
}
distance[start] = 0;
visited[start] = 1;

let next = start;

&lt;!--2번조건인 가장 최단거리인 노드를 찾아 반환한다.--&gt;
const findNextV = () =&gt; {
  let min = Infinity;
  let minV = null;

  for (let i = 1; i &lt;= V; i++) {
    if (!visited[i]) {
      if (distance[i] &lt; min) {
        min = distance[i];
        minV = i;
      }
    }
  }

  if (!minV) return -1;
  else return minV;
};

while (1) {
  const cur = next;

  for (let i = 0; i &lt; adjArr[cur].length; i++) {
    const [to, dis] = adjArr[cur][i];
    &lt;!-- 1번 조건을 만족하는 노드들의 최단거리를 갱신해준다. --&gt;
    if (!visited[to]) {
      if (distance[to] &gt; distance[cur] + dis)
        distance[to] = distance[cur] + dis;
    }
  }

  const vaild = findNextV();
  if (vaild === -1) break;
  next = vaild;
  visited[next] = 1;
}

let answer = &quot;&quot;;
for (let i = 1; i &lt;= V; i++) {
  if (distance[i] === Infinity) answer += &quot;INF\n&quot;;
  else answer += `${distance[i]}\n`;
}
console.log(answer);</code></pre>
<p>위 코드를 작성하여 제출하면 통과는 할 수 있지만 시간복잡도 측면에서는 상당히 비효율적인 것을 확인했다. 그 이유는 다음과 같다.</p>
<ol>
<li>모든 노드를 탐색한다. O(N)</li>
<li>각 노드마다 다음으로 갈 수 있는 노드를 찾기 위해 모든 노드를 탐색한다. O(N)</li>
</ol>
<p>1번과 2번을 동시에 진행하기에 시간복잡도는 log(N^2)을 나타낸다. 노드가 100,000개 라면 100억 번의 탐색을 진행해야하기 때문이다. 이러한 비효율적인 시간복잡도를 해결하기 위해최소힙을 사용하여 다음 노드를 탐색하는 과정을 logN으로 줄여 시간복잡도를 O(NlogN)으로 만들 수 있다.</p>
<pre><code class="language-js">class MinHeap {
  constructor() {
    this.heap = [];
  }
  push(node, weight) {
    this.heap.push({ node, weight });
    if (this.heap.length === 1) return;
    let curIdx = this.heap.length - 1;
    let parentIdx = parseInt((curIdx - 1) / 2);
    while (
      curIdx &gt; 0 &amp;&amp;
      this.heap[parentIdx].weight &gt; this.heap[curIdx].weight
    ) {
      this.swap(curIdx, parentIdx);
      curIdx = parentIdx;
      parentIdx = parseInt((curIdx - 1) / 2);
    }
  }
  pop() {
    if (this.heap.length === 0) return -1;
    if (this.heap.length === 1) return this.heap.pop();
    const answer = this.heap[0];
    this.heap[0] = this.heap.pop();

    let pos = 0;

    while (1) {
      let cur = pos;
      let leftIdx = cur * 2 + 1;
      let rightIdx = cur * 2 + 2;
      if (leftIdx &gt; this.heap.length - 1) break;

      if (this.heap[cur].weight &gt; this.heap[leftIdx].weight) cur = leftIdx;
      if (
        rightIdx &lt; this.heap.length &amp;&amp;
        this.heap[cur].weight &gt; this.heap[rightIdx].weight
      )
        cur = rightIdx;

      if (cur === pos) break;
      this.swap(pos, cur);
      pos = cur;
    }

    return answer;
  }
  swap(a, b) {
    const tmp = this.heap[a];
    this.heap[a] = this.heap[b];
    this.heap[b] = tmp;
  }
}

const heap = new MinHeap();
const visited = new Array(V + 1).fill(0);
const distance = new Array(V + 1).fill(Infinity);
const adjArr = Array.from({ length: V + 1 }, () =&gt; new Array());

for (let i = 0; i &lt; E; i++) {
  const [from, to, dis] = input[i];
  adjArr[from].push({ to, dis });
}

distance[start] = 0;
heap.push(start, 0);

while (heap.heap.length) {
  const { node, weight } = heap.pop();
  visited[node] = 1;

  for (let i = 0; i &lt; adjArr[node].length; i++) {
    const { to, dis } = adjArr[node][i];
    if (!visited[to]) {
      if (distance[to] &gt; weight + dis) {
        distance[to] = weight + dis;
        heap.push(to, distance[to]);
      }
    }
  }
}
let answer = &quot;&quot;;
for (let i = 1; i &lt;= V; i++) {
  if (distance[i] === Infinity) answer += &quot;INF\n&quot;;
  else answer += `${distance[i]}\n`;
}
console.log(answer);
</code></pre>
<p><img src="https://velog.velcdn.com/images/hyuntae_/post/e30e26f6-6cbb-44ec-99a4-29749cc98baf/image.png" alt=""></p>
<p>첫번째 풀이는 2616ms, 두번째 풀이는 1112ms로 시간복잡도가 상당히 개선된 것을 확인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[react-query]]></title>
            <link>https://velog.io/@hyuntae_/react-query</link>
            <guid>https://velog.io/@hyuntae_/react-query</guid>
            <pubDate>Sun, 26 Feb 2023 08:59:03 GMT</pubDate>
            <description><![CDATA[<h1 id="react-query-이전">react-query 이전</h1>
<p>대개 react query를 도입하기 전에는 서버와의 API통신과 비동기 데이터 관리에 redux를 사용했습니다.
비동기 미들웨어인 redux-thunk, redux-saga를 사용하기도 하며 다양한 Custom미들웨어를 만들어 사용하기도 했습니다.
하지만 redux는 비동기 데이터를 관리하기 위한 전문 라이브러리가 아니라, 범용적으로 사용할 수 있는 전역 상태 관리 라이브러리이기에 redux 미들웨어로 비동기 상태를 불러오고 그 값을 보관할 수는 있지만, 내부적인 구현은 모두 개발자가 알아서 해야하기에 팀 구성원들이 많아지고 협업 관계가 복잡하게 구성될 수록 데이터를 관리하는 방식과 방법이 모두 달라지는 점이 발생합니다.</p>
<h1 id="react-query-도입">react-query 도입</h1>
<p>redux를 사용한 API요청과 비동기 데이터 관리의 불편함을 해소하기 위해 react-query를 사용할 수 있습니다.
react-query는 react application에서 서버의 상태를 불러오고, 캐싱하며, 지속적으로 동기화하고 업데이트하는 작업을 도와주는 라이브러리입니다. react-query는 우리에게 친숙한 Hook을 사용하여 react component 내부에서 자연스럽게 서버(또는 비동기적인 요청이 필요한 source)의 데이터를 사용할 수 있는 방법을 가지고 있습니다.</p>
<blockquote>
<p>react-query는 API요청을 Query 와 Mutation 이라는 두 가지 유형으로 나누어 생각한다.</p>
</blockquote>
<h2 id="query-요청">query 요청</h2>
<pre><code>// 가장 기본적인 형태의 React Query useQuery Hook 사용 예시
const { data } = useQuery(
  queryKey, // 이 Query 요청에 대한 응답 데이터를 캐시할 때 사용할 Unique Key (required)
  fetchFn, // 이 Query 요청을 수행하기 위한 Promise를 Return 하는 함수 (required)
  options, // useQuery에서 사용되는 Option 객체 (optional)
);</code></pre><p>react-query의 useQuery Hook은 요청마다(API마다) 구분되는 Unique Key를 필요로 한다. react-query는 이 Unique key로 서버 상태를 로컬에 캐시하고 관리한다.</p>
<h2 id="mutation-요청">mutation 요청</h2>
<pre><code>// 가장 기본적인 형태의 React Query useMutation Hook 사용 예시
const { mutate } = useMutation(
  mutationFn, // 이 Mutation 요청을 수행하기 위한 Promise를 Return 하는 함수 (required)
  options, // useMutation에서 사용되는 Option 객체 (optional)
);</code></pre><p>useMutation Hook으로 수행되는 Mutation 요청은 http method POST,PUT,DELETE 요청과 같이 서버에 Side Effect를 발생시켜 서버의 상태를 변경시킬 때 사용한다.
useMutation Hook의 첫번째 파라미터는 이 Mutation 요청을 수행하기 위한 Promise를 Return 하는 함수 이며, useMutation의 return 값 중 mutate함수를 호출하여 서버에 side effect를 발생시킬 수 있다.</p>
<h1 id="참고">참고</h1>
<ul>
<li><a href="https://velog.io/@jay/10-minute-react-query-concept">https://velog.io/@jay/10-minute-react-query-concept</a></li>
<li><a href="https://velog.io/@jay/5-minute-react-query-essential">https://velog.io/@jay/5-minute-react-query-essential</a></li>
<li><a href="https://tech.kakao.com/2022/06/13/react-query/">https://tech.kakao.com/2022/06/13/react-query/</a></li>
<li><a href="https://tech.kakaopay.com/post/react-query-1/">https://tech.kakaopay.com/post/react-query-1/</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>