<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dulcis-hortus.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 02 Aug 2024 08:28:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dulcis-hortus.log</title>
            <url>https://velog.velcdn.com/images/dulcis-hortus/profile/f2bee04d-2e1b-48b5-a1ec-809d1034108d/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dulcis-hortus.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dulcis-hortus" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[React] 복잡한 state 구조 다루기 ]]></title>
            <link>https://velog.io/@dulcis-hortus/react-useEffect-Clean-up-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@dulcis-hortus/react-useEffect-Clean-up-%ED%95%A8%EC%88%98</guid>
            <pubDate>Fri, 02 Aug 2024 08:28:21 GMT</pubDate>
            <description><![CDATA[<h2 id="상황">상황</h2>
<p>테스트용 데이터를 생성하는 화면을 만들었다. 그런데 데이터의 구조가 상당히 복잡했다. 리스트 안에 추가될 수 있는 객체가 두 종류였고 depth도 깊었다. 컴포넌트로 쪼개서 관리하다보니 하위 컴포넌트가 3개 정도 나왔는데 마지막 컴포넌트에서 최상위 state를 조작할 때쯤엔 이게 맞나 싶을 정도였다.</p>
<pre><code class="language-javascript">// state 구조 예시
[
// 객체 종류1: 기간일 때  
{
seq: 0,
byPeriod: {
day: 10,
amt: 100000,
startDt: &quot;2024-08-10&quot;
}},
// 객체 종류2: 날짜일 때
{
seq: 1,
byDate: [
{
amt: 1000000,
date: &quot;2024-08-10&quot;,
tempKey: &quot;eljfeijjsl&quot;
},
{
amt: 1000000,
date: &quot;2024-08-11&quot;,
tempKey: &quot;glajekjlkfj&quot;
},
{
amt: 1000000,
date: &quot;2024-08-12&quot;,
tempKey: &quot;helkjslkjflj&quot;
}}]</code></pre>
<p>위 구조에서 날짜일 때 객체는 삭제, 수정 시 byDate 안에서 다시 객체를 조작해야 하기에 임시키를 추가해주었다.
그러니까, 특정 데이터를 수정하려면 </p>
<p>1) 전체 데이터를 map을 돌려서 seq가 일치하는 객체를 찾고 
2) 기간별일 경우엔 byPeriod, 날짜별일 경우엔 byDate 안으로 들어가서 
3) 날짜별일 경우 다시 map을 돌려 tempKey가 일치하는 객체를 찾아서 
4) 타켓 인자의 데이터를 업데이트해주면 된다! 
5) 물론 이 과정에서 타겟이 아닌 객체들은 그대로 반환해주어야 한다. </p>
<p>(<del>어 써놓고 보니 할 만 한데...?</del>)</p>
<br />

<h2 id="해결책">해결책</h2>
<p>전역 상태관리로 풀어야 하는 건가 고민했는데 보다 근본적인 해결책이 있었다. <strong>state는 데이터 구조를 플랫하게 만드는 게 권장된다.</strong> </p>
<ul>
<li><a href="https://ko.react.dev/learn/choosing-the-state-structure#avoid-deeply-nested-state">https://ko.react.dev/learn/choosing-the-state-structure#avoid-deeply-nested-state</a></li>
</ul>
<p>이번 작업에서 state 구조가 복잡해진 이유는 개발 초기엔 데이터 가공 과정을 줄이려고 api 전송 형식을 최대한 맞추려 했고 이후엔 컴포넌트 UI 구조를 그대로 따라갔기 때문이다.</p>
<p>api 전송 형식이나 UI에 관계 없이 <strong>state 데이터 구조를 완전히 독립적으로 관리하는 것</strong>이 핵심인 것 같다.</p>
<pre><code class="language-javascript">// 개선안
// 객체 종류1: 기간일 때  
[{
seq: 0,
type: &quot;byPeriod&quot;,
day: 10,
amt: 100000,
startDt: &quot;2024-08-10&quot;
},
// 객체 종류2: 날짜일 때
{
seq: 1,
type: &quot;byDate&quot;,
&quot;eljfeijjsl&quot;: {
amt: 1000000,
date: &quot;2024-08-10&quot;,
},
&quot;glajekjlkfj&quot;: {
amt: 1000000,
date: &quot;2024-08-11&quot;,
},
&quot;helkjslkjflj&quot;: {
amt: 1000000,
date: &quot;2024-08-12&quot;,
}}]</code></pre>
<BR />

<p>구조가 1 depth 정도 줄어들었다. 이렇게 변경해보고 어떤지 확인해봐야겠다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[XLSX] 엑셀 파일 생성 시, 셀 데이터타입 초기값 변경하기]]></title>
            <link>https://velog.io/@dulcis-hortus/XLSX-%EC%85%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%ED%83%80%EC%9E%85-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dulcis-hortus/XLSX-%EC%85%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%ED%83%80%EC%9E%85-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 25 Jun 2024 08:03:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이거.. 필요한 사람 있을까 싶어서 올립니다...</p>
</blockquote>
<h2 id="이슈-사항">이슈 사항</h2>
<p><img src="https://velog.velcdn.com/images/dulcis-hortus/post/a23a80de-cf7d-42e9-bb22-32a934c93417/image.png" alt=""></p>
<p>XLSX 라이브러리를 이용하여 엑셀 파일을 생성하면 셀이 모두 일반 타입으로 생성되지만, 다른 타입으로 생성하고 싶을 경우!!
나의 경우엔 텍스트였음!
공식 문서 페이지...
<a href="https://docs.sheetjs.com/docs/csf/cell">https://docs.sheetjs.com/docs/csf/cell</a></p>
<h3 id="우여곡절-끝에-찾아낸-방법">(우여곡절 끝에 찾아낸) 방법</h3>
<pre><code class="language-javascript">const workbook = XLSX.utils.book_new()</code></pre>
<p>여기서 workbook을 콘솔로 찍어보면
SSF라는 인자가 있음 -&gt; 이 친구가 우리가 찾는 바로 그것임..
기본 셀은 모두 &#39;General&#39;로 생성되고 이 중 원하는 포맷을 찾아서 바뀌주면 된다.
(참고로 텍스트는 &#39;@&#39;입니다.)</p>
<p><code>worksheet[셀 위치].z = &#39;@&#39;</code> 로 변경해주면 된다!</p>
<p>다만, 미리 셀을 생성해두어야 하기에</p>
<p>1) 범위 정하고
2) {t: &#39;s&#39;, v:&#39;&#39;, z:&#39;@&#39;} 로 데이터 생성해서 
셀 범위까지 위 작업을 반복해서 추가해주어야 한다!</p>
<p>만일 숫자 관련이라면 t: &#39;s&#39;가 아니라 다른 값을 넣어주어야 한다.
(얘도 형식 관련임...)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 모던 리액트 Deep Dive 1장 - 1.3]]></title>
            <link>https://velog.io/@dulcis-hortus/React-%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-Deep-Dive-1%EC%9E%A5-1.3</link>
            <guid>https://velog.io/@dulcis-hortus/React-%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-Deep-Dive-1%EC%9E%A5-1.3</guid>
            <pubDate>Tue, 26 Mar 2024 14:07:48 GMT</pubDate>
            <description><![CDATA[<h2 id="본문-요약-🌱">본문 요약 🌱</h2>
<blockquote>
<p>리액트에서 클래스가 중요한 이유 -&gt; &quot;함수형 컴포넌트 이전까진 모든 컴포넌트가 클래스로 작성되어 있었기 때문!&quot;</p>
</blockquote>
<h3 id="클래스란-무엇인가요🤔">클래스란 무엇인가요?🤔</h3>
<p>=&gt; 특정한 형태의 객체를 반복적으로 만들기 위해 사용되는 것이 바로 클래스입니다. 쿠키를 찍어내는 쿠키틀 같은 것으로 이해하면 편해요! 🍪🍪🍪</p>
<h3 id="클래스의-구성-요소는">클래스의 구성 요소는?</h3>
<ul>
<li>constructor : 객체를 생성하는 데 사용하는 특수한 메서드 (<del>쿠키 반죽입니다</del>)</li>
<li>메서드 (인스턴스 메서드) : 인스턴스와 연결해서 사용할 수 있는 함수 (<del>쿠키 토핑 레시피입니다</del>) </li>
<li>정적 메서드 : 클래스로만 호출이 가능한 함수 (<del>쿠키틀로 뭔가를 하는 것입니다</del>)</li>
<li>setter : 클래스 필드에 값을 할당할 수 있도록 함</li>
<li>getter : setter로 할당한 값을 읽어올 수 있도록 함</li>
</ul>
<br />

<h3 id="인스턴스-메서드-feat-prototype🖍️">인스턴스 메서드 (feat. prototype)🖍️</h3>
<blockquote>
<p>여기서 중요한 건 메서드 자체보다 <strong>prototype</strong>! JS는 <strong>프로토타입 기반 언어</strong>예요.</p>
</blockquote>
<p>&quot;인스턴스 메서드는 자바스크립트의 prototype에 선언되므로 프로토타입 메서드로 불리기도 한다.&quot;</p>
<h4 id="prototype을-설명하자면❤️🔥">prototype을 설명하자면...❤️‍🔥</h4>
<p>=&gt; 클래스로 인스턴스를 생성하면, 클래스 내부에 있는 메서드도 사용이 가능합니다. 인스턴스 메서드는 prototype에 선언되기 때문이에요. prototype은 박스로 이해하면 쉬워요. 그것도 그냥은 보이지 않는 그림자 박스예요. ⬛️
모든 객체는 각자의 prototype 객체를 가지고 있어요. 객체에서 어떤 메서드를 호출했을 때, 일단 그 객체의 그림자 박스(prototype)를 먼저 뒤져보아요. 음.. 없네요. 그럼 어떻게 하냐면 그 객체의 부모의 그림자 박스(prototype)를 뒤져서 메서드를 찾아요!
클래스로 만들어진 인스턴스는 부모 자식 관계를 가지기 때문에, 인스턴스는 클래스에서 정의된 인스턴스 메서드를 쓸 수 있는 거예요. 이를 <strong>프로토타입 체이닝</strong> 이라고 부릅니다. 😎</p>
<h3 id="클래스의-핵심-개념---상속">클래스의 핵심 개념 -&gt; 상속</h3>
<p>클래스에서 상속을 빼면 JS를 객체 지향 언어로 만들고자 했던 개발자들의 열정을 외면하는 일이에요.🥲 extends를 사용해서 기존 클래스를 상속 받는 새로운 클래스를 만들 수 있습니다. </p>
<blockquote>
<p>하지만 바벨을 통해 코드를 트랜스파일해보면 <strong>클래스 코드도 내부적으로는 기존부터 사용되던 프로토타입 방식을 사용하는 것</strong>임을 확인할 수 있습니다. </p>
</blockquote>
<p>그래서 클래스를 일종의 문법적 설탕(syntactic sugar)이라고 칭하기도 합니다.🍭🍬🍭🍬</p>
<br />

<h2 id="심화하기✨">심화하기✨</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 모던 리액트 Deep Dive 1장 - 1.2]]></title>
            <link>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-Deep-Dive-1%EC%9E%A5-1.2-gvkewl3u</link>
            <guid>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-Deep-Dive-1%EC%9E%A5-1.2-gvkewl3u</guid>
            <pubDate>Tue, 19 Mar 2024 13:54:01 GMT</pubDate>
            <description><![CDATA[<h2 id="본문-요약💫">본문 요약💫</h2>
<blockquote>
<p>자바스크립트에서 <strong>함수란 1) 작업을 수행하거나 값을 계산하는 등의 과정을 표현하고, 2) 이를 하나의 블록으로 묶어 실행 단위로 만들어 놓은 것</strong></p>
</blockquote>
<blockquote>
<p>함수를 정의하는 4가지 방식</p>
</blockquote>
<ol>
<li><strong>함수 선언문</strong> </li>
<li><strong>함수 표현식</strong></li>
<li>Function 생성자</li>
<li><strong>화살표 함수</strong></li>
</ol>
<blockquote>
<p>자바스크립트에서 <strong>함수는 일급 객체다.</strong> <strong>일급 객체</strong>란 <strong>다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체</strong>를 위미한다. 함수는 1)다른 함수의 매개변수가 될 수도 있고, 2)반환값이 될 수도 있으며, 앞에서 본 것처럼 3)할당도 가능하므로 일급 객체가 되기 위한 조건을 모두 갖추고 있다.</p>
</blockquote>
<blockquote>
<p><strong>함수의 호이스팅은 함수 선언문이 코드 맨 앞단에 작성된 것처럼 작동하는 자바스크립트의 특징을 의미한다.</strong> 함수 표현식과의 가장 큰 차이점이다.</p>
</blockquote>
<blockquote>
<p>함수의 호이스팅은 <strong>함수에 대한 선언을 실행 전에 미리 메모리에 등록하는 작업을 의미한다.</strong> 이 특징 때문에 어디에서나 정상적으로 함수를 호출할 수 있게 된다.</p>
</blockquote>
<blockquote>
<p>반면 <strong>함수 표현식은 함수를 변수에 할당한다. 변수도 마찬가지로 호이스팅이 발생한다.</strong> <strong>그러나</strong> 함수의 호이스팅과는 다르게, <strong>호이스팅되는 시점에서 var의 경우 undefined로 초기화한다는 차이가 있다.</strong> (그럼 자연스럽게 const, let은..? 이라는 의문이 떠오른다. 이는 아래에 심화에서 풀 예정!)</p>
</blockquote>
<blockquote>
<p><strong>화살표 함수</strong>는 앞서 언급한 함수 생성 방식과 몇 가지 큰 차이점이 있다. 우선 constructor를 사용할 수 없기에 <a href="https://ko.javascript.info/constructor-new#ref-390">생성자 함수</a>로 사용이 불가능하다. 또한 arguments가 존재하지 않는다.</p>
</blockquote>
<blockquote>
<p>그리고 <strong>화살표 함수와 일반 함수의 가장 큰 차이점은 바로 this 바인딩이다.</strong> <strong>this는</strong> 화살표 함수 이전까지는 함수를 정의할 때 결정되는 것이 아니라, <strong>함수가 어떻게 호출되느냐에 따라 동적으로 결정되었다.</strong> 하지만 <strong>화살표 함수는 내부에서 this를 참조하면 상위 스코프의 this를 그대로 따르게 된다.</strong> </p>
</blockquote>
<blockquote>
<p>따라서 화살표 함수와 일반 함수를 사용할 때, 특히 <strong>this를 사용할 수밖에 없는 클래스 컴포넌트 내부에서</strong> 각별한 주의가 필요하다.</p>
</blockquote>
<blockquote>
<p><strong>함수를 인자로 받거나 결과로 새로운 함수를 반환하는 함수를 고차 함수라고 한다.</strong> 이 중 함수를 반환하는 경우엔 <strong>클로저</strong>를 생성하기도 한다.</p>
</blockquote>
<blockquote>
<p><strong>순수 함수</strong>는 <strong>부수 효과가 없고, 언제 어디서나 어떠한 상황에서든 동일한 인수를 받으면 동일한 결과를 반환해야 한다.</strong> 그리고 이러한 작동 와중에 <strong>외부에 어떠한 영향도 미쳐서는 안 된다.</strong></p>
</blockquote>
<blockquote>
<p>리액트의 관점에서 본다면 부수 효과를 처리하는 훅인 <strong>useEffect의 작동을 최소화하는 것이 함수의 역할을 좁히고, 버그를 줄이며, 컴포넌트의 안정성을 높일 수 있다.</strong></p>
</blockquote>
<br />

<h2 id="심화해보기✨">심화해보기✨</h2>
<blockquote>
<p><strong>키워드 &gt;&gt; 
호이스팅, this 바인딩, 클로저, 순수 함수, 함수형 프로그래밍</strong>  </p>
</blockquote>
<p>호이스팅 - <a href="https://taenami.tistory.com/87">https://taenami.tistory.com/87</a>, <a href="https://developer.mozilla.org/ko/docs/Glossary/Hoisting">https://developer.mozilla.org/ko/docs/Glossary/Hoisting</a></p>
<p>this 바인딩 - <a href="https://seungtaek-overflow.tistory.com/21">https://seungtaek-overflow.tistory.com/21</a></p>
<p>클로저 - <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures">https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures</a></p>
<p>순수 함수 - <a href="https://maxkim-j.github.io/posts/js-pure-function/">https://maxkim-j.github.io/posts/js-pure-function/</a></p>
<p>함수형 프로그래밍 - <a href="https://medium.com/@4538asd/react-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EA%B3%BC-usestate-312a5e5a3c70">https://medium.com/@4538asd/react-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EA%B3%BC-usestate-312a5e5a3c70</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 모던 리액트 Deep Dive 1장 - 1.1]]></title>
            <link>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-Deep-Dive-1%EC%9E%A5-1.1</link>
            <guid>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%AA%A8%EB%8D%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-Deep-Dive-1%EC%9E%A5-1.1</guid>
            <pubDate>Tue, 12 Mar 2024 23:32:18 GMT</pubDate>
            <description><![CDATA[<h2 id="본문-정리">본문 정리</h2>
<p>리액트 컴포넌트의 렌더링이 일어나는 이유 중 하나가 바로 props의 동등 비교에 따른 결과다. 이는 객체의 얕은 비교를 기반으로 이뤄진다.</p>
<p>JS의 데이터 타입은 원시 타입과 객체 타입으로 나눠진다. </p>
<ul>
<li>원시 타입: boolean, null, undefined, number, string, symbol, bigint</li>
<li>객체 타입: object</li>
</ul>
<p>원시 타입은 메서드를 갖지 않는다.</p>
<p>undefined는 &#39;선언됐지만 항당되지 않은 값&#39;이고 null은 &#39;명시적으로 비어 있음을 나타내는 값&#39;으로 사용하는 것이 일반적이다.</p>
<p>정수와 실수를 구분해 저장하는 다른 언어와 다르게, 자바스크립트는 모든 숫자를 하나의 타입에 저장했었다. 현재는 BigInt가 추가되어 기존 number에서 저장할 수 없었던 큰 숫자도 저장이 가능하다.</p>
<p>자바스크립트 문자열의 특징은 원시 타입이며 변경 불가능하다는 것이다.</p>
<p>Symbol은 중복되지 않는 어떠한 고유한 값을 나타내기 위해 만들어졌다.</p>
<p>두 타입은 저장 방식에서 차이가 있다.
원시 타입은 불변 형태의 값으로 저장되며 값을 비교한다.
참조 타입은 프로퍼티를 삭제, 추가, 수정할 수 있으므로 변경 가능한 형태로 저장되며 값을 복사할 때도 값이 아닌 참조를 전달하게 된다.</p>
<p>Object.is</p>
<p>=== 와의 차이점</p>
<p>shallowEqual 함수</p>
<p>리액트에서 사용되는 props는 객체이고 여기에 있는 props만 일차적으로 비교하면 되기 때문이다.</p>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li><p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Equality_comparisons_and_sameness">동등 비교(===)에 대해서</a></p>
<blockquote>
<p>숫자의 경우 약간 다른 의미 체계를 사용하여 서로 다른 두 가지 경우를 처리합니다. 첫 번째는 부동 소수점 0이 양수 또는 음수로 부호화 된다는점입니다. 이는 특정 수학 해를 나타내는 데 유용하지만, 대부분의 상황에서는 +0과 -0의 차이를 신경 쓰지 않으므로 엄격한 동등은 두 값을 동일한 값으로 취급합니다. 두 번째는 부동 소수점이 NaN이라는 숫자가 아닌 값의 개념을 포함하여 어떤 불분명한 수학 문제(예: 양의 무한대에 음의 무한대를 더함)에 대한 해를 나타내는 경우입니다. 엄격한 동등은 NaN을 다른 모든 값과 같지 않은 것으로 취급합니다. ((x !== x)가 true인 유일한 경우는 x가 NaN일 때입니다.)</p>
</blockquote>
</li>
<li><p><a href="https://okky.kr/questions/1432062">react에서 shallowEqual() 함수를 이용한 얕은 비교에 관한 질문</a></p>
<blockquote>
<p>아래에 shallowEqual소스코드를 분석해두었으니 참고해보시면 좋겠습니다. 원본 소스코드는 아래 링크를 확인해주세요
<a href="https://github.com/facebook/react/blob/8e2bde6f2751aa6335f3cef488c05c3ea08e074a/packages/shared/shallowEqual.js">https://github.com/facebook/react/blob/8e2bde6f2751aa6335f3cef488c05c3ea08e074a/packages/shared/shallowEqual.js</a></p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] TUI grid resize 에러]]></title>
            <link>https://velog.io/@dulcis-hortus/TUI-grid-resize-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@dulcis-hortus/TUI-grid-resize-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Wed, 21 Feb 2024 05:32:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>한 줄 요약 : &quot;TUI grid 너비 변경될 때 transition 사용하면 refreshLayout() 함수가 작동을 안 해요!&quot;</strong></p>
</blockquote>
<h2 id="상황">상황</h2>
<p>왼쪽 사이드에 메뉴바가 있는 어드민 사이트가 있는데 이 사이드 메뉴바를 접어서 콘텐츠를 볼 수 있는 영역을 넓히려 했다!
하지만 TUI grid가 자동으로 변경되지 않아서 메뉴바는 접혀도 본문의 너비는 그대로였다🫠</p>
<h2 id="해결">해결</h2>
<p>여기서 사용된 TUI grid 컴포넌트는 부모 컴포넌트의 사이즈가 변경되면 자동으로 width값을 측정해서 리렌더링하게 되어 있다. 그런데 간혹 변경을 자동으로 인지하지 못할 경우 Instance Methods 중 <code>refreshLayout()</code> 함수를 사용해 수동으로(?) 리렌더링을 발생시킬 수 있다.</p>
<ul>
<li><a href="https://github.com/nhn/tui.grid/blob/master/packages/toast-ui.react-grid/README.md">TUI 공식 문서 - Instance Methods 사용법</a></li>
<li><a href="https://nhn.github.io/tui.grid/latest/Grid#refreshLayout">refreshLayout 함수</a></li>
</ul>
<p>하지만 이렇게 해결되었다면 이 포스팅은 나오지 않았을 것이다...!
분명 메뉴가 숨김 처리될 때마다 refreshLayout 함수를 작동시켰는데도 버그는 해결되지 않았다😂</p>
<h2 id="진짜-해결">(진짜) 해결</h2>
<p><a href="https://github.com/nhn/tui.grid/issues/861">정확히 이런 상황이었습니다</a></p>
<p><strong>부모 컴포넌트 변경에 css transition가 쓰이면 TUI grid는 컴포넌트 변경을 인지하지 못한다!</strong>
<del>(아니 대체 왜..? 이거 고쳐주세요!)</del>
transition을 제거하자 refreshLayout()는 제 역할을 잘 해냈고 본문의 너비도 늘어났다.
해결!✨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 숫자1 <= (타겟 숫자) <= 숫자2 ]]></title>
            <link>https://velog.io/@dulcis-hortus/JS-%EB%B6%80%EB%93%B1%ED%98%B8-%EB%AA%A8%EC%96%91</link>
            <guid>https://velog.io/@dulcis-hortus/JS-%EB%B6%80%EB%93%B1%ED%98%B8-%EB%AA%A8%EC%96%91</guid>
            <pubDate>Wed, 09 Nov 2022 01:12:46 GMT</pubDate>
            <description><![CDATA[<h2 id="배경">배경</h2>
<p>특정 숫자가 어느 2개 숫자 사이에 있음을 확인하는 코드가 필요하다.</p>
<h2 id="내가-짠-코드">내가 짠 코드</h2>
<pre><code class="language-javascript">숫자1 &lt;= (특정 숫자) &lt;= 숫자2</code></pre>
<p>이렇게 했더니 특정 숫자가 숫자2보다 큰 데도 true로 판별되었다.</p>
<h2 id="수정-코드">수정 코드</h2>
<pre><code class="language-javascript">(특정 숫자) &gt;= 숫자1 &amp;&amp; (특정 숫자) &lt;= 숫자2</code></pre>
<p>이렇게 짜야 제대로 판별된다.
<del><code>&lt;=</code> 이 부등호가 인정되지 않는 것 같다.</del>
<del>수학 교과서에서야 어느 쪽으로 쓰던 인정되지만 javascript에서는 <code>&gt;=</code>만 &#39;작거나 같다&#39;로 인정된다.</del>
댓글로 달아주셔서 문제의 원인을 제대로 알았다!
부등호의 문제가 아니라, 왼쪽 부등호가 먼저 실행되면서 중간값이 boolean으로 변경되기 때문에 벌어진 현상이었다.
해결은 했지만 앞으로는 문제의 원인을 좀더 파고 들면 더 좋을 것 같다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트/웹] 뒤로가기 막기]]></title>
            <link>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%9B%B9-%EB%92%A4%EB%A1%9C%EA%B0%80%EA%B8%B0-%EB%A7%89%EA%B8%B0</link>
            <guid>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%9B%B9-%EB%92%A4%EB%A1%9C%EA%B0%80%EA%B8%B0-%EB%A7%89%EA%B8%B0</guid>
            <pubDate>Wed, 19 Oct 2022 01:36:48 GMT</pubDate>
            <description><![CDATA[<h2 id="배경">배경</h2>
<p>사용자가 관심매장을 신청하고, 완료가 되면 목록 페이지로 이동한다. 이후 뒤로가기를 하면 다시 신청 페이지가 나오는데 기획쪽에서 이걸 막고 싶다는 의견이 나왔다.
완성도가 떨어져 보여서 나도 막는 게 좋을 것 같다고 생각했다.</p>
<h2 id="해결책">해결책</h2>
<ol>
<li>history stack을 직접 조작해보려고 했다. </li>
</ol>
<p><strong>하지만 이건 보안 상의 이유로 안 된다고 한다!!</strong>
하긴 누가 내 인터넷 접속 기록을 막 바꿀 수 있다면 문제가 크게 생길 거 같다...
뒤로가기를 누르면 메인 페이지로 이동시키려 했는데 야심찬 계획이 틀어졌다.😥</p>
<ol start="2">
<li>신청 페이지 -&gt; 신청 완료 페이지 -&gt; 목록 페이지 순서에서 컴포넌트를 하나 더 추가했다. 신청 페이지 -&gt; <code>replace 컴포넌트</code>-&gt; 신청 완료 페이지  -&gt; 목록 페이지 이렇게!
replace 컴포넌트의 목적은 한 번 완료 페이지를 거치고 나면 다시 신청 페이지로 돌아가지 못하게 하는 것이다.</li>
</ol>
<p><strong>정확히 말하면, 강제로 완료 페이지로 돌려보낸다.</strong></p>
<pre><code class="language-javascript">// replace 컴포넌트

import { useEffect } from &quot;react&quot;;
import { useHistory, useLocation } from &quot;react-router-dom&quot;;

const ReplaceComp = () =&gt; {
  const location = useLocation();
  const history = useHistory();
  const { isOkRoute } = location.state;

  useEffect(() =&gt; {
    if (isOkRoute) {
      history.push(&quot;/my-store/completion&quot;);
    } 
  }, []);
  return null;
};

export default ReplaceComp;</code></pre>
<p>신청 페이지에서 url을 보내줄 때 이렇게 state를 함께 전달해준다.</p>
<pre><code class="language-javascript">    history.push({
          pathname: &quot;/my-store/replace&quot;,
          state: { isOkRoute: true },
        });</code></pre>
<p>사실, <code>history.go(-1)</code>을 할 때 <code>isOkRoute: false</code>로 바꿀 수 있다면 분기를 태워서 메인으로 보내는 것도 가능할 텐데 그 방법은 찾지 못했다. 찾으면 덧붙여 쓰겠음!😁</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[RN] 프로젝트 생성 시 에러]]></title>
            <link>https://velog.io/@dulcis-hortus/RN-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EC%8B%9C-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@dulcis-hortus/RN-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EC%8B%9C-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Sat, 08 Oct 2022 04:05:41 GMT</pubDate>
            <description><![CDATA[<h2 id="배경">배경</h2>
<p>새로운 RN 프로젝트를 만들 때마다 같은 에러를 만나는데 매번 해결책을 검색하고 있다.🫠 이럴 바엔 정리해두자고 생각해서 쓰는 포스팅!</p>
<h2 id="에러-메시지">에러 메시지</h2>
<blockquote>
<p>TypeError: cli.init is not a function for react native</p>
</blockquote>
<h2 id="해결">해결</h2>
<p><a href="https://stackoverflow.com/questions/72768245/typeerror-cli-init-is-not-a-function-for-react-native">https://stackoverflow.com/questions/72768245/typeerror-cli-init-is-not-a-function-for-react-native</a></p>
<blockquote>
<p>That is error is from the new version 0.69.0 You can use npx react-native init ProjectName --version 0.68.2 and then upgrade to v 0.69 later.</p>
</blockquote>
<p>특정 버그가 있는 npx 버전을 사용해서 발생하는 에러이기 때문에 버전을 낮추면 된다고 한다.
실제로 이 방법으로 프로젝트를 잘 생성했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 2개의 API 결과값 합치기]]></title>
            <link>https://velog.io/@dulcis-hortus/JS-2%EA%B0%9C%EC%9D%98-API-%EA%B2%B0%EA%B3%BC%EA%B0%92-%ED%95%A9%EC%B9%98%EA%B8%B0</link>
            <guid>https://velog.io/@dulcis-hortus/JS-2%EA%B0%9C%EC%9D%98-API-%EA%B2%B0%EA%B3%BC%EA%B0%92-%ED%95%A9%EC%B9%98%EA%B8%B0</guid>
            <pubDate>Fri, 23 Sep 2022 02:21:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>최종 코드는 맨 밑에 &#39;덧2&#39; 참고!</p>
</blockquote>
<h2 id="배경">배경</h2>
<p>조건에 따라 api를 각각 따로 호출하고, 결과값은 합쳐서 state에 저장해야 하는 상황이다. 간단한 문제일 줄 알았는데 <code>.then()</code> 안에 있는 결과값을 어떻게 밖으로 빼고 가공해야 몰라서 한참 헤맸다.</p>
<pre><code class="language-javascript">if (조건문1) {
  fetch
    .get(&#39;uri&#39;)
    .then((res1) =&gt; res1)  // type : arr
    .catch(() =&gt; {})
}

if (조건문2) {
  fetch
    .get(&#39;uri&#39;)
    .then((res2) =&gt; res2)  // type : arr
    .catch(() =&gt; {})
}

// 원하는 결과값
setResult([...res1, ...res2]) // 대충 느낌만 반영한 코드</code></pre>
<h2 id="해결-방법">해결 방법</h2>
<ol>
<li>조건문보다 상위 스코프에 변수를 2개 생성하고, fetch에서 반환되는 값을 각각 변수에 저장해준다.</li>
<li>해당 변수들(promise)을 넣은 리스트를 <code>Promise.all()</code> 함수를 통해 single promise로 만들고, 이를 <code>.then()</code>으로 받으면 2차원 array로 쓸 수 있게 된다.</li>
<li><code>[].concat.apply([], exampleArr)</code> 함수를 사용해서 1차원 배열로 만든다!<pre><code class="language-javascript">let aPromise;
let bPromise;
if (조건문1) {
   aPromise = fetch
               .get(&#39;uri&#39;)
               .then((res1) =&gt; res1)
               .catch(() =&gt; {})
}
if (조건문2) {
    bPromise = fetch
                .get(&#39;uri&#39;)
                .then((res2) =&gt; res2)
                .catch(() =&gt; {});
}
// .filter() :api 반환값이 null일 경우, promise가 undefined이기 때문에 걸러주어야 한다
return [aPromise, bPromise].filter((promise) =&gt; promise); 
})
.then((promises) =&gt; {
  Promise.all(promises)
    .then((twoDimenArr) =&gt; {
           setResult([].concat.apply([], twoDimenArr));
         });
       }
     })
</code></pre>
</li>
</ol>
<pre><code>더 간단한 방법이 있을 것 같지만 현재로선 이것이 최선이었습니다...🤗
어쨌든 해결!✨

### + 덧
흐름은 그대로 쓰고, 코드만 리펙토링 해보았다. 불필요한 `.then()`이 한 번 줄었다.
```javascript
let aPromise;
let bPromise;
   if (조건문1) {
      aPromise = fetch
                  .get(&#39;uri&#39;)
                  .then((res1) =&gt; res1)
                  .catch(() =&gt; {})
   }
   if (조건문2) {
       bPromise = fetch
                   .get(&#39;uri&#39;)
                   .then((res2) =&gt; res2)
                   .catch(() =&gt; {});
   }
return Promise.all([aPromise, bPromise].filter((promise) =&gt; promise));
})
  .then((twoDepthArr) =&gt; {
       setResult([].concat.apply([], twoDepthArr));
  })</code></pre><h3 id="-덧2">+ 덧2</h3>
<p>데이터가 null일 때 만들어지는 promise는 위 코드처럼 filter로 걸러낼 수 없다! 
<a href="https://www.aleksandrhovhannisyan.com/blog/async-functions-that-return-booleans/">Be Careful with Async Functions that Return Booleans</a></p>
<blockquote>
<p>a Promise object is not one of the eight falsy values in JavaScript, so checking its truthiness is pointless: When coerced to a boolean, a Promise is always true.</p>
</blockquote>
<p>promise는 false로 치지 않는 타입이므로, 강제로 체크할 경우 무조건 true로 확인된다는 뜻.
그러므로, 데이터가 null로 올 경우를 거르고 싶다면 two depth array로 변환된 다음에 걸러야 유효하다.</p>
<pre><code class="language-javascript">let aPromise;
let bPromise;
   if (조건문1) {
      aPromise = fetch
                  .get(&#39;uri&#39;)
                  .then((res1) =&gt; res1)
                  .catch(() =&gt; {})
   }
   if (조건문2) {
       bPromise = fetch
                   .get(&#39;uri&#39;)
                   .then((res2) =&gt; res2)
                   .catch(() =&gt; {});
   }
return Promise.all([aPromise, bPromise]);
})
  .then((twoDepthArr) =&gt; {
     // 방어 코드
     const filterArr = twoDepthArr.filter((item) =&gt; Boolean(item));
     setResult([].concat.apply([], filterArr));
  })</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] UI 스켈레톤 만들기!]]></title>
            <link>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8-UI-%EC%8A%A4%EC%BC%88%EB%A0%88%ED%86%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8-UI-%EC%8A%A4%EC%BC%88%EB%A0%88%ED%86%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 01 Sep 2022 07:15:30 GMT</pubDate>
            <description><![CDATA[<h2 id="배경">배경</h2>
<p>데이터 로딩이 너무 느려서... 1초 정도 화면에 빈 공백이 생긴다.
사용자가 뒤로가기를 누를 정도는 아니지만, 좀더 편의성을 높이기 위해 스켈레톤을 만들기로 했다.
스켈레톤이란 직역하면 뼈대라는 뜻으로 실제 UI가 그려지기 전에 윤곽을 잡아둘 때 사용된다. 즉, 사용자에게 이 부분에 정보가 채워질 것을 암시한다.
<img src="https://velog.velcdn.com/images/dulcis-hortus/post/2d71e845-1161-46cc-aaf2-062102aa4797/image.png" alt="구글에서 사용하는 스켈레톤"></p>
<p>구글에서 사용하는 스켈레톤 예시.
(<del>이 노래 추천해요 👍</del>)</p>
<br>

<h2 id="문제">문제</h2>
<p>UI를 그리는 것까지는 문제가 없었는데...
문제는 shimmer 효과이다. shimmer는 반짝인다는 뜻으로, 스켈레톤 위로 스치는 빛의 그림자 같은 것이다. (아주 시적인 표현이지만 대강 알아들었길 바란다..)
마치 로딩이 빙글빙글 돌면서 뭔가가 계속 일어나고 있음을 알려주는 것처럼 이 효과가 있어야 스켈레톤도 제 역할을 다 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dulcis-hortus/post/6833085e-7bb4-4e67-907b-3de167b10057/image.png" alt=""></p>
<p>저 반 투명한 하얀 선이 shimmer 맞다. 하지만 저렇게 보여지는 게 아니라 각 아이템 하나마다 각각 따로 shimmer가 들어가야 한다.</p>
<p>이건 결국 css의 문제로 넘어간다. <code>@keyframes</code>, <code>transform</code>에 대한 이해가 부족하기 때문에 자유자재로 쓸 수 없는 것이다. 따라서 이번 기회에 css 애니메이션을 잘 익혀서 완성해보기로 하였다.</p>
<p>기간은 9월 9일까지!🙌</p>
<br>

<h2 id="작업-과정">작업 과정</h2>
<p><img src="https://velog.velcdn.com/images/dulcis-hortus/post/b845c800-ea74-4f7b-8a1b-9abbbc54db00/image.png" alt=""></p>
<p>각 아이템마다 shimmer를 넣는 건 성공했는데, 모두 똑같은 타이밍에 움직여서 부자연스럽다. 좀더 자연스럽게 움직일 수 있을 것 같은데...
<code>animation</code> 개별 속성과 <code>@keyframes</code> 사용법을 더 찾아봐야겠다.</p>
<h3 id="-추가">+ 추가</h3>
<p><img src="https://velog.velcdn.com/images/dulcis-hortus/post/22903519-f384-4a90-9b21-9ae682ce6f9c/image.png" alt=""></p>
<p>shimmer 너비를 늘리니까 훨씬 자연스럽다.
일단 이렇게 기획자분한테 컨펌을 받고 추가 수정이 필요하면 넣도록 하자.</p>
<h3 id="-추가2">+ 추가2</h3>
<p>반응이 좋았다!🥳🎉🎉
다른 페이지도 적용시켜보자는 의견이 있어서 조만간 페이지를 수정하면서 적용해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] shallow compare?]]></title>
            <link>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8-shallow-compare</link>
            <guid>https://velog.io/@dulcis-hortus/%EB%A6%AC%EC%95%A1%ED%8A%B8-shallow-compare</guid>
            <pubDate>Wed, 13 Jul 2022 01:54:31 GMT</pubDate>
            <description><![CDATA[<h2 id="배경">배경</h2>
<p>리액트 공식 문서 -&gt; pureComponent 설명할 때 shouldComponentUpdate() 함수가 나온다. 이 함수가 얕은 비교를 수행한다.</p>
<h2 id="정리">정리</h2>
<p>얕은 비교(shallow compare)란 참조 타입의 출처가 변했는지만 확인하는 것을 의미한다. 가령 object 타입일 경우, 내부 값이 변동된다고 해도 출처(메모리 주소)가 변동되지 않았다면 얕은 비교로는 동일하다고 나온다.</p>
<ul>
<li><a href="https://stackoverflow.com/questions/36084515/how-does-shallow-compare-work-in-react">https://stackoverflow.com/questions/36084515/how-does-shallow-compare-work-in-react</a></li>
</ul>
<blockquote>
</blockquote>
<pre><code class="language-javascript">// 예시 객체
user = {
  name: &quot;John&quot;,
  surname: &quot;Doe&quot;
}</code></pre>
<blockquote>
</blockquote>
<pre><code class="language-javascript">// 객체의 데이터값은 변했지만 동일하다고 판별
const user = this.state.user;
user.name = &quot;Jane&quot;;
console.log(user === this.state.user); // true</code></pre>
<pre><code class="language-javascript">// 객체의 데이터값은 같지만 다르다고 판별
const user = clone(this.state.user);
console.log(user === this.state.user); // false</code></pre>
<p>출처(메모리 주소)는 얕은 복사와도 관련이 깊으므로 이참에 원시타입, 참조타입의 특징을 다시 확인해보면 좋을 것 같다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[git] 유용한 명령어들]]></title>
            <link>https://velog.io/@dulcis-hortus/git-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%93%A4</link>
            <guid>https://velog.io/@dulcis-hortus/git-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%93%A4</guid>
            <pubDate>Tue, 05 Jul 2022 00:51:25 GMT</pubDate>
            <description><![CDATA[<h3 id="유용한-링크-⬇">유용한 링크 ⬇</h3>
<p><a href="https://dangitgit.com/ko">Oh Shit, Git!?!</a> 😱</p>
<h3 id="짧은-코멘트">짧은 코멘트</h3>
<ol>
<li>번역이 엄청 재밌다ㅋㅋ</li>
<li>글쓴이의 생생한 <del>빡침</del> 경험이 느껴진다.</li>
<li>커밋한 이후에 파일을 수정하게 되서 다시 커밋 -&gt; rebase로 하는 경우가 많았는데,
여기서 소개해준 다른 방식을 써보는 게 좋을 것 같다.</li>
</ol>
<pre><code>git add . # 빠뜨린 파일 추가
git commit --amend --no-edit  # 마지막 커밋 메시지 변경 없이 커밋에 추가</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[에러] window.scrollTo가 작동하지 않을 때]]></title>
            <link>https://velog.io/@dulcis-hortus/%EC%97%90%EB%9F%AC-window.scrollTo%EA%B0%80-%EC%9E%91%EB%8F%99%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C</link>
            <guid>https://velog.io/@dulcis-hortus/%EC%97%90%EB%9F%AC-window.scrollTo%EA%B0%80-%EC%9E%91%EB%8F%99%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C</guid>
            <pubDate>Wed, 22 Jun 2022 07:14:06 GMT</pubDate>
            <description><![CDATA[<h2 id="상황">상황</h2>
<p>페이지를 이동할 때마다 자동으로 스크롤을 최상단으로 이동시키려 했다. 이를 위해 <code>window.scrollTo(0,0)</code>을 사용했지만 제대로 작동하지 않았다.</p>
<h2 id="해결">해결</h2>
<p>구글 검색어 : why is not window.scrollTo working</p>
<ul>
<li><a href="https://stackoverflow.com/questions/1174863/javascript-scrollto-method-does-nothing">JavaScript scrollTo method does nothing?</a></li>
</ul>
<blockquote>
<p>Solution:</p>
<pre><code class="language-css">html { height: 100%; overflow:auto; }
body { height: 100%; }</code></pre>
</blockquote>
<p>```</p>
<p>작업 중이던 코드의 index.css 파일에도 <code>body {overflow-x:hidden}</code>이 걸려있었다. <strong>모바일 모드일 때 화면 우측에 빈 여백이 남아서 추가된 코드였다.</strong> 
페이지네이션은 PC 기준으로 작업하고 있었기에 @media로 분리된 PC CSS에서는 해당 코드를 제거하였다.
이후 다시 테스트하자 정상적으로 작동하였다.
해결!✨</p>
<h3 id="-추가">+ 추가</h3>
<p>index.css 파일은 굉장히 큰 단위의 css이고, 더구나 body 전체에 {overflow-x: hidden}을 걸어야 한다면 어딘가 잘못 구현된 컴포넌트가 있다는 뜻으로 이해했다. (굳이 자식 컴포넌트가 부모 컴포넌트의 영역 밖으로 넘어갈 이유가 없었다.)
코드를 확인해보니 배너 부분의 css가 잘못되어 있었고 이를 해결하자 우측 여백은 없어졌다. 
따라서 해당 코드도 사용할 필요가 없어졌기에 아예 코드에서 제거할 수 있었다!🎉🎉🎉</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[html] input 이미지 파일만 올리게 하기]]></title>
            <link>https://velog.io/@dulcis-hortus/html-input-%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%8C%8C%EC%9D%BC%EB%A7%8C-%EC%98%AC%EB%A6%AC%EA%B2%8C-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dulcis-hortus/html-input-%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%8C%8C%EC%9D%BC%EB%A7%8C-%EC%98%AC%EB%A6%AC%EA%B2%8C-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 17 Jun 2022 05:45:59 GMT</pubDate>
            <description><![CDATA[<h2 id="배경">배경</h2>
<p>이미지 파일만 올라가야 하는 부분이 있어서 input에 <code>accept=&quot;image/*&quot;</code>을 추가해주었다. 하지만 사용자가 파일 유형을 모든 종류의 파일로 바꿔서 다른 유형 파일을 올리는 경우가 생긴다. </p>
<h2 id="원인">원인</h2>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/HTML/Element/Input/file"><code>&lt;input type=&quot;file&quot;&gt;</code></a></li>
</ul>
<blockquote>
<p>accept 특성은 선택한 파일 유형을 검증하지는 않으며, 단순히 브라우저가 사용자를 올바른 파일 유형으로 유도하도록 힌트를 제공할 뿐입니다. <strong>(대부분의 경우) 사용자가 파일 선택 창의 옵션을 설정해 accept를 덮어쓰고 자신이 원하는 아무 파일이나 선택할 수 있으므로, 파일 입력 칸에 잘못된 유형의 파일이 올 수 있습니다.</strong></p>
</blockquote>
<p>...자연스러운 현상이었다!🤩</p>
<h2 id="해결책">해결책</h2>
<blockquote>
<p>그러므로, 반드시 적절한 <strong>서버 사이드 유효성 검증</strong>을 통해 accept 특성을 보조해야 합니다.</p>
</blockquote>
<p>여기에서 말하는 서버는 api 서버를 말하는 것 같다. (api error)
프론트에서 할 수 있는 방법도 찾아보자.</p>
<ul>
<li><a href="https://www.codegrepper.com/code-examples/javascript/how+to+check+file+is+image+or+not+in+javascript">“how to check file is image or not in javascript” Code Answer</a></li>
</ul>
<pre><code class="language-javascript">function isFileImage(file) {
    return file &amp;&amp; file[&#39;type&#39;].split(&#39;/&#39;)[0] === &#39;image&#39;;
}</code></pre>
<p>이렇게 한번 더 검증해주면 끝이다.
해결!✨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TSX] tsx 컴포넌트 사용 에러]]></title>
            <link>https://velog.io/@dulcis-hortus/TSX-tsx-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%82%AC%EC%9A%A9-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@dulcis-hortus/TSX-tsx-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%82%AC%EC%9A%A9-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Tue, 24 May 2022 02:20:07 GMT</pubDate>
            <description><![CDATA[<h2 id="에러-코드">에러 코드</h2>
<p>TS2786: &#39;BsButton&#39; cannot be used as a JSX component.   </p>
<p>Its element type &#39;ReactElement&lt;any, any&gt; | Component&lt;Pick&lt;Props, &quot;text&quot; | &quot;variant&quot; | &quot;onClick&quot; | &quot;href&quot; | &quot;wide&quot; | &quot;disabled&quot; | &quot;bgColor&quot; | &quot;textColor&quot; | &quot;fontWeight&quot; | &quot;size&quot; | &quot;value&quot; | &quot;startIcon&quot; | &quot;endIcon&quot;&gt; &amp; WithWidthProps, any, any&gt; | null&#39; is not a valid JSX element.     </p>
<p>Type &#39;Component&lt;Pick&lt;Props, &quot;text&quot; | &quot;variant&quot; | &quot;onClick&quot; | &quot;href&quot; | &quot;wide&quot; | &quot;disabled&quot; | &quot;bgColor&quot; | &quot;textColor&quot; | &quot;fontWeight&quot; | &quot;size&quot; | &quot;value&quot; | &quot;startIcon&quot; | &quot;endIcon&quot;&gt; &amp; WithWidthProps, any, any&gt;&#39; is not assignable to type &#39;Element | ElementClass | null&#39;.       </p>
<p>Type &#39;Component&lt;Pick&lt;Props, &quot;text&quot; | &quot;variant&quot; | &quot;onClick&quot; | &quot;href&quot; | &quot;wide&quot; | &quot;disabled&quot; | &quot;bgColor&quot; | &quot;textColor&quot; | &quot;fontWeight&quot; | &quot;size&quot; | &quot;value&quot; | &quot;startIcon&quot; | &quot;endIcon&quot;&gt; &amp; WithWidthProps, any, any&gt;&#39; is not assignable to type &#39;ElementClass&#39;.         </p>
<p>The types returned by &#39;render()&#39; are incompatible between these types.           </p>
<p>Type &#39;React.ReactNode&#39; is not assignable to type &#39;import(&quot;C:/Users/bsit/go/src/sharing-platform-web-app/node_modules/@types/react-transition-group/node_modules/@types/react/index&quot;).ReactNode&#39;.</p>
<h2 id="해결">해결</h2>
<p>??</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[slackbot] 설치할 봇 사용자가 없습니다]]></title>
            <link>https://velog.io/@dulcis-hortus/slackbot-%EC%84%A4%EC%B9%98%ED%95%A0-%EB%B4%87-%EC%82%AC%EC%9A%A9%EC%9E%90%EA%B0%80-%EC%97%86%EC%8A%B5%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@dulcis-hortus/slackbot-%EC%84%A4%EC%B9%98%ED%95%A0-%EB%B4%87-%EC%82%AC%EC%9A%A9%EC%9E%90%EA%B0%80-%EC%97%86%EC%8A%B5%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Thu, 03 Feb 2022 05:21:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 포스팅은 아무리 구글링을 해도 나오지 않던 이 에러를 맞닥뜨릴 또 다른 누군가를 위해 작성되었습니다.</p>
</blockquote>
<br>

<h2 id="배경">배경</h2>
<p><img src="https://images.velog.io/images/dulcis-hortus/post/745dfac6-80fb-42de-8ace-a35dbb497165/image.png" alt=""></p>
<h3 id="에러-메시지">에러 메시지</h3>
<blockquote>
<p>앱에 설치할 봇 사용자가 없습니다.
이 앱은 위크스페이스에 봇을 설치할 권한을 요청하지만 현재는 봇으로 구성되어 있지 않습니다.</p>
</blockquote>
<p>평화롭게 슬랙봇을 만들던 어느 날, 아니 슬랙봇을 만들기 시작한 첫날부터 이런 에러를 만났다.
&#39;봇 사용자?? 슬랙 워크스페이스에서 만들어야 하는 건가?&#39;
하지만 아무리 워크스페이스 설정을 뒤져봐도 봇 사용자 설정은 나오지 않았다...</p>
<br>

<h2 id="원인">원인</h2>
<ul>
<li><a href="https://api.slack.com/bot-users">Enabling interactions with bots</a></li>
</ul>
<p><img src="https://images.velog.io/images/dulcis-hortus/post/030bfe7e-049b-4e54-84ac-fd60870fefa3/image.png" alt=""></p>
<p>공식문서를 보면 이렇게 봇 사용자를 만들어야 한다고 되어 있다.
(<del>어디에서 만드는지 좀 더 구체적으로 알려주길 바라는 마음이 10000% 정도 있다.</del>)
문서에서는 Bot Users 메뉴 안에 Add a Bot User 버튼이 따로 있다는데 해당 메뉴도, 버튼은 찾지 못했다.</p>
<p>대신 우여곡절 끝에 찾아낸 방법을 정리한다.</p>
<ol>
<li><p><img src="https://images.velog.io/images/dulcis-hortus/post/b1015170-dd89-4b19-9bb8-c862be88312a/image.png" alt=""></p>
</li>
<li><p><img src="https://images.velog.io/images/dulcis-hortus/post/22762677-7a16-4ced-af46-cf8591435f88/image.png" alt=""></p>
</li>
<li><p><img src="https://images.velog.io/images/dulcis-hortus/post/7bb31dfd-494c-4df6-b26f-c7286c051765/image.png" alt=""></p>
</li>
<li><p><img src="https://images.velog.io/images/dulcis-hortus/post/a06e88ea-6826-4e3d-bfa5-b5038274ef4c/image.png" alt=""></p>
</li>
</ol>
<p>이렇게 저장하고 다시 <code>Install to Workspace</code> 누르면 잘 연결된다.
해결!✨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[문제 해결] "Permission Denied" trying to run Python on Windows 10]]></title>
            <link>https://velog.io/@dulcis-hortus/%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-Permission-Denied-trying-to-run-Python-on-Windows-10</link>
            <guid>https://velog.io/@dulcis-hortus/%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-Permission-Denied-trying-to-run-Python-on-Windows-10</guid>
            <pubDate>Mon, 24 Jan 2022 06:10:19 GMT</pubDate>
            <description><![CDATA[<h2 id="배경">배경</h2>
<p>window store에서 python을 깔고 실행하려는데 Permission Denied 에러가 발생했다. </p>
<br>

<h2 id="원인-및-해결">원인 및 해결</h2>
<ul>
<li><a href="https://stackoverflow.com/questions/56974927/permission-denied-trying-to-run-python-on-windows-10">&quot;Permission Denied&quot; trying to run Python on Windows 10</a></li>
</ul>
<blockquote>
<p>if I try using PowerShell, it just opens the Windows store as if the app isn&#39;t installed so I&#39;m thinking it can&#39;t see the contents of my <code>/c/Users/david/AppData/Local/Microsoft/WindowsApps/</code> folder for some reason.</p>
</blockquote>
<blockquote>
<p>PowerShell을 사용하면 마치 python이 깔려있지 않은 것처럼 윈도우 스토어를 실행합니다. <code>/c/Users/david/AppData/Local/Microsoft/WindowsApps/</code> 안에 있는 파일을 읽지 못하는 것 같습니다.</p>
</blockquote>
<blockquote>
<p>It looks like this added two &quot;stubs&quot; called python.exe and python3.exe into the %USERPROFILE%\AppData\Local\Microsoft\WindowsApps folder, and in my case, this was inserted before my existing Python executable&#39;s entry in the PATH.</p>
</blockquote>
<blockquote>
<p>python.exe와 python3.exe라는 두 개의 스텁이 <code>%USERPROFILE%\AppData\Local\Microsoft\WindowsApps</code> 폴더 안에 추가되었고 제 경우엔 기존에 존재하던 python 경로보다 앞에 경로가 추가되었습니다.</p>
</blockquote>
<blockquote>
<p>Moving this entry below the correct Python folder (partially) corrected the issue.</p>
</blockquote>
<blockquote>
<p>이 항목을 올바른 python 폴더 아래 움직이는 방법이 (완전하진 않지만) 이 문제를 해결했습니다.</p>
</blockquote>
<p>하지만 내 경우엔 WindowsApps에 있는 파일을 옮길 권한을 전체 공개로 풀었음에도 파일이 옮겨지지 않아서 그 밑에 나온 백그라운드 설정을 끄는 것으로 해결했다.✨</p>
<br>

<h3 id="결론">&lt;결론&gt;</h3>
<p>python을 설치할 때는 window 스토어가 아니라 python 공홈에서 설치하자.😑</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] Why does JavaScript map function return undefined?]]></title>
            <link>https://velog.io/@dulcis-hortus/JS-Why-does-JavaScript-map-function-return-undefined</link>
            <guid>https://velog.io/@dulcis-hortus/JS-Why-does-JavaScript-map-function-return-undefined</guid>
            <pubDate>Mon, 10 Jan 2022 08:43:19 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-상황">문제 상황</h2>
<pre><code class="language-javascript">      const AllCourierIds = [{id: 1, ...}, {id: 2, ...}, {id: 3, ...}]
        .map((courier) =&gt; {
          if (courier.id !== 3) {
            return courier.id;
          }
        }).join();</code></pre>
<p><img src="https://images.velog.io/images/dulcis-hortus/post/ea730987-0613-4e76-b739-12fddfda7611/image.png" alt="">
원했던 결과물(<code>AllCourierIds</code>)는 &#39;1,2&#39; 였는데 ,가 끝에 붙어서 나왔다.</p>
<br>

<h2 id="원인">원인</h2>
<pre><code class="language-javascript">      const AllCourierIds = [{id: 1, ...}, {id: 2, ...}, {id: 3, ...}]
        .map((courier) =&gt; {
          if (courier.id !== 3) {
            return courier.id;
          }
        })    // [1, 2, undefined]
        .join();  // ,로 구분하기 때문에 &#39;1,2,&#39;가 나오는 게 맞다</code></pre>
<br>

<h2 id="해결책">해결책</h2>
<ul>
<li><a href="https://stackoverflow.com/questions/16037049/why-does-javascript-map-function-return-undefined">Why does JavaScript map function return undefined?</a></li>
</ul>
<blockquote>
<p>The map function is used to map one value to another, but it looks like you actually want to filter the array, which a map function is not suitable for.</p>
</blockquote>
<blockquote>
<p><strong>map 함수는 한 개의 값을 다른 값으로 바꿀 때 사용됩니다.</strong> 하지만 당신이 원한 건 배열에서 특정 원소를 필터링하는 것 같네요. 그럼 map 함수는 적합하지 않습니다.</p>
</blockquote>
<h3 id="수정한-코드">수정한 코드</h3>
<pre><code class="language-javascript">      const AllCourierIds = [{id: 1, ...}, {id: 2, ...}, {id: 3, ...}]
        .filter((courier) =&gt; courier.id !== 3)
        .map((courier) =&gt; courier.id)
        .join();
</code></pre>
<br>
🤔오늘의 교훈 : 필터링을 할 때는 filter() 함수를 사용하자]]></description>
        </item>
        <item>
            <title><![CDATA[[SSL] 공개키와 개인키]]></title>
            <link>https://velog.io/@dulcis-hortus/SSL-%EA%B3%B5%EA%B0%9C%ED%82%A4%EC%99%80-%EA%B0%9C%EC%9D%B8%ED%82%A4</link>
            <guid>https://velog.io/@dulcis-hortus/SSL-%EA%B3%B5%EA%B0%9C%ED%82%A4%EC%99%80-%EA%B0%9C%EC%9D%B8%ED%82%A4</guid>
            <pubDate>Fri, 07 Jan 2022 06:09:09 GMT</pubDate>
            <description><![CDATA[<h2 id="자물쇠와-열쇠">자물쇠와 열쇠</h2>
<blockquote>
<p>공개키/개인키 방식은 특정 자물쇠를 만드는 방법과 그 자물쇠를 열 수 있는 열쇠로 비유할 수 있다. </p>
</blockquote>
<p>자물쇠를 만드는 방법(암호화하는 키)이 공개키일 수도 있고, 반대로 열쇠(복호화하는 키)가 공개키일 수도 있다.
중요한 건 둘 다 공개되면 암호화의 의미를 잃어버린다는 점이다.
이 자물쇠는 특정 열쇠로만 열 수 있기에 (=암호화가 되면 맞는 쌍으로만 복호화가 가능하기에) 그 자체로 보증이 된다.</p>
<br>


<h2 id="예시---ssl-인증">예시 - SSL 인증</h2>
<p>SSL 인증 과정에서도 공개키/개인키 암호화가 사용된다.
웹 사이트가 안전한지 보장해주는 인증기관(CA)는 웹 사이트가 안전하다면 인증서를 개인키로 암호화해서 서버로 돌려준다. 
브라우저들은 CA의 공개키를 가지고 있기 때문에 인증서를 받아 복호화할 수 있다.
만일 열쇠가 맞아서 복호화가 된다면 이 인증서는 CA가 개인키로 만든 게 맞고, 이 키는 오직 CA만 알고 있으므로 공식적으로 인증 받은 사이트라는 의미가 된다.</p>
]]></description>
        </item>
    </channel>
</rss>