<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bori_note.log</title>
        <link>https://velog.io/</link>
        <description>정신차려 이 각박한 세상속에서</description>
        <lastBuildDate>Thu, 17 Oct 2024 08:55:45 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bori_note.log</title>
            <url>https://velog.velcdn.com/images/bori_note/profile/3127f0f4-4e35-4a5d-bc5a-4c8b4049ebc9/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bori_note.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bori_note" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[FE 위클리 페이퍼]]></title>
            <link>https://velog.io/@bori_note/FE-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</link>
            <guid>https://velog.io/@bori_note/FE-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</guid>
            <pubDate>Thu, 17 Oct 2024 08:55:45 GMT</pubDate>
            <description><![CDATA[<h3 id="✅css의-cascading에-대해-설명해-주세요">✅CSS의 Cascading에 대해 설명해 주세요.</h3>
<p>CSS의 Cascading은 스타일 적용 시 우선순위에 따라 스타일이 결정되며, 중요도, 명시도, 소스 순서가 영향을 미칩니다.</p>
<h3 id="✅시맨틱-태그를-사용하면-좋은-점을-설명해-주세요">✅시맨틱 태그를 사용하면 좋은 점을 설명해 주세요.</h3>
<p>시맨틱 태그는 코드의 가독성을 높이고, SEO와 웹 접근성을 향상시킵니다.</p>
<h3 id="✅사용자가-브라우저-font-size를-변경하였을-때-이에-대응해서-웹-화면의-요소가-크거나-작아지도록-하려면-어떻게-스타일링-하면-좋을까요">✅사용자가 브라우저 font-size를 변경하였을 때, 이에 대응해서 웹 화면의 요소가 크거나 작아지도록 하려면 어떻게 스타일링 하면 좋을까요?</h3>
<p>rem 또는 em 단위를 사용해 브라우저 폰트 크기에 따라 요소 크기를 유동적으로 조정할 수 있습니다.</p>
<blockquote>
<p>rem 은 항상 루트 폰트 사이즈를 참조합니다. 일반적인 브라우저 기본 폰트 크기가 16px이고 이를 이용하면  사용자가 브라우저 폰트 크기 설정을 변경하지 않는 일반적인 경우, 1rem 은 16px 크기라 보고 스타일링 하면 됩니다.</p>
</blockquote>
<h3 id="✅position의-속성들과-각각의-특징을-설명해-주세요">✅position의 속성들과 각각의 특징을 설명해 주세요.</h3>
<ul>
<li>static: 기본 값으로 위치 지정 없음.</li>
<li>relative: 요소의 원래 위치를 기준으로 이동.</li>
<li>absolute: 부모 요소를 기준으로 이동.</li>
<li>fixed: 뷰포트를 기준으로 고정.</li>
<li>sticky: 스크롤에 따라 상대적 또는 고정 위치가 적용됩니다.<h3 id="✅git에서-branch-merge-방법들과-각-방법의-특징을-설명해-주세요">✅Git에서 branch merge 방법들과 각 방법의 특징을 설명해 주세요.</h3>
</li>
<li>merge: 일반적으로 많이 사용하는 merge 방법이며, 커밋 이력을 모두 남길 때 사용합니다. 장점이자 단점은 모든 커밋과 분기했던 branch 히스토리가 남는다는 점입니다.</li>
<li>Squash &amp; Merge: 분기했던 branch에 있던 내용 a, b, c 커밋을 모두 합쳐 하나의 새로운 커밋을 만듭니다. 지저분한 커밋 히스토리들을 하나로 합쳐서, 기능상 의미있는 하나의 커밋만 남길 때 사용합니다.</li>
<li>Rebase &amp; Merge: 분기했던 branch의 기준을 최신 Base로 설정하고, merge하는 방법. 머지 커밋을 남길 필요가 없는 merge의 경우 사용하면 좋습니다. 커밋 그래프가 하나의 라인으로 그려져 가독성에 좋습니다.</li>
</ul>
<h3 id="✅git-flow-브랜치-전략에-대해-설명해-주세요">✅Git Flow 브랜치 전략에 대해 설명해 주세요.</h3>
<p>master는 프로덕션 릴리즈 브랜치, develop은 개발 브랜치로, feature, release, hotfix 브랜치로 기능 개발과 버그 수정을 관리하는 전략입니다.</p>
<blockquote>
<ul>
<li>안정적인 배포를 위한 구조가 갖춰져 있습니다.</li>
<li>긴 개발 주기에 적합하며, 복잡한 기능 개발과 버그 수정에 유용합니다.</li>
<li>배포 전 마무리 작업과 테스트를 위한 release 브랜치를 사용할 수 있습니다.</li>
<li>브랜치가 많아지고 관리해야 할 작업이 증가할 수 있습니다. 이를 보완하기 위한 <a href="https://github.com/nvie/gitflow">git flow 도구</a>가 있을 정도죠.</li>
<li>작은 규모의 프로젝트에서는 비효율적일 수 있습니다.</li>
</ul>
</blockquote>
<h3 id="✅자바스크립트에서-와-가-어떻게-다른지-설명해-주세요">✅자바스크립트에서 ==와 ===가 어떻게 다른지 설명해 주세요.</h3>
<p>==는 타입 변환을 허용한 비교, ===는 타입과 값 모두를 엄격하게 비교합니다.</p>
<blockquote>
<p>== 는 암묵적 형변환이이 일어나는데, 암묵적 형변환이 일어날 것을 모두 기억하거나 예측하는 것이 어렵기 때문에, 의도하지 않은 결과를 얻게 될 가능성이 크고 이로 인해 오류 가능성이 커집니다. 따라서 동등함을 비교하기 위해서 === 를 사용하는 것이 좋습니다.</p>
</blockquote>
<h3 id="✅자바스크립트에서-얕은-복사shallow-copy와-깊은-복사deep-copy에-대해-설명해-주세요">✅자바스크립트에서 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)에 대해 설명해 주세요.</h3>
<p>얕은 복사는 객체의 참조만 복사해 원본과 복사본이 같은 객체를 가리키는 방식입니다. 반면, 깊은 복사는 객체의 모든 속성을 새로 복사해 독립적인 객체를 생성하는 방식입니다.</p>
<h3 id="✅var-let-const를-중복-선언-허용-스코프-호이스팅-관점에서-서로-비교해-주세요">✅var, let, const를 중복 선언 허용, 스코프, 호이스팅 관점에서 서로 비교해 주세요.</h3>
<ul>
<li>중복 선언: var는 중복 선언이 가능하지만, let과 const는 불가능합니다.</li>
<li>스코프: var는 함수 스코프를 따르고, let과 const는 블록 스코프를 따릅니다.</li>
<li>호이스팅: var는 선언과 초기화가 동시에 호이스팅되고, let과 const는 선언만 호이스팅되어 초기화 전에 접근하면 에러가 발생합니다.</li>
</ul>
<h3 id="✅브라우저가-어떻게-동작하는지-설명해-주세요">✅브라우저가 어떻게 동작하는지 설명해 주세요.</h3>
<p>브라우저는 URL을 입력하면 해당 서버에 HTTP 요청을 보내고, 서버로부터 HTML, CSS, JavaScript 파일을 받아 해석하여 DOM 트리와 렌더 트리를 생성하고 화면에 렌더링합니다.</p>
<blockquote>
<ol>
<li>DOM(Document Object Model), CSSOM(CSS Object Model) 생성</li>
<li>Render Tree 생성</li>
<li>Layout</li>
<li>Paint</li>
</ol>
</blockquote>
<h3 id="✅이벤트-버블링-캡쳐링-위임에-대해-설명해-주세요">✅이벤트 버블링, 캡쳐링, 위임에 대해 설명해 주세요.</h3>
<ul>
<li>이벤트 버블링: 이벤트가 하위 요소에서 상위 요소로 전파됩니다.</li>
<li>이벤트 캡쳐링: 이벤트가 상위 요소에서 하위 요소로 전파됩니다.</li>
<li>이벤트 위임: 상위 요소에 이벤트 리스너를 설정해 하위 요소들의 이벤트를 처리하는 방식입니다.<h3 id="✅자바스크립트에서-this에-대해-설명해-주세요">✅자바스크립트에서 this에 대해 설명해 주세요.</h3>
this는 함수가 호출될 때의 실행 문맥에 따라 값이 결정됩니다. 일반 함수에서는 전역 객체를 가리키고, 메서드에서는 해당 객체를 가리키며, 화살표 함수에서는 상위 스코프의 this를 가리킵니다.</li>
</ul>
<h3 id="✅렉시컬-스코프lexical-scope에-대해-설명해-주세요">✅렉시컬 스코프(Lexical scope)에 대해 설명해 주세요.</h3>
<p>렉시컬 스코프는 함수가 정의될 때의 스코프 체인을 기준으로 변수의 유효 범위가 결정되는 것을 의미합니다. 즉, 함수 호출 위치가 아닌 정의 위치에 따라 변수를 참조합니다.</p>
<h3 id="✅http-메소드에-대해-설명해-주세요">✅HTTP 메소드에 대해 설명해 주세요.</h3>
<ul>
<li>GET: 서버에서 데이터를 요청할 때 사용합니다.</li>
<li>POST: 서버에 데이터를 제출할 때 사용합니다.</li>
<li>PUT: 서버의 데이터를 업데이트할 때 사용합니다.</li>
<li>DELETE: 서버의 데이터를 삭제할 때 사용합니다.</li>
</ul>
<blockquote>
<ul>
<li>PATCH: 기존 리소스의 부분적인 수정을 위한 메소드</li>
<li>HEAD: 특정 리소스를 GET 메소드로 요청했을 때 돌아올 헤더를 받기 위한 메소드</li>
<li>OPTIONS: 주어진 URL 또는 서버에 대해 허용된 통신 옵션을 받기 위한 메소드</li>
<li>CONNECT:  요청한 리소스에 대해 양방향 연결을 시작하는 메소드</li>
</ul>
</blockquote>
<h3 id="✅예시의-코드를-실행할-때-콘솔에-출력될-값과-그-이유를-설명해-주세요">✅예시의 코드를 실행할 때, 콘솔에 출력될 값과 그 이유를 설명해 주세요.</h3>
<pre><code class="language-js">// 1번
let num = 1;

// 2번
setTimeout(() =&gt; {
  num = 2;
}, 0);

// 3번
num = 3;

// 4번
console.log(num);</code></pre>
<blockquote>
<p>자바스크립트는 싱글 스레드 기반의 언어입니다. 여기서 스레드란 작은 단위의 실행 흐름을 말합니다. 자바스크립트 엔진을 간단히 살펴보면 위와 같이 Memory Heap과 Call Stack(자료구조에서 배우게 될 stack처럼 구현된 저장소)으로 구성되어 있는데요. 하나의 스레드에서 하나의 Call Stack으로 현재 어떤 함수가 동작하고 있는지, 그 함수 내에서 어떤 함수가 동작하고, 다음에 어떤 함수가 호출되어야 하는지 등을 제어합니다.</p>
</blockquote>
<ul>
<li>1번 실행으로 <code>num</code> 에 1이 할당됩니다.</li>
<li>2번 실행으로 <code>setTimeout</code> 을 Web API(NodeJS의 경우 Timers 모듈)가 처리하도록 넘깁니다. 이때 중요한 건, 즉시 <code>setTimeout</code> 에 있는 callback을 실행할지 판단하지 않습니다. 따라서 <code>num</code> 은 여전히 1인 상태입니다.</li>
<li>한편, Web API에서는 <code>setTimeout</code> 작업이 완료되면 <code>setTimeout</code> callback 함수를 Callback Queue에 등록합니다.</li>
<li>3번 실행으로 <code>num</code> 에 3이 재할당됩니다.</li>
<li>4번 실행으로 화면에 3이 출력됩니다.</li>
<li>이렇게 모든 실행을 마치면, 그 후에 Event Loop를 돌아 Callback Queue에 있는 callback 함수를 Call Stack으로 가져와 실행하고 <code>num</code> 은 2가 재할당됩니다.</li>
</ul>
<h3 id="✅프로토타입-체인에-대해-설명해-주세요">✅프로토타입 체인에 대해 설명해 주세요.</h3>
<p>프로토타입 체인은 객체가 다른 객체의 속성이나 메서드를 상속받을 수 있도록 연결된 구조입니다. 자바스크립트는 프로토타입을 통해 객체 간 상속을 구현하며, 객체가 다른 객체의 프로토타입을 참조할 수 있습니다.</p>
<h3 id="✅ajax에-대해-설명해-주세요">✅AJAX에 대해 설명해 주세요.</h3>
<p>AJAX는 비동기적으로 서버와 통신해 전체 페이지를 새로고침하지 않고도 데이터 일부를 업데이트할 수 있는 기술입니다. 주로 XML, JSON 형식의 데이터를 서버에서 받아와 웹페이지에 적용합니다.</p>
<h3 id="✅시간복잡도와-공간복잡도에-대해-설명해-주세요">✅시간복잡도와 공간복잡도에 대해 설명해 주세요.</h3>
<ul>
<li>시간복잡도: 알고리즘의 실행 시간이 입력 크기에 따라 어떻게 변하는지 분석합니다.</li>
<li>공간복잡도: 알고리즘이 사용하는 메모리 양을 입력 크기에 따라 분석합니다.</li>
</ul>
<h3 id="✅자바스크립트-실행-컨텍스트execution-context에-대해-설명해-주세요">✅자바스크립트 실행 컨텍스트(Execution context)에 대해 설명해 주세요.</h3>
<p>실행 컨텍스트는 자바스크립트 코드가 실행되는 환경을 정의하며, 변수, 함수 선언, this 등의 정보를 포함합니다. 실행 컨텍스트는 콜 스택에 쌓여서 실행됩니다.</p>
<h3 id="✅해시-테이블과-해시-함수에-대해-설명해-주세요">✅해시 테이블과 해시 함수에 대해 설명해 주세요.</h3>
<p>해시 테이블은 데이터를 키-값 쌍으로 저장하는 자료구조로, 해시 함수를 사용해 키를 해시값으로 변환하여 저장 위치를 결정합니다. 이를 통해 빠른 데이터 검색이 가능합니다.</p>
<h3 id="✅정적-배열과-동적-배열을-비교해-설명해-주세요">✅정적 배열과 동적 배열을 비교해 설명해 주세요.</h3>
<p>정적 배열: 크기가 고정되어 있으며, 선언 시 메모리가 미리 할당됩니다.
동적 배열: 크기가 가변적이며, 필요에 따라 메모리가 동적으로 할당됩니다.</p>
<h3 id="✅리액트에서-virtual-dom이-무엇인지-이를-사용하는-이유는-무엇인지-설명해-주세요">✅리액트에서 Virtual DOM이 무엇인지, 이를 사용하는 이유는 무엇인지 설명해 주세요.</h3>
<p>Virtual DOM은 실제 DOM을 가상으로 표현한 객체로, 변경 사항을 최소화하여 효율적인 업데이트를 위해 사용합니다. 가상 DOM 생성 후 이전 가상 DOM과 비교 후 바뀐 부분만 DOM에 업데이트한다.</p>
<blockquote>
<p>리액트는 Virtual DOM을 활용해 UI의 이상적인 “가상”적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화 합니다. 이 과정을 재조정(Reconciliation)이라고 합니다.
이렇게 연산이 끝난 뒤 최종적인 변화를 실제 DOM에 반영해서 여러 번 reflow, repaint가 발생할 수 있는 작업을 한번으로 줄여줍니다. 또한, Virtual DOM은 변화에 대한 관리를 자동화하고 추상화해서 작업의 편의성을 제공합니다.</p>
</blockquote>
<h3 id="✅리액트에서-배열을-렌더링할-때-key를-써야-하는-이유에-대해-설명해-주세요">✅리액트에서 배열을 렌더링할 때 key를 써야 하는 이유에 대해 설명해 주세요.</h3>
<p>key는 배열의 요소를 고유하게 식별해, 리액트가 변화를 효율적으로 감지하고 렌더링 성능을 최적화할 수 있게 합니다.</p>
<blockquote>
<p>리액트는 <code>key</code> 속성을 지원합니다. 자식들이 <code>key</code>를 가지고 있다면, 리액트는 <strong><code>key</code>를 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인</strong>합니다.
<code>key</code> 는 리액트가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕습니다. <code>key</code> 는 엘리먼트에 안정적인 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야 합니다.
<code>key</code> 는 리스트의 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 문자열을 사용하는 것이 가장 좋습니다. 대부분의 경우 데이터의 id를 <code>key</code> 로 사용합니다. <code>key</code>는 배열의 항목들 사이(형제 사이)에서 고유하면 되고, 전역에서 고유할 필요는 없습니다.</p>
</blockquote>
<h3 id="✅리액트-생명주기life-cycle에-대해-설명해-주세요">✅리액트 생명주기(life cycle)에 대해 설명해 주세요.</h3>
<p>리액트 생명주기는 컴포넌트가 생성, 업데이트, 제거되는 과정을 나타내며, 주요 단계로는 Mounting, Updating, Unmounting이 있습니다.</p>
<h3 id="✅웹-페이지-렌더링-방식-csr-ssr-ssg-각각의-특징과-각-방식을-어떤-상황에-사용하면-좋을지-설명해-주세요">✅웹 페이지 렌더링 방식 CSR, SSR, SSG 각각의 특징과 각 방식을 어떤 상황에 사용하면 좋을지 설명해 주세요.</h3>
<ul>
<li><p>CSR: 클라이언트에서 렌더링, 초기 로딩은 느리지만 이후 빠름. SPA에 적합.</p>
</li>
<li><p>SSR: 서버에서 렌더링, 초기 로딩이 빠르고 SEO에 유리. 자주 변하는 콘텐츠에 적합.</p>
</li>
<li><p>SSG: 정적 페이지를 생성, 속도가 매우 빠르고 CDN에 적합. 자주 변하지 않는 콘텐츠에 유리.</p>
<h3 id="✅본인이-생각하는-css-in-js의-장점과-단점을-설명해-주세요">✅본인이 생각하는 CSS-in-JS의 장점과 단점을 설명해 주세요.</h3>
</li>
<li><p>장점: 컴포넌트 단위로 스타일을 관리해 유지보수가 쉽고, 동적 스타일 적용에 유리합니다.</p>
</li>
<li><p>단점: 런타임에서 스타일을 처리하므로 성능 저하가 있을 수 있고, 파일 크기가 커질 수 있습니다.</p>
<h3 id="✅presentational--container-디자인-패턴에-대해-설명해-주세요">✅Presentational &amp; Container 디자인 패턴에 대해 설명해 주세요.</h3>
</li>
</ul>
<p>Presentational 컴포넌트는 UI에만 집중하고, Container 컴포넌트는 상태 관리와 로직을 처리하는 패턴으로, 관심사의 분리를 통해 유지보수성과 재사용성을 높입니다.</p>
<h3 id="✅javascript만-사용하는-것과-비교해-typescript를-사용하는-이유에-대해-설명해-주세요">✅JavaScript만 사용하는 것과 비교해 TypeScript를 사용하는 이유에 대해 설명해 주세요.</h3>
<p>TypeScript는 정적 타입을 지원해 코드의 안정성과 가독성을 높이고, 개발 중 오류를 사전에 방지할 수 있습니다.</p>
<p>이런 TypeScript의 특징을 통해 <strong>컴파일 단계에서 오류를 포착</strong>할 수 있고, <strong>코드를 통해 어떤 타입의 값이 필요한지 쉽게 파악할 수 있어 코드의 흐름을 이해하기 쉽고 협업에 도움</strong>이 됩니다.</p>
<p>이 밖에도 vscode와 같은 IDE를 통해 <strong>인텔리센스 활용이 가능</strong>해지고, 인터페이스, 제네릭 등을 지원해 <strong>객체지향 프로그래밍에 도움</strong>을 줍니다.</p>
<h3 id="✅typescript의-동작-원리에-대해-설명해-주세요">✅TypeScript의 동작 원리에 대해 설명해 주세요.</h3>
<p>TypeScript는 정적 타입 검사 기능을 제공하는 JavaScript의 상위 집합 언어입니다. 코드를 컴파일할 때 타입 오류를 감지하고, 이 과정에서 JavaScript로 변환되어 브라우저에서 실행됩니다. 런타임에 타입이 제거되므로 브라우저에서는 일반적인 JavaScript로 동작합니다.</p>
<h3 id="✅리액트만-사용할-때와-비교해-nextjs를-사용하는-이유에-대해-설명해-주세요">✅리액트만 사용할 때와 비교해 Next.js를 사용하는 이유에 대해 설명해 주세요.</h3>
<p>React는 클라이언트 사이드 렌더링(CSR)을 주로 처리하지만, Next.js는 서버 사이드 렌더링(SSR), 정적 사이트 생성(SSG), 라우팅 기능을 내장하여 SEO와 초기 로딩 성능을 개선할 수 있습니다. 이 외에도 API 라우팅, 이미지 최적화 같은 추가 기능을 제공합니다.</p>
<blockquote>
<p>기본적으로 순수 리액트는 SPA(Single Page Application)이고, CSR(Client Side Rendering)을 합니다. 웹 사이트를 요청할 때 현재 사용하지 않지만 앱에 필요한 모든 컴포넌트를 다운로드하고, 빈 html을 가져와 script를 로딩합니다. 이로 인해 앱의 첫 로딩 시간이 오래 걸려 유저 경험을 헤치고, SEO(Search Engine Optimization)에 취약 하다는 단점이 있습니다.</p>
</blockquote>
<ul>
<li>프리렌더링<ul>
<li>기본적으로 프리렌더링을 지원합니다. 데이터의 성격에 따라 <code>getStaticProps</code>를 활용해 빌드 단계에서 데이터를 받아 정적 생성 또는 SSR(Server Side Rendering)할 수 있습니다. 이를 통해 이미 렌더링된 html 문서를 가져올 수 있어서 첫 로딩이 빨라져 유저 경험에 좋고, SEO에도 강점이 있습니다.</li>
</ul>
</li>
<li>이미지 최적화<ul>
<li><code>Image</code> 컴포넌트를 통해 필요한 크기에 맞는 이미지를 제공하고, lazy-loading을 통해 필요한 순간 이미지를 가져와 최적화 합니다.</li>
</ul>
</li>
<li>Client side navigation<ul>
<li><code>Link</code> 컴포넌트를 통해 페이지 이동할 때 페이 전체를 불러오는 것이 아니라 필요한 데이터만 가져오기 때문에 이동 속도도 빨라지고, 넘어가는 동작도 부드럽습니다.</li>
</ul>
</li>
<li>코드 스플리팅<ul>
<li>Webpack과 같은 번들러 설정과 <code>import()</code> 문법, <code>lazy</code>, <code>Suspense</code> 사용없이도 빌드 과정에서 페이지 단위로 자동으로 코드 스플리팅을 지원합니다.</li>
</ul>
</li>
<li>개발자 경험<ul>
<li>파일 시스템 라우팅, 리다이렉트, 스타일링을 위한 환경 설정(Sass, CSS Modules, Tailwind 등)등을 제공해 좋은 개발자 경험을 제공합니다.</li>
</ul>
</li>
</ul>
<h3 id="✅nextjs에서-ssr을-실행하는-과정과-hydration에-대해-설명해-주세요">✅Next.js에서 SSR을 실행하는 과정과 hydration에 대해 설명해 주세요.</h3>
<p>SSR에서는 서버가 요청을 받으면 React 컴포넌트를 서버에서 렌더링해 HTML을 클라이언트로 전송합니다. 클라이언트는 이 HTML을 초기 화면으로 사용하고, 그 이후에 React가 클라이언트에서 해당 HTML을 재사용하며 인터랙티브 기능을 활성화하는 과정을 hydration이라고 합니다.</p>
<h3 id="✅cors-에러에-대해-설명하고-어떻게-해결하면-될지-설명해-주세요">✅CORS 에러에 대해 설명하고, 어떻게 해결하면 될지 설명해 주세요.</h3>
<p>CORS(Cross-Origin Resource Sharing) 에러는 클라이언트가 다른 도메인으로 요청할 때 서버가 해당 도메인에서 오는 요청을 허용하지 않아 발생합니다. 이를 해결하려면 서버에서 응답 헤더에 Access-Control-Allow-Origin을 설정해 허용할 도메인을 명시해야 합니다.</p>
<blockquote>
<ul>
<li>먼저 백엔드 서버 개발자에게 에러가 발생하는 출처를 허용하도록 Access-Control-Allow-Origin 설정을 요청하는 방법이 있습니다.</li>
<li>CORS 허용을 위한 설정 요청을 할 수 없는 경우, 동일 출처 정책을 강제하지 않는 proxy 서버를 활용해 우회하는 방법이 있습니다. Next.js의 경우 <code>rewrites</code> 를 활용할 수 있습니다</li>
</ul>
</blockquote>
<h3 id="✅seo가-무엇인지-설명하고-개선을-위해-어떤-작업을-할-수-있을지-설명해-주세요">✅SEO가 무엇인지 설명하고, 개선을 위해 어떤 작업을 할 수 있을지 설명해 주세요.</h3>
<p>SEO(Search Engine Optimization)는 검색 엔진에서 웹사이트의 가시성을 높이기 위한 작업입니다. SEO 개선을 위해 메타 태그 작성, 시맨틱한 HTML 사용, 페이지 로딩 속도 최적화, 서버 사이드 렌더링(SSR) 등을 활용할 수 있습니다.</p>
<h3 id="✅클로저가-무엇인지-그리고-javascript에서-어떤-경우에-활용하면-좋을지-예시와-함께-설명해-주세요">✅클로저가 무엇인지 그리고 JavaScript에서 어떤 경우에 활용하면 좋을지 예시와 함께 설명해 주세요.</h3>
<p>클로저는 함수가 생성될 때 자신의 스코프 외부에 있는 변수를 기억하고, 그 변수를 함수가 종료된 후에도 접근할 수 있는 개념입니다. 예를 들어, 함수 내부에서 변수를 캡처하여 반환하는 패턴으로, 비공개 데이터를 저장하거나 함수 상태를 유지할 때 유용합니다.</p>
<pre><code class="language-js">function makeCounter() {
  let count = 0;
  return function () {
    return ++count;
  };
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2</code></pre>
<h3 id="✅세션-기반-인증과-토큰-기반-인증을-비교해서-설명해-주세요">✅세션 기반 인증과 토큰 기반 인증을 비교해서 설명해 주세요.</h3>
<ul>
<li>세션 기반 인증: 서버가 사용자 로그인 정보를 서버에 저장하고 세션 ID를 브라우저 쿠키로 전달해 인증합니다. 서버 측 부담이 큽니다.</li>
<li>토큰 기반 인증: 로그인 시 서버가 JWT 같은 토큰을 발급하고, 클라이언트가 이후 요청에 이 토큰을 보내 인증합니다. 서버는 상태를 저장하지 않아 확장성이 좋습니다.</li>
</ul>
<h3 id="✅authorization-code를-활용하는-구글-소셜-로그인을-실행하기까지-유저-프론트엔드-백엔드-openid-connect-프로바이더-사이에-어떤-과정을-거치는지-설명해-주세요">✅Authorization Code를 활용하는 구글 소셜 로그인을 실행하기까지 유저, 프론트엔드, 백엔드, OpenID Connect 프로바이더 사이에 어떤 과정을 거치는지 설명해 주세요.</h3>
<ol>
<li>유저가 프론트엔드에서 구글 로그인 버튼을 클릭합니다.</li>
<li>프론트엔드가 구글의 OpenID Connect 프로바이더에 인증 코드를 요청합니다.</li>
<li>유저는 구글 로그인 페이지에서 인증을 완료하고, 구글이 프론트엔드에 Authorization Code를 반환합니다.</li>
<li>프론트엔드는 이 코드를 백엔드로 전송하고, 백엔드는 구글에 이 코드를 제출해 액세스 토큰과 ID 토큰을 받습니다.</li>
<li>백엔드는 받은 토큰을 통해 유저 정보를 확인하고, 로그인 처리를 완료합니다.</li>
</ol>
<h3 id="✅react-query가-만들어진-이유와-react-query를-사용할-때-얻게-되는-이점에-대해-설명해-주세요">✅React Query가 만들어진 이유와 React Query를 사용할 때 얻게 되는 이점에 대해 설명해 주세요.</h3>
<p>React Query는 서버 상태 관리의 복잡성을 줄이기 위해 만들어졌습니다. 서버 데이터의 캐싱, 동기화, 업데이트를 자동으로 처리하며, 비동기 요청의 로딩, 에러 처리, 데이터 패칭을 쉽게 관리할 수 있습니다. 이를 통해 코드의 복잡성이 줄고 성능이 향상됩니다.</p>
<h3 id="✅서버-상태와-클라이언트-상태의-차이에-대해-설명해-주세요">✅서버 상태와 클라이언트 상태의 차이에 대해 설명해 주세요.</h3>
<ul>
<li>서버 상태: 서버에 저장된 데이터로, 클라이언트에서 요청을 통해 가져와야 하는 상태입니다. 변경 사항은 서버와 동기화가 필요합니다.</li>
<li>클라이언트 상태: 사용자 인터페이스와 관련된 데이터로, 서버와 상관없이 클라이언트에서만 관리하는 상태입니다.</li>
</ul>
<h3 id="✅storybook을-사용할-때-기대할-수-있는-장점에-대해-설명해-주세요">✅Storybook을 사용할 때 기대할 수 있는 장점에 대해 설명해 주세요.</h3>
<p>Storybook을 사용하면 컴포넌트를 독립적으로 개발하고 테스트할 수 있으며, 팀원과 컴포넌트 라이브러리를 공유하기 쉽습니다. 또한, 각 컴포넌트의 다양한 상태를 미리보기로 확인할 수 있어 개발 효율성과 협업이 개선됩니다.</p>
<h3 id="✅테스트-코드를-작성하면서-개발할-때-장점과-단점에-대해-설명해-주세요">✅테스트 코드를 작성하면서 개발할 때 장점과 단점에 대해 설명해 주세요.</h3>
<ul>
<li>장점: 코드의 안정성이 높아지고, 리팩토링 시 오류를 미리 방지할 수 있으며, 유지보수가 쉬워집니다.</li>
<li>단점: 초기 개발 속도가 느려질 수 있으며, 테스트 코드 유지에도 시간이 필요합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코어자바스크립트] 2-2. 2-3. VariableEnvironment, LexicalEnvironment]]></title>
            <link>https://velog.io/@bori_note/%EC%BD%94%EC%96%B4%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-2-2.-2-3.-VariableEnvironment-LexicalEnvironment</link>
            <guid>https://velog.io/@bori_note/%EC%BD%94%EC%96%B4%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-2-2.-2-3.-VariableEnvironment-LexicalEnvironment</guid>
            <pubDate>Wed, 24 Apr 2024 11:56:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>실행 컨텍스트는 두 가지 주요 구성 요소인 Lexical Environment와 Variable Environment로 이루어져 있다.
<img src="https://velog.velcdn.com/images/bori_note/post/5c1d030e-da6d-4244-95e9-1731de3f409b/image.png" alt=""></p>
</blockquote>
<h1 id="2-2variableenvironment">2-2.VariableEnvironment</h1>
<blockquote>
<ul>
<li>VariableEnvironment 에 담기는 내용은 LexicalEnvironment와 같지만, 최초 실행 시의 스냅샷을 유지한다는 점이 다르다.</li>
</ul>
</blockquote>
<ul>
<li>실행 컨텍스트를 처음 생성할 때 VariableEnvironment에 정보를 먼저 담고, 이를 그대로 복사해서 LexicalEnvironment를 만들고, 이후에는 LexicalEnvironment를 주로 활용한다.</li>
<li>내부는 environmentRecord와 outer-EnvironmentReference로 구성되어있다.</li>
</ul>
<h2 id="📘역할">📘역할</h2>
<ul>
<li>VariableEnvironment는 변수 선언(var, let, const)과 관련된 정보를 저장한다.</li>
<li>변수 선언 시 VariableEnvironment에 해당 변수의 정보가 저장된다.</li>
<li>VariableEnvironment는 변수의 값을 관리하고 접근할 수 있게 한다.</li>
</ul>
<h2 id="📘구조">📘구조</h2>
<ul>
<li>VariableEnvironment는 environmentRecord와 outer 속성으로 구성된다.</li>
<li>environmentRecord는 변수 선언 정보를 저장한다.</li>
<li>outer 속성은 상위 VariableEnvironment에 대한 참조를 가진다.<h2 id="📘활용">📘활용</h2>
</li>
<li>VariableEnvironment를 통해 변수의 값을 관리하고 접근할 수 있다.</li>
<li>변수 선언 및 할당, 변수 값 참조 등의 작업이 VariableEnvironment를 통해 이루어진다.</li>
</ul>
<h1 id="2-3-lexicalenvironment">2-3. LexicalEnvironment</h1>
<h2 id="📘역할-1">📘역할</h2>
<ul>
<li>코드의 구문 구조와 관련된 정보를 저장한다.</li>
<li>함수 선언, 변수 선언 등 코드의 구조적 정보가 LexicalEnvironment에 저장된다.</li>
<li>변수와 함수의 스코프 정보를 관리한다.</li>
</ul>
<h2 id="📍environmentrecord와-호이스팅">📍environmentRecord와 호이스팅</h2>
<ul>
<li>environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.</li>
<li>컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집한다.</li>
<li>실행 컨텍스트가 코드를 실행하기 전에 이미 변수 정보를 모두 수집함. 즉, 코드 실행 전에도 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명들을 모두 알고있다. -&gt; 호이스팅<blockquote>
<p><strong>호이스팅</strong></p>
<ul>
<li>변수와 함수 선언이 코드 실행 전에 메모리 공간에 할당되는 현상</li>
<li>변수 선언의 경우, var 키워드로 선언된 변수는 undefined로 초기화되지만, let과 const 키워드로 선언된 변수는 초기화되지 않는다.</li>
<li>함수 선언의 경우, 함수 전체가 메모리에 할당되지만, 함수 표현식은 호이스팅되지 않는다.</li>
</ul>
</blockquote>
</li>
</ul>
<h3 id="🔖변수-호이스팅">🔖변수 호이스팅</h3>
<pre><code class="language-js">function a (x) { 
    console.log(x); //------------ (1)
    var x;
    console.log(x); //------------ (2)
    var x = 2;
    console.log(x); //------------ (3)
}

a(1)</code></pre>
<pre><code>1
undefined
2</code></pre><p>를 예상했지만?</p>
<p>실제 결과는?</p>
<pre><code>1
1
2</code></pre><pre><code class="language-js">function a (x) { 
    var x;  // 수집 대상 1의 변수 선언 부분
    var x;  // 수집 대상 2의 변수 선언 부분
    var x;  // 수집 대상 3의 변수 선언 부분

    x = 1;  // 수집 대상 1의 할당 부분
    console.log(x); //------------ (1)
    console.log(x); //------------ (2)
    x = 2; // 수집 대상 2의 할당 부분
    console.log(x); //------------ (3)
}

a(1)</code></pre>
<p>호이스팅을 할 때 변수는 선언부와 할당부를 나누어 ‘선언부&#39;만 끌어올린다!!</p>
<h2 id="📍함수-선언문과-함수-표현식">📍함수 선언문과 함수 표현식</h2>
<p>함수선언문</p>
<ul>
<li>function 정의부만 존재하고 별도의 할당 명령이 없는 것</li>
<li>함수명이 반드시 정의되어 있어야 함</li>
</ul>
<p>함수표현식</p>
<ul>
<li>정의한 function을 별도의 변수에 할당하는 것</li>
<li>함수명 없어도 됨<blockquote>
<p>기명 함수표현식: 함수명 정의한 것
익명 함수표현식: 함수명 정의 안 한 것</p>
</blockquote>
</li>
</ul>
<h3 id="❓함수-선언문과-함수-표현식의-호이스팅-차이">❓함수 선언문과 함수 표현식의 호이스팅 차이</h3>
<p>함수 선언문</p>
<ul>
<li>호이스팅의 영향을 받습니다.</li>
<li>변수 선언과 함께 함수 전체가 호이스팅되어 함수를 선언하기 전에도 호출할 수 있다.<pre><code class="language-js">console.log(a()); // Output: &quot;Hello, world!&quot;
</code></pre>
</li>
</ul>
<p>function a() {
  return &quot;Hello, world!&quot;;
}</p>
<pre><code>함수 표현식
- 호이스팅의 영향을 받지 않습니다.
- 변수 선언만 호이스팅되고, 함수 할당은 호이스팅되지 않는다.
- 함수 표현식을 사용하는 경우 함수를 선언하기 전에는 호출할 수 없다.
```js
console.log(a()); // TypeError: a is not a function

var a = function() {
  return &quot;Hello, world!&quot;;
};</code></pre><h2 id="📍스코프-스코프-체인-outerenvironmentreference">📍스코프, 스코프 체인, outerEnvironmentReference</h2>
<h3 id="📘스코프">📘스코프</h3>
<ul>
<li>식별자(변수, 함수)에 대한 유효범위</li>
<li>ES5 까지의 자바스크립트는 전역 공간을 제외하면, 오직 함수에 의해서만 스코프가 생성된다.</li>
</ul>
<h3 id="📘스코프의-종류">📘스코프의 종류</h3>
<p>전역 스코프(Global Scope)</p>
<ul>
<li>전역 변수: 전역 공간에서 선언한 변수(어디서든 접근할 수 있는 변수)</li>
<li>전역 함수: 어디서든 호출할 수 있는 함수<blockquote>
<p>전역 스코프는 피하는 것이 좋다. 변수 충돌, 메모리 누수 등의 문제가 발생할 수 있다.</p>
</blockquote>
</li>
</ul>
<pre><code class="language-js">var globalVar = &#39;Hello, global!&#39;;

function globalFunc() {
  console.log(globalVar); // &#39;Hello, global!&#39;
}

globalFunc();</code></pre>
<p>지역 스코프(Local Scope)</p>
<ul>
<li>지역 변수: 함수 내부에서 선언한 변수</li>
<li>함수 스코프: 함수 내부에서만 유효한 변수와 함수</li>
<li>블록 스코프: { } 내부에서만 유효한 변수와 함수 (ES6 let, const 도입)<blockquote>
<p>지역 스코프를 사용하면 변수 충돌을 방지하고 메모리 관리를 효율적으로 할 수 있다.</p>
</blockquote>
</li>
</ul>
<pre><code class="language-js">function localFunc() {
  var localVar = &#39;Hello, local!&#39;;
  console.log(localVar); // &#39;Hello, local!&#39;
}

localFunc();
console.log(localVar); // ReferenceError: localVar is not defined</code></pre>
<p>렉시컬 스코프(Lexical Scope)</p>
<ul>
<li>함수가 정의된 위치에 따라 결정되는 스코프</li>
<li>함수 내부에서 외부 변수에 접근할 수 있다.</li>
</ul>
<pre><code class="language-js">var x = 5;

function outer() {
  var x = 10;
  function inner() {
    console.log(x); // 10
  }
  inner();
}

outer();
console.log(x); // 5</code></pre>
<p>inner() 함수는 outer() 함수 내부에 정의되어 있기 때문에, inner() 함수 내부의 x는 outer() 함수의 지역 변수 x를 참조한다. </p>
<h3 id="📗스코프-체인">📗스코프 체인</h3>
<ul>
<li>변수와 함수를 찾는 과정에서 스코프가 중첩되어 계층적으로 구성된 구조</li>
<li>식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것 -&gt; outerEnvironmentReference가 함<pre><code class="language-js">var x = 5; // 전역 스코프
</code></pre>
</li>
</ul>
<p>function outer() {
  var x = 10; // outer 함수 스코프
  function inner() {
    console.log(x); // 10 (outer 함수 스코프의 x)
  }
  inner();
}</p>
<p>outer();
console.log(x); // 5 (전역 스코프의 x)</p>
<p>```</p>
<ol>
<li>전역 변수 x가 선언</li>
<li>outer() 함수 내부에서 지역 변수 x가 선언</li>
<li>inner() 함수 내부에서 x를 참조하면, 가장 가까운 스코프인 outer() 함수 스코프의 x를 찾아 10을 출력한다.</li>
<li>outer() 함수 실행이 끝나면 전역 스코프로 돌아간다.</li>
<li>전역 변수 x의 값인 5가 출력된다.</li>
</ol>
<blockquote>
<p>스코프 체인은 변수와 함수를 찾는 과정에서 스코프가 중첩되어 계층적으로 구성된 구조다. 자바스크립트 엔진은 코드를 실행하기 전에 스코프 체인을 구성하며, 변수와 함수를 찾을 때 스코프 체인을 따라 올라가며 검색한다.
의도치 않은 변수 값 변경을 방지하고 효율적인 메모리 관리를 할 수 있다. 또한 렉시컬 스코프 규칙에 따라 함수가 선언될 때의 환경을 기반으로 변수와 함수를 찾는 방식을 사용한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코어자바스크립트] 2-1. 실행컨텍스트란?]]></title>
            <link>https://velog.io/@bori_note/%EC%BD%94%EC%96%B4%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-2-1.-%EC%8B%A4%ED%96%89%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%9E%80</link>
            <guid>https://velog.io/@bori_note/%EC%BD%94%EC%96%B4%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-2-1.-%EC%8B%A4%ED%96%89%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%9E%80</guid>
            <pubDate>Wed, 24 Apr 2024 02:54:38 GMT</pubDate>
            <description><![CDATA[<h1 id="✨실행컨텍스트란">✨실행컨텍스트란?</h1>
<ul>
<li>실행할 코드에 제공할 환경 정보들을 모아놓은 객체</li>
<li>동일 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고, 이를 콜 스택(call stack)에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다.</li>
<li>실행 컨텍스트를 구성하는 방법: <strong>함수를 실행하는 것</strong></li>
</ul>
<p>실행 컨텍스트와 콜 스택 예시)</p>
<pre><code class="language-js">var a = 1;
function outer() {
    function inner() {
        console.log(a); // undefined
        var a = 3;
    }
    inner();
    console.log(a);  // 1
}
outer();
console.log(a);    // 1</code></pre>
<p><img src="https://velog.velcdn.com/images/bori_note/post/d658f103-0986-4511-a2d6-cd454f91cd9e/image.png" alt=""></p>
<ol>
<li>전역 실행 컨텍스트가 생성되고, 전역 변수 a가 1로 초기화</li>
<li>outer 함수가 호출되면 새로운 실행 컨텍스트가 생성된다. </li>
<li>inner 함수가 정의되고, inner 함수 실행 컨텍스트가 outer 함수 실행 컨텍스트 내부에 생성된다.</li>
<li>inner 함수가 호출되면 새로운 실행 컨텍스트가 생성된다. 
 inner 함수 내부에서 a 변수가 선언되지만 초기화되지 않아 undefined가 출력된다. 
 이후 a 변수가 3으로 초기화된다.</li>
<li>inner 함수 실행이 종료되면 해당 실행 컨텍스트가 제거된다.</li>
<li>outer 함수 내부의 console.log(a)에서 전역 변수 a의 값인 1이 출력된다.</li>
<li>outer 함수 실행이 종료되면 해당 실행 컨텍스트가 제거된다.</li>
<li>전역 실행 컨텍스트에서 console.log(a)가 실행되어 전역 변수 a의 값인 1이 출력된다.</li>
</ol>
<blockquote>
<p>전역컨텍스트</p>
</blockquote>
<ul>
<li>전역 컨텍스트는 자바스크립트 코드 실행의 시작점이 되는 실행 컨텍스트</li>
<li>브라우저에서는 window 객체가, Node.js에서는 global 객체가 전역 컨텍스트를 나타낸다.</li>
</ul>
<p>실행컨텍스트가 활성화될 때 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는 데 필요한 환경 정보들을 수집해서 실행 컨텍스트에 저장한다.</p>
<ul>
<li>VariableEnvironment: 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보
선언시점의 LexicalEnvironment의 스냅샷으로, 변경사항은 반영되지 않음. </li>
<li>LexicalEnvironment: 처음에는 VariableEnvironment와 같지만 변경사항이 실시간으로 반영됨 </li>
<li>ThisBinding: this 식별자가 바라봐야 할 대상 객체. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[8주차 위클리 페이퍼]]></title>
            <link>https://velog.io/@bori_note/8%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</link>
            <guid>https://velog.io/@bori_note/8%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</guid>
            <pubDate>Tue, 23 Apr 2024 03:21:45 GMT</pubDate>
            <description><![CDATA[<h1 id="❓-웹-페이지-렌더링-방식-csr-ssr-ssg-각각의-특징과-각-방식을-어떤-상황에-사용하면-좋을지-설명해-주세요">❓ 웹 페이지 렌더링 방식 CSR, SSR, SSG 각각의 특징과 각 방식을 어떤 상황에 사용하면 좋을지 설명해 주세요.</h1>
<h2 id="📍클라이언트-사이드-렌더링-csr-client-side-rendering">📍클라이언트 사이드 렌더링 (CSR, Client-Side Rendering)</h2>
<ul>
<li>브라우저에서 JavaScript를 실행하여 페이지를 렌더링하는 방식</li>
<li>초기 로딩 시간이 느리지만, 이후 동적 업데이트가 빠름</li>
<li>검색 엔진 최적화(SEO)가 어려움</li>
<li>단일 페이지 애플리케이션(SPA)에 적합<blockquote>
<p>단일 페이지 애플리케이션(SPA), 동적 UI 업데이트가 중요한 경우</p>
</blockquote>
</li>
</ul>
<h2 id="📍서버-사이드-렌더링-ssr-server-side-rendering">📍서버 사이드 렌더링 (SSR, Server-Side Rendering)</h2>
<ul>
<li>서버에서 HTML을 생성하여 클라이언트에 전달하는 방식</li>
<li>초기 로딩 속도가 빠르고 SEO에 유리</li>
<li>서버 부하가 증가할 수 있음</li>
<li>동적 업데이트가 상대적으로 느림<blockquote>
<p>검색 엔진 최적화(SEO)가 중요한 경우, 초기 로딩 속도가 중요한 경우</p>
</blockquote>
</li>
</ul>
<h2 id="📍정적-사이트-생성-ssg-static-site-generation">📍정적 사이트 생성 (SSG, Static Site Generation)</h2>
<ul>
<li>빌드 시점에 HTML 파일을 생성하여 제공하는 방식</li>
<li>초기 로딩 속도가 매우 빠르고 SEO에 유리</li>
<li>동적 콘텐츠 업데이트가 어려움</li>
<li>정적 웹사이트에 적합<blockquote>
<p>정적 콘텐츠 중심의 웹사이트, 빠른 초기 로딩 속도가 중요한 경우</p>
</blockquote>
</li>
</ul>
<h1 id="❓-본인이-생각하는-css-in-js의-장점과-단점을-설명해-주세요">❓ 본인이 생각하는 CSS-in-JS의 장점과 단점을 설명해 주세요.</h1>
<blockquote>
<p>CSS-in-JS란?</p>
</blockquote>
<ul>
<li>CSS-in-JS는 JavaScript 코드 내에서 CSS 스타일을 작성하는 방식</li>
</ul>
<p>예시) styled component</p>
<pre><code class="language-js">import styled from &#39;styled-components&#39;;

const Button = styled.button`
  background-color: blue;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  font-size: 16px;
  cursor: pointer;

  &amp;:hover {
    background-color: darkblue;
  }
`;

function App() {
  return (
    &lt;div&gt;
      &lt;Button&gt;Click me&lt;/Button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>장점</p>
<ul>
<li>컴포넌트 단위로 스타일을 관리할 수 있어 유지보수가 용이</li>
<li>동적 스타일 적용이 쉬움</li>
<li>코드 스플리팅을 통한 성능 향상</li>
<li>스타일 충돌 방지</li>
</ul>
<p>단점</p>
<ul>
<li>초기 로딩 시간이 길어질 수 있음</li>
<li>개발자 경험이 기존 CSS 방식과 다름</li>
<li>서버 사이드 렌더링(SSR) 시 추가 작업이 필요</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[P3_S5] 쿠키, 세션 스토리지, 로컬 스토리지 이해하기]]></title>
            <link>https://velog.io/@bori_note/P3S5-%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%EB%A1%9C%EC%BB%AC-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bori_note/P3S5-%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%EB%A1%9C%EC%BB%AC-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 20 Apr 2024 02:38:35 GMT</pubDate>
            <description><![CDATA[<h1 id="✨쿠키">✨쿠키</h1>
<p>사용자가 성공적인 로그인을 하면 서버는 클라이언트에 인증서를 보낸다. 클라이언트가 이 인증서를 가져오면 어떤 사람인지 구별하는 것이다. 클라이언트는 리퀘스트를 보낼 때마다 이 인증서를 함께 보내야 한다. → 이럴 때 쿠키를 쓴다.</p>
<ul>
<li>서버로부터 리스폰스로 쿠키를 받으면, 클라이언트에서는 별도로 작업을 해주지 않아도 알아서 웹 브라우저가 알아서 저장하고 리퀘스트를 보낼 때도 웹 브라우저가 알아서 보낸다.</li>
<li>자바스크립트를 통해서 쿠키 값을 추가, 수정, 참조할 수 있다.</li>
<li>수명을 지정할 수 있다. 수명이 다한 쿠키는 알아서 지워진다.</li>
</ul>
<h2 id="📍사용법"><strong>📍사용법</strong></h2>
<h3 id="📘서버에서-리스폰스할-때"><strong>📘서버에서 리스폰스할 때</strong></h3>
<p>서버에서 <code>set-cookie</code> 헤더를 리스폰스로 보내주면 웹 브라우저는 알아서 쿠키 값을 저장한다.</p>
<pre><code>
Set-Cookie: session-id=1234; Domain=codeit.kr; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=2592000;
</code></pre><p>여기서 맨 앞의 <code>session-id</code> 는 쿠키의 Key에 해당하는 값</p>
<p><code>1234</code> 는 Value에 해당하는 값</p>
<p><code>domain</code>, <code>path</code>, <code>samesite</code> 등은 쿠키의 Attribute에 해당하는 값 (헤더 이름이나 Attribute 이름들은 대소문자를 구분하지 않기 때문에 <code>set-cookie</code>, <code>domain</code>, <code>path</code>, <code>samesite</code> 로 쓸 수도 있음)</p>
<h3 id="📘클라이언트에서-보낼-때"><strong>📘클라이언트에서 보낼 때</strong></h3>
<p>리퀘스트를 보낼 때 주소에 해당하는 쿠키가 저장되어 있다면 웹 브라우저가 알아서 쿠키를 보내준다.</p>
<pre><code>
Cookie: session-id=1234; Domain=codeit.kr; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=2592000;
</code></pre><h3 id="📘자바스크립트로-사용하기"><strong>📘자바스크립트로 사용하기</strong></h3>
<p>보통은 서버가 쿠키 값을 만들고, 클라이언트에서는 웹 브라우저에 맡기고 건드리지 않는 것이 권장된다. 하지만 클라이언트에서 자바스크립트로 쿠키를 생성/수정/참조 할 수도 있다.</p>
<ul>
<li><p>추가, 수정하기</p>
<p>  변수 할당처럼 보이지만 이 코드는 새로운 쿠키를 추가하거나 </p>
<pre><code class="language-jsx">
  document.cookie = &quot;session-id=1234; Domain=codeit.kr; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=2592000;&quot;;
</code></pre>
</li>
<li><p>값 참조하기</p>
<ul>
<li><p><code>document.cookie</code> 값을 참조하면 모든 쿠키의 키와 값이 <code>;</code> 문자로 구분된 문자열</p>
</li>
<li><p><code>split()</code> 함수를 사용해서 직접 분리해서 사용하거나, <a href="https://stackoverflow.com/questions/10730362/get-cookie-by-name">스택오버플로우</a>사용</p>
<p>  <a href="https://stackoverflow.com/questions/10730362/get-cookie-by-name">Get cookie by name</a></p>
</li>
<li><p><strong><a href="https://www.npmjs.com/package/cookie">cookie</a></strong> 라는 NPM 패키지를 활용할 수도 있다.</p>
</li>
</ul>
</li>
<li><p>값 지우기</p>
<p>  <code>Max-Age</code> 값을 0으로 업데이트하면 (수명이 0) 지워진다.</p>
<pre><code class="language-jsx">
  document.cookie = &quot;session-id; Max-Age: 0;&quot;;</code></pre>
<h2 id="📍알아두면-좋은-attribute"><strong>📍알아두면 좋은 Attribute</strong></h2>
<h3 id="📘domain"><strong>📘Domain</strong></h3>
<p>  브라우저가 쿠키를 보낼 도메인을 지정한다. </p>
<p>  예를 들어서 <code>Domain=codeit.kr</code> 이라고 하면 <code>https://codeit.kr</code> 은 물론이고 <code>https://abc.codeit.kr</code>, <code>https://xyz.abc.codeit.kr</code> 같은 서브 도메인에 리퀘스트를 할 때도 쿠키를 보낸다.</p>
<h3 id="📘path"><strong>📘Path</strong></h3>
<p>  브라우저가 쿠키를 보낼 경로를 지정한다. </p>
<p>  예를 들어서 <code>Path=/</code> 라고 하면 <code>/</code> 아래에 있는 경로들, <code>/abc</code>, <code>/abc/xyz</code> 같은 경로에 리퀘스트를 보낼 때에도 모두 쿠키를 보낸다.</p>
<h3 id="📘httponly-⭐️"><strong>📘HttpOnly ⭐️</strong></h3>
<p>  <code>document.cookie</code> 값을 자바스크립트로 사용할 수 있으면, 해커들이 악성 코드를 사용자들에게 유포해 쿠키를 훔칠 수 있다.</p>
<p>  로그인 정보 같은 <strong>민감한 정보는 되도록이면 자바스크립트로 조작하지 말기.</strong> </p>
<p>  <code>HttpOnly</code> 를 사용하면 쿠키를 자바스크립트로 사용하지 못하게 막을 수 있다.</p>
<h3 id="📘secure-⭐️"><strong>📘Secure ⭐️</strong></h3>
<p>  <code>Secure</code> 를 지정하면 HTTPS 리퀘스트를 보낼 때만 쿠키를 보낸다. (<code>SameSite=None</code> 을 지정하면 반드시 <code>Secure</code> 도 함께 지정해야 함)</p>
<h3 id="📘samesite-⭐️"><strong>📘SameSite ⭐️</strong></h3>
<p>  자바스크립트를 사용하면 현재 사이트에서 다른 사이트로 리퀘스트를 보낼 수 있고, 사이트의 링크를 클릭하면 웹 브라우저는 다른 사이트로 GET 리퀘스트를 보내며 이동한다. 그리고 이미지 파일을 다운 받거나 할 때도 GET 리퀘스트를 보내서 받는다.</p>
<p>  <code>codeit.kr</code> 이라는 사이트에서 쿠키를 발급해서 사용자가 저장하고 있는데, 사용자가 <code>hacker-site.com</code> 이라는 악성 사이트에 접속한다고 하면) </p>
<p>  이 악성 사이트는 <code>codeit.kr</code> 이라는 사이트로 리퀘스트를 보내서 개인정보를 탈취하는 사이트 → 악성 사이트에서 코드잇으로 리퀘스트를 보낼 때 쿠키가 함께 간다면 개인정보를 탈취당한다. → <strong><a href="https://ko.wikipedia.org/wiki/%EC%82%AC%EC%9D%B4%ED%8A%B8_%EA%B0%84_%EC%9A%94%EC%B2%AD_%EC%9C%84%EC%A1%B0">크로스 사이트 요청 위조 (Cross-site request forgery, CSRF, XSRF)</a></strong></p>
<p>  CSRF를 방지하기 위해서 제3자 사이트(Third-party site)에서 쿠키를 보내지 못하게 <code>SameSite</code> 를 지정해야 한다.</p>
<p>  <code>SameSite</code> 속성의 값에는 <code>Strict</code>, <code>Lax</code> (아무것도 지정하지 않았을 때 기본값), <code>None</code>이 있다. </p>
<table>
<thead>
<tr>
<th></th>
<th>SameSite=Strict</th>
<th>SameSite=Lax</th>
<th>SameSite=None</th>
</tr>
</thead>
<tbody><tr>
<td>다른 사이트에서 우리 사이트로 리퀘스트를 보낼 때</td>
<td>X</td>
<td>X</td>
<td>O</td>
</tr>
<tr>
<td>다른 사이트에서 이미지 파일 등을 받을 때</td>
<td>X</td>
<td>X</td>
<td>O</td>
</tr>
<tr>
<td>다른 사이트에서 사용자가 링크를 클릭해 우리 사이트로 이동할 때</td>
<td>X</td>
<td>O</td>
<td>O</td>
</tr>
<tr>
<td>우리 사이트에서 우리 사이트로 리퀘스트를 보낼 때</td>
<td>O</td>
<td>O</td>
<td>O</td>
</tr>
</tbody></table>
<h3 id="📘expires-와-max-age-⭐️"><strong>📘<code>Expires</code> 와 <code>Max-Age</code> ⭐️</strong></h3>
<p>  쿠키의 수명을 지정하는 속성</p>
<p>  <code>Expires</code>로 만료될 시기를 지정하거나, <code>Max-Age</code> 로 쿠키의 수명을 지정할 수 있다.</p>
<ul>
<li><strong>세션 쿠키(Session Cookie)</strong>는 <code>Expires</code> 나 <code>Max-Age</code> 를 지정하지 않으면 만들 수 있다. (세션 쿠키는 브라우저를 닫으면 지워지는 쿠키)</li>
<li><strong>영속적인 쿠키(Persistent Cookie)</strong>는 <code>Expries</code> 나 <code>Max-Age</code> 로 수명을 지정해서 만들 수 있다. (수명이 다하면 지워지는 쿠키)</li>
</ul>
</li>
</ul>
<p><a href="https://www.youtube.com/watch?v=OpoVuwxGRDI">쿠키, 세션, 캐시가 뭔가요?</a></p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies">HTTP 쿠키 - HTTP | MDN</a></p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Set-Cookie">Set-Cookie - HTTP | MDN</a></p>
<p><a href="https://seob.dev/posts/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%BF%A0%ED%82%A4%EC%99%80-SameSite-%EC%86%8D%EC%84%B1/">브라우저 쿠키와 SameSite 속성 / seob.dev</a></p>
<p><a href="https://blog.pumpkin-raccoon.com/80">react-cookie 쉽게 사용하기</a></p>
<p><a href="https://owasp.org/www-community/attacks/csrf">Cross Site Request Forgery (CSRF) | OWASP Foundation</a></p>
<h1 id="✨세션-스토리지와-로컬-스토리지"><strong>✨세션 스토리지와 로컬 스토리지</strong></h1>
<p>쿠키는 서버에서 만들고, 클라이언트는 거의 건드리지 않는다. 그런데 사이트에 따라서는 클라이언트에서만 사용하는 데이터인데, 저장해 놓고 사용하고 싶은 경우가 있다.</p>
<p>→ 세션 스토리지(Session Storage)와 로컬 스토리지(Local Stroage)</p>
<h2 id="📘세션-스토리지"><strong>📘세션 스토리지</strong></h2>
<ul>
<li>현재 탭에서만 유효한 저장소</li>
<li>탭을 닫으면 데이터가 사라진다.</li>
<li>다른 탭과 데이터는 공유되지 않는다.</li>
</ul>
<pre><code class="language-jsx">// 값을 저장하는 코드. (이미 값이 있다면) 수정하는 코드
const data = inputElement.value;
sessionStorage.setItem(&#39;draft&#39;, data);</code></pre>
<pre><code class="language-jsx">// 값을 참조해서 사용할 때
const draftData = sessionStorage.getItem(&#39;draft&#39;);</code></pre>
<pre><code class="language-jsx">// 값 지우기
sessionStorage.removeItem(&#39;draft&#39;);</code></pre>
<h2 id="📘로컬-스토리지"><strong>📘로컬 스토리지</strong></h2>
<ul>
<li>해당 사이트에서 유효한 저장소</li>
<li>탭을 닫거나 브라우저를 닫아도 데이터가 유지된다.</li>
<li>여러 탭 사이에서도 데이터가 공유된다.</li>
</ul>
<pre><code class="language-jsx">// 사용자가 사이드바 감추기 버튼을 클릭했을 때
// 값을 저장, 수정
localStorage.setItem(&#39;show-sidebar&#39;, &#39;false&#39;);</code></pre>
<pre><code class="language-jsx">
// 사용자가 처음 접속했을 때
const showSidebar = localStorage.getItem(&#39;show-sidebar&#39;) === &#39;true&#39;;</code></pre>
<pre><code class="language-jsx">// 값 지우기
localStorage.removeItem(&#39;show-sidebar&#39;);</code></pre>
<h2 id="❓쿠키와-세션-로컬-스토리지의-다른-점"><strong>❓쿠키와 세션, 로컬 스토리지의 다른 점?</strong></h2>
<ul>
<li>클라이언트가 만들고 관리하는 데이터</li>
<li>자바스크립트로 편리하게 조작할 수 있다.</li>
<li>만료 기간(수명)이 없다</li>
<li>쿠키보다 사용할 수 있는 용량이 크다.</li>
</ul>
<h2 id="📘스토리지가-변경되었을-때-처리하기"><strong>📘스토리지가 변경되었을 때 처리하기</strong></h2>
<p>스토리지가 변경되었을 때 <code>&#39;storage&#39;</code> 라는 이벤트가 발생한다.</p>
<pre><code class="language-jsx">window.addEventListener(&quot;storage&quot;, () =&gt; {
  const showSidebar = localStorage.getItem(&#39;show-sidebar&#39;) === &#39;true&#39;;
    // showSidebar 값 적용하기
});</code></pre>
<p><a href="https://developer.mozilla.org/ko/docs/Web/API/Web_Storage_API">Web Storage API - Web API | MDN</a></p>
<p><a href="https://web.dev/i18n/ko/storage-for-the-web/">웹용 스토리지</a></p>
<p><a href="https://ko.javascript.info/localstorage">localStorage와 sessionStorage</a></p>
<h1 id="✨활용-사례">✨활용 사례</h1>
<h2 id="📍쿠키">📍쿠키</h2>
<p><strong>로그인</strong></p>
<p>(세션 기반 인증의 경우) </p>
<p>처음에 세션 ID를 쿠키로 보낸다. 로그인에 성공하면 서버 쪽에서 세션 상태를 업데이트한다.</p>
<p>(토큰 기반 인증의 경우) </p>
<p>로그인에 성공하면 서버가 토큰을 발급해 쿠키로 보내준다.</p>
<p><strong>하루동안 팝업 다시 보지 않기</strong></p>
<p>하루 동안 다시 보지 않기’를 체크하고 닫으면 클라이언트가 수명이 1일인 쿠키를 만든다.</p>
<p>다음에 접속했을 때는 이 쿠키를 확인해서 팝업을 보여주지 않는다.</p>
<p>👇🏻만약 로컬 스토리지로 구현한다면?👇🏻</p>
<p><a href="https://www.sohamkamani.com/javascript/localstorage-with-ttl-expiry/">How to Set Expiry Time (TTL) for LocalStorage With Javascript</a></p>
<p><strong>장바구니</strong></p>
<p>(세션 기반 인증을 사용하는 경우) </p>
<p>처음 세션 ID를 쿠키로 보낸다. 이 세션 ID를 기반으로 서버에서 장바구니 정보를 저장한다.</p>
<h2 id="📍세션-스토리지-로컬-스토리지">📍세션 스토리지, 로컬 스토리지</h2>
<p><strong>초안(draft) 임시 저장하기</strong></p>
<p>텍스트를 입력하다가 실수로 창을 닫아 버리더라도 내용이 남아있게 할 수 있다.</p>
<p><strong>웹 앱</strong></p>
<p>웹 기반으로 만들어진 프로그램(웹 앱)들은 세션, 로컬 스토리지를 적극적으로 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[7주차 위클리 페이퍼]]></title>
            <link>https://velog.io/@bori_note/7%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</link>
            <guid>https://velog.io/@bori_note/7%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</guid>
            <pubDate>Tue, 16 Apr 2024 05:24:59 GMT</pubDate>
            <description><![CDATA[<h1 id="❓리액트에서-배열을-렌더링할-때-key를-써야-하는-이유에-대해-설명해-주세요">❓리액트에서 배열을 렌더링할 때 key를 써야 하는 이유에 대해 설명해 주세요.</h1>
<ul>
<li>Key는 React에서 컴포넌트 리스트를 렌더링할 때 각 항목을 고유하게 식별하는 데 사용된다.</li>
<li>각 요소에 고유한 key 값을 부여함으로써 리액트는 각 요소를 안정적으로 식별할 수 있다. </li>
<li>요소의 추가, 삭제, 변경 등의 변화를 효과적으로 감지할 수 있다.</li>
<li>배열의 고유한 식별자(예: id)를 key 값으로 사용하는 것이 가장 좋다.</li>
</ul>
<p><strong>key를 사용하지 않으면❓</strong></p>
<ul>
<li>배열의 변화를 잘 감지하지 못해 전체 배열을 다시 렌더링할 수 있다.</li>
<li>배열의 순서가 변경되면 React는 각 항목을 새로운 위치에 렌더링할 때 이전 위치와의 비교를 통해 업데이트한다. 하지만 key 값이 없는 경우 React는 각 항목을 고유하게 식별할 수 없어서 예기치 않은 결과가 발생할 수 있다.<pre><code class="language-jsx">import React from &#39;react&#39;;
</code></pre>
</li>
</ul>
<p>function ExampleComponent() {
  const items = [&#39;A&#39;, &#39;B&#39;, &#39;C&#39;];</p>
<p>  const listItems = items.map((item, index) =&gt;
    <li key={index}>
      {item}
    </li>
  );</p>
<p>  return (
    <ul>
      {listItems}
    </ul>
  );
}</p>
<p>export default ExampleComponent;</p>
<pre><code>
# ❓리액트 생명주기(life cycle)에 대해 설명해 주세요.
React 컴포넌트의 라이프 사이클은 컴포넌트가 생성, 업데이트, 소멸되는 일련의 단계를 말한다. 라이프 사이클 메소드는 이러한 단계에서 실행되는 함수들을 말하며, 클래스형 컴포넌트에서 주로 사용된다.
![](https://velog.velcdn.com/images/bori_note/post/cd021bb8-6d86-4388-8369-eb2ccff28cf9/image.png)


## 1. Mounting (생성 단계)
&gt;컴포넌트가 처음 렌더링될 때 발생하는 단계

- constructor(): 컴포넌트가 생성될 때 호출되는 생성자 메소드.
- static getDerivedStateFromProps(): props를 통해 파생된 상태를 설정할 때 사용되는 메소드.
- render(): UI를 렌더링하는 메소드.
- componentDidMount(): 컴포넌트가 실제 DOM에 추가된 후 호출되는 메소드.


## 2. Updating (업데이트 단계)
&gt;컴포넌트가 업데이트될 때 발생하는 단계

- static getDerivedStateFromProps(): props를 통해 파생된 상태를 업데이트할 때 호출.
- shouldComponentUpdate(): 컴포넌트가 업데이트 여부를 결정하는 메소드.
- render(): UI를 업데이트하는 메소드.
- getSnapshotBeforeUpdate(): 업데이트 전에 이전의 DOM 상태를 캡처하는 메소드.
- componentDidUpdate(): 컴포넌트가 업데이트를 완료한 후 호출되는 메소드.

## 3. Unmounting (소멸 단계)
&gt;컴포넌트가 DOM에서 제거될 때 발생하는 단계

- componentWillUnmount(): 컴포넌트가 제거되기 전에 호출되는 메소드.

## 4. Error Handling (에러 처리 단계)
&gt;컴포넌트 렌더링 중 에러가 발생할 때 호출되는 단계

- static getDerivedStateFromError: 하위 컴포넌트에서 오류가 발생할 때 호출.
- componentDidCatch: 하위 컴포넌트에서 발생한 오류를 처리하기 위해 호출.

```jsx
// 실행했을 때의 순서: -1-, -2- ...
// 버튼 클릭 후의 순서: (1), (2) ...
import React, { Component } from &#39;react&#39;;

class LifeCycleExample extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
    // 컴포넌트가 생성될 때 호출되는 생성자 메서드 -1-
    console.log(&#39;Constructor called&#39;); 
  }

  static getDerivedStateFromProps(props, state) {
    // props로부터 state를 도출할 때 호출되는 메서드 -2- (1)
    console.log(&#39;getDerivedStateFromProps called&#39;); 
    return null;
  }

  componentDidMount() {
    // 컴포넌트가 마운트된 직후 호출되는 메서드 -4-
    console.log(&#39;componentDidMount called&#39;); 
  }

  shouldComponentUpdate(nextProps, nextState) {
    // 컴포넌트 업데이트 여부를 결정할 때 호출되는 메서드  (2)
    console.log(&#39;shouldComponentUpdate called&#39;); 
    return true;
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 실제 DOM에 변화가 반영되기 직전 호출되는 메서드  (4)
    console.log(&#39;getSnapshotBeforeUpdate called&#39;); 
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 컴포넌트 업데이트 직후 호출되는 메서드 -6-  (5)
    console.log(&#39;componentDidUpdate called&#39;);
  }

  componentWillUnmount() {
    // 컴포넌트가 언마운트되기 직전 호출되는 메서드 -5-
    console.log(&#39;componentWillUnmount called&#39;); 
  }

  handleClick = () =&gt; {
    this.setState((prevState) =&gt; ({
      count: prevState.count + 1,
    }));
  };

  render() {
    // 컴포넌트가 렌더링될 때 호출되는 메서드 -3-  (3)
    console.log(&#39;Render called&#39;); 
    return (
      &lt;div&gt;
        &lt;h1&gt;React Lifecycle Example&lt;/h1&gt;
        &lt;p&gt;Count: {this.state.count}&lt;/p&gt;
        &lt;button onClick={this.handleClick}&gt;Increment&lt;/button&gt;
      &lt;/div&gt;
    );
  }
}

export default LifeCycleExample;
</code></pre><p><a href="https://ko.legacy.reactjs.org/docs/react-component.html#the-component-lifecycle">https://ko.legacy.reactjs.org/docs/react-component.html#the-component-lifecycle</a></p>
<h2 id="📍함수형-컴포넌트와-hooks">📍함수형 컴포넌트와 Hooks</h2>
<p>최근에는 함수형 컴포넌트와 Hooks가 많이 사용되고 있다. 함수형 컴포넌트에서는 생명주기 메서드 대신 useEffect Hook을 사용하여 비슷한 작업을 수행할 수 있다. </p>
<blockquote>
<p>useEffect는 컴포넌트가 마운트, 업데이트, 언마운트될 때 특정 작업을 수행할 수 있게 해준다. <br>
예) useEffect의 두 번째 인자로 빈 배열을 전달하면 마운트 시에만 실행되는 것과 유사한 효과를 얻을 수 있다. 또한 의존성 배열을 통해 특정 state 변화에 반응하도록 할 수 있다.</p>
</blockquote>
<p>이처럼 함수형 컴포넌트와 Hooks를 사용하면 생명주기 메서드를 직접 사용하지 않고도 컴포넌트의 상태 관리와 부수 효과 처리가 가능하다.</p>
<pre><code class="language-jsx">import React, { useState, useEffect } from &#39;react&#39;;

function LifeCycleHooksExample() {
  const [count, setCount] = useState(0);

  useEffect(() =&gt; {
    // 컴포넌트가 마운트되거나 업데이트될 때 호출되는 useEffect
    console.log(&#39;Component mounted or updated&#39;); 

    return () =&gt; {
      // 컴포넌트가 언마운트될 때 호출되는 cleanup 함수
      console.log(&#39;Component will unmount&#39;); 
    };
  }, []);

  useEffect(() =&gt; {
    // count state가 변경될 때 호출되는 useEffect
    console.log(&#39;Count state changed&#39;);
  }, [count]);

  const handleClick = () =&gt; {
    setCount(count + 1);
  };

  return (
    &lt;div&gt;
      &lt;h1&gt;React Lifecycle Hooks Example&lt;/h1&gt;
      &lt;p&gt;Count: {count}&lt;/p&gt;
      &lt;button onClick={handleClick}&gt;Increment&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default LifeCycleHooksExample;
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[P3_S4] Styled-Components]]></title>
            <link>https://velog.io/@bori_note/P3S4-Styled-Components</link>
            <guid>https://velog.io/@bori_note/P3S4-Styled-Components</guid>
            <pubDate>Mon, 15 Apr 2024 11:37:28 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-jsx">npm install styled-components</code></pre>
<h1 id="✨nesting-문법">✨Nesting 문법</h1>
<ul>
<li>CSS 규칙 안에서 CSS 규칙을 만드는 것.</li>
</ul>
<h2 id="📘--선택자"><strong>📘 <code>&amp;</code> 선택자</strong></h2>
<p><code>&amp;</code> 선택자를 사용해서 앞에서 만든 버튼 컴포넌트를 호버하거나 클릭했을 때 배경색이 바뀌게👇🏻👇🏻</p>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5426&amp;directory=4-1.gif&amp;name=4-1.gif">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5426&amp;directory=4-1.gif&amp;name=4-1.gif</a></p>
<p>src/Button.js</p>
<pre><code class="language-jsx">
import styled from &#39;styled-components&#39;;

const Button = styled.button`
  background-color: #6750a4;
  border: none;
  color: #ffffff;
  padding: 16px;

  &amp;:hover,
  &amp;:active {
    background-color: #463770;
  }
`;

export default Button;
</code></pre>
<ul>
<li>Nesting에서 <code>&amp;</code>는 부모 선택자를 의미한다.</li>
<li>위 코드에서는 버튼 컴포넌트의 클래스를 뜻하는 건데요.</li>
<li>버튼 컴포넌트가 <code>.Button</code>이라는 클래스 이름을 쓸 때 <code>&amp;:hover</code>는 <code>.Button:hover</code>와 같은 의미</li>
</ul>
<pre><code class="language-css">
.Button {
  background-color: #6750a4;
  border: none;
  color: #ffffff;
  padding: 16px;
}

.Button:hover,
.Button:active {
  background-color: #463770;
}
</code></pre>
<h2 id="📘컴포넌트-선택자"><strong>📘컴포넌트 선택자</strong></h2>
<p>Styled Components에선 클래스 이름을 쓰지 않는다. 그럼 컴포넌트 안에 있는 또 다른 컴포넌트를 선택하고 싶으면 어떻게 해야 할까?</p>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5426&amp;directory=4-2.gif&amp;name=4-2.gif">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5426&amp;directory=4-2.gif&amp;name=4-2.gif</a></p>
<ul>
<li>컴포넌트를 선택자로 쓰고 싶을 때는 <code>${Icon}</code>같이 컴포넌트 자체를 템플릿 리터럴 안에 넣어주면 된다.</li>
</ul>
<pre><code class="language-jsx">
import styled from &#39;styled-components&#39;;
import nailImg from &#39;./nail.png&#39;;

const Icon = styled.img`
  width: 16px;
  height: 16px;
`;

const StyledButton = styled.button`
  background-color: #6750a4;
  border: none;
  color: #ffffff;
  padding: 16px;

  &amp; ${Icon} {
    margin-right: 4px;
  }

  &amp;:hover,
  &amp;:active {
    background-color: #463770;
  }
`;

function Button({ children, ...buttonProps }) {
  return (
    &lt;StyledButton {...buttonProps}&gt;
      &lt;Icon src={nailImg} alt=&quot;nail icon&quot; /&gt;
      {children}
    &lt;/StyledButton&gt;
  );
}

export default Button;
</code></pre>
<p>자손 결합자(Descendant Combinator)로 쓴 <code>&amp; ${Icon} { ... }</code> 부분을 기존 CSS로 표현해 본다면 아래처럼 나타낼 수 있다. </p>
<pre><code class="language-css">
.StyledButton {
  ...
}

.StyledButton .Icon {
  margin-right: 4px;
}
</code></pre>
<p>특히, <code>&amp;</code>와 자손 결합자를 사용하는 경우에는 <code>&amp;</code>를 생략할 수 있다. 즉 <code>${Icon}</code>만 써도 똑같이 동작한다. .</p>
<pre><code class="language-jsx">
 const StyledButton = styled.button`
  background-color: #6750a4;
  border: none;
  color: #ffffff;
  padding: 16px;

  ${Icon} {
    margin-right: 4px;
  }

  &amp;:hover,
  &amp;:active {
    background-color: #463770;
  }
`;
</code></pre>
<p>참고로 Nesting은 여러 겹으로 할 수도 있다.</p>
<pre><code class="language-jsx">
const StyledButton = styled.button`
  ...
  &amp;:hover,
  &amp;:active {
    background-color: #7760b4;

    ${Icon} {
      opacity: 0.2;
    }
  }
`;
</code></pre>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5426&amp;directory=4-3.gif&amp;name=4-3.gif">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5426&amp;directory=4-3.gif&amp;name=4-3.gif</a></p>
<p><code>&amp;:hover, &amp;:active { ... }</code> 안에 있는 <code>${Icon}</code> 선택자를 CSS 코드로 표현해 보면 </p>
<pre><code class="language-css">
.StyledButton:hover .Icon,
.StyledButton:active .Icon {
  opacity: 0.5;
}</code></pre>
<h1 id="✨다이나믹-스타일링">✨다이나믹 스타일링</h1>
<h2 id="📘---안에-값변수-사용하기"><strong>📘<code>${ ... }</code> 안에 값(변수) 사용하기</strong></h2>
<ul>
<li>가장 기본적인 사용법은 JavaScript 변수를 그대로 넣는 방식</li>
</ul>
<pre><code class="language-jsx">const a = 1;
const b = 2;
const str = `${a} 더하기 ${b}는 ${a + b} 입니다.`;</code></pre>
<p>아래 예시 코드에서 <code>${SIZES[&#39;medium&#39;]}</code> 부분은 숫자 20을 뜻하기 때문에, <code>font-size: ${SIZES[&#39;medium&#39;]}px;</code>는 <code>font-size: 20px;</code>란 코드가 된다.</p>
<pre><code class="language-jsx">
const SIZES = {
  large: 24,
  medium: 20,
  small: 16
};

const Button = styled.button`
  ...
  font-size: ${SIZES[&#39;medium&#39;]}px;
`;
</code></pre>
<h2 id="📘---안에-함수-사용하기"><strong>📘<code>${ ... }</code> 안에 함수 사용하기</strong></h2>
<ul>
<li>Prop에 따라 스타일을 다르게 적용하는 함수를 넣기</li>
<li>함수의 파라미터로는 Props를 받고, 리턴 값으로는 스타일 코드를 리턴하면 된다.</li>
</ul>
<pre><code class="language-jsx">
const SIZES = {
  large: 24,
  medium: 20,
  small: 16
};

const Button = styled.button`
  ...
  font-size: ${(props) =&gt; SIZES[props.size]}px;
`;
</code></pre>
<p>만약에 구조 분해(Destructuring)하면 아래처럼 쓸 수 있다.</p>
<pre><code class="language-jsx">font-size: ${({ size }) =&gt; SIZES[size]}px;</code></pre>
<p><code>size</code> Prop이 값이 없거나 잘못된 값이면 어떻게 될까? </p>
<p>Styled Components에서는 <code>undefined</code> 값을 빈 문자열로 처리해 주기 때문에 <code>font-size: px</code> 같은 잘못된 CSS 코드가 된다. 그래서 가능하면 기본 값을 정해주는 게 좋다. (널 병합 연산자 쓰기..)</p>
<pre><code class="language-jsx">
font-size: ${({ size }) =&gt; SIZES[size] ?? SIZES[&#39;medium&#39;]}px;</code></pre>
<h2 id="📘논리-연산자-사용하기"><strong>📘논리 연산자 사용하기</strong></h2>
<p>함수를 사용할 때 많이 사용하는 패턴 중 하나는 논리 연산자를 사용하는 것. </p>
<p>예를 들어서, <code>round</code>라는 Prop이 참일 때 컴포넌트의 모서리를 둥글게 만듦</p>
<pre><code class="language-jsx">
const Button = styled.button`
  ...
  ${({ round }) =&gt; round &amp;&amp; `
      border-radius: 9999px;
    `}
`;
</code></pre>
<p><code>round</code> 값이 참이면 그 뒤에 값까지 계산하기 때문에 <code>border-radius: 9999px</code>이라는 문자열이 리턴돼서 적용된다. 반대로, <code>round</code> 값이 거짓이면 그냥 <code>false</code>가 리턴돼서 아무런 값도 적용되지 않는다. </p>
<h2 id="📘삼항-연산자-사용하기"><strong>📘삼항 연산자 사용하기</strong></h2>
<p><code>round</code> 가 참이면 완전히 둥근 모서리를 보여주고, 거짓이면 <code>3px</code> 정도로 살짝 부드럽게 깎인 모서리를 보여주고 싶다면 아래와 같이 삼항 연산자로 쓸 수 있다.</p>
<pre><code class="language-jsx">
border-radius: ${({ round }) =&gt; round ? `9999px` : `3px`};</code></pre>
<h1 id="✨스타일-재사용-상속">✨스타일 재사용: 상속</h1>
<p>HTML 태그에 스타일링하는 건 <code>styled.tagname</code>을 사용해서 할 수 있다. 그런데, JSX 문법으로 직접 만든 컴포넌트나, Styled Components를 사용해 이미 만들어진 다른 컴포넌트에 스타일을 입히려면 어떻게 해야 할까? → 상속</p>
<h2 id="📘styled-함수"><strong>📘<code>styled()</code> 함수</strong></h2>
<p>Styled Components로 만들어진 컴포넌트를 상속하려면 <code>styled()</code> 함수를 사용하면 된다. </p>
<p>src/Button.js</p>
<pre><code class="language-jsx">
import styled from &#39;styled-components&#39;;

const SIZES = {
  large: 24,
  medium: 20,
  small: 16,
};

const Button = styled.button`
  background-color: #6750a4;
  border: none;
  color: #ffffff;
  font-size: ${({ size }) =&gt; SIZES[size] ?? SIZES[&#39;medium&#39;]}px;
  padding: 16px;

  ${({ round }) =&gt;
    round
      ? `
      border-radius: 9999px;
    `
      : `
      border-radius: 3px;
    `}

  &amp;:hover,
  &amp;:active {
    background-color: #463770;
  }
`;

export default Button;
</code></pre>
<p>src/App.js</p>
<pre><code class="language-jsx">
import styled from &#39;styled-components&#39;;
import Button from &#39;./Button&#39;;

const SubmitButton = styled(Button)`
  background-color: #de117d;
  display: block;
  margin: 0 auto;
  width: 200px;

  &amp;:hover {
    background-color: #f5070f;
  }
`;

function App() {
  return (
    &lt;div&gt;
      &lt;SubmitButton&gt;계속하기&lt;/SubmitButton&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5431&amp;directory=9-1.png&amp;name=9-1.png">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5431&amp;directory=9-1.png&amp;name=9-1.png</a></p>
<p><code>Button</code> 컴포넌트의 스타일을 상속해서 새로운 버튼 <code>SubmitButton</code>을 만들고, <code>App</code> 컴포넌트 안에 <code>SubmitButton</code>을 배치하고 있다.</p>
<p><code>styled(Button)</code></p>
<p><code>SubmitButton</code>이 <code>Button</code>의 스타일을 상속받게 된다. <code>Button</code> 컴포넌트에 <code>SubmitButton</code>의 스타일이 상속됐기 때문에, 마찬가지로 글씨는 흰색이다.</p>
<h2 id="📘jsx로-직접-만든-컴포넌트에-styled-사용하기"><strong>📘JSX로 직접 만든 컴포넌트에 <code>styled()</code> 사용하기</strong></h2>
<p><code>styled.tagname</code>으로 만든 컴포넌트는 바로 <code>styled()</code> 함수를 사용할 수 있지만, 그렇지 않은 컴포넌트는 따로 처리가 필요하다. </p>
<p>예시) 약관을 보여주는 <code>TermsOfService</code>라는 컴포넌트가 있으면</p>
<p>src/TermsOfService.js</p>
<pre><code class="language-jsx">
function TermsOfService() {
  return (
    &lt;div&gt;
      &lt;h1&gt;㈜코드잇 서비스 이용약관&lt;/h1&gt;
      &lt;p&gt;
        환영합니다.
        &lt;br /&gt;
        Codeit이 제공하는 서비스를 이용해주셔서 감사합니다. 서비스를
        이용하시거나 회원으로 가입하실 경우 본 약관에 동의하시게 되므로, 잠시
        시간을 내셔서 주의 깊게 살펴봐 주시기 바랍니다.
      &lt;/p&gt;
      &lt;h2&gt;제 1 조 (목적)&lt;/h2&gt;
      &lt;p&gt;
        본 약관은 ㈜코드잇이 운영하는 기밀문서 관리 프로그램인 Codeit에서
        제공하는 서비스를 이용함에 있어 이용자의 권리, 의무 및 책임사항을
        규정함을 목적으로 합니다.
      &lt;/p&gt;
    &lt;/div&gt;
  );
}

export default TermsOfService;
</code></pre>
<p><code>TermsOfService</code>는 JSX 문법을 직접 사용해서 바로 컴포넌트가 만들어졌다. </p>
<p>이 컴포넌트를 <code>styled()</code> 함수로 감싸면</p>
<p>src/App.js</p>
<pre><code class="language-jsx">
import styled from &#39;styled-components&#39;;
import Button from &#39;./Button&#39;;
import TermsOfService from &#39;./TermsOfService&#39;;

const StyledTermsOfService = styled(TermsOfService)`
  background-color: #ededed;
  border-radius: 8px;
  padding: 16px;
  margin: 40px auto;
  width: 400px;
`;

const SubmitButton = styled(Button)`
  background-color: #de117d;
  display: block;
  margin: 0 auto;
  width: 200px;

  &amp;:hover {
    background-color: #f5070f;
  }
`;

function App() {
  return (
    &lt;div&gt;
      &lt;StyledTermsOfService /&gt;
      &lt;SubmitButton&gt;계속하기&lt;/SubmitButton&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5431&amp;directory=9-2.png&amp;name=9-2.png">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5431&amp;directory=9-2.png&amp;name=9-2.png</a></p>
<p><code>styled()</code>로 지정한 스타일이 적용되지 않는다.</p>
<p><code>StyledTermsOfService</code>에 지정한 배경색이랑 너비가 적용이 안 된 거 같다. 왜 그럴까?</p>
<p>Styled Components는 내부적으로 <code>className</code>을 따로 생성합니다. 그리고, 자체적으로 생성된 <code>className</code>이 있는 부분에 <code>styled()</code> 함수의 스타일이 입혀진다.</p>
<p>그런데, JSX 문법으로 직접 만든 컴포넌트는 <code>styled()</code> 함수가 적용될 <code>className</code>에 대한 정보가 없다. <code>styled()</code> 함수에서 지정한 스타일이 입혀질 부분이 어딘지 알 수 없으니 스타일이 적용되지 않은 것이다.</p>
<p>이렇게, Styled Components를 사용하지 않고 직접 만든 컴포넌트는 <code>className</code> 값을 Prop으로 따로 내려줘야 <code>styled()</code> 함수를 사용할 수 있다. </p>
<p>src/TermsOfService.js</p>
<pre><code class="language-jsx">
function TermsOfService({ className }) {
  return (
    &lt;div className={className}&gt;
      ...
    &lt;/div&gt;
  );
}
</code></pre>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5431&amp;directory=9-3.png&amp;name=9-3.png">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5431&amp;directory=9-3.png&amp;name=9-3.png</a></p>
<p>직접 만든 컴포넌트에 <code>className</code> Prop을 따로 내려주는 건 <code>syled()</code> 함수가 적용될 부분의 <code>className</code>을 별도로 정해주는 거라고 이해하자. </p>
<p>위 코드의 경우엔, <code>&lt;div&gt;</code> 태그에 <code>className</code>을 내려줬기 때문에 <code>styled(TermsOfService)</code>에서 작성한 코드는 <code>TermsOfService</code> 안에 있는 <code>&lt;div&gt;</code> 태그에 적용된다.</p>
<p>정리하자면, 스타일 상속을 하려면 <code>styled()</code> 함수를 사용하면 되는데, <code>styled.tagname</code>으로 만든 컴포넌트는 <code>styled()</code> 함수로 바로 상속하면 되고, Styled Components를 사용하지 않고 직접 만든 컴포넌트에는 클래스 이름을 내려준 후에 <code>styled()</code> 함수로 상속해야 한다.</p>
<h1 id="✨스타일-재사용-css-함수">✨스타일 재사용: css 함수</h1>
<p>가끔 중복되는 CSS 코드들을 변수처럼 저장해서 여러 번 다시 사용하고 싶을 때가 있다. → css 함수</p>
<p><code>Button</code> 컴포넌트와 <code>Input</code> 컴포넌트에 같은 글자 크기를 갖도록 하는 상황을 생각해보자. </p>
<p><code>size</code>라는 Prop으로 <code>small</code>, <code>medium</code>, <code>large</code> 각각에 지정된 크기를 전달하면 16, 20, 24 픽셀로 글자 크기를 지정하려 한다. 가장 단순한 방법은 아래처럼 똑같은 코드를 두 번 작성하는 형태가 될 것이다.</p>
<pre><code class="language-jsx">
import styled from &#39;styled-components&#39;;

const SIZES = {
  large: 24,
  medium: 20,
  small: 16
};

const Button = styled.button`
  ...
  font-size: ${({ size }) =&gt; SIZES[size] ?? SIZES[&#39;medium&#39;]}px;
`;

const Input = styled.input`
  ...
  font-size: ${({ size }) =&gt; SIZES[size] ?? SIZES[&#39;medium&#39;]}px;
`;</code></pre>
<pre><code class="language-jsx">import styled, { css } from &#39;styled-components&#39;;

const SIZES = {
  large: 24,
  medium: 20,
  small: 16
};

const fontSize = css`
  font-size: ${({ size }) =&gt; SIZES[size] ?? SIZES[&#39;medium&#39;]}px;
`;

const Button = styled.button`
  ...
  ${fontSize}
`;

const Input = styled.input`
  ...
  ${fontSize}
`;
</code></pre>
<p>일반적인 템플릿 리터럴을 쓰는 게 아니라 <code>css</code>라는 태그 함수를 붙여서 쓴다는 점!!! </p>
<p>Props를 받아서 사용하는 함수가 들어있기 때문에 <strong>반드시</strong> <code>css</code> 함수를 사용해야 한다.</p>
<p>만약에 아래 코드처럼 함수를 삽입하지 않는 단순한 문자열이라면 일반적인 템플릿 리터럴을 써도 된다.</p>
<pre><code class="language-jsx">
const boxShadow = `
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
`;
</code></pre>
<p>하지만, 이런 경우에도 항상 <code>css</code> 함수를 사용하도록 습관화하는 걸 권장 드립니다.</p>
<pre><code class="language-jsx">
const boxShadow = css`
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
`;</code></pre>
<h1 id="✨글로벌-스타일">✨글로벌 스타일</h1>
<p>CSS를 작성하다 보면, 모든 컴포넌트에 적용하고 싶은 코드가 생기는 경우가 있다. ( 폰트나 <code>box-sizing: border-box</code> ) </p>
<p>→ 글로벌 스타일 컴포넌트를 사용하자. 글로벌 스타일 컴포넌트를 최상위 컴포넌트에서 렌더링 하면 글로벌 스타일이 항상 적용된 상태가 되도록 할 수 있다.</p>
<pre><code class="language-jsx">import { createGlobalStyle } from &#39;styled-components&#39;;

const GlobalStyle = createGlobalStyle`
  * {
    box-sizing: border-box;
  }

  body {
    font-family: &#39;Noto Sans KR&#39;, sans-serif;
  }
`;

function App() {
  return (
    &lt;&gt;
      &lt;GlobalStyle /&gt;
      &lt;div&gt;글로벌 스타일&lt;/div&gt;
    &lt;/&gt;
  );
}

export default App;</code></pre>
<p><code>createGlobalStyle</code>이라는 함수는 다른 Styled Components 함수들과 마찬가지로 템플릿 리터럴 문법으로 사용한다. 이 함수는 <code>&lt;style&gt;</code> 태그를 컴포넌트로 만드는 것이다. 실제로 <code>&lt;style&gt;</code> 태그가 저 위치에 생기는 건 아니고, Styled Components가 내부적으로 처리해서 <code>&lt;head&gt;</code> 태그 안에 우리가 작성한 CSS 코드를 넣어 준다.</p>
<h1 id="✨애니메이션">✨애니메이션</h1>
<h2 id="📘키프레임이란"><strong>📘키프레임이란?</strong></h2>
<p>영상과 애니메이션은 여러 개의 사진을 연속으로 보여주면서 마치 움직이는 듯한 효과를 만들어 낸다. 이때 연속으로 보여주는 한 장 한 장의 이미지를 프레임이라고 한다. 이때 &#39;움직임의 기준이 되는 프레임&#39;을 &#39;키프레임&#39;이라고 부른다.</p>
<p>CSS에서 키프레임은 CSS 애니메이션을 만들 때 기준이 되는 지점을 정하고, 적용할 CSS 속성을 지정하는 문법을 뜻한다. </p>
<p>예시) 아래 HTML/CSS 코드는 <code>.ball</code>이라는 <code>&lt;div&gt;</code> 태그를 위아래로 움직이는 애니메이션이다. 시작 지점에서는 기본값인 <code>translateY(0%)</code>를 적용하고, 애니메이션의 중간 시점에서는 <code>translateY(100%)</code>를 적용한 다음, 마지막에는 기본값인 <code>translateY(0%)</code> 을 적용하고 있다.</p>
<pre><code class="language-html">&lt;div class=&quot;ball&quot;&gt;&lt;/div&gt;</code></pre>
<pre><code class="language-jsx">@keyframes bounce {
  0% {
    transform: translateY(0%);
  }

  50% {
    transform: translateY(100%);
  }

  100% {
    transform: translateY(0%);
  }
}

.ball {
  animation: bounce 1s infinite;
  background-color: #ff0000;
  border-radius: 50%;
  height: 50px;
  width: 50px;
}
</code></pre>
<p><code>@keyframes</code>로 키프레임 애니메이션을 선언한 다음에, <code>animation</code> 속성에서 이름으로 쓰고 있다.</p>
<h2 id="📘keyframes-함수"><strong>📘<code>keyframes</code> 함수</strong></h2>
<p>Styled Components에서는 키프레임 애니메이션을 어떻게 넣을 수 있을까? </p>
<p>플레이스홀더 애니메이션은 사이트에서 보여줄 내용을 로딩하는 동안 내용이 들어갈 자리에 미리 네모나 동그라미 같은 걸 보여주면서, 애니메이션으로 로딩 중이라는 걸 보여주는 것이다.</p>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5437&amp;directory=15-2.gif&amp;name=15-2.gif">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5437&amp;directory=15-2.gif&amp;name=15-2.gif</a></p>
<p>플레이스홀더 애니메이션을 HTML/CSS 코드로 간단히 만들어보면 아래와 같다.</p>
<pre><code class="language-html">
&lt;div class=&quot;placeholder&quot;&gt;
  &lt;div class=&quot;placeholder-item a&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;placeholder-item b&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;placeholder-item c&quot;&gt;&lt;/div&gt;
&lt;/div
</code></pre>
<pre><code class="language-css">
@keyframes placeholder-glow {
  50% {
    opacity: 0.2;
  }
}

.placeholder {
  animation: placeholder-glow 2s ease-in-out infinite;
}

.placeholder-item {
  background-color: #888888;
  height: 20px;
  margin: 8px 0;
}

.a {
  width: 60px;
  height: 60px;
  border-radius: 50%;
}

.b {
  width: 400px;
}

.c {
  width: 200px;
}
</code></pre>
<p>여기서 <code>placeholder-glow</code>라는 애니메이션 코드는 애니메이션의 중간인 50% 시점에 0.2의 불투명도로 만드는 것이다. 불투명도의 기본값이 1이니까, 불투명도가 0.2로 낮아졌다가 다시 1로 높아지는 애니메이션이 된다.</p>
<p>Styled Components 버전)</p>
<p><code>@keyframes</code>는 <code>keyframes</code>라는 함수를 쓰면 된다. <code>styled</code> 함수와 마찬가지로 템플릿 리터럴로 사용하는 태그 함수다. 여기서 특히 <code>keyframes</code>로 만든 애니메이션을 <code>${placeholderGlow}</code>처럼 템플릿 리터럴에 삽입하는 형태로 사용했다는 점!!</p>
<p>src/Placeholder.js</p>
<pre><code class="language-jsx">
import styled, { keyframes } from &#39;styled-components&#39;;

const placeholderGlow = keyframes`
  50% {
    opacity: 0.2;
  }
`;

export const PlaceholderItem = styled.div`
  background-color: #888888;
  height: 20px;
  margin: 8px 0;
`;

const Placeholder = styled.div`
  animation: ${placeholderGlow} 2s ease-in-out infinite;
`;

export default Placeholder;
</code></pre>
<p>src/App.js</p>
<pre><code class="language-jsx">
import styled from &#39;styled-components&#39;;
import Placeholder, { PlaceholderItem } from &#39;./Placeholder&#39;;

const A = styled(PlaceholderItem)`
  width: 60px;
  height: 60px;
  border-radius: 50%;
`;

const B = styled(PlaceholderItem)`
  width: 400px;
`;

const C = styled(PlaceholderItem)`
  width: 200px;
`;

function App() {
  return (
    &lt;div&gt;
      &lt;Placeholder&gt;
        &lt;A /&gt;
        &lt;B /&gt;
        &lt;C /&gt;
      &lt;/Placeholder&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>참고로, <code>keyframes</code> 함수가 리턴하는 변수는 단순한 문자열이 아니라 JavaScript 객체다. 리턴되는 값이 이런 객체이기 때문에 반드시 <code>styled</code> 함수나 <code>css</code> 함수를 통해 사용해야 한다!</p>
<pre><code class="language-jsx">{
    id: &quot;sc-keyframes-bEnYbJ&quot;
    inject: ƒ (e, t)
    name: &quot;bEnYbJ&quot;
    rules: &quot;\n  50% {\n    opacity: 0.2;\n  }\n&quot;
    toString: ƒ ()
}</code></pre>
<h1 id="✨테마">✨테마</h1>
<h2 id="📘themeprovider로-테마-설정-사용하기"><strong>📘<code>ThemeProvider</code>로 테마 설정 사용하기</strong></h2>
<p>테마 기능을 만들기 위해서는 현재 테마로 설정된 값을 사이트 전체에서 참조할 수 있어야 한다. React에서는 이런 상황에서 Context라는 걸 사용한다. Styled Components에서도 Context를 기반으로 테마를 사용할 수 있다. Context를 내려주는 컴포넌트로 <code>ThemeProvider</code>라는 걸 사용하면 된다.</p>
<p>App.js</p>
<pre><code class="language-jsx">
import { ThemeProvider } from &quot;styled-components&quot;;
import Button from &quot;./Button&quot;;

function App() {
  const theme = {
    primaryColor: &#39;#1da1f2&#39;,
  };

  return (
    &lt;ThemeProvider theme={theme}&gt;
      &lt;Button&gt;확인&lt;/Button&gt;
    &lt;/ThemeProvider&gt;
  );
}

export default App;
</code></pre>
<p><code>ThemeProvider</code>라는 Context Provider를 사용해서 <code>theme</code>이라는 객체를 내려준다. 이렇게 하면 <code>ThemeProvider</code> 안에 있는 Styled Components로 만든 컴포넌트에서는 Props를 사용하듯이 <code>theme</code>이라는 객체를 쓸 수 있다.</p>
<p>예를 들어서 <code>Button</code> 컴포넌트에서 <code>theme</code> 값을 써 볼게요. Prop 값을 사용하듯이  <code>theme</code>이라는 값을 쓰면 된다. 기존에 있던 배경색 대신에 아래처럼 함수를 삽입해서 테마 값을 사용한다. </p>
<p>src/Button.js</p>
<pre><code class="language-jsx">
const Button = styled.button`
  background-color: ${({ theme }) =&gt; theme.primaryColor};
  /* ... */
`;
</code></pre>
<p>만약에 여러 테마를 선택하게 하고 싶다면 <code>useState</code> 를 활용해서 테마를 바꿔주면 된다.</p>
<pre><code class="language-jsx">
import { useState } from &#39;react&#39;;
import { ThemeProvider } from &#39;styled-components&#39;;
import Button from &#39;./Button&#39;;

function App() {
  const [theme, setTheme] = useState({
    primaryColor: &#39;#1da1f2&#39;,
  });

  const handleColorChange = (e) =&gt; {
    setTheme((prevTheme) =&gt; ({
      ...prevTheme,
      primaryColor: e.target.value,
    }));
  };

  return (
    &lt;ThemeProvider theme={theme}&gt;
      &lt;select value={theme.primaryColor} onChange={handleColorChange}&gt;
        &lt;option value=&quot;#1da1f2&quot;&gt;blue&lt;/option&gt;
        &lt;option value=&quot;#ffa800&quot;&gt;yellow&lt;/option&gt;
        &lt;option value=&quot;#f5005c&quot;&gt;red&lt;/option&gt;
      &lt;/select&gt;
      &lt;br /&gt;
      &lt;br /&gt;
      &lt;Button&gt;확인&lt;/Button&gt;
    &lt;/ThemeProvider&gt;
  );
}

export default App;
</code></pre>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5439&amp;directory=17-2.gif&amp;name=17-2.gif">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5439&amp;directory=17-2.gif&amp;name=17-2.gif</a></p>
<p>그런데, 만약 테마 설정 페이지를 만든다고 하면 테마 값을 일반적인 컴포넌트에서 참조할 필요도 생길 것이다. 그럴 때는 <code>ThemeContext</code>를 불러오면 된다. 이 값은 React Context이기 때문에 <code>useContext</code>로 쓴다.</p>
<pre><code class="language-jsx">
import { useContext } from &#39;react&#39;;
import { ThemeContext } from &#39;styled-components&#39;;

// ...

function SettingPage() {
  const theme = useContext(ThemeContext); // { primaryColor: &#39;#...&#39; }
}</code></pre>
<h1 id="💡tip">💡tip</h1>
<h2 id="📍버튼-모양-링크가-필요할-때"><strong>📍버튼 모양 링크가 필요할 때</strong></h2>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5441&amp;directory=19-1.png&amp;name=19-1.png">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5441&amp;directory=19-1.png&amp;name=19-1.png</a></p>
<p>사이트를 개발하다보면 모양은 버튼이지만 역할은 링크인 경우가 있다. 예를 들어 페이스북의 로그인 페이지에 있는 &quot;Create new account&quot; 버튼은 모양은 버튼이지만 &quot;Log In&quot;이랑 달리 <code>&lt;a&gt;</code> 태그로 되어 있고, 클릭하면 회원가입 페이지로 이동한다.</p>
<p>버튼의 스타일 코드는 버튼 컴포넌트에 있을텐데, 이걸 <code>&lt;a&gt;</code> 태그 버전으로도 만들어야 하는 걸까? 이렇게 반복되는 스타일링 코드를 어떻게 관리하면 좋을까? </p>
<p>이럴 때 간편하게 사용할 수 있는게 바로 <code>as</code> 라는 Prop 이다. </p>
<p>예를 들어서 아래와 같이 <code>Button</code> 이라는 컴포넌트가 <code>&lt;button&gt;</code> 태그로 만들어져 있을 때, 이걸 <code>&lt;a&gt;</code> 태그로 바꿔서 사용할 수 있다.</p>
<pre><code class="language-jsx">
const Button = styled.button`
  /* ... */
`;
</code></pre>
<p><code>as</code> 로 태그 이름을 내려주면 해당하는 태그로 사용할 수 있다. 굳이 버튼 모양의 링크 컴포넌트를 만들 필요가 없다!!</p>
<pre><code class="language-jsx">&lt;Button href=&quot;https://example.com&quot; as=&quot;a&quot;&gt;
  LinkButton
&lt;/Button&gt;
</code></pre>
<h2 id="📍원치-않는-props가-전달될-때"><strong>📍원치 않는 Props가 전달될 때</strong></h2>
<p>Prop을 Spread 문법을 사용해서 <code>&lt;a&gt;</code> 태그로 전달하는 <code>Link</code> 컴포넌트가 있다고 해 보자. 그리고 <code>StyledLink</code> 라는 걸 만들어서 <code>underline</code> 이라는 불린 Prop으로 스타일링 해보자.</p>
<pre><code class="language-jsx">
import styled from &#39;styled-components&#39;;

function Link({ className, children, ...props }) {
  return (
    &lt;a {...props} className={className}&gt;
      {children}
    &lt;/a&gt;
  );
};

const StyledLink = styled(Link)`
  text-decoration: ${({ underline }) =&gt; underline ? `underline` : `none`};
`;

function App() {
  return (
    &lt;StyledLink underline={false} href=&quot;https://codeit.kr&quot;&gt;
      Codeit으로 가기
    &lt;/StyledLink&gt;
  );
}

export default App;
</code></pre>
<p>위 코드를 실행하면 경고가 뜬다.</p>
<pre><code>
react-dom.development.js:86 Warning: Received `false` for a non-boolean attribute `underline`.

If you want to write it to the DOM, pass a string instead: underline=&quot;false&quot; or underline={value.toString()}.

If you used to conditionally omit it with underline={condition &amp;&amp; value}, pass underline={condition ? value : undefined} instead.
    at a
    at Link (http://localhost:3000/static/js/bundle.js:26:5)
    at O (http://localhost:3000/static/js/bundle.js:44495:6)
    at App
</code></pre><p>HTML 태그에 <code>underline</code> 이라는 속성을 지정했는데, 그 속성의 값이 문자열이 아니라서 생긴 오류다. <code>&lt;a&gt;</code> 태그에는 <code>underline</code> 이라는 속성이 없다. </p>
<p>이 문제의 근본적인 원인은 <code>&lt;a {...props} className={className}&gt;</code> 이 부분이다. Spread를 하는 과정에서 의도하지 않은 <code>underline</code> Prop까지 내려간 것이 원인이다.</p>
<p><code>underline</code> Prop이 전달되는 순서를 정리해 보면…</p>
<ol>
<li><code>StyledLink</code> 컴포넌트에서 <code>underline</code> 이라는 Prop을 받는다</li>
<li><code>StyledLink</code> 가 스타일링하고 있는 <code>Link</code> 컴포넌트에 <code>underline</code> Prop이 전달된다</li>
<li><code>Link</code> 컴포넌트에서 Spread 문법을 통해 <code>&lt;a&gt;</code> 태그에 <code>underline</code> Prop이 전달된다.</li>
</ol>
<p>이럴 때는 구조 분해 코드를 조금 고쳐서 <code>underline</code>을 제외하면 원치 않는 Prop이 전달되는 걸 막을 수 있다.</p>
<pre><code class="language-jsx">
function Link({ className, children, underline, ...props }) {
  return (
    &lt;a {...props} className={className}&gt;
      {children}
    &lt;/a&gt;
  );
};
</code></pre>
<p><code>underline</code> 이라는 Prop은 <code>Link</code> 에서 쓰려고 만든 게 아니라 <code>StyledLink</code> 컴포넌트에서만 쓰려고 만든 건데, <code>Link</code> 에 Prop으로 전달되는게 좀 더 근본적인 문제인 거 같다. </p>
<p>이럴 때 아예 Prop이 전달되지 않게 만드는 방법이 있다. 바로 Transient Prop이라는 걸 사용하는 것이다.</p>
<p>Transient(일시적) Prop을 사용하면 Styled Components로 스타일링하는 컴포넌트에서만 Prop을 사용하고, 스타일링의 대상이 되는 컴포넌트에는 Prop이 전달되지 않도록 할 수 있다. </p>
<p><code>StyledLink</code> 컴포넌트 안에서만 Prop을 사용하고 <code>Link</code>에는 전달하지 않는 예시) </p>
<p>Transient Prop을 만들려면 앞에다 <code>$</code> 기호를 붙여주면 된다. 아래 코드에서 <code>$underline</code> Prop은 <code>StyledLink</code> 안에서만 사용되고, <code>Link</code> 로 전달되지는 않는다.</p>
<pre><code class="language-jsx">
import styled from &#39;styled-components&#39;;

function Link({ className, children, ...props }) {
  return (
    &lt;a {...props} className={className}&gt;
      {children}
    &lt;/a&gt;
  );
};

const StyledLink = styled(Link)`
  text-decoration: ${({ $underline }) =&gt; $underline ? `underline` : `none`};
`;

function App() {
  return (
    &lt;StyledLink $underline={false} href=&quot;https://codeit.kr&quot;&gt;
      Codeit으로 가기
    &lt;/StyledLink&gt;
  );
}

export default App;
</code></pre>
<h1 id="✨태그-함수tagged-function"><strong>✨태그 함수(Tagged Function)</strong></h1>
<p>태그 함수(Tagged Function)는 템플릿 리터럴 문법을 사용해서 실행할 수 있는 함수다. .</p>
<pre><code class="language-jsx">
function h1(strings, ...values) {
  return [strings, values];
}
const result = h1`color: pink;`;
console.log(result); // [[&#39;color: pink;&#39;], []]
</code></pre>
<p><code>h1</code> 이라는 함수는 첫 번째 파라미터로 <code>strings</code>, 그리고 나머지 파라미터들을 <code>values</code> 배열로 받는다. 이 함수를 템플릿 리터럴로 실행했다. 이렇게 일반적인 형태로 함수를 선언하고, 템플릿 리터럴로 실행하면 특정한 형태로 파라미터가 전달된다.</p>
<p><code>console.log</code> 로 출력하면 <code>strings</code> 에는 입력한 문자열이 배열로 나온다. <code>values</code> 는 빈 배열로 출력된다.</p>
<p>템플릿 리터럴에 값을 삽입하고 출력하면)</p>
<pre><code class="language-jsx">
function h1(strings, ...values) {
  return [strings, values];
}
const backgroundColor = &#39;black&#39;;
const result2 = h1`
  background-color: ${backgroundColor};
  color: pink;
`;
console.log(result2);
// [[&#39;\n  background-color: &#39;, &#39;;\n  color: pink;\n&#39;], [&#39;black&#39;]]
</code></pre>
<p><code>strings</code> 에는 값이 삽입되는 부분 앞뒤의 문자열들이 잘려서 배열로 들어가 있고, <code>values</code>에는 삽입된 값들이 배열로 들어가 있다. 이것이 태그 함수의 기본적인 동작이다. 이걸 사용해서 CSS 스타일이 생성된 리액트 컴포넌트를 만드는 것이 Styled Components의 핵심 아이디어다.</p>
<p>간단하게 <code>&lt;style&gt;</code> 태그를 렌더링하는 컴포넌트를 만들어 보자.</p>
<pre><code class="language-jsx">
function h1(strings, ...values) {
  // React 컴포넌트를 만든다
  function Component({ children }) {
    // 템플릿 리터럴에서 받은 값을 CSS 코드로 만든다
    let style = &#39;&#39;;
    for (let i = 0; i &lt; strings.length; ++i) {
      style += strings[i];
      if (values[i]) {
        style += values[i];
      }
    }

    // CSS 코드에 따라 클래스 이름을 만든다
    const className = `my-sc-${style.length}`;

    // `&lt;style&gt;` 태그로 만든 CSS 코드를 렌더링한다
    return (
      &lt;&gt;
        &lt;style&gt;{`.${className} {${style}}`}&lt;/style&gt;
        &lt;h1 className={className}&gt;{children}&lt;/h1&gt;
      &lt;/&gt;
    );
  }
  return Component;
}

const backgroundColor = &#39;black&#39;;
const StyledH1 = h1`
  background-color: ${backgroundColor};
  color: pink;
`;

function App() {
  return &lt;StyledH1&gt;Hello World&lt;/StyledH1&gt;;
}

export default App;
</code></pre>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5443&amp;directory=21-1.png&amp;name=21-1.png">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5443&amp;directory=21-1.png&amp;name=21-1.png</a></p>
<p>태그 함수 안에서 컴포넌트를 만들고 이걸 리턴하는 것이다. 이 컴포넌트는 우리가 태그 함수에서 집어넣은 CSS 코드를 <code>&lt;style&gt;</code> 태그에 렌더링하는 컴포넌트다.</p>
<p>Styled Components에서는 내부적으로 클래스네임을 알아서 생성해주기 때문에 우리가 클래스 이름을 신경 쓸 필요가 없다.</p>
<p>함수를 삽입하는 예시) </p>
<p>단순히 <code>strings</code> 와 <code>values</code> 배열을 합쳐주는 게 아니라, React 컴포넌트의 Props를 활용하는 함수가 삽입되는 경우를 처리할 것임.</p>
<pre><code class="language-jsx">
function h1(strings, ...values) {
  // React 컴포넌트를 만든다
  function Component({ children, ...props }) {
    // 템플릿 리터럴에서 받은 값을 CSS 코드로 만든다
    let style = &#39;&#39;;
    for (let i = 0; i &lt; strings.length; ++i) {
      style += strings[i];
      // 삽입된 값이 함수이면 props를 가지고 실행한 값을 CSS에 넣는다.
      if (typeof values[i] === &#39;function&#39;) {
        const fn = values[i];
        style += fn(props);

        // 그 외에 값이 존재하면 CSS에 문자열로 넣는다.
      } else if (values[i]) {
        style += values[i];
      }
    }

    // CSS 코드에 따라 클래스 이름을 만든다
    const className = `my-sc-${style.length}`;

    // `&lt;style&gt;` 태그로 만든 CSS 코드를 렌더링한다
    return (
      &lt;&gt;
        &lt;style&gt;{`.${className} {${style}}`}&lt;/style&gt;
        &lt;h1 className={className}&gt;{children}&lt;/h1&gt;
      &lt;/&gt;
    );
  }
  return Component;
}

const backgroundColor = &#39;black&#39;;
const StyledH1 = h1`
  color: pink;
  ${({ dark }) =&gt; dark &amp;&amp; &#39;background-color: black;&#39;}
`;

function App() {
  return &lt;StyledH1 dark&gt;Hello World&lt;/StyledH1&gt;;
}

export default App;
</code></pre>
<p><code>Component</code> 함수 안에서 CSS 코드를 생성하는 부분에 함수를 처리하는 부분이 추가되었다. </p>
<ol>
<li>템플릿 리터럴로 태그 함수 <code>h1</code> 을 실행해서, <code>StyledH1</code> 이라는 컴포넌트가 만들어진다.</li>
<li><code>App</code> 컴포넌트를 렌더링하면 <code>StyledH1</code> 컴포넌트도 렌더링한다.</li>
<li><code>StyledH1</code> 컴포넌트에서는 CSS 코드를 생성해서 <code>&lt;style&gt;</code> 태그로 넣는다. 이때 함수로 삽입된 값(<code>${({ dark }) =&gt; dark &amp;&amp; &#39;background-color: black;&#39;}</code> 부분)은 함수이기 때문에, Props를 가지고 실행해서 CSS로 만든다. 우리 코드에서는 <code>dark</code> 라는 값이 있기 때문에, CSS에는 <code>background-color: black;</code> 이라는 값으로 반영된다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[P3_S3] React로 웹사이트 만들기]]></title>
            <link>https://velog.io/@bori_note/P3S3-React%EB%A1%9C-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@bori_note/P3S3-React%EB%A1%9C-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 15 Apr 2024 08:13:39 GMT</pubDate>
            <description><![CDATA[<h1 id="✨리액트-라우터">✨리액트 라우터</h1>
<p>리액트 컴포넌트로 페이지를 나누는 라이브러리</p>
<pre><code class="language-jsx">npm install react-router-dom@6</code></pre>
<pre><code class="language-jsx">import { BrowserRouter } from &#39;react-router-dom&#39;;
import App from &#39;./components/App&#39;;
import HomePage from &#39;./pages/HomePage&#39;;

function Main() {
  return (
    &lt;BrowserRouter&gt;
      &lt;App&gt;
        &lt;HomePage /&gt;
      &lt;/App&gt;
    &lt;/BrowserRouter&gt;
  );
}

export default Main;</code></pre>
<h2 id="📘라우터"><strong>📘라우터</strong></h2>
<p>리액트 라우터를 사용하려면 반드시 라우터라는 컴포넌트가 필요하다. → <code>BrowserRouter</code> </p>
<p>이 컴포넌트를 최상위 컴포넌트에서 감싸주면 모든 곳에서 사용할 수 있다.</p>
<pre><code class="language-jsx">
import { BrowserRouter } from &#39;react-router-dom&#39;;

function App() {
  return &lt;BrowserRouter&gt; ... &lt;/BrowserRouter&gt;;
}
</code></pre>
<h2 id="📘페이지-나누는-방법"><strong>📘페이지 나누는 방법</strong></h2>
<p><code>Routes</code> 컴포넌트 안에다가 <code>Route</code> 컴포넌트를 배치해서 각 페이지를 나눠줄 수 있다.</p>
<p>이때 <code>Routes</code> 안에서는 위에서부터 차례대로 <code>Route</code>를 검사한다.</p>
<p>현재 경로와 <code>path</code> prop이 일치하는 <code>Route</code> 를 찾는다.</p>
<pre><code class="language-jsx">
&lt;Routes&gt;
  &lt;Route path=&quot;/&quot; element={&lt;HomePage /&gt;} /&gt;
  &lt;Route path=&quot;posts&quot; element={&lt;PostListPage /&gt;} /&gt;
  &lt;Route path=&quot;posts/1&quot; element={&lt;PostPage /&gt;} /&gt;
&lt;/Routes&gt;
</code></pre>
<h2 id="📘링크"><strong>📘링크</strong></h2>
<p>리액트 라우터에서는 <code>&lt;a&gt;</code> 태그 대신에 <code>Link</code> 컴포넌트를 사용한다.</p>
<p><code>to</code> 라는 prop으로 이동할 경로를 정해주면 된다.</p>
<pre><code class="language-jsx">&lt;Link to=&quot;/posts&quot;&gt;블로그&lt;/Link&gt;</code></pre>
<h2 id="📘하위-페이지-나누기"><strong>📘하위 페이지 나누기</strong></h2>
<p><code>Route</code> 컴포넌트 안에다가 <code>Route</code> 컴포넌트를 배치하면 된다.</p>
<p>이때 하위 페이지에서  최상위 경로에 해당하는 경로는 <code>path</code> prop이 아니라 <code>index</code> 라는 prop을 사용하면 된다.</p>
<pre><code class="language-jsx">
&lt;Routes&gt;
  &lt;Route path=&quot;/&quot;&gt;&lt;HomePage /&gt;&lt;/Route&gt;&lt;Route path=&quot;posts&quot; element={&lt;PostLayout /&gt;} &gt;
    &lt;Route index element={&lt;PostListPage /&gt;}  /&gt;
    &lt;Route path=&quot;1&quot; element={&lt;PostPage /&gt;}  /&gt;
  &lt;/Route&gt;
&lt;/Routes&gt;
</code></pre>
<p>이때 부모 <code>Route</code> 컴포넌트에 <code>element</code> 를 지정하고,</p>
<p>아래처럼 <code>Outlet</code> 이라는 컴포넌트를 활용하면 공통된 레이아웃을 지정해줄 수 있다.</p>
<p>PostLayout.js</p>
<pre><code class="language-jsx">
import { Outlet } from &#39;react-router-dom&#39;;

function PostLayout() {
  return (
    &lt;div&gt;
      &lt;h1&gt;블로그&lt;/h1&gt;
      &lt;hr /&gt;
      &lt;Outlet /&gt;
    &lt;/div&gt;
  );
}

export default PostLayout;
</code></pre>
<h2 id="📘동적인-경로-다루기"><strong>📘동적인 경로 다루기</strong></h2>
<p>콜론 (<code>:</code>) 으로 시작하는 문자열을 사용하면 경로에 파라미터를 지정할 수 있다.</p>
<p>예시) 아래처럼 <code>/posts/:postId</code> 라는 경로는 <code>/posts/123</code> 이라던지</p>
<p><code>/posts/abc</code> 라는 주소로 접속하면 <code>123</code> 이나 <code>abc</code> 라는 값을 <code>postId</code> 라는 파라미터로 받는다.</p>
<pre><code class="language-jsx">
&lt;Routes&gt;
  &lt;Route path=&quot;/&quot;&gt;&lt;HomePage /&gt;&lt;/Route&gt;&lt;Route path=&quot;posts&quot; element={&lt;PostLayout /&gt;} &gt;
    &lt;Route index element={&lt;PostListPage /&gt;}  /&gt;
    &lt;Route path=&quot;:postId&quot; element={&lt;PostPage /&gt;}  /&gt;
  &lt;/Route&gt;
&lt;/Routes&gt;
</code></pre>
<p>경로 파라미터를 사용하려면 <code>useParams</code> 라는 훅을 사용하면 된다.</p>
<pre><code class="language-jsx">
function PostPage() {
  const { postId } = useParams();
  // ...
}</code></pre>
<h2 id="📘쿼리-사용하기"><strong>📘쿼리 사용하기</strong></h2>
<p><code>useSearchParams</code> 라는 Custom hook으로 <code>SearchParams</code> 객체를 받아올 수 있다. 이 hook은 <code>SearchParams</code> 객체와 Setter 함수를 배열형으로 리턴한다.</p>
<p>이때 쿼리 값은 <code>SearchParams</code> 의 <code>get</code> 함수로 가져온다.</p>
<pre><code class="language-jsx">
import { useSearchParams } from &#39;react-router-dom&#39;;

function PostListPage() {
  const [searchParams, setSearchParams] = useSearchParams();
  const filterQuery = searchParams.get(&#39;filter&#39;);

  // ...
}
</code></pre>
<p>만약 쿼리 값을 변경하고 주소를 이동하고 싶다면 Setter 함수에 객체를 넘겨주면 된다.</p>
<p>이때 객체의 프로퍼티로 쿼리 값을 지정할 수 있다.</p>
<p>예시) <code>?filter=react</code> 라는 쿼리로 이동하기</p>
<pre><code class="language-jsx">setSearchParams({
  filter: &#39;react&#39;,
});</code></pre>
<h2 id="📘페이지-이동하기"><strong>📘페이지 이동하기</strong></h2>
<h3 id="📗navigate-컴포넌트"><strong>📗Navigate 컴포넌트</strong></h3>
<p>리턴값으로 <code>Navigate</code> 컴포넌트를 리턴하면 <code>to</code> prop으로 지정한 경로로 이동한다.</p>
<pre><code class="language-jsx">
function PostPage() {
  // ...

  const post = getPost(postId);

  // post가 없는 경우 /posts 페이지로 이동
  if (!post) {
    return &lt;Navigate to=&quot;/posts&quot; /&gt;;
  }

  // ...
}
</code></pre>
<h3 id="📗usenavigate-hook"><strong>📗useNavigate Hook</strong></h3>
<p><code>useNavigate</code> 라는 hook으로 <code>navigate</code> 함수를 가져오면 이 함수를 통해 페이지를 이동할 수 있다.</p>
<pre><code class="language-jsx">
const navigate = useNavigate();

const handleClick = () =&gt; {
  // ... 어떤 작업을 한 다음에 페이지를 이동
  navigate(&#39;/wishlist&#39;);
}
</code></pre>
<h1 id="❓link-navigate-usenavigate는-언제-쓰는게-좋을까"><strong>❓Link, Navigate, useNavigate는 언제 쓰는게 좋을까?</strong></h1>
<h2 id="📍link"><strong>📍Link</strong></h2>
<ul>
<li><strong>사용자가 클릭해서 페이지를 이동하도록 할 때</strong></li>
<li>하이퍼링크 텍스트나 페이지를 이동하는 버튼, 이미지 등</li>
<li>대부분의 경우 <code>Link</code> 만으로도 충분하다.</li>
</ul>
<h2 id="📍navigate"><strong>📍Navigate</strong></h2>
<ul>
<li>특정 경로에서 <strong>렌더링 시점에 다른 페이지로 이동시키고 싶을 때</strong></li>
</ul>
<p><strong>예시:</strong></p>
<ul>
<li>쇼핑몰의 회원 전용 페이지에 로그인없이 들어와서 로그인 페이지로 리다이렉트하는 경우</li>
<li>쇼핑몰의 상품 상세 페이지에서 제품이 품절되었거나 삭제되어서 다른 페이지로 이동시키는 경우</li>
</ul>
<h2 id="📍usenavigate"><strong>📍useNavigate</strong></h2>
<p><strong>특정한 코드의 실행이 끝나고 나서 페이지를 이동시키고 싶을 때</strong></p>
<p><strong>예시:</strong></p>
<ul>
<li>쇼핑몰에서 장바구니에 담기를 눌렀을 때 리퀘스트를 보내고 장바구니 페이지로 이동시키는 경우</li>
<li>쇼핑몰에서 결제하기 버튼을 누르고 나서 모든 결제가 완료된 후에 페이지를 이동시키는 경우</li>
<li>리다이렉트된 로그인 페이지에서 로그인을 완료한 후에 처음 진입했던 페이지로 돌아가는 경우</li>
</ul>
<hr>
<p>각 페이지에 들어갔을 때 페이지 제목이 바꾸려면?</p>
<p><code>react-helmet</code> 이라는 라이브러리</p>
<pre><code class="language-jsx">import { Helmet } from &#39;react-helmet&#39;;
import Button from &#39;../components/Button&#39;;
import Container from &#39;../components/Container&#39;;
import Lined from &#39;../components/Lined&#39;;
import styles from &#39;./HomePage.module.css&#39;;
import landingImg from &#39;../assets/landing.svg&#39;;

function HomePage() {
  return (
    &lt;&gt;
      &lt;Helmet&gt;
        &lt;title&gt;Codethat - 코딩이 처음이라면, 코드댓&lt;/title&gt;
      &lt;/Helmet&gt;
      &lt;div className={styles.bg} /&gt;
      &lt;Container className={styles.container}&gt;
        &lt;div className={styles.texts}&gt;
          &lt;h1 className={styles.heading}&gt;
            &lt;Lined&gt;코딩이 처음이라면,&lt;/Lined&gt;
            &lt;br /&gt;
            &lt;strong&gt;코드댓&lt;/strong&gt;
          &lt;/h1&gt;
          &lt;p className={styles.description}&gt;
            11만 명이 넘는 비전공자, 코딩 입문자가 코드댓 무제한 멤버십을
            선택했어요.
            &lt;br /&gt;
            지금 함께 시작해보실래요?
          &lt;/p&gt;
          &lt;div&gt;
            &lt;Button&gt;지금 시작하기&lt;/Button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div className={styles.figure}&gt;
          &lt;img src={landingImg} alt=&quot;그래프, 모니터, 윈도우, 자물쇠, 키보드&quot; /&gt;
        &lt;/div&gt;
      &lt;/Container&gt;
    &lt;/&gt;
  );
}

export default HomePage;</code></pre>
<ul>
<li><code>Helmet</code> 이라는 컴포넌트로 감싼 다음에, 안에다가 <code>&lt;title&gt;</code> 태그를 배치하면 이 컴포넌트가 렌더링 될 때 HTML의 <code>&lt;title&gt;</code> 태그를 덮어쓸 수 있다.</li>
</ul>
<h1 id="✨리액트를-렌더링하는-방식">✨리액트를 렌더링하는 방식</h1>
<p>SPA</p>
<p>하나의 HTML 문서 안에서 자바스크립트로 여러 페이지를 보여주는 사이트</p>
<h2 id="📘클라이언트사이드-렌더링client-side-rendering"><strong>📘클라이언트사이드 렌더링(Client-side Rendering)</strong></h2>
<ul>
<li>웹 브라우저에서 자바스크립트로 HTML을 만드는 것</li>
<li>리액트로 할 수 있는 가장 기본적인 방식의 렌더링</li>
<li>리액트로 작성한 코드는 자바스크립트로 변환이 가능하다.(트랜스파일링)</li>
</ul>
<p>클라이언트사이드 렌더링은 <strong>자바스크립트로 변환된 리액트 코드를 웹 브라우저에서 실행해서 HTML을 만드는</strong> 것</p>
<h2 id="📘서버사이드-렌더링server-side-rendering"><strong>📘서버사이드 렌더링(Server-side Rendering)</strong></h2>
<ul>
<li>서버에서 HTML을 만들고 리스폰스로 보내주는 것</li>
<li>백엔드 서버에서 리퀘스트를 받으면 상황에 맞는 HTML을 만들어서 리스폰스로 보내주는 방식을</li>
<li>서버에서 HTML을 만든다는 뜻</li>
</ul>
<h2 id="📘정적-사이트-생성static-site-generation"><strong>📘정적 사이트 생성(Static Site Generation)</strong></h2>
<ul>
<li>미리 HTML 파일을 만들어서 서버를 배포하는 것</li>
<li>서버에서 렌더링 하는 것도 좋지만, 데이터가 거의 바뀌지 않는다면 매번 새로 만드는 건 낭비다. 그래서 미리 HTML 파일로 만들고 이걸 서버로 배포하는 방법을 사용한다.(정적 사이트 생성)<ul>
<li>서버에서는 리퀘스트가 들어오면 HTML 파일을 읽어서 리스폰스로 보내주는 것.</li>
<li>&#39;정적 사이트 생성&#39;에서 정적이라는 말의 의미는 HTML을 파일로 만든다는 것</li>
<li>개발자가 새로 배포하지 않는다면 서버에서 보내주는 HTML이 달라지지 않는다는 의미다.</li>
<li>리액트 코드로 HTML 파일을 만든다는 뜻</li>
</ul>
</li>
</ul>
<h1 id="✨렌더링을-활용한-리액트-기술-세-가지"><strong>✨렌더링을 활용한 리액트 기술 세 가지</strong></h1>
<h2 id="📘nextjs"><strong>📘Next.js</strong></h2>
<p>리액트 서버사이드 렌더링을 편하게</p>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5122&amp;directory=p4wxlvwuf-nextjs-example.png&amp;name=p4wxlvwuf-nextjs-example.png">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5122&amp;directory=p4wxlvwuf-nextjs-example.png&amp;name=p4wxlvwuf-nextjs-example.png</a></p>
<blockquote>
<p>Next.js 기본 예제 프로젝트 모습</p>
</blockquote>
<p>리액트에서는 서버사이드 렌더링을 하는 기능들을 제공하고 있지만, 아주 기본적인 방법만 제공한다. 때문에 매번 작성해야 하는 코드의 양도 많고 복잡하다. 그래서 개발자들은 <strong>서버사이드 렌더링을 대신 구현해주는 기술</strong>들을 만들기 시작했다.</p>
<p>리액트 라우터랑은 다르게 HTML 파일을 나누듯이 자바스크립트 파일을 나눠 놓으면 곧바로 페이지로 사용할 수 있다는 장점이 있다.</p>
<h2 id="📘gatsby"><strong>📘Gatsby</strong></h2>
<p>리액트로 정적 사이트 만들기</p>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5122&amp;directory=bfuyqam0x-gatsby-example.png&amp;name=bfuyqam0x-gatsby-example.png">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5122&amp;directory=bfuyqam0x-gatsby-example.png&amp;name=bfuyqam0x-gatsby-example.png</a></p>
<blockquote>
<p>gatsby-starter-blog 프로젝트의 모습</p>
</blockquote>
<p>빌드라는 건 리액트로 작성된 소스코드들을 브라우저가 알아들을 수 있도록 만드는 것이다.</p>
<p>Gatsby는 리액트 코드를 미리 렌더링 해서 프로젝트를 빌드할 때 HTML 파일로 만든다.(정적 사이트 생성) Gatsby를 사용하면 리액트로 만든 사이트를 빌드해서 손쉽게 HTML 파일로 만들 수 있다.</p>
<h2 id="📘react-native"><strong>📘React Native</strong></h2>
<p>모바일 앱의 화면도 리액트로</p>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5122&amp;directory=react-native-example.png&amp;name=react-native-example.png">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=5122&amp;directory=react-native-example.png&amp;name=react-native-example.png</a></p>
<blockquote>
<p>리액트 네이티브에서 텍스트와 버튼을 배치한 예시</p>
</blockquote>
<p>React Native는 리액트로 작성한 코드를 모바일 앱으로 만들 수 있게 해 준다. 리액트 코드로 개발하면 웹과 안드로이드와 iOS 앱에서 사용하는 공통적인 코드를 한 번에 개발할 수 있다는 장점이 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 상태 관리 라이브러리]]></title>
            <link>https://velog.io/@bori_note/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
            <guid>https://velog.io/@bori_note/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</guid>
            <pubDate>Sun, 14 Apr 2024 11:54:54 GMT</pubDate>
            <description><![CDATA[<h1 id="✨상태-관리">✨상태 관리</h1>
<p>화면에서 사용하는 데이터를 관리하는 것</p>
<h2 id="flux">Flux</h2>
<ul>
<li>데이터의 변경을 한 곳에서 하면서 흐름을 정리</li>
</ul>
<p><a href="https://opensource.fb.com/">Home | Meta Open Source</a></p>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/6bf245e8-dd07-48b2-b775-05b6b4aa4af9/8c06757f-da78-43e9-9573-2e3b61c7dfcb/Untitled.png" alt="Untitled"></p>
<ul>
<li>데이터를 변경하고 싶으면 액션을 만들어서 dispatcher에게 전달.</li>
<li>dispathcher는 액션을 모아 stroe에 넘겨줌.</li>
<li>store는 들어온 액션을 차례대로 보면서 데이터를 변경함.</li>
<li>store에서 데이터가 변경되면 컴포넌트들은 다시 렌더링함.</li>
</ul>
<p>컴포넌트 - 액션 만들기</p>
<p>store - 데이터 변경하기</p>
<p>dispatcher = store로 전달되는 액션을 차례대로 살펴보기</p>
<h2 id="redux">Redux</h2>
<p><a href="https://redux.js.org/">Redux - A JS library for predictable and maintainable global state management | Redux</a></p>
<p>데이터 하나를 추가하려고 매번 무의미한 리덕스 코드를 작성하게 됨</p>
<p>리덕스를 통해서 비동기를 처리하려면 코드가 복잡해지고 알아보기 힘듦</p>
<p>클라이언트 상태와 서버상태를 구분함 (데이터의 출처를 기반으로)</p>
<p>프론트 개발자들은 클라이언트 상태 관리에만 집중하고 </p>
<p>서버상태는 서버랑 동기화하면 되지 않을까 하는 아이디어를 떠올림👇🏻👇🏻 </p>
<h2 id="react-query">React Query</h2>
<p><a href="https://tanstack.com/query/v3/">TanStack Query</a></p>
<h2 id="swr">SWR</h2>
<p><a href="https://swr.vercel.app/">React Hooks for Data Fetching – SWR</a></p>
<p>리액트 쿼리 + SWR</p>
<p>리퀘스트를 정해 놓으면 알아서 서버랑 동기화를 하기 때문에 편하게 서버 상태 관리를 할 수 있다. </p>
<h2 id="recoil">Recoil</h2>
<p><a href="https://recoiljs.org/ko/">Recoil</a></p>
<blockquote>
<p>Context는 편리하지만 데이터를 추가할 때마다 Context도 하나씩 늘어난다는 문제점이 있다. Context 한 개에 모든 데이터를 모으면 데이터가 변할 때마다 상관없는 컴포넌트들도 함께 재렌더링 된다. 그리고, 데이터의 구조가 컴포넌트 구조와 섞인다.</p>
</blockquote>
<ul>
<li>단순한 상태 관리 라이브러리</li>
<li>전역적으로 쓸 수 있는 useState</li>
<li>공유할 데이터(Atom)를 컴포넌트 구조와 별개로 분리하는 것<ul>
<li>Atom: 어디서나 갖다 쓸 수 있는 state 같은 것</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[P3_S2] React로 데이터 다루기]]></title>
            <link>https://velog.io/@bori_note/P2S1-React%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A4%EB%A3%A8%EA%B8%B0</link>
            <guid>https://velog.io/@bori_note/P2S1-React%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A4%EB%A3%A8%EA%B8%B0</guid>
            <pubDate>Sun, 14 Apr 2024 11:54:22 GMT</pubDate>
            <description><![CDATA[<h1 id="✨배열-렌더링하기">✨배열 렌더링하기</h1>
<h2 id="map-으로-렌더링하기"><strong><code>map</code> 으로 렌더링하기</strong></h2>
<p>배열 메소드 <code>map</code>에서 콜백 함수의 리턴 값으로 리액트 엘리먼트를 리턴하면 된다.</p>
<pre><code class="language-jsx">
import items from &#39;./pokemons&#39;;

function Pokemon({ item }) {
  return (
    &lt;div&gt;
      No.{item.id} {item.name}
    &lt;/div&gt;
  );
}

function App() {
  return (
    &lt;ul&gt;
      {items.map((item) =&gt; (
        &lt;li key={item.id}&gt;
          &lt;Pokemon item={item} /&gt;
        &lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}

export default App;</code></pre>
<p>참고로 반드시 JSX의 중괄호 안에서 <code>map</code> 함수를 써야 하는 것은 아님.</p>
<p>아래처럼 <code>renderedItems</code> 라는 변수에 <code>map</code>의 결과를 지정해도 똑같이 렌더링 하게 된다.</p>
<pre><code class="language-jsx">import items from &#39;./pokemons&#39;;

function Pokemon({ item }) {
  return (
    &lt;div&gt;
      No.{item.id} {item.name}
    &lt;/div&gt;
  );
}

function App() {
  const renderedItems = items.map((item) =&gt; (
    &lt;li key={item.id}&gt;
      &lt;Pokemon item={item} /&gt;
    &lt;/li&gt;
  ));

  return (
    &lt;ul&gt;
      {renderedItems}
    &lt;/ul&gt;
  );
}

export default App;
</code></pre>
<h2 id="sort-로-정렬하기"><strong><code>sort</code> 로 정렬하기</strong></h2>
<p>배열 메소드의 <code>sort</code> 메소드를 사용하면 배열을 정렬할 수 있다.</p>
<pre><code class="language-jsx">import { useState } from &#39;react&#39;;
import items from &#39;./pokemons&#39;;

function Pokemon({ item }) {
  return (
    &lt;div&gt;
      No.{item.id} {item.name}
    &lt;/div&gt;
  );
}

function App() {
  const [direction, setDirection] = useState(1);
  const handleAscClick = () =&gt; setDirection(1);
  const handleDescClick = () =&gt; setDirection(-1);

  const sortedItems = items.sort((a, b) =&gt; direction * (a.id - b.id));

  return (
    &lt;div&gt;
      &lt;div&gt;
        &lt;button onClick={handleAscClick}&gt;도감번호 순서대로&lt;/button&gt;
        &lt;button onClick={handleDescClick}&gt;도감번호 반대로&lt;/button&gt;
      &lt;/div&gt;
      &lt;ul&gt;
        {sortedItems.map((item) =&gt; (
          &lt;li key={item.id}&gt;
            &lt;Pokemon item={item} /&gt;
          &lt;/li&gt;
        ))}
      &lt;/ul&gt;
    &lt;/div&gt;
  );
}

export default App;</code></pre>
<h2 id="filter-로-삭제하기"><strong><code>filter</code> 로 삭제하기</strong></h2>
<p>배열 메소드 중 <code>filter</code> 와 배열형 스테이트를 활용하면 삭제 기능을 간단히 구현할 수 있다.</p>
<pre><code class="language-jsx">import { useState } from &#39;react&#39;;
import mockItems from &#39;./pokemons&#39;;

function Pokemon({ item, onDelete }) {
  const handleDeleteClick = () =&gt; onDelete(item.id);

  return (
    &lt;div&gt;
       No.{item.id} {item.name}
      &lt;button onClick={handleDeleteClick}&gt;삭제&lt;/button&gt;
    &lt;/div&gt;
  );
}

function App() {
  const [items, setItems] = useState(mockItems);

  const handleDelete = (id) =&gt; {
    const nextItems = items.filter((item) =&gt; item.id !== id);
    setItems(nextItems);
  };

  return (
    &lt;ul&gt;
      {items.map((item) =&gt; (
        &lt;li key={item.id}&gt;
          &lt;Pokemon item={item} onDelete={handleDelete} /&gt;
        &lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}

export default App;</code></pre>
<h3 id="❓const-nextitems--itemsfilteritem--itemidhttpitemid--id">❓<code>const nextItems = items.filter((item) =&gt; [item.id](http://item.id/) !== id);</code></h3>
<ul>
<li><strong><code>filter</code></strong> 함수는 배열의 각 요소에 대해 주어진 콜백 함수를 실행하고, 그 결과가 참인 요소들로 이루어진 새로운 배열을 반환한다.</li>
<li>예를 들어, <strong><code>items</code></strong> 배열에 다음과 같은 요소들이 있다고 가정하면:</li>
</ul>
<pre><code class="language-jsx">const items = [
    { id: 1, name: &#39;Apple&#39; },
    { id: 2, name: &#39;Banana&#39; },
    { id: 3, name: &#39;Orange&#39; }
];</code></pre>
<p>그리고 <strong><code>id</code></strong> 변수에는 <strong><code>2</code></strong>가 저장되어 있다고 가정하면</p>
<ol>
<li>첫 번째 요소(<strong><code>{ id: 1, name: &#39;Apple&#39; }</code></strong>)의 <strong><code>id</code></strong>는 <strong><code>2</code></strong>와 다르기 때문에 결과에 포함</li>
<li>두 번째 요소(<strong><code>{ id: 2, name: &#39;Banana&#39; }</code></strong>)의 <strong><code>id</code></strong>는 <strong><code>2</code></strong>와 같기 때문에 결과에 포함되지 않음</li>
<li>세 번째 요소(<strong><code>{ id: 3, name: &#39;Orange&#39; }</code></strong>)의 <strong><code>id</code></strong>는 <strong><code>2</code></strong>와 다르기 때문에 결과에 포함</li>
</ol>
<h2 id="반드시-key-를-내려주자"><strong>반드시 <code>key</code> 를 내려주자</strong></h2>
<ul>
<li>각 요소를 렌더링 할 때는 <code>key</code> Prop을 내려줘야 한다.</li>
<li>이때 가장 바깥쪽에 있는 (최상위) 태그에다가 <code>key</code> Prop을 지정하면 된다.</li>
<li>반드시 <code>id</code> 일 필요는 없고 각 데이터를 구분할 수 있는 고유한 값이면 무엇이든 <code>key</code> 로 활용해도 상관없다.</li>
</ul>
<pre><code class="language-jsx">import items from &#39;./pokemons&#39;;

function Pokemon({ item }) {
  return (
    &lt;div&gt;
      No.{item.id} {item.name}
    &lt;/div&gt;
  );
}

function App() {
  return (
    &lt;ul&gt;
      {items.map((item) =&gt; (
        &lt;li key={item.name}&gt;
          &lt;Pokemon item={item} /&gt;
        &lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}

export default App;</code></pre>
<h3 id="❓배열을-렌더링할-때-key를-지정하지-않으면"><strong>❓배열을 렌더링할 때 key를 지정하지 않으면?</strong></h3>
<ul>
<li>key를 지정하더라도 배열의 인덱스같이 데이터를 구분할 수 없는 고유하지 않은 값으로 key를 지정하면 렌더링이 잘못될 수 있다.</li>
<li>각 데이터를 구분할 수 있는 고유한 값으로 key를 꼭 정해줘야 함!!</li>
</ul>
<h1 id="데이터-가져오면서-무한루프에-빠짐">데이터 가져오면서 무한루프에 빠짐</h1>
<pre><code class="language-jsx">import React, { useState } from &quot;react&quot;;
import ReviewList from &quot;./components/ReviewList&quot;;
import { getReviews } from &quot;./api&quot;;

function App() {
  const [items, setItems] = useState([]);
  const [order, setOrder] = useState(&quot;createdAt&quot;); // 최신순
  const sortedItems = items.sort((a, b) =&gt; b[order] - a[order]); // 평점 높은 순(내림차순)

  const handleNewestClick = () =&gt; setOrder(&quot;createdAt&quot;);
  const handBestClick = () =&gt; setOrder(&quot;rating&quot;);
  const handleDelete = (id) =&gt; {
    // 현재 순회 중인 요소의 id가 주어진 id와 같지 않은 경우를 체크
    const nextItems = items.filter((item) =&gt; item.id !== id);
    setItems(nextItems);
  };

  const handleLoad = async () =&gt; {
    // 비동기로 리퀘스를 보냈다가 리스폰스가 도착하면 reviews 변수를 지정하고
    const { reviews } = await getReviews();
    // setItems를 통해 state 변경 -&gt; App 컴포넌트를 다시 렌더링함
    setItems(reviews)
  };

  // 다시 이 함수가 렌더링 되어 무한루프가 생김
  handleLoad()

  return (
    &lt;div&gt;
      &lt;div&gt;
        &lt;button onClick={handleNewestClick}&gt;최신순&lt;/button&gt;
        &lt;button onClick={handBestClick}&gt;베스트순&lt;/button&gt;
      &lt;/div&gt;
      &lt;ReviewList items={sortedItems} onDelete={handleDelete} /&gt;
      &lt;button&gt;불러오기&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;</code></pre>
<p>→ useEffect로 해결하기</p>
<h1 id="✨useeffect">✨useEffect</h1>
<h2 id="📘처음-한-번만-실행하기"><strong>📘처음 한 번만 실행하기</strong></h2>
<pre><code class="language-tsx">useEffect(() =&gt; {
  // 실행할 코드
}, []);</code></pre>
<p>컴포넌트가 처음 렌더링 되고 나면 리액트가 콜백 함수를 기억해뒀다가 실행한다.</p>
<p>그 이후로는 콜백 함수를 실행하지 않는다.</p>
<h2 id="📘값이-바뀔-때마다-실행하기"><strong>📘값이 바뀔 때마다 실행하기</strong></h2>
<pre><code class="language-tsx">useEffect(() =&gt; {
  // 실행할 코드
}, [dep1, dep2, dep3, ...]);</code></pre>
<p>컴포넌트가 처음 렌더링 되고 나면 리액트가 콜백 함수를 기억해뒀다가 실행한다.</p>
<p>그 이후로 렌더링 할 때는 디펜던시 리스트에 있는 값들을 확인해서 하나라도 바뀌면  콜백 함수를 기억해뒀다가 실행한다.</p>
<h1 id="✨페이지네이션">✨페이지네이션</h1>
<p>책의 페이지처럼 데이터를 나눠서 제공하는 것</p>
<h2 id="📘오프셋-기반-페이지네이션">📘오프셋 기반 페이지네이션</h2>
<p>offset: 상쇄하다 = 지금까지 받아온 데이터의 개수</p>
<ul>
<li>받아온 개수 기준</li>
</ul>
<h2 id="📘커서-기반-페이지네이션">📘커서 기반 페이지네이션</h2>
<p>cursor: 데이터를 가리키는 값 = 지금까지 받은 데이터를 표시한 책갈피</p>
<p>데이터가 바뀌더라고 커서가 가리키는 데이터 값은 변하지 않음 → 데이터 중복이나 빠짐없이 데이터를 잘 가져온다.</p>
<ul>
<li>데이터를 가리키는 커서 기준</li>
</ul>
<h1 id="✨조건부-렌더링">✨조건부 렌더링</h1>
<h2 id="논리-연산자-활용하기"><strong>논리 연산자 활용하기</strong></h2>
<h2 id="📘and-연산자"><strong>📘AND 연산자</strong></h2>
<pre><code class="language-tsx">
import { useState } from &#39;react&#39;;

function App() {
  const [show, setShow] = useState(false);

  const handleClick = () =&gt; setShow(!show);

  return (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;토글&lt;/button&gt;
      {show &amp;&amp; &lt;p&gt;보인다 👀&lt;/p&gt;}
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p><code>show</code> 값이 <code>true</code> 이면 렌더링 하고, <code>false</code> 이면 렌더링 하지 않는다.</p>
<h2 id="or-연산자"><strong>OR 연산자</strong></h2>
<pre><code class="language-tsx">
import { useState } from &#39;react&#39;;

function App() {
  const [hide, setHide] = useState(true);

  const handleClick = () =&gt; setHide(!hide);

  return (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;토글&lt;/button&gt;
      {hide || &lt;p&gt;보인다 👀&lt;/p&gt;}
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p><code>hide</code> 값이 <code>true</code> 이면 렌더링 하지 않고, <code>false</code> 이면 렌더링 한다.</p>
<h2 id="📘삼항-연산자-활용하기"><strong>📘삼항 연산자 활용하기</strong></h2>
<pre><code class="language-tsx">
import { useState } from &#39;react&#39;;

function App() {
  const [toggle, setToggle] = useState(false);

  const handleClick = () =&gt; setToggle(!toggle);

  return (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;토글&lt;/button&gt;
      {toggle ? &lt;p&gt;✅&lt;/p&gt; : &lt;p&gt;❎&lt;/p&gt;}
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>삼항 연산자를 사용하면 참, 거짓일 경우에 다르게 렌더링해줄 수 있다.</p>
<p><code>toggle</code> 의 값이 참일 경우엔 &#39;✅&#39;을, 거짓일 경우에는 &#39;❎&#39;를 렌더링한다.</p>
<h2 id="📘렌더링되지-않는-값들"><strong>📘렌더링되지 않는 값들</strong></h2>
<pre><code class="language-tsx">
function App() {
  const nullValue = null;
  const undefinedValue = undefined;
  const trueValue = true;
  const falseValue = false;
  const emptyString = &#39;&#39;;
  const emptyArray = [];

  return (
    &lt;div&gt;
      &lt;p&gt;{nullValue}&lt;/p&gt;
      &lt;p&gt;{undefinedValue}&lt;/p&gt;
      &lt;p&gt;{trueValue}&lt;/p&gt;
      &lt;p&gt;{falseValue}&lt;/p&gt;
      &lt;p&gt;{emptyString}&lt;/p&gt;
      &lt;p&gt;{emptyArray}&lt;/p&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>위 컴포넌트에서 중괄호 안에 있는 값들은 모두 아무것도 렌더링하지 않는다.</p>
<pre><code class="language-tsx">
function App() {
  const zero = 0;
  const one = 1;

  return (
    &lt;div&gt;
      &lt;p&gt;{zero}&lt;/p&gt;
      &lt;p&gt;{one}&lt;/p&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>이 값들은 각각 숫자 0과 1을 렌더링 한다.</p>
<h2 id="⚠️조건부-렌더링을-사용할-때-주의할-점"><strong>⚠️조건부 렌더링을 사용할 때 주의할 점</strong></h2>
<pre><code class="language-tsx">
import { useState } from &#39;react&#39;;

function App() {
  const [num, setNum] = useState(0);

  const handleClick = () =&gt; setNum(num + 1);

  return (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;더하기&lt;/button&gt;
      {num &amp;&amp; &lt;p&gt;num이 0 보다 크다!&lt;/p&gt;}
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p><code>num</code> 값이 0일 때는 <code>false</code> 로 계산되니까 뒤의 값을 계산하지 않기 때문에 아무것도 렌더링 하지 않는 코드 같지만, 숫자 0은 0으로 렌더링 된다.</p>
<p>그래서 처음 실행했을 때 <strong>숫자 0이 렌더링</strong> 되고 &#39;더하기&#39; 버튼을 눌러서 <code>num</code> 값이 증가하면 <code>num이 0 보다 크다!</code> 가 렌더링 된다.</p>
<p>그래서 이런 경우엔 아래처럼 보다 명확한 논리식을 써주는 게 좋다.</p>
<p><code>true</code> 나 <code>false</code> 값은 리액트에서 렌더링 하지 않기 때문!!</p>
<pre><code class="language-tsx">
import { useState } from &#39;react&#39;;

function App() {
  const [num, setNum] = useState(0);

  const handleClick = () =&gt; setNum(num + 1);

  return (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;더하기&lt;/button&gt;
      {(num &gt; 0) &amp;&amp; &lt;p&gt;num이 0 보다 크다!&lt;/p&gt;}
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<h1 id="✨usestate">✨useState</h1>
<h2 id="📘초깃값-지정하기"><strong>📘초깃값 지정하기</strong></h2>
<pre><code class="language-tsx">const [state, setState] = useState(initialState);</code></pre>
<p><code>useState</code> 함수에 값을 전달하면 초깃값으로 지정할 수 있다.</p>
<h2 id="📘콜백으로-초깃값-지정하기"><strong>📘콜백으로 초깃값 지정하기</strong></h2>
<pre><code class="language-tsx">const [state, setState] = useState(() =&gt; {
  // 초기값을 계산
  return initialState;
});</code></pre>
<p>초깃값을 계산해서 넣는 경우 콜백을 사용하면 좋다.</p>
<pre><code class="language-jsx">function ReviewForm() {
  const savedValues = getSavedValues(); // ReviewForm을 렌더링할 때마다 실행됨
  const [values, setValues] = useState(savedValues);
  // ...
}</code></pre>
<p><code>getSavedValues</code> 라는 함수를 통해서 컴퓨터에 저장된 초깃값을 가져온다고 해보면,</p>
<p><code>savedValues</code> 라는 값은 처음 렌더링 한 번만 계산하면 되는데, 매 렌더링 때마다 불필요하게 <code>getSavedValues</code> 함수를 실행해서 저장된 값을 가져온다.</p>
<pre><code class="language-jsx">
function ReviewForm() {
  const [values, setValues] = useState(() =&gt; {
    const savedValues = getSavedValues(); // 처음 렌더링할 때만 실행됨
    return savedValues
  });
  // ...
}
</code></pre>
<p>이럴 때는 이렇게 콜백 형태로 초깃값을 지정해주면 처음 렌더링 할 때 한 번만 콜백을 실행해서 초깃값을 만들고, 그 이후로는 콜백을 실행하지 않기 때문에 <code>getSavedValues</code> 를 불필요하게 실행하지 않는다.</p>
<p>단, 이때 이 콜백 함수가 리턴할 때까지 리액트가 렌더링하지 않고 기다린다.
콜백 함수의 실행이 오래 걸릴 수록 초기 렌더링이 늦어진다.</p>
<h1 id="✨setter-함수-사용하기"><strong>✨Setter 함수 사용하기</strong></h1>
<pre><code class="language-tsx">const [state, setState] = useState(0);

const handleAddClick = () =&gt; {
  setState(state + 1);
}</code></pre>
<p>Setter 함수에다가 값을 전달하면, 해당하는 값으로 변경된다. </p>
<p>주의할 점) 배열이나 객체 같은 참조형은 반드시 새로운 값을 만들어서 전달해야 한다.</p>
<p><strong>참조형 State 사용의 잘못된 예</strong></p>
<pre><code class="language-tsx">
const [state, setState] = useState({ count: 0 });

const handleAddClick = () =&gt; {
  state.count += 1; // 참조형 변수의 프로퍼티를 수정
  setState(state); // 참조형이기 때문에 변수의 값(레퍼런스)는 변하지 않음
}
</code></pre>
<p><strong>참조형 State 사용의 올바른 예</strong></p>
<pre><code class="language-tsx">
const [state, setState] = useState({ count: 0 });

const handleAddClick = () =&gt; {
  setState({ ...state, count: state.count + 1 }); // 새로운 객체 생성
}
</code></pre>
<h2 id="📘콜백으로-state-변경"><strong>📘콜백으로 State 변경</strong></h2>
<pre><code class="language-jsx">
setState((prevState) =&gt; {
  // 다음 State 값을 계산
  return nextState;
});
</code></pre>
<p>만약 이전 State 값을 참조하면서 State를 변경하는 경우, 비동기 함수에서 State를 변경하게 되면 최신 값이 아닌 State 값을 참조하는 문제가 있다.
이럴 때는 콜백을 사용해서 처리할 수 있다. 파라미터로 올바른 State 값을 가져와서 사용할 수 있다.</p>
<p><strong>콜백으로 State를 변경하는 예시</strong></p>
<pre><code class="language-tsx">
const [count, setCount] = useState(0);

const handleAddClick = async () =&gt; {
  await addCount();
  setCount((prevCount) =&gt; prevCount + 1);
}
</code></pre>
<h1 id="✨입력-폼-form">✨입력 폼 (form)</h1>
<h2 id="📘onchange"><strong>📘onChange</strong></h2>
<ul>
<li>리액트에선 순수 HTML과 다르게 <strong><code>onChange</code> Prop을 사용하면 입력 값이 바뀔 때마다 핸들러 함수를 실행한다</strong>.</li>
<li><code>oninput</code> 이벤트와 비슷.</li>
</ul>
<h2 id="📘htmlfor"><strong>📘htmlFor</strong></h2>
<ul>
<li><code>&lt;label /&gt;</code> 태그에서 사용하는 속성인 <code>for</code> 는 자바스크립트 반복문 키워드인 <code>for</code> 와 겹치기 때문에 리액트에서는 <code>htmlFor</code> 를 사용한다.</li>
</ul>
<h2 id="🔖폼을-다루는-기본적인-방법"><strong>🔖폼을 다루는 기본적인 방법</strong></h2>
<ul>
<li>스테이트를 만들고 <code>target.value</code> 값을 사용해서 값을 변경해 줄 수 있다.</li>
<li><code>value</code> Prop으로 스테이트 값을 내려주고, <code>onChange</code> Prop으로 핸들러 함수를 넘긴다.</li>
</ul>
<pre><code class="language-jsx">
function TripSearchForm() {
  const [location, setLocation] = useState(&#39;Seoul&#39;);
  const [checkIn, setCheckIn] = useState(&#39;2022-01-01&#39;);
  const [checkOut, setCheckOut] = useState(&#39;2022-01-02&#39;);

  const handleLocationChange = (e) =&gt; setLocation(e.target.value);

  const handleCheckInChange = (e) =&gt; setCheckIn(e.target.value);

  const handleCheckOutChange = (e) =&gt; setCheckOut(e.target.value);

  return (
    &lt;form&gt;
      &lt;h1&gt;검색 시작하기&lt;/h1&gt;
      &lt;label htmlFor=&quot;location&quot;&gt;위치&lt;/label&gt;
      &lt;input id=&quot;location&quot; name=&quot;location&quot; value={location} placeholder=&quot;어디로 여행가세요?&quot; onChange={handleLocationChange} /&gt;
      &lt;label htmlFor=&quot;checkIn&quot;&gt;체크인&lt;/label&gt;
      &lt;input id=&quot;checkIn&quot; type=&quot;date&quot; name=&quot;checkIn&quot; value={checkIn} onChange={handleCheckInChange} /&gt;
      &lt;label htmlFor=&quot;checkOut&quot;&gt;체크아웃&lt;/label&gt;
      &lt;input id=&quot;checkOut&quot; type=&quot;date&quot; name=&quot;checkOut&quot; value={checkOut} onChange={handleCheckOutChange} /&gt;
      &lt;button type=&quot;submit&quot;&gt;검색&lt;/button&gt;
    &lt;/form&gt;
  )
}
</code></pre>
<h2 id="🔖폼-값을-객체-하나로-처리하기"><strong>🔖폼 값을 객체 하나로 처리하기</strong></h2>
<p>이벤트 객체의 <code>target.name</code> 과 <code>target.value</code> 값을 사용해서 값을 변경해 줄 수도 있었습니다.</p>
<p>이렇게하면 객체형 스테이트 하나만 가지고도 값을 처리할 수 있었죠.</p>
<pre><code class="language-jsx">
function TripSearchForm() {
  const [values, setValues] = useState({
    location: &#39;Seoul&#39;,
    checkIn: &#39;2022-01-01&#39;,
    checkOut: &#39;2022-01-02&#39;,
  })

  const handleChange = (e) =&gt; {
    const { name, value } = e.target;
    setValues((prevValues) =&gt; ({
      ...prevValues,
      [name]: value,
    }));
  }

  return (
    &lt;form&gt;
      &lt;h1&gt;검색 시작하기&lt;/h1&gt;
      &lt;label htmlFor=&quot;location&quot;&gt;위치&lt;/label&gt;
      &lt;input id=&quot;location&quot; name=&quot;location&quot; value={values.location} placeholder=&quot;어디로 여행가세요?&quot; onChange={handleChange} /&gt;
      &lt;label htmlFor=&quot;checkIn&quot;&gt;체크인&lt;/label&gt;
      &lt;input id=&quot;checkIn&quot; type=&quot;date&quot; name=&quot;checkIn&quot; value={values.checkIn} onChange={handleChange} /&gt;
      &lt;label htmlFor=&quot;checkOut&quot;&gt;체크아웃&lt;/label&gt;
      &lt;input id=&quot;checkOut&quot; type=&quot;date&quot; name=&quot;checkOut&quot; value={values.checkOut} onChange={handleChange} /&gt;
      &lt;button type=&quot;submit&quot;&gt;검색&lt;/button&gt;
    &lt;/form&gt;
  )
}
</code></pre>
<h2 id="🔖기본-submit-동작-막기"><strong>🔖기본 submit 동작 막기</strong></h2>
<ul>
<li>HTML 폼의 기본 동작은 <code>submit</code> 타입의 버튼을 눌렀을 때 페이지를 이동한다.</li>
<li>이벤트 객체의 <code>preventDefault</code> 를 사용하면 이 동작을 막을 수 있다.</li>
</ul>
<pre><code class="language-jsx">
const handleSubmit = (e) =&gt; {
  e.preventDefault();
  // ...
}</code></pre>
<h1 id="✨제어-컴포넌트controlled-component">✨제어 컴포넌트(Controlled Component)</h1>
<ul>
<li>인풋의 value값을 리액트에서 지정</li>
<li>리액트에서 사용하는 값과 살제 인풋 값이 항상 일치</li>
<li>값을 예측하기가 쉽고 인풋에 쓰는 값을 여러 군데서 쉽게 바꿀 수 있다는 장점이 있다.</li>
<li>State냐 Prop이냐는 중요하지 않고, 리액트로 <code>value</code> 를 지정한다는 것이 핵심</li>
</ul>
<pre><code class="language-jsx">function TripSearchForm() {
  const [values, setValues] = useState({
    location: &#39;Seoul&#39;,
    checkIn: &#39;2022-01-01&#39;,
    checkOut: &#39;2022-01-02&#39;,
  })

  const handleChange = (e) =&gt; {
    const { name, value } = e.target;
    setValues((prevValues) =&gt; ({
      ...prevValues,
      [name]: value,
    }));
  }

  return (
    &lt;form&gt;
      &lt;h1&gt;검색 시작하기&lt;/h1&gt;
      &lt;label htmlFor=&quot;location&quot;&gt;위치&lt;/label&gt;
      &lt;input id=&quot;location&quot; name=&quot;location&quot; value={values.location} placeholder=&quot;어디로 여행가세요?&quot; onChange={handleChange} /&gt;
      &lt;label htmlFor=&quot;checkIn&quot;&gt;체크인&lt;/label&gt;
      &lt;input id=&quot;checkIn&quot; type=&quot;date&quot; name=&quot;checkIn&quot; value={values.checkIn} onChange={handleChange} /&gt;
      &lt;label htmlFor=&quot;checkOut&quot;&gt;체크아웃&lt;/label&gt;
      &lt;input id=&quot;checkOut&quot; type=&quot;date&quot; name=&quot;checkOut&quot; value={values.checkOut} onChange={handleChange} /&gt;
      &lt;button type=&quot;submit&quot;&gt;검색&lt;/button&gt;
    &lt;/form&gt;
  )
}</code></pre>
<pre><code class="language-jsx">function TripSearchForm({ values, onChange }) {
  return (
    &lt;form&gt;
      &lt;h1&gt;검색 시작하기&lt;/h1&gt;
      &lt;label htmlFor=&quot;location&quot;&gt;위치&lt;/label&gt;
      &lt;input id=&quot;location&quot; name=&quot;location&quot; value={values.location} placeholder=&quot;어디로 여행가세요?&quot; onChange={onChange} /&gt;
      &lt;label htmlFor=&quot;checkIn&quot;&gt;체크인&lt;/label&gt;
      &lt;input id=&quot;checkIn&quot; type=&quot;date&quot; name=&quot;checkIn&quot; value={values.checkIn} onChange={onChange} /&gt;
      &lt;label htmlFor=&quot;checkOut&quot;&gt;체크아웃&lt;/label&gt;
      &lt;input id=&quot;checkOut&quot; type=&quot;date&quot; name=&quot;checkOut&quot; value={values.checkOut} onChange={onChange} /&gt;
      &lt;button type=&quot;submit&quot;&gt;검색&lt;/button&gt;
    &lt;/form&gt;
  )
}
</code></pre>
<h1 id="✨비제어-컴포넌트unontrolled-component">✨비제어 컴포넌트(Unontrolled Component)</h1>
<ul>
<li>인풋의 value값을 리액트에서 지정하지 않음</li>
<li>file input</li>
</ul>
<pre><code class="language-jsx">function TripSearchForm({ onSubmit }) {
  return (
    &lt;form onSubmit={onSubmit} &gt;
      &lt;h1&gt;검색 시작하기&lt;/h1&gt;
      &lt;label htmlFor=&quot;location&quot;&gt;위치&lt;/label&gt;
      &lt;input id=&quot;location&quot; name=&quot;location&quot; placeholder=&quot;어디로 여행가세요?&quot; /&gt;
      &lt;label htmlFor=&quot;checkIn&quot;&gt;체크인&lt;/label&gt;
      &lt;input id=&quot;checkIn&quot; type=&quot;date&quot; name=&quot;checkIn&quot; /&gt;
      &lt;label htmlFor=&quot;checkOut&quot;&gt;체크아웃&lt;/label&gt;
      &lt;input id=&quot;checkOut&quot; type=&quot;date&quot; name=&quot;checkOut&quot; /&gt;
      &lt;button type=&quot;submit&quot;&gt;검색&lt;/button&gt;
    &lt;/form&gt;
  )
}
</code></pre>
<p><code>onSubmit</code> 함수에서는 폼 태그를 참조할 수 있다.
👇🏻값들을 참조하려면 이벤트 객체의 <code>target</code> 활용👇🏻</p>
<pre><code class="language-jsx">const handleSubmit = (e) =&gt; {
  e.preventDefault();
  const form = e.target;
  const location = form[&#39;location&#39;].value;
  const checkIn = form[&#39;checkIn&#39;].value;
  const checkOut = form[&#39;checkOut&#39;].value;
  // ....
}</code></pre>
<p>폼 태그로 곧바로 <code>FormData</code> 를 바로 만드는 것도 가능하다.</p>
<pre><code class="language-jsx">const handleSubmit = (e) =&gt; {
  e.preventDefault();
  const form = e.target;
  const formData = new FormData(form);
  // ...
}</code></pre>
<h1 id="✨ref와-useref">✨ref와 useRef</h1>
<h2 id="📘ref-객체-생성"><strong>📘Ref 객체 생성</strong></h2>
<pre><code class="language-jsx">import { useRef } from &#39;react&#39;;

// ...

const ref = useRef();
</code></pre>
<p><code>useRef</code> 함수로 Ref 객체를 만들 수 있다.</p>
<h2 id="📘-ref-prop-사용하기"><strong>📘 <code>ref</code> Prop 사용하기</strong></h2>
<pre><code class="language-jsx">const ref = useRef();

// ...

&lt;div ref={ref}&gt; ... &lt;/div&gt;</code></pre>
<p><code>ref</code> Prop에다가 앞에서 만든 Ref 객체를 내려주면 된다.</p>
<h2 id="📘ref-객체에서-dom-노드-참조하기"><strong>📘Ref 객체에서 DOM 노드 참조하기</strong></h2>
<pre><code class="language-jsx">const node = ref.current;
if (node) {
  // node 를 사용하는 코드
}</code></pre>
<p>Ref 객체의 <code>current</code> 라는 프로퍼티를 사용하면 DOM 노드를 참조할 수 있다.</p>
<p><code>current</code> 값은 없을 수도 있으니까 반드시 값이 존재하는지 검사하고 사용해야 하는 점!!!</p>
<h3 id="예시-이미지-크기-구하기"><strong>예시: 이미지 크기 구하기</strong></h3>
<p><code>img</code> 노드의 크기를 <code>ref</code> 를 활용해서 출력하는 예시</p>
<p><code>img</code> 노드에는 너비 값인 <code>width</code> 와 높이 값인 <code>height</code> 라는 속성이 있다.</p>
<p>Ref 객체의 <code>current</code> 로 DOM 노드를 참조해서 두 속성 값을 가져왔다.</p>
<pre><code class="language-jsx">
import { useRef } from &#39;react&#39;;

function Image({ src }) {
  const imgRef = useRef();

  const handleSizeClick = () =&gt; {
    const imgNode = imgRef.current;
    if (!imgNode) return;

    const { width, height } = imgNode;
    console.log(`${width} x ${height}`);
  };

  return (
    &lt;div&gt;
      &lt;img src={src} ref={imgRef} alt=&quot;크기를 구할 이미지&quot; /&gt;
      &lt;button onClick={handleSizeClick}&gt;크기 구하기&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<h1 id="✨사이드-이펙트side-effect"><strong>✨사이드 이펙트(Side Effect)</strong></h1>
<ul>
<li>사이드 이펙트는 한국어로는 &#39;부작용&#39;이라는 뜻</li>
</ul>
<p>예를 들면 감기약을 먹었을 때 </p>
<p>감기 증상은 없어졌지만 (작용) <strong>피부가 붉게 올라오면 (부작용)</strong></p>
<pre><code class="language-tsx">
let count = 0;

function add(a, b) {
  const result = a + b;
  count += 1; // 함수 외부의 값을 변경
  return result;
}

const val1 = add(1, 2);
const val2 = add(-4, 5);</code></pre>
<ul>
<li><code>add</code> 함수는 실행하면서 함수 외부의 상태(count 변수)가 바뀌기 때문에, 이런 함수를 <strong>&quot;사이드 이펙트가 있다&quot;</strong>고 한다.</li>
<li><code>console.log</code> 함수를 사용하면 값을 계산해서 리턴하는 게 아니라 웹 브라우저 콘솔 창에 문자열을 출력한다. ⇒ 외부 상태를 변경해서 문자열을 출력하는 것이다.</li>
</ul>
<p>함수 안에서 함수 바깥에 있는 값이나 상태를 변경하는 걸 &#39;사이드 이펙트&#39;라고 부른다.</p>
<h2 id="📘사이드-이펙트와-useeffect"><strong>📘사이드 이펙트와 useEffect</strong></h2>
<p><code>useEffect</code> 는 리액트 컴포넌트 함수 안에서 <strong>사이드 이펙트를 실행하고 싶을 때</strong> 사용하는 함수다.</p>
<p>예를 들면 DOM 노드를 직접 변경한다거나,</p>
<p>브라우저에 데이터를 저장하고,</p>
<p>네트워크 리퀘스트를 보내는 것</p>
<p>주로 <strong>리액트 외부에 있는 데이터나 상태를 변경할 때</strong> 사용한다.</p>
<h2 id="📘페이지-정보-변경"><strong>📘페이지 정보 변경</strong></h2>
<pre><code class="language-jsx">
useEffect(() =&gt; {
  document.title = title; // 페이지 데이터를 변경
}, [title]);
</code></pre>
<h2 id="📘네트워크-요청"><strong>📘네트워크 요청</strong></h2>
<pre><code class="language-jsx">
useEffect(() =&gt; {
  fetch(&#39;https://example.com/data&#39;) // 외부로 네트워크 리퀘스트
    .then((response) =&gt; response.json())
    .then((body) =&gt; setData(body));
}, [])
</code></pre>
<h2 id="📘데이터-저장"><strong>📘데이터 저장</strong></h2>
<pre><code class="language-jsx">
useEffect(() =&gt; {
  localStorage.setItem(&#39;theme&#39;, theme); // 로컬 스토리지에 테마 정보를 저장
}, [theme]);
</code></pre>
<p>참고: <code>localStorage</code> 는 웹 브라우저에서 데이터를 저장할 수 있는 기능</p>
<h2 id="📘타이머"><strong>📘타이머</strong></h2>
<pre><code class="language-jsx">
useEffect(() =&gt; {
  const timerId = setInterval(() =&gt; {
    setSecond((prevSecond) =&gt; prevSecond + 1);
  }, 1000); // 1초마다 콜백 함수를 실행하는 타이머 시작

  return () =&gt; {
    clearInterval(timerId);
  }
}, []);
</code></pre>
<p>참고: <code>setInterval</code> 이라는 함수를 쓰면 일정한 시간마다 콜백 함수를 실행할 수 있다.</p>
<h1 id="✨useeffect를-쓰면-좋은-경우"><strong>✨useEffect를 쓰면 좋은 경우</strong></h1>
<p>데이터 불러오는 기능 만들 때 처음에는 <code>onclick</code> 이벤트 핸들러에서 네트워크 리퀘스트를 보냈고, 페이지가 열리자마자 데이터를 불러오기 위해서 <code>useEffect</code> 를 사용하는 걸로 바꿨다.</p>
<p>하지만 만약 둘 다 가능한 경우라면 핸들러 함수를 써야 하는 걸까? 아니면 <code>useEffect</code> 를 써야 하는 걸까?</p>
<p>정해진 규칙은 없습니다. 하지만 <code>useEffect</code> 는 쉽게 말해서 &#39;동기화&#39;에 쓰면 유용한 경우가 많다. (동기화는 컴포넌트 안에 데이터와 리액트 바깥에 있는 데이터를 일치시키는 것)</p>
<h2 id="📍핸들러-함수만-사용한-예시"><strong>📍핸들러 함수만 사용한 예시</strong></h2>
<pre><code class="language-jsx">// 인풋 입력에 따라 페이지 제목을 바꾸는 컴포넌트
import { useState } from &#39;react&#39;;

const INITIAL_TITLE = &#39;Untitled&#39;;

function App() {
  const [title, setTitle] = useState(INITIAL_TITLE);

  const handleChange = (e) =&gt; {
    const nextTitle = e.target.value;
    setTitle(nextTitle);
    document.title = nextTitle;
  };

  const handleClearClick = () =&gt; {
    const nextTitle = INITIAL_TITLE;
    setTitle(nextTitle);
    document.title = nextTitle;
  };

  return (
    &lt;div&gt;
      &lt;input value={title} onChange={handleChange} /&gt;
      &lt;button onClick={handleClearClick}&gt;초기화&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<ul>
<li><code>handleChange</code> 함수와 <code>handleClearClick</code> 함수가 있다.</li>
<li>모두 <code>title</code> 스테이트를 변경한 후에 <code>document.title</code> 도 함께 변경하고 있다.</li>
<li>여기서 <code>document.title</code> 값을 바꾸는 건 외부의 상태를 변경하는 거니까 사이드 이펙트다.</li>
</ul>
<p>만약 새로 함수를 만들어서 <code>setTitle</code> 을 사용하는 코드를 추가할 때마다 <code>document.title</code> 값도 변경해야 한다는 걸 기억해뒀다가 관련된 코드를 작성해야 한다는 점</p>
<h2 id="📍useeffect를-사용한-예시"><strong>📍useEffect를 사용한 예시</strong></h2>
<pre><code class="language-jsx">
import { useEffect, useState } from &#39;react&#39;;

const INITIAL_TITLE = &#39;Untitled&#39;;

function App() {
  const [title, setTitle] = useState(INITIAL_TITLE);

  const handleChange = (e) =&gt; {
    const nextTitle = e.target.value;
    setTitle(nextTitle);
  };

  const handleClearClick = () =&gt; {
    setTitle(INITIAL_TITLE);
  };

  useEffect(() =&gt; {
    document.title = title;
  }, [title]);

  return (
    &lt;div&gt;
      &lt;input value={title} onChange={handleChange} /&gt;
      &lt;button onClick={handleClearClick}&gt;초기화&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<ul>
<li><code>useEffect</code> 를 사용한 예시에서는 <code>document</code>를 다루는 사이드 이펙트 부분만 따로 처리하고 있다.</li>
<li>이렇게 하면 <code>setTitle</code> 함수를 쓸 때마다 <code>document.title</code> 을 변경하는 코드를 신경 쓰지 않아도 되니까 편리하다.</li>
<li>게다가 처음 렌더링 되었을 때 &#39;Untitled&#39;라고 페이지 제목을 변경하는 효과까지 낼 수 있다.</li>
<li><code>useEffect</code> 는 리액트 안과 밖의 데이터를 일치시키는데 활용하면 좋다. (<code>useEffect</code> 를 사용했을 때 반복되는 코드를 줄이고, 동작을 쉽게 예측할 수 있는 코드를 작성할 수 있기 때문)</li>
</ul>
<h1 id="✨정리-함수-cleanup-function"><strong>✨정리 함수 (Cleanup Function)</strong></h1>
<pre><code class="language-jsx">useEffect(() =&gt; {
  // 사이드 이펙트

  return () =&gt; {
    // 사이드 이펙트에 대한 정리
  }
}, [dep1, dep2, dep3, ...]);
</code></pre>
<ul>
<li><code>useEffect</code> 의 콜백 함수에서 사이드 이펙트를 만들면 정리가 필요한 경우가 있다.</li>
<li>이럴 때 콜백 함수에서 리턴 값으로 정리하는 함수를 리턴할 수 있다.</li>
<li>리턴한 정리 함수에서는 사이드 이펙트에 대한 뒷정리를 한다.</li>
</ul>
<p>예를 들면 이미지 파일 미리보기를 구현할 때 Object URL을 만들어서 브라우저의 메모리를 할당(<code>createObjectURL</code>) 했다. 정리 함수에서는 이때 할당한 메모리를 다시 해제(<code>revokeObjectURL</code>)했다.</p>
<h2 id="📘정리-함수가-실행되는-시점"><strong>📘정리 함수가 실행되는 시점</strong></h2>
<p>쉽게 말해서 <strong>콜백을 한 번 실행했으면, 정리 함수도 반드시 한 번 실행된다</strong>.</p>
<p>정확히는 새로운 콜백 함수가 호출되기 전에 실행되거나 (앞에서 실행한 콜백의 사이드 이펙트를 정리), 컴포넌트가 화면에서 사라지기 전에 실행된다 (맨 마지막으로 실행한 콜백의 사이드 이펙트를 정리).</p>
<h2 id="📍예시-타이머"><strong>📍예시: 타이머</strong></h2>
<pre><code class="language-jsx">
import { useEffect, useState } from &#39;react&#39;;

function Timer() {
  const [second, setSecond] = useState(0);

  useEffect(() =&gt; {
    const timerId = setInterval(() =&gt; {
      console.log(&#39;타이머 실행중 ... &#39;);
      setSecond((prevSecond) =&gt; prevSecond + 1);
    }, 1000);
    console.log(&#39;타이머 시작 🏁&#39;);

    return () =&gt; {
      clearInterval(timerId);
      console.log(&#39;타이머 멈춤 ✋&#39;);
    };
  }, []);

  return &lt;div&gt;{second}&lt;/div&gt;;
}

function App() {
  const [show, setShow] = useState(false);

  const handleShowClick = () =&gt; setShow(true);
  const handleHideClick = () =&gt; setShow(false);

  return (
    &lt;div&gt;
      {show &amp;&amp; &lt;Timer /&gt;}
      &lt;button onClick={handleShowClick}&gt;보이기&lt;/button&gt;
      &lt;button onClick={handleHideClick}&gt;감추기&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<ul>
<li>일정한 시간 간격마다 콜백 함수를 실행하는 <code>setInterval</code> 이라는 함수도 정리가 필요한 사이드 이펙트다.</li>
<li>이 컴포넌트는 렌더링이 끝나면 타이머를 시작하고, 화면에서 사라지면 타이머를 멈춘다.</li>
<li>사용자가 &#39;보이기&#39; 버튼을 눌렀을 때 <code>show</code> 값이 참으로 바뀌면서 다시 렌더링된다.</li>
<li>조건부 렌더링에 의해서 <code>Timer</code> 컴포넌트를 렌더링한다.<ul>
<li><code>Timer</code> 컴포넌트에서는 <code>useEffect</code> 에서 타이머를 시작하고, 정리 함수를 리턴한다.</li>
<li>콘솔에는 &#39;타이머 시작 🏁&#39;이 출력</li>
<li>다시 사용자가 &#39;감추기&#39; 버튼을 누르면 <code>show</code> 값이 거짓으로 바뀌면서 다시 렌더링된다.</li>
<li>조건부 렌더링에 의해서 이제 <code>Timer</code> 컴포넌트를 렌더링 하지 않는다.</li>
<li>그럼 리액트에선 마지막으로 앞에서 기억해뒀던 정리 함수를 실행해준다.</li>
<li>타이머를 멈추고 콘솔에는 &#39;타이머 멈춤 ✋&#39;이 출력된다.</li>
</ul>
</li>
</ul>
<p>정리 함수를 리턴하면 사이드 이펙트를 정리하고 안전하게 사용할 수 있다는 것.</p>
<h1 id="✨리액트-훅">✨리액트 훅</h1>
<p>리액트가 제공하는 기능에 연결해서 그 값이나 기능을 사용하는 함수</p>
<h2 id="📍hook의-규칙"><strong>📍Hook의 규칙</strong></h2>
<ul>
<li>반드시 리액트 컴포넌트 함수(Functional Component) 안에서 사용해야 함</li>
<li>컴포넌트 함수의 최상위에서만 사용 (조건문, 반복문 안에서 못 씀)</li>
</ul>
<h1 id="📘usestate"><strong>📘useState</strong></h1>
<p><strong>State 사용하기</strong></p>
<pre><code class="language-jsx">const [state, setState] = useState(initialState);</code></pre>
<p><strong>콜백으로 초깃값 지정하기</strong></p>
<p>초깃값을 계산하는 코드가 복잡한 경우에 활용</p>
<pre><code class="language-jsx">const [state, setState] = useState(() =&gt; {
  // ...
  return initialState;
});</code></pre>
<p><strong>State 변경</strong></p>
<pre><code class="language-jsx">setState(nextState);</code></pre>
<p><strong>이전 State를 참조해서 State 변경</strong></p>
<p>비동기 함수에서 최신 State 값을 가져와서 새로운 State 값을 만들 때</p>
<pre><code class="language-jsx">setState((prevState) =&gt; {
  // ...
  return nextState
});</code></pre>
<h1 id="📘useeffect"><strong>📘useEffect</strong></h1>
<p>컴포넌트 함수에서 사이드 이펙트(리액트 외부의 값이나 상태를 변경할 때)에 활용하는 함수</p>
<p><strong>처음 렌더링 후에 한 번만 실행</strong></p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  // ...
}, []);</code></pre>
<p><strong>렌더링 후에 특정 값이 바뀌었으면 실행</strong></p>
<ul>
<li>참고로 처음 렌더링 후에도 한 번 실행됨</li>
</ul>
<pre><code class="language-jsx">useEffect(() =&gt; {
  // ...
}, [dep1, dep2, dep3, ...]);</code></pre>
<p><strong>사이드 이펙트 정리(Cleanup)하기</strong></p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  // 사이드 이펙트

  return () =&gt; {
    // 정리
  }
}, [dep1, dep2, dep3, ...]);</code></pre>
<h1 id="📘useref"><strong>📘useRef</strong></h1>
<p><strong>생성하고 DOM 노드에 연결하기</strong></p>
<pre><code class="language-jsx">const ref = useRef();

// ...

return &lt;div ref={ref}&gt;안녕 리액트!&lt;/div&gt;;</code></pre>
<p><strong>DOM 노드 참조하기</strong></p>
<pre><code class="language-jsx">const node = ref.current;
if (node) {
  // node를 사용하는 코드
}</code></pre>
<h1 id="📘usecallback"><strong>📘useCallback</strong></h1>
<p>함수를 매번 새로 생성하는 것이 아니라 디펜던시 리스트가 변경될 때만 함수를 생성</p>
<pre><code class="language-jsx">const handleLoad = useCallback((option) =&gt; {
  // ...
}, [dep1, dep2, dep3, ...]);</code></pre>
<h1 id="📘custom-hook"><strong>📘Custom Hook</strong></h1>
<p>자주 사용하는 Hook 코드들을 모아서 함수로 만들 수 있다.</p>
<p>이때 <code>useOOO</code> 처럼 반드시 맨 앞에 <code>use</code> 라는 단어를 붙여서 다른 개발자들이 Hook이라는 걸 알 수 있게 해줘야 한다.</p>
<p><a href="https://usehooks.com/">useHooks – The React Hooks Library</a></p>
<p><a href="https://github.com/streamich/react-use">https://github.com/streamich/react-use</a></p>
<h3 id="📗useasync"><strong>📗useAsync</strong></h3>
<p>비동기 함수의 로딩, 에러 처리를 하는 데 사용할 수 있는 함수</p>
<p>함수를 <code>asyncFunction</code> 이라는 파라미터로 추상화해서 <code>wrappedFunction</code> 이라는 함수를 만들어 사용하는 방식을 눈여겨보자.</p>
<pre><code class="language-jsx">
function useAsync(asyncFunction) {
  const [pending, setPending] = useState(false);
  const [error, setError] = useState(null);

  const wrappedFunction = useCallback(async (...args) =&gt; {
    setPending(true);
    setError(null);
    try {
      return await asyncFunction(...args);
    } catch (error) {
      setError(error);
    } finally {
      setPending(false);
    }
  }, [asyncFunction]);

  return [pending, error, wrappedFunction];
}
</code></pre>
<h3 id="📗usetoggle"><strong>📗useToggle</strong></h3>
<p><code>toggle</code> 함수를 호출할 때마다 <code>value</code> 값이 참/거짓으로 번갈아가며 바뀐다.</p>
<p>ON/OFF 스위치 같은 걸 만들 때 유용함</p>
<pre><code class="language-jsx">
function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  const toggle = () =&gt; setValue((prevValue) =&gt; !prevValue);
  return [value, toggle];
}
</code></pre>
<h3 id="📗usetimer"><strong>📗useTimer</strong></h3>
<p><code>start</code> 를 실행하면 <code>callback</code> 이라는 파라미터로 넘겨 준 함수를 <code>timeout</code> 밀리초 마다 실행하고, <code>stop</code> 을 실행하면 멈춘다.</p>
<p><code>setInterval</code> 이란 함수는 웹 브라우저에 함수를 등록해서 일정한 시간 간격마다 실행한다. 실행할 때마다 사이드 이펙트를 만들고, 사용하지 않으면 정리를 해줘야 한다.</p>
<p><code>clearInterval</code> 이라는 함수를 실행해서 사이드 이펙트를 정리하는 부분을 눈여겨보자.</p>
<pre><code class="language-jsx">
function useTimer(callback, timeout) {
  const [isRunning, setIsRunning] = useState(false);

  const start = () =&gt; setIsRunning(true);

  const stop = () =&gt; setIsRunning(false);

  useEffect(() =&gt; {
    if (!isRunning) return;

    const timerId = setInterval(callback, timeout); // 사이드 이펙트 발생
    return () =&gt; {
      clearInterval(timerId); // 사이드 이펙트 정리
    };
  }, [isRunning, callback, timeout]);

  return [start, stop];
}</code></pre>
<h1 id="✨디펜던시exhaustive-deps">✨디펜던시(exhaustive-deps)</h1>
<h2 id="📘exhaustive-deps-규칙"><strong>📘exhaustive-deps 규칙</strong></h2>
<p>아래 코드는 num 버튼을 누르면 <code>num</code> 스테이트 값이 증가되고,</p>
<p>count 버튼을 누르면 <code>count</code> 스테이트 값을 증가시키는 컴포넌트다.</p>
<p>이때 <code>count</code> 스테이트 값을 증가시키면서 콘솔에는 <code>num</code> 스테이트 값을 출력한다.</p>
<p><code>useEffect</code> Hook에서는 1초마다 <code>addCount</code> 함수를 실행하는 타이머를 실행한다.</p>
<pre><code class="language-jsx">
import { useEffect, useState } from &#39;react&#39;;

function App() {
  const [count, setCount] = useState(0);
  const [num, setNum] = useState(0);

  const addCount = () =&gt; {
    setCount(c =&gt; c + 1);
    console.log(`num: ${num}`);
  }

  const addNum = () =&gt; setNum(n =&gt; n + 1);

  useEffect(() =&gt; {
    console.log(&#39;timer start&#39;);
    const timerId = setInterval(() =&gt; {
      addCount();
    }, 1000);

    return () =&gt; {
      clearInterval(timerId);
      console.log(&#39;timer end&#39;);
    };
  }, []);

  return (
    &lt;div&gt;
      &lt;button onClick={addCount}&gt;count: {count}&lt;/button&gt;
      &lt;button onClick={addNum}&gt;num: {num}&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>이 코드를 실행해보면 1초마다 <code>count</code> 값이 증가하는데,</p>
<p>버튼을 클릭해서 <code>num</code> 스테이트의 값이 바뀌더라도 콘솔 출력에서는 숫자가 바뀌지 않고 0만 계속 출력된다는 문제가 있다.</p>
<p>그 이유는 <code>useEffect</code> 안에서 <code>addCount</code> 라는 함수를 사용하는데, 이 함수에서는 <code>num</code> 스테이트 값을 잘못 참조하기 때문이다. (과거의 <code>num</code> 스테이트 값을 계속해서 참조하고 있기 때문)</p>
<p>⚠️이런 문제점을 경고해주는 규칙이 <code>react-hooks/exhaustive-deps</code> 라는 규칙</p>
<p>리액트에서는 <strong>Prop이나 State와 관련된 값은 되도록이면 빠짐없이 디펜던시에 추가</strong>해서 항상 최신 값으로 <code>useEffect</code> 나 <code>useCallback</code> 을 사용하도록 권장하고 있다.</p>
<pre><code class="language-jsx">
import { useEffect, useState } from &quot;react&quot;;

function App() {
  const [count, setCount] = useState(0);
  const [num, setNum] = useState(0);

  const addCount = () =&gt; {
    setCount((c) =&gt; c + 1);
    console.log(`num: ${num}`);
  };

  const addNum = () =&gt; setNum((n) =&gt; n + 1);

  useEffect(() =&gt; {
    console.log(&#39;timer start&#39;);
    const timerId = setInterval(() =&gt; {
      addCount();
    }, 1000);

    return () =&gt; {
      clearInterval(timerId);
      console.log(&#39;timer end&#39;);
    };
  }, [addCount]);

  return (
    &lt;div&gt;
      &lt;button onClick={addCount}&gt;count: {count}&lt;/button&gt;
      &lt;button onClick={addNum}&gt;num: {num}&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>하지만 이렇게 하면 한 가지 문제가 있다. </p>
<p>실행해서 콘솔을 확인해보면 <code>count</code> 가 바뀔 때마다 타이머를 새로 시작하고 종료하는 걸 반복한다.</p>
<p><code>addCount</code> 라는 함수는 렌더링 할 때마다 새로 만들어지는데, 이걸 디펜던시 리스트에 추가했기 때문에 <code>useEffect</code> 의 콜백이 매번 불필요하게 실행되는 버그가 있다.</p>
<h2 id="📘usecallback으로-함수-재사용하기"><strong>📘useCallback으로 함수 재사용하기</strong></h2>
<pre><code class="language-jsx">
import { useCallback, useEffect, useState } from &quot;react&quot;;

function App() {
  const [count, setCount] = useState(0);
  const [num, setNum] = useState(0);

  const addCount = useCallback(() =&gt; {
    setCount((c) =&gt; c + 1);
    console.log(`num: ${num}`);
  }, [num]);

  const addNum = () =&gt; setNum((n) =&gt; n + 1);

  useEffect(() =&gt; {
    console.log(&#39;timer start&#39;);
    const timerId = setInterval(() =&gt; {
      addCount();
    }, 1000);

    return () =&gt; {
      clearInterval(timerId);
      console.log(&#39;timer end&#39;);
    };
  }, [addCount]);

  return (
    &lt;div&gt;
      &lt;button onClick={addCount}&gt;count: {count}&lt;/button&gt;
      &lt;button onClick={addNum}&gt;num: {num}&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>디펜던시 리스트에 추가한 함수가 매번 바뀌는 문제를 해결하려면 함수를 <code>useCallback</code> 으로 감싸주면 된다.</p>
<p><code>useCallback</code> 을 사용하면 함수를 매번 생성하는 게 아니라 리액트에다 함수를 기억해둘 수 있다.</p>
<p>이때 리액트는 <code>useCallback</code> 의 디펜던시 리스트 값이 바뀔 때만 함수를 새로 만들어준다. <code>addCount</code> 함수에서는 <code>num</code> 이라는 스테이트를 참조하고 있으니까 이 값을 디펜던시 리스트에 추가했다. 이렇게하면 리액트는 <code>num</code> 값이 바뀔 때만 <code>addCount</code> 함수를 새로 만들 것이다.</p>
<p><code>useEffect</code> 의 디펜던시로 <code>addCount</code> 가 들어가 있으니까, 결론적으로 <code>useEffect</code> 의 콜백은 <code>num</code> 값이 바뀔 때만 새로 실행된다.</p>
<p><strong>이런 식으로 컴포넌트 안에서 만든 함수를 디펜던시 리스트에 사용할 때는 <code>useCallback</code> 훅으로 매번 함수를 새로 생성하는 걸 막을 수 있다.</strong></p>
<h2 id="📘파라미터를-활용하자"><strong>📘파라미터를 활용하자</strong></h2>
<p><code>addCount</code> 라는 함수에서 <code>num</code> 값을 꼭 직접 참조할 필요는 없다.</p>
<p>그래서 <code>useCallback</code> 을 쓰지 않고, 아래처럼 파라미터로 받아오게 할 수 있다.</p>
<p>이렇게 하면 <code>addCount</code> 함수 자체만 놓고 보면 바깥에 있는 스테이트 값을 직접적으로 참조하지 않기 때문에 오래된 스테이트 값을 참조할 염려가 없다.</p>
<pre><code class="language-jsx">
import { useEffect, useState } from &quot;react&quot;;

function App() {
  const [count, setCount] = useState(0);
  const [num, setNum] = useState(0);

  const addCount = (log) =&gt; {
    setCount((c) =&gt; c + 1);
    console.log(log);
  }

  const addNum = () =&gt; setNum((n) =&gt; n + 1);

  useEffect(() =&gt; {
    console.log(&#39;timer start&#39;);
    const timerId = setInterval(() =&gt; {
      addCount(`num ${num}`);
    }, 1000);

    return () =&gt; {
      clearInterval(timerId);
      console.log(&#39;timer end&#39;);
    };
  }, [num]);

  return (
    &lt;div&gt;
      &lt;button onClick={addCount}&gt;count: {count}&lt;/button&gt;
      &lt;button onClick={addNum}&gt;num: {num}&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>디펜던시 리스트를 이렇게 바꾸면 <code>num</code> 값이 바뀔 때마다 타이머를 재시작한다는 게 좀 더 명확해진다.</p>
<p>Prop이나 State 값을 사용할 때는 이렇게 되도록이면 파라미터로 넘겨서 사용하면,</p>
<p>어떻게 사용되는지 코드에서 명확하게 보여줄 수 있다.</p>
<h1 id="✨context">✨Context</h1>
<ul>
<li>많은 컴포넌트에서 사용하는 데이터를 반복적인 prop(prop drilling) 전달 없이 공유</li>
</ul>
<p>prop Drilling</p>
<p>상위 컴포넌트에서 하위 컴포넌트로 반복해서 prop을 내려주는 상황</p>
<p>→ 해결) context를 만듦</p>
<h2 id="📘context-만들기"><strong>📘Context 만들기</strong></h2>
<p>Context는 <code>createContext</code> 라는 함수를 통해 만들 수 있다.</p>
<pre><code class="language-jsx">import { createContext } from &#39;react&#39;;

const LocaleContext = createContext();</code></pre>
<p>참고로 이때 아래처럼 기본값을 넣어줄 수도 있다.</p>
<pre><code class="language-jsx">
import { createContext } from &#39;react&#39;;

const LocaleContext = createContext(&#39;ko&#39;);</code></pre>
<h2 id="📘context-적용하기"><strong>📘Context 적용하기</strong></h2>
<p>Context를 쓸 때는 반드시 값을 공유할 범위를 정하고 써야 한다.</p>
<p>이때 범위는 Context 객체에 있는 <code>Provider</code> 라는 컴포넌트로 정해줄 수 있다.</p>
<p>이때 <code>Provider</code>의 <code>value</code> prop으로 공유할 값을 내려주면 된다.</p>
<pre><code class="language-jsx">
import { createContext } from &#39;react&#39;;

const LocaleContext = createContext(&#39;ko&#39;);

function App() {
  return (
    &lt;div&gt;
       ... 바깥의 컴포넌트에서는 LocaleContext 사용불가

       &lt;LocaleContext.Provider value=&quot;en&quot;&gt;
          ... Provider 안의 컴포넌트에서는 LocaleContext 사용가능
       &lt;/LocaleContext.Provider&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<h2 id="📘context-값-사용하기"><strong>📘Context 값 사용하기</strong></h2>
<p><code>useContext</code> 라는 Hook을 사용하면 값을 가져와 사용할 수 있다.</p>
<p>이때 아규먼트로는 사용할 Context를 넘겨주면 된다.</p>
<pre><code class="language-jsx">
import { createContext, useContext } from &#39;react&#39;;

const LocaleContext = createContext(&#39;ko&#39;);

function Board() {
  const locale = useContext(LocaleContext);
  return &lt;div&gt;언어: {locale}&lt;/div&gt;;
}

function App() {
  return (
    &lt;div&gt;
       &lt;LocaleContext.Provider value=&quot;en&quot;&gt;
          &lt;Board /&gt;
       &lt;/LocaleContext.Provider&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="📘state-hook와-함께-활용하기"><strong>📘State, Hook와 함께 활용하기</strong></h2>
<p>Provider 역할을 하는 컴포넌트를 하나 만들고, 여기서 State를 만들어서 <code>value</code> 로 넘겨줄 수 있다.</p>
<p>그리고 아래의 <code>useLocale</code> 같이 <code>useContext</code> 를 사용해서 값을 가져오는 커스텀 Hook을 만들 수도 있다. 이렇게 하면 Context에서 사용하는 State 값은 반드시 우리가 만든 함수를 통해서만 쓸 수 있기 때문에 안전한 코드를 작성하는데 도움이 된다.</p>
<pre><code class="language-jsx">
import { createContext, useContext, useState } from &#39;react&#39;;

const LocaleContext = createContext({});

export function LocaleProvider({ children }) {
  const [locale, setLocale] = useState();
  return (
    &lt;LocaleContext.Provider value={{ locale, setLocale }}&gt;
      {children}
    &lt;/LocaleContext.Provider&gt;
  );
}

export function useLocale() {
  const context = useContext(LocaleContext);

  if (!context) {
    throw new Error(&#39;반드시 LocaleProvider 안에서 사용해야 합니다&#39;);
  }

  const { locale } = context;
  return locale;
}

export function useSetLocale() {
  const context = useContext(LocaleContext);

  if (!context) {
    throw new Error(&#39;반드시 LocaleProvider 안에서 사용해야 합니다&#39;);
  }

  const { setLocale } = context;
  return setLocale;
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코어자바스크립트] 1. 데이터 타입]]></title>
            <link>https://velog.io/@bori_note/%EC%BD%94%EC%96%B4%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-1.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@bori_note/%EC%BD%94%EC%96%B4%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-1.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85</guid>
            <pubDate>Sat, 13 Apr 2024 05:41:08 GMT</pubDate>
            <description><![CDATA[<h1 id="✨데이터-타입의-종류">✨데이터 타입의 종류</h1>
<p><img src="https://velog.velcdn.com/images/bori_note/post/0459feff-a9e6-4ff3-a695-4d68941686a2/image.png" alt=""></p>
<h2 id="📘기본형">📘기본형</h2>
<ul>
<li>기본형 데이터를 변수에 할당하면 값 자체가 변수에 복제된다. 변수와 값 사이에 직접적인 관계를 갖지 않고, 값이 변경되어도 다른 변수에는 영향을 주지 않는다. (불변값)</li>
<li>숫자, 문자열, 불리언, null, undefined, Symbol </li>
</ul>
<h2 id="📘참조형">📘참조형</h2>
<ul>
<li>값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제 (가변값)</li>
<li>참조형 데이터를 변수에 할당하면 값이 담긴 메모리 주소(참조)가 변수에 복제된다.</li>
<li>배열, 객체, 함수</li>
</ul>
<h1 id="✨데이터타입에-관한-배경지식">✨데이터타입에 관한 배경지식</h1>
<p>과거에는 메모리 용량이 제한적이었기 때문에 작은 크기의 정수형 타입(short 등)을 사용하여 메모리를 절약했다. 하지만 특정 범위를 벗어나는 값이 입력되면 오류가 발생하거나 잘못된 값이 저장되는 문제가 있었다. 이를 해결하기 위해 사용자가 직접 형변환을 해야 했다.</p>
<p>현대의 자바스크립트와 같은 프로그래밍 언어는 메모리 용량이 훨씬 커졌기 때문에 이러한 메모리 제약이 크게 줄었다. 숫자형 데이터의 크기에 대한 걱정이 덜해졌고, 대부분의 경우에는 64비트, 즉 <strong>8바이트</strong>를 사용하여 숫자를 저장한다. 그래서 형변환이나 메모리 제약과 같은 문제를 걱정할 필요가 없어졌다.</p>
<blockquote>
<p>모든 데이터는 바이트 단위의 식별자 (<strong>메모리 주솟값</strong>)을 통해 서로 구분하고 연결할 수 있다.</p>
</blockquote>
<h2 id="📘식별자와-변수">📘식별자와 변수</h2>
<p>변수: 변할 수 있는 수(변경 가능한 데이터가 담길 수 있는 공간)
식별자: 어떤 데이터를 식별하는 데 사용하는 이름 (변수명)</p>
<h1 id="✨변수-선언과-데이터-할당">✨변수 선언과 데이터 할당</h1>
<blockquote>
<p>변수 영역: 변수와 상수를 구분 짓는 곳
데이터 영역: 불변성 여부를 구분하는 곳</p>
</blockquote>
<pre><code class="language-js">var a = &#39;abc&#39;</code></pre>
<p><img src="https://velog.velcdn.com/images/bori_note/post/25dd3b18-6693-49d5-9ebf-d37f66c015a8/image.png" alt=""></p>
<pre><code class="language-js">var obj 1 = { 
    a: 1 , 
    b: &#39;bbb&#39; 
}; </code></pre>
<p><img src="https://velog.velcdn.com/images/bori_note/post/803d3928-066a-4be2-b3fc-0ad66026602c/image.png" alt="">
참조형은 객체의 변수 영역이 별도로 존재한다. 객체가 별도로 할애한 영역은 변수 영역일 뿐 데이터 영역은 기존의 공간의 메모리 공간을 그대로 활용하고 있다.
변수에는 다른 값을 얼마든지 대입할 수 있기 때문에 참조형은 가변값이라고 하는 것이다.</p>
<h2 id="📘변수-영역과-데이터-영역을-분리하는-이유">📘변수 영역과 데이터 영역을 분리하는 이유</h2>
<p>*<em>- 구조화된 메모리 관리
*</em>프로그램의 변수와 데이터를 각각의 목적에 맞게 분리하여 메모리를 효율적으로 관리할 수 있다.
*<em>- 변수와 데이터의 독립성 유지
*</em>변수와 데이터가 분리되면 변수에 대한 수정이 데이터에 영향을 미치지 않으며, 데이터의 변경이 변수에 영향을 주지 않는다.</p>
<h1 id="✨기본형-데이터와-참조형-데이터">✨기본형 데이터와 참조형 데이터</h1>
<h2 id="📘불변값">📘불변값</h2>
<ul>
<li>값이 생성된 후에 변경할 수 없는 특성</li>
<li>기본형 데이터는 모두 불변값이다.</li>
<li>새로운 값을 할당하면 이전 값은 변하지 않고 메모리에 새로운 공간에 저장된다.</li>
<li>데이터 영역에 저장된 값은 모두 불변값<pre><code class="language-js">var name = &#39;보&#39;
name = name + &#39;미&#39;</code></pre>
변수name에 문자열 &#39;보&#39; 를 할당했다가 뒤에 &#39;미&#39;를 추가하면 기존의 &#39;보&#39;가 &#39;보미&#39;로 바뀌는 것이 아니라 새로운 문자열 &#39;보미&#39;를 만들어 그 주소를 변수에 저장한다. </li>
<li><blockquote>
<p>&#39;보&#39;와 &#39;보미&#39;는 완전히 별개의 데이터! </p>
</blockquote>
</li>
<li><blockquote>
<p>주소가 다르다고 생각하자.
변수영역에 name이 생기고, 데이터 영역의 (예를 들어) 5003에 보가 저장되어있는 것이고, 나중에 보미라는 문자열이 5004에 저장되는 것. 그래서 변수영역의 값이 5003에서5004로 바뀐다는 뜻.</p>
</blockquote>
</li>
</ul>
<h2 id="📘가변값">📘가변값</h2>
<ul>
<li>값이 생성된 후에 변경할 수 있는 특성</li>
<li>참조형 데이터는 같은 메모리 주소를 공유하는 다른 변수나 객체에 영향을 줄 수 있다.<pre><code class="language-js">let person = { name: &#39;보미&#39; };
person.name = &#39;보라&#39;; // 객체 내의 속성 값 변경 가능</code></pre>
<h2 id="📘변수-복사-비교">📘변수 복사 비교</h2>
<h3 id="기본형">기본형</h3>
<pre><code class="language-js">// 기본형 데이터 (원시형)
let num1 = 10; // 숫자
let str1 = &quot;Hello&quot;; // 문자열
</code></pre>
</li>
</ul>
<p>// 변수에 복사된 값 확인
let copiedNum1 = num1;
let copiedStr1 = str1;</p>
<p>// 변수의 값 변경
num1 = 20;
str1 = &quot;World&quot;;</p>
<p>// 변수의 값과 복제된 값 출력
console.log(num1); // 20
console.log(copiedNum1); // 10
console.log(str1); // &quot;World&quot;
console.log(copiedStr1); // &quot;Hello&quot;</p>
<pre><code>### 참조형

```js
// 참조형 데이터 (객체형)
let obj1 = { name: &quot;John&quot;, age: 30 }; // 객체
let arr1 = [1, 2, 3, 4, 5]; // 배열

// 변수에 복사된 값 확인
let copiedObj1 = obj1;
let copiedArr1 = arr1;

// 변수의 값 변경
obj1.name = &quot;Jane&quot;;
arr1.push(6);

// 변수의 값과 복제된 값 출력
console.log(obj1); // { name: &quot;Jane&quot;, age: 30 }
console.log(copiedObj1); // { name: &quot;Jane&quot;, age: 30 }
console.log(arr1); // [1, 2, 3, 4, 5, 6]
console.log(copiedArr1); // [1, 2, 3, 4, 5, 6]</code></pre><h1 id="✨불변-객체">✨불변 객체</h1>
<p>불변 객체: 한 번 생성되면 그 상태를 변경할 수 없는 객체</p>
<h2 id="❓불변-객체가-왜-필요할까">❓불변 객체가 왜 필요할까</h2>
<p>값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우가 있다.</p>
<pre><code class="language-js">// 가변성으로 인한 문제점

const obj1 = { value: 10 };

const obj2 = obj1;

obj1.value = 20;

console.log(obj2.value); // 출력 결과: 20</code></pre>
<p>obj1과 obj2가 동일한 객체를 가리키고 있다.</p>
<h2 id="📘불변-객체-만드는-간단한-방법">📘불변 객체 만드는 간단한 방법</h2>
<p>해결책)</p>
<pre><code class="language-js">const obj1 = { value: 10 };

// 객체의 프로퍼티를 복사하여 새로운 객체 생성
const obj2 = { ...obj1 };

obj1.value = 20;

console.log(obj2.value); // 출력 결과: 10</code></pre>
<p>obj1을 복사하여 새로운 객체를 만든다.</p>
<h2 id="📘얕은-복사와-깊은-복사">📘얕은 복사와 깊은 복사</h2>
<h3 id="얕은-복사-shallow-copy">얕은 복사 (Shallow Copy)</h3>
<ul>
<li>객체의 최상위 레벨만을 복사하고 내부 객체는 참조로 복사하는 방식</li>
<li>바로 아래 단계의 값만 복사한다.</li>
<li>프로퍼티 값이 객체일 경우에는 해당 객체를 참조로 복사한다. (복사된 객체와 원본 객체가 같은 객체를 참조)</li>
<li>복사된 객체의 프로퍼티 값이 변경되면, 원본 객체의 프로퍼티 값도 변경될 수 있다.</li>
<li>Object.assign()이나 Spread 연산자(...)<pre><code class="language-js">// 얕은 복사 예시
</code></pre>
</li>
</ul>
<p>// 원본 객체
const originalObj = {
  name: &#39;보미&#39;,
  age: 25,
  address: {
    city: &#39;서울&#39;,
    country: &#39;대한민국&#39;
  }
};</p>
<p>// 얕은 복사
const shallowCopyObj = { ...originalObj };</p>
<p>// 원본 객체와 얕은 복사된 객체의 주소 확인
console.log(originalObj === shallowCopyObj); // false</p>
<p>// 내부 객체의 주소 확인
// 내부 객체인 address는 얕은 복사되어 참조로 복사되었기 때문에 동일한 메모리 주소를 가짐
console.log(originalObj.address === shallowCopyObj.address); // true</p>
<p>// 내부 객체 변경
originalObj.address.city = &#39;제주도&#39;;</p>
<p>// 얕은 복사된 객체의 내부 객체도 변경되었는지 확인
// originalObj의 address 객체를 변경 -&gt; shallowCopyObj.address도 같은 객체를 참조하고 있기 때문에 shallowCopyObj 객체에서 address 객체의 변화가 반영됨
console.log(shallowCopyObj.address.city); // 제주도</p>
<pre><code>
### 깊은 복사 (Deep Copy)
- 원본 객체의 모든 프로퍼티들을 재귀적으로 복사하여 완전히 새로운 객체를 생성한다.
- 새로운 객체와 원본 객체는 완전히 독립적이며, 서로 다른 메모리 공간을 참조한다.
- 복사된 객체의 프로퍼티 값이 변경되어도, 원본 객체의 프로퍼티 값은 영향을 받지 않는다.
- JSON.stringify()와 JSON.parse()
```js
// 원본 객체
const originalObj = {
  name: &#39;보미&#39;,
  age: 25,
  address: {
    city: &#39;서울&#39;,
    country: &#39;대한민국&#39;
  }
};

// 깊은 복사
const deepCopyObj = JSON.parse(JSON.stringify(originalObj));

// 원본 객체와 깊은 복사된 객체의 주소 확인
console.log(originalObj === deepCopyObj); // false

// 내부 객체의 주소 확인
console.log(originalObj.address === deepCopyObj.address); // false

// 내부 객체 변경
originalObj.address.city = &#39;제주도&#39;;

// 깊은 복사된 객체의 내부 객체가 변경되었는지 확인
console.log(deepCopyObj.address.city); // 서울
</code></pre><ul>
<li>깊은 복사를 사용하면 내부 객체의 변경이 원본 객체나 다른 복사된 객체에 영향을 주지 않는다. </li>
<li>깊은 복사는 JSON 문자열을 중간에 생성하므로 일부 데이터 타입이나 함수 등을 복사할 수 없는 한계가 있다.</li>
</ul>
<h1 id="✨undefined와-null">✨undefined와 null</h1>
<h2 id="📘undefined">📘undefined</h2>
<ul>
<li><strong>변수에 값이 할당되지 않았거나, 객체에서 존재하지 않는 프로퍼티에 접근할 때</strong> 반환된다.</li>
<li><strong>변수가 선언되었지만 초기화되지 않았거나, 함수에서 반환값이 명시되지 않은 경우</strong>에도 undefined가 반환된다.</li>
<li>자바스크립트 엔진이 자동으로 부여하는 값으로, 직접 할당하는 경우도 있지만 보통은 변수가 초기화되지 않았을 때 발생한다.</li>
</ul>
<h2 id="📘null">📘null</h2>
<ul>
<li>null은 의도적으로 &quot;값이 없음&quot;을 나타내는 데 사용된다.</li>
<li>변수에 의도적으로 값이 없음을 할당하거나, 객체의 프로퍼티를 삭제할 때 주로 사용된다.</li>
<li>null은 객체가 존재하지 않음을 나타내는 것이 아니라, 객체가 있는데 그 값이 없음을 나타낸다.</li>
</ul>
<pre><code class="language-js">// 변수 선언만 하고 초기화되지 않은 경우
let undefinedVariable;
let nullVariable = null;

// 값이 없음을 나타내는 변수에 대한 비교
console.log(undefinedVariable === undefined); // true
console.log(nullVariable === null); // true

// undefined와 null 비교
// 서로 다른 데이터 타입이므로 === 비교 연산자를 사용하여 비교할 때 false 반환
console.log(undefined === null); // false</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[오프셋 기반 페이지네이션과 커서 기반 페이지네이션의 차이]]></title>
            <link>https://velog.io/@bori_note/%EC%98%A4%ED%94%84%EC%85%8B-%EA%B8%B0%EB%B0%98-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98%EA%B3%BC-%EC%BB%A4%EC%84%9C-%EA%B8%B0%EB%B0%98-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@bori_note/%EC%98%A4%ED%94%84%EC%85%8B-%EA%B8%B0%EB%B0%98-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98%EA%B3%BC-%EC%BB%A4%EC%84%9C-%EA%B8%B0%EB%B0%98-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Sat, 13 Apr 2024 03:19:44 GMT</pubDate>
            <description><![CDATA[<p>오프셋 기반 페이지네이션과 커서 기반 페이지네이션은 데이터베이스에서 데이터를 페이지 단위로 가져오는 두 가지 일반적인 방법이다.</p>
<h1 id="✨오프셋-기반-페이지네이션">✨오프셋 기반 페이지네이션</h1>
<h2 id="📘동작-방식">📘동작 방식</h2>
<p>데이터를 가져올 때 오프셋(offset)과 리미트(limit)를 사용하여 명시된 개수의 항목을 건너뛰고 가져온다. </p>
<pre><code class="language-jsx">function App() {
  const [users, setUsers] = useState([]);
  const [page, setPage] = useState(1);
  const limit = 20; // 한 페이지에 표시할 아이템 수

  useEffect(() =&gt; {
    const fetchUsers = async () =&gt; {
      try {
        // 페이지 번호와 limit을 곱하여 offset 값을 계산합니다.
        const offset = (page - 1) * limit;
        // 데이터베이스에서 offset부터 limit까지의 아이템을 가져옵니다.
        const response = await axios.get(`/users?offset=${offset}&amp;limit=${limit}`);
        setUsers(response.data);
      } catch (error) {
        console.error(&#39;Error fetching users:&#39;, error);
      }
    };

    fetchUsers();
  }, [page]);

  return (
    &lt;div&gt;
      &lt;h1&gt;Users&lt;/h1&gt;
      &lt;ul&gt;
        {users.map(user =&gt; (
          &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;
        ))}
      &lt;/ul&gt;
      &lt;button onClick={() =&gt; setPage(page =&gt; page + 1)}&gt;Next Page&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>offset: 지금까지 받아온 데이터 개수
limit: 한 페이지에 표시할 아이템 수</p>
<p>전체 데이터가 20개이고 페이지당 표시할 항목의 수가 10개인 경우)</p>
<pre><code class="language-jsx">// 첫 번째 페이지의 데이터를 가져올 때
const offsetFirstPage = 0;
const limit = 10;
// 데이터베이스에서 0부터 limit(10)까지의 아이템을 가져온다.
// 따라서 첫 번째 페이지에는 첫 10개의 항목이 표시된다.</code></pre>
<pre><code class="language-jsx">// 두 번째 페이지의 데이터를 가져올 때
const offsetSecondPage = 10;
// 데이터베이스에서 10부터 limit(10)까지의 아이템을 가져온다.
// 따라서 두 번째 페이지에는 11번째부터 20번째까지의 항목이 표시된다.</code></pre>
<blockquote>
<p>첫번째 페이지에서는 offset이 0이므로 데이터베이스에서 0부터 limit만큼의 아이템을 가져온다.</p>
</blockquote>
<blockquote>
<p>두 번째 페이지에서는 offset이 limit의 배수가 되므로 다음 limit만큼의 아이템을 가져온다. </p>
</blockquote>
<blockquote>
<p>페이지를 변경할 때마다 이전 페이지의 아이템을 건너뛰고 새로운 페이지의 아이템을 가져오는 원리다.</p>
</blockquote>
<h2 id="📘장점">📘장점</h2>
<ul>
<li>간단하고 직관적인 구현이 가능하다.</li>
<li>데이터가 적을 때에는 효율적이다.</li>
</ul>
<h2 id="📘단점">📘단점</h2>
<ul>
<li>대량의 데이터를 처리할 때 성능이 저하될 수 있다. (각 페이지를 가져올 때마다 모든 이전 데이터를 건너뛰어야 하기 때문)</li>
<li>데이터가 변경되면 페이지의 인덱스가 변경될 수 있다.</li>
</ul>
<h1 id="✨커서-기반-페이지네이션">✨커서 기반 페이지네이션</h1>
<h2 id="📘동작-방식-1">📘동작 방식</h2>
<ul>
<li>이전 페이지에서 가져온 마지막 항목의 커서(cursor)를 사용하여 다음 페이지의 항목을 가져온다. </li>
<li>이전 페이지와 다음 페이지 사이의 관계를 유지하며 데이터를 가져온다.</li>
</ul>
<h2 id="📘장점-1">📘장점</h2>
<ul>
<li>대량의 데이터를 처리할 때 성능이 좋다.(이전 페이지의 데이터를 매번 다시 탐색하지 않기 때문)</li>
<li>데이터가 변경되어도 페이지 인덱스를 업데이트할 필요가 없다.</li>
</ul>
<h2 id="📘단점-1">📘단점</h2>
<ul>
<li>구현이 복잡하다.(이전 페이지의 커서를 유지하고 다음 페이지의 데이터를 가져오는 방식을 처리해야 함)</li>
<li>커서를 유지하는 데 필요한 추가적인 리소스가 있을 수 있다.</li>
</ul>
<p>데이터베이스의 크기와 사용 사례에 따라 오프셋 기반 페이지네이션과 커서 기반 페이지네이션 중에서 선택할 수 있다. </p>
<blockquote>
<p><strong><em>데이터베이스가 작고 변경이 자주 일어나지 않는 경우에는 오프셋 기반 페이지네이션</em></strong>을 사용할 수 있지만, 
<strong>대용량의 데이터나 변경이 빈번한 경우에는 커서 기반 페이지네이션</strong>을 사용하는 것이 좋다.</p>
</blockquote>
<pre><code class="language-jsx">function App() {
  const [users, setUsers] = useState([]);
  const [cursor, setCursor] = useState(null); // 초기 커서는 null로 설정
  const perPage = 10; // 페이지당 보여질 개수

  useEffect(() =&gt; {
    const fetchUsers = async () =&gt; {
      try {
        const response = await axios.get(`/users?cursor=${cursor}&amp;limit=${perPage}`);
        setUsers(response.data.users);
        setCursor(response.data.next_cursor); // 다음 페이지의 커서를 설정
      } catch (error) {
        console.error(&#39;Error fetching users:&#39;, error);
      }
    };

    fetchUsers();
  }, [cursor, perPage]);

  return (
    &lt;div&gt;
      &lt;h1&gt;Users&lt;/h1&gt;
      &lt;ul&gt;
        {users.map(user =&gt; (
          &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;
        ))}
      &lt;/ul&gt;
      {cursor &amp;&amp; &lt;button onClick={() =&gt; setCursor(cursor)}&gt;Next Page&lt;/button&gt;}
    &lt;/div&gt;
  );
}</code></pre>
<blockquote>
<p>초기 커서를 null로 설정하고, 페이지를 로드할 때마다 해당 페이지의 커서를 사용하여 다음 페이지의 데이터를 가져오도록 한다.
페이지당 보여질 개수를 perPage 상수로 설정하고, 커서 기반 페이지네이션을 구현할 때 각 페이지마다 이 값을 사용하여 쿼리를 수행한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 기초 세션 특강을 듣고...]]></title>
            <link>https://velog.io/@bori_note/React-%EA%B8%B0%EC%B4%88-%EC%84%B8%EC%85%98-%ED%8A%B9%EA%B0%95%EC%9D%84-%EB%93%A3%EA%B3%A0</link>
            <guid>https://velog.io/@bori_note/React-%EA%B8%B0%EC%B4%88-%EC%84%B8%EC%85%98-%ED%8A%B9%EA%B0%95%EC%9D%84-%EB%93%A3%EA%B3%A0</guid>
            <pubDate>Mon, 08 Apr 2024 11:11:24 GMT</pubDate>
            <description><![CDATA[<p>이정환 강사님 React 기초 세션 특강
주제 : React 기초
일시 : 4월 8일(월) 오후 7시 ~ 8시30분(1.5h)</p>
<blockquote>
<p>리액트는 왜 만들어지게 되었을까?
리액트의 설계 의도는 무엇일까?</p>
</blockquote>
<h1 id="web의-눈부신-발전과-리액트의-탄생">web의 눈부신 발전과 리액트의 탄생</h1>
<p>기술이 어떤 시대적인 배경에서 어떤 문제를 해결하기 위해 탄생했는지 알아야 한다.</p>
<blockquote>
<p>90_00: 읽기 전용 기반의 웹 서비스가 대부분
오늘날: 사용자와 실시간으로 상호작용하는 서비스들이 대세, read-write 기반의 웹 서비스
-&gt; <strong>현대 웹서비스 개발에서 화면 업데이트(인터렉션) 구현의 중요성이 매우 높아짐</strong></p>
</blockquote>
<p>JQuery</p>
<ul>
<li>바닐라 js 기반으로 DOM을 직접 수정하는 방식으로 동작함.</li>
<li>크로스 브라우징 이슈를 해결하고자 만들어짐.</li>
<li>쉽고 빠르게 다양한 업데이트를 구현하기에는 무리가 있었음.</li>
</ul>
<p>리액트</p>
<ul>
<li>화면 업데이트를 더 쉽게 구현할 수 있음.</li>
<li>화면 업데이트가 빠름, 최적화가 거의 자동으로 이루어짐</li>
</ul>
<h1 id="리액트3가지-핵심-특징">리액트3가지 핵심 특징</h1>
<h2 id="1-컴포넌트-기반의-ui-표현">1. 컴포넌트 기반의 UI 표현</h2>
<p>컴포넌트: 구성요소 -&gt; 화면을 구성(UI를 구성)하는 요소
UI 모듈화 가능 -&gt; 중복 코드 최소화</p>
<h2 id="2-선언형-프로그래밍을-통한-쉬운-업데이트">2. 선언형 프로그래밍을 통한 쉬운 업데이트</h2>
<p>선언형 프로그래밍: 과정은 생략하고 목적만 간결히 명시하는 방법
명령형 프로그래밍: 목적을 이루기 위한 모든 일련의 과정을 설명하는 방식</p>
<p>리액트에서는 선언형으로 업데이트를 구현할 수 있다. -&gt; 업데이트를 위한 모든 로직을 일일이 작성하지 않아도 됨.</p>
<h3 id="state-상태">State 상태</h3>
<ul>
<li>컴포넌트 내부에 선언할 수 있는 특별한 변수</li>
<li>컴포넌트가 현재 어떤 상태인지 저장하는 역할</li>
<li>상태 값에 따라 컴포넌트가 렌더링 하는 UI를 바꿀 수 있음</li>
</ul>
<p>✔️리엑트에서는 업데이트를 구현할 때 state만 바꾸면 된다.</p>
<h2 id="3-자체-렌더링-프로세스를-통한-빠른-업데이트">3. 자체 렌더링 프로세스를 통한 빠른 업데이트</h2>
<p>리액트는 화면 업데이트를 쉽게 구현할 수 있으면서 동시에 빠르게 처리한다.
❓어떻게 이럴 수 있을까?</p>
<p>브라우저는 어떻게 동작하는가?</p>
<blockquote>
<ol>
<li>HTML 파싱:
HTML 문서를 파싱하여 DOM 트리를 생성</li>
<li>CSS 파싱:
CSS 파일이나 스타일 태그의 내용을 파싱하여 스타일 규칙을 생성</li>
<li>렌더 트리 구성:
DOM 트리와 CSSOM(스타일 트리)을 결합하여 렌더 트리를 생성</li>
<li>레이아웃: (reflow)
렌더 트리를 기반으로 각 요소의 크기와 위치를 계산하여 레이아웃을 수행</li>
<li>페인팅: (repainting)
레이아웃이 완료되면 화면에 픽셀을 그리는 과정인 페인팅이 수행</li>
</ol>
<p>-&gt; 업데이트할 때마다 DOM 전체가 업데이트 된다.
-&gt; 리액트는 동시에 발생한 업데이트를 모아서 한번에 수정한다.(자동으로 해줌)</p>
</blockquote>
<p>Virtual DOM</p>
<ul>
<li>DOM을 자바스크립트 객체로 흉내낸 것으로 일종의 복제판임</li>
<li>리액트는 가상 DOM을 사용한다.</li>
<li>엘리먼트를 새로 렌더링할 때 리액트는 그 모습을 실제 DOM트리에 바로 반영하는 것이 아니라 가상 DOM에 적용한다.</li>
<li>리액트는 state 변경 전의 가상 DOM과 변경 후의 가상 DOM을 비교한다. 그래서 바뀐 부분만 찾아내고 각각에 해당하는 실제 DOM 노드를 변경한다.</li>
</ul>
<p>뷰도 배워야 하나?
배우면 좋겠지만 비추.
뷰가 할 수 있는 건 리액트도 다 할 수 있음</p>
<p>리덕스: 리덕스 툴킷 많이 씀. 경량화된 라이브러리
주스탕: 리덕스에서 더 경량화 된 라이브러리 (추천 + 리덕스도 함께 하는 것도 추천)</p>
<p>면접에서) 프로젝트에서 리덕스를 썼다고 가정했다면, 주스탕과 리덕스의 차이점에 대해 설명하면서 왜 더 좋았는지 이걸 써는지 어필..</p>
<p>토이프로젝트를 대하는 방식에 대해 생각해보자
<a href="https://velog.io/@dus532/series/%EC%86%8C%ED%94%84%ED%8A%B8%EC%BD%98-%EB%B7%B0%EC%96%B4%EC%89%BD-%EC%A0%9C%EC%9E%91%EA%B8%B0">https://velog.io/@dus532/series/%EC%86%8C%ED%94%84%ED%8A%B8%EC%BD%98-%EB%B7%B0%EC%96%B4%EC%89%BD-%EC%A0%9C%EC%9E%91%EA%B8%B0</a></p>
<p>챗지피티를 활용해서 10년된 개발자가 짠 코드 알려줘해서 -&gt; 코드 비교해보기</p>
<p>리액트 설계원리에 맞춰서 코딩해라
<a href="https://react.dev/learn/thinking-in-react">https://react.dev/learn/thinking-in-react</a></p>
<p>리액트는 Flux패턴</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[P3_S1] React 웹 개발 시작하기]]></title>
            <link>https://velog.io/@bori_note/P2S1-React-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bori_note/P2S1-React-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 08 Apr 2024 05:48:29 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-jsx">// 리액트 앱 만들기
// .은 현재 디렉토리에서 프로젝트를 만들 것이라는 뜻
npm init react-app .

// or
npm init react-app [폴더 이름]

// 프로젝트 실행(개발 모드 시행)
npm run start

//개발 모드 종료
Crtl + c</code></pre>
<h1 id="✨jsx란"><strong>✨JSX란?</strong></h1>
<ul>
<li>JSX는 자바스크립트의 확장 문법</li>
<li>리액트로 코드를 작성할 때 HTML 문법과 비슷한 이 JSX 문법을 활용하면 훨씬 더 편리하게 화면에 나타낼 코드를 작성할 수가 있다.</li>
</ul>
<pre><code class="language-jsx">const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));
root.render(&lt;h1&gt;안녕 리액트!&lt;/h1&gt;);</code></pre>
<p>render 메소드는 첫 번째 아규먼트 값을 활용해서 HTML요소를 만들고, 두 번째 아규먼트 값에 그 요소를 넣어 주는 역할을 함.</p>
<h1 id="✨jsx-문법"><strong>✨JSX 문법</strong></h1>
<p>JSX는 자바스크립트로 HTML과 같은 문법을 사용할 수 있도록 만들어주는 편리한 문법이지만, 그만큼 꼭 지켜야 할 규칙들도 있습니다.</p>
<h2 id="html과-다른-속성명"><strong>HTML과 다른 속성명</strong></h2>
<h3 id="1-속성명은-카멜-케이스로-작성하기"><strong>1. 속성명은 카멜 케이스로 작성하기</strong></h3>
<p>JSX 문법에서도 태그에 속성을 지정해 줄 수 있다. 단, 여러 단어가 조합된 몇몇 속성들을 사용할 때는 반드시 카멜 케이스(Camel Case)로 작성해야 한다.</p>
<p>단, 예외적으로 HTML에서 비표준 속성을 다룰 때 활용하는 <code>data-*</code> 속성은 카멜 케이스(Camel Case)가 아니라 기존의 HTML 문법 그대로 작성해야 한다.</p>
<pre><code class="language-jsx">  &lt;div&gt;
    상태 변경:
    &lt;button className=&quot;btn&quot; data-status=&quot;대기중&quot;&gt;대기중&lt;/button&gt;
    &lt;button className=&quot;btn&quot; data-status=&quot;진행중&quot;&gt;진행중&lt;/button&gt;
    &lt;button className=&quot;btn&quot; data-status=&quot;완료&quot;&gt;완료&lt;/button&gt;
  &lt;/div&gt;</code></pre>
<h3 id="2-자바스크립트-예약어와-같은-속성명은-사용할-수-없다"><strong>2. 자바스크립트 예약어와 같은 속성명은 사용할 수 없다</strong></h3>
<p>JSX 문법도 결국은 자바스크립트 문법이기 때문에, <code>for</code>나 <code>class</code>처럼 자바스크립트의 문법에 해당하는 예약어와 똑같은 이름의 속성명은 사용할 수 없다.
그래서 HTML의 <code>for</code>의 경우에는 자바스크립트의 반복문 키워드 <code>for</code>와 겹치기 때문에 <code>htmlFor</code>로, HTML의 <code>class</code> 속성도 자바스크립트의 클래스 키워드 <code>class</code>와 겹치기 때문에 <code>className</code>으로 작성해야 한다.</p>
<pre><code class="language-jsx">  &lt;form&gt;
    &lt;label htmlFor=&quot;name&quot;&gt;이름&lt;/label&gt;
    &lt;input id=&quot;name&quot; className=&quot;name-input&quot; type=&quot;text&quot; /&gt;
  &lt;/form&gt;</code></pre>
<h2 id="반드시-하나의-요소로-감싸기---fragment"><strong>반드시 하나의 요소로 감싸기 - Fragment</strong></h2>
<p>JSX 문법을 활용할 때는 반드시 하나의 요소로 감싸야 한다. </p>
<p><code>Fragment</code>로 감싸주면 의미 없는 부모 태그를 만들지 않아도 여러 요소를 작성할 수 있다.</p>
<pre><code class="language-jsx">
ReactDOM.render(
  &lt;Fragment&gt;
    &lt;p&gt;안녕&lt;/p&gt;
    &lt;p&gt;리액트!&lt;/p&gt;
  &lt;/Fragment&gt;</code></pre>
<p>참고로 <code>Fragment</code>는 아래 코드처럼 빈 태그로 감싸는 단축 문법으로 활용할 수도 있다.</p>
<pre><code class="language-jsx">
  &lt;&gt;
    &lt;p&gt;안녕&lt;/p&gt;
    &lt;p&gt;리액트!&lt;/p&gt;
  &lt;/&gt;</code></pre>
<h2 id="자바스크립트-표현식-넣기"><strong>자바스크립트 표현식 넣기</strong></h2>
<p>JSX 문법에서 <strong>중괄호({})</strong>를 활용하면 자바스크립트 표현식을 넣을 수 있다.</p>
<pre><code class="language-jsx">import ReactDOM from &#39;react-dom&#39;;

const product = &#39;맥북&#39;;

ReactDOM.render(
  &lt;h1&gt;나만의 {product} 주문하기&lt;/h1&gt;,
  document.getElementById(&#39;root&#39;)
);</code></pre>
<p>단, JSX 문법에서 중괄호는 자바스크립트 <strong>표현식</strong>을 다룰 때 활용하기 때문에, 중괄호 안에서 for, if문 등의 문장은 다룰 수 없다는 점!
그런데도 만약 JSX 문법을 활용할 때 조건문이 꼭 필요하다면 조건 연산자를, 반복문이 꼭 필요하다면 배열의 반복 메소드를 활용할 수는 있다…</p>
<h1 id="✨리액트-엘리먼트">✨<strong>리액트 엘리먼트</strong></h1>
<p>JSX 문법으로 작성한 요소는 결과적으로 자바스크립트 객체가 된다.</p>
<pre><code class="language-jsx">import ReactDOM from &#39;react-dom&#39;;

const element = &lt;h1&gt;안녕 리액트!&lt;/h1&gt;;
console.log(element);
ReactDOM.render(element, document.getElementById(&#39;root&#39;));</code></pre>
<pre><code>{$$typeof: Symbol(react.element), type: &quot;h1&quot;, key: null, ref: null, props: {…}, …}</code></pre><ul>
<li>이런 객체를 리액트 엘리먼트라고 부름.</li>
<li>이 리액트 엘리먼트를 <code>ReactDOM.render</code> 함수의 아규먼트로 전달하게 되면, 리액트가 객체 형태의 값을 해석해서 HTML 형태로 브라우저에 띄워주는 것.</li>
<li>리액트 엘리먼트는 리액트로 화면을 그려내는데 가장 기본적인 요소다.</li>
</ul>
<h1 id="✨리액트-컴포넌트">✨<strong>리액트 컴포넌트</strong></h1>
<ul>
<li>리액트 컴포넌트는 리액트 엘리먼트를 조금 더 자유롭게 다루기 위한 하나의 문법이다.</li>
<li>컴포넌트를 만드는 가장 간단한 방법은 자바스크립트의 함수를 활용하는 것임.</li>
<li>아래 코드에서 JSX 문법으로 작성된 하나의 요소를 리턴하는 <code>Hello</code> 함수가 바로 하나의 컴포넌트다.</li>
</ul>
<pre><code class="language-jsx">import ReactDOM from &#39;react-dom&#39;;

function Hello() {
  return &lt;h1&gt;안녕 리액트&lt;/h1&gt;;
}

const element = (
  &lt;&gt;
    &lt;Hello /&gt;
    &lt;Hello /&gt;
    &lt;Hello /&gt;
  &lt;/&gt;
);

ReactDOM.render(element, document.getElementById(&#39;root&#39;));</code></pre>
<ul>
<li>컴포넌트를 작성하면, 위 코드에서 <code>element</code> 변수 안의 JSX 코드에서 볼 수 있듯 컴포넌트 함수 이름을 통해 하나의 태그처럼 활용할 수 있다.</li>
<li>이런 특성을 모듈 문법으로 활용하면 훨씬 더 독립적으로 컴포넌트 특성에 집중해서 코드를 작성할 수 있다.</li>
<li>리액트 컴포넌트의 이름은 반드시 <strong>첫 글자를 대문자</strong>로 작성해야 한다는 것.</li>
</ul>
<h1 id="✨props"><strong>✨Props</strong></h1>
<p>JSX 문법에서 컴포넌트를 작성할 때 컴포넌트에도 속성을 지정할 수 있다. 리액트에서 이렇게 컴포넌트에 지정한 속성들을 <strong>Props</strong>라고 부른다.</p>
<ul>
<li><strong>Props는 Properties의 약자</strong></li>
<li>컴포넌트에 속성을 지정해주면 <strong>각 속성이 하나의 객체로 모여서 컴포넌트를 정의한 함수의 첫 번째 파라미터로 전달된다</strong>.</li>
</ul>
<h1 id="✨children"><strong>✨Children</strong></h1>
<p><code>props</code>에는 <code>children</code>이라는 프로퍼티(prop, 프롭)가 있다.</p>
<p>JSX 문법으로 컴포넌트를 작성할 때 컴포넌트를 단일 태그가 아니라 여는 태그와 닫는 태그의 형태로 작성하면, 그 안에 작성된 코드가 바로 이 <code>children</code> 값에 담기게 된다.</p>
<p>Button.js</p>
<pre><code class="language-jsx">
function Button({ children }) {
  return &lt;button&gt;{children}&lt;/button&gt;;
}

export default Button;
</code></pre>
<p>App.js</p>
<pre><code class="language-jsx">
import Button from &#39;./Button&#39;;
import Dice from &#39;./Dice&#39;;

function App() {
  return (
    &lt;div&gt;
      &lt;div&gt;
        &lt;Button&gt;던지기&lt;/Button&gt;
        &lt;Button&gt;처음부터&lt;/Button&gt;
      &lt;/div&gt;
      &lt;Dice color=&quot;red&quot; num={2} /&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<ul>
<li>JSX 문법으로 컴포넌트를 작성할 때 어떤 정보를 전달할 때는 일반적인 <code>props</code>의 속성값을 주로 활용하고,</li>
<li>화면에 보여질 모습을 조금 더 직관적인 코드로 작성하고자 할 때 <code>children</code> 값을 활용한다.</li>
<li><strong><code>props</code></strong>는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하는 데 사용되는 객체이며, <strong><code>children</code></strong>은 React 컴포넌트의 태그 내에 포함된 내용을 나타냄</li>
</ul>
<h1 id="✨state"><strong>✨State</strong></h1>
<ul>
<li>state는 상태가 바뀔 때마다 화면을 새롭게 그려내는 방식으로 동작을 한다.</li>
<li>리액트에서 state를 만들고, state를 바꾸기 위해 <code>useState</code>라는 함수를 쓴다.</li>
</ul>
<pre><code class="language-jsx">
import { useState } from &#39;react&#39;;

// ...

  const [num, setNum] = useState(1);

// ...
</code></pre>
<ul>
<li>보통 Destructuring 문법으로 작성한다.</li>
<li><code>useState</code> 함수가 초깃값을 아규먼트로 받고 그에 따른 실행 결과로 요소 2개를 가진 배열의 형태로 리턴을 하기 때문.</li>
<li>첫 번째 요소가 바로 state이고, 두 번째 요소가 이 state를 바꾸는 setter 함수</li>
<li>첫 번째 변수는 원하는 state의 이름(<code>num</code>)을 지어주고, 두 번째 변수에는 state 이름 앞에 set을 붙인 다음 카멜 케이스로 이름을 지어주는 것(<code>setNum</code>)이 일반적이다.</li>
<li>state는 변수에 새로운 값을 할당하는 방식으로 변경하는 것이 아니라 이 setter 함수를 활용해야 한다. setter 함수는 호출할 때 전달하는 아규먼트 값으로 state 값을 변경해 준다.</li>
</ul>
<p>setter 함수를 활용해서 이벤트 핸들러를 등록해두면, 이벤트가 발생할 때마다 상태가 변하면서 화면이 새로 그려지는 것이다.</p>
<pre><code class="language-jsx">
import { useState } from &#39;react&#39;;
import Button from &#39;./Button&#39;;
import Dice from &#39;./Dice&#39;;

function App() {
  const [num, setNum] = useState(1);

  const handleRollClick = () =&gt; {
    setNum(3); // num state를 3으로 변경!
  };

  const handleClearClick = () =&gt; {
    setNum(1); // num state를 1로 변경!
  };

  return (
    &lt;div&gt;
      &lt;Button onClick={handleRollClick}&gt;던지기&lt;/Button&gt;
      &lt;Button onClick={handleClearClick}&gt;처음부터&lt;/Button&gt;
      &lt;Dice color=&quot;red&quot; num={num} /&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<h1 id="✨참조형-state"><strong>✨참조형 State</strong></h1>
<p>자바스크립트의 자료형은 크게 기본형(Primitive type)과 참조형(Reference type)로 나뉜다.</p>
<pre><code class="language-jsx">// ...

  const [gameHistory, setGameHistory] = useState([]);

  const handleRollClick = () =&gt; {
    const nextNum = random(6);
    gameHistory.push(nextNum);
    setGameHistory(gameHistory); // state가 제대로 변경되지 않는다!
  };

// ...</code></pre>
<ul>
<li>배열 값을 가진 <code>gameHistory</code>에 <code>push</code> 메소드를 이용해서 배열의 값을 변경한 다음, 변경된 배열을 setter 함수로 state를 변경하려고 하면 코드가 제대로 동작하지 않는다.</li>
<li><code>gameHistory</code> state는 배열 값 자체를 가지고 있는 게 아니라 그 배열의 주솟값을 참조하고 있는 것이다. 때문에 <code>push</code> 메소드로 배열 안에 요소를 변경했다고 하더라도 결과적으로 참조하는 배열의 주솟값은 변경된 것이 아니다.</li>
<li>결과적으로 리액트 입장에서는 <code>gameHistory</code> state가 참조하는 주솟값은 여전히 똑같기 때문에 상태(state)가 바뀌었다고 판단하지 않는 것이다.</li>
<li>참조형 state를 활용할 때는 반드시 새로운 참조형 값을 만들어 state를 변경해야 한다.</li>
</ul>
<p>가장 간단한 방법은 Spread 문법(<code>...</code>) 을 활용하는 것!!</p>
<pre><code class="language-jsx">
// ...

  const [gameHistory, setGameHistory] = useState([]);

  const handleRollClick = () =&gt; {
    const nextNum = random(6);
    setGameHistory([...gameHistory, nextNum]); // state가 제대로 변경된다!
  };

// ...
</code></pre>
<p><strong>참조형 state를 활용할 땐 반드시 새로운 참조형 값을 만들어서 state를 변경해야 한다는 점!!</strong></p>
<h1 id="✨컴포넌트">✨컴포넌트</h1>
<h2 id="📘컴포넌트-장점">📘컴포넌트 장점</h2>
<ul>
<li>반복적인 개발 줄어듦.</li>
<li>오류를 고치기 쉽다.</li>
<li>일을 쉽게 나눌 수 있다.</li>
</ul>
<h1 id="✨리액트가-렌더링하는-방식">✨리액트가 렌더링하는 방식</h1>
<p>기본적으로 HTML 요소들은 DOM 트리라고 하는 자료구조로 저장되어 있다.</p>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/6bf245e8-dd07-48b2-b775-05b6b4aa4af9/4780e476-0894-42d6-8b37-5ef92d59bbfc/Untitled.png" alt="Untitled"></p>
<p>리액트는 가상 DOM이라는 자료구조를 사용한다.</p>
<p>그래서 엘리먼트를 새로 렌더링할 때 리액트는 그 모습을 실제 DOM트리에 바로 반영하는 것이 아니라 가상 DOM에 적용한다.  </p>
<p>리액트는 state 변경 전의 가상 DOM과 변경 후의 가상 DOM을 비교한다. 그래서 바뀐 부분만 찾아내고 각각에 해당하는 실제 DOM 노드를 변경한다. </p>
<ul>
<li>개발자가 직접 DOM 노드를 신경쓸 필요가 없어 단순하고 깔끔한 코드를 작성할 수 있다.</li>
<li>변경 사항들을 리액트가 적당히 모아 적당히 나눠서 브라우저에 전달한다. → 변경사항들을 효율적으로 관리할 수 있다.</li>
</ul>
<blockquote>
<p>가상 DOM을 활용해 효율적인 화면을 처리할 수 있다.</p>
</blockquote>
<h2 id="⚠️dom-vs-virtual-dom">⚠️DOM vs Virtual DOM</h2>
<table>
<thead>
<tr>
<th></th>
<th>DOM</th>
<th>Virtual DOM</th>
</tr>
</thead>
<tbody><tr>
<td>업데이트</td>
<td>느림</td>
<td>빠름</td>
</tr>
<tr>
<td>메모리</td>
<td>낭비 심함</td>
<td>낭비 덜함</td>
</tr>
<tr>
<td>새로운 element 업데이트 방식</td>
<td>새로운 DOM 생성</td>
<td>가상 DOM 생성 후 이전 가상 DOM과 비교 후 바뀐 부분만 DOM에 업데이트</td>
</tr>
</tbody></table>
<h1 id="✨디자인을-적용하는-방법"><strong>✨디자인을 적용하는 방법</strong></h1>
<h2 id="📘이미지-불러오기"><strong>📘이미지 불러오기</strong></h2>
<p>이미지 파일은 <code>import</code> 구문을 통해 불러오고, 불러온 이미지 주소를 <code>src</code> 속성으로 사용.</p>
<pre><code class="language-jsx">
import diceImg from &#39;./assets/dice.png&#39;;

function Dice() {
  return &lt;img src={diceImg} alt=&quot;주사위 이미지&quot; /&gt;;
}

export default App;
</code></pre>
<h2 id="📘인라인-스타일"><strong>📘인라인 스타일</strong></h2>
<p>리액트에서 인라인 스타일은 문자열이 아닌 <strong>객체형</strong>으로 사용한다. 프로퍼티 이름은 CSS 속성 이름으로, 프로퍼티 값은 CSS 속성 값으로 쓰는데, 이때 프로퍼티 이름은 아래의 <code>boarderRadius</code> 처럼 <strong>대시 기호 없이 카멜 케이스로</strong> 써야 한다.</p>
<pre><code class="language-jsx">
import diceImg from &#39;./assets/dice.png&#39;;

const style = {
  borderRadius: &#39;50%&#39;,
  width: &#39;120px&#39;,
  height: &#39;120px&#39;,
};

function Dice() {
  return &lt;img style={style} src={diceImg} alt=&quot;주사위 이미지&quot; /&gt;;
}

export default App;
</code></pre>
<h2 id="📘css-파일-불러오기"><strong>📘CSS 파일 불러오기</strong></h2>
<p><code>import</code> 구문으로 파일을 불러오고 <code>from</code> 키워드 없이 쓰기</p>
<pre><code class="language-jsx">
import diceImg from &#39;./assets/dice.png&#39;;
import &#39;./Dice.css&#39;;

function Dice() {
  return &lt;img src={diceImg} alt=&quot;주사위 이미지&quot; /&gt;;
}

export default App;
</code></pre>
<h2 id="📘클래스네임-사용하기"><strong>📘클래스네임 사용하기</strong></h2>
<p>CSS 파일에 정의된 클래스명을 <code>className</code> prop에 문자열로 넣어주면 됩니다. 이때 재사용성을 위해 <code>className</code> prop을 부모 컴포넌트에서 받으면 더 좋다.</p>
<pre><code class="language-jsx">
import diceImg from &#39;./assets/dice.png&#39;;
import &#39;./Dice.css&#39;;

function Dice({ className = &#39;&#39; }) {
  const classNames = `Dice ${className}`;
  return &lt;img className={classNames} src={diceImg} alt=&quot;주사위 이미지&quot; /&gt;;
}

export default App;
</code></pre>
<h1 id="✨편리하게-클래스네임을-쓰는-방법"><strong>✨편리하게 클래스네임을 쓰는 방법</strong></h1>
<p>개수가 늘어날수록 아래처럼 알아보기 힘들어진다는 문제점이 있다.</p>
<p><strong>템플릿 문자열을 사용한 예</strong></p>
<pre><code class="language-jsx">
function Button({ isPending, color, size, invert, children }) {
  const classNames = `Button ${isPending ? &#39;pending&#39; : &#39;&#39;} ${color} ${size} ${invert ? &#39;invert&#39; : &#39;&#39;}`;
  return &lt;button className={classNames}&gt;{children}&lt;/button&gt;;
}

export default Button;
</code></pre>
<p><strong>배열을 사용한 예</strong></p>
<pre><code class="language-jsx">
function Button({ isPending, color, size, invert, children }) {
  const classNames = [
    &#39;Button&#39;,
    isPending ? &#39;pending&#39; : &#39;&#39;,
    color,
    size,
    invert ? &#39;invert&#39; : &#39;&#39;,
  ].join(&#39; &#39;);
  return &lt;button className={classNames}&gt;{children}&lt;/button&gt;;
}

export default Button;
</code></pre>
<p>지저분하게 느껴지고, 매번 반복되는 코드를 작성한다는 번거로움이 있다. 개발자들은 이럴 때 라이브러리라는 걸 쓰는데, 다른 개발자가 미리 만들어 놓은 코드를 이용해서 편하게 개발하는 것이다.</p>
<p>클래스네임의 경우에도 편리하게 사용할 수 있는 라이브러리가 많이 있다. → <code>classnames</code>라는 라이브러리 </p>
<p>클래스네임에만 집중할 수 있어 훨씬 읽기 편해진다. </p>
<h2 id="classnames-라이브러리를-사용한-예"><strong>classnames 라이브러리를 사용한 예</strong></h2>
<pre><code class="language-jsx">
import classNames from &#39;classnames&#39;;

function Button({ isPending, color, size, invert, children }) {
  return (
    &lt;button
      className={classNames(
        &#39;Button&#39;,
        isPending &amp;&amp; &#39;pending&#39;,
        color,
        size,
        invert &amp;&amp; &#39;invert&#39;,
      )}&gt;
     { children }
   &lt;/button &gt;
  );
}

export default Button;
</code></pre>
<p><code>classnames</code> 은 NPM이라는 프로그램을 통해 설치할 수 있다. 터미널에서 <code>npm install classnames</code> 을 입력하고 설치한 다음에, 위 예시처럼 <code>import</code> 로 불러와서 사용하면 된다. </p>
<hr>
<p>리액트 빌드하기</p>
<pre><code class="language-jsx">npm run build

npx serve build</code></pre>
<h1 id="✨명령어-정리">✨명령어 정리</h1>
<h2 id="프로젝트-생성하기"><strong>프로젝트 생성하기</strong></h2>
<pre><code>npm init react-app .</code></pre><p>터미널에서 원하는 디렉토리에 들어가서 <code>npm init react-app .</code>를 입력하면 현재 디렉토리에 리액트 프로젝트를 생성</p>
<h2 id="개발-모드-실행하기"><strong>개발 모드 실행하기</strong></h2>
<pre><code>npm start (npm run start)</code></pre><p>터미널에서 <code>npm run start</code>를 입력하면 개발 모드 서버가 실행</p>
<h2 id="실행-중인-서버-종료하기"><strong>실행 중인 서버 종료하기</strong></h2>
<pre><code>ctrl + c</code></pre><p>서버가 실행 중인 터미널에서 <code>ctrl + c</code>를 입력하면 서버가 종료</p>
<h2 id="개발된-프로젝트-빌드하기"><strong>개발된 프로젝트 빌드하기</strong></h2>
<pre><code>npm run build</code></pre><p>터미널에서 <code>npm run build</code>를 입력하면 빌드를 시작</p>
<h2 id="빌드한-것-로컬에서-실행하기"><strong>빌드한 것 로컬에서 실행하기</strong></h2>
<pre><code>npx serve build</code></pre><p>터미널에서 <code>npx serve build</code>를 입력하면 serve 프로그램을 다운 받고 build 폴더에서 서버가 실행</p>
<ul>
<li>브라우저는 jsx를 바로 이해하지 못한다</li>
<li>그래서 Babel 같은 트랜스파일러가 우리의 jsx 코드를 트랜스파일해 줘야 한다</li>
<li>리액트 프로젝트 배포 전 &#39;빌드&#39;라는 과정을 거쳐야 하는데, 빌드 과정 중에 트랜스파일링도 이루어진다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[6주차 위클리 페이퍼]]></title>
            <link>https://velog.io/@bori_note/6%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</link>
            <guid>https://velog.io/@bori_note/6%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</guid>
            <pubDate>Mon, 08 Apr 2024 03:46:29 GMT</pubDate>
            <description><![CDATA[<h1 id="❓예시의-코드를-실행할-때-콘솔에-출력될-값과-그-이유를-설명해-주세요">❓예시의 코드를 실행할 때, 콘솔에 출력될 값과 그 이유를 설명해 주세요.</h1>
<pre><code class="language-js">// 1번
let num = 1;

// 2번
setTimeout(() =&gt; {
  num = 2;
}, 0);

// 3번
num = 3;

// 4번
console.log(num);</code></pre>
<p>답: 3
먼저 num에 1이 저장된다.
setTimeout함수는 비동기 함수라 이벤트 루프에 의해 태스크 큐에 대기한다.
다음 num에 3이 저장되고, 콘솔에 3이 출력된다. </p>
<hr>
## 이벤트 루프
자바스크립트는 싱글 스레드 언어지만 비동기 작업을 통해 멀티 태스크를 수행할 수 있다.

<p>비동기 로직은 이벤트 루프를 통해 처리된다.</p>
<p><img src="https://velog.velcdn.com/images/bori_note/post/ed138135-045c-4d84-816f-6f143706fc27/image.png" alt=""></p>
<p><strong>힙</strong></p>
<ul>
<li>객체가 저장되는 메모리 공간</li>
</ul>
<p><strong>콜 스택</strong></p>
<ul>
<li>함수 호출 시 실행 컨텍스트가 생성된다.</li>
<li>함수를 호출하면 실행 컨텍스트가 순차적으로 콜 스택에 푸시되어 순차적으로 실행된다. 단 하나의 콜 스택을 사용하기 때문에 최상위 실행 컨텍스트가 종료되어 콜 스택에서 제거되기 전까지는 다른 어떤 태스크도 실행하지 않음.</li>
</ul>
<p><strong>이벤트 루프</strong></p>
<ul>
<li>콜 스택이 비어있고 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기 중인 함수를 콜 스택으로 이동시킨다.(FIFO)</li>
</ul>
<p><strong>태스트 큐</strong></p>
<ul>
<li>비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역</li>
</ul>
<p>예시)</p>
<pre><code class="language-js">console.log(1+1)
setTimeout(function() {console.log(2+2)}, 1000)
console.log(3+3)</code></pre>
<ul>
<li><p>실행결과는?</p>
<pre><code class="language-js">  2
  6
  4</code></pre>
</li>
<li><p>동작순서</p>
<ol>
<li>console.log(1+1) 이 콜 스택에 올라감 → 2 출력</li>
<li>setTimeout 함수가 실행되어 타이머가 설정되고 비동기 작업 시작. → setTimeout함수의 콜백함수는 태스크 큐에서 대기.</li>
<li>console.log(3+3) 이 콜 스택에 올라감 → 6 출력</li>
<li>setTimeout안의 console.log(2+2)가 태스크 큐로 이동</li>
<li>콜 스택이 비어있나 확인 후, console.log(2+2) 콜 스택에 올라감 → 4 출력</li>
</ol>
</li>
</ul>
<h3 id="마이크로-태스트--매크로-태스크">마이크로 태스트 &amp; 매크로 태스크</h3>
<p>태스크 큐는 마이크로 태스크큐와 매크로 태스크큐로 나뉜다.</p>
<ul>
<li>마이크로태스트: Promise의 then, catch, finally와 같은 마이크로 태스크</li>
<li>매크로 태스크: setTimeout 및 비동기 작업의 콜백 함수</li>
</ul>
<p>마이크로태스크 큐는 태스크 큐보다 우선순위가 높다. → 이벤트 루프는 콜 스택이 비면 먼저 마이크로태스크 큐에서 대기하고 있는 함수를 가져와 실행한다. → 마이크로태스크 큐가 비면 매크로태스크 큐에서 대기하고 있는 함수를 가져와 실행한다.</p>
<h1 id="❓리액트에서-virtual-dom이-무엇인지-이를-사용하는-이유는-무엇인지-설명해-주세요">❓리액트에서 Virtual DOM이 무엇인지, 이를 사용하는 이유는 무엇인지 설명해 주세요.</h1>
<h2 id="📘dom">📘DOM</h2>
<p>Document Object Model의 약자. -&gt; 문서 객체 모델
DOM은 HTML 문서를 파싱해서 문서의 객체 모델을 생성한다.</p>
<blockquote>
<p>[브라우저 렌더링 과정]</p>
</blockquote>
<ol>
<li>HTML 파싱:
HTML 문서를 파싱하여 DOM 트리를 생성</li>
<li>CSS 파싱:
CSS 파일이나 스타일 태그의 내용을 파싱하여 스타일 규칙을 생성</li>
<li>렌더 트리 구성:
DOM 트리와 CSSOM(스타일 트리)을 결합하여 렌더 트리를 생성</li>
<li>레이아웃:
렌더 트리를 기반으로 각 요소의 크기와 위치를 계산하여 레이아웃을 수행</li>
<li>페인팅:
레이아웃이 완료되면 화면에 픽셀을 그리는 과정인 페인팅이 수행</li>
</ol>
<p>⚠️DOM은 새로운 요청이나 변경사항이 있을 때마다 매번 렌더링을 함.</p>
<h2 id="📘virtual-dom">📘Virtual DOM</h2>
<p>리액트는 가상 DOM을 사용한다.
엘리먼트를 새로 렌더링할 때 리액트는 그 모습을 실제 DOM트리에 바로 반영하는 것이 아니라 가상 DOM에 적용한다.  </p>
<p>리액트는 state 변경 전의 가상 DOM과 변경 후의 가상 DOM을 비교한다. 그래서 바뀐 부분만 찾아내고 각각에 해당하는 실제 DOM 노드를 변경한다. </p>
<blockquote>
<ul>
<li>개발자가 직접 DOM 노드를 신경쓸 필요가 없어 단순하고 깔끔한 코드를 작성할 수 있다.</li>
</ul>
</blockquote>
<ul>
<li>변경 사항들을 리액트가 적당히 모아 적당히 나눠서 브라우저에 전달한다. →** 변경사항들을 효율적으로 관리할 수 있다.**</li>
</ul>
<h2 id="⚠️dom-vs-virtual-dom">⚠️DOM vs Virtual DOM</h2>
<table>
<thead>
<tr>
<th></th>
<th>DOM</th>
<th>Virtual DOM</th>
</tr>
</thead>
<tbody><tr>
<td>업데이트</td>
<td>느림</td>
<td>빠름</td>
</tr>
<tr>
<td>메모리</td>
<td>낭비 심함</td>
<td>낭비 덜함</td>
</tr>
<tr>
<td>새로운 element 업데이트 방식</td>
<td>새로운 DOM 생성</td>
<td>가상 DOM 생성 후 이전 가상 DOM과 비교 후 바뀐 부분만 DOM에 업데이트</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[5주차 위클리 페이퍼]]></title>
            <link>https://velog.io/@bori_note/5%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</link>
            <guid>https://velog.io/@bori_note/5%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</guid>
            <pubDate>Thu, 04 Apr 2024 04:09:38 GMT</pubDate>
            <description><![CDATA[<h1 id="❓이벤트-버블링-캡쳐링-위임에-대해-설명해-주세요">❓이벤트 버블링, 캡쳐링, 위임에 대해 설명해 주세요.</h1>
<h2 id="이벤트-버블링">이벤트 버블링</h2>
<p>이벤트가 시작된 시점에서 위로 전파가 되는 현상</p>
<pre><code class="language-html">&lt;div id=&quot;parent&quot;&gt;
  &lt;button id=&quot;child&quot;&gt;Click me&lt;/button&gt;
&lt;/div&gt;

&lt;script&gt;
document.getElementById(&#39;parent&#39;).addEventListener(&#39;click&#39;, function() {
  console.log(&#39;Parent clicked&#39;);
});

document.getElementById(&#39;child&#39;).addEventListener(&#39;click&#39;, function(event) {
  console.log(&#39;Child clicked&#39;);
  event.stopPropagation(); // 이벤트 버블링 중지
});
&lt;/script&gt;</code></pre>
<h2 id="이벤트-캡쳐링">이벤트 캡쳐링</h2>
<p>이벤트가 시작된 시점에서 아래로 전파가 되는 현상</p>
<pre><code class="language-html">&lt;div id=&quot;parent&quot;&gt;
  &lt;button id=&quot;child&quot;&gt;Click me&lt;/button&gt;
&lt;/div&gt;

&lt;script&gt;
document.getElementById(&#39;parent&#39;).addEventListener(&#39;click&#39;, function() {
  console.log(&#39;Parent clicked during capturing&#39;);
}, true); // capturing 설정

document.getElementById(&#39;child&#39;).addEventListener(&#39;click&#39;, function() {
  console.log(&#39;Child clicked&#39;);
});
&lt;/script&gt;</code></pre>
<h2 id="이벤트-위임">이벤트 위임</h2>
<p>버블링 개념을 활용하면 훨씬 효과적인 이벤트 관리를 할 수 있다. 예를 들어 자식 요소 각각에 이벤트 핸들러를 하나씩 등록할 필요 없이 부모 요소에서 한 번에 자식 요소들에 발생한 이벤트를 관리할 수도 있다.</p>
<p>-&gt; 이벤트를 다루는 방식을 자식 요소의 이벤트를 부모 요소에 위임한다고 해서 이벤트 위임(Event Delegation)이라고 부른다.</p>
<pre><code class="language-html">&lt;ul id=&quot;parent-list&quot;&gt;
  &lt;li&gt;Item 1&lt;/li&gt;
  &lt;li&gt;Item 2&lt;/li&gt;
  &lt;li&gt;Item 3&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;
document.getElementById(&#39;parent-list&#39;).addEventListener(&#39;click&#39;, function(event) {
  if (event.target.tagName === &#39;LI&#39;) {
    console.log(&#39;Clicked on&#39;, event.target.textContent);
  }
});
&lt;/script&gt;
</code></pre>
<h1 id="❓http-메소드에-대해-설명해-주세요">❓HTTP 메소드에 대해 설명해 주세요.</h1>
<p>GET: 기존 데이터를 조회하는 리퀘스트
POST: 새 데이터를 추가하는 리퀘스트
PUT: 기존 데이터를 수정하는 리퀘스트
DELETE: 기존 데이터를 삭제하는 리퀘스트</p>
<h2 id="get-post-put-delete-이외의-메소드들">GET, POST, PUT, DELETE 이외의 메소드들</h2>
<h3 id="1-patch">(1) PATCH</h3>
<p>PATCH 메소드는 기존의 데이터를 수정할 때 사용하는 메소드다.</p>
<p>PUT은 기존 데이터를 아예 새로운 데이터로 덮어씀으로써 수정하려고 할 때 쓰는 메소드이고, PATCH는 새 데이터로 기존 데이터의 일부를 수정하려고 할 때 쓰는 메소드다.</p>
<p>그러니까 예를 들어 서버에</p>
<pre><code>
{
  &quot;id&quot;: 3,
  &quot;name&quot;: &quot;Michael&quot;,
  &quot;age&quot;: 25
}</code></pre><p>이런 식으로 표현되는 데이터가 있을 때, 리퀘스트에 PATCH 메소드를 설정하고</p>
<pre><code>{
  &quot;age&quot;: 30
}</code></pre><p>이라는 데이터를 바디에 담아서 보내면,</p>
<pre><code>{
  &quot;id&quot;: 3,
  &quot;name&quot;: &quot;Michael&quot;,
  &quot;age&quot;: 30
}</code></pre><p>서버의 기존 데이터는 이렇게 age 속성만 갱신되지만, 같은 리퀘스트더라도 PUT 메소드를 설정해서 보내면</p>
<pre><code>{
  &quot;age&quot;: 30
}</code></pre><p>이렇게 서버의 데이터에는 age 속성만 남게 된다.</p>
<p>⇒ 데이터를 수정하는 메소드 중에서 PUT은 덮어쓰기, PATCH는 일부 수정를 의미한다.</p>
<h3 id="2-head">(2) HEAD</h3>
<p>메소드에는 HEAD 메소드라는 것도 있다. HEAD 메소드는 GET 메소드와 동일하다. 대신 GET 리퀘스트를 보냈을 때 받았을 리스폰스에서 바디 부분은 제외하고, 딱 헤드 부분만 받는다는 점이 다르다.</p>
<p>❓왜 이런 메소드가 필요할까?
예를 들어, 웹 브라우저가 서버로부터 용량이 엄청나게 큰 영상 파일을 받고자 하는 상황. 만약 파일의 용량이 너무 큰 경우에는 파일을 받지 않으려고 한다. 이때 파일의 용량만 조사하기 위해서 HEAD 메소드가 담긴 리퀘스트를 보내볼 수 있다.</p>
<p>이때 Content-length와 같이 컨텐츠 용량을 나타내는 헤더가 있어서 파일의 용량 정보는 알게 될 수도 있다. 그럼 이 용량을 사용자에게 보여주고 그래도 영화 파일을 시청할 건지 물어보는 코드를 작성할 수 있다. 바로 이렇게 실제 데이터가 아니라 데이터에 관한 정보만 얻으려고 하는 상황 등에 HEAD 메소드가 활용된다.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods">https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[비동기 처리 방법 3가지]]></title>
            <link>https://velog.io/@bori_note/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95-3%EA%B0%80%EC%A7%80</link>
            <guid>https://velog.io/@bori_note/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95-3%EA%B0%80%EC%A7%80</guid>
            <pubDate>Tue, 02 Apr 2024 08:20:09 GMT</pubDate>
            <description><![CDATA[<h1 id="비동기-처리-방법-3가지">비동기 처리 방법 3가지</h1>
<h2 id="1-콜백">1. 콜백</h2>
<ul>
<li>가장 기본적인 비동기 처리방식</li>
<li>콜백 함수를 사용해 비동기 작업의 결과를 처리함</li>
<li>여러 개의 중첩된 콜백을 사용하면 가독성이 떨어짐</li>
<li>setTimeout(), 이벤트 처리 함수 등…</li>
</ul>
<pre><code class="language-jsx">function fetchData(callback) {
  setTimeout(function() {
    callback(&#39;Data received&#39;);
  }, 1000);
}

fetchData(function(data) {
  console.log(data);
});</code></pre>
<h2 id="2-promise">2. Promise</h2>
<ul>
<li><p>콜백 지옥과 같은 문제를 해결하기 위한 목적으로 등장</p>
</li>
<li><p>비동기 작업이 성공 또는 실패했을 때의 상태를 나타냄</p>
<ul>
<li><p>Pending: 아직 결과 값이 반환되지 않은 진행 중인 상태</p>
</li>
<li><p>fulfilled: 성공</p>
</li>
<li><p>rejected: 실패</p>
</li>
<li><p>settled: 결과 값이 성공 혹은 실패로 반환된 상태</p>
<p>⇒ settled된 값은 재실행 될 수 없다. </p>
</li>
</ul>
</li>
<li><p>성공 시 resolve 콜백을 호출하고, 실패 시 reject 콜백을 호출함</p>
</li>
<li><p>.then()과 .catch()를 사용해 비동기 작업의 결과를 처리함</p>
<ul>
<li>then 메소드는 Promise 객체 반환 → then, catch 메소드 사용가능 → 연속적인 then 메소드 사용 → <strong><em>Promise chaining</em></strong></li>
<li>catch 이후에도 chaining 가능</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">function fetchData() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(&#39;Data received&#39;);
    }, 1000);
  });
}

fetchData()
  .then(function(data) {
    console.log(data);
  })
  .catch(function(error) {
    console.error(error);
  });</code></pre>
<h3 id="2-1-promiseall">2-1. Promise.all()</h3>
<ul>
<li>배열로 된 여러 개의 프로미스들을 인자로 받아 단 하나의 프로미스를 반환함</li>
<li>모든 프로미스가 성공적으로 이행되면 결과 배열이 반환되며, 하나라도 실패하면 첫 번째 실패한 프로미스의 결과가 반환</li>
</ul>
<pre><code class="language-jsx">const promise1 = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; resolve(&#39;Promise 1&#39;), 1000);
});

const promise2 = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; resolve(&#39;Promise 2&#39;), 2000);
});

const promise3 = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; resolve(&#39;Promise 3&#39;), 3000);
});

Promise.all([promise1, promise2, promise3])
  .then(results =&gt; {
    console.log(results); // [&#39;Promise 1&#39;, &#39;Promise 2&#39;, &#39;Promise 3&#39;]
  })
  .catch(error =&gt; {
    console.error(error); // 첫 번째 실패한 프로미스의 에러
  });</code></pre>
<h2 id="3-asyncawait">3. async/await</h2>
<ul>
<li>프로미스를 기반으로 동작하며, 비동기 코드를 동기식처럼 작성할 수 있도록 함</li>
<li>async 함수 내부에서 await 키워드를 사용하여 프로미스가 처리될 때까지 대기하고, 그 결과를 반환함</li>
</ul>
<pre><code class="language-jsx">async function fetchData() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(&#39;Data received&#39;);
    }, 1000);
  });
}

async function processData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

processData();</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[P2_S3] 자바스크립트 웹 개발 기본기_비동기 + Promise]]></title>
            <link>https://velog.io/@bori_note/P1S3-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8%EA%B8%B0%EB%B9%84%EB%8F%99%EA%B8%B0-Promise</link>
            <guid>https://velog.io/@bori_note/P1S3-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8%EA%B8%B0%EB%B9%84%EB%8F%99%EA%B8%B0-Promise</guid>
            <pubDate>Mon, 01 Apr 2024 05:07:38 GMT</pubDate>
            <description><![CDATA[<h1 id="3-비동기-실행과-promise-객체">(3) 비동기 실행과 Promise 객체</h1>
<pre><code class="language-jsx">console.log(&#39;Start!&#39;);

fetch(&#39;https://www.google.com&#39;)
  .then((response) =&gt; response.text())
  .then((result) =&gt; { console.log(result); });

console.log(&#39;End&#39;);
</code></pre>
<p>지금 이 코드에는 다음과 같은 2개의 콜백이 있다.</p>
<p>(1) <strong>(response) ⇒ response.text()</strong>
(2) <strong>(result) ⇒ { console.log(result); }</strong></p>
<p>fetch 함수가 리퀘스트를 보내고, 서버의 리스폰스를 받게 되면 그때서야 이 콜백들이 순서대로 실행된다.</p>
<p>[전체 코드의 실행 순서]</p>
<ol>
<li>console.log(&#39;Start&#39;);</li>
<li>fetch 함수(리퀘스트 보내기 및 콜백 등록)</li>
<li>console.log(&#39;End&#39;);</li>
<li>리스폰스가 오면 2. 에서 then 메소드로 등록해뒀던 콜백 실행</li>
</ol>
<p>특정 작업을 시작(리퀘스트 보내기)하고 완벽하게 다 처리(리스폰스를 받아서 처리)하기 전에, 실행 흐름이 바로 다음 코드로 넘어가고, 나중에 콜백이 실행되는 것을 <strong>&#39;비동기 실행&#39;</strong>이라고 한다. 이에 반해 한번 시작한 작업은 다 처리하고 나서야, 다음 코드로 넘어가는, 우리에게 익숙한 방식의 실행은 <strong>&#39;동기 실행&#39;</strong>이라고 한다. </p>
<p>👇🏻만약 이 코드에서 fetch 함수가 비동기 실행되지 않고, 동기 실행되는 함수였다고 <strong>가정</strong>한다면?👇🏻</p>
<ol>
<li>console.log(&#39;Start&#39;);</li>
<li>fetch 함수(리퀘스트 보내기)</li>
<li>리스폰스가 올 때까지 코드 실행이 잠시 &#39;정지&#39;되고, 리스폰스가 오면 필요한 처리 수행</li>
<li>console.log(&#39;End&#39;);</li>
</ol>
<p>동기 실행은 한번 시작한 작업을 완료하기 전까지 코드의 실행 흐름이 절대 그 다음 코드로 넘어가지 않는다. 일단 시작한 작업을 완벽하게 처리하고 난 다음에야 그 다음 코드로 실행 흐름이 넘어간다. 따라서 동기 실행의 경우 코드가 보이는 순서대로, 실행된다.</p>
<p>이와 다르게 비동기 실행은 한번 작업을 시작해두고, 그 작업이 완료되기 전이더라도 콜백만 등록해두고, 코드의 실행 흐름이 바로 그 다음 코드로 넘어간다. 그리고 추후에 특정 조건이 만족되면 콜백이 실행됨으로써 해당 작업을 완료하는 방식이다. 따라서 비동기 실행에서는 코드가 꼭 등장하는 순서대로 실행되는 것이 아니다.</p>
<p>❓&#39;비동기 실행&#39;이라는 건 왜 존재하는 걸까 </p>
<p>그건 바로 보통 &#39;비동기 실행&#39;이 &#39;동기 실행&#39;에 비해, <strong>동일한 작업을 더 빠른 시간 내에 처리</strong>할 수 있기 때문이다. </p>
<blockquote>
<p>fetch 함수가 &#39;동기 실행&#39;된다고 가정했을 때</p>
</blockquote>
<blockquote>
<p>fetch 함수가 실행되고 리스폰스가 올 때까지 기다린다는 것 → 바로 리스폰스가 올 때까지는 아무런 작업도 할 수 없다는 뜻이다. 그만큼 <strong>시간을 낭비</strong>하게 되는 셈.</p>
</blockquote>
<p>하지만 만약 비동기 실행이라면 일단 리퀘스트 보내기, 콜백 등록까지만 해두고, 바로 다음 작업(<code>console.log(&#39;End&#39;);</code>)을 시작함으로써 시간을 절약할 수 있다.</p>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4365&directory=Untitled.png&name=Untitled.png" alt=""></p>
<p>이미지 상단은 fetch 함수가 동기 실행된다고 <strong>가정했을 때</strong>
이미지 하단은 fetch 함수가 비동기 실행되는 <strong>실제 모습</strong></p>
<p>지금 동기 실행에서는 모든 작업이 순차적으로 수행되고 있다. 이에 비해, 비동기 실행에서는 리스폰스를 기다리는 시간 동안 그 이후의 작업을 미리 처리하고 있다. 그래서 비동기 실행이 최종 작업 종료 시간이 더 짧다.</p>
<h2 id="📘settimeout-함수"><strong>📘setTimeout 함수</strong></h2>
<p>특정 함수의 실행을 원하는 시간만큼 뒤로 미루기 위해 사용하는 함수</p>
<pre><code class="language-jsx">console.log(&#39;a&#39;);
setTimeout(() =&gt; { console.log(&#39;b&#39;); }, 2000);
console.log(&#39;c&#39;);</code></pre>
<p>setTimeout 함수는 첫 번째 파라미터에 있는</p>
<p><code>() ⇒ { console.log(&#39;b&#39;); }</code>,</p>
<p>이 콜백의 실행을, 두 번째 파라미터에 적힌 2000 밀리세컨즈(=2초) 뒤로 미룬다. 그래서 이 코드를 실행하면</p>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4366&directory=Untitled.png&name=Untitled.png" alt=""></p>
<p>이렇게 a와 c가 먼저 출력되고, <strong>약 2초가 지난 후에 b가 출력된다</strong>. </p>
<p>fetch 함수에서는 콜백이 실행되는 조건이, &#39;리스폰스가 도착했을 때&#39;였다면, setTimeout에서 콜백이 실행되는 조건은, &#39;설정한 밀리세컨즈만큼의 시간이 경과했을 때&#39;이다. 어쨌든 둘 다 콜백의 실행을 나중으로 미룬다는 점에서는 비슷하다.</p>
<h2 id="📘setinterval-함수"><strong>📘setInterval 함수</strong></h2>
<p>특정 콜백을 일정한 시간 간격으로 실행하도록 등록하는 함수</p>
<pre><code class="language-jsx">console.log(&#39;a&#39;);
setInterval(() =&gt; { console.log(&#39;b&#39;); }, 2000);
console.log(&#39;c&#39;);</code></pre>
<p>이렇게 쓰면 이제 b를 출력하는 콜백이 2초 간격으로 계속 실행된다. 실제로 확인해보면</p>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4366&directory=Untitled%201.png&name=Untitled+1.png" alt=""></p>
<p>a와 c가 출력되고, 약 2초 뒤에 b가 출력된 후 그 뒤로 계속 2초 간격으로 b가 반복 출력된다. </p>
<h2 id="📘addeventlistener-메소드"><strong>📘addEventListener 메소드</strong></h2>
<p>만약 사용자가 웹 페이지에서 어떤 버튼 등을 클릭했을 때, 실행하고 싶은 함수가 있다면</p>
<p>(1) 해당 DOM 객체의 onclick 속성에 그 함수를 설정하거나, 
(2) 해당 DOM 객체의 addEventListener 메소드의 파라미터로 전달하면 된다.</p>
<p>(1) onclick 속성</p>
<pre><code class="language-jsx">...

btn.onclick = function (e) { 
// 해당 이벤트 객체가 파라미터 e로 넘어온다.
  console.log(&#39;Hello Codeit!&#39;);
};

// 또는 arrow function 형식으로 이렇게 나타낼 수도 있다.
btn.onclick = (e) =&gt; {
  console.log(&#39;Hello Codeit!&#39;);
};

...</code></pre>
<p>(2) addEventListener 메소드</p>
<pre><code class="language-jsx">...

btn.addEventListener(&#39;click&#39;, function (e) { 
// 해당 이벤트 객체가 파라미터 e로 넘어온다.
  console.log(&#39;Hello Codeit!&#39;);
});

// 또는 arrow function 형식으로 이렇게 나타낼 수도 있다.
btn.addEventListener(&#39;click&#39;, (e) =&gt; {
  console.log(&#39;Hello Codeit!&#39;);
});

...</code></pre>
<p>이렇게 클릭과 같은 특정 이벤트가 발생했을 때 실행할 콜백을 등록하는 addEventListener 메소드도 비동기 실행과 관련이 있다. 파라미터로 전달된 콜백이 당장 실행되는 것이 아니라, 나중에 특정 조건(클릭 이벤트 발생)이 만족될 때(마다) 실행되기 때문이다.</p>
<pre><code class="language-jsx">
setTimeout(콜백, 시간)
setInterval(콜백, 시간)
addEventListener(이벤트 이름, 콜백)
</code></pre>
<p>→ 함수의 아규먼트로 바로 콜백을 넣는다. </p>
<p>그런데 fetch 함수는 이 함수들과는 전혀 다르게 생겼다.</p>
<pre><code class="language-jsx">fetch(&#39;https://www.google.com&#39;)
  // fetch 함수가 리턴하는 객체의 then 메소드를 사용해서 콜백을 등록
  .then((response) =&gt; response.text()) 
  .then((result) =&gt; { console.log(result); });</code></pre>
<p>fetch 함수는 콜백을 파라미터로 바로 전달받는 게 아니라, <strong>fetch 함수가 리턴하는 어떤 객체의 then 메소드를 사용해서 콜백을 등록한다.</strong></p>
<p>위에서 본 함수들처럼, fetch 함수도 이런 식으로 코드를 써야할 것만 같은데.</p>
<pre><code class="language-jsx">fetch(&#39;https://www.google.com&#39;, (response) =&gt; response.text())</code></pre>
<p>❓왜 fetch 함수만 사용하는 형식이 다른 걸까? </p>
<p>그건 바로 fetch 함수는, 좀 더 새로운 방식으로 비동기 실행을 지원하는 자바스크립트 문법과 연관이 있기 때문. 사실 <strong>fetch 함수는 Promise 객체라는 것을 리턴하고, 이 Promise 객체는 비동기 실행을 지원하는 또 다른 종류의 문법에 해당</strong>하기 때문.</p>
<h1 id="✨promise">✨Promise</h1>
<p>어떤 작업에 관한 “상태 정보”를 가지고 있는 것.</p>
<ul>
<li>pending: 진행 중</li>
<li>fulfilled: 성공 → 작업 성공 결과(response)</li>
<li>rejected: 실패 → 작업 실패 정보</li>
</ul>
<p>then 메소드란 프로미스 객체가 fulfilled 상태가 되었을 때 실행할 콜백을 등록하는 메소드</p>
<h2 id="📘promise--chaining">📘Promise  Chaining</h2>
<p>then 메소드를 연속적으로 붙이는 것</p>
<ul>
<li>promise객체의 then 메소드는 또 다른 promise 객체를 리턴한다.</li>
<li>이 새로운 promise 객체는 처음에는 pending 상태지만 then 메소드 안의 콜백이 실행되고 어떤 값을 리턴하는지에 따라 그 상태가 달라진다.</li>
<li>만약 콜백에서 promise 객체를 리턴하면 앞으로 promise 객체가 갖게 될 상태와 결과를 그대로 따라서 갖게 된다.</li>
<li>프로미스 객체 이외의 값(ex. 숫자, 문자열, 일반 객체)이라면 fulfilled 상태가 되고 해당 리턴값을 작업 성공 결과로 갖게 된다.</li>
</ul>
<h2 id="📘text-json-메소드도-promise-객체를-리턴한다">📘text, json 메소드도 Promise 객체를 리턴한다.</h2>
<pre><code class="language-jsx">console.log(&#39;Start!&#39;);

fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; response.text())
  .then((result) =&gt; {
    const users = JSON.parse(result);
    // ...
  });

console.log(&#39;End&#39;);
</code></pre>
<p>response 객체의 text 메소드로 리스폰스의 내용을 추출(<code>response.text();</code>)하고 이것을 Deserialize하거나(<code>JSON.parse(result);</code>)</p>
<pre><code class="language-jsx">
console.log(&#39;Start!&#39;);

fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; response.json())
  .then((users) =&gt; {
    // ...
  });

console.log(&#39;End&#39;);
</code></pre>
<p>response 객체의 json 메소드로 리스폰스의 내용 추출과 Deserialize를 한 번에 수행(<code>response.json()</code>)할 수 있다.</p>
<p>중요한 사실!! → <strong>text 메소드와 json 메소드가 사실은 Promise 객체를 리턴하는 메소드라는 사실!</strong></p>
<h3 id="1-text-메소드"><strong>1. text 메소드</strong></h3>
<p>fetch 함수로 리스폰스를 잘 받으면, response 객체의 <strong>text 메소드</strong>는, <strong>fulfilled 상태</strong>이면서 <strong>리스폰스의 바디에 있는 내용을 string 타입으로 변환한 값</strong>을 &#39;작업 성공 결과&#39;로 가진 Promise 객체를 리턴한다. </p>
<p>이때 그 작업 성공 결과는 string 타입. 이때 그 값이 만약 JSON 데이터라면 이전에 배운 것처럼 JSON 객체의 parse 메소드로 Deserialize를 해줘야한다.(<code>JSON.parse(result);</code>)</p>
<h3 id="2-json-메소드"><strong>2. json 메소드</strong></h3>
<p>fetch 함수로 리스폰스를 잘 받으면, response 객체의 <strong>json 메소드</strong>는, <strong>fulfilled 상태</strong>이면서, <strong>리스폰스의 바디에 있는 JSON 데이터를 자바스크립트 객체로 Deserialize해서 생겨난 객체</strong>를 &#39;작업 성공 결과&#39;로 가진 Promise 객체를 리턴한다. </p>
<p>만약 리스폰스의 바디에 있는 내용이 JSON 타입이 아니라면 에러가 발생하고 Promise 객체는 rejected 상태가 되면서 그 &#39;작업 실패 정보&#39;를 갖게 된다.</p>
<p>👇🏻👇🏻❓&#39;then 메소드가 리턴했던 Promise 객체(A)는 그 콜백에서 리턴한 Promise 객체(B)와 동일한 상태와 결과를 갖게 된다&#39;는 규칙과 연관지어서 생각해보면</p>
<p>→ 콜백에서 리턴한 Promise 객체로부터 새로운 Chain이 시작된다는 말과도 같다.</p>
<p>response 객체의 text 메소드 또는 json 메소드 이후에 등장하는 then 메소드부터는 string 타입의 값이나 자바스크립트 객체를 갖고 바로 원하는 작업을 할 수 있다. </p>
<h2 id="📘then-메소드">📘then 메소드</h2>
<pre><code class="language-jsx">const successCallback = function () { };
const errorCallback = function () { };

fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;) // Promise-A
  .then(successCallback, errorCallback); // Promise-B</code></pre>
<p>(1) fetch 메소드가 리턴하는 Promise 객체를 Promise-A 객체라고 하고, 
(2) then 메소드가 리턴하는 Promise 객체를 Promise-B 객체라고 해보자.</p>
<ol>
<li>fetch 함수의 작업이 성공해서 Promise-A 객체가 <strong>fulfilled 상태</strong>가 된 경우 : then 메소드 안의 &quot;첫 번째&quot; 콜백인 <strong>successCallback</strong>이 실행된다.</li>
<li>fetch 함수의 작업이 실패해서 Promise-A 객체가 <strong>rejected 상태</strong>가 된 경우 : then 메소드 안의 &quot;두 번째&quot; 콜백인 <strong>errorCallback</strong>이 실행된다.</li>
</ol>
<p>여기서 중요한 점)</p>
<p><strong>Promise-B는, 실행된 successCallback 또는 errorCallback에서 무엇을 리턴하느냐</strong>에 따라</p>
<ul>
<li>그 <strong>상태</strong>(fulfilled or rejected)와</li>
<li><strong>결과</strong>(작업 성공 결과 or 작업 실패 정보)가 결정된다.</li>
</ul>
<h2 id="1-실행된-콜백이-어떤-값을-리턴하는-경우"><strong>1. 실행된 콜백이 어떤 값을 리턴하는 경우</strong></h2>
<p>successCallback이 실행되든, errorCallback이 실행되든, 실행된 콜백에서 어떤 값을 리턴하는 경우다. 이때 그 값의 종류에 따라</p>
<ul>
<li>Promise 객체인 경우와</li>
<li>Promise 객체 이외의 경우,</li>
</ul>
<p>이 2가지 경우로 다시 나눌 수 있다.</p>
<h3 id="1-promise-객체를-리턴하는-경우"><strong>(1) Promise 객체를 리턴하는 경우</strong></h3>
<pre><code class="language-jsx">
fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; response.json())
  .then((result) =&gt; { console.log(result) });</code></pre>
<ul>
<li><code>(response) ⇒ response.json()</code> 이 콜백은 Promise 객체를 리턴하는 코드다.</li>
<li>콜백에서 Promise 객체를 리턴하는 경우에는 그 콜백을 등록한 then 메소드가 리턴했던 Promise 객체가 <strong>콜백이 리턴한 Promise 객체의 상태와 결과를 똑같이 따라 갖게 된다.</strong></li>
<li>즉, 코드의 첫 번째 then 메소드가 리턴했던 Promise 객체는, response 객체의 json 메소드가 리턴한 Promise 객체가 추후에 갖게 되는 상태와 결과를 그대로 따라서 갖게 된다는 뜻다.</li>
<li>콜백에서 리턴하는 Promise 객체를 then 메소드가 그대로 리턴한다고 생각해도 됨. 그 다음부터는 <strong>콜백에서 리턴한 Promise 객체로부터 다시 Promise Chain이 쭉 이어져 나간다</strong>고 보면 된다.</li>
</ul>
<h3 id="2-promise-객체-이외의-값을-리턴하는-경우"><strong>(2) Promise 객체 이외의 값을 리턴하는 경우</strong></h3>
<p>콜백이 꼭 Promise 객체만을 리턴하는 것은 아니다. 그냥 단순한 숫자, 문자열, 일반 객체 등을 리턴할 수도 있다. 이런 경우에 then 메소드가 리턴했던 Promise 객체는 <strong>fulfilled 상태</strong>가 되고 <strong>작업 성공 결과로 그 값을 갖게 된다.</strong></p>
<pre><code class="language-jsx">
// Internet Disconnected

fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; response.json(), (error) =&gt; &#39;Try again!&#39;)
  .then((result) =&gt; { console.log(result) });
</code></pre>
<p>예시)</p>
<p>지금 인터넷이 안 되는 상황에서 이 코드를 실행했다고 해보자. </p>
<ul>
<li>fetch 함수의 작업이 실패해서 두 번째 콜백인 <code>(error) ⇒ &#39;Try again!</code> 이 실행된다.</li>
<li>두 번째 콜백은 <strong>&#39;Try again!&#39;</strong>이라는 문자열을 리턴하고 있다. 이렇게 하면 해당 콜백을 등록한 then 메소드가 리턴했던 Promise가 fulfilled 상태가 되고, 그 작업 성공 결과로 &#39;Try again&#39; 문자열을 갖게 된다.</li>
</ul>
<h2 id="2-실행된-콜백이-아무-값도-리턴하지-않는-경우"><strong>2. 실행된 콜백이 아무 값도 리턴하지 않는 경우</strong></h2>
<pre><code class="language-jsx">
// Internet Disconnected

fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; response.json(), (error) =&gt; { alert(&#39;Try again!&#39;); })
  .then((result) =&gt; { console.log(result) });</code></pre>
<p>콜백이 무언가를 리턴하는 게 아니라 이 코드에서처럼 단순히 alert 함수만 실행하고 끝난다고 해보자. </p>
<ul>
<li>결과적으로 이 콜백은 아무런 값도 리턴하지 않은 것과 같다. 자바스크립트에서는 함수가 아무것도 리턴하지 않으면 undefined를 리턴한 것으로 간주된다.</li>
<li>방금 전 <strong>1. (2) 규칙에 따라</strong> then 메소드가 리턴했던 Promise 객체는 <strong>fulfilled 상태</strong>가 되고, <strong>그 작업 성공 결과로 undefined</strong>를 갖게 된다.</li>
</ul>
<h2 id="3-실행된-콜백-내부에서-에러가-발생했을-때"><strong>3. 실행된 콜백 내부에서 에러가 발생했을 때</strong></h2>
<p>콜백이 실행되다가 에러가 발생하는 경우가 있다. </p>
<p>예시)</p>
<pre><code class="language-jsx">
fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; {
        ...
        add(1, 2); // ReferenceError 발생
        ...
  });</code></pre>
<p>이렇게 정의하지도 않은 함수를 콜백에서 사용해서 에러가 발생하거나</p>
<pre><code class="language-jsx">
fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; {
        ...
        throw new Error(&#39;failed&#39;);
        ...
  });
</code></pre>
<p>특정 경우에 인위적으로 throw 문을 써서 에러를 발생시키는 경우도 있다.</p>
<p>이렇게 콜백이 실행되다가 에러가 발생한다면, then 메소드가 리턴했던 Promise 객체는 어떻게 될까? </p>
<p>이 경우에는 Promise 객체가 <strong>rejected 상태</strong>가 되고, 작업 실패 정보로 해당 에러 객체를 갖게 된다. </p>
<pre><code class="language-jsx">
const promise = fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; { throw new Error(&#39;test&#39;); });
</code></pre>
<p><strong>promise</strong> 를 입력해 then 메소드가 리턴한 Promise 객체의 내부를 살펴보면👇🏻</p>
<p><a href="https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=4374&amp;directory=Untitled.png&amp;name=Untitled.png">https://bakey-api.codeit.kr/api/files/resource?root=static&amp;seqId=4374&amp;directory=Untitled.png&amp;name=Untitled.png</a></p>
<p>지금 [[PromiseState]]는 Promise 객체의 상태를, [[PromiseResult]]는 Promise 객체의 결과(작업 성공 결과 또는 작업 실패 정보)를 나타내는 내부 슬롯이다.</p>
<p>현재 Promise 객체가 rejected 상태이고, 발생한 Error 객체를 그 작업 실패 정보로 갖고 있다는 것을 알 수 있다. 이렇게 콜백 실행 중에 에러가 발생하면, then 메소드가 리턴한 Promise 객체는 <strong>rejected 상태</strong>가 되고, 그 <strong>작업 실패 정보로 해당 Error 객체를 갖게 된다.</strong></p>
<h2 id="4-아무런-콜백도-실행되지-않을-때"><strong>4. 아무런 콜백도 실행되지 않을 때</strong></h2>
<pre><code class="language-jsx">
// Internet Disconnected

fetch(&#39;https://www.google.com&#39;) // Promise-1
  .then((response) =&gt; response.text()) // Promise-2
  .then((result) =&gt; { console.log(result) }, (error) =&gt; { alert(error) });
</code></pre>
<p>then 메소드의 아무런 콜백도 실행되지 않는 경우가 있다. </p>
<p>인터넷을 끊고 나서 위 코드를 실행했다고 해보자. </p>
<ul>
<li>fetch 함수가 리턴한 Promise-1 객체는 rejected 상태가 되기 때문에, 첫 번째 then 메소드의 두 번재 콜백이 실행되어야 다. 그런데 지금 두 번째 콜백이 없다. 이 경우에는 아무런 콜백도 실행되지 않는다. 이런 경우에 then 메소드가 리턴한 Promise-2 객체는 어떻게 될까? 이런 경우에 then 메소드가 리턴했던 Promise-2 객체는, <strong>이전 Promise 객체와 동일한 상태와 결과를 갖게 된다.</strong> → 지금 Promise-2 객체는 Promise-1 객체처럼 rejected 상태가 되고, 똑같은 작업 실패 정보를 갖게 된다.</li>
<li>그럼 rejected 상태가 된 Promise-2의 then 메소드에는 이제 두 번째 콜백이 존재하기 때문에 그 두 번째 콜백이 실행된다.</li>
<li>아무런 콜백도 실행되지 않는 경우에는 그 이전 Promise 객체의 상태와 결과가 그대로 이어진다는 사실!</li>
</ul>
<p>예시)</p>
<pre><code class="language-jsx">fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; {
    return response.json(); // &lt;- Case(1)
    return 10; // &lt;- Case(2)
    // &lt;- Case(3)
    throw new Error(&#39;failed&#39;); // &lt;- Case(4)
})
  .then((result) =&gt; {
    console.log(result);
  });

// 존재하지 않는 URL
fetch(&#39;https://jsonplaceholder.typicode.commmmmm/users&#39;)
  .then((response) =&gt; response.json()) // &lt;- Case(5)
  .then((result) =&gt; { }, (error) =&gt; { console.log(error) });</code></pre>
<p><em>then 메소드가 리턴한 Promise객체를 A라고 할 때</em></p>
<p><strong>Case(1)</strong>: 콜백에서 Promise 객체를 리턴</p>
<ul>
<li>콜백이 리턴한 Promise  객체를 B라고 하면 A는 B와 동일한 상태와 결과를 갖게 된다.</li>
</ul>
<p><strong>Case(2)</strong>: 콜백에서 Promise 객체가 아닌 일반적인 값을 리턴</p>
<ul>
<li>A는 fulfilled 상태가 되고, 해당 리턴값을 작업 성공 결과로 갖게 된다.</li>
</ul>
<p><strong>Case(3)</strong>: 콜백에서 아무것도 리턴하지 않음</p>
<ul>
<li>undefined 리턴함 → A는 fulfilled 상태가 되고, undefined를 작업 성공 결과로 갖게 된다.</li>
</ul>
<p><strong>Case(4)</strong>: 콜백 실행 중 에러 발생</p>
<ul>
<li>A는 rejected 상태가 되고, 해당 에러 객체를 작업 실패 정보로 갖게 된다.</li>
</ul>
<p><strong>Case(5)</strong>: 콜백이 실행되지 않음</p>
<ul>
<li>A는 호출된 <code>then</code> 메소드의 주인에 해당하는, 이전 Promise 객체와 동일한 상태와 결과를 가진다.</li>
</ul>
<h1 id="✨catch">✨catch</h1>
<p>❓어떻게 fetch 함수에서 발생한 에러가 catch 메소드 안의 콜백에까지 전달될 수 있는 걸까?</p>
<pre><code class="language-jsx">// catch문
fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; response.text())
  .catch((error) =&gt; { console.log(error); })
  .then((result) =&gt; { console.log(result); });

// catch 말고 then을 쓸 때
fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;) // Promise-A
  .then((response) =&gt; response.text()) // Promise-B
  .then(undefined, (error) =&gt; { console.log(error); }) // Promise-C
  .then((result) =&gt; { console.log(result); }); // Promise-D
</code></pre>
<ul>
<li>catch 메소드는 사실 then 메소드의 첫 번째 인자로 undefined을 넣은 것과 같다.</li>
<li>fetch 함수의 작업이 실패해서 Promise-A 객체가 rejected 상태가 되면, 첫 번째 then 메소드의 두 번째 콜백이 실행되어야 한다. 하지만 지금 첫 번째 then 메소드에는 두 번째 콜백이 없기 때문에 아무 콜백도 실행되지 않는다.</li>
<li><strong>Promise-B 객체가 Promise-A와 똑같은 rejected 상태가 되고, 동일한 작업 실패 정보를 갖게 된다.</strong></li>
<li>rejected 상태가 된 Promise-B에 붙은 then 메소드에는 두 번째 콜백이 있기 때문에 이 두 번째 콜백이 실행된다. 즉, catch 메소드의 콜백이 실행된다.</li>
</ul>
<h2 id="📘catch-메소드를-여러-개-쓰는-경우">📘catch 메소드를 여러 개 쓰는 경우</h2>
<p>catch 메소드를 마지막뿐만 아니라 Promise Chain 중간중간에 쓰는 경우도 존재한다. <strong>만약 중간에 에러가 발생해도 catch 메소드가 그 대안을 뒤로 넘겨줄 수 있으면 catch 메소드를 중간에 써도 된다.</strong></p>
<pre><code class="language-jsx">fetch(&#39;https://friendbook.com/my/newsfeeds&#39;)
  .then((response) =&gt; response.json()) // -- A
  .then((result) =&gt; { // -- B
    const feeds = result;
    // 피드 데이터 가공...
    return processedFeeds;
  })
  .catch((error) =&gt; { // -- C
    // 미리 저장해둔 일반 뉴스를 보여주기
    const storedGeneralNews = getStoredGeneralNews();
    return storedGeneralNews;
  })
  .then((result) =&gt; { /* 화면에 표시 */ }) // -- D
  .catch((error) =&gt; { /* 에러 로깅 */ }); // -- E</code></pre>
<ul>
<li>만약 서버로부터 뉴스피드가 잘 조회되면 현재 코드에서 <strong>A, B, D 줄에 있는 콜백들</strong>이 잘 실행되고, 사용자에게 뉴스피드가 잘 표시될 것이다.</li>
<li>하지만 만약 사용자의 컴퓨터가 인터넷에 연결되어 있지 않은 상태라서 fetch 함수의 작업이 실패한다면?<ul>
<li>이 Promise Chain의 작업은 실패했다고 생각하고, 마지막에만 catch 메소드를 두고 끝내면 되는 걸까? 꼭 그렇지는 않다.</li>
</ul>
</li>
<li>fetch 함수의 작업이 실패하면 C 줄의 콜백이 실행된다. (SNS 서비스의 웹 페이지에서는 나중에 오프라인 상태가 될 때를 대비해서 모든 사람이 공통으로 볼 수 있는, 텍스트로만 이루어진 <strong>최근 일반 뉴스 데이터</strong>를 갱신해서 웹 브라우저에 저장한다고 해보자)<ul>
<li>C줄의 콜백은 바로 이렇게 저장해둔 일반 뉴스 데이터를 그대로 가져오는 기능을 한다. <strong>이렇게 되면 인터넷이 안 되는 상황에서도 나만을 위한 최적화된 뉴스피드는 못 보지만 일반적인 세상 뉴스는 사용자가 볼 수 있다.</strong></li>
</ul>
</li>
<li>Promise Chain 중에서 <strong>비록 에러가 발생했다고 해도 만약 실패한 작업 대신 다른 방법을 통해서 작업을 정상적으로 끝마칠 수 있는 상황이라면 catch 메소드를 중간에 사용하기도 한다.</strong></li>
<li>Promise Chain 중에서 단 하나의 작업이라도 실패하면 전체 작업이 실패했다고 봐도 되는 경우에는 그냥 Promise Chain 마지막에만 catch 메소드를 써주면 되겠지만, 어떤 작업들은 에러가 발생하더라도 다른 방식으로 복구해서 살려낼 방법이 있다면 catch 메소드 안의 콜백에서 그런 복구 작업을 해주면 된다.</li>
</ul>
<p>⇒ catch 메소드를 Promise Chain의 마지막에 늘 써줘야 하는 것은 맞지만, 작업을 살릴 방법이 있다면 Promise Chain 중간에 catch 메소드를 써도 된다는 사실!!</p>
<h1 id="✨finally">✨finally</h1>
<p>어떤 작업이 성공하든 실패하든 상관없이(Promise 객체가 fulfilled상태가 되든  rejected 상태가 되든 상관없이) 항상 실행하고 싶을 콜백이 있을 때 씀.</p>
<hr>
<h1 id="❓promise-객체는-왜-등장했을까9">❓Promise 객체는 왜 등장했을까?9</h1>
<p>사실 Promise 객체가 등장하기 전에도 비동기적인 처리를 할 수 있는 방법은 있었다. 
(setTimeout 함수나, addEventListener 메소드)</p>
<pre><code class="language-jsx">setTimeout(callback, milliseconds);
addEventListener(eventname, callback);</code></pre>
<p>이것들은 모두 직접 파라미터에 콜백을 전달하는 형식으로 정의되어 있다. 만약 fetch 함수를 이런 식으로 만들었다면</p>
<pre><code class="language-jsx">fetch(&#39;https://first.com&#39;, callback)</code></pre>
<p>그런데 왜 이런 방법이 선택되지 않고, 굳이 Promise 객체라는 문법이 도입된 것일까?</p>
<p>그 이유는 바로 함수에 콜백을 직접 넣는 형식은 <strong>콜백 헬(callback hell)</strong>이라고 하는 문제를 일으킬 수도 있기 때문이다.</p>
<p>만약 fetch 함수가 지금과 같이 Promise 객체를 리턴하는 게 아니라 setTimeout 함수처럼 콜백을 직접 집어넣는 형식의 함수였다면…</p>
<pre><code class="language-jsx">
fetch(&#39;https://first.com&#39;, (response) =&gt; {
  // Do Something
  fetch(&#39;https://second.com&#39;, (response) =&gt; {
    // Do Something
    fetch(&#39;https;//third.com&#39;, (response) =&gt; {
      // Do Something
      fetch(&#39;https;//fourth.com&#39;, (response) =&gt; {
        // Do Something
      });
    });
  });
});</code></pre>
<p><strong>⇒ 가독성이 떨어진다.</strong> </p>
<p>이런 현상을 <strong>콜백 지옥 또는 콜백 헬(callback hell)</strong>이라고 한다. 또는 지옥의 피라미드(Pyramid of Doom)라고도 한다.</p>
<p>하지만 fetch 함수는 Promise 객체를 리턴하기 때문에</p>
<pre><code class="language-jsx">fetch(&#39;https://first.com&#39;)
  .then((response) =&gt; {
    // Do Something
    return fetch(&#39;https://second.com&#39;);
  })
  .then((response) =&gt; {
    // Do Something
    return fetch(&#39;https://third.com&#39;);
  })
  .then((response) =&gt; {
    // Do Something
    return fetch(&#39;https://third.com&#39;);
  });</code></pre>
<p>Promise Chaining을 해서 좀 더 깔끔한 코드로 여러 비동기 작업을 순차적으로 처리할 수 있다. 
이렇게 <strong>Promise 객체를 사용하면 callback hell 문제를 해결할 수 있다.</strong></p>
<p>이 뿐만 아니라 기존에 콜백을 직접 넣는 방식에 비해 Promise 객체의 문법은 비동기 작업에 관한 좀 더 세밀한 개념들이 반영되어 있다. 이전의 방식에서는 <em>콜백에 필요한 인자를 넣어주고 실행하면 되는 단순한 방식이었다면, Promise 객체 문법에는 pending, fulfilled, rejected 상태, 작업 성공 결과 및 작업 실패 정보(이유), then, catch, finally 메소드 등과 같은 비동기 작업에 관한 보다 정교한 설계가 문법 자체에 반영되어 있다</em>는 것을 알 수 있다.</p>
<p>Promise 객체라는 개념은,</p>
<p>(1) <strong>callback hell 문제를 해결</strong>하고,<br>(2) <strong>비동기 작업 처리</strong>에 관한 좀 더 세밀한 처리를 자바스크립트 문법 단에서 해결하기 위해 등장했고,</p>
<p><strong>자바스크립트의 2015년도 표준인 ES6(=ES2015)</strong>에 추가되었다.</p>
<h1 id="✨promisify">✨promisify</h1>
<p>언제 Promise 객체를 직접 만들게 되는 걸까? 다양한 경우들이 있지만, <strong>전통적인 형식의 비동기 실행 함수를 사용하는 코드를, Promise 기반의 코드로 변환하기 위해</strong> Promise 객체를 직접 만드는 경우가 많다. </p>
<h2 id="1-settimeout-함수-예시"><strong>1. setTimeout 함수 예시</strong></h2>
<pre><code class="language-jsx">function wait(text, milliseconds) {
  setTimeout(() =&gt; text, milliseconds);
}</code></pre>
<p>wait 함수는 특정 밀리세컨즈만큼 시간이 지난 후에 text 파라미터로 전달받은 값을 리턴하는 함수다. 이 wait 함수를 Promise Chaining 코드에서 사용하면</p>
<pre><code class="language-jsx">fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; response.text())
  .then((result) =&gt; { console.log(result); });</code></pre>
<p>바로 이 Promise Chaining 코드에 wait 함수를 추가하면</p>
<pre><code class="language-jsx">function wait(text, milliseconds) {
  setTimeout(() =&gt; text, milliseconds);
}

fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; response.text())
  // 2초 후에 리스폰스의 내용 뒤에 &#39;by Codeit&#39; 추가하고 리턴
  .then((result) =&gt; wait(`${result} by Codeit`, 2000)) 
  .then((result) =&gt; { console.log(result); });
</code></pre>
<p>기존 코드에 두 번째 then 메소드를 추가하고, 그 안에서 wait 함수를 호출했다. 이렇게 쓰면 2초 후에 리스폰스의 내용 뒤에 by Codeit이라는 문구를 붙여서 출력될 것 같다. </p>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4388&directory=jo6wlcosl-Untitled.png&name=jo6wlcosl-Untitled.png" alt=""></p>
<p><strong>리스폰스의 내용과 by Codeit이 출력되지 않았다. 그 대신 undefined가 출력되었다.</strong></p>
<p>왜 그런 걸까? 
그 이유는 바로 wait 함수에 있다.</p>
<pre><code class="language-jsx">function wait(text, milliseconds) {
  setTimeout(() =&gt; text, milliseconds);
}</code></pre>
<p>이 wait 함수는 내부에서 <strong>setTimeout 함수를 호출한다</strong>. </p>
<p>그리고 <strong>setTimeout 함수의 첫 번째 파라미터로 들어간 콜백이 2초 후에 text를 리턴한다</strong>. </p>
<p>그런데 여기서 혼동하면 안 되는 것은 <strong>wait 함수가</strong></p>
<pre><code class="language-jsx">...
  .then((result) =&gt; { return wait(`${result} by Codeit`, 2000); })
...</code></pre>
<p><strong>이 두 번째 then 메소드 안의 콜백에서 실행될 때,</strong></p>
<p><strong>wait 함수는 setTimeout 함수를 실행할 뿐 아무것도 리턴하지 않는다.</strong> 
setTimeout 함수 안의 콜백이 2초 후에 리턴하는 <strong>text는, wait 함수의 리턴값이 아니다.</strong></p>
<ul>
<li>wait 함수는 단지 setTimeout 함수를 실행하고 아무것도 리턴하지 않는 함수일 뿐.</li>
<li>자바스크립트에서는 이전에 배운대로 함수에서 아무것도 리턴하지 않으면 undefined를 리턴하는 것으로 간주하기 때문에 wait 함수의 리턴값은 undefined.</li>
<li>따라서 세 번째 then 메소드의 콜백으로 undefined가 넘어가고, undefined가 출력된 것.</li>
</ul>
<p>setTimeout은 비동기 실행되는 함수다. <strong>Promise Chaining 안에서 이렇게 비동기 실행되는 함수를 바로 사용하면, 나중에 실행되는 부분의 리턴값(여기서는 text)를 Promise Chain에서 사용할 수 없게 된다.</strong></p>
<p>해결방법)</p>
<pre><code class="language-jsx">// function wait(text, milliseconds) {
//   setTimeout(() =&gt; text, milliseconds);
// }

function wait(text, milliseconds) {
  const p = new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; { resolve(text); }, 2000);
  });
  return p;
}</code></pre>
<ul>
<li>wait 함수 안에서 Promise 객체를 직접 생성했고, executor 함수 안에서 setTimeout 함수를 호출했다.</li>
<li>setTimeout 함수 안의 콜백에서 resolve 함수를 호출하는데 이 때 그 아규먼트로 text를 넣었다.</li>
<li>Promise 객체 p는 2초 후에 fulfilled 상태가 될 것이고, 그 작업 성공 결과는 파라미터 text의 값이 될 될 것이다. <strong>wait 함수는 이제 Promise 객체 p를 리턴한다.</strong></li>
</ul>
<p>자, 이 상태에서 코드를 다시 실행해보면</p>
<pre><code class="language-jsx">
function wait(text, milliseconds) {
  const p = new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; { resolve(text); }, 2000);
  });
  return p;
}

fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; response.text())
  .then((result) =&gt; wait(`${result} by Codeit`, 2000)) // 2초 후에 리스폰스의 내용 뒤에 &#39;by Codeit&#39; 추가하고 리턴
  .then((result) =&gt; { console.log(result); });
</code></pre>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4388&directory=fbmbi7m6k-Untitled%201.png&name=fbmbi7m6k-Untitled+1.png" alt=""></p>
<p>이번에는 약 2초 후에 리스폰스의 내용이 잘 출력되고,</p>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4388&directory=5d9bljl8g-Untitled%202.png&name=5d9bljl8g-Untitled+2.png" alt=""></p>
<p>⇒ 기존의 비동기 실행 함수(여기서는 setTimeout)의 콜백이 리턴하는 값을 Promise Chain에서 사용하고 싶다면, 해당 함수를 감싸서 Promise 객체를 직접 생성하는 코드를 작성해야 한다. 그리고 그 Promise 객체를 리턴해야 방금처럼 Promise Chain에서 해당 리턴값을 받아서 사용할 수 있다.</p>
<p>이렇게 전통적인 형식의 비동기 실행 함수를 Promise 객체로 감싸서 그 Promise 객체를 리턴하는 형식으로 만드는 작업을 <strong>Promisify(프로미스화하다)</strong>라고 한. </p>
<h2 id="2-콜백-헬callback-hell과-promise"><strong>2. 콜백 헬(callback hell)과 Promise</strong></h2>
<p>Node.js는 오늘날 자바스크립트를 서버에서도 실행할 수 있게 해주는 또 다른 &#39;자바스크립트 실행 환경&#39;이다. 이 Node.js에서는 브라우저에서와는 또 다른 비동기 함수들이 제공된다.</p>
<p>Node.js에는 다음과 같이 특정 파일의 내용을 읽기 위해 사용되는 readFile이라는 비동기 실행 메소드가 있다.</p>
<pre><code class="language-jsx">fs.readFile(&#39;file1.txt&#39;, &#39;utf8&#39;, (error, data) =&gt; {
  if (err) {
    console.log(err);
  } else {
    console.log(data);
  }
});</code></pre>
<ul>
<li>fs는 readFile 메소드를 가진 객체로, 파일에 관한 기능들을 갖고 있다.</li>
<li>readFile 메소드는 첫 번째 파라미터로 파일의 이름, 두 번째 파라미터로 파일 해석 기준(인코딩 기준), 세 번째 파라미터로 콜백을 받는다.</li>
<li>readFile 함수는 파일을 읽다가 에러가 발생하면 콜백의 첫 번째 파라미터(error)에, 해당 에러 객체를 전달하고 콜백을 실행한다. 만약 파일을 정상적으로 다 읽었으면 콜백의 두 번째 파라미터(data)에, 읽어들인 파일 내용을 전달하고 콜백을 실행한다.</li>
<li>readFile 메소드도, 콜백을 파라미터에 바로 넣는 비동기 실행 함수라는 점에서 setTimeout 함수, addEventListener 메소드와 비슷하다.<ul>
<li>콜백 헬(callback hell) 문제) 위 코드에서 이제 <strong>file1.txt 파일의 내용을 출력하고 나서 그 다음에 file2.txt라는 파일의 내용을 또 출력</strong>해야한다고 해보자.</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">
fs.readFile(&#39;file1.txt&#39;, &#39;utf8&#39;, (error1, data1) =&gt; {
  if (error1) {
    console.log(error1);
  } else {
    console.log(data1);
    fs.readFile(&#39;file2.txt&#39;, &#39;utf8&#39;, (error2, data2) =&gt; {
      if (error2) {
        console.log(error2);
      } else {
        console.log(data2);
      }
    });
  }
});
</code></pre>
<p>이렇게 코드를 쓰면 file1.txt의 내용이 출력되고, 그 다음에 file2.txt의 내용이 출력될 것이다. 코드가 그런데 이제 그 다음으로 file3.txt의 내용도 출력해야 한다고 해보자.</p>
<pre><code class="language-jsx">fs.readFile(&#39;file1.txt&#39;, &#39;utf8&#39;, (error1, data1) =&gt; {
  if (error1) {
    console.log(error1);
  } else {
    console.log(data1);
    fs.readFile(&#39;file2.txt&#39;, &#39;utf8&#39;, (error2, data2) =&gt; {
      if (error2) {
        console.log(error2);
      } else {
        console.log(data2);
        fs.readFile(&#39;file3.txt&#39;, &#39;utf8&#39;, (error3, data3) =&gt; {
          if (error3) {
            console.log(error3);
          } else {
            console.log(data3);
          }
        });
      }
    });
  }
});
</code></pre>
<p>⇒ 코드 읽기 어려워짐</p>
<p>콜백을 바로 파라미터에 집어넣는 전통적인 형식의 비동기 실행 함수들은 이런 문제가 있다. 바로 순차적으로 비동기 실행 함수들을 실행하려고 하면 콜백 안에 또 콜백이 있고, 그 안에 또 콜백이 있는 <strong>콜백 헬(콜백 지옥, callback hell)</strong> 현상을 초래하게 된다는 것이다.</p>
<p>해결책) Promisify</p>
<pre><code class="language-jsx">function readFile_promisified(filename) {
  const p = new Promise((resolve, reject) =&gt; {
    fs.readFile(filename, &#39;utf8&#39;, (error, data) =&gt; {
      if (error) {
        reject(error); // 에러 발생 시 -&gt; rejected
      } else {
        resolve(data); // 파일 내용 읽기 완료 -&gt; fulfilled
      }
    });
  });
  return p;
}</code></pre>
<ul>
<li>readFile_promisified라는 이름의 함수를 정의하고, 함수 안에서는 Promise 객체를 직접 생성하고 있다.</li>
<li>Promise 객체가 생성될 때 실행되는 <strong>executor</strong> 함수 안에서는 fs 객체의 readFile 메소드를 호출했다.</li>
<li>여기서 중요한 것은 작업을 수행하다가 에러가 나면 <strong>readFile 함수의 콜백에서 reject 함수를 호출</strong>하고, 파일의 내용을 정상적으로 다 읽었을 때는 <strong>resolve 함수를 호출</strong>한다는 사실.</li>
</ul>
<pre><code class="language-jsx">
...                         (error, data) =&gt; {
  if (error) {
    reject(error); // 에러 발생 시 -&gt; rejected
  } else {
    resolve(data); // 파일 내용 읽기 완료 -&gt; fulfilled
  }
}</code></pre>
<ul>
<li>reject 함수의 파라미터에는 error 객체를, resolve 함수의 파라미터에는 파일의 내용인 data를 전달했다.</li>
</ul>
<pre><code class="language-jsx">
readFile_promisified(&#39;file1.txt&#39;)
  .then((data) =&gt; { console.log(data); return readFile_promisified(&#39;file2.txt&#39;); })
  .then((data) =&gt; { console.log(data); return readFile_promisified(&#39;file3.txt&#39;); })
  .then((data) =&gt; { console.log(data); })
  .catch((error) =&gt; { console.log(error); });
</code></pre>
<p>readFile_promisified 함수는 Promise 객체를 리턴하기 때문에 이렇게 자유롭게 Promise Chain 안에서 사용할 수 있다.</p>
<h2 id="3-promisify를-하면-안-되는-함수들도-있다"><strong>3. Promisify를 하면 안 되는 함수들도 있다.</strong></h2>
<p>기존의 전통적인 형식의 비동기 실행 함수도 원하는 경우에는 Promisify해서 콜백 헬을 방지할 수 있다. 하지만 전통적인 형식의 비동기 실행 함수라고 해서 모두 Promisify해서 사용해도 되는 것은 아니다.</p>
<p>기존의 비동기 실행 함수들 중에서도 <strong>그 콜백을 한번만 실행</strong>하는 것들(setTimeout, readFile 등)만 Promisify해서 사용해도 된다.</p>
<p>이것들과 달리 만약 콜백을 여러 번 실행하는 함수들(setInterval, addEventListener 등)인 경우에는 이렇게 Promisify하면 안 된다. </p>
<p>→ <strong>Promise 객체는 한번 pending 상태에서 fulfilled 또는 rejected 상태가 되고나면 그 뒤로는 그 상태와 결과가 바뀌지 않기 때문</strong></p>
<pre><code class="language-jsx">const box = document.getElementById(&#39;test&#39;);
let count = 0;

function addEventListener_promisified(obj, eventName) { // 이런 Promisify는 하지 마세요
  const p = new Promise((resolve, reject) =&gt; {
    obj.addEventListener(eventName, () =&gt; { // addEventListener 메소드
      count += 1;
      resolve(count);
    });
  });
  return p;
}

addEventListener_promisified(box, &#39;click&#39;)
.then((eventCount) =&gt; { console.log(eventCount); });</code></pre>
<p>addEventListener_promisified 함수는 DOM 객체의 addEventListener 메소드를 Promisify한 함수다.</p>
<p>지금 Promise 객체가 생성될 때 실행되는 executor 함수 안에서는, DOM 객체에 어떤 이벤트가 발생할 때, 실행할 콜백을 등록하고 있다. 
특정 이벤트가 발생할 때마다 count라고 하는 변수의 값을 1씩 늘려서 resolve 함수의 파라미터로 전달해서 실행하도록 하는 내용이 들어있다.</p>
<p>마지막 코드를 보면,</p>
<p>addEventListener_promisified 함수의 아규먼트로 DOM 객체 box와 문자열 &#39;click&#39;을 넣어서 box 객체가 클릭 이벤트에 반응하도록 했다. </p>
<p>하지만 이 코드를 실행하고 box를 클릭해보면 
<strong>처음에 1이 딱 출력되고 나서 그 다음 count 값들은 출력되지 않는다.</strong></p>
<p>왜냐하면 pending 상태에 있던 Promise 객체(여기서는 p 객체)가 한번 fulfilled 상태 또는 rejected 상태가 되고 나면 Promise 객체의 상태 및 결과가 고정되어 그 뒤로는 바뀌지 않기 때문.</p>
<p>따라서 지금 위 코드에 보이는 resolve(count)라고 하는 코드가 box 버튼을 클릭할 때마다 여러 번 실행된다고 해도 p 객체가 갖고 있는 상태와 결과는 변하지 않는다. 그래서 then 메소드 안의 콜백도 처음 클릭했을 때 딱 한번 실행되고 끝이다.</p>
<p>이렇게 콜백이 여러 번 실행되어야하는 비동기 실행 함수인 경우에는 Promisify를 하면 안 된다. </p>
<p><strong>처음부터 바로 fulfilled 상태이거나 rejected 상태인 Promise 객체를 만드는 것도 가능하다.👇🏻👇🏻</strong></p>
<h1 id="✨이미-상태가-결정된-promise-객체-만들기"><strong>✨이미 상태가 결정된 Promise 객체 만들기</strong></h1>
<h2 id="1-fulfilled-상태의-promise-객체-만들기"><strong>(1) fulfilled 상태의 Promise 객체 만들기</strong></h2>
<pre><code class="language-jsx">const p = Promise.resolve(&#39;success&#39;);</code></pre>
<p>Promise의 resolve라는 메소드를 사용하면 바로 fulfilled 상태의 Promise 객체를 만들 수 있다. 위와 같이 쓰면 fulfilled 상태이면서, 작업 성공 결과로 <strong>문자열 &#39;success&#39;</strong>를 가진 Promise 객체를 만들 수 있다.</p>
<h2 id="2-rejected-상태의-promise-객체-만들기"><strong>(2) rejected 상태의 Promise 객체 만들기</strong></h2>
<pre><code class="language-jsx">const p = Promise.reject(new Error(&#39;fail&#39;));</code></pre>
<p>Promise의 reject라는 메소드를 사용하면 바로 rejected 상태의 Promise 객체를 만들 수 있다. 위와 같이 쓰면 rejected 상태이면서, 작업 실패 정보로, <strong>fail이라는 메시지를 가진 Error 객체</strong>를 가진 Promise 객체를 만들 수 있다.</p>
<p>Promise 객체를 직접 생성하는 방법)</p>
<pre><code class="language-jsx">const p = new Promise((resolve, reject) =&gt; {

});</code></pre>
<p><strong>new 생성자와 executor 함수</strong>를 사용하는 것 말고도 <strong>resolve 메소드</strong>나, <strong>reject 메소드</strong>를 사용하는 방법도 있다는 사실!! 
resolve 메소드나 reject 메소드로 생성한 Promise 객체도 이때까지 우리가 배운 것과 동일하게 작동한다.</p>
<pre><code class="language-jsx">const p = Promise.resolve(&#39;success&#39;);
p.then((result) =&gt; { console.log(result); }, (error) =&gt; { console.log(error); });</code></pre>
<p>이 코드에서는 첫 번째 콜백이 실행되어서 작업 성공 결과인 문자열 success가 출력되고</p>
<pre><code class="language-jsx">const p = Promise.reject(new Error(&#39;fail&#39;));
p.then((result) =&gt; { console.log(result); }, (error) =&gt; { console.log(error); });</code></pre>
<p>이 코드에서는 두 번째 콜백이 실행되어서 작업 실패 정보인 Error 객체의 내용이 출력된다.</p>
<p>어떤 비동기 작업을 처리할 필요가 있다면, new 생성자와 executor 함수를 사용해서 Promise 객체를 만들어야 하지만, 그렇지 않고 바로 상태가 이미 결정된 Promise 객체를 만들고 싶을 때는 이 resolve 또는 reject 메소드를 사용한다.</p>
<p>구체적으로 예를 들자면, 함수 안에서 리턴하는 값이 여러 개인 경우 모든 리턴값을 Promise 객체로 통일하고 싶을 때, 종종 resolve 또는 reject 메소드를 쓴다.</p>
<pre><code class="language-jsx">function doSomething(a, b) {
    //~~
  if (problem) {
    throw new Error(&#39;Failed due to..&#39;));
  } else {
    return fetch(&#39;https://~&#39;);
  }
}</code></pre>
<p>문제(problem이 falsy인 경우)가 없는 경우에만 fetch 함수를 호출해서 Promise 객체를 리턴하는 함수가 있다고 해보자.</p>
<p>만약 문제가 발생하는 경우에는 바로 Error 객체를 throw하고 있다. 만약 문제가 존재하는 경우에도 Promise 객체를 리턴하고 싶다면 reject 메소드를 써서 작성할 수 있다.</p>
<pre><code class="language-jsx">function doSomething(a, b) {
  // ~~
  if (problem) {
    return Promise.reject(new Error(&#39;Failed due to..&#39;));
  } else {
    return fetch(&#39;https://~&#39;);
  }
}</code></pre>
<p>지금 문제가 있는 경우에도 에러를 바로 throw하는 게 아니라, 생성한 에러를 Promise 객체의 작업 실패 정보로 설정해서, 그 Promise 객체를 리턴하는 것으로 바꿨다. 만약 어떤 함수가 어떤 상황이든 항상 Promise 객체를 리턴하는 것으로 통일하고 싶은 경우에는 resolve나 reject 메소드를 유용하게 사용할 수 있다.</p>
<h1 id="2-promise-객체의-작업-성공-결과-또는-작업-실패-정보"><strong>2. Promise 객체의 작업 성공 결과 또는 작업 실패 정보</strong></h1>
<p> fulfilled 또는 rejected 상태가 결정된 Promise 객체라도 then 메소드를 붙이면, 콜백에서 해당 작업 성공 결과 또는 작업 실패 정보를 받아올 수 있다.</p>
<p>Promise 객체의 상태가 fulfilled 또는 rejected 상태이기만 하면, <strong>어느 시점이든, 몇 번이든</strong> then 메소드를 붙여서 해당 결과를 가져올 수 있다.</p>
<pre><code class="language-jsx">const p = new Promise((resolve, reject) =&gt; {
    // 2초 후에 fulfilled 상태가 됨
  setTimeout(() =&gt; { resolve(&#39;success&#39;); }, 2000); 
});

// Promise 객체가 pending 상태일 때 콜백 등록
p.then((result) =&gt; { console.log(result); }); 

// Promise 객체가 fulfilled 상태가 되고 나서 콜백 등록
setTimeout(() =&gt; { p.then((result) =&gt; { console.log(result); }); }, 5000); 
</code></pre>
<p>Promise가 pending 상태일 때 등록한 콜백이든, fulfilled 상태가 된 후에 등록한 콜백이든 잘 실행되는 것을 알 수 있다.
이렇게 어느 시점이든, 몇 번의 then 메소드를 붙이든 상관없이, pending 상태만 아니라면 항상 then 메소드로 Promise 객체의 결과를 추출할 수 있다.</p>
<p>Promise 객체는 항상 결과를 줄 수 있는 공급자(Provider)이고 그것의 then 메소드는 그 결과를 소비하는 콜백인 소비자(Consumer)를 설정하는 메소드라는 사실!!</p>
<h1 id="✨여러-promise-객체-다루기">✨여러 Promise 객체 다루기</h1>
<h2 id="1-all-메소드"><strong>1. all 메소드</strong></h2>
<pre><code class="language-jsx">// 1번 직원 정보
const p1 = fetch(&#39;https://learn.codeit.kr/api/members/1&#39;).then((res) =&gt; res.json());
// 2번 직원 정보
const p2 = fetch(&#39;https://learn.codeit.kr/api/members/2&#39;).then((res) =&gt; res.json());
// 3번 직원 정보
const p3 = fetch(&#39;https://learn.codeit.kr/api/members/3&#39;).then((res) =&gt; res.json());

Promise
  .all([p1, p2, p3])
  .then((results) =&gt; {
    console.log(results); // Array : [1번 직원 정보, 2번 직원 정보, 3번 직원 정보]
  });
</code></pre>
<ul>
<li>서로 다른 3개의 URL로 리퀘스트를 보내는 fetch 함수들이다. 1번, 2번, 3번 직원의 정보를 각각 요청하고 있다.</li>
<li>Promise의 <strong>all</strong>이라는 메소드를 호출하고 있고, all 메소드의 아규먼트로는 <strong>배열 하나</strong>가 들어있다. 그 배열의 요소들은, 각 직원 정보를 요청하고 받아서 Deserialize까지 수행한 작업 성공 결과를 담고 있는 Promise 객체들인 p1, p2, p3 객체다.</li>
<li><strong>all 메소드도 then 메소드처럼 새로운 Promise 객체를 리턴</strong>한다.</li>
<li>all 메소드는 이렇게 아규먼트로 들어온 <strong>배열 안에 있는 모든 Promise 객체가 pending 상태에서 fulfilled 상태가 될 때까지 기다린다.</strong></li>
<li>모든 Promise 객체들이 fulfilled 상태가 되면, all 메소드가 리턴했던 Promise 객체는 fulfilled 상태가 되고, 각 Promise 객체의 작업 성공 결과들로 이루어진 배열을, 그 작업 성공 결과로 갖게 된다.</li>
</ul>
<p>이렇게 all 메소드가 리턴한 Promise 객체는,</p>
<p>(1) 각 개별 Promise 객체의 작업 성공 결과로 이루어진 배열을 
(2) 자신의 작업 성공 결과로 갖는다는 것을 알 수 있다.</p>
<p>⇒ all 메소드는 <strong>여러 Promise 객체의 작업 성공 결과를 기다렸다가 모두 한 번에 취합</strong>하기 위해서 사용한다.</p>
<p>그런데 만약 p1~3 객체들 중 하나라도, rejected 상태가 되면?</p>
<pre><code class="language-jsx">// 1번 직원 정보
const p1 = fetch(&#39;https://learn.codeit.kr/api/members/1&#39;).then((res) =&gt; res.json());
// 2번 직원 정보
const p2 = fetch(&#39;https://learn.codeit.kr/api/members/2&#39;).then((res) =&gt; res.json());
// 3번 직원 정보
const p3 = fetch(&#39;https://learnnnnnn.codeit.kr/api/members/3&#39;).then((res) =&gt; res.json());

Promise
  .all([p1, p2, p3])
  .then((results) =&gt; {
    console.log(results); // Array : [1번 직원 정보, 2번 직원 정보, 3번 직원 정보]
  });</code></pre>
<ul>
<li>fetch 함수에서 문제가 발생해서 p3가 rejected 상태가 되면, all 메소드가 리턴한 Promise 객체는 p3 객체처럼 rejected 상태가 되고 동일한 작업 실패 정보를 갖게 된다.</li>
<li>all 메소드는 하나의 Promise 객체라도 rejected 상태가 되면, 전체 작업이 실패한 것으로 간주해야 할 때 사용한다.</li>
</ul>
<p>그리고 이렇게 Promise 객체가 하나라도 rejected 상태가 되는 경우에 대비하려면 </p>
<pre><code class="language-jsx">// 1번 직원 정보
const p1 = fetch(&#39;https://learn.codeit.kr/api/members/1&#39;).then((res) =&gt; res.json());
// 2번 직원 정보
const p2 = fetch(&#39;https://learn.codeit.kr/api/members/2&#39;).then((res) =&gt; res.json());
// 3번 직원 정보
const p3 = fetch(&#39;https://learnnnnnn.codeit.kr/api/members/3&#39;).then((res) =&gt; res.json());

Promise
  .all([p1, p2, p3])
  .then((results) =&gt; {
    console.log(results); // Array : [1번 직원 정보, 2번 직원 정보, 3번 직원 정보]
  })
  .catch((error) =&gt; {
    console.log(error);
  });</code></pre>
<p>그냥 이렇게 catch 메소드를 붙여주면 된다.</p>
<h2 id="2-race-메소드"><strong>2. race 메소드</strong></h2>
<ul>
<li>race 메소드도 all 메소드와 마찬가지로 여러 Promise 객체들이 있는 배열을 아규먼트로 받는다. race 메소드도 all 메소드처럼 Promise 객체를 리턴한다.</li>
<li>작동방식의 차이점이 있음) race 메소드가 리턴한 Promise 객체는 아규먼트로 들어온 배열의 여러 Promise 객체들 중에서 <strong>가장 먼저 fulfilled 상태 또는 rejected 상태가 된 Promise 객체와 동일한 상태와 결과를 갖게 된다.</strong></li>
</ul>
<pre><code class="language-jsx">const p1 = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; resolve(&#39;Success&#39;), 1000);
});
const p2 = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; reject(new Error(&#39;fail&#39;)), 2000);
});
const p3 = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; reject(new Error(&#39;fail2&#39;)), 4000);
});

Promise
  .race([p1, p2, p3])
  .then((result) =&gt; {
    console.log(result); // hello 출력
  })
  .catch((value) =&gt; {
    console.log(value);
  });</code></pre>
<p>지금 race 메소드 안의 배열에 들어있는 Promise 객체들 중에서 무엇이 가장 빨리 fulfileld 또는 rejected 상태가 될까? 
→ 1초 후에 fulfilled 상태가 되는 p1 객체</p>
<p>p1 객체는 1초 후에 fulfilled 상태가 되고, 그 작업 성공 결과로 문자열 Success를 가지게 된다. 
p2는 2초 후에, p3는 4초 후에 rejected 상태가 된다.</p>
<p>⇒ <em>race 메소드가 리턴한 Promise 객체는 이 중에서 가장 빨리 상태 정보가 결정된 p1 객체와 동일한 상태와 결과를 가진다.</em> 
⇒ 말그대로 race 메소드는 여러 Promise 객체들을 레이스(race, 경쟁)시켜서 가장 빨리 상태가 결정된 Promise 객체를 선택하는 메소드 </p>
<h2 id="그-외-메소드">그 외 메소드</h2>
<p><strong>각 메소드가 리턴한 Promise 객체가 A라고 할 때</strong>,</p>
<p><strong>allSettled 메소드</strong> : 배열 내의 모든 Promise 객체가 fulfilled 또는 rejected 상태가 되기까지 기다리고, pending 상태의 Promise 객체가 하나도 없게 되면, A의 상태값은 fulfilled 상태가 되고 그 작업 성공 결과로, 하나의 배열을 갖게 된다.</p>
<p>이 배열에는 아규먼트로 받았던 배열 내의 각 promise 객체의</p>
<p>(1) 최종 상태를 status 프로퍼티, 
(2) 그 작업 성공 결과는 value 프로퍼티, 
(3) 그 작업 실패 정보는 reason 프로퍼티</p>
<p>에 담은 객체들이 요소로 존재한다. </p>
<pre><code class="language-jsx">[
   {status: &quot;fulfilled&quot;, value: 1},
   {status: &quot;fulfilled&quot;, value: 2},
   {status: &quot;fulfilled&quot;, value: 3},
   {status: &quot;rejected&quot;,  reason: Error: an error}
]</code></pre>
<p>참고로 <strong>fulfilled 상태와 rejected 상태를 묶어서 settled 상태라고 한다.</strong> </p>
<p>allSettled 메소드는 말 그대로 배열 속 Promise 객체들이 settled 상태가 되기만 하면 되는 것. 이에 반해 위에서 배운 all 메소드는 모든 Promise 객체들이 fulfilled 상태가 되기를 기다리는 것.</p>
<p><strong>any 메소드</strong> : 여러 Promise 객체들 중에서 가장 먼저 fulfilled 상태가 된 Promise 객체의 상태와 결과가 A에도 똑같이 반영된다. 만약 모든 Promise 객체가 rejected 상태가 되어버리면 AggregateError라고 하는 에러를 작업 실패 정보로 갖고 rejected 상태가 된다. <em>any라는 단어의 뜻처럼 배열 속의 Promise 객체 중 단 하나라도 fulfilled 상태가 되면 되는 것</em>.</p>
<h1 id="✨axios">✨axios</h1>
<p><strong>fetch 함수</strong>가 Ajax 통신을 하는 함수다. 개발 실무에서는 이 fetch 함수 말고도 Ajax 통신을 할 수 있는 방법이 존재한다. ⇒  <strong>axios</strong> 라고 하는 외부 패키지를 사용하는 것. </p>
<pre><code class="language-jsx">axios
  .get(&#39;https://jsonplaceholder.typicode.com/users&#39;)
  .then((response) =&gt; {
    console.log(response);
  })
  .catch((error) =&gt; {
    console.log(error);
  });</code></pre>
<ul>
<li>axios 패키지에서 제공하는 axios 객체를 사용해서 GET 리퀘스트를 보내고 그 리스폰스를 받는 코드다.</li>
<li>자세히 보면 지금 코드에서 axios.get이라고 쓰여 있는 부분만 fetch로 바꾸면 fetch 함수와 사용법이 비슷하다</li>
<li>사실 <strong>axios 객체에서 리퀘스트를 보내는 많은 메소드들이 fetch 함수처럼 Promise 객체를 리턴한다.</strong></li>
<li>axios 객체에는 fetch 함수에는 없는 다음과 같은 몇 가지 기능 및 장점이 있다.)<ul>
<li>모든 리퀘스트, 리스폰스에 대한 공통 설정 및 공통된 전처리 함수 삽입 가능</li>
<li>serialization, deserialization을 자동으로 수행</li>
<li>특정 리퀘스트에 대해 얼마나 오랫동안 리스폰스가 오지 않으면 리퀘스트를 취소할지 설정 가능(request timeout)</li>
<li>업로드 시 진행 상태 정보를 얻을 수 있음</li>
<li>리퀘스트 취소 기능 지원</li>
</ul>
</li>
</ul>
<p><a href="https://github.com/axios/axios">https://github.com/axios/axios</a></p>
<h1 id="p1_s3-자바스크립트-웹-개발-기본기">[P1_S3] 자바스크립트 웹 개발 기본기</h1>
<p>(4) async/await을 활용한 세련된 비동기 코드</p>
<h1 id="4-asyncawait을-활용한-세련된-비동기-코드">(4) async/await을 활용한 세련된 비동기 코드</h1>
<pre><code class="language-jsx">/* fetch(&#39;https://www.google.com&#39;)
    .then((response) =&gt; response.text())
    .then((result) =&gt; { console.log(result); }); */

async function fetchAndPrint() {
  console.log(2);
  const response = await fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;);
  console.log(7);
  const result = await response.text();
  console.log(result);
}

console.log(1);
fetchAndPrint();
console.log(3);
console.log(4);
console.log(5);
console.log(6);</code></pre>
<ul>
<li>async/await 구문은 기존의 Promise 객체를 사용하는 코드(Promise Chaining)를</li>
</ul>
<p>(1) 개발자가 더 편하게 작성할 수 있도록 하고
(2) 코드의 가독성을 높이기 위해서</p>
<p>도입된 일종의 <strong>Syntactic sugar</strong>(기존 문법을 더 편하게 사용할 수 있도록 하는 문법적 장치)에 해당한다.</p>
<p>원래는)</p>
<ul>
<li>전통적인 형식의 비동기 실행 함수에 콜백을 바로 전달하거나,</li>
<li>Promise 객체 뒤에 <strong>.then</strong> 메소드를 붙이는 것보다는</li>
</ul>
<p>그냥 코드를 차례대로 써나가는 것이 더 익숙한 방식이다. 
→ <strong>async/await 구문이 Promise 객체를 우리에게 이미 익숙한 동기 실행 코드 방식으로 다룰 수 있게 해주는 문법이다</strong>.</p>
<pre><code class="language-jsx">// 위 코드 출력 결과
1
2
3
4
5
6
7
[리스폰스의 내용]
</code></pre>
<p>원래코드)</p>
<pre><code class="language-jsx">
/* fetch(&#39;https://www.google.com&#39;)
    .then((response) =&gt; response.text())
    .then((result) =&gt; { console.log(result); }); */

function fetchAndPrint() {
  console.log(2);
  fetch(&#39;https://jsonplaceholder.typicode.com/users&#39;)
    .then((response) =&gt; {
      console.log(7);
      return response.text();
    })
    .then((result) =&gt; { console.log(result); });
}

console.log(1);
fetchAndPrint();
console.log(3);
console.log(4);
console.log(5);
console.log(6);
</code></pre>
<p>async/await 구문을 사용하면,</p>
<p>(1) Promise 객체를 사용할 때, then 메소드 등을 사용하지 않고도 
(2) 마치 동기 실행 코드처럼 코드를 훨씬 더 간단하고 편하게 작성할 수 있다.</p>
<ul>
<li>async 함수 안의 코드가 실행되다가 await을 만나면, 일단 await 뒤의 코드가 실행되고, 코드의 실행 흐름이 async 함수 바깥으로 나가서 나머지 코드를 다 실행한다.</li>
<li>그 이후로는, await 뒤에 있던 Promise 객체가 fulfilled 상태가 되기를 기다린다. 그리고 기다리던 Promise 객체가 fulfilled 상태가 되면 await이 Promise 객체의 작업 성공 결과를 리턴한다.</li>
</ul>
<h1 id="✨async-함수-안에서-리턴하는-값의-종류에-따라-어떤-promise-객체를-리턴하게-될까">✨async 함수 안에서 리턴하는 값의 종류에 따라 어떤 Promise 객체를 리턴하게 될까.</h1>
<h2 id="1-어떤-값을-리턴하는-경우"><strong>1. 어떤 값을 리턴하는 경우</strong></h2>
<h3 id="1-promise-객체를-리턴하는-경우-1"><strong>(1) Promise 객체를 리턴하는 경우</strong></h3>
<p>async 함수 안에서 Promise 객체를 리턴하는 경우에는 <strong>해당 Promise 객체와 동일한 상태와 작업 성공 결과(또는 작업 실패 정보)를 가진 Promise 객체</strong>를 리턴한다.</p>
<pre><code class="language-jsx">async function fetchAndPrint() {
  return new Promise((resolve, reject)=&gt; {
    setTimeout(() =&gt; { resolve(&#39;abc&#39;); }, 4000);
  });
}

fetchAndPrint();</code></pre>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4110&directory=Untitled.png&name=Untitled.png" alt=""></p>
<p>이렇게 pending 상태의 Promise 객체를 리턴하기도 하고</p>
<pre><code class="language-jsx">
async function fetchAndPrint() {
  return Promise.resolve(&#39;Success&#39;);
}

fetchAndPrint();</code></pre>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4110&directory=Untitled%201.png&name=Untitled+1.png" alt=""></p>
<p>이미 fulfilled 상태인 Promise 객체나</p>
<pre><code class="language-jsx">
async function fetchAndPrint() {
  return Promise.reject(new Error(&#39;Fail&#39;));
}

fetchAndPrint();</code></pre>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4110&directory=Untitled%202.png&name=Untitled+2.png" alt=""></p>
<p>이미 rejected 상태인 Promise 객체를 리턴하는 경우 전부 다 해당한다. </p>
<h3 id="2-promise-객체-이외의-값을-리턴하는-경우-1"><strong>(2) Promise 객체 이외의 값을 리턴하는 경우</strong></h3>
<p>async 함수 내부에서 Promise 객체 이외에 숫자나 문자열, 일반 객체 등을 리턴하는 경우에는, <strong>fulfilled 상태이면서, 리턴된 값을 작업 성공 결과로 가진 Promise 객체</strong>를 리턴한다.</p>
<pre><code class="language-jsx">async function fetchAndPrint() {
  return 3;
}

fetchAndPrint();</code></pre>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4110&directory=Untitled%203.png&name=Untitled+3.png" alt=""></p>
<pre><code class="language-jsx">
async function fetchAndPrint() {
  return &#39;Hello&#39;;
}

fetchAndPrint();
</code></pre>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4110&directory=Untitled%204.png&name=Untitled+4.png" alt=""></p>
<pre><code class="language-jsx">
async function fetchAndPrint() {
  const member = {
    name: &#39;Jerry&#39;,
    email: &#39;jerry@codeitmall.kr&#39;,
    department: &#39;sales&#39;,
  };

  return member;
}

fetchAndPrint();
</code></pre>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4110&directory=Untitled%205.png&name=Untitled+5.png" alt=""></p>
<h1 id="2-아무-값도-리턴하지-않는-경우"><strong>2. 아무 값도 리턴하지 않는 경우</strong></h1>
<pre><code class="language-jsx">
async function fetchAndPrint() {
  console.log(&#39;Hello Programming!&#39;);
}

fetchAndPrint();
</code></pre>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4110&directory=Untitled%206.png&name=Untitled+6.png" alt=""></p>
<p>함수에서 아무런 값도 리턴하지 않으면 자바스크립트에서 undefined를 리턴한 것으로 간주한다</p>
<p>이 경우에는 <strong>fulfilled 상태이면서, undefined를 작업 성공 결과로 가진 Promise 객체</strong>가 리턴</p>
<h2 id="3-async-함수-내부에서-에러가-발생했을-때"><strong>3. async 함수 내부에서 에러가 발생했을 때</strong></h2>
<pre><code class="language-jsx">
async function fetchAndPrint() {
  throw new Error(&#39;Fail&#39;);
}

fetchAndPrint();
</code></pre>
<p><img src="https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4110&directory=Untitled%207.png&name=Untitled+7.png" alt=""></p>
<p>async 함수 안에서 에러가 발생하면, <strong>rejected 상태이면서, 해당 에러 객체를 작업 실패 정보로 가진 Promise 객체</strong>가 리턴된다.</p>
<h1 id="✨async를-붙이는-위치">✨async를 붙이는 위치</h1>
<p>자바스크립트에서 함수를 표현하는 방법에는</p>
<ol>
<li><strong>Function Declaration(함수 선언식)</strong></li>
<li><strong>Function Expression(함수 표현식)</strong><ol>
<li>함수에 이름이 붙어있는 Named Function Expression </li>
<li>함수에 이름이 없는 Anonymous Function Expression</li>
</ol>
</li>
<li><strong>Arrow Function(화살표 함수)</strong></li>
</ol>
<p>👇🏻async 붙이는 위치👇🏻</p>
<pre><code class="language-jsx">// 1) Function Declaration
async function example1(a, b) {
  return a + b;
}

// 2-a) Function Expression(Named)
const example2_1= async function add(a, b) {
  return a + b;
};

// 2-b) Function Expression(Anonymous)
const example2_2 = async function(a, b) {
  return a + b;
};

// 3) Arrow Function
const example3_1 = async (a, b) =&gt; {
  return a + b;
};

// 3) Arrow Function(shortened)
const example3_2 = async (a, b) =&gt; a + b;</code></pre>
<p>👇🏻즉시실행함수(Immediately-invoked function expression, IIFE)👇🏻</p>
<pre><code class="language-jsx">(async function print(sentence) {
  console.log(sentence);
  return sentence;
}(&#39;I love JavaScript!&#39;));

(async function (a, b) {
  return a + b;
}(1, 2));

(async (a, b) =&gt; {
  return a + b;
})(1, 2);

(async (a, b) =&gt; a + b)(1, 2);</code></pre>
<h1 id="✨async-함수를-작성할-때-주의해야할-성능-문제">✨<strong>async 함수를 작성할 때 주의해야할 성능 문제</strong></h1>
<pre><code class="language-jsx">async function getResponses(urls) {
  for(const url of urls){
    const response = await fetch(url);
    console.log(await response.text());
  }
}</code></pre>
<ul>
<li><code>getResponses</code> 함수는 urls라는 파라미터로, 여러 개의 URL들이 있는 배열을 받아서, 순서대로 각 URL에 리퀘스트를 보내고, 그 리스폰스의 내용을 출력하는 함수다.</li>
<li>문제점 ⇒ 이전 URL에 리퀘스트를 보내고 리스폰스를 받아서 출력하고 나서야, 다음 URL에 대한 리퀘스트를 보낼 수 있다는 점이다. 즉, <strong>순차적인 작업 처리를 한다는 점이다.</strong> 왜냐하면 이전 URL에 대해서 await 문이 붙은 Promise 객체가 fulfilled 상태가 될 때까지는 그 다음 URL에 대한 작업들이 시작될 수 없기 때문이다.</li>
</ul>
<p>만약 리스폰스의 내용의 순서가 중요하지 않은 경우라면?</p>
<pre><code class="language-jsx">async function fetchUrls(urls){
  for(const url of urls){
    (async () =&gt; { // 추가된 부분!
      const response = await fetch(url);
      console.log(await response.text());
    })(); // 추가된 부분
  }
}</code></pre>
<ul>
<li>각 url에 리퀘스트를 보내고 리스폰스를 받는 코드를, <strong>별도의 즉시실행되는 async 함수</strong>로 감쌌다.</li>
<li>이렇게 코드를 고치면 일단 각 URL에 대해서 fetch 함수를 실행해서 리퀘스트를 보내는 것을 순서대로 바로 실행한다. 이렇게 코드를 수정하면, 일단 모든 URL에 대한 리퀘스트를 쭉 보내버리고, 먼저 리스폰스가 오는 순서대로 그 내용이 출력된다.</li>
</ul>
<hr>
<h1 id="✨비동기-실행-정리">✨비동기 실행 정리</h1>
<h2 id="📘비동기-실행의-정의">📘비동기 실행의 정의</h2>
<ul>
<li>특정 작업이 시작되고, 그 작업이 모두 완료되기 전에 바로 다음 코드가 실행되는 방식의 실행, 나머지 작업은 나중에 콜백을 통해 수행되는 방식의 실행</li>
<li>특정 처리를 나중으로 미루는 방식의 실행</li>
<li>콜백을 등록해두고, 추후에 특정 조건이 만족되면 그 콜백으로 나머지 작업을 수행하는 방식의 실행</li>
</ul>
<p>⇒ <strong>특정 처리를 담당하는 존재(콜백)의 실행을 나중으로 미룬다!</strong></p>
<h2 id="📘비동기-실행-관련-문법-3가지">📘비동기 실행 관련 문법 3가지</h2>
<p>(1) 파라미터로 바로 콜백을 전달하는 형태의 전통적인 비동기 실행 함수
(2) Promise
(3) async/await</p>
<h3 id="1-파라미터로-바로-콜백을-전달하는-형태의-전통적인-비동기-실행-함수"><strong>1. 파라미터로 바로 콜백을 전달하는 형태의 전통적인 비동기 실행 함수</strong></h3>
<p><strong>setTimeout, setInterval 함수, DOM 객체의 addEventListener 메소드 등</strong>이 여기에 해당한다.</p>
<pre><code class="language-jsx">setTimeout(() =&gt; {
  console.log(&#39;asynchronously executed&#39;);
}, 2000);

button.addEventListener(&#39;click&#39;, (event) =&gt; { console.log(&#39;You Clicked&#39;); });</code></pre>
<p>함수의 파라미터로 콜백을 바로 전달하는 방식은 여전히 많은 경우에 쓰이고 있지만, 
<strong>여러 비동기 작업의 순차적인(sequential) 처리가 필요한 경우</strong>에 이런 함수들로 코드를 작성하면,</p>
<pre><code class="language-jsx">fs.readFile(&#39;file1.txt&#39;, &#39;utf8&#39;, (error1, data1) =&gt; {
  if (error1) {
    console.log(error1);
  } else {
    console.log(data1);
    fs.readFile(&#39;file2.txt&#39;, &#39;utf8&#39;, (error2, data2) =&gt; {
      if (error2) {
        console.log(error2);
      } else {
        console.log(data2);
        fs.readFile(&#39;file3.txt&#39;, &#39;utf8&#39;, (error3, data3) =&gt; {
          if (error3) {
            console.log(error3);
          } else {
            console.log(data3);
          }
        });
      }
    });
  }
});</code></pre>
<p>⇒ 코드의 가독성이 급격하게 떨어지는 <strong>콜백 헬(callback hell) 문제</strong>가 발생할 가능성이 높다.</p>
<h3 id="2-promise"><strong>2. Promise</strong></h3>
<pre><code class="language-jsx">
fetch(&#39;https://www.google.com&#39;)
  .then((response) =&gt; response.text())
  .then((result) =&gt; { console.log(result); })
  .catch((error) =&gt; { console.log(error); })
  .finally(() =&gt; { console.log(&#39;exit&#39;); });</code></pre>
<p><strong>Promise 객체를 사용하면 콜백 헬 문제를 방지하면서, 여러 비동기 작업을 순차적으로 처리할 수 있다</strong>.</p>
<p>기존의 1.과 같은 전통적인 비동기 실행 함수들 중에서도 그 콜백이 단 한 번만 실행되는 함수들은</p>
<pre><code class="language-jsx">function readFile_promisified(filename) {
  const p = new Promise((resolve, reject) =&gt; {
    fs.readFile(filename, &#39;utf8&#39;, (error, data) =&gt; {
      if (error) {
        reject(error); // 에러 발생 시 -&gt; rejected
      } else {
        resolve(data); // 파일 내용 읽기 완료 -&gt; fulfilled
      }
    });
  });
  return p;
}</code></pre>
<p>이런 식으로 <strong>Promisify</strong>해서 콜백 헬의 가능성을 없애고, Promise Chain 안에서 그 콜백의 리턴값을 사용할 수 있다.</p>
<p>rejected 상태의 Promise 객체에 대비하기 위한 catch 메소드, 어느 상황이든 항상 마지막에 실행해야 할 코드가 있을 때 사용하는 finally 메소드도 있다.</p>
<h3 id="3-async--await-구문"><strong>3. async / await 구문</strong></h3>
<pre><code class="language-jsx">async function fetchAndPrint() {
  try {
    const response = await fetch(&#39;https://www.google.www&#39;);
    const result = await response.text();
    console.log(result);
  } catch(error) {
    console.log(error);
  } finally {
    console.log(&#39;exit&#39;);
  }
}

fetchAndPrint();</code></pre>
<p>async/await 구문은 <strong>Promise 객체를 다루는 코드(Promise Chaining 코드 등)를 사람들이 좀더 익숙하게 느끼는 동기 실행 스타일의 코드로 작성할 수 있게 해주는 Syntactic sugar</strong>라고 했다. </p>
<p>async 함수 안의 내용이 순차적으로 실행되다가도, <strong>await 문을 만나면 await 바로 뒤에 붙은 코드를 실행해두고, 일단은 함수 바깥으로 코드 흐름이 바뀐다!!</strong></p>
<p>⇒ <strong>Promise 기반의 코드들은 가능한 경우에 모두 async/await 구문으로 전환해서 작성하는 게 더 좋다.</strong></p>
<hr>
<p>2021년 1월을 기준으로 아직 위의 3가지 비동기 실행 관련 문법들은 서로 상호보완적인 것들이라고 할 수 있다. 왜냐하면 아직 아래와 같이 하나가 다른 하나를 완벽히 대체하지 못하는 측면이 있기 때문이다.</p>
<ol>
<li>콜백을 함수의 파라미터로 바로 전달하는 전통적인 방식의 비동기 실행 함수들 중에서도 setInterval, addEventListener처럼 그 콜백이 단 한번이 아니라 여러 번 실행되어야 하는 것들은 Promisify해서 사용하면 안 된다. Promise 객체는 한번 fulfilled 또는 rejected 상태가 되고나면 그 상태와 결과가 다시는 바뀌지 않기 때문이다.</li>
<li>async/await 구문의 경우, await은 async 함수 안에서만 사용할 수 있고, 코드의 top-level(어떤 함수 블록 안에 포함된 것이 아닌 코드의 최상위 영역)에서는 사용될 수는 없다. 그래서 코드의 top-level에서 async 함수가 리턴한 Promise 객체의 작업 성공 결과를 가져오려면 await을 사용할 수는 없고, 여전히 then 메소드를 사용해야한다.</li>
</ol>
<p>콜백 헬 문제를 해결하기 위해 Promise 객체가 등장했고, Promise를 좀 더 편하게 다루기 위해서 async/await 구문이 등장했다.</p>
<h1 id="✨두-가지-종류의-콜백">✨두 가지 종류의 콜백</h1>
<p>자바스크립트에서 콜백은 어떤 함수의 파라미터로 전달되는 모든 함수를 의미하는 개념이다. 그러니까 어떤 함수의 파라미터로 전달되기만 한다면 해당 함수는 그 함수의 콜백이 되는 것. </p>
<p>이런 콜백은</p>
<p><strong>1. 동기 실행되는 콜백</strong>과
<strong>2. 비동기 실행되는 콜백</strong></p>
<p>으로 나뉜다. </p>
<p>그렇다면 1번에 해당하는 콜백에는 어떤 것들이 있을까? </p>
<p>예를 들어, 자바스크립트 배열의 메소드 중에서 <strong>filter라는 메소드</strong></p>
<p>→ 이 메소드는 배열의 여러 요소들 중에서 특정 조건을 만족하는 요소들만을 추려서 그 요소들로만 이루어진 새로운 배열을 리턴하는 메소드다.</p>
<pre><code class="language-jsx">const arr = [1, 2, 3, 4, 5, 6];

const newArr = arr.filter(function isOdd(num) {
  return (num % 2 === 1);
});

console.log(newArr); // [1, 3, 5]</code></pre>
<ul>
<li><code>arr</code>라는 배열에서 <strong>홀수</strong>만을 추출해서 해당 홀수들만으로 이루어진 새로운 배열을 리턴한다.</li>
<li><code>filter</code> 함수의 파라미터 부분을 보면 <code>isOdd</code>(홀수인가요?)라는 함수가 들어있다.</li>
<li><code>filter</code> 함수는 <code>arr</code> 배열에서 각 요소를 하나씩 순회하면서 매 요소마다 isOdd 함수를 실행하고, 해당 함수가 true를 리턴하는 요소들만을 추출한다.</li>
</ul>
<p>👇🏻Arrow Function 형식👇🏻</p>
<pre><code class="language-jsx">const arr = [1, 2, 3, 4, 5, 6];

const newArr = arr.filter((num) =&gt; num % 2);

console.log(newArr); // [1, 3, 5]</code></pre>
<p><strong>filter 메소드 안의 콜백은 &#39;동기 실행되는 콜백&#39;</strong>이다. 
즉, 이 콜백은 우리가 이번 토픽에서 배웠던 &#39;비동기 실행되는 콜백&#39;과는 달리, 아주 정직하게 순서대로 실행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이벤트 루프와 태스크 큐]]></title>
            <link>https://velog.io/@bori_note/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EC%99%80-%ED%83%9C%EC%8A%A4%ED%81%AC-%ED%81%90</link>
            <guid>https://velog.io/@bori_note/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EC%99%80-%ED%83%9C%EC%8A%A4%ED%81%AC-%ED%81%90</guid>
            <pubDate>Fri, 29 Mar 2024 10:27:45 GMT</pubDate>
            <description><![CDATA[<h1 id="이벤트-루프와-브라우저-환경">이벤트 루프와 브라우저 환경</h1>
<p>자바스크립는 싱글 스레드로 동작한다.</p>
<p>싱글 스레드: 한 번에 하나의 태스크만 처리한다는 뜻</p>
<p>(실제 면접에 받은 질문: 이벤트 루프에 대해 설명하는 것.)
❓자바스크립트를 멀티 스레드처럼 사용하는 방법이 뭔가요? 이벤트 루프에 대해서 설명해주세요.</p>
<p>자바스크립트는 싱글 스레드 언어지만 비동기 작업을 통해 멀티 태스크를 수행할 수 있다.</p>
<p>비동기 로직은 이벤트 루프를 통해 처리된다.</p>
<p><img src="https://velog.velcdn.com/images/bori_note/post/ed138135-045c-4d84-816f-6f143706fc27/image.png" alt=""></p>
<p><strong>힙</strong></p>
<ul>
<li>객체가 저장되는 메모리 공간</li>
</ul>
<p><strong>콜 스택</strong></p>
<ul>
<li>함수 호출 시 실행 컨텍스트가 생성된다.</li>
<li>함수를 호출하면 실행 컨텍스트가 순차적으로 콜 스택에 푸시되어 순차적으로 실행된다. 단 하나의 콜 스택을 사용하기 때문에 최상위 실행 컨텍스트가 종료되어 콜 스택에서 제거되기 전까지는 다른 어떤 태스크도 실행하지 않음.</li>
</ul>
<p><strong>이벤트 루프</strong></p>
<ul>
<li>콜 스택이 비어있고 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기 중인 함수를 콜 스택으로 이동시킨다.(FIFO)</li>
</ul>
<p><strong>태스트 큐</strong></p>
<ul>
<li>비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역</li>
</ul>
<p>예시)</p>
<pre><code class="language-js">console.log(1+1)
setTimeout(function() {console.log(2+2)}, 1000)
console.log(3+3)</code></pre>
<ul>
<li><p>실행결과는?</p>
<pre><code class="language-js">  2
  6
  4</code></pre>
</li>
<li><p>동작순서</p>
<ol>
<li>console.log(1+1) 이 콜 스택에 올라감 → 2 출력</li>
<li>setTimeout 함수가 실행되어 타이머가 설정되고 비동기 작업 시작. → setTimeout함수의 콜백함수는 태스크 큐에서 대기.</li>
<li>console.log(3+3) 이 콜 스택에 올라감 → 6 출력</li>
<li>setTimeout안의 console.log(2+2)가 태스크 큐로 이동</li>
<li>콜 스택이 비어있나 확인 후, console.log(2+2) 콜 스택에 올라감 → 4 출력</li>
</ol>
</li>
</ul>
<h2 id="마이크로-태스트--매크로-태스크">마이크로 태스트 &amp; 매크로 태스크</h2>
<p><img src="https://velog.velcdn.com/images/bori_note/post/38d03833-19b2-4e4f-b26c-3238b16f8d27/image.gif" alt=""></p>
<p>태스크 큐는 마이크로 태스크큐와 매크로 태스크큐로 나뉜다.</p>
<ul>
<li>마이크로태스트: Promise의 then, catch, finally와 같은 마이크로 태스크</li>
<li>매크로 태스크: setTimeout 및 비동기 작업의 콜백 함수</li>
</ul>
<p>마이크로태스크 큐는 태스크 큐보다 우선순위가 높다. → 이벤트 루프는 콜 스택이 비면 먼저 마이크로태스크 큐에서 대기하고 있는 함수를 가져와 실행한다. → 마이크로태스크 큐가 비면 매크로태스크 큐에서 대기하고 있는 함수를 가져와 실행한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[4주차 위클리 페이퍼]]></title>
            <link>https://velog.io/@bori_note/4%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</link>
            <guid>https://velog.io/@bori_note/4%EC%A3%BC%EC%B0%A8-%EC%9C%84%ED%81%B4%EB%A6%AC-%ED%8E%98%EC%9D%B4%ED%8D%BC</guid>
            <pubDate>Fri, 29 Mar 2024 07:28:24 GMT</pubDate>
            <description><![CDATA[<h1 id="❓자바스크립트에서-얕은-복사shallow-copy와-깊은-복사deep-copy에-대해-설명해-주세요">❓자바스크립트에서 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)에 대해 설명해 주세요.</h1>
<h2 id="얕은-복사shallow-copy">얕은 복사(Shallow Copy)</h2>
<ul>
<li>객체나 배열을 복사할 때, 복사된 객체는 원본 객체의 참조를 공유한다.
즉, 복사된 객체의 내부 객체는 실제로 복사되지 않고, 원본 객체와 같은 내부 객체를 참조한다.</li>
<li>주로 Object.assign()이나 스프레드 연산자(...)를 사용하여 이루어진다.</li>
</ul>
<p>예시)</p>
<pre><code class="language-js">let originalArray = [1, 2, 3];

// 또는 let shallowCopy = [...originalArray];
let shallowCopy = originalArray.slice();

shallowCopy[0] = 5;

console.log(originalArray); // [1, 2, 3]
console.log(shallowCopy); // [5, 2, 3]</code></pre>
<h2 id="깊은-복사deep-copy">깊은 복사(Deep Copy)</h2>
<ul>
<li>객체나 배열을 복사할 때, 복사된 객체는 원본 객체와 완전히 분리되어 있다. 내부 객체까지 모두 복사된다.</li>
<li>이러한 방식으로 복사된 객체는 원본 객체의 변경에 영향을 받지 않는다.</li>
<li>자바스크립트에서는 기본적으로 깊은 복사를 지원하지 않기 때문에 직접 구현하거나 외부 라이브러리를 사용해야 한다.</li>
</ul>
<p>예시)</p>
<pre><code class="language-js">let originalArray = [[1], [2], [3]];
let deepCopy = JSON.parse(JSON.stringify(originalArray));

deepCopy[0][0] = 5;

console.log(originalArray); // [[1], [2], [3]]
console.log(deepCopy); // [[5], [2], [3]]</code></pre>
<p>JSON.stringify()를 통해 객체를 문자열로 변환하고, 다시 JSON.parse()를 사용하여 문자열을 객체로 변환함으로써 깊은 복사를 할 수 있다. 하지만 이 방법은 모든 데이터를 복사하기 때문에 성능에 영향을 줄 수 있다. 또한, 함수나 순환 참조된 객체 등을 복사할 수 없다.</p>
<h1 id="❓var-let-const-를-중복-선언-허용-스코프-호이스팅-관점에서-서로-비교해-주세요">❓var, let, const 를 중복 선언 허용, 스코프, 호이스팅 관점에서 서로 비교해 주세요.</h1>
<h2 id="중복-선언-허용">중복 선언 허용</h2>
<ul>
<li>var: 중복 선언이 허용된다. 같은 변수를 여러 번 선언해도 오류가 발생하지 않는다. 다만, 이전 값은 무시되고 새로운 값으로 변수가 재할당된다.</li>
<li>let, const: 중복 선언이 허용되지 않는다. 같은 이름의 변수를 두 번 선언하면 구문 오류가 발생한다.</li>
</ul>
<h2 id="스코프scope">스코프(Scope)</h2>
<ul>
<li>var: 함수 스코프를 갖는다. 함수 내에서 선언된 변수는 해당 함수 전체에서 접근할 수 있다.</li>
<li>let, const: 블록 스코프를 갖는다. {} 내에서 선언된 변수는 해당 블록 내에서만 접근할 수 있다.</li>
</ul>
<h2 id="호이스팅hoisting">호이스팅(Hoisting)</h2>
<ul>
<li>var: 선언 단계가 호이스팅된다. -&gt; 변수 선언은 해당 스코프의 최상단으로 끌어올려진다. 초기화는 그 자리에 남아 있다.</li>
<li>let, const: 호이스팅은 발생하지만 TDZ(Temporal Dead Zone)라는 것이 존재한다. TDZ 내부에서 변수를 참조하려고 하면 ReferenceError가 발생한다. -&gt; 선언 이전에 변수에 접근하는 것은 안전하지 않다.</li>
</ul>
<pre><code class="language-js">// var
console.log(x); // undefined (호이스팅)
var x = 10;
console.log(x); // 10

// let
console.log(y); // ReferenceErrorinitialization
let y = 20;
console.log(y); // 20

// const
console.log(z); // ReferenceErrorinitialization
const z = 30;
console.log(z); // 30</code></pre>
<h3 id="⚠️tdz">⚠️TDZ</h3>
<p>Temporal Dead Zone의 약자(일시적 사각지대)</p>
<p>let과 const 변수가 선언되기 전에 접근하려고 할 때 발생하는 현상 -&gt; 변수가 선언되었지만 초기화되기 이전의 시점을 나타낸다.</p>
<p>TDZ 특성</p>
<ul>
<li><p>접근 시 오류 발생: TDZ 내에서 변수에 접근하면 ReferenceError가 발생한다. -&gt; 변수가 아직 초기화되지 않았기 때문에 해당 변수에 접근할 수 없음.</p>
</li>
<li><p>호이스팅: let이나 const로 선언된 변수는 호이스팅된다. -&gt; 해당 변수의 선언은 스코프의 최상단으로 끌어올려진다. 하지만 초기화는 호이스팅되지 않으며, TDZ 내에서 변수에 접근하면 ReferenceError가 발생한다.</p>
</li>
<li><p>블록 스코프 내에서 적용: TDZ는 블록 스코프에서만 적용된다. -&gt; let이나 const로 선언된 변수는 해당 블록 내에서만 TDZ에 영향을 받는다.</p>
</li>
</ul>
<p>🙏🏻TDZ는 코드의 안전성을 높이고 실수를 줄이기 위해 도입된 개념으로, <strong>변수의 사용을 명시적으로 초기화한 후에만 안전하게 접근할 수 있도록 한다.</strong></p>
]]></description>
        </item>
    </channel>
</rss>