<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>rara.log</title>
        <link>https://velog.io/</link>
        <description>코드치는 개발자가 아닌 생각하는 개발자! </description>
        <lastBuildDate>Sun, 09 Apr 2023 14:25:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>rara.log</title>
            <url>https://velog.velcdn.com/cloudflare/rara-record/ba74da32-32b5-4d23-8874-2808be7a4387/1592464912077-5.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. rara.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/rara-record" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[데브로드 9주차]]></title>
            <link>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-9%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-9%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sun, 09 Apr 2023 14:25:18 GMT</pubDate>
            <description><![CDATA[<h2 id="한-주-돌아보기">한 주 돌아보기</h2>
<p>데브로드 과제나 수업이 좀 밀려있는 상황이었다.
노트도 정리 못한 부분이 있어서
어제 오늘, 지난 수업까지 훑어보고 노트도 추가해보았다.
이번주 과제를 해보고 있는데, 테스트코드 연습 및 여태까지 배운 내용을
정리하기 딱 좋은 것 같다. 아주 좋아.
시간이 좀 걸리더라도, 데브로드가 끝나고 나서라도 혼자 다 해봐야겠다.</p>
<p>퇴근 후 공부 보다는 회사를 한두시간 일찍 출근해서 수업을 듣는 편인데,
내일은 모각코 출석을 해야할 것 같아서.. 
오늘은 여기까지하구 내일부터는 더 열심히 달려야.
회사 서비스 오픈도 이제 3주차니까, 이제 숨 좀 쉴 수 있으려나?..ㅎ;</p>
<h2 id="앞으로의-방향">앞으로의 방향</h2>
<p>수업이 늘 생각할 거리를 많이 만들어주어서, 
회사 프로젝트에서도 적용해 보고 싶지만 사실상 도입이 어려운 부분이 있다.
해서 늘 아쉬운 마음이 크기도 하고, 끝나고 혼자 뭐라도 만들어봐야지 했는데!
마침 백엔드하는 친구가 이직을 준비한다는 얘기를 듣게 되어서
곧 친구 이직용 포폴을 도와줄 겸,
작은 프로젝트 하나를 만들 볼 예정이다.
배운 내용을 토대로 진행하면서, 리액트 쿼리를 한번 도입해보면 어떨까 싶다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데브로드 6주차]]></title>
            <link>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-6%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-6%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sun, 12 Mar 2023 11:01:47 GMT</pubDate>
            <description><![CDATA[<p>이번주는 관심사의 분리가 가장 중요한 키워드였던 것 같다.
인터렉션만 담당하는 부분과 UI만 렌더링 하는 컴포넌트가 각각 분리되고,
외부에서 상태를 관리하여 각각 독립적으로 움직이는 건 알겠다.</p>
<p>그런데 뭔가 아직 체계가 안잡혀서 너무 혼란스럽다..
하는 일은 같은데, 값은 공유하지 않는 
UI가 같은 컴포넌트를 독립적으로 만들 경우에는
그냥 useState로 관리를 하면 되는건가 ㅠㅠ
그러면 공통 컴포넌트로 만들기가 너무 힘들지 않나.. 아</p>
<p>좀 답답하기는 한데.. 
일단은 이번주차의 키워드라도 최대한 습득 해보고 있다.
과제도 제출하기는 했는데.. 안보고 혼자 제대로 만들어 봐야겠음..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데브로드 4주차]]></title>
            <link>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-4%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-4%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sun, 26 Feb 2023 13:49:57 GMT</pubDate>
            <description><![CDATA[<p>반성이 많은 한 주다.
부지런히 움직이기만 한다고 더 많은 것을 흡수하는 건 아닌 것 같다.
컨디션 관리도 못했고, 아침 공부도 효율적이게 쓰지는 못했다.</p>
<p>이번주 수업을 듣고 useEffect와 state에 대해 많은 생각을 했다.
아직도 와닿지 않는 것들이 많다. 리액트 외부에서의 일을 처리한다는 건
무슨 소린지는 아직 잘 모르겠다.
명시적인 함수 호출, 상태를 잘 골라내는 법, 등에 대해 많이 찾아보고 읽어봤는데 막상 노트에 정리하려면 어디서부터 어떻게 적어야 할지를 모르겠다.
이것 또한 아직 내 것이 아니어서 그렇겠지.
머릿 속에 둥둥 떠다니는 지식의 조각들을 잘 조합해보고 싶은데 쉽지 않다.</p>
<p>과제는 지금의 나에게는 조금 어려웠다.
장바구니에 담긴 아이템들을, 삭제할 때 id값으로 받아와서
중복인 것들 모두가 지워지는 현상과, 
영수증부터 조금 해매서 풀이를 봤는데 배울 게 많았다ㅎ
일단 컴포넌트를 재사용하지 않고 있었는데 그 부분부터..
cart로직 아이템을 삭제할 때, id가 아닌 index를 사용하면 되는거구나ㅎ...
정말 갈 길이 멀다.</p>
<ol>
<li><p>회사에서는 계속 웹소켓을 써서, http통신을 너무 오랜만에 했더니
fetchAPI를 어떻게 썼더라? 하면서 머리가 하얘지기도 했다..</p>
</li>
<li><p>useLocalStorage는 지금 프로젝트에서도 적용해서
유용하게 쓸 수 있을 것 같은 생각이 든다. 아주 편하고 좋아..</p>
</li>
<li><p>children을 받을때, type에 추가하지 않고 기존 타입에서 확장해서 쓰면 유용하다는 것을 배웠다. 늘 </p>
<pre><code class="language-typescript">type Test = {
 test: string
 children: React.ReactNode
}
</code></pre>
</li>
</ol>
<pre><code>이렇게만 썼는데,

```typescript 
type Test = {
  test: string
} &amp; HTMLAttributes&lt;Element&gt;;
</code></pre><p>이런 방법이 있다니.</p>
<ol start="4">
<li>카트 로직과 영수증 로직은, 올라온 과제풀이를 다시보고 내일 아침에 체크 할 생각이다. 지금 나에게 부족한 점을 매꿔줄 것 같앙.
재밌는 과제였당</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[데브로드 3주차]]></title>
            <link>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%83%9D%EC%A1%B4%EC%BD%94%EC%8A%A4-3%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%83%9D%EC%A1%B4%EC%BD%94%EC%8A%A4-3%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sun, 19 Feb 2023 10:32:17 GMT</pubDate>
            <description><![CDATA[<p>진짜 이번주는 너무 바빴다. 스터디 발표 준비로 PPT도 만들어야 했고 
회사 프로젝트도 생각할 게 많았고, 데브로드 강의도 듣고
노트정리도 하고, 과제도 하고 너어무 바빠.
그렇다고 허투로 하고 싶지는 않은데, 
어떻게 하면 시간을 더 효율적으로 쓸 수 있을까. </p>
<p>일을 시작한지 오늘로 딱 5개월..! 그동안 일하면서 컴포넌트 설계에 대해
고민이 많았었는데, 이번 강의를 듣고 현재 나에게 부족한 부분이 어떤건지 조금 더 분명하게 알게 된 것 같다. 컴포넌트를 구성하고, 분리하는 작업과 
상태에 대한 이해도가 부족하다고 늘 생각했는데, 그 체계가 조금 더 뚜렷하게 잡힐 수 있었다. 물론 아직 부족한 게 많지만; 와닿지 않는 것도 많고.. </p>
<p>공부가 더 필요하다고 느낀 부분은
SSOT 
SRP
DRY 원칙 
그리고 아직 상태에 대한 이해도 ㅠㅠ</p>
<p>과제 진행도 재밌게 했다. 다시 보니 보완했으면 좋겠는 부분은 있지만.. 
맘먹고 아주 잘게 잘게 나눠봐야지 했는데, 너무 불필요하게 많이 나눈 것 같기도하고 ㅎㅎ; 중간점을 잘 찾아봐야겠다.</p>
<p>다 해놓고 보니 과제풀이가 올라와있어서
비교해보며 보완할 점을 찾아야겠다 ㅎ</p>
<p>내일은 회사를 더 일찍가야겠당..
출근하면서, 위에 대한 레퍼런스들 읽어보고 도착해서 노트 정리를 해야지!
오늘 과제에 대한 테스트 코드도 작성 해보고 싶은데, 
이건 내일 퇴근하고 진행하구!
화요일부터 다음주 강의 올라온 걸 들어야겠다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데브로드 2주차]]></title>
            <link>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%83%9D%EC%A1%B4%EC%BD%94%EC%8A%A4-2%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%83%9D%EC%A1%B4%EC%BD%94%EC%8A%A4-2%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sun, 12 Feb 2023 07:51:12 GMT</pubDate>
            <description><![CDATA[<h2 id="jsx없이-리액트-만들기">JSX없이 리액트 만들기.</h2>
<p>jsx가 리액트에서만 쓰는 문법인 줄로만 알고 있었다.
공부하면서 브라우저 랜더링에 관해서부터 한번 더 살펴보았다.</p>
<p>브라우저는 DOM을 통해 어플리케이션을 렌더링하고,
html을 파싱해서 만든 엘리먼트를 사용한다.
엘리먼트는 문서를 구성하는 기본 요소가 되고,
document.createElement()함수로 만들 수 있다.</p>
<p>리액트를 이루는 최소단위는 리액트 엘리먼트이고, 
이것은 브라우저 DOM 엘리먼트와 달리 일반 객체이다.</p>
<p>React.createElement()를 써서, 리액트 엘리먼트 트리를 갱신하는데 쓸 수 있다.
JSX를 사용하면 React.createElement() 호출을 반복해야 하는 불편을 해소해준다. 필수적이지는 않지만 리액트에 잘 어울린다.
즉, JSX는 createElement 함수에 대한 문법적 설탕(Syntactic sugar)일뿐이다.</p>
<p>이렇게 매번 작은 React Element 트리, VDOM 트리를 만든다.
VDOM은 브라우저 API위에 있는 자바스크립트 라이브러리에서 구현되는 개념으로 
메모리상에 있는 가상적인 DOM이다. 
VDOM을 쓰는 이유는, 빨라서라기 보다는 충분히 빠르기도 하지만 유지보수를 가능하게 하기 때문에 사용한다.</p>
<p>리액트 엘리먼트를 가상 돔으로 만들어 실제 돔에 반영해주는 역할은 React.DOM이 해주고, 이 과정은 재조정이라고 한다.
개발자는 key prop을 통해 재조정을 조절할 수 있다.</p>
<h2 id="느낀점">느낀점</h2>
<p>기초가 가장 중요하다는 것을 느낀다. 
머릿속에 그림이 조금 더 명확하게 그려지는 느낌이다.
이렇게 차곡차곡 쌓아서 지금보다 리액트를 더욱 리액트스럽게 다룰 수 있었으면 좋겠다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데브로드 1주차]]></title>
            <link>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%83%9D%EC%A1%B4%EC%BD%94%EC%8A%A4-1%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@rara-record/%EB%8D%B0%EB%B8%8C%EB%A1%9C%EB%93%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%83%9D%EC%A1%B4%EC%BD%94%EC%8A%A4-1%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sun, 05 Feb 2023 07:04:22 GMT</pubDate>
            <description><![CDATA[<h2 id="배운점">배운점</h2>
<ol>
<li><p>프로젝트 개발 환경 세팅 (TS + React + Jest + ESLint + Parcel)을 이렇게 처음부터 끝까지 해본 적은 처음이었다. 
README문서 업데이트도 틈틈히 해주어야 겠다.</p>
</li>
<li><p>회사 프로젝트는 이미 lint설정도 다 되어있는 상태라, 별 생각 없이 사용했는데 스스로 설정 해보려니 헷갈리는 부분이 많았다.. 좋은 경험이 된 것 같다.</p>
</li>
<li><p>Jest는 처음 접해봤는데, 어렵다기 보다는 테스트를 어떻게 해야할지 감이 잘 안잡힌다고 해야하나.. 조금 더 공부가 필요할 것 같다.</p>
</li>
</ol>
<h2 id="느낀점">느낀점</h2>
<p>github 의 CI 기준을 통과하는데 여자저차 시행착오가 조금 있었다. markdownlint도 처음 알았다.. 오늘 다시 한번 강의를 훑고, 개념 정리 노트를 조금 더 추가 해봐야겠다. </p>
<h2 id="정리">정리</h2>
<p>이번주는 gitbook에 시간을 너무 많이 쏟아서 정신이 없었는데.. (아직도 해결 못함.. 개념정리는 우선 사용하고 있던 노션에), 다음주부터는 회사에 조금 더 일찍가서 미리 공부해야지! 퇴근하고 공부보다 아침공부 후 저녁에 일찍 자는 게 나은 것 같다. 화이팅~!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP와 HTTPS의 차이점]]></title>
            <link>https://velog.io/@rara-record/HTTP%EC%99%80-HTTPS%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@rara-record/HTTP%EC%99%80-HTTPS%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Mon, 27 Jun 2022 16:07:30 GMT</pubDate>
            <description><![CDATA[<h2 id="http-프로토콜-hypertext-transfer-protocol">HTTP 프로토콜 (Hypertext Transfer Protocol)</h2>
<p>웹 상에서 클라이언트와 서버 간에 요청/응답(request/response)으로 정보를 주고 받을 수 있는 프로토콜로, 주로 HTML문서를 주고받는 데에 쓰인다.</p>
<ul>
<li><strong>프로토콜이란</strong>
컴퓨터 내부, 또는 컴퓨터 사이에서 데이터의 교환 방식을 정의하는 규칙 체계</li>
</ul>
<h2 id="https-프로토콜-hypertext-transfer-protocol-over-secure-socket-layer">HTTPS 프로토콜 (HyperText Transfer Protocol over Secure Socket Layer)</h2>
<p>웹 통신 프로토콜인 HTTP의 보안이 강화된 버전의 프로토콜</p>
<h2 id="http-vs-https">HTTP vs HTTPS</h2>
<ul>
<li>클라이언트인 웹브라우저가 서버에 HTTP를 통해 웹 페이지나, 이미지 정보를 요청하면 서버는 이 요청에 응답하여 요구하는 정보를 제공하게 된다. </li>
<li>웹 페이지(HTML)는 텍스트이고, HTTP를 통해 이런 텍스트 정보를 교환하는 것이다.</li>
<li>이때 주고받는 텍스트 정보에 주민등록번호나 비밀번호와 같이 민감한 정보가 포함된 상태에서 네트워크 상에서 중간에 제3자가 정보를 가로챈다면 보안상 큰 문제가 발생한다.</li>
<li><strong>즉, HTTPS를 사용하면, 서버와 클라이언트 사이의 모든 통신 내용이 암호화 된다는 것.</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리렌더링 이해하기(인라인 스타일을 쓰지 말아야 하는 이유)]]></title>
            <link>https://velog.io/@rara-record/%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0%EC%9D%B8%EB%9D%BC%EC%9D%B8-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%9D%84-%EC%93%B0%EC%A7%80-%EB%A7%90%EC%95%84%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@rara-record/%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0%EC%9D%B8%EB%9D%BC%EC%9D%B8-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%9D%84-%EC%93%B0%EC%A7%80-%EB%A7%90%EC%95%84%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Sun, 19 Jun 2022 20:07:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>스타일에 객체를 넣으면 안되는 이유는 무엇일까?</p>
</blockquote>
<h2 id="예시코드">예시코드</h2>
<pre><code class="language-javascript">      &lt;div style={{ marginTop: 10 }}&gt;
        &lt;Button type=&quot;primary&quot; htmlType=&quot;submit&quot; loading={false}&gt;
          로그인
        &lt;/Button&gt;
        &lt;Link href=&quot;/signup&quot;&gt;
          &lt;a&gt;
            &lt;Button&gt;회원가입&lt;/Button&gt;
          &lt;/a&gt;
        &lt;/Link&gt;
      &lt;/div&gt;</code></pre>
<h3 id="---는-false이기-때문이다">{} === {} 는 false이기 때문이다.</h3>
<p>객체끼리 비교하면, 무조건 false가 나온다.
리액트는 vulture dom으로 검사를 하면서 어디가 달라졌는지 찾다가 
이전 버전과 현재 버전을 비교해 달라진 부분을 리렌더링 한다.
즉, 변한 것이 없어도 객체끼리 비교하면 늘 false가 나오므로, 다르다고 간주하여
리액트는 리렌더링을 해버린다.</p>
<h2 id="해결방법">해결방법</h2>
<blockquote>
<p>styled-components를 쓰거나, useMemo를 쓴다.</p>
</blockquote>
<h3 id="usememo로-해결하는-법">useMemo로 해결하는 법</h3>
<pre><code class="language-javascript"> const styled = useMemo(() =&gt; ({ marginTop: 10 }), [])

 return (
   &lt;div style={styled}&gt;
     &lt;Button type=&quot;primary&quot; htmlType=&quot;submit&quot; loading={false}&gt;
       로그인
      &lt;/Button&gt;
      &lt;Link href=&quot;/signup&quot;&gt;&lt;a&gt;&lt;Button&gt;회원가입&lt;/Button&gt;&lt;/a&gt;&lt;/Link&gt;
   &lt;/div&gt;
 )

</code></pre>
<p>✔ useCallback : 함수를 캐싱한다
✔ useMemo : 값을 캐싱한다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useCallback을 써야할때는?]]></title>
            <link>https://velog.io/@rara-record/useCallback%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EB%95%8C%EB%8A%94</link>
            <guid>https://velog.io/@rara-record/useCallback%EC%9D%84-%EC%8D%A8%EC%95%BC%ED%95%A0%EB%95%8C%EB%8A%94</guid>
            <pubDate>Sun, 19 Jun 2022 19:55:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>useCallback을 쓰는 이유가, 최적화를 위해서라는데 어떨때 써야하는가?</p>
</blockquote>
<h3 id="컴포넌트에-props로-넘겨주는-함수는-usecallback을-써준다">컴포넌트에 props로 넘겨주는 함수는, useCallback을 써준다.</h3>
<p>그래야 <strong>최적화</strong>가 되기 때문이다.</p>
<h3 id="예시코드">예시코드</h3>
<pre><code class="language-react">const LoginForm = () =&gt; {
  const [id, setId] = useState(&quot;&quot;);

  const onChangeId = useCallback((e) =&gt; {
    setId(e.target.value);
  }, []);

  return (
    &lt;Form&gt;
      &lt;div&gt;
        &lt;label htmlFor=&quot;user-id&quot;&gt;아이디&lt;/label&gt;
        &lt;br /&gt;
        &lt;Input
          name=&quot;user-id&quot;
          type=&quot;e-mail&quot;
          value={id}
          onChange={onChangeId}
          required
        /&gt;
      &lt;/div&gt;
    &lt;/Form&gt;
  );
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Storybook 익히기 #1]]></title>
            <link>https://velog.io/@rara-record/Storybook-%EC%9D%B5%ED%9E%88%EA%B8%B0-1</link>
            <guid>https://velog.io/@rara-record/Storybook-%EC%9D%B5%ED%9E%88%EA%B8%B0-1</guid>
            <pubDate>Thu, 28 Apr 2022 12:24:19 GMT</pubDate>
            <description><![CDATA[<p>스터디에서 좋은 사람들을 만나서, 프로젝트를 만들기로 했다.
디자인과 백엔드를 담당해주시는 분이 한 분 있고, 프론트는 나까지 두 명이다. 
이번에 스토리북을 써보기로 해서, 
디자인이 나오기 전까지 스토리북과 리덕스를 대략적으로 공부하고 있는데,
오늘 첫 날이라, 대략적인 정리를 해봐야겠다고 생각했다.
아직 디자인이 나오지 않아서 공식문서에서 제공하는 예제 파일을 조금 변경해서 공부하고 있다.
간단한 투두리스트를 만드는 것!</p>
<p>오늘 공부 한 파일 중 일부인 todo.js 파일이다.</p>
<pre><code class="language-javascript">
// todo.js
const Todo = ({ todo: { id, title, state }}) =&gt; {
  return (
    &lt;div className={`list-item ${state}`}&gt;
      &lt;label className=&quot;checkbox&quot;&gt;
        &lt;input
          type=&quot;checkbox&quot;
        /&gt;
        &lt;span className=&quot;checkbox-custom&quot; /&gt;
      &lt;/label&gt;

      &lt;div className=&quot;title&quot;&gt;
        &lt;input
          type=&quot;text&quot;
          value={title}
          readOnly={true}
          placeholder=&quot;Input title&quot;
        /&gt;
      &lt;/div&gt;

      &lt;div className=&quot;actions&quot; onClick={(event) =&gt; event.stopPropagation()}&gt;
        {state !== &quot;TODO_CHECKED&quot; &amp;&amp; (
          &lt;a&gt;
            &lt;span className={`icon-star`}
            /&gt;
          &lt;/a&gt;
        )}
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Todo;</code></pre>
<p>todo.stories.js를 생성한다.
<strong>export default를 생성해서, 스토리북에게 우리의 컴포넌트가 무엇인지 알려주어야 한다.</strong></p>
<pre><code class="language-javascript">// todo.stories.js
import Todo from &quot;./Todo&quot;;

export default {
  component: Todo, // 스토리북아~! 참조해야 할 컴포넌트가 이거야
  title: &quot;Todo&quot;, //  Storybook 앱의 사이드바에 나타날 컴포넌트 이름
};
</code></pre>
<ol>
<li>스토리는 주어진 state안에서 렌더링된 요소를 리던하는 함수다. 하나의 <code>Template</code> 변수를 만들어서, 확장 해 나가는 식이다.</li>
<li>확장할때에는 <code>bind</code>함수를 사용한다</li>
<li>인수(arguments) 또는 간단히 줄여서 <code>args</code>를 사용하여 컴포넌트를 수정한다. args의 값이 변하면 컴포넌트도 함께 변합니다.</li>
</ol>
<p><strong>Template 생성하기</strong></p>
<pre><code class="language-javascript">// todo.stories.js
export default {
  component: Todo, 
  title: &quot;Todo&quot;, 
};

const Template = (args) =&gt; &lt;Todo {...args} /&gt;;</code></pre>
<p><strong>bind 함수를 이용해서 확장하기!</strong></p>
<pre><code class="language-javascript">export default {
  component: Todo, 
  title: &quot;Todo&quot;, 
};

const Template = (args) =&gt; &lt;Todo {...args} /&gt;;

// 디폴트 먼저 생성
export const Default = Template.bind({});
Default.args = {
  todo: {
    id: &quot;1&quot;,
    title: &quot;Test Todo&quot;,
    state: &quot;TODO_INBOX&quot;,
    updateAt: new Date(2022, 0, 1, 9, 0),
  },
};

export const Pinned = Template.bind({});
Pinned.args = {
  todo: {
    ...Default.args.todo,
    state: &quot;TODO_PINNED&quot;,
  },
};

export const Checked = Template.bind({});
Checked.args = {
  todo: {
    ...Default.args.todo,
    state: &quot;TODO_CHECKED&quot;,
  },
};
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[로그아웃 로직, 인증 라우터 설정]]></title>
            <link>https://velog.io/@rara-record/%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83-%EB%A1%9C%EC%A7%81-%EC%9D%B8%EC%A6%9D-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@rara-record/%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83-%EB%A1%9C%EC%A7%81-%EC%9D%B8%EC%A6%9D-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 21 Apr 2022 22:53:39 GMT</pubDate>
            <description><![CDATA[<h2 id="오늘-한-일">오늘 한 일</h2>
<h3 id="1-로그인--회원가입-로직-리팩토링">1. 로그인 &amp; 회원가입 로직 리팩토링</h3>
<p>기존에 <code>AuthForm</code>이라는 컴포넌트에서 fetch API를 활용해서 초기 로직을 구현했었는데,
일단 작동만 하게 코드를 짰더니, 너무 코드가 길어져서 리팩토링을 해야겠다고 생각했습니다.
<code>store</code>폴더에는 authStore라는 파일을 만들었고, 그 안에서 인증에 관한 상태관리를 하도록 만들었습니다.
기존 fetch로 작성된 코드는, <code>api</code>폴더에 <code>auth.ts</code>라는 파일을 생성해서, firebase 인증 API만 불러오는 파일을 만들었고, 브라우저에 내장된 fatch함수에서  HTTP 비동기 통신 라이브러리인 axios로 바꿔주었습니다.</p>
<h3 id="2-로그아웃-로직-구현">2. 로그아웃 로직 구현</h3>
<p>로그아웃시 바뀌어야 할 값은 하나, 
우리의 상태
즉, 토근을 비우면 됩니다.
그리고 <code>authStore</code>에서 만들어둔 isLoggedIn이라는 boolean 타입 변수을,
<code>false</code>로 업데이트해주면  끝!</p>
<h3 id="3-인증-상태에-따른-라우터-설정">3. 인증 상태에 따른 라우터 설정</h3>
<p>profile 페이지는 로그인 사용자만 보여야 하는 페이지이기 때문에,
로그인하지 않은 사용자는 접근이 불가능하게 만들어야 합니다.
방법은? 라우터를 사용합니다.
1.사용자의 현재 인증 상태를 특정 라우트의 조건으로 삼고!
2.사용자 미인증 시에는 profile 라우트 설정을 아예 렌더링 하지 않게 합니다.
3.그러면 미인증 사용자는 프로필 페이지에 방문할 수 없게된다.</p>
<p><code>App</code> 컴포넌트에서, <code>AuthStore</code>를 불러옵니다.
AuthStore안에 있는 isLoggedIn 변수를 이용해서,</p>
<pre><code class="language-typescript">// App.tsx
const AuthStroe = useContext(AuthStore);

{!AuthStroe.isLoggedIn &amp;&amp; (
  &lt;Route path=&quot;/auth&quot; element={&lt;AuthPage /&gt;} /&gt;
)}

{AuthStroe.isLoggedIn &amp;&amp; (
  &lt;Route path=&quot;/profile&quot; element={&lt;Profile /&gt;} /&gt;
)}
  &lt;/Route&gt;
&lt;Route path=&quot;/*&quot; element={&lt;NotFound /&gt;} /&gt;

export default observer(App);</code></pre>
<p>이렇게 해주면 끝!
사용자가 &#39;접근 하지 말아야 할&#39; 페이지에 &#39;접근&#39; 했다면,
NotFound 페이지를 보여주게끔 추가도 해봤습니다.</p>
<h2 id="느낀점">느낀점</h2>
<ol>
<li>mobX를 아직 미숙하게 다루는 것 같아서, 한번 공식 문서를 다시 읽어봐야겠다고 생각했습니다.</li>
<li>mobX로 작성된, store를 불러올때는 해당 컴포넌트에 observer를 써주는 걸 잊지 말아야 겠다고 생각했습니다.</li>
<li>axios 사용시, body값을 넣고 싶다면 axios.post(url,{ // something }) 이렇게 써주면 됩니다.</li>
</ol>
<h2 id="할-일">할 일</h2>
<ol>
<li>localStorage를 활용해서, 사용자 인증 state 유지하기</li>
<li>프로젝트 디자인 수정 <ul>
<li>font: pretendard</li>
<li>포인트 color : #E67800 고려중..</li>
<li>디자인 시스템 : black: #222, gray: #737373</li>
<li>메인 페이지 디자인 수정</li>
</ul>
</li>
<li>React Hook Form 추가하기</li>
<li>campDetail fawardRef를 적용해서 해결해보기</li>
<li>로딩 스피너 구현</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Typescript에서 useRef Error]]></title>
            <link>https://velog.io/@rara-record/TypeScript-React%EC%97%90%EC%84%9C-useRef</link>
            <guid>https://velog.io/@rara-record/TypeScript-React%EC%97%90%EC%84%9C-useRef</guid>
            <pubDate>Tue, 12 Apr 2022 15:32:38 GMT</pubDate>
            <description><![CDATA[<h2 id="에러-내용">에러 내용</h2>
<blockquote>
<p>개체가 &#39;null&#39;인 것 같습니다.</p>
</blockquote>
<h2 id="문제-코드">문제 코드</h2>
<pre><code class="language-typescript">interface IProps {
  targetCamp: ICampDetail;
}

const ContentsSection = ({ targetCamp }: IProps) =&gt; {
  const containerRef = useRef&lt;HTMLElement&gt;(null);
  const imgRef = useRef&lt;HTMLElement&gt;(null);
  const [height, setHeight] = useState&lt;number&gt;(0);

  const onloadImages = useCallback(() =&gt; {
    const imgs = imgRef.current.querySelectorAll(&#39;img&#39;);
    for (let i = 0; i &lt; imgs.length; i++) {
      imgs[i].onload = () =&gt; {
        if (i === imgs.length - 1) getHeight();
      };
    }
  }, []);

  const getHeight = () =&gt; {
    setHeight(containerRef.current.clientHeight);
  };

  useEffect(() =&gt; {
    targetCamp &amp;&amp; onloadImages();
  }, [onloadImages, targetCamp]);

  return (
    &lt;Container ref={containerRef}&gt;
      {targetCamp &amp;&amp; (
       &lt;ImageBox ref={imgRef}&gt;
          {targetCamp.images.map((img, index) =&gt; (
            &lt;img key={index} src={img} art=&quot;상세페이지 이미지&quot; /&gt;
          ))} 
       &lt;/ImageBox&gt;
      )}
    &lt;/Container&gt;
  );</code></pre>
<h2 id="코드-설명">코드 설명</h2>
<p><code>ContentsSection</code> 컴포넌트 안에서, <code>ContentsSection</code>의 clientHeight값과, <code>ImageBox</code>안의 이미지들을 구하고 싶습니다. <code>Container</code>와 <code>ImageBox</code> 컴포넌트를 useRef로 받습니다. <code>containerRef</code>와 <code>imgRef</code> 모두 DOM을 참조하기 위함입니다. </p>
<pre><code class="language-typescript">containerRef.current
imgRef.current</code></pre>
<p>이 부분에서, 개체가 null인 것 같다는 오류가 뜹니다.</p>
<h2 id="문제-해결-후-코드">문제 해결 후 코드</h2>
<pre><code class="language-typescript">interface IProps {
  targetCamp: ICampDetail;
}

const ContentsSection = ({ targetCamp }: IProps) =&gt; {
  const containerRef = useRef&lt;HTMLElement&gt;(null);
  const imgRef = useRef&lt;HTMLElement&gt;(null);
  const [height, setHeight] = useState&lt;number&gt;(0);

  const onloadImages = useCallback(() =&gt; {
    if (imgRef.current) { // if문 추가
      const imgs = imgRef.current.querySelectorAll(&#39;img&#39;);
      for (let i = 0; i &lt; imgs.length; i++) {
        imgs[i].onload = () =&gt; {
          if (i === imgs.length - 1) getHeight();
        };
      }
    }
  }, []);

  const getHeight = () =&gt; {
    containerRef.current &amp;&amp; // AND연산자 추가
      setHeight(containerRef.current.clientHeight);
  };

  useEffect(() =&gt; {
    targetCamp &amp;&amp; onloadImages();
  }, [onloadImages, targetCamp]);

  return (
    &lt;Container ref={containerRef}&gt;
      {targetCamp &amp;&amp; (
       &lt;ImageBox ref={imgRef}&gt;
          {targetCamp.images.map((img, index) =&gt; (
            &lt;img key={index} src={img} art=&quot;상세페이지 이미지&quot; /&gt;
          ))} 
       &lt;/ImageBox&gt;
      )}
    &lt;/Container&gt;
  );</code></pre>
<p>if문과 AND연산자를 이용했습니다.
<strong>초기값이 null이어서, null이 반환되면 안되니 조건을 추가해서 에러를 해결</strong>했습니다.</p>
<h2 id="useref-정의">useRef 정의</h2>
<p>useRef를 사용하다보면, 심심찮게 에러를 뿜어내고는 하는데
이 참에, useRef 대해 이 문서를 보고 자세히 공부를 해봐야 겠다고 생각하여,
<a href="https://driip.me/7126d5d5-1937-44a8-98ed-f9065a7c35b5">https://driip.me/7126d5d5-1937-44a8-98ed-f9065a7c35b5</a>
이 문서를 보고 공부를 해보았습니다.</p>
<h2 id="useref의-반환타입은-2가지">useRef의 반환타입은 2가지</h2>
<pre><code class="language-typescript">interface MutableRefObject&lt;T&gt; {
    current: T;
}

interface RefObject&lt;T&gt; {
    readonly current: T | null;
}</code></pre>
<p>useRef의 반환타입에는 2가지가 있고, 그저 함수 초깃값을 .current에 저장할 뿐입니다.</p>
<h2 id="useref의-정의는-3가지">useRef의 정의는 3가지</h2>
<pre><code class="language-typescript">useRef&lt;T&gt;(initialValue: T): MutableRefObject&lt;T&gt;; // 1
useRef&lt;T&gt;(initialValue: T|null): RefObject&lt;T&gt;; // 2
useRef&lt;T = undefined&gt;(): MutableRefObject&lt;T | undefined&gt;; // 3</code></pre>
<ol>
<li>인자의 타입과, 제네릭 타입이 T로 <strong>일치</strong>하는 경우, <code>MutableRefObject&lt;T&gt;</code>를 반환</li>
<li>인자의 타입이 <strong>null</strong>을 허용하는 경우, <code>RefObject&lt;T&gt;</code>를 반환</li>
<li>제네릭의 타입이 <strong>undefined</strong>인 경우(타입을 제공하지 않은 경우), <code>MutableRefObject&lt;T | undefined&gt;</code>를 반환 </li>
</ol>
<h2 id="상황별-useref의-사용">상황별 useRef의 사용</h2>
<ol>
<li>useRef로 로컬변수 사용하기</li>
</ol>
<ul>
<li>타입 과 인자가 &quot;서로 같은 타입&quot;인 경우, <code>MutableRefObject&lt;T&gt;</code> 반환</li>
<li>current 프로퍼티 그 자체를 직접 변경할 수 있습니다.<pre><code class="language-typescript">const localValRef = useRef&lt;number&gt;(숫자)</code></pre>
[예시코드]
: 버튼을 클릭할 경우 localVarRef.current의 값이 1씩 증가하는 코드 입니다.<pre><code class="language-typescript">import React, { useRef } from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>const App = () =&gt; {
  const localVarRef = useRef<number>(0);</p>
<p>  const handleButtonClick = () =&gt; {
        if (localVarRef.current) {
        localVarRef.current += 1;
        console.log(localVarRef.current);
        }
  };</p>
<p>  return (
    <div className="App">
      <button onClick={handleButtonClick}>+1</button>
    </div>
  );
};</p>
<p>export default App;</p>
<pre><code>2. useRef로 HTML엘리먼트 객체를 참조하기
- 인자에 null을 허용하는 타입(T|null)을 할당할 경우, `RefObject&lt;T&gt;` 반환
- current 프로퍼티를 직접 수정할 수 없습니다. 

```typescript
const containerRef = useRef&lt;HTMLElement&gt;(null)</code></pre><h2 id="나의-코드에서는-어떤-타입을-반환하는가">나의 코드에서는 어떤 타입을 반환하는가?</h2>
<p>내 코드에서는 DOM을 참조하고 싶은거니까, <code>RefObject&lt;T&gt;</code>를 반환합니다.</p>
<h3 id="refobjectt는-값을-수정할-수-없다고-하는데-왜-정상적으로-작동할까"><code>RefObject&lt;T&gt;</code>는 값을 수정할 수 없다고 하는데, 왜 정상적으로 작동할까?</h3>
<blockquote>
<p>정의 상 current 프로퍼티만 읽기 전용으로, current 프로퍼티의 하위 프로퍼티는 여전히 수정 가능하다. 이는 readonly가 shallow(얕은객체복사)이기 때문입니다.</p>
</blockquote>
<h3 id="ref-타입설정">Ref 타입설정?</h3>
<pre><code class="language-typescript">const containerRef = useRef() as React.MutableRefObject&lt;HTMLElement&gt;;
const imgRef = useRef() as React.MutableRefObject&lt;HTMLElement&gt;;</code></pre>
<p>useRef의 타입을 이렇게 설정하면, <code>MutableRefObject&lt;T&gt;</code>를 반환하는 것을 
확인하였습니다. 이 경우, <code>onloadImages</code> 와 <code>getHeight</code>에 각각 if문이나, 
AND연산자를 걸지 않아도 동작합니다.</p>
<pre><code class="language-typescript">  const onloadImages = useCallback(() =&gt; {
    const imgs = imgRef.current.querySelectorAll(&#39;img&#39;);
    for (let i = 0; i &lt; imgs.length; i++) {
      imgs[i].onload = () =&gt; {
        if (i === imgs.length - 1) getHeight();
      };
    }
  }, []);

  const getHeight = () =&gt; {
   setHeight(containerRef.current.clientHeight);
  };</code></pre>
<h2 id="정리">정리</h2>
<p>정리하면 다음과 같습니다.</p>
<pre><code class="language-typescript">const localVarRef = useRef&lt;여기&gt;(저기);</code></pre>
<p>로컬 변수 용도로 useRef를 사용하는 경우, <code>MutableRefObject&lt;T&gt;</code>를 사용해야 하므로 <br><strong>제네릭 타입과 같은 타입의 초깃값</strong>을 넣어주자. (여기와 저기를 같게)</p>
<pre><code class="language-typescript">const inputRef = useRef&lt;HTMLInputElement&gt;(null);</code></pre>
<p>DOM을 직접 조작하기 위해 프로퍼티로 useRef 객체를 사용할 경우, <br/><code>RefObject&lt;T&gt;</code>를 사용해야 하므로 <strong>초깃값으로 null</strong>을 넣어주자.</p>
<pre><code class="language-typescript">useRef() as React.MutableRefObject&lt;T&gt;</code></pre>
<p><code>MutableRefObject&lt;T&gt;</code>를 반환한다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[index가 필요할때는 순수 for문을 쓰자]]></title>
            <link>https://velog.io/@rara-record/index%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%A0%EB%95%8C%EB%8A%94-%EC%88%9C%EC%88%98-for%EB%AC%B8%EC%9D%84-%EC%93%B0%EC%9E%90</link>
            <guid>https://velog.io/@rara-record/index%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%A0%EB%95%8C%EB%8A%94-%EC%88%9C%EC%88%98-for%EB%AC%B8%EC%9D%84-%EC%93%B0%EC%9E%90</guid>
            <pubDate>Mon, 11 Apr 2022 13:44:08 GMT</pubDate>
            <description><![CDATA[<h2 id="코드-설명">코드 설명</h2>
<p>이미지가 다 로드되고 나면, 이미지를 감싸고 있는 컴포넌트의 높이를 구해야 했다. <br/>구한 높이값은 다른 컴포넌트(<code>Sidebar</code>)에 props로 넘겨준다. </p>
<h2 id="리팩토링-전-코드">리팩토링 전 코드</h2>
<pre><code class="language-typescript">
interface IProps {
  targetCamp: ICampDetail;
}

const ContentsSection = ({ targetCamp }: IProps) =&gt; {
  const containerRef = useRef() as React.MutableRefObject&lt;HTMLElement&gt;;
  const imgRef = useRef() as React.MutableRefObject&lt;HTMLElement&gt;;
  const [height, setHeight] = useState&lt;number&gt;(0);

  // 문제 함수
  const onloadImages = useCallback(() =&gt; {
      const imgs = imgRef.current.querySelectorAll(&#39;img&#39;);
    const len: number = imgs.length;
    let count: number = 0;

    for(let img of imgs) {
     img.onload = () =&gt; {
       count++;
       if(count === len) getHeight();
      }
    }
  }, []);

  const getHeight = () =&gt; {
    const newHeight = containerRef.current.clientHeight;
    setHeight(newHeight);
  };

  useEffect(() =&gt; {
    targetCamp &amp;&amp; onloadImages();
    window.addEventListener(&#39;resize&#39;, getHeight);
    return () =&gt; {
      window.removeEventListener(&#39;resize&#39;, getHeight);
    };
  }, [onloadImages, targetCamp]);

  return (
      &lt;Container ref={containerRef}&gt;
        {targetCamp.images.map((img, index) =&gt; (
              &lt;img key={index} src={img} /&gt;
        ))}
        &lt;Sidebar height={height}/&gt;
    &lt;/Container&gt;
  )
};
export default ContentsSection;</code></pre>
<p>이 코드에서,</p>
<blockquote>
<p>Function declared in a loop contains unsafe references to variable(s) &#39;count&#39;, &#39;count&#39;.</p>
</blockquote>
<p>라는 ESLint 오류가 떴는데, 루프 안에서 count 변수가 안전하지 않다는 내용이었다.
ESLint 공식문서를 찾아보니, 예상대로 작동하지 않을 수 있는 코드 부분을 강조하기 위해 발생하며 언어 작동 방식에 대한 오해를 나타낼 수 있다고 한다. 즉, 루프 내에서 함수를 작성하면 코드가 문제 없이 실행될 수는 있지만 일부 상황에서는 예기치 않게 작동할 수 있으므로 주의를 준 것 같다.</p>
<h2 id="리팩토링-후-코드">리팩토링 후 코드</h2>
<pre><code class="language-typescript">const ContentsSection = ({ targetCamp }: IProps) =&gt; {
  const containerRef = useRef() as React.MutableRefObject&lt;HTMLElement&gt;;
  const imgRef = useRef() as React.MutableRefObject&lt;HTMLElement&gt;;
  const [height, setHeight] = useState&lt;number&gt;(0);

  // 리팩토링 후
  const onloadImages = useCallback(() =&gt; {
    const imgs = imgRef.current.querySelectorAll(&#39;img&#39;);
    for (let i = 0; i &lt; imgs.length; i++) {
      imgs[i].onload = () =&gt; {
        if (i === imgs.length - 1) getHeight();
      };
    }
  }, []);

  const getHeight = () =&gt; {
    const newHeight = containerRef.current.clientHeight;
    setHeight(newHeight);
  };

  useEffect(() =&gt; {
    targetCamp &amp;&amp; onloadImages();
    window.addEventListener(&#39;resize&#39;, getHeight);
    return () =&gt; {
      window.removeEventListener(&#39;resize&#39;, getHeight);
    };
  }, [onloadImages, targetCamp]);

  return (      
    &lt;Container ref={containerRef}&gt;
        {targetCamp.images.map((img, index) =&gt; (
              &lt;img key={index} src={img} /&gt;
        ))}
        &lt;Sidebar height={height}/&gt;
    &lt;/Container&gt;

  );
};

export default ContentsSection;</code></pre>
<p><code>for of</code>문을 <code>for</code>문으로 바꿔서, 리팩토링을 진행했다.
<code>index</code>가 필요한 상황이어서, 굳이 <code>for of</code>를 써서 <code>count</code>변수를 만들지 않아도 되기 때문이었다. 대신 <code>index</code>는 0부터 시작하기 때문에, 총 길이에서 하나를 빼준 값과 <code>index</code> 값이 같으면, getHeight 함수를 실행하도록 했다.</p>
]]></description>
        </item>
    </channel>
</rss>