<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dding_yull.log</title>
        <link>https://velog.io/</link>
        <description>ɪ ʜᴏᴘᴇ ᴛᴏ sᴏʟᴠᴇ ʀᴇᴀʟ ᴘʀᴏʙʟᴇᴍs👩🏻‍💻❤️</description>
        <lastBuildDate>Thu, 06 Mar 2025 15:06:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dding_yull.log</title>
            <url>https://velog.velcdn.com/images/dding_yull/profile/54f46122-e646-4036-8603-afd2c2f3ef15/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dding_yull.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dding_yull" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[크롬 브라우저에서 Share API 무한 로딩 문제 해결]]></title>
            <link>https://velog.io/@dding_yull/%ED%81%AC%EB%A1%AC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C-Share-API-%EB%AC%B4%ED%95%9C-%EB%A1%9C%EB%94%A9-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@dding_yull/%ED%81%AC%EB%A1%AC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C-Share-API-%EB%AC%B4%ED%95%9C-%EB%A1%9C%EB%94%A9-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Thu, 06 Mar 2025 15:06:55 GMT</pubDate>
            <description><![CDATA[<p><strong>🔁 최근 특정 크롬 브라우저에서 navigator.share를 호출할 때, 공유 창이 열리지만 무한 로딩 상태에 빠지는 현상이 있었다.</strong></p>
<p>아래 콘솔로 확인해보면 navigator.share는 true로 나오니 API는 존재하는데 
Object.keys(navigator.share)를 봤을 땐 메서드가 비어있었다... 
처음에 해당 문제를 찾을 때 대부분 API 존재여부를 확인하라는 내용이 많다보니 메서드가 없을 것이라고는 생각도 못했었다
브라우저가 Share API를 캐시된 상태에서 업데이트되면서 API의 활성화 여부와 실제 구현 상태가 일치하지 않았던 것 같다.</p>
<pre><code class="language-javascript">console.log(&#39;navigator.share 지원 여부:&#39;, !!navigator.share);  // true
console.log(&#39;Share API 메서드:&#39;, Object.keys(navigator.share));  // []</code></pre>
<h3 id="내가-생각한-문제-원인">내가 생각한 문제 원인</h3>
<ul>
<li>크롬 플래그 설정이 비활성화 상태이거나 최신 업데이트 반영이 되지 않음</li>
<li>브라우저 캐시로 인해 이전 상태가 유지되면서 API는 있는데 메서드가 없었고 </li>
<li>그로 인해 share창이 열리면서 API가 호출되지만 안에 메서드가 없어서 내용이 안보이고 무한로딩 현상이 지속되었다.
때문에 시크릿 모드에서도 문제 발생 (시크릿 모드에서도 특정 브라우저 설정이 유지됨)</li>
<li>시크릿 모드에서도 같은 현상이 발생하니 당연히 캐시는 아닐것이라 생각했었는데 시크릿 모드라도 유지되는 상태들이 있다는 것을 배우게 됐다.</li>
</ul>
<blockquote>
<p>💡 [시크릿 모드에서도 동일한 문제가 발생하는 이유]
시크릿 모드는 브라우징 기록, 쿠키, 사이트 데이터를 삭제하지만, 브라우저 설정과 API 상태는 유지되기 때문에 같은 문제가 발생할 수 있었다.</p>
</blockquote>
<table>
<thead>
<tr>
<th>✅ 시크릿 모드에서 지워지는 항목</th>
<th>❌ 시크릿 모드에서도 유지되는 항목</th>
</tr>
</thead>
<tbody><tr>
<td>브라우징 기록</td>
<td>브라우저 설정 (<code>chrome://flags</code> 포함)</td>
</tr>
<tr>
<td>쿠키 및 사이트 데이터</td>
<td>하드웨어 가속 설정</td>
</tr>
<tr>
<td>양식 입력 데이터</td>
<td>확장 프로그램 설정</td>
</tr>
<tr>
<td></td>
<td>API 구현 상태</td>
</tr>
</tbody></table>
<h3 id="해결-방법">해결 방법</h3>
<p><strong>1단계. 크롬 플래그 설정 확인</strong></p>
<ol>
<li><p>크롬 주소창에 chrome://flags 입력</p>
</li>
<li><p>해당 페이지의 검색창에 &quot;Web Share&quot; 검색</p>
</li>
<li><p>해당 옵션이 Enabled(활성화) 상태인지 확인 후 필요하면 변경</p>
</li>
</ol>
<hr>
<p><strong>2단계. 브라우저 캐시 삭제 및 업데이트</strong></p>
<ol>
<li><p>캐시 삭제</p>
</li>
<li><p>크롬 설정 → 설정 &gt; 개인정보 및 보안 &gt; 인터넷 사용 기록 삭제</p>
</li>
<li><p>캐시된 이미지 및 파일 삭제 (쿠키도 함께 삭제하는 것이 권장됨)</p>
</li>
<li><p>크롬 업데이트 확인</p>
</li>
<li><p>설정 &gt; Chrome 정보에서 최신 버전인지 확인</p>
</li>
<li><p>최신 버전이 아니라면 업데이트 후 브라우저 재시작</p>
</li>
</ol>
<hr>
<ul>
<li>공식문서
<a href="https://developer.mozilla.org/ko/docs/Web/API/Navigator/share">https://developer.mozilla.org/ko/docs/Web/API/Navigator/share</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴파일과 번들링]]></title>
            <link>https://velog.io/@dding_yull/%EC%BB%B4%ED%8C%8C%EC%9D%BC%EA%B3%BC-%EB%B2%88%EB%93%A4%EB%A7%81</link>
            <guid>https://velog.io/@dding_yull/%EC%BB%B4%ED%8C%8C%EC%9D%BC%EA%B3%BC-%EB%B2%88%EB%93%A4%EB%A7%81</guid>
            <pubDate>Wed, 25 Dec 2024 23:29:03 GMT</pubDate>
            <description><![CDATA[<p>지금 내 프로젝트가 어떻게 빌드되고 있는지에 대해 알아보다가
자주 듣던 컴파일, 번들링에 대해 좀 더 베이직한 의미에 대해 한번 찾아봤다. 
대략적으로 이해했는데 실제로 직접 해보며 이해도를 높여가고 싶군..!</p>
<h3 id="컴파일">컴파일</h3>
<ul>
<li>예를들면 리액트에서 jsx, tsx로 작성한 코드 → javascript로 변환하는 과정</li>
<li>나보다는 컴퓨터가 더 잘 이해하도록</li>
<li>보통 Babel이 이 역할을 담당</li>
</ul>
<hr/>

<h3 id="번들링">번들링</h3>
<ul>
<li>여러개의 다양한 리소스를 하나의 파일 (또는 몇개의 파일)로 묶는 과정</li>
<li>보통 Webpack, Parcel, Vite가 번들러로 사용됨</li>
</ul>
<p>[예시] src의 3개 javascript를 dist경로의 bundle.js에 묶음</p>
<pre><code>src/
├── App.js
├── Header.js
├── Footer.js</code></pre><p>⬇️</p>
<pre><code>dist/
├── bundle.js</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[웹 어플리케이션 빌드 프로세스_지금 내 프로젝트는 어떻게 빌드되는 중이지?]]></title>
            <link>https://velog.io/@dding_yull/%EC%A7%80%EA%B8%88-%EB%82%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B9%8C%EB%93%9C%ED%95%A0%EB%95%8C-shell%ED%8C%8C%EC%9D%BC%EC%9D%80</link>
            <guid>https://velog.io/@dding_yull/%EC%A7%80%EA%B8%88-%EB%82%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B9%8C%EB%93%9C%ED%95%A0%EB%95%8C-shell%ED%8C%8C%EC%9D%BC%EC%9D%80</guid>
            <pubDate>Wed, 25 Dec 2024 23:22:44 GMT</pubDate>
            <description><![CDATA[<p>오늘 귓동냥으로 좀 더 알아봐야겠다고 생각한 부분은 빌드할 때 shell파일에 대한 전반적인 내용이다. 
일을 하다보면 이미 세팅되어있던 방식대로 빌드하고 그때그때 에러만 수정하면서 배포하는 경우가 많아서 오늘은 기본적인 부분들부터 찾아보고 정리해보았다. 
보충해야할 내용은 있겠으나 일단 조금씩 더 알아보고 기록해보쟈!
(잘못된 부분은 알려주세요!)</p>
<h2 id="❓지금-내-프로젝트에서-빌드할때-shell파일은-어디에-어떻게">❓지금 내 프로젝트에서 빌드할때 shell파일은 어디에 어떻게?</h2>
<ul>
<li><p>root의 shell 파일에 실행시키기 위해 필요한 명령어를 적어두고 빌드 전에 쉘스크립트대로 실행시킨 후 빌드한다.</p>
</li>
<li><p>그럼 내가 하는 프로젝트에는 shell파일이 어딨지?</p>
</li>
<li><p>현재 나는 직접 프로젝트의 yarn 명령어로 진행!</p>
</li>
<li><p>yarn 명령어는 프로젝트 최상단의 package.json파일을 읽어서 실행되는데
알게된 김에 dev, stg, production에 따라 명령어 정리하고 dev 스크립트도 단순화해보았다.</p>
</li>
</ul>
<blockquote>
<pre><code>&quot;build:base&quot;: &quot;react-scripts --max_old_space_size=4096 build&quot;,
&quot;build-dev&quot;: &quot;env-cmd -f .env.development npm run build:base&quot;,</code></pre></blockquote>
<ul>
<li><code>env-cmd -f .env.development</code> : .env.development파일의 환경 변수를 로드하라는 명령어</li>
<li><code>react-scripts build</code> : react 프로젝트에서 기본 빌드 스크립트임, 정적 파일로 빌드함</li>
<li><code>--max_old_space_size=4096</code> : node.js의 메모리 제한을 4096MB로 늘린다는 명령어</li>
</ul>
<hr/>


<h3 id="❓위에서-정적-파일로-빌드함에-대해-조금-더-찾아보자면">❓위에서 “정적 파일로 빌드함”에 대해 조금 더 찾아보자면?</h3>
<ul>
<li>React는 기본적으로 SPA(single page application)로 html, css, javascript로 변환하여 빌드한다는 의미</li>
</ul>
<h3 id="❓변환하는-과정을-조금-더-자세히-찾아보자면">❓변환하는 과정을 조금 더 자세히 찾아보자면?</h3>
<p>   1️⃣ 어플리케이션 번들링 &amp; 최적화 진행</p>
<ul>
<li>react 코드 (.jsx, .tsx) → javascript로 변환 (=컴파일 &amp; 번들링)</li>
<li>webpack으로 의존성과 자바스크립트 모듈을 하나의 파일 또는 여러 파일로 병합</li>
<li>필요하지 않은 코드 제거</li>
<li>webpack은 번들링하는 도구이며, react-script에 들어가 있어서 이것만 있으면 됨,
   다만 의존하는 webpack 버전, 관련 패키지는 yarn.lock에서 확인 가능</li>
<li>코드 압축 + 난독화 → 파일 크기 최소화</li>
</ul>
<p>2️⃣ HTML 파일 생성</p>
<ul>
<li>최종적으로 SPA로 동작하기 위한 기본 HTML 파일(index.html) 생성</li>
<li>이 html에 번들링된 javascript 파일 <script>태그로 삽입</li>
</ul>
<pre><code class="language-jsx">* index.html

&lt;div id=&quot;root&quot;&gt;
// root 안에 react 컴포넌트가 렌더링 됨 
&lt;/div&gt;</code></pre>
<p>  3️⃣ css & 기타 파일 처리</p>
<ul>
<li>css 파일은 최적화(압축), 필요한 경우 javascript에 포함됨</li>
<li>이미지, 폰트, JSON 파일 등 정적 자산은 최적화된 상태로 bulid/ 디렉토리에 복사</li>
</ul>
<p>  4️⃣ 최적화된 정적 파일들은 bulid/ 디렉토리에 저장되고 이 디렉토리가 서버로 배포</p>
<ul>
<li><p>참고용</p>
<ul>
<li><p>asset-manifest.json</p>
</li>
<li><p>4번의 생성된 정적파일들의 맵핑 정보 저장, 서버에서 캐싱 & 정적파일 경로 관리로 사용</p>
</li>
<li><p>robots.txt: SEO & 웹 크롤링 설정 파일</p>
<pre><code>  ```jsx
  build/
  ├── static/
  │   ├── css/
  │   │   └── main.12345.css
  │   ├── js/
  │   │   └── main.67890.js
  │   └── media/
  │       └── logo.abcdef.svg
  ```</code></pre></li>
</ul>
</li>
</ul>
<p> 5️⃣ CSR를 통해 실행됨 </p>
<ul>
<li>서버 (Nginix, Apache 등)에서 bulid/ 경로의 파일들을 전달</li>
<li>브라우저는 서버에게 받은 후 index.html을 읽고 javascript파일 로드 + 렌더링 + DOM에 삽입</li>
<li>SPA 동작하여 라우팅, UI가 업데이트 됨</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[비교연산자 === 과 ==의 차이]]></title>
            <link>https://velog.io/@dding_yull/%EB%B9%84%EA%B5%90%EC%97%B0%EC%82%B0%EC%9E%90-%EA%B3%BC-%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@dding_yull/%EB%B9%84%EA%B5%90%EC%97%B0%EC%82%B0%EC%9E%90-%EA%B3%BC-%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Tue, 13 Feb 2024 07:17:12 GMT</pubDate>
            <description><![CDATA[<h3 id="비교-연산자-중--와-의-차이점에-대해-기록해두고자-한다">비교 연산자 중  <code>===</code>와 <code>==</code>의 차이점에 대해 기록해두고자 한다.</h3>
<p>대충 알고 있을 때 제대로 비교하기 위해선 <code>===</code>를 사용해야한다 정도만 알고 있었던 것 같다. 가끔 쫌쫌따리로 스프링 코드를 볼 때 <code>==</code>가 많이 나오길래 같은 의미이지만 스프링에선 이 연산자로도 비교가 가능한줄 알았다. </p>
<h3 id="javascript기반의-react에서-이-비교-연산자의-차이점은-_무엇을-비교하는가_에-있다">javascript기반의 react에서 이 비교 연산자의 차이점은 _<strong>&#39;무엇을 비교하는가&#39;</strong>_에 있다.</h3>
<h3 id="--">{} <code>===</code> {}</h3>
<pre><code>✅ 엄격한 같음
✅ 먼저 타입을 비교하고 타입이 동일하지 않을 경우 false를 바로 반환한다.</code></pre><pre><code class="language-typescript">const a = 100;
const b = &#39;100&#39;;

console.log(a === b); // false</code></pre>
<pre><code>✅ {} 객체 안에 들어있는 실제 데이터를 각각 비교하기 때문에 true가 된다.</code></pre><h3 id="---1">{} <code>==</code> {}</h3>
<pre><code>✅ 느슨한 같음</code></pre><pre><code class="language-typescript">const a = 100;
const b = &#39;100&#39;;
console.log(a === b); // true</code></pre>
<pre><code>✅ {} 객체의 실제 데이터가 아니라 참조를 기반으로 비교하기 때문에 false가 된다. 동일하게 생긴 데이터라도 참조하는 곳(데이터를 담아두는 곳)이 다를 수 있기 때문이다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[CI/CD에 대해 알아보기]]></title>
            <link>https://velog.io/@dding_yull/CICD%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dding_yull/CICD%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Thu, 08 Feb 2024 01:11:32 GMT</pubDate>
            <description><![CDATA[<p>프론트엔드 개발자로 일하면서 지식의 부족함을 많이 느껴 짬이 날때마다 그 동안 궁금했던 단어들을 찾아보고자 한다. 
그 중 어제 찾아봤던 부부은 CI/CD에 대한 부분이다. 
사실 세팅까지 해서 적용해보는게 의미가 있는 부분이라고 생각하긴 하지만 현재 업무에는 이미 적용되어있는 부분이 있어서 일단 어떤 의미인지 먼저 찾아봤다. </p>
<h2 id="cicd">CI/CD</h2>
<table>
<thead>
<tr>
<th align="left"><center>CI</center></th>
<th align="right"><center>CD</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><center>Continuous Integration</center></td>
<td align="right"><center>Continuous Delivery</center></td>
</tr>
<tr>
<td align="left"><center>지속적 통합</center></td>
<td align="right"><center>지속적 배포</center></td>
</tr>
</tbody></table>
<p><span style="color:olivedrab">CI(지속적 통합)</span>은 프로젝트를 <span style="color:olivedrab">빌드</span>하고 <span style="color:olivedrab">테스트</span>하는 과정을 자동화한 것 
로컬에서 작업한 코드를 main브랜치에 올려 merge하면 자동으로 빌드된 후 테스트가 진행된다.</p>
<p><span style="color:#00BFFF">CD(지속적 배포)</span>는 말 그대로 빌드된 프로젝트를 <span style="color:#00BFFF">서버에 업데이트</span>하여 올리는 것</p>
<p>👉🏼 오랜시간이 걸리는 [빌드, 테스트, 서버에 업데이트]를 자동화하게되면 시간비용도 절약하고 안정성도 보장할 수 있는 유익이 있다.</p>
<h2 id="how-to">HOW TO?</h2>
<p>1️⃣ 배포할 방법 1개를 선택하여 설정한다(젠킨스, aws 등 선택하면 됨)</p>
<p>2️⃣ AWS배포하기 참고 → <a href="https://velog.io/@coddingyun/AWS-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B0%B0%ED%8F%AC">https://velog.io/@coddingyun/AWS-프론트엔드-배포</a></p>
<p>3️⃣ Git의 tab버튼 중 &#39;Git Action&#39;을 선택하여 진입
main 브랜치로 merge하면?
: 자동으로 프로젝트 빌드 → 테스트 → S3에 업데이트된 파일 업로드 </p>
<p>3️⃣-1. Git의 actions에 가서 set up a workflow yourself로 템플릿 생성</p>
<p>3️⃣-2. main.yml파일에 코드를 작성해준다 (git action에 관한 파일)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[반복적인 코드는 utils의 공통함수로 분리하기]]></title>
            <link>https://velog.io/@dding_yull/%EB%B0%98%EB%B3%B5%EC%A0%81%EC%9D%B8-%EC%BD%94%EB%93%9C%EB%8A%94-utils%EC%9D%98-%EA%B3%B5%ED%86%B5%ED%95%A8%EC%88%98%EB%A1%9C-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dding_yull/%EB%B0%98%EB%B3%B5%EC%A0%81%EC%9D%B8-%EC%BD%94%EB%93%9C%EB%8A%94-utils%EC%9D%98-%EA%B3%B5%ED%86%B5%ED%95%A8%EC%88%98%EB%A1%9C-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 06 Feb 2024 07:01:31 GMT</pubDate>
            <description><![CDATA[<p>오늘 일하는 중 시간적 여유가 생겨 이때다 싶어 보기 불편했던 코드를 보다 간편하게 수정했다. 
수정한 부분은 마이페이지의 프로필 변경 함수이다. 
새로운 제품들이 들어오고 UI도 함께 리뉴얼 시키면서 프로필이미지를 설정하는 부분들의 UI도 함께 수정되었다. </p>
<p>그러면서 이번 작업에서 아쉬웠던 점은 시간 비용이 높았던 점이다.
시간에 맞춰 진행하다보니 프로필이미지를 세팅하는 tsx파일 안에 함수를 작성하였고 
이후 기획서에 추가된 사항들을 반영하다보니 공통된 역할을 하는 함수인데 각 tsx파일마다 작성하고 혹시 별도의 예외처리를 해줘야하는지 여러 테스트를 진행하며 많은 시간을 소요하게 되었다. </p>
<p>내가 작업하는 곳 외에는 최대한 건드리지 않아야한다는 생각에 자주 사용하진 않았지만 충분히 사용할 가치가 있다고 생각되어 utils에 공통 함수를 작성하였다. </p>
<p>(보안상 구체적인 용어를 사용하지 않음)
리뉴얼 이전 고객 -&gt; 1. 프로필이 있을 경우, 프로필 변경 화면 시 랜덤 프로필 제공/ 2. 없을 경우 / 3. 에러데이터 처리를 백엔드에서 처리 불가
리뉴얼 후 고객 -&gt; 4. 랜덤 프로필 제공 / 5. 있을 경우 </p>
<p>공통함수로 분리하고자 했던 Point</p>
<ol>
<li>5가지 상황에 따라 이미지 주소가 리턴되는 함수는 하나로 정리할 것:handleProfileImage</li>
<li>데이터 오류를 체크하는 함수는 따로 분리할 것: isCheckEmptyProfileImgpath</li>
<li>10가지의 프로필 랜덤이미지도 utils에서 가져와 사용하도록 할  것</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Failed to read a named property 'toJSON' from 'Window' 에러]]></title>
            <link>https://velog.io/@dding_yull/Failed-to-read-a-named-property-toJSON-from-Window-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@dding_yull/Failed-to-read-a-named-property-toJSON-from-Window-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Thu, 01 Feb 2024 07:55:46 GMT</pubDate>
            <description><![CDATA[<h2 id="에러-메세지">에러 메세지</h2>
<blockquote>
<p>Failed to read a named property &#39;toJSON&#39; from &#39;Window&#39;: Blocked a frame with origin &quot;<a href="http://localhost:3000/">http://localhost:3000</a>&quot; from accessing a cross-origin frame.</p>
</blockquote>
<h3 id="💭-내가-한-삽질">💭 내가 한 삽질</h3>
<p>cross-origin 부분 때문에 검색하게되면 보안 문제, 서버에서 특정 조건을 추가해줘야 한다 같은 완전 다른 방향으로 검색 결과가 나올 수 있다 나 같은 경우는 관련된 것 같아 보이는 글 조차 별로 없어서 한참 삽질을 했다 아까운 내 시간…. 그래도 덕분에 하나 배웠다</p>
<h3 id="🚌-원인을-찾아간-과정">🚌 원인을 찾아간 과정</h3>
<ul>
<li>다른 사람이 해당 코드를 실행 시켰을 때는 같은 오류가 나오지 않았다.</li>
<li>어떤 화면을 랜더해도 횟수가 다를 뿐 콘솔에 에러가 나타났다.
이 2가지 덕분에 찾을 수 있었다. 서버의 문제가 아니라는 것은 확실했고, 내 세팅의 문제 중 하나일 것이다</li>
</ul>
<h3 id="결론">결론</h3>
<p>내가 사용하고 있는 크롬 환경설정의 문제였다... 
1️⃣ 확인하는 방법은 크롬 좌측 상단의 설정부분에서 아래 사진의 단계를 따라 &#39;확장 프로그램 관리&#39;에 들어가게 되면 이런식의 화면이 나온다.
<img src="https://velog.velcdn.com/images/dding_yull/post/91c2afd3-9728-4901-a30a-8f6e20879bc4/image.png" alt=""></p>
<p>2️⃣ 아래 그림처럼 필요에 따라 설치해놓은 프로그램을 해제하면 해당 에러가 사라진다 
전부 해제했을 때 사라지는게 확실하다면 하나씩 활성화하면서 찾으면 된다. 
내 경우엔 모두 해제 후 해당 에러가 사라졌는데 다시 활성화 + 다시 켜도 그대로 에러는 사라져 있었다.
<img src="https://velog.velcdn.com/images/dding_yull/post/f985af90-21ef-4e88-b587-9a5ff6dae281/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[model에서 타입 정의할 때 types.optional 차이점]]></title>
            <link>https://velog.io/@dding_yull/model%EC%97%90%EC%84%9C-%ED%83%80%EC%9E%85-%EC%A0%95%EC%9D%98%ED%95%A0-%EB%95%8C-types.optional-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@dding_yull/model%EC%97%90%EC%84%9C-%ED%83%80%EC%9E%85-%EC%A0%95%EC%9D%98%ED%95%A0-%EB%95%8C-types.optional-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Wed, 31 Jan 2024 02:51:21 GMT</pubDate>
            <description><![CDATA[<p>이제 막 1년이 넘어 2년차 프론트엔드 개발자가 되었다. 점점 더 궁금한 부분들이 많아 찾아보게되는데 대부분 일을 관리하는 개인 노션에 적다보니 아쉽다는 생각이 들어 블로그에도 적어보려 한다. </p>
<h3 id="오늘-찾아본-소소한-궁금증은-파라미터의-타입을-정의할-때-typesoptional의-역할이다">오늘 찾아본 소소한 궁금증은 파라미터의 타입을 정의할 때 types.optional의 역할이다.</h3>
<p>타입을 정의할 때 같은 string이더라도 어떻게 정의하는지 다르다. </p>
<pre><code class="language-typescript">a: types.maybeNull(types.optional(types.string, &#39;&#39;)),
b: types.optional(types.maybeNull(types.string, &#39;&#39;)),
c: types.maybeNull(types.string)</code></pre>
<p>types.optional는 기본값을 어떻게 세팅하는지에 영향을 준다 </p>
<h3 id="1typesmaybenulltypesoptionaltypesstring-">1.types.maybeNull(types.optional(types.string), &#39;&#39;)</h3>
<ul>
<li>속성이 문자열을 가질 수 있으며, 값이 제공되지 않을 경우 기본값으로 null이 아닌 빈 문자열로 설정
즉, 기본값은 빈 문자열이 아닌 null이 되고, 속성이 문자열을 가질 수 있는 것을 의미한다.</li>
</ul>
<p>javascript</p>
<pre><code class="language-typescript">const MyModel = types.model({
  myProperty: types.maybeNull(types.optional(types.string), &#39;&#39;),
});</code></pre>
<h3 id="2typesoptionaltypesmaybenulltypesstring--기본값이-빈-문자열">2.types.optional(types.maybeNull(types.string), &#39;&#39;) (기본값이 빈 문자열)</h3>
<ul>
<li>속성이 null 또는 문자열을 가질 수 있으며, 값이 제공되지 않을 경우 빈 문자열로 기본값을 설정한다는 것을 의미한다.</li>
</ul>
<pre><code class="language-typescript">const MyModel = types.model({
  myProperty: types.optional(types.maybeNull(types.string), &#39;&#39;),
});
</code></pre>
<h3 id="3-typesmaybenulltypesstring-기본값-없음">3. types.maybeNull(types.string) (기본값 없음)</h3>
<ul>
<li>속성이 null 또는 문자열을 가질 수 있다는 것을 나타냄 그러나 명시적인 기본값이 없으므로, 속성이 null일 경우 디폴트로는 null이 되는 것을 의미한다<pre><code class="language-typescript">const MyModel = types.model({
myProperty: types.maybeNull(types.string),
});</code></pre>
</li>
</ul>
<p>단순한 string타입이더라도 기본값이 null인지, &#39;&#39;인지, 둘다 가능한지 등에 따라 프론트엔드에서는 노출되는 기준에서 버그가 충분히 생길 수 있기 때문에 주의하자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[popstate]]></title>
            <link>https://velog.io/@dding_yull/popstate</link>
            <guid>https://velog.io/@dding_yull/popstate</guid>
            <pubDate>Mon, 16 Oct 2023 04:46:28 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-typescript"> if (typeof window !== &#39;undefined&#39;) {
      window.addEventListener(&#39;popstate&#39;, () =&gt; {
        const params = new URLSearchParams(window.location.search);
        const newTab = params.get(&#39;ctegrySid&#39;);
        if (newTab) setTab(Number(newTab));
      });
 }</code></pre>
<h3 id="popstate">popstate</h3>
<p>이벤트는 웹 브라우저의 히스토리 스택에서 상태가 변경될 때 발생하는 이벤트입니다. 이 이벤트는 주로 브라우저의 뒤로 가기 및 앞으로 가기 버튼을 사용하여 페이지를 탐색하거나 JavaScript를 사용하여 브라우저 히스토리를 조작할 때 유용하게 활용됩니다.</p>
<p>popstate 이벤트 핸들러는 브라우저의 히스토리 스택에서 페이지 전환으로 인한 상태 변경을 감지할 수 있습니다. 예를 들어, 브라우저의 뒤로 가기 버튼을 클릭하면 popstate 이벤트가 트리거되고, 개발자는 이 이벤트를 사용하여 페이지 내용을 업데이트하거나 다른 작업을 수행할 수 있습니다.</p>
<p>위의 코드는 popstate 이벤트를 사용하여 현재 URL의 쿼리 매개변수 중에서 &#39;ctegrySid&#39; 매개변수의 값을 읽어와서 이 값을 &#39;setTab&#39; 함수에 전달하는 예제입니다. 이렇게 하면 페이지가 뒤로 가기 또는 앞으로 가기로 변경될 때 &#39;ctegrySid&#39; 매개변수의 값에 따라 해당 탭을 설정할 수 있습니다.</p>
<p>❗️ tab을 통해 움직이는 동작을 구현하는데 뒤로가기 히스토리에 따라서 tab과 관련된 UI가 함께 수정될 수 있도록 popstate를 활용하여 코드를 작성하였다 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[용어TIL]포팅과 컨버팅]]></title>
            <link>https://velog.io/@dding_yull/%EC%9A%A9%EC%96%B4TIL%ED%8F%AC%ED%8C%85%EA%B3%BC-%EC%BB%A8%EB%B2%84%ED%8C%85</link>
            <guid>https://velog.io/@dding_yull/%EC%9A%A9%EC%96%B4TIL%ED%8F%AC%ED%8C%85%EA%B3%BC-%EC%BB%A8%EB%B2%84%ED%8C%85</guid>
            <pubDate>Wed, 11 Oct 2023 07:47:24 GMT</pubDate>
            <description><![CDATA[<h2 id="포팅-porting-이식">포팅 (Porting, 이식)</h2>
<p>다른 환경에서도 작동할 수 있도록, 즉 aos에서만 되던 기능을 ios에서도 작동할 수 있도록 하는 것<strong>텍스트</strong></p>
<h2 id="컨버팅-converting-바꿔주다-텍스트">컨버팅 (Converting, 바꿔주다) <strong>텍스트</strong></h2>
<ul>
<li>A를 B에서도 이용할 수 있도록 사용하는 언어를 바꿔주는 것, 즉 리액트가 사용된 코드를 뷰로 바꿔주는 것</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AOS]prompt_context:1: command not found: nbsp]]></title>
            <link>https://velog.io/@dding_yull/AOSpromptcontext1-command-not-found-nbsp</link>
            <guid>https://velog.io/@dding_yull/AOSpromptcontext1-command-not-found-nbsp</guid>
            <pubDate>Mon, 28 Aug 2023 08:47:36 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dding_yull/post/c8bf8b6f-9a24-4e24-bbf3-72fcabcc787c/image.jpeg" alt=""></p>
<p>안드로이드 빌드하다가 ctrl+c, enter 뭘해도 나오던 문구... 
사라지지 않아서 nbsp를 잘못 쓴 곳이 있나 찾아봐도 찾지 못하다가 
zshrc를 통해 원인을 찾고 해결해서 기록해둔다 😇
(사실 너무 별거 아니라서 어이없지만... ㅎㅎ )</p>
<blockquote>
<p>🚨 prompt_context:1: command not found: nbsp</p>
</blockquote>
<p>1️⃣ vscode의 터미널 또는 터미너스에서 진행하세요~ (당연하지만 혹시 모를까봐)</p>
<p>2️⃣ 터미널에 vi ~/.zshrc 작성해서 zshrc화면으로 진입</p>
<p>3️⃣ nbsp와 관련된 함수를 삭제 또는 주석처리 : 주석처리는 함수 앞에 ‘#’를 작성하면 됨 </p>
<p>4️⃣ :wq로 zshrc에서 빠져 나온 후 source ~/.zshrc로 변경 된 내용 저장해주면 해결 완료</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React]리액트 서명기능 ]]></title>
            <link>https://velog.io/@dding_yull/React%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%84%9C%EB%AA%85%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@dding_yull/React%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%84%9C%EB%AA%85%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Thu, 24 Aug 2023 00:35:36 GMT</pubDate>
            <description><![CDATA[<p><code>react-signature-canvas</code> 라이브러리를 통해 유저의 서명을 받아야하는 화면을 개발하였다
참고) 보안상 이유도 있다보니 코드는 축약하거나 단순화해서 기록하였다!</p>
<blockquote>
<p><a href="https://www.npmjs.com/package/react-signature-canvas">https://www.npmjs.com/package/react-signature-canvas</a></p>
</blockquote>
<p>사용하기 위해선 설치 후 import해준다
<code>import SignatureCanvas from &#39;react-signature-canvas&#39;;</code></p>
<p><img src="https://velog.velcdn.com/images/dding_yull/post/644d54ea-b652-4182-ae78-b2b4c6627b98/image.png" alt=""></p>
<h2 id="1️⃣-실제-해당-ui가-보여지는-부분엔-ui를-리턴하는-함수로-작성">1️⃣ 실제 해당 ui가 보여지는 부분엔 ui를 리턴하는 함수로 작성</h2>
<pre><code class="language-typescript">&lt;Box&gt;{createSignScreen()}&lt;/Box&gt;</code></pre>
<pre><code class="language-typescript">const createSignScreen = () =&gt; {
 if (
   signImgPath &amp;&amp; resetSign &amp;&amp; loadFile) || 
   signImgPath &amp;&amp; !resetSign &amp;&amp; !uploadFile) ||
   !signImgPath &amp;&amp; resetSign &amp;&amp; uploadFile) ||
   !signImgPath &amp;&amp; !resetSign &amp;&amp; uploadFile) 
    ) {
      return (
        &lt;Box&gt;
          &lt;Box
            component={&#39;img&#39;}
            src={getSignImg()}
          /&gt;
          &lt;IconSignClear
            onClick={() =&gt; {
              setOpenAlert(true);
            }}
          /&gt;
        &lt;/Box&gt;
      );
    } else {
      return (
        &lt;&gt;
          &lt;IconSignClear
            onClick={clearSignature}
          /&gt;
          &lt;Stack sx={{ position: &#39;relative&#39;, cursor: &#39;pointer&#39; }}&gt;
            &lt;Typography
              variant={&#39;Kor_24_b&#39;}
            &gt;
              서명{&#39; &#39;}
            &lt;/Typography&gt;
            &lt;SignatureCanvas
              ref={canvasRef}
              canvasProps={{
                className: &#39;signature-canvas&#39;,
              }}
              onBegin={() =&gt; {}}
              clearOnResize={false}
              backgroundColor={&#39;#FAFAFA&#39;}
              onEnd={handleSignatureEnd}
            /&gt;
          &lt;/Stack&gt;
        &lt;/&gt;
      );
    }
  };</code></pre>
<h2 id="2️⃣-그려진-ui에-클릭이벤트를-포함한-기능들을-함수로-추가">2️⃣ 그려진 UI에 클릭이벤트를 포함한 기능들을 함수로 추가</h2>
<h3 id="✅-getsignimg---유저가-서명한-사인을-이미지로-src-가져오는-함수">✅ getSignImg( ) : 유저가 서명한 사인을 이미지로 src 가져오는 함수</h3>
<ul>
<li>resetSign, uploadFile이 없고 signImgPath, 곧 지금 유저가 사인한 이미지만 있을 경우 그 이미지 주소를 리턴하고</li>
<li>아닐 경우, 이미 uploadFile이 있을 경우엔 그 서명을 src 주소로 리턴</li>
</ul>
<pre><code class="language-jsx">const getSignImg = () =&gt; {
    let src = &#39;&#39;;
    if (signImgPath &amp;&amp; !resetSign &amp;&amp; !uploadFile)){
      src =
        REACT_APP_IMAGE_STORAGE + signImgPath || &#39;/assets/placeholder.svg&#39;;
    } else {
      if (uploadFile) {
        src = uploadFile;
      }
    }
    return src;
  };</code></pre>
<h3 id="✅-clearsignature-">✅ clearSignature( )</h3>
<ul>
<li>위의 코드들에서는 이해를 돕기 위해  store이름 없이 작성하였는데(간략하게 작성하기 위하여) api call 이해를 돕기 위해 이 함수에서만 analysisAgreementStore도 함께 작성하였음  </li>
</ul>
<pre><code class="language-jsx">const clearSignature = () =&gt; {
    canvasRef.current.clear(); //현재 작성한 서명 이미지 삭제 (=clear)
    analysisAgreementStore.setProps({
      resetSign: true,
    }); // 해당 스토어에 resetSign을 true값으로 업데이트
    analysisAgreementStore.analysisAgreement.setProps({
      uploadFile: &#39;&#39;,
    }); // 해당 스토어의 uploadFile을 리셋
    setIsSigned(false);
  };</code></pre>
<h3 id="✅-handlesignatureend-">✅ handleSignatureEnd( )</h3>
<pre><code class="language-jsx">const handleSignatureEnd = () =&gt; {
    analysisAgreementStore.analysisAgreement.setProps({
      uploadFile: canvasRef.current.toDataURL(),
    });
    setIsSigned(true);
  };</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]useCallback과 useMemo 차이점]]></title>
            <link>https://velog.io/@dding_yull/TILuseCallback%EA%B3%BC-useMemo-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@dding_yull/TILuseCallback%EA%B3%BC-useMemo-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Tue, 22 Aug 2023 00:45:00 GMT</pubDate>
            <description><![CDATA[<h2 id="🔖--usecallback"><strong>🔖  useCallback</strong></h2>
<ul>
<li><p>성능 최적화를 위해 사용되는 리액트 훅</p>
</li>
<li><p>React 컴포넌트가 렌더링될 때마다 해당 컴포넌트 내부의 함수들도 새로 생성되는데 컴포넌트 구조가 복잡해지고 하위 컴포넌트들이 불필요하게 리렌더링될 때 성능 문제가 발생할 수 있음</p>
<p>  특히 함수형 컴포넌트 내부에서 정의된 함수들은 렌더링마다 새로 생성되므로, 이 함수들이 자식 컴포넌트들의 props로 전달되면 자식 컴포넌트들 역시 불필요한 리렌더링이 발생할 수 있음</p>
</li>
<li><p><strong><code>useCallback</code></strong> 훅은 이러한 상황에서 함수를 메모이제이션하고 재사용할 수 있도록</p>
<p>  해당 함수는 컴포넌트가 리렌더링될 때마다 새로 생성되지 않고 이전에 생성된 함수를 재사용하여 불필요하 리렌더링을 방지 </p>
</li>
<li><p>예시</p>
<pre><code class="language-jsx">  import React, { useState, useCallback } from &#39;react&#39;;

  const ParentComponent = () =&gt; {
    const [count, setCount] = useState(0);

    // 이 함수는 렌더링마다 새로 생성됩니다.
    // 자식 컴포넌트에게 전달하면 자식 컴포넌트들도 불필요하게 리렌더링될 수 있습니다.
    const handleClick = () =&gt; {
      console.log(&#39;Button clicked!&#39;);
    };

    return (
      &lt;div&gt;
        &lt;p&gt;Count: {count}&lt;/p&gt;
        {/* 자식 컴포넌트에게 handleClick 함수를 전달합니다 */}
        &lt;ChildComponent onClick={handleClick} /&gt;
      &lt;/div&gt;
    );
  }

  function ChildComponent({ onClick }) {
    console.log(&#39;ChildComponent rendered&#39;);
    return &lt;button onClick={onClick}&gt;Click me&lt;/button&gt;;
  }
</code></pre>
<p>  위의 예제에서 <strong><code>handleClick</code></strong> 함수는 렌더마다 새로 생성되기 때문에 <strong><code>ChildComponent</code></strong>가 리렌더링될 때마다 새로운 함수가 전달되어 불필요한 리렌더링이 발생할 수 있습니다. 이 문제를 해결하기 위해 <strong><code>handleClick</code></strong> 함수를 <strong><code>useCallback</code></strong>으로 감싸면:</p>
<pre><code class="language-jsx">  const ParentComponent = () =&gt; {
    // ...

    // 함수를 useCallback으로 감싸서 메모이제이션합니다.
    const handleClick = useCallback(() =&gt; {
      console.log(&#39;Button clicked!&#39;);
    }, []); // 의존성 배열이 빈 배열이므로 함수는 컴포넌트 수준에서 한 번만 생성됩니다.

    // ...
  }
</code></pre>
<p>  이제 <strong><code>handleClick</code></strong> 함수는 컴포넌트가 리렌더링될 때마다 같은 함수를 재사용하므로 자식 컴포넌트의 불필요한 리렌더링이 방지됩니다.</p>
</li>
</ul>
<h2 id="🔖--usememo"><strong>🔖  useMemo</strong></h2>
<ul>
<li>성능 최적화를 위해 사용되는 리액트 훅</li>
<li><strong><code>useMemo</code></strong>는 계산 비용이 높은 함수 호출의 결과 값을 이전에 계산된 결과 값과 비교하여 변경되지 않았다면 이전 결과 값을 사용하는 데 사용</li>
<li>객체가 매번 새로 생성되지 않고 이전에 생성된 객체가 재사용되어 최적화된 성능을 유지하기 위함</li>
</ul>
<h3 id="❓usecallback--usememo-모두-성능-최적화를-위해-리액트에서-사용되는-훅-그럼-뭐가-다르지"><strong>❓<code>useCallback</code> , <code>useMemo</code> 모두 성능 최적화를 위해 리액트에서 사용되는 훅? 그럼 뭐가 다르지?</strong></h3>
<ul>
<li><strong><code>useCallback</code></strong>은 함수를 메모이제이션하여 자식 컴포넌트의 불필요한 리렌더링을 방지하는 데 사용</li>
<li><strong><code>useMemo</code></strong>는 계산 결과 값을 캐시하여 성능을 향상시키는 데 사용</li>
</ul>
<ol>
<li><p><strong>useCallback</strong>:</p>
<ul>
<li><p><strong><code>useCallback</code></strong>은 함수를 메모이제이션하여 자식 컴포넌트에 전달할 때 불필요한 리렌더링을 방지하는 데 사용, 같은 함수가 재생성되지 않음</p>
</li>
<li><p>주로 이벤트 핸들러나 자식 컴포넌트에게 전달되는 함수를 최적화할 때 사용</p>
</li>
<li><p>예시</p>
<pre><code class="language-jsx">  const getDnaCardDetailData = useCallback(
      async (param: number) =&gt; {
        await dnaCardDetailStore.getDnaCardDetail(param);
        if (responseStore.responseInfo.resultCode === &#39;S&#39;) {
          if (dnaCardDetailStore.dnaCardDetail.ctegryList.length &gt; 0) {
                    console.log(&#39;실행&#39;);
          } 
      },
      [dnaCardDetailStore, dnaResultStore],
    );</code></pre>
</li>
</ul>
</li>
<li><p><strong>useMemo</strong>:</p>
<ul>
<li><strong><code>useMemo</code></strong>는 계산 비용이 높은 함수의 반환 값을 메모이제이션하는 데 사용</li>
<li>계산 비용이 높은 함수의 결과 값을 캐시하여 동일한 인풋에 대한 계산을 재실행하지 않음</li>
<li>주로 계산 결과를 캐시하고 해당 결과를 여러 번 사용해야 할 때 사용</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AOS]빌드 성공했는데 코드반영 안될 때]]></title>
            <link>https://velog.io/@dding_yull/AOS%EB%B9%8C%EB%93%9C-%EC%84%B1%EA%B3%B5%ED%96%88%EB%8A%94%EB%8D%B0-%EC%BD%94%EB%93%9C%EB%B0%98%EC%98%81-%EC%95%88%EB%90%A0-%EB%95%8C</link>
            <guid>https://velog.io/@dding_yull/AOS%EB%B9%8C%EB%93%9C-%EC%84%B1%EA%B3%B5%ED%96%88%EB%8A%94%EB%8D%B0-%EC%BD%94%EB%93%9C%EB%B0%98%EC%98%81-%EC%95%88%EB%90%A0-%EB%95%8C</guid>
            <pubDate>Fri, 18 Aug 2023 08:02:30 GMT</pubDate>
            <description><![CDATA[<p>요청받은 버그를 해결하기 위해 안드로이드 빌드를 해서 dev에서 수정하고 STG에서 확인해서 최종 완료 처리를 해야했다. 
😅 aos 개발 경험이 별로 없어 걱정 되는 마음으로 빌드를 진행했다. </p>
<h3 style='color:#7ad7be'>[내가 빌드 진행한 순서]</h3>

<h3 id="1️⃣-web말고-app-vscode-파일-오픈">1️⃣ web말고 app vscode 파일 오픈!</h3>
<h3 id="2️⃣-envdevelopment파일에-가서-바로보는-url-주소-수정">2️⃣ &#39;.env.development&#39;파일에 가서 바로보는 URL 주소 수정</h3>
<ul>
<li>env파일의 이름은 다를 수 있으나 난 로컬에서 확인할 때는 .env.development에서 주소를 수정해서 본다.</li>
<li>만약 서버도 다른 곳을 사용하고 있다면 해당하는 주소로 수정해줘야한다. <pre><code class="language-typescript">REACT_WEB_URL=http://192.168.20.27:3000 (필수)
</code></pre>
</li>
</ul>
<p>REACT_APP_API_URL=<a href="https://api.%EB%8F%84%EB%A9%94%EC%9D%B8%EC%9D%B4%EB%A6%84.net">https://api.도메인이름.net</a> (바라보는 서버가 다를 경우에만 수정)</p>
<pre><code>### 3️⃣ vscode 터미널 | 터미널 | 터미너스 평소 사용하던 어떤것을 사용해도 괜찮다. 이 중 1곳에 aos 빌드 명령어를 작성한다. 
- package.json에 명령어를 선언하고
- vscode 터미널에 명령어를 작성하여 빌드를 했다
```typescript
//package.json에 명령어 선언 (예시)


&quot;scripts&quot;: {
    &quot;android&quot;: &quot;react-native run-android &amp;&amp; react-native start&quot;,
    &quot;aos:start&quot;: &quot;env-cmd -f .env.development react-native run-android --variant=debug&quot;,
  },

//터미널에 작성하는 명령어
aos:start</code></pre><p><img src="https://velog.velcdn.com/images/dding_yull/post/de24252c-2456-4ea7-a07c-414f3d74e316/image.png" alt=""></p>
<h3 id="원래는-이렇게-명령어를-작성하면-빌드가-되어야한다-그러나">원래는 이렇게 명령어를 작성하면 빌드가 되어야한다! 그러나</h3>
<h3 style='color:#7ad7be'>➡️ 발생한 현상</h3>

<p>: 원래는 이렇게하면 빌드가 되어야하는데..! 안드로이드 폰에서는 빌드가 되고 앱이 꺼졌다가 다시 꺼지지만 Web코드를 아무리 바꿔봤자 변화가 없었다... </p>
<h3 style='color:#7ad7be'>☑️ 해결한 방법</h3>

<p>vscode가 아닌 이전 빌드되어져있던 또는 yarn start되어있던 터미너스에 가서 </p>
<ul>
<li>ctrl + c로 한번 끊고 </li>
<li>다시 yarn start로 시작해 줘야한다 </li>
<li>그래도 안될 경우 &#39;r&#39;을 눌러 reload를 했더니 다시 번들링이 되면서 원하는 주소를 바라봄 </li>
<li>최종적으로 터미너스에 나오는 주소가 내가 원하는 주소가 맞는지 확인하면 더 정확하다
<img src="https://velog.velcdn.com/images/dding_yull/post/c36b1774-2fcc-4611-99ee-f34da19c42f6/image.png" alt=""></li>
</ul>
<p>이렇게 단순한데 분명 주소도 바꿔놨고 빌드도 성공으로 뜨는데 로컬에서 수정한 코드가 반영이 안되서 grdle도 다시하고 캐시도 지워보고 다양하게 시간을 쬐꼼 날렸다 ㅎㅎ 
그래도 이번엔 금방 혹시나?하는 의심 덕분에 이유를 알게되어 덕분에 칼퇴를 해야겠다 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[좋아요_handleLike]]></title>
            <link>https://velog.io/@dding_yull/%EC%A2%8B%EC%95%84%EC%9A%94handleLike</link>
            <guid>https://velog.io/@dding_yull/%EC%A2%8B%EC%95%84%EC%9A%94handleLike</guid>
            <pubDate>Thu, 17 Aug 2023 00:35:17 GMT</pubDate>
            <description><![CDATA[<h3 id="좋아요-기능-구현-로직"><strong>좋아요 기능 구현 로직</strong></h3>
<ol>
<li><p>로그인 여부 확인 </p>
<ul>
<li><p>isAuthenticated</p>
<pre><code class="language-jsx">  const { isAuthenticated } = useAuthContext(); //로그인 여부 체크

  if (isAuthenticated) {
      ...코드
  } else {
    setAlertMessage(&#39;로그인 후 이용 가능합니다.&#39;);
    setAlertOpen(true);
  }</code></pre>
</li>
</ul>
</li>
<li><p>emotion code에 따라 좋아요 여부 등록</p>
<ul>
<li><p>emotion code  : 400101 (좋아요), 400102 (싫어요)</p>
</li>
<li><p>myEmotionCd.code가 400101일 경우 좋아요 삭제</p>
</li>
<li><p>myEmotionCd.code가 400101이 아닐 경우 좋아요 추가</p>
<pre><code class="language-jsx">const emotionCd = mbtiStore.mbti.myEmotionCd?.code === 400101 ? 400102 : 400101;
if (mbtiStore.mbti.myEmotionCd?.code === 400101) {
await mbtiStore.removeLike(mbtiStore.mbti.mbtiSid);
} else {
await mbtiStore.addLike(mbtiStore.mbti.mbtiSid, emotionCd);
}     </code></pre>
</li>
</ul>
</li>
</ol>
<ol start="3">
<li>상태에 따라 2번이 완료 된 후 해당 컨텐츠의 정보를 다시 불러와서 state에 담아주기 </li>
</ol>
<pre><code class="language-jsx">mbtiStore.get(Number(id)).then(() =&gt;
  setContent(mbtiStore.mbti)
);</code></pre>
<h3 id="-좋아요-클릭이벤트-코드">*** 좋아요 클릭이벤트 코드</h3>
<pre><code class="language-tsx">const handleLike = async () =&gt; {
    if (isAuthenticated) {
      const emotionCd = mbtiStore.mbti.myEmotionCd?.code === 400101 ? 400102 : 400101;
      if (mbtiStore.mbti.myEmotionCd?.code === 400101) {
        await mbtiStore.removeLike(mbtiStore.mbti.mbtiSid);
      } else {
        await mbtiStore.addLike(mbtiStore.mbti.mbtiSid, emotionCd);
      }
      mbtiStore.get(Number(id)).then(() =&gt;
        setContent(mbtiStore.mbti)
      );
    } else {
      setAlertMessage(&#39;로그인 후 이용 가능합니다.&#39;);
      setAlertOpen(true);
    }
  };</code></pre>
<h3 id="좋아요-ui"><strong>좋아요 UI</strong></h3>
<pre><code class="language-typescript">&lt;Box
    id=&quot;mbtiLike&quot;
  sx={{
    ...likeStyle,
    background: mbtiStore.mbti.myEmotionCd?.code === 400101 ? &#39;&#39; : &#39;#FAFAFA&#39;,
    textAlign: &#39;center&#39;,
    cursor: &#39;pointer&#39;,
    border: mbtiStore.mbti.myEmotionCd?.code === 400101 ? &#39;1px solid #eeeeee&#39; : &#39;&#39;,
  }}
   onClick={handleLike}
&gt;
  {mbtiStore.mbti.myEmotionCd?.code === 400101 ? (
   &lt;&gt;
    &lt;Iconify icon={&#39;ph:heart-straight-fill&#39;} width={25} color={theme.palette.primary.main}/&gt;
    &lt;Typography variant={&#39;Kor_12_r&#39;} sx={{ color: theme.palette.primary.main }}&gt;
     {numberComma(content.likeCnt || 0)}
    &lt;/Typography&gt;
    &lt;/&gt;
    ) : (
   &lt;&gt;
     &lt;Iconify icon={&#39;ph:heart-straight-bold&#39;} width={25} color={&#39;#9DA0A5&#39;} /&gt;
     &lt;Typography variant={&#39;Kor_12_r&#39;} sx={{ color: &#39;#9DA0A5&#39; }}&gt;
      {numberComma(content.likeCnt)}
     &lt;/Typography&gt;
   &lt;/&gt;
  )}
&lt;/Box&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Review 별표시]]></title>
            <link>https://velog.io/@dding_yull/Review-%EB%B3%84%ED%91%9C%EC%8B%9C</link>
            <guid>https://velog.io/@dding_yull/Review-%EB%B3%84%ED%91%9C%EC%8B%9C</guid>
            <pubDate>Wed, 16 Aug 2023 02:14:20 GMT</pubDate>
            <description><![CDATA[<h2 id="🔖--별을-선택했을-시-선택한-별을-포함하여-앞쪽의-별까지-모두-선택되도록-하는-함수">🔖  별을 선택했을 시 선택한 별을 포함하여 앞쪽의 별까지 모두 선택되도록 하는 함수</h2>
<p>(리뷰작성 시 총점을 표시를 5개의 별로 표시할 경우 )</p>
<p><img src="https://velog.velcdn.com/images/dding_yull/post/7a4e643f-620c-4675-bd55-1d6e91c949df/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dding_yull/post/48ee51a3-7b88-4b82-b398-288725b94ec4/image.png" alt=""></p>
<pre><code class="language-typescript">const handleClickStar = (index: number) =&gt; {
    const starStates = [...clickStar];

    for (let i = 0; i &lt; starStates.length; i++) {
      starStates[i] = i &lt;= index ? true : false;
    }
    setClickStar(starStates);
    setEvalScore(index + 1);
  };</code></pre>
<ul>
<li>왼쪽인 <strong><code>i &lt;= index</code></strong>는 <strong><code>i</code></strong>가 <strong><code>index</code></strong>보다 작거나 같은지를 평가하는 조건</li>
<li><code>starStates</code>** 배열의 인덱스가 <strong><code>index</code></strong>보다 작거나 같은 경우에 대해 <strong><code>true</code></strong>를 할당하고, 그 외의 경우에는 <strong><code>false</code></strong>를 할당하는 로직을 나타냅니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[useCallback과 useMemo의 차이점은?]]></title>
            <link>https://velog.io/@dding_yull/useCallback%EA%B3%BC-useMemo%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80</link>
            <guid>https://velog.io/@dding_yull/useCallback%EA%B3%BC-useMemo%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80</guid>
            <pubDate>Fri, 11 Aug 2023 05:18:06 GMT</pubDate>
            <description><![CDATA[<h2 id="🔖--usecallback"><strong>🔖  useCallback</strong></h2>
<p><em>- 성능 최적화를 위해 사용되는 리액트 훅</em></p>
<ul>
<li><p>React 컴포넌트가 렌더링될 때마다 해당 컴포넌트 내부의 함수들도 새로 생성되는데 컴포넌트 구조가 복잡해지고 하위 컴포넌트들이 불필요하게 리렌더링될 때 성능 문제가 발생할 수 있음</p>
<p>  특히 함수형 컴포넌트 내부에서 정의된 함수들은 렌더링마다 새로 생성되므로, 이 함수들이 자식 컴포넌트들의 props로 전달되면 자식 컴포넌트들 역시 불필요한 리렌더링이 발생할 수 있음</p>
</li>
<li><p><strong><code>useCallback</code></strong> 훅은 이러한 상황에서 함수를 메모이제이션하고 재사용할 수 있도록</p>
<p>  해당 함수는 컴포넌트가 리렌더링될 때마다 새로 생성되지 않고 이전에 생성된 함수를 재사용하여 불필요하 리렌더링을 방지 </p>
</li>
<li><p>예시</p>
<pre><code class="language-jsx">  import React, { useState, useCallback } from &#39;react&#39;;

  const ParentComponent = () =&gt; {
    const [count, setCount] = useState(0);

    // 이 함수는 렌더링마다 새로 생성됩니다.
    // 자식 컴포넌트에게 전달하면 자식 컴포넌트들도 불필요하게 리렌더링될 수 있습니다.
    const handleClick = () =&gt; {
      console.log(&#39;Button clicked!&#39;);
    };

    return (
      &lt;div&gt;
        &lt;p&gt;Count: {count}&lt;/p&gt;
        {/* 자식 컴포넌트에게 handleClick 함수를 전달합니다 */}
        &lt;ChildComponent onClick={handleClick} /&gt;
      &lt;/div&gt;
    );
  }

  function ChildComponent({ onClick }) {
    console.log(&#39;ChildComponent rendered&#39;);
    return &lt;button onClick={onClick}&gt;Click me&lt;/button&gt;;
  }
</code></pre>
<p>  위의 예제에서 <strong><code>handleClick</code></strong> 함수는 렌더마다 새로 생성되기 때문에 <strong><code>ChildComponent</code></strong>가 리렌더링될 때마다 새로운 함수가 전달되어 불필요한 리렌더링이 발생할 수 있습니다. 이 문제를 해결하기 위해 <strong><code>handleClick</code></strong> 함수를 <strong><code>useCallback</code></strong>으로 감싸면:</p>
<pre><code class="language-jsx">  const ParentComponent = () =&gt; {
    // ...

    // 함수를 useCallback으로 감싸서 메모이제이션합니다.
    const handleClick = useCallback(() =&gt; {
      console.log(&#39;Button clicked!&#39;);
    }, []); // 의존성 배열이 빈 배열이므로 함수는 컴포넌트 수준에서 한 번만 생성됩니다.

    // ...
  }
</code></pre>
<p>  이제 <strong><code>handleClick</code></strong> 함수는 컴포넌트가 리렌더링될 때마다 같은 함수를 재사용하므로 자식 컴포넌트의 불필요한 리렌더링이 방지</p>
</li>
</ul>
<h2 id="🔖--usememo"><strong>🔖  useMemo</strong></h2>
<p><em>- 성능 최적화를 위해 사용되는 리액트 훅</em></p>
<ul>
<li><strong><code>useMemo</code></strong>는 계산 비용이 높은 함수 호출의 결과 값을 이전에 계산된 결과 값과 비교하여 변경되지 않았다면 이전 결과 값을 사용하는 데 사용</li>
<li>객체가 매번 새로 생성되지 않고 이전에 생성된 객체가 재사용되어 최적화된 성능을 유지하기 위함</li>
</ul>
<h2 id="❓usecallback--usememo-모두-성능-최적화를-위해-리액트에서-사용되는-훅-그럼-뭐가-다르지"><strong>❓<code>useCallback</code> , <code>useMemo</code> 모두 성능 최적화를 위해 리액트에서 사용되는 훅? 그럼 뭐가 다르지?</strong></h2>
<ul>
<li><strong><code>useCallback</code></strong>은 함수를 메모이제이션하여 자식 컴포넌트의 불필요한 리렌더링을 방지하는 데 사용</li>
<li><strong><code>useMemo</code></strong>는 계산 결과 값을 캐시하여 성능을 향상시키는 데 사용</li>
</ul>
<ol>
<li><p><strong>useCallback</strong>:</p>
<ul>
<li><p><strong><code>useCallback</code></strong>은 함수를 메모이제이션하여 자식 컴포넌트에 전달할 때 불필요한 리렌더링을 방지하는 데 사용, 같은 함수가 재생성되지 않음</p>
</li>
<li><p>주로 이벤트 핸들러나 자식 컴포넌트에게 전달되는 함수를 최적화할 때 사용</p>
</li>
<li><p>예시</p>
<pre><code class="language-jsx">  const getDnaCardDetailData = useCallback(
      async (param: number) =&gt; {
        await dnaCardDetailStore.getDnaCardDetail(param);
        if (responseStore.responseInfo.resultCode === &#39;S&#39;) {
          if (dnaCardDetailStore.dnaCardDetail.ctegryList.length &gt; 0) {
                    console.log(&#39;실행&#39;);
          } 
      },
      [dnaCardDetailStore, dnaResultStore],
    );</code></pre>
</li>
</ul>
</li>
<li><p><strong>useMemo</strong>:</p>
<ul>
<li><strong><code>useMemo</code></strong>는 계산 비용이 높은 함수의 반환 값을 메모이제이션하는 데 사용</li>
<li>계산 비용이 높은 함수의 결과 값을 캐시하여 동일한 인풋에 대한 계산을 재실행하지 않음</li>
<li>주로 계산 결과를 캐시하고 해당 결과를 여러 번 사용해야 할 때 사용</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[cheerio]]></title>
            <link>https://velog.io/@dding_yull/cheerio</link>
            <guid>https://velog.io/@dding_yull/cheerio</guid>
            <pubDate>Wed, 09 Aug 2023 02:50:17 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.npmjs.com/package/cheerio">https://www.npmjs.com/package/cheerio</a></p>
<p>cheerio는 HTML에서 document.querySelector()처럼 HTML 요소를 골라낼 수 있는 라이브러리이다. </p>
<pre><code class="language-typescript">&lt;ul id=&quot;alphabet&quot;&gt;
  &lt;li class=&quot;a&quot;&gt;A&lt;/li&gt;
  &lt;li class=&quot;b&quot;&gt;B&lt;/li&gt;
  &lt;li class=&quot;c&quot;&gt;C&lt;/li&gt;
&lt;/ul&gt;</code></pre>
<pre><code class="language-typescript">const cheerio = require(&#39;cheerio&#39;);
const $ = cheerio.load(&#39;&lt;ul id=&quot;alphabet&quot;&gt;...&lt;/ul&gt;&#39;);

$.html(); //HTML Load
//=&gt; &lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;ul id=&quot;fruits&quot;&gt;...&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;

$(&#39;.a&#39;, &#39;#alphabet&#39;).text();
//=&gt; A

$(&#39;ul .c&#39;).attr(&#39;class&#39;);
//=&gt; C

$(&#39;li[class=b]&#39;).html();
//=&gt; B</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React]drag와 onClick 이벤트 제어하기]]></title>
            <link>https://velog.io/@dding_yull/Reactdrag%EC%99%80-onClick-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%9C%EC%96%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dding_yull/Reactdrag%EC%99%80-onClick-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%9C%EC%96%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 07 Aug 2023 05:23:39 GMT</pubDate>
            <description><![CDATA[<p>drag와 onClick 기능이 함께 있을 경우 drag를 하나의 스테이트에 따로 관리하여 제어하였다</p>
<pre><code class="language-typescript">//drag 관리하는 state
const [isDragging, setIsDragging] = useState&lt;boolean&gt;(false);

  const beforeChange = () =&gt; {
    setIsDragging(true)
  }

  const afterChange = () =&gt; {
    setIsDragging(false)
  }


//drag 기능의 carousel
  const carouselSettings = {
    dots: true,
    arrows: false,
    centerMode: false,
    swipeToSlide: true,
    adaptiveHeight: true,
    beforeChange: beforeChange,
    afterChange: afterChange,
  };

//return될 UI
&lt;SliderStyle&gt;
 &lt;Carousel ref={carouselRef} {...carouselSettings}&gt;
  {SampleCardList.map((card: any, index: number) =&gt; {
    return (
      &lt;Image
        key={index}
        src={card.imgPath}
        ratio={&#39;1/1&#39;}
        effect={&#39;opacity&#39;}
        onClick={(e :any) =&gt; {
        !isDragging &amp;&amp; navigate(card.path);
        }}
        sx={{ width: &#39;100%&#39;, height: &#39;100%&#39;, cursor: &#39;pointer&#39; }}
       /&gt;
      );
    })}
 &lt;/Carousel&gt;
&lt;/SliderStyle&gt;


// SliderStyle 따로 스타일링 (혹시 SliderStyle이 뭔지 헷갈릴까봐 참고용으로 첨부)
const SliderStyle = styled(&#39;div&#39;)({
  &#39;.slick-dots li&#39;: {
    margin: 0,
    marginRight: pxToRem(4),
  },
  &#39;.slick-dots li button:before&#39;: {
    opacity: 100,
    color: &#39;#EEEEEE&#39;,
  },
  &#39;.slick-dots li.slick-active button:before&#39;: {
    opacity: 100,
    color: &#39;#FF7F3F&#39;,
  },
  &#39;.slick-dots li, .slick-dots li button, .slick-dots li button:before&#39;: {
    width: pxToRem(8),
    height: pxToRem(8),
  },
});</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[diabled상태의 버튼 시 레이어 아래요소 클릭 막는법]]></title>
            <link>https://velog.io/@dding_yull/diabled%EC%83%81%ED%83%9C%EC%9D%98-%EB%B2%84%ED%8A%BC-%EC%8B%9C-%EB%A0%88%EC%9D%B4%EC%96%B4-%EC%95%84%EB%9E%98%EC%9A%94%EC%86%8C-%ED%81%B4%EB%A6%AD-%EB%A7%89%EB%8A%94%EB%B2%95</link>
            <guid>https://velog.io/@dding_yull/diabled%EC%83%81%ED%83%9C%EC%9D%98-%EB%B2%84%ED%8A%BC-%EC%8B%9C-%EB%A0%88%EC%9D%B4%EC%96%B4-%EC%95%84%EB%9E%98%EC%9A%94%EC%86%8C-%ED%81%B4%EB%A6%AD-%EB%A7%89%EB%8A%94%EB%B2%95</guid>
            <pubDate>Mon, 24 Jul 2023 00:55:48 GMT</pubDate>
            <description><![CDATA[<h3 id="버그">버그</h3>
<p>버튼 disable처리된 상태일 때 클릭 이벤트 버그가 있어 수정했다.
버튼이 항상 bottom:0으로 하단에 있다보니 
버튼이 diabled되어있는 상태에서 스크롤을 내리다가 클릭이벤트가 있는 요소와 겹칠 경우 
<strong>겹쳐있는 아래 요소의 클릭이벤트가 진행되는 문제</strong>였다. </p>
<h3 id="해결-방법">해결 방법</h3>
<p>disabled 되어있을 때 <strong>onClick을 undefined인 코드를 추가</strong>해 이벤트가 없도록 수정하였다. </p>
<h3 id="새롭게-알게된-방법--pointer-events">새롭게 알게된 방법 : pointer-events</h3>
<p>수정하면서 새롭게 알게된 CSS 속성 중에서 강제로 이벤트를 제어 할 수 있는 방법이 있어 기록한다. </p>
<pre><code class="language-css">pointer-events: auto;
pointer-events: none;
pointer-events: visiblePainted; 
pointer-events: visibleFill;    
pointer-events: visibleStroke;  
pointer-events: visible;        
pointer-events: painted;        
pointer-events: fill;         
pointer-events: stroke;       
pointer-events: all; </code></pre>
<ul>
<li><p>pointer-events: none
➡️ 요소가 포인터 이벤트의 대상이 되지 않는다. 그러나 해당 요소의 자손이 다른 pointer-events 값을 지정한 경우, 그 자손은 대상이 될 수 있다.</p>
</li>
<li><p>pointer-events: auto
➡️ 요소가 pointer-events 속성을 지정하지 않은 것처럼 행동한다.</p>
</li>
<li><p>적용 사례
➡️ 공통으로 사용하고 있는 버튼.tsx파일에 pointerEvents속성을 추가하여 추가될 수 있도록 적용할 수 있다
<img src="https://velog.velcdn.com/images/dding_yull/post/2a2708b9-5732-4801-a671-eb95ac4e20fe/image.png" alt=""></p>
</li>
</ul>
<p>참고: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events">https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events</a></p>
]]></description>
        </item>
    </channel>
</rss>