<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>맨 땅에 프론트엔드 개발자 되기</title>
    <link>https://babycoder05.tistory.com/</link>
    <description>2021.11 ~ 디자인을 전공한, 문득 개발이 재밌어 직업을 바꾼, 프론트엔드 개발 공부 일기</description>
    <language>ko</language>
    <pubDate>Thu, 18 Jun 2026 17:42:02 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>헬로코딩</managingEditor>
    <image>
      <title>맨 땅에 프론트엔드 개발자 되기</title>
      <url>https://tistory1.daumcdn.net/tistory/4959355/attach/4269a9f694244d02ba8484a0741a7cef</url>
      <link>https://babycoder05.tistory.com</link>
    </image>
    <item>
      <title>NextJS를 사용하는 이유 / SSR, SSG, ISR</title>
      <link>https://babycoder05.tistory.com/entry/NextJS%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-SSR-SSG-ISR</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;선수지식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSR 방식에 대한 이해도가 있어야 한다.&lt;/p&gt;
&lt;figure id=&quot;og_1693367128884&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;리액트를 사용하는 이유 (SPA, CSR, SSR)&quot; data-og-description=&quot;리액트를 사용하는 이유 (SPA, CSR, SSR) 많은 개발자들에게 사랑받고 있는 리액트!! 현재, 많은 현업 개발자들에 의해 쓰이고 있고, 프론트엔드 개발에 관심이 있다면 다들 한번씩은 들어봤을 것 같&quot; data-og-host=&quot;babycoder05.tistory.com&quot; data-og-source-url=&quot;https://babycoder05.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-SPA-CSR-SSR&quot; data-og-url=&quot;https://babycoder05.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-SPA-CSR-SSR&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ewY6Q/hyTMfIY5go/jKIMyu2y53rMDmKklNTVv1/img.jpg?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437,https://scrap.kakaocdn.net/dn/RZ8JQ/hyTL5zBeL0/0F8qlqiDFMwF2wv6iKW6m1/img.jpg?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437,https://scrap.kakaocdn.net/dn/bhqysI/hyTMb0UdEi/mgzhXNaVcUQ1dESZHW4p6K/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500&quot;&gt;&lt;a href=&quot;https://babycoder05.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-SPA-CSR-SSR&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://babycoder05.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-SPA-CSR-SSR&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ewY6Q/hyTMfIY5go/jKIMyu2y53rMDmKklNTVv1/img.jpg?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437,https://scrap.kakaocdn.net/dn/RZ8JQ/hyTL5zBeL0/0F8qlqiDFMwF2wv6iKW6m1/img.jpg?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437,https://scrap.kakaocdn.net/dn/bhqysI/hyTMb0UdEi/mgzhXNaVcUQ1dESZHW4p6K/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;리액트를 사용하는 이유 (SPA, CSR, SSR)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리액트를 사용하는 이유 (SPA, CSR, SSR) 많은 개발자들에게 사랑받고 있는 리액트!! 현재, 많은 현업 개발자들에 의해 쓰이고 있고, 프론트엔드 개발에 관심이 있다면 다들 한번씩은 들어봤을 것 같&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;babycoder05.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NextJS&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NextJS는 ReactJS를 기반으로 한 웹 프레임워크다. ReactJS에서 제공하는 기능 외에 추가적으로 SSR(서버사이드렌더링) 및 이미지 최적화 등의 기능을 더한 것이다. ReactJS만 사용해서도 자체적으로 서버를 구성해 SSR 방식을 구현할 수도 있다. 그렇지만 NextJS가 제공하는 기능들이 최적화가 잘 되어 있기 때문에 가장 널리 사용되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR과 CSR의 동작 방식의 차이로 설정해주어야 하는 것들이 다르기 때문에 그 차이점에 대해 명확히 알고 있어야 NextJS 어플리케이션의 동작을 구성할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SSR&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR과 CSR을 구분하는 가장 큰 차이점은 웹페이지를 완성해서 클라이언트에 보내주는지 아닌지와 관련되어 있다. SSR의 경우 서버에서 완성된 페이지를 렌더링하고 나서 페이지를 클라이언트(브라우저)에 보내주고, CSR의 경우 웹페이지를 구축할 수 있는 index.html 파일과 JavaScript 파일만 보내준 뒤 브라우저에서 페이지를 렌더링한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;b&gt;CSR&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;브라우저에서 페이지 요청 -&amp;gt; &amp;lt;div id=&quot;root&quot;&amp;gt;&amp;lt;/div&amp;gt;만 가지고 있는 빈 HTML 파일과 js 파일(bundle.js / 전체 루트에 대한 내용을 모두 포함), 스타일시트 등등 보내줌 -&amp;gt; 브라우저에서 페이지를 렌더링 함 -&amp;gt; 클라이언트에 보임&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;b&gt;SSR&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;브라우저에서 페이지 요청 -&amp;gt; 요청한 루트에 해당하는 페이지를 서버에서 렌더링 함 -&amp;gt; 완성된 html 파일을 먼저 클라이언트에 보내주고 (브라우저는 바로 보여주고) js 파일과 스타일시트 등등 내려받음 -&amp;gt; 다 내려받으면 페이지가 동작할 수 있는 상태가 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 엄밀히 말하자면 요즘에 쓰는 SSR 방식은 CSR 방식을 혼합해서 사용하기 때문에 Universal Rendering이라고 불러야 한다. 요즘의 거의 모든 웹사이트는 단순히 정보 제공만 하는 단방향 웹사이트 보다는 사용자가 상호작용을 하는 양방향 웹앱이 대부분이기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, CSR만 사용해도 되는데 어떤 경우에 SSR을 사용할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;웹사이트가 거대해서 bundle.js의 로딩속도가 길 때&lt;/li&gt;
&lt;li&gt;SEO(검색엔진 최적화)가 필요할 때&lt;/li&gt;
&lt;li&gt;위키피디아 같이 사용자와 상호작용이 없는 웹페이지일 때 (비용상으로 CSR이 필요하지 않으므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEO와 관련한 내용을 설명하자면, 배포작업을 마쳐 웹사이트를 클라이언트에 제공할 준비가 완료되면, 구글, 네이버와 같은 검색엔진들은 상시 웹크롤링 프로그램을 통해 그러한 사이트들의 정보를 추출하고 유저가 검색어를 통해 요청을 했을 때 정보를 제공하기 위해 준비해놓는다. 유저한테 노출이 잘 되게 하기 위해 검색엔진들의 웹크롤링 프로그램이 정보를 잘 추출할 수 있도록 만드는 작업을 검색엔진 최적화(Search Engine Optimization) 라고 한다. 웹크롤링 프로그램들은 웹사이트들의 html 파일로 정보를 추출하기 때문에 (물론, 인가가 필요없는 api의 경우에 한해 데이터 요청 후 정보를 추출하는 웹 크롤러도 있다.) 빈 html만 서버에서 가지고 있는 CSR 방식은 SEO에 취약할 수 밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 SSR 방식으로 미리 상호작용 없는 기본 상태의 html 파일을 서버에서 렌더링 해놓고 웹크롤링에 정보를 제공해줄 수 있도록 하는 방식을 사용하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 불편함을 느끼지 않도록 CSR과 SSR이 필요한 부분을 각각 파악해 기능을 잘 구현하는 것이 개발자의 능력(?)이라고 할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- getServerSideProps&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1693374396106&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Page({ data }) {
  // Render data...
}

// SSR
export async function getServerSideProps(context) {
  // 외부 API로 데이터 가져오기
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  // Pass data to the page via props
  return { props: { data } }
}

export default Page&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NextJS 기본적으로 정적으로 페이지를 생성해두지만, 데이터 페칭이 필요한 경우 getServerSideProps 함수를 이용해서 데이터 페칭 또한 SSR로 구현할 수 있다. (서버에서 데이터를 불러온다는 뜻) 그러나 NextJS는 getServerSideProps를 정말 필요할 때만 사용하라고 권고한다. CDN에 캐싱이 되지 않기 때문에 느리기 때문이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SSG&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG는 Static Site Generation의 약자로 정적 페이지 생성을 말한다. NextJS는 기본적으로 모든 페이지를 서버에서 정적으로 생성해두는데, SSR과 SSG의 차이점은 데이터 페칭을 언제하는 가에 따라 다르다. SSR 방식은 클라이언트가 페이지를 요청하는 시점에 데이터를 요청하고 렌더링 후 클라이언트에 보내주는 반면에, SSG 방식은 빌드 시점에 데이터를 요청해 빌드해놓기 때문에 SSR보다 더 빠르다. 데이터가 계속해서 자주 바뀌는 경우에는 SSR 방식을 사용하는 것이 맞겠지만, 블로그 글 같이 한번 작성된 글이 잘 수정되지 않는 경우에는 SSG 방식을 사용하는 것이 성능과 비용 면에서 낫다. 단, 빌드하는 시점에만 데이터를 요청하기 때문에 실제 데이터베이스에 데이터가 변경되더라도 반영이 되지 않으므로 잘 판단해서 구현해야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- getStaticProps&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1693375113974&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function SSGPage({ dateTime }: SSGPageProps) {
  return (
    &amp;lt;main&amp;gt;
      &amp;lt;TimeSection dateTime={dateTime} /&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}

export const getStaticProps: GetStaticProps = async () =&amp;gt; {
  const res = await axios.get('https://worldtimeapi.org/api/ip');

  return {
    props: { dateTime: res.data.datetime },
  };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getStaticProps 함수는 NextJS에서 제공하는 기능으로 정적 페이지 생성에서 데이터를 요청할 수 있도록 도와준다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ISR&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ISR은 Incremental Static Regenration의 약자로 SSG와 마찬가지로 빌드 시점에 페이지를 렌더링 한 후, 설정한 시간 마다 페이지를 새로 렌더링한다. SSG의 단점을 보완한 방식이라고 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1693375504271&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function ISR20Page({ dateTime }: ISR20PageProps) {
  return (
    &amp;lt;main&amp;gt;
      &amp;lt;TimeSection dateTime={dateTime} /&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}

export const getStaticProps: GetStaticProps = async () =&amp;gt; {
  const res = await axios.get('https://worldtimeapi.org/api/ip');

  return {
    props: { dateTime: res.data.datetime },
    revalidate: 20,
  };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 getStaticProps 함수를 이용하는 방식은 동일하고 추가적으로 revalidate 의 설정 값으로 제공된 값(seconds)에 따라 페이지가 새로 렌더링된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/Next JS</category>
      <category>getServerSideProps</category>
      <category>getStaticProps</category>
      <category>ISR</category>
      <category>nextjs</category>
      <category>ssg</category>
      <category>SSR</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/100</guid>
      <comments>https://babycoder05.tistory.com/entry/NextJS%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-SSR-SSG-ISR#entry100comment</comments>
      <pubDate>Wed, 30 Aug 2023 15:09:13 +0900</pubDate>
    </item>
    <item>
      <title>React 상태관리 툴 비교하기 (redux-toolkit vs recoil vs zustand)</title>
      <link>https://babycoder05.tistory.com/entry/React-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%ED%88%B4-%EB%B9%84%EA%B5%90%ED%95%98%EA%B8%B0-redux-toolkit-vs-recoil-vs-zustand</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 우리나라 개발 커뮤니티를 보면 recoil + react-query 조합을 사용하는 것이 최신 트렌드로 자리 잡고 있는 듯하다. (요즘이 아니라 1~2년 된 트렌드 같다. 2023년 기준) 그러나 남들이 사용한다고 다 따라서 사용하면 면접에서 질문을 받을 때 당황할 수 있기 때문에(?)가 아니라 사용하는 라이브러리의 스펙을 정확히 알고 만드는 제품의 성능을 향상시키기 위해 차이점을 알고 사용하면 좋을 것 같아서 한번 정리해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이 글은 개발 지식이 매우 미천한 자의 미숙한 글이므로 참고만 부탁드리며, 사실이 아닐 경우 정정을 해주시면 매우 감사하겠습니다.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@reduxjs/toolkit vs recoil vs zustand&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 리액트 상태관리 툴인 redux는 이미 너무 많은 사람들이 까고 있기 때문에(보일러 플레이트가 너무 길다, 러닝커브가 높다 등) 위 세개의 라이브러리를 비교해보려고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2822&quot; data-origin-height=&quot;2348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1SLjp/btslOCY7qhr/RFV17X9L3rvPQGy06PsOE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1SLjp/btslOCY7qhr/RFV17X9L3rvPQGy06PsOE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1SLjp/btslOCY7qhr/RFV17X9L3rvPQGy06PsOE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1SLjp%2FbtslOCY7qhr%2FRFV17X9L3rvPQGy06PsOE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2822&quot; height=&quot;2348&quot; data-origin-width=&quot;2822&quot; data-origin-height=&quot;2348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 npm trends 순위로는 redux가 가장 다운로드 수가 많고, 그 다음으로는 redux-toolkit, zustand, recoil 순이다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- redux-toolkit&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redux-toolkit은 욕을 많이 먹은 redux의 불편한 점을 개선해서 나온 것으로 기존 redux를 사용하던 나는 굉장히 쉽게 익힐 수가 있었다. 보일러 플레이트도 많이 줄고, (여전히 recoil이나 zustand와 비교하면 길지만) 무엇보다 RTK Query를 내장하고 있기 때문에 별도의 데이터 페칭 라이브러리 없이 캐싱, 자동 데이터 리페칭 등의 기능을 사용할 수 있다. 관심사끼리 slice로 나눠서 코드 스플리팅도 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- recoil&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;recoil을 처음 사용해봤을 때의 느낌은 '이게 다라고?' 였다. 그만큼 보일러 플레이트가 없고 사용법도 useState를 사용하는 것 만큼이나 쉽다. 그 말인 즉슨 정해진 바가 없기 때문에 자신만의 코드 스플리팅 철학이나 코드 작성 규칙이 없는 사람들에게는 어떻게 사용하는거지?? 라는 물음이 들 수 있겠다는 생각이 들었다. (&lt;s&gt;내가 그랬다.&lt;/s&gt;) 아직 DevTools도 완전치 않아서 콘솔로 확인해야 하고, 데이터 페칭 라이브러리를 별도로 설치해야 한다. 그래서 요즘 recoil + react-query 조합으로 많이 사용하는 것 같다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- zustand&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리나라보다는 해외에서 사용을 더 많이 하는 것 같은 zustand다. jotai의 개발자가 만들었다고 하는데, recoil 만큼이나 가볍고, 미들웨어로 DevTools도 제공하고 있다. zustand의 특이한 점은 상태를 구독하기 위해 provider로 래핑을 하지 않는다는 것이다. 대신 클로저를 이용하여 스토어를 제공한다. zustand 또한 별도의 데이터 페칭 라이브러리를 함께 사용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zustand는 검색해도 한글로 된 자료가 많이 나오지는 않는 것으로 봐서는 우리나라에서는 큰 호응을 얻지 못 하는 느낌이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교를 해봤지만 무엇이 압도적으로 더 좋다고 생각하지 않는다. 사용하기 편리한 것, 익숙한 것도 하나의 라이브러리 선정 기준이 될 수 있다. 그리고 입사하는 회사의 기술스택에 따라 맞추는 것도 능력이기 때문에 여러가지 툴은 다양하게 사용해보는 것이 좋고, 무엇보다 라이브러리를 깊이 이해하고 각 라이브러리에 맞게 성능 향상 방법을 고민하는 것이 더 중요하다고 생각한다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>Recoil</category>
      <category>redux</category>
      <category>redux-toolkit</category>
      <category>zustand</category>
      <category>리액트</category>
      <category>비교</category>
      <category>상태관리</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/99</guid>
      <comments>https://babycoder05.tistory.com/entry/React-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%ED%88%B4-%EB%B9%84%EA%B5%90%ED%95%98%EA%B8%B0-redux-toolkit-vs-recoil-vs-zustand#entry99comment</comments>
      <pubDate>Thu, 29 Jun 2023 17:55:05 +0900</pubDate>
    </item>
    <item>
      <title>Next.js + TypeScript + Tailwind CSS 시작하기 / Tailwind CSS 장단점</title>
      <link>https://babycoder05.tistory.com/entry/Nextjs-TypeScript-Tailwind-CSS-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Tailwind-CSS-%EC%9E%A5%EB%8B%A8%EC%A0%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드의 기술은 정말 빨리 변하는 것 같다. 불과 1년 전만 해도 React와 Vue가 프론트엔드 기술 시장의 점유율을 가지고 싸우는 것 같더니 React가 우세한 것 같다가 이제는 Next.js가 대세로 자리잡고 있는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(리액트 개발자들이 진화한 걸까?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹페이지를 만들 때 SEO(검색엔진 최적화)는 빼놓고 생각할 수 없는 문제라 React에 부가 라이브러리를 설치하는 방법으로 보완하는 방법도 있었지만 (이 글에서 싱글 페이지 어플리케이션이 왜 SEO에 취약한 지는 다루지 않겠다. 많은 설명 글들이 있으니 찾아보시길..) Next.js는 정말 편리하게 서버사이드 렌더링을 지원해주고 다른 부가적인 기능들이 많기 때문에 리액트 좀 다뤄봤던 개발자들은 안 쓸 수가 없는 프레임워크인 것 같다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시작하기&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1684200155169&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npx create-next-app@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 터미널의 내가 프로젝트를 위치시킬 폴더 경로 안에서 입력하면 차례대로 프로젝트의 설정을 정할 수 있는 프롬프트가 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnU38G/btsf2V30T4I/6hck763IYmApg1m5NpHOkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnU38G/btsf2V30T4I/6hck763IYmApg1m5NpHOkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnU38G/btsf2V30T4I/6hck763IYmApg1m5NpHOkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnU38G%2Fbtsf2V30T4I%2F6hck763IYmApg1m5NpHOkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;994&quot; height=&quot;254&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 이름을 입력하기!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z3bnH/btsf9B4pvgX/Zn8c8M4Tmxa1GCfKiTyFK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z3bnH/btsf9B4pvgX/Zn8c8M4Tmxa1GCfKiTyFK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z3bnH/btsf9B4pvgX/Zn8c8M4Tmxa1GCfKiTyFK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ3bnH%2Fbtsf9B4pvgX%2FZn8c8M4Tmxa1GCfKiTyFK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1042&quot; height=&quot;260&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript를 사용할 건가요? &quot;Yes&quot; (방향키로 조작할 수 있다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdq8Ch/btsfWXu86o4/OqZjKd9HcMhk9773Pkc36K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdq8Ch/btsfWXu86o4/OqZjKd9HcMhk9773Pkc36K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdq8Ch/btsfWXu86o4/OqZjKd9HcMhk9773Pkc36K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdq8Ch%2FbtsfWXu86o4%2FOqZjKd9HcMhk9773Pkc36K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1006&quot; height=&quot;258&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESLint를 사용할 건가요? Lint 좋아하는 사람들은 &quot;Yes&quot;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diTTS7/btsf438dTmH/FFFnqZanUMBwu2dThLMI2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diTTS7/btsf438dTmH/FFFnqZanUMBwu2dThLMI2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diTTS7/btsf438dTmH/FFFnqZanUMBwu2dThLMI2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiTTS7%2Fbtsf438dTmH%2FFFFnqZanUMBwu2dThLMI2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1022&quot; height=&quot;252&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind CSS를 사용할 건가요? &quot;Yes&quot;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/du24p0/btsf6GEOwbi/4SbcsvhZs807JNiB1b0Ml1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/du24p0/btsf6GEOwbi/4SbcsvhZs807JNiB1b0Ml1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/du24p0/btsf6GEOwbi/4SbcsvhZs807JNiB1b0Ml1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdu24p0%2Fbtsf6GEOwbi%2F4SbcsvhZs807JNiB1b0Ml1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1074&quot; height=&quot;258&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src 디렉토리를 사용할 건가요?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 주로 src 디렉토리를 사용해서 public 폴더와 분리시키는 편이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXyax1/btsf6GdKRyS/z2yJtaQDh84nmgrkWUQBpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXyax1/btsf6GdKRyS/z2yJtaQDh84nmgrkWUQBpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXyax1/btsf6GdKRyS/z2yJtaQDh84nmgrkWUQBpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXyax1%2Fbtsf6GdKRyS%2Fz2yJtaQDh84nmgrkWUQBpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1092&quot; height=&quot;262&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App Router를 사용할 건가요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App Router는 매우매우 최신에 나온 기술인데, (글을 쓰고 있는 기점으로) Next.js에서 page폴더를 통한 라우팅 방법을 사용하지 않고 라우팅을 할 수 있도록 만든 기술이다. 아직 안정화 단계가 아닌 듯하여 패스 &quot;No&quot;.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IIC5M/btsfXd5G3lA/0JQyWKehSQU11ThBcpp9uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IIC5M/btsfXd5G3lA/0JQyWKehSQU11ThBcpp9uK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IIC5M/btsfXd5G3lA/0JQyWKehSQU11ThBcpp9uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIIC5M%2FbtsfXd5G3lA%2F0JQyWKehSQU11ThBcpp9uK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1074&quot; height=&quot;260&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import 할 때 경로를 커스터마이징 할 것인지 묻는 항목인데, &quot;Yes&quot;.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qHzxb/btsf0RnSSHz/9Z4VKgoMH6k80KONZWSgPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qHzxb/btsf0RnSSHz/9Z4VKgoMH6k80KONZWSgPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qHzxb/btsf0RnSSHz/9Z4VKgoMH6k80KONZWSgPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqHzxb%2Fbtsf0RnSSHz%2F9Z4VKgoMH6k80KONZWSgPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1132&quot; height=&quot;262&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이렇게 디폴트로 커스터마이징 된 경로를 사용할 것인지 묻는다. &quot;Yes&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 마치고 나면 프로젝트 설치가 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 쓰고 있는 시점 불과 한달 전에도 프롬프트가 달랐는데 한달 뒤에 또 프롬프트가 어떻게 달라질지 모르겠다. Next.js가 정말 빨리 업그레이드 되고 있고, 또 그만큼 사용량도 늘어나는 추세라 예의주시하고 있어야 할 것 같다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Tailwind CSS&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js를 사용할 때, Tailwind CSS를 많이 사용한다는 얘기를 들어서 한번 사용해봤는데, 왜 사용하는지 알 것 같으면서도 불편한 점들이 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 장점과 단점, 그리고 단점을 보완하는 방법 등에 대해 생각해봤는데 이를 정리해보려고 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Next.js에서 CSS-in-JS 보다 도입이 쉽다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React의 스타일 툴로 많이 사용되는 Styled-components나 Emotion은 CSS-in-JS로 CSS 코드처럼 보이지만, 사실 JavaScript 코드이고, 브라우저에서 JavaScript 파일이 읽혀야지만 CSS 적용이 된다. 서버사이드 렌더링을 하는 Next.js 경우 이러한 CSS-in-JS툴을 사용하려면 별도의 설정을 해주어야 사용이 가능한데, Tailwind CSS는 순수 CSS로 파일을 생성해주기 때문에 JavaScript에 의존하지 않아도 된다. (서버사이드 렌더링의 장점을 살릴 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 클래스명을 고민하지 않아도 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스명을 짓는 일은 은근 까다로운 일이고, 프로젝트가 커질 수록 클래스명을 기억하는 것이 쉽지 않은데, Tailwind CSS는 미리 클래스명이 지정되어 있고, 그것을 가져다 쓰는 것이라 클래스명을 지을 필요가 없다. 그래서 클래스명 고민하는 시간을 아낄 수 있고, 클래스명이 충돌돼서 예기치 못하게 다른 스타일이 적용된다던가 하는 일이 발생하지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1684201615636&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function ItemDetail() {
  return (
    &amp;lt;div className=&quot;px-4 py-10&quot;&amp;gt; // px-4 =&amp;gt; padding이 x축으로 4rem
      &amp;lt;div&amp;gt;
        &amp;lt;h2 className=&quot;text-2xl font-bold text-gray-900&quot;&amp;gt;Similar items&amp;lt;/h2&amp;gt;
        &amp;lt;div className=&quot;mt-6 grid grid-cols-2 gap-4&quot;&amp;gt;
          {[1, 2, 3, 4, 5, 6].map((_, i) =&amp;gt; (
            &amp;lt;div key={i}&amp;gt;
              &amp;lt;div className=&quot;h-56 w-full mb-4 bg-slate-300&quot; /&amp;gt;
              &amp;lt;h3 className=&quot;text-gray-700 -mb-1&quot;&amp;gt;Galaxy S60&amp;lt;/h3&amp;gt;
              &amp;lt;span className=&quot;text-xs font-medium text-gray-900&quot;&amp;gt;$6&amp;lt;/span&amp;gt;
            &amp;lt;/div&amp;gt;
          ))}
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Just-in-time 컴파일러&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 Tailwind CSS가 다른 스타일링 툴과 비교한 장점이라기 보다 최근에 버전 3 이상으로 업그레이드 되면서 개선된 점인데, 버전 3 이전에는 모든 클래스명을 다 가지고 있고, 그것을 불러오는 개념이라 용량이 굉장히 컸다고 한다. 버전 3 이상부터는 Just-in-time 컴파일러를 통해서 실제 프로젝트에 사용된 클래스명만 가지고 와서 프로젝트에 주입해주기 때문에 빌드 시 프로젝트의 스타일링 파일 용량이 줄어들었다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 콘텐츠와 스타일의 관심사가 합쳐져 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML(처럼 보이는 JSX) 과 스타일링 코드를 한 파일 안에 작성하기 때문에 해당 요소에 어떤 스타일이 적용되어 있는 지 파악하기가 쉽다. 이걸 단점으로 지적하는 사람들도 있는데, 콘텐츠와 스타일링의 내용이 혼재되어 있어서 내용을 각각 파악하기가 어렵다는 이유 때문이다. 나는 개인적으로 HTML 확인하고 그 클래스명 찾아서 CSS 파일 가서 다시 스타일링 내용 찾아보는 과정보다 혼재되어 있어서 바로 찾아볼 수 있는 점이 장점으로 느껴졌다. Visual Studio Code는 코드 하이라이팅 기능이 너무 잘 되어 있어서 색깔로 구분이 되어 있다보니 내용을 못 찾겠다는 등의 불편함은 느껴지지 않았다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 코드가 못생겼다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 CSS를 작성할 때는 한 속성 다음에 줄바꿈을 해서 세로로 볼 수 있도록 코드를 짜는데, 스타일링 코드를 HTML 안에 작성하다 보니 옆으로 길어져서 코드가 가독성이 떨어진다. 물론, 프리티어 등을 통해 보완할 수 있겠지만 개인적으로 일반 CSS 파일처럼 파악이 쉽게 되지 않는 느낌이 있었다. 예를 들어, input 요소에는 hover, focus, disabled 등 속성 선택자들이 많은데, 이걸 한 줄에 죽 적으니 정말 코드 가독성이 떨어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 초기 러닝커브가 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 명으로 CSS를 지정하다 보니 일반 CSS 코드 말고 Tailwind CSS의 CSS 코드를 익혀야 한다. 그래서 처음에 익숙해질 때 까지 시간이 조금 걸린다. 물론 CSS와 매우 유사하게 되어 있어서 빠르게 익힐 수 있지만, 처음 사용할 때는 조금 불편하다. Tailwind CSS에서 공식적으로 제공하는 VS code Extension인 &quot;Tailwind CSS IntelliSense&quot;를 설치하면 좀 더 쉽게 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Tailwind CSS부터 배운다면, 일반 CSS 학습을 해칠 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 일반 CSS를 잘 모르고, 빠르게 시작하기 위해 Tailwind CSS 부터 학습한다면, 나중에 일반 CSS를 익힐 때 헷갈릴 수 있다. 코드가 유사한데, 다르므로...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 스타일링 코드 재사용을 하려면 컴포넌트를 다 제작해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 개인적으로 가장 불편한 부분이라고 느꼈는데, 디자인 시스템이라던가 일관된 스타일링 적용이 필요할 때, 하나의 클래스 지정을 해놓고 그 클래스명을 계속 가져다 쓰는 것이 아니라 컴포넌트를 다 제작해서 사용해야 한다. 안 그러면, 똑같은 코드를 매 요소마다 복붙해서 사용해야한다. 이 방법이 매우 비효율적인 이유는 수정이 필요할 때 다 일일이 수정을 해줘야 하기 때문에 가급적 컴포넌트를 만드는 것이 효율적이다. 예를 들어, table 태그 안에는 thead, tbody, tr, td 등의 여러 요소가 들어가는데, 공통 스타일링을 적용하기 위해서는 이 각 요소들을 스타일링이 적용된 컴포넌트화 시켜야 한다. 클래스명으로 간단하게 사용하다가 이렇게 만들려니 약간 불편함이 느껴졌지만.. 익숙하지 않아서 그런 것 같기도 하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점이 분명 존재하긴 하지만, 서버사이드 렌더링과 조합이 잘 맞고, 편리한 점도 분명 있기 때문에 Next.js를 사용할 때는 Tailwind CSS를 기꺼이 사용할 것 같다.&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/Next JS</category>
      <category>NeXT</category>
      <category>nextjs</category>
      <category>tailwind css</category>
      <category>tailwind css 장단점</category>
      <category>TypeScript</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/98</guid>
      <comments>https://babycoder05.tistory.com/entry/Nextjs-TypeScript-Tailwind-CSS-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Tailwind-CSS-%EC%9E%A5%EB%8B%A8%EC%A0%90#entry98comment</comments>
      <pubDate>Tue, 16 May 2023 11:14:15 +0900</pubDate>
    </item>
    <item>
      <title>React에서 react-i18next 사용하는 방법</title>
      <link>https://babycoder05.tistory.com/entry/React%EC%97%90%EC%84%9C-react-i18next-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 맡은 프로젝트 중 처음으로 다국어 지원 웹사이트를 만들게 되었다. 관리자 페이지여서 SEO는 따로 필요 없었기 때문에 React로 진행하기로 했고, 그에 따라 &lt;a href=&quot;https://github.com/i18next/react-i18next&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;react-i18next&lt;/a&gt; 사용방법을 정리해보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 다국어 지원을 제공하는 &lt;a href=&quot;https://github.com/i18next/next-i18next&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;next-i18next&lt;/a&gt; 라이브러리도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGkzFq/btsbmSYm0dw/gVNe0kMwlxFk2RvpTTIGUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGkzFq/btsbmSYm0dw/gVNe0kMwlxFk2RvpTTIGUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGkzFq/btsbmSYm0dw/gVNe0kMwlxFk2RvpTTIGUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGkzFq%2FbtsbmSYm0dw%2FgVNe0kMwlxFk2RvpTTIGUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;442&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 큰 회사에서는 어떤 방식으로 번역문을 관리하는지 모르겠지만 작은 프로젝트 단위에서는 json 방식으로 직접 보관하는 방법을 사용할 수 있다. (더 규모가 큰 프로젝트에서는 개발자가 아닌 번역 담당자가 번역문을 수정할 수 있도록 다른 방법을 찾아봐야 할 것 같긴 하다.)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단계 1: 패키지 설치&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 프로젝트 내에 react-i18next와 i18next 패키지를 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681954151935&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i react-i18next i18next&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단계 2: i18n config 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src 폴더 아래에 locales 라는 폴더를 새로 만들고 그 안에 i18n.ts (or i18n.js) 파일을 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681954894573&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/locales/i18n.ts

import i18n from &quot;i18next&quot;;
import { initReactI18next } from &quot;react-i18next&quot;;
import translationEN from &quot;locales/en/translation.json&quot;;
import translationKO from &quot;locales/ko/translation.json&quot;;

const resources = {
  en: {
    translation: translationEN
  },
  ko: {
    translation: translationKO
  }
};

i18n
  .use(initReactI18next)
  .init({
    resources,
    lng: &quot;ko&quot;, // 기본 설정 언어, 'cimode'로 설정할 경우 키 값으로 출력된다.
    fallbackLng: &quot;en&quot;, // 번역 파일에서 찾을 수 없는 경우 기본 언어
    interpolation: {
      escapeValue: false
    }
  });

export default i18n;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면, translationEN과 translationKO를 import 하는 데서 에러가 날 텐데, 이제 번역 파일을 생성해줄 차례다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단계 3: 번역 파일 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;locales 폴더 아래에 번역 국가 폴더를 만들고 각 폴더 아래에 translation.json 파일을 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681955094470&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/locales/ko/translation.json

{
  &quot;header&quot;: {
    &quot;mypage&quot;: &quot;마이페이지&quot;,
    &quot;logout&quot;: &quot;로그아웃&quot;,
    &quot;login&quot;: &quot;로그인&quot;,
    &quot;register&quot;: &quot;회원가입&quot;,
    &quot;language&quot;: &quot;언어설정&quot;,
    &quot;help&quot;: &quot;고객센터&quot;
  },
  &quot;nav&quot;: {
    &quot;dashboard&quot;: &quot;대시보드&quot;,
    &quot;shopping-mall&quot;: &quot;쇼핑몰 관리&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681955135353&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/locales/en/translation.json

{
  &quot;header&quot;: {
    &quot;mypage&quot;: &quot;My page&quot;,
    &quot;logout&quot;: &quot;Sign Out&quot;,
    &quot;login&quot;: &quot;Sign In&quot;,
    &quot;register&quot;: &quot;Sign Up&quot;,
    &quot;language&quot;: &quot;Languages&quot;,
    &quot;help&quot;: &quot;Help&quot;
  },
  &quot;nav&quot;: {
    &quot;dashboard&quot;: &quot;Dashboard&quot;,
    &quot;shopping-mall&quot;: &quot;Shopping Mall&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단계 4: 번들링을 위해 index.tsx에 import 하기&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1681955641715&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './global.scss';
import App from './App';
import reportWebVitals from './reportWebVitals';
import &quot;locales/i18n&quot;; // 이렇게 import 한다.

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단계 5: 번역 사용하기&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1681956168310&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 사용할 컴포넌트

import React, { useState, useRef, useCallback, useEffect } from &quot;react&quot;;
import { useNavigate } from &quot;react-router-dom&quot;;
import { useTranslation } from &quot;react-i18next&quot;;
import i18n from &quot;locales/i18n&quot;;

export default function Header() {
  const { t } = useTranslation();
  const languageRef = useRef&amp;lt;null | HTMLDivElement&amp;gt;(null);
  const navigate = useNavigate();
  const [isLanguageMenuOpen, setLanguageMenuOpen] = useState&amp;lt;boolean&amp;gt;(false);

  // 외부 클릭 시 닫기
  const handleUserClose = useCallback((e: any) =&amp;gt; {
    if (isLanguageMenuOpen &amp;amp;&amp;amp; languageRef.current !== null &amp;amp;&amp;amp; !languageRef.current.contains(e.target)) setLanguageMenuOpen(false);
  }, [isUserMenuOpen, isLanguageMenuOpen]);

  useEffect(() =&amp;gt; {
    document.addEventListener(&quot;click&quot;, handleUserClose);
    return () =&amp;gt; document.removeEventListener(&quot;click&quot;, handleUserClose)
  }, [handleUserClose]);

  // 언어 변경하기
  const changeLanguage = (lang: string) =&amp;gt; {
    i18n.changeLanguage(lang);
    setLanguageMenuOpen(false);
  };

  return (
    &amp;lt;header className=&quot;header&quot;&amp;gt;
      &amp;lt;div className=&quot;header-gnb&quot;&amp;gt;
        &amp;lt;div className=&quot;header-inner&quot;&amp;gt;
          &amp;lt;nav className=&quot;header-gnb-nav&quot;&amp;gt;
            &amp;lt;div className=&quot;header-gnb-nav-link&quot; onClick={() =&amp;gt; navigate(&quot;/user/signin&quot;)}&amp;gt;{t(`header.login`)}&amp;lt;/div&amp;gt;
            &amp;lt;div className=&quot;header-gnb-nav-link&quot; onClick={() =&amp;gt; navigate(&quot;/user/register&quot;)}&amp;gt;{t(`header.register`)}&amp;lt;/div&amp;gt;
            &amp;lt;div ref={languageRef} className=&quot;header-gnb-nav-link lang-en&quot; onClick={() =&amp;gt; setLanguageMenuOpen(prev =&amp;gt; !prev)}&amp;gt;
              {t(`header.language`)}
              {isLanguageMenuOpen &amp;amp;&amp;amp; (
                &amp;lt;ul className=&quot;header-gnb-nav-link-dropDown&quot;&amp;gt;
                  &amp;lt;li className=&quot;header-gnb-nav-link-dropDown-item&quot; onClick={() =&amp;gt; changeLanguage(&quot;ko&quot;)}&amp;gt;한국어&amp;lt;/li&amp;gt;
                  &amp;lt;li className=&quot;header-gnb-nav-link-dropDown-item&quot; onClick={() =&amp;gt; changeLanguage(&quot;en&quot;)}&amp;gt;English&amp;lt;/li&amp;gt;
                &amp;lt;/ul&amp;gt;
              )}
            &amp;lt;/div&amp;gt;
            &amp;lt;div className=&quot;header-gnb-nav-link&quot;&amp;gt;{t(`header.help`)}&amp;lt;/div&amp;gt;
          &amp;lt;/nav&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      {isLoggingIn &amp;amp;&amp;amp; (
        &amp;lt;div className=&quot;header-snb&quot; onMouseEnter={() =&amp;gt; setSubMenuOpen(true)} onMouseLeave={() =&amp;gt; setSubMenuOpen(false)}&amp;gt;
          &amp;lt;div className=&quot;header-inner&quot;&amp;gt;
            &amp;lt;nav className=&quot;header-snb-nav&quot;&amp;gt;
              &amp;lt;div className=&quot;header-snb-nav-link&quot;&amp;gt;{t(`nav.dashboard`)}&amp;lt;/div&amp;gt;
              &amp;lt;div className=&quot;header-snb-nav-link&quot;&amp;gt;{t(`nav.shopping-mall`)}&amp;lt;/div&amp;gt;
            &amp;lt;/nav&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/header&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 다국어 지원이라고 해서 어려울 것 같아서 겁먹었는데 생각보다 너무 간편하게 라이브러리 구성이 되어 있어서 사용하기 굉장히 편리했다. 다음번엔 SSR에서도 다국어 지원을 구성하는 방법을 사용해봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>i18n</category>
      <category>react</category>
      <category>react-i18next</category>
      <category>다국어지원</category>
      <category>리액트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/97</guid>
      <comments>https://babycoder05.tistory.com/entry/React%EC%97%90%EC%84%9C-react-i18next-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry97comment</comments>
      <pubDate>Thu, 20 Apr 2023 11:05:47 +0900</pubDate>
    </item>
    <item>
      <title>Refresh Token 프론트엔드 보관 위치</title>
      <link>https://babycoder05.tistory.com/entry/Refresh-Token-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B3%B4%EA%B4%80-%EC%9C%84%EC%B9%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh Token 프론트엔드 보관 위치에 대해 노션 AI에게 물어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;=&amp;gt; 답변&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh token은 보안을 강화하기 위한 인증 시스템에서 자주 사용되는 기술입니다. 이 문서에서는 Refresh token frontend에서 사용하는 방법에 대해 설명합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Refresh Token이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh token은 사용자 인증 토큰이 만료되었을 때, 새로운 토큰을 발급해주는 기술입니다. 이를 통해 사용자는 로그인하지 않아도 서비스를 계속 사용할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Frontend에서 Refresh Token 사용방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh Token을 어디에 저장해야 하는지는 상황에 따라 다릅니다. 하지만 일반적으로는 쿠키를 사용하여 저장하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Frontend에서 Refresh token을 사용하는 방법은 크게 두 가지가 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 쿠키를 이용한 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh token을 쿠키에 저장하고, 서버에 요청할 때마다 쿠키를 함께 전송하여 사용할 수 있습니다. 이 방법은 서버에서 쿠키를 직접 다루기 때문에 보안성이 높습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿠키는 브라우저에서 관리되며, 서버에 전송됩니다.&lt;/li&gt;
&lt;li&gt;따라서 쿠키에 저장하는 경우 &lt;a href=&quot;https://namu.wiki/w/CSRF&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CSRF 공격&lt;/a&gt;에 노출될 가능성이 높아집니다.&lt;/li&gt;
&lt;li&gt;쿠키는 보안상의 이유로 httpOnly 옵션을 사용하여 JavaScript에서 접근할 수 없도록 설정해야 합니다.&lt;/li&gt;
&lt;li&gt;Refresh Token은 유효기간이 길기 때문에, 쿠키에 저장될 때는 안전하게 암호화되어야 합니다. 또한 HTTPS 프로토콜을 사용해야 하며, 쿠키를 안전하게 관리하기 위한 다양한 방법이 있으니 이를 참고하여 구현해야 합니다.&lt;/li&gt;
&lt;li&gt;쿠키를 사용하면 브라우저를 닫았다가 다시 열어도 쿠키가 유지되므로, 사용자가 다시 로그인하지 않아도 자동으로 인증할 수 있습니다.&lt;/li&gt;
&lt;li&gt;다른 브라우저 탭에서도 쿠키를 공유할 수 있기 때문에, 여러 개의 탭에서 동시에 로그인 하거나 로그아웃 할 때 문제가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;하지만, 서버 측에서 쿠키를 쉽게 확인할 수 있기 때문에, 보안적인 측면에서 유리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Local Storage를 이용한 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh token을 Local Storage에 저장하고, 서버에 요청할 때마다 Local Storage에서 값을 가져와 사용할 수 있습니다. 이 방법은 쿠키보다 사용이 간편하지만, &lt;a href=&quot;https://namu.wiki/w/XSS&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;XSS 공격&lt;/a&gt;에 취약할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. Session Storage를 이용한 방법&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;session storage는 브라우저 탭당 별도로 관리됩니다.&lt;/li&gt;
&lt;li&gt;session storage는 브라우저를 닫았다가 다시 열 경우 저장된 데이터가 삭제 되기 때문에, 새로 로그인해야 합니다.&lt;/li&gt;
&lt;li&gt;따라서 session storage에 저장하는 경우 CSRF 공격에 노출되는 위험이 줄어듭니다.&lt;/li&gt;
&lt;li&gt;local storage와 마찬가지로 XSS 공격에 취약할 수 있습니다.&lt;/li&gt;
&lt;li&gt;XSS 공격에 대비하기 위해 access token의 유효기간을 짧게 가져가는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;다른 브라우저 탭에서는 session storage를 공유하지 않기 때문에, 여러 개의 탭에서 동시에 로그인 하거나 로그아웃 해도 문제가 발생하지 않습니다.&lt;/li&gt;
&lt;li&gt;하지만, 서버 측에서 session storage를 확인하기 어렵기 때문에, 보안적인 측면에서 불리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, refresh token을 보관하는 위치는 사용하는 애플리케이션의 보안 요구사항에 따라 결정되어야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Refresh Token 만료 되었을 때&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh token이 만료 되었을 때 자동 로그인 기능을 구현하려면, Frontend에서 로그인 정보를 Local Storage나 쿠키에 저장해두고, 페이지를 로드 할 때마다 해당 정보를 확인하여 자동 로그인을 구현할 수 있습니다. 이를 위해서는 로그인 페이지에서 로그인 정보를 저장하는 코드와, 페이지를 로드 할 때마다 해당 정보를 확인하는 코드를 구현해야 합니다. 이때, 보안상의 이유로 로그인 정보를 암호화하여 저장하고, HTTPS 프로토콜을 사용하여 정보가 안전하게 전송되도록 해야합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Refresh token은 사용자 인증 시스템에서 매우 중요한 역할을 합니다. Frontend 에서도 적절히 사용하여 서비스의 보안성을 높일 수 있습니다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/Browser &amp;amp; Network</category>
      <category>frontend</category>
      <category>JavaScript</category>
      <category>Refresh Token</category>
      <category>리프레시 토큰</category>
      <category>보관 위치</category>
      <category>프론트엔드</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/96</guid>
      <comments>https://babycoder05.tistory.com/entry/Refresh-Token-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B3%B4%EA%B4%80-%EC%9C%84%EC%B9%98#entry96comment</comments>
      <pubDate>Wed, 19 Apr 2023 16:20:01 +0900</pubDate>
    </item>
    <item>
      <title>Git 브랜치 전략에 대하여</title>
      <link>https://babycoder05.tistory.com/entry/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%A0%84%EB%9E%B5%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사가 작다보니 정해진 Git 브랜치 전략이 없었는데 여유가 좀 생긴 시기에 브랜치 전략을 정해보자는 이야기가 나왔다. 나도 이런 협업 규칙들이나 컨벤션에 관심이 많아서 이 기회에 찾아본 것들을 나름대로 정리해보려고 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Git Flow&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;브랜치 네이밍 규칙&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;master: 제품으로 출시될 수 있는 브랜치&lt;/li&gt;
&lt;li&gt;feature: 기능을 개발하는 브랜치 featrue/{구현기능명} (ex. feature/login)&lt;/li&gt;
&lt;li&gt;develop: 다음 출시 버전을 개발하는 브랜치&lt;/li&gt;
&lt;li&gt;release: 이번 출시 버전을 준비하는 브랜치&lt;/li&gt;
&lt;li&gt;hotfix: 출시 버전에서 발생한 버그를 수정하는 브랜치&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;1524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byMVjG/btrXcfzcAXP/JcwRuZ2C9IixoQeW0kDla0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byMVjG/btrXcfzcAXP/JcwRuZ2C9IixoQeW0kDla0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byMVjG/btrXcfzcAXP/JcwRuZ2C9IixoQeW0kDla0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyMVjG%2FbtrXcfzcAXP%2FJcwRuZ2C9IixoQeW0kDla0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1150&quot; height=&quot;1524&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;1524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;브랜치 관리 규칙&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;feature: feature 브랜치는 기능의 구현을 담당한다. feature 브랜치는 develop 브랜치에서 생성되며, develop 브랜치로 머지된다. 머지된 후에는 해당 브랜치가 삭제된다.&lt;/li&gt;
&lt;li&gt;develop: develop 브랜치는 개발을 진행할 때 중심 브랜치다. 하나의 feature 브랜치는 기능 개발이 완료된 후 develop 브랜치에 머지된다. develop 브랜치는 배포할 수준의 기능이 갖춰지면 release 브랜치로 머지된다.&lt;/li&gt;
&lt;li&gt;release: release 브랜치는 develop 브랜치에서 개발이 완료된 사항을 검토하는 브랜치다. 충분한 테스트를 통해 버그를 검사하고 수정해 배포할 준비가 완전히 되었다고 판단되면 master로 머지해 배포한다.&lt;/li&gt;
&lt;li&gt;hotfix: hotfix 브랜치는 배포된 소스에서 버그가 발생하면 생성되는 브랜치다. release 브랜치를 거쳐 버그 검사를 했지만 예상치 못하게 배포 후 발견된 버그들에 대해서 수정한다.&lt;/li&gt;
&lt;li&gt;master: master 브랜치는 최종적으로 배포되는 가장 중심의 브랜치다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Github Flow&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github Flow는 Git Flow의 브랜치 전략이 너무 복잡하고 적용하기 어렵다고 해서 생겨난 브랜치 전략이다. Github Flow는 master 브랜치 하나만을 가지고 진행하는 방식이다. master 브랜치는 어떤 기능이 구현되든, 오류가 수정되든 모두 master에서 머지되어 항상 update된 상태를 유지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. master 브랜치에서 개발이 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 기능 구현이나 버그가 발생하면 feature/{구현기능명} 브랜치에서 개발을 하고, commit log를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. push를 하면 pull request를 날릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. pull request를 통해 팀원들 간의 피드백, 버그 찾는 과정이 진행된다. release 브랜치가 없으므로 이 과정이 꼼꼼하게 진행되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 모든 리뷰와 테스트가 마치면 master 브랜치에 머지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github Flow는 과정을 많이 줄인 만큼 단순하지만 pull request에서 팀원 간의 충분한 리뷰와 피드백이 진행되지 않으면 배포된 자체에서 버그가 발생할 수 있으므로 주의해야 한다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/Git</category>
      <category>branch</category>
      <category>git</category>
      <category>git flow</category>
      <category>github flow</category>
      <category>policy</category>
      <category>깃</category>
      <category>브랜치</category>
      <category>전략</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/95</guid>
      <comments>https://babycoder05.tistory.com/entry/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%A0%84%EB%9E%B5%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC#entry95comment</comments>
      <pubDate>Thu, 26 Jan 2023 11:51:41 +0900</pubDate>
    </item>
    <item>
      <title>CSS 방법론 BEM 알아보기</title>
      <link>https://babycoder05.tistory.com/entry/CSS-%EB%B0%A9%EB%B2%95%EB%A1%A0-BEM-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;BEM&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS 는 Cascading Style Sheet 의 약자다. Cascading 의 뜻을 검색해보면 '폭포수처럼 흘러내리는' 이라는 뜻이 나온다. 이것은 CSS가 부모에서부터 자식으로 폭포수처럼 상속되어 적용된다는 뜻을 가지고 있다. 그래서 CSS를 작성할 때는 이 상속관계에 대해서 잘 알고있어야 현재 Element에서 어떤 속성이 적용되고 있는지 알 수 있다. 이러한 CSS 작성 규칙을 예측 가능하게 잘 해보자는 의미에서 나온 방법론이 바로 &lt;u&gt;&lt;b&gt;BEM&lt;/b&gt;&lt;/u&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BEM은 CSS 작성을 할 때 필요한 Class 명을 어떻게 지을 것인가에 대한 방법론이다. Depth가 별로 깊지 않은 프로젝트에서라면 아무렇게나 클래스 명을 지어도 상관없지만 프로젝트의 규모가 커지고 Depth가 깊어질 수록 클래스 명을 중첩되지 않게 잘 지어야 현재 Element에서 어떤 CSS가 적용되는지 예측 가능하게 되고, !important 를 남발하지 않게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;BEM의 기본 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BEM은 각각 Block, Element, Modifier를 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게만 말하면 어렵게 느껴질 수 있는데, 큰 덩어리 단위의 블록, 그리고 그 블록을 구성하는 요소, 마지막으로 요소에 대한 변형을 말한다. 그리고 이 세가지를 __, --, -로 구분한다.&lt;/p&gt;
&lt;pre id=&quot;code_1670209814597&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form class=&quot;search-form&quot;&amp;gt;
    &amp;lt;input class=&quot;search-form__input&quot; /&amp;gt;
    &amp;lt;button class=&quot;search-form__button&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
    &amp;lt;button class=&quot;search-form__button--color--blue&quot;&amp;gt;Clear&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서 볼 때, Block은 search-form, Element는 input과 button, Modifier는 color--blue 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 앞에 Block 이름을 쓰고, 그 안의 요소는 __로 연결한다. 그 요소에서 색상 혹은 사이즈 등 변형이 있다면 --로 연결한다. 두 단어 이상이 연결될 때는 - 하나로 연결한다. 이 세가지가 바로 BEM을 구성하는 요소들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제를 SCSS와 합쳐서 사용하면 아래와 같은 형태가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1670214935322&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.search-form{
  &amp;amp;__input{

  }
  &amp;amp;__button{
    &amp;amp;--color{
      &amp;amp;--blue{
        
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SCSS 와 함께 사용할 경우, nesting 이 가능하기 때문에 더 가독성이 높아지고, mixin을 잘 정의해두면 공통 모듈 사용하기도 쉬워진다. CSS 방법론은 어디까지나 효율적으로 클래스 명을 잘 작성하자는 데에 의의가 있기 때문에 필수로 지켜야 할 사항은 아니지만, 어떻게 해야 프로젝트가 커지더라도 효율적으로 클래스 명을 지을 수 있을 지에 대한 좋은 고민은 필요하다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/CSS3 &amp;amp; SASS(SCSS)</category>
      <category>BEM</category>
      <category>CSS</category>
      <category>CSS 방법론</category>
      <category>SasS</category>
      <category>scss</category>
      <category>방법론</category>
      <category>클래스명</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/94</guid>
      <comments>https://babycoder05.tistory.com/entry/CSS-%EB%B0%A9%EB%B2%95%EB%A1%A0-BEM-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0#entry94comment</comments>
      <pubDate>Mon, 5 Dec 2022 13:39:13 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 날짜 형식 두 자리 수 만드는 방법</title>
      <link>https://babycoder05.tistory.com/entry/JavaScript-%EB%82%A0%EC%A7%9C-%ED%98%95%EC%8B%9D-%EB%91%90-%EC%9E%90%EB%A6%AC-%EC%88%98-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript 에서 new Date() 내장 함수로 월(getMonth)이나 일(getDate)을 가져올 경우, 한 자리 수 날짜는 한 자리로, 두 자리 수 날짜는 두 자리 수로 표기된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 자리 수 표기로 통일하는 방법은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1669955570258&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 월 2자리 표시
(&amp;ldquo;0&amp;rdquo; + (this.getMonth() + 1)).slice(-2);
// 날짜 2자리 표시
(&amp;ldquo;0&amp;rdquo; + this.getDate()).slice(-2);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slice(-2)를 하면 끝에 두 자리를 가져오므로 해석해보자면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 한 자리일 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;0&quot; + &quot;1&quot; = &quot;01&quot; =&amp;gt; 끝 두 자리 &quot;01&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 두 자리일 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;0&quot; + &quot;12&quot; = &quot;012&quot; =&amp;gt; 끝 두 자리 &quot;12&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/JavaScript</category>
      <category>JavaScript</category>
      <category>날짜</category>
      <category>두자리</category>
      <category>만드는 방법</category>
      <category>변형</category>
      <category>변환</category>
      <category>자바스크립트</category>
      <category>형식</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/93</guid>
      <comments>https://babycoder05.tistory.com/entry/JavaScript-%EB%82%A0%EC%A7%9C-%ED%98%95%EC%8B%9D-%EB%91%90-%EC%9E%90%EB%A6%AC-%EC%88%98-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%B0%A9%EB%B2%95#entry93comment</comments>
      <pubDate>Fri, 2 Dec 2022 13:35:58 +0900</pubDate>
    </item>
    <item>
      <title>React 외부 영역 클릭 시 닫기</title>
      <link>https://babycoder05.tistory.com/entry/React-%EC%99%B8%EB%B6%80-%EC%98%81%EC%97%AD-%ED%81%B4%EB%A6%AD-%EC%8B%9C-%EB%8B%AB%EA%B8%B0</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;1060&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P2OIm/btrSruVM7ie/nD8KKtoUiTJvsZUgYVR0N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P2OIm/btrSruVM7ie/nD8KKtoUiTJvsZUgYVR0N1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P2OIm/btrSruVM7ie/nD8KKtoUiTJvsZUgYVR0N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP2OIm%2FbtrSruVM7ie%2FnD8KKtoUiTJvsZUgYVR0N1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1098&quot; height=&quot;1060&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;1060&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 메뉴 혹은 모달, 그리고 커스텀 셀렉트박스를 만들 때, 외부 영역을 클릭하면 닫히게 만들고 싶을 경우, 사용하는 방법은 크게 두 가지가 있다.&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useState 활용하기&lt;/b&gt;&lt;/h3&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useState } from &quot;react&quot;;

const Sample = () =&amp;gt; {
&amp;nbsp;&amp;nbsp;const [isDropMenuOpen, setDropMenuOpen] = useState(false);

&amp;nbsp;&amp;nbsp;const toggleDropMenu = (e: React.MouseEvent&amp;lt;HTMLLIElement&amp;gt;) =&amp;gt; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;e.stopPropagation(); // 이벤트 캡쳐링 방지
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setDropMenuOpen(prevState =&amp;gt; !prevState);
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;return (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;SampleContainer
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	// 전체 영역 태그
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 전체 영역 아무 곳을 클릭해도 드롭메뉴가 닫힘
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onClick={() =&amp;gt; setDropMenuOpen(false)}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div className=&quot;sample-header&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ul className=&quot;sample-header-menu&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li className=&quot;sample-header-menu-item&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span className=&quot;material-symbols-outlined icon&quot;&amp;gt;print&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li className=&quot;sample-header-menu-item&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span className=&quot;material-symbols-outlined icon&quot;&amp;gt;download&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li className=&quot;sample-header-menu-item&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	// 토글버튼에 토글기능 넣어주기
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onClick={toggleDropMenu}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span className=&quot;material-symbols-outlined icon&quot;&amp;gt;more_vert&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/ul&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{isDropMenuOpen &amp;amp;&amp;amp; (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ul className=&quot;sample-header-dropmenu&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li className=&quot;sample-header-dropmenu-item&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span className=&quot;material-symbols-outlined icon&quot;&amp;gt;link&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span className=&quot;text&quot;&amp;gt;링크 복사&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/ul&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/SampleContainer&amp;gt;
&amp;nbsp;&amp;nbsp;)
}

export default Sample;&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;useState 만을 활용하는 방법은 버튼에 useState로 토글기능을 주고, 전체를 감싸고 있는 태그 위치에 state를 false로 만들도록 하는 것이다. 여기서 중요한 점은 이 방법을 사용할 경우에는 이벤트 캡쳐링이 발생하므로 이벤트 캡쳐링을 방지하는 코드를 꼭 넣어주어야 한다는 것이다. 이벤트 버블링과 캡쳐링에 대해 모른다면 꼭 알아두어야 하는 개념이니 한번 살펴보면 좋다.&lt;br&gt;&amp;nbsp;&lt;br&gt;위의 방법은 경우에 따라 사용하기가 어려울 수도 있다. 그럴 경우에는 addEventListener를 이용하는 방법도 있다.&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;addEventListener 이용하기&lt;/b&gt;&lt;/h3&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;import React, { useEffect, useRef, useState } from &quot;react&quot;;

const Sample = () =&amp;gt; {
&amp;nbsp;&amp;nbsp;const dropMenuRef = useRef&amp;lt;HTMLDivElement | null&amp;gt;(null);
&amp;nbsp;&amp;nbsp;const [isDropMenuOpen, setDropMenuOpen] = useState&amp;lt;boolean&amp;gt;(false);

&amp;nbsp;&amp;nbsp;useEffect(() =&amp;gt; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const handleOutsideClose = (e: {target: any}) =&amp;gt; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	// useRef current에 담긴 엘리먼트 바깥을 클릭 시 드롭메뉴 닫힘
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(isDropMenuOpen &amp;amp;&amp;amp; (!dropMenuRef.current.contains(e.target))) setDropMenuOpen(false);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.addEventListener('click', handleOutsideClose);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return () =&amp;gt; document.removeEventListener('click', handleOutsideClose);
&amp;nbsp;&amp;nbsp;}, [isDropMenuOpen]);

&amp;nbsp;&amp;nbsp;return (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;header&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;nav className=&quot;navContainer&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div ref={dropMenuRef} className=&quot;dropmenu-wrapper&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div className=&quot;userNameContainer&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onClick={() =&amp;gt; setDropMenuOpen(!isDropMenuOpen)}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{isDropMenuOpen ? (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span className=&quot;material-symbols-outlined icon&quot;&amp;gt;arrow_drop_up&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) : (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span className=&quot;material-symbols-outlined icon&quot;&amp;gt;arrow_drop_down&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{isDropMenuOpen &amp;amp;&amp;amp; (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div className=&quot;dropmenu&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ul className=&quot;dropmenu-list&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li className=&quot;dropmenu-list-item&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onClick={handleLogout}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span className=&quot;logout-btn&quot;&amp;gt;로그아웃&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/li&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/ul&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/nav&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/header&amp;gt;
&amp;nbsp;&amp;nbsp;)
}

export default Sample;&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 외부영역이 아닌 곳에 useRef 를 이용하여 current 에 엘리먼트를 담는다. 그리고 addEventListener를 이용하여 useRef에 담긴 엘리먼트가 아닌 곳을 클릭했을 때 드롭메뉴를 닫는 이벤트를 등록해준다! useEffect 안에서 등록한 이벤트는 컴포넌트의 마운트가 해제될 때 이벤트도 삭제되어 성능을 향상시킬 수 있도록 꼭 리턴 값에 removeEventListener로 이벤트를 삭제하도록 하자.&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;두 방법의 차이점&lt;/b&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;EventListener는 말그대로 등록된 이벤트에 맞는 동작이 일어나도록 듣고(감시하고) 있기 때문에 removeEventListener를 적절하게 해주지 않으면 메모리 누수의 원인이 된다. 그러나 useState를 이용한 방법은 부모 태그와 자식 태그의 위치를 이용한 방법이기 때문에 때때로 사용하기 어려운 환경일 수 있다. 그러므로 상황에 따라 적절한 방법을 택하면 될 것 같다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>react</category>
      <category>닫기</category>
      <category>모달</category>
      <category>셀렉트</category>
      <category>외부 영역 클릭 시 닫기</category>
      <category>외부영역</category>
      <category>클릭시</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/92</guid>
      <comments>https://babycoder05.tistory.com/entry/React-%EC%99%B8%EB%B6%80-%EC%98%81%EC%97%AD-%ED%81%B4%EB%A6%AD-%EC%8B%9C-%EB%8B%AB%EA%B8%B0#entry92comment</comments>
      <pubDate>Wed, 30 Nov 2022 12:07:03 +0900</pubDate>
    </item>
    <item>
      <title>CSS 텍스트 드래그 방지</title>
      <link>https://babycoder05.tistory.com/entry/CSS-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%93%9C%EB%9E%98%EA%B7%B8-%EB%B0%A9%EC%A7%80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;웹브라우저에서 내가 만든 웹페이지의 글 내용을 드래그 못 하게 하는 방법.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 CSS로 처리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1669686741526&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;body {
  -webkit-user-select:none;
  -moz-user-select:none;
  -ms-user-select:none;
  user-select:none
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 별로 다 막아준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 영역에서는 드래그를 허용하고 싶으면, 위의 코드를 body 영역에 기본으로 넣어주고, 원하는 영역에 아래 코드를 적용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1669686831759&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.allow-to-drag {
  -webkit-user-select:all;
  -moz-user-select:all;
  -ms-user-select:all;
  user-select:all
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트만 드래그를 허용하고 싶을 때,&lt;/p&gt;
&lt;pre id=&quot;code_1669686872573&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.allow-to-drag-text {
  -webkit-user-select:text;
  -moz-user-select:text;
  -ms-user-select:text;
  user-select:text
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 꼭 드래그 방지도 되지만, 웹사이트 내에서 텍스트에 커서가 올라갈 때마다 커서가 변하지 않도록 사용자 경험을 높일 수도 있다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/CSS3 &amp;amp; SASS(SCSS)</category>
      <category>CSS</category>
      <category>드래그</category>
      <category>싫어요</category>
      <category>텍스트</category>
      <category>텍스트 드래그 막기</category>
      <category>텍스트 드래그 방지</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/91</guid>
      <comments>https://babycoder05.tistory.com/entry/CSS-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%93%9C%EB%9E%98%EA%B7%B8-%EB%B0%A9%EC%A7%80#entry91comment</comments>
      <pubDate>Tue, 29 Nov 2022 10:55:57 +0900</pubDate>
    </item>
    <item>
      <title>RNCSafeAreaProvider was not found in the UIManager 에러 해결</title>
      <link>https://babycoder05.tistory.com/entry/RNCSafeAreaProvider-was-not-found-in-the-UIManager-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;모바일 앱 개발에 있어서 기기 사이즈, 노치 크기 등이 다 다르므로, react-native-safe-area-context 라이브러리를 사용하면, iOS 와 안드로이드에서 모두 Safe Area(기기 사이즈, 노치 크기 등으로 가려지지 않는 안전지대)를 확보할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;에러 해결&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ql1ed/btrQTESXhVy/z71btkVaxQyyaYZ6A2qvmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ql1ed/btrQTESXhVy/z71btkVaxQyyaYZ6A2qvmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ql1ed/btrQTESXhVy/z71btkVaxQyyaYZ6A2qvmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fql1ed%2FbtrQTESXhVy%2Fz71btkVaxQyyaYZ6A2qvmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;1334&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;react-native-safe-area-context 라이브러리를 설치했는데도 불구하고 위와 같은 에러 메시지가 나온다면, 아래 커맨드를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1668065468761&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npx pod-install ios&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qG2PV/btrQTHhNCCO/zmhQKRxl93RUQXdcHCCDRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qG2PV/btrQTHhNCCO/zmhQKRxl93RUQXdcHCCDRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qG2PV/btrQTHhNCCO/zmhQKRxl93RUQXdcHCCDRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqG2PV%2FbtrQTHhNCCO%2FzmhQKRxl93RUQXdcHCCDRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;584&quot; height=&quot;368&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 나서 다시 Metro를 실행해주면, 에러가 해결된다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/React Native</category>
      <category>React Native</category>
      <category>react-native-safe-area-context</category>
      <category>RNCSafeAreaView was not found in the UIManager</category>
      <category>리액트 네이티브</category>
      <category>설치 오류</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/90</guid>
      <comments>https://babycoder05.tistory.com/entry/RNCSafeAreaProvider-was-not-found-in-the-UIManager-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0#entry90comment</comments>
      <pubDate>Thu, 10 Nov 2022 16:33:46 +0900</pubDate>
    </item>
    <item>
      <title>React Native 버전 0.60 이상 react-native-vector-icons 설치방법</title>
      <link>https://babycoder05.tistory.com/entry/React-Native-%EB%B2%84%EC%A0%84-060-%EC%9D%B4%EC%83%81-react-native-vector-icons-%EC%84%A4%EC%B9%98%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;라이브러리 설치&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1668061627127&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install react-native-vector-icons
// for TypeScript
$ npm install --save-dev @types/react-native-vector-icons&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;라이브러리 연결&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 0.59 버전 이하에서는 아래 명령어로 바로 연결을 해줄 수 있었으나,&lt;/p&gt;
&lt;pre id=&quot;code_1668061754981&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ react-native link react-native-vector-icons&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0.60 버전 이상부터는 수동으로 연결을 해주어야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- iOS&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native CLI로 프로젝트를 시작했다는 가정하에 진행한다. (Expo CLI로 시작했을 경우, Expo 패키지에 기본적으로 포함되어 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native CLI로 프로젝트를 시작하려면, 기본적으로 Xcode가 깔려있어야 한다. Xcode를 실행하여, 프로젝트 폴더 안의 ios/[프로젝트명].xcworkspace 파일을 연다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfoGGU/btrQQcXlqIM/eFKXzQHB9nIGxOCnpeHUB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfoGGU/btrQQcXlqIM/eFKXzQHB9nIGxOCnpeHUB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfoGGU/btrQQcXlqIM/eFKXzQHB9nIGxOCnpeHUB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfoGGU%2FbtrQQcXlqIM%2FeFKXzQHB9nIGxOCnpeHUB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1482&quot; height=&quot;1330&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 다음, 해당 프로젝트에서 우클릭을 하여 New Group 을 눌러 Fonts 라는 폴더를 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bewLq9/btrQUbicp0L/JToaPN3xPqVmk2KzCQ3rfK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bewLq9/btrQUbicp0L/JToaPN3xPqVmk2KzCQ3rfK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bewLq9/btrQUbicp0L/JToaPN3xPqVmk2KzCQ3rfK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbewLq9%2FbtrQUbicp0L%2FJToaPN3xPqVmk2KzCQ3rfK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;468&quot; height=&quot;333&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음, node_modules/react-native-vector-icons/Fonts 폴더 안에 있는 .ttf 파일 전부를 아까 만들었던 Fonts 폴더로 드래그 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pOJlK/btrQTlsq2qQ/2WPehr0x9VoO3bNFub5Qnk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pOJlK/btrQTlsq2qQ/2WPehr0x9VoO3bNFub5Qnk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pOJlK/btrQTlsq2qQ/2WPehr0x9VoO3bNFub5Qnk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpOJlK%2FbtrQTlsq2qQ%2F2WPehr0x9VoO3bNFub5Qnk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;877&quot; height=&quot;348&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일들을 드래그하면 아래와 같은 모달 창이 뜨는데, Copy items if needed에 체크를 하고, Finish 버튼을 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VtzGc/btrQOOJxShb/yZadIVQXSMySTuKmtaWgVk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VtzGc/btrQOOJxShb/yZadIVQXSMySTuKmtaWgVk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VtzGc/btrQOOJxShb/yZadIVQXSMySTuKmtaWgVk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVtzGc%2FbtrQOOJxShb%2FyZadIVQXSMySTuKmtaWgVk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;423&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음, ios/[프로젝트명]/Info.plist 파일 안에 아래 내용을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1668062757593&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;
&amp;lt;plist version=&quot;1.0&quot;&amp;gt;
&amp;lt;dict&amp;gt;
    ...
  &amp;lt;key&amp;gt;UIViewControllerBasedStatusBarAppearance&amp;lt;/key&amp;gt;
  &amp;lt;false/&amp;gt;
  &amp;lt;key&amp;gt;UIAppFonts&amp;lt;/key&amp;gt;
  &amp;lt;array&amp;gt;
    &amp;lt;string&amp;gt;AntDesign.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;Entypo.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;EvilIcons.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;Feather.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;FontAwesome.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;FontAwesome5_Brands.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;FontAwesome5_Regular.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;FontAwesome5_Solid.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;Foundation.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;Ionicons.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;MaterialCommunityIcons.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;MaterialIcons.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;Octicons.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;SimpleLineIcons.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;Zocial.ttf&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;Fontisto.ttf&amp;lt;/string&amp;gt;
  &amp;lt;/array&amp;gt;
&amp;lt;/dict&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 Xcode에서 cmd + shift + k를 눌러 Clean Build Folder를 해준다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- android&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드 설정은 iOS 보다 간단하다. 프로젝트 내의 android/app/build.gradle 파일을 열고 아래 내용을 추가해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1668062953049&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
apply from: &quot;../../node_modules/react-native-vector-icons/fonts.gradle&quot; // add this line
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 node_modules/react-native-vector-icons/Fonts 폴더 안에 있는 파일들을 android/app/src/main/assets/fonts 폴더로 복사한다. (assets/fonts 폴더가 없다면 생성한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로, 안드로이드 프로젝트를 안드로이드 스튜디오로 열어 자동적으로 Gradle sync가 이루어질 수 있도록 한다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/React Native</category>
      <category>React Native</category>
      <category>react-native-vector-icons</category>
      <category>리액트 네이티브</category>
      <category>설치방법</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/89</guid>
      <comments>https://babycoder05.tistory.com/entry/React-Native-%EB%B2%84%EC%A0%84-060-%EC%9D%B4%EC%83%81-react-native-vector-icons-%EC%84%A4%EC%B9%98%EB%B0%A9%EB%B2%95#entry89comment</comments>
      <pubDate>Thu, 10 Nov 2022 15:55:32 +0900</pubDate>
    </item>
    <item>
      <title>React Native 프로젝트 폴더 구조 셋팅 &amp;amp; 절대 경로 설정 &amp;amp; 정적파일(이미지) 경로 설정</title>
      <link>https://babycoder05.tistory.com/entry/React-Native-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%8F%B4%EB%8D%94-%EA%B5%AC%EC%A1%B0-%EC%85%8B%ED%8C%85-%EC%A0%95%EC%A0%81%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B2%BD%EB%A1%9C-%EC%84%A4%EC%A0%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;* 예시 프로젝트는 React Native CLI로 만들어진 프로젝트임을 참고 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 개발 환경 설정은 공식 문서를 참고하세요.&lt;/p&gt;
&lt;figure id=&quot;og_1667970921899&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Setting up the development environment &amp;middot; React Native&quot; data-og-description=&quot;This page will help you install and build your first React Native app.&quot; data-og-host=&quot;reactnative.dev&quot; data-og-source-url=&quot;https://reactnative.dev/docs/environment-setup&quot; data-og-url=&quot;https://reactnative.dev/docs/environment-setup&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cg44Dk/hyQwC7slwB/kd3VVPcyZ9RAgDXF6pQ90k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/xi6KL/hyQwCfi7cZ/rLImPk7ZYiTs2MkXfkkxuK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/NHYSy/hyQwDFhVmS/U5JIVn2TcOiLH33u8K7KK1/img.png?width=1724&amp;amp;height=1158&amp;amp;face=0_0_1724_1158&quot;&gt;&lt;a href=&quot;https://reactnative.dev/docs/environment-setup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://reactnative.dev/docs/environment-setup&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cg44Dk/hyQwC7slwB/kd3VVPcyZ9RAgDXF6pQ90k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/xi6KL/hyQwCfi7cZ/rLImPk7ZYiTs2MkXfkkxuK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/NHYSy/hyQwDFhVmS/U5JIVn2TcOiLH33u8K7KK1/img.png?width=1724&amp;amp;height=1158&amp;amp;face=0_0_1724_1158');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Setting up the development environment &amp;middot; React Native&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This page will help you install and build your first React Native app.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;reactnative.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React Native 프로젝트 폴더 구조 셋팅&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native init 명령어로 프로젝트를 시작하면 아래와 같은 폴더 구조를 기본으로 셋팅해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;1232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vQEut/btrQKCn4CCW/UsWVTbMnKuRBfapz0YG9bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vQEut/btrQKCn4CCW/UsWVTbMnKuRBfapz0YG9bK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vQEut/btrQKCn4CCW/UsWVTbMnKuRBfapz0YG9bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvQEut%2FbtrQKCn4CCW%2FUsWVTbMnKuRBfapz0YG9bK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;442&quot; height=&quot;1232&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;1232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로젝트 루트에 components 폴더와 assets 폴더를 구성할 수도 있지만 app과 관련된 것들을 app 폴더에서 관리해주기 위해 아래와 같이 폴더 구조를 셋업했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1094&quot; data-origin-height=&quot;1792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRsBSv/btrQJvJ11SR/bkPKjCQRg8geKzhzjvTMm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRsBSv/btrQJvJ11SR/bkPKjCQRg8geKzhzjvTMm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRsBSv/btrQJvJ11SR/bkPKjCQRg8geKzhzjvTMm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRsBSv%2FbtrQJvJ11SR%2FbkPKjCQRg8geKzhzjvTMm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1094&quot; height=&quot;1792&quot; data-origin-width=&quot;1094&quot; data-origin-height=&quot;1792&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app 폴더 하위로 App.js 파일을 옮기고, App.js 파일과 같은 위치에 assets, components, screens, utils 와 같은 폴더를 구성했다. 그리고 assets 폴더 하위로 fonts와 images 폴더를 두었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;정적파일(이미지) 경로 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 파일이나 폰트 파일과 같이 정적 파일을 상대 경로로 import 하지 않기 위해 각 폴더 하위에 package.json 파일을 생성하고, 아래와 같이 작성했다.&lt;/p&gt;
&lt;pre id=&quot;code_1667971757669&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;:&quot;images&quot;,
  &quot;version&quot;:&quot;0.0.1&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1667971769747&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;:&quot;assets&quot;,
  &quot;version&quot;:&quot;0.0.1&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어두면 아래와 같이 정적파일 경로를 불러올 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1667971838541&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Image
	style={styles.backgroundImage}
	source={require('images/image02.jpg')}
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;import 시 절대 경로 설정하기&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- babel-plugin-module-resolver 설치하기&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1668059007151&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install --save-dev babel-plugin-module-resolver&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- babel.config.js 에서 alias 설정하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;babel.config.js 파일을 아래와 같이 수정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1668059247027&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    [
      'module-resolver',
      {
        root: ['./src'],
        extensions: [
          '.ios.ts',
          '.android.ts',
          '.ts',
          '.ios.tsx',
          '.android.tsx',
          '.tsx',
          '.jsx',
          '.js',
          '.json',
        ],
        alias: {
          '@': './src',
          '@components': './src/components',
          '@screens': './src/screens',
          '@assets': './src/assets',
        },
      },
    ],
  ],
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- tsconfig.json 파일 수정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript를 사용 중이라면, tsconfig.json 파일도 아래와 같이 수정해주어야 에러를 막을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1668059371606&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;extends&quot;: &quot;@tsconfig/react-native/tsconfig.json&quot;,     /* Recommended React Native TSConfig base */
  &quot;compilerOptions&quot;: {
    ..., // 다른 옵션들...
    &quot;baseUrl&quot;: &quot;./src&quot;,
    &quot;paths&quot;: {
      &quot;@/*&quot;: [&quot;./*&quot;],
      &quot;@components/*&quot;: [&quot;components/*&quot;],
      &quot;@screens/*&quot;: [&quot;screens/*&quot;],
      &quot;@assets/*&quot;: [&quot;assets/*&quot;],
    }                                /* Skip type checking all .d.ts files. */
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 공부 일지/React Native</category>
      <category>React Native</category>
      <category>리액트 네이티브</category>
      <category>이미지</category>
      <category>절대경로</category>
      <category>정적파일</category>
      <category>폴더 구조</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/88</guid>
      <comments>https://babycoder05.tistory.com/entry/React-Native-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%8F%B4%EB%8D%94-%EA%B5%AC%EC%A1%B0-%EC%85%8B%ED%8C%85-%EC%A0%95%EC%A0%81%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B2%BD%EB%A1%9C-%EC%84%A4%EC%A0%95#entry88comment</comments>
      <pubDate>Wed, 9 Nov 2022 14:31:42 +0900</pubDate>
    </item>
    <item>
      <title>React Native를 사용하는 이유 (장단점)</title>
      <link>https://babycoder05.tistory.com/entry/React-Native%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-%EC%9E%A5%EB%8B%A8%EC%A0%90</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React Native&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native는 React를 만든 페이스북이 만든 모바일 앱 개발을 위한 JavaScript 기반 프레임워크다. React와 React Native의 가장 큰 차이점은 React는 JavaScript 라이브러리 이지만, React Native는 프레임워크라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React는 JavaScript 라이브러리 이므로 JavaScript를 쓰는 느낌에 더 가깝다면, React Native는 만들어진 프레임을 가져다 쓰는 느낌에 가깝다. 그러나 이 둘의 근본은 같아서 React를 어느정도 사용해본 사람들은 금방 React Native도 익숙해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native는 HTML이나 CSS를 사용하지 않는다. 그 대신 JavaScript 쓰레드로부터의 메시지를 사용하여 네이티브 뷰를 조작한다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Hello World 예시&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1667884296037&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import { AppRegistry, Text } from 'react-native';

const HelloWorldApp = () =&amp;gt; &amp;lt;Text&amp;gt;Hello world!&amp;lt;/Text&amp;gt;;
export default HelloWorldApp;

// Skip this line if using Create React Native App
AppRegistry.registerComponent('HelloWorld', () =&amp;gt; HelloWorldApp);

// The React native code can also be imported from another component with the following code:
import HelloWorldApp from './HelloWorldApp';&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React Native의 장점&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 크로스 플랫폼&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native를 이용하면 기존 Native 앱 개발에 있어서 android는 Kotlin, ios는 Swift를 이용해서 따로 개발해야했던 반면에, 하나의 코드로 android와 ios를 둘 다 개발할 수 있다는 장점이 있다. 공식 버전 이외의 커뮤니티 버전을 사용하면, 윈도우, 우분투 등 다른 OS에서 작동하는 앱도 만들 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 네이티브와 호환&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 크로스 플랫폼 앱 개발과 React Native의 가장 큰 차이점이자 장점은 HTML, CSS, JavaScript로 이루어진 웹뷰로 렌더링을 하는 것이 아니라 해당 플랫폼의 표준 렌더링 API를 사용한다는 점이다. React Native는 작성된 마크업을 플랫폼에 따라 그에 상응하는 진짜 네이티브 엘리먼트로 전환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native의 리렌더링 갱신 주기는 React와 마찬가지로 props 혹은 state가 변경될 때 이다. 그러나 UI는 네이티브 엘리먼트 이므로, React를 이용하여 모바일 앱을 만들 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 낮은 러닝커브&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React와 매우 유사하므로 React를 통한 웹 개발 경험이 있다면, 매우 쉽게 React Native를 익힐 수 있다. 그리고 Emulator를 이용하여 에러 메시지를 잘 표시해주기 때문에 개발 경험이 매우 좋다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Hot Reload&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native는 좋은 성능의 Hot Reloading 기능을 제공하고 있다. 간혹 개발을 하다보면 iOS의 실기기를 연결해서 개발을 진행할 때 Hot Reload가 안 되는 경우가 있었는데, 원인을 찾지는 못했다. 그렇지만, Emulator나 안드로이드 실기기에서는 Hot Reloading 기능이 매우 잘 작동했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React Native의 단점&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- React Native와 Native와의 간극&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native는 대상 플랫폼의 네이티브 엘리먼트로 변환시켜주긴 하지만, 어쨌는 Native 앱은 아니기 때문에 React Native와 대상 플랫폼 사이에서 발생하는 문제의 디버깅은 매우 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 새로운 버전의 네이티브가 나왔을 때(예를 들어, 업데이트 된 안드로이드 버전에서 새로운 API 세트를 제공할 때) React Native에서 이를 지원해줄 때까지 시간이 걸린다. 그러므로, 사용목적에 따라 네이티브를 이용할 지 React Native를 이용할 지 결정해야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 디버깅의 어려움&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native는 매우 좋은 디버깅 기능을 제공하고 있지만 그것은 React Native 생명 주기와 관련된 내용이거나 JavaScript와 관련된 내용에 대해서고, 실제로 개발을 진행하면서 느낀 점은 네이티브 쪽과 관력된 사항에는 디버깅이 어려웠다. 예를 들어, 이전에 iOS나 안드로이드 개발을 해본 적이 없는 무지랭이 상태에서 카메라를 사용하는 앱을 개발하는데, 카메라 권한 동의를 받아야 한다는 사실을 몰라서 그냥 개발을 했더니 실기기에서 앱이 자꾸만 꺼지는 현상이 발생했다. 그러나 에러메시지가 나오지 않아서 원인을 찾는데 한참이 걸렸다. 그리고 카메라 화면에서 다른 탭 메뉴로 이동할 때, 카메라 화면에서 켜둔 플래시가 계속 켜져 있는 등 네이티브 쪽과 관련한 부분들을 잘 알고 있어야 디버깅이 가능했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 아직 정식 버전이 출시되지 않았음&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 이 글을 작성하고 있는 시점에 React Native의 최신 버전은 0.71이다. 정식 버전이 출시되지 않았더라도 간단한 앱을 만드는데 큰 무리가 없지만, 계속해서 버전이 올라가고 정식 버전 출시를 앞둔 상황이라 이전에 만들었던 앱의 버전을 올리는 작업이 추후에 요구될 수 있다. 버전을 올리는 과정에서 다른 라이브러리의 호환성 문제 등 신경 쓸 것이 많다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 애니메이션 구현의 어려움&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native는 거의 모든 CSS 기능을 구현하려고 노력했지만, CSS 애니메이션 구현 툴인 keyframes 는 옮겨오지 못했다. 그래서 복잡한 애니메이션을 구현하려면 복잡한 과정이 요구된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 다수의 플랫폼 검증&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드와 iOS를 동시 개발하기 때문에 한 플랫폼에서는 구현이 가능한 것이 다른 플랫폼에서는 구현이 안 될 수 있다. 예를 들어, &quot;iOS 쪽에서 이 기능을 사용하면 심사에 걸릴 것 같으니 이 기능은 제거하자&quot;, 혹은 동시 개발이 장점인데 결국 iOS와 안드로이드의 일부 기능을 따로 개발해야 할 수 있다. 그래서 React Native 개발자는 업무량이 많아질 수도 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React Native를 사용하여 개발된 앱&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Facebook&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Instagram&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Airbnb&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Skype&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Uber Eats&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React Native를 사용하는 기업&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Walmart&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Microsoft&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Meta&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Tesla&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Salesforce&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. Uber&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. Bloomberg&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. Airbnb&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. Shopify&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. Pinterest&lt;/p&gt;</description>
      <category>코딩 공부 일지/React Native</category>
      <category>React Native</category>
      <category>리액트 네이티브</category>
      <category>사용하는 이유</category>
      <category>장단점</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/87</guid>
      <comments>https://babycoder05.tistory.com/entry/React-Native%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-%EC%9E%A5%EB%8B%A8%EC%A0%90#entry87comment</comments>
      <pubDate>Tue, 8 Nov 2022 14:13:23 +0900</pubDate>
    </item>
    <item>
      <title>tsconfig.json 파일 알아보기</title>
      <link>https://babycoder05.tistory.com/entry/tsconfigjson-%ED%8C%8C%EC%9D%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;tsc와 tsconfig.json&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript를 사용하기 위해서 tsconfig.json 파일은 필수사항은 아니다. TypeScript가 전역으로 설치되어 있다면, tsc 명령어를 통해서 .ts로 작성된 파일을 .js로 컴파일 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1666142116415&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ tsc example.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 실행하면 동일한 경로에 js로 컴파일링된 example.js 파일이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 왜 tsconfig.json 파일을 설정할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vscode는 기본적으로 TypeScript에 대한 intellisense를 지원한다. (여담이지만, TypeScript도 마이크로소프트가 만들고, vscode도 마이크로소프트가 만듦. 마이크로소프트 다해먹어!! 짱짱맨!!) 우리가 개발을 하면서 어떤 코드가 잘못되었는지 빨간 밑줄을 쳐주거나 사용가능한 옵션들을 보여주는 것은 다 intellisense가 하는 일이다. 바로 이 &lt;u&gt;&lt;b&gt;TypeScript intellisense가 어떤 기준으로 코드 분석을 해야하는지 설정해주고 컴파일링은 어떤 방식을 해야하는지 설정해주는 파일이 바로 tsconfig.json 파일&lt;/b&gt;&lt;/u&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;tsconfig.json 의 속성값 알아보기&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&quot;include&quot;&lt;/i&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;include 속성에서는 배열 값 안에 어떤 파일 경로를 컴파일링 대상에 포함시킬 것인지를 명시해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1666144165202&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; { 
  &quot;include&quot;: [
    &quot;src&quot;
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;src&quot;라고 표시하면 src 경로 안에 있는 모든 파일들을 컴파일링 대상에 포함시킨다는 뜻이다. 반대로, exclude는 어떤 대상을 컴파일링 대상에서 제외시킬 것인지를 명시적으로 적어준다. exclude를 지정하지 않으면 [&quot;node_modules&quot;, &quot;bower_components&quot;, &quot;jspm_packages&quot;] 와 outDir에 지정한 경로가 기본값이 된다. 여기서 포함되는 파일들은 TypeScript가 지원하는 확장자만을 포함한다. 예시로, &lt;b&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;.ts, .tsx, .d.ts&lt;/span&gt;&lt;/b&gt; 가 있으며 allowJs를 활성화시키면 .js, .jsx도 포함시킨다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&quot;compilerOptions&quot;&lt;/i&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;compilerOptions 속성에는 컴파일링을 어떻게 할 것인가에 대한 항목을 설정해준다. 굉장히 많은 항목들이 있는데, 그 중 대표적인 것들만 알아보려고 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- target&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target에는 컴파일링 후의 최종 결과물이 어떤 버전의 JavaScript 문법으로 변환할 것인지를 정해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1666144797791&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;es5&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값은 &quot;ES3&quot;이다. 만약, ES3에 없는 JavaScript 기능을 코드에 작성하면 컴파일러는 에러를 출력한다. &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;여기서 중요한 점은 TypeScript와 Babel은 서로 독립적이기 때문에 tsconfig.json 파일에 명시해놓은 컴파일링 옵션이 Babel 의 컴파일링 옵션을 정의하지 않는다는 것이다. Babel 의 설정을 정의하고 싶다면 babel.config.json 파일을 통해서 설정해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- strict&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strict 를 true로 지정하면 TypeScript의 타입 검사 옵션 중에 strict 어쩌구 관련된 모든 것을 다 true로 만든다. 기본값은 false 이지만, true 로 설정해야 TypeScript를 사용하는 이점을 더 누릴 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- baseUrl&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;baseUrl 에는 import 를 할 때 경로의 기본값을 설정해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1666146946733&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;src&quot; // &quot;./&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;src&quot; 로 설정하면 절대경로로 import 를 할 수 있고, &quot;./&quot; 로 설정하면 상대경로로 import 를 할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- skipLibCheck&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;skipLibCheck 를 true 로 지정하면 .d.ts 파일의 타입 검사를 생략시킬 수 있다. 외부 라이브러리의 .d.ts 파일에 타입 정의가 잘못되어 있을 경우에 .d.ts 파일의 검사를 생략시키고, 내부 프로젝트에는 .d.ts 파일을 생성하지 않으면 외부 라이브러리의 타입 체크를 건너뛸 수 있다. 일반적으로 .d.ts 파일은 외부용으로 사용되므로 내부 프로젝트에서는 타입 정의를 .ts 파일에서 하고 import 해서 사용한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- lib&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lib 에는 현재 프로젝트에서 사용하는 특정 기능에 대한 문법 타입을 추가해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1666147695490&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;lib&quot;: [
      &quot;DOM&quot;,
      &quot;DOM.Iterable&quot;,
      &quot;ESNext&quot;
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript는 기본으로 DOM 관련 API 를 추가해주지 않기 때문에 만약 웹 브라우저에서 실행되어야 하는 코드라면 lib 에 &quot;DOM&quot;이라고 명시해주어야 document 같은 DOM 관련 코드를 실행해도 에러가 나지 않는다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- esModuleInterop&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import/export 구문은 ES6 에서 새롭게 나온 기능이기 때문에 module.exports = XXX 를 통해 내보내진 객체는 import 와 호환이 안 된다. 이러한 문제의 불편함을 해소하기 위해 exModuleInterop 을 true 로 설정해주면 컴파일링에서 자동으로 문제를 해소해준다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- isolatedModules&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;isolatedModules 를 true 로 설정하면 프로젝트 내의 모든 각각의 소스코드가 담긴 파일을 모듈로 만들기를 강제한다. 소스코드 파일에서 import 또는 export 를 사용하면 그 파일은 모듈이 된다. 소스코드 파일 안에 import 나 export 가 없다면 그 파일은 전역 공간으로 정의되는데 이때 isolatedModules 가 true 로 되어 있다면, 에러를 출력한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- noEmit&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;noEmit 을 true 로 설정하면 컴파일링 된 js 파일이 나오지 않는다. 물론, 최종 빌드 과정에서는 당연히 컴파일링 된 js 파일이 나온다. (브라우저는 ts 파일을 해석할 수 없으므로!!) 개발 과정에서 컴파일링 된 js 파일이 나오지 않게 하고 단순 타입 체크 용으로 사용할 것이라면 noEmit 을 true 로 설정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 디테일 한 옵션들을 알고싶다면 TypeScript 공식문서에서 확인할 수 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1666153292757&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;TSConfig Reference - Docs on every TSConfig option&quot; data-og-description=&quot;From allowJs to useDefineForClassFields the TSConfig reference includes information about all of the active compiler flags setting up a TypeScript project.&quot; data-og-host=&quot;www.typescriptlang.org&quot; data-og-source-url=&quot;https://www.typescriptlang.org/tsconfig&quot; data-og-url=&quot;https://www.typescriptlang.org/tsconfig/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/tsconfig&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.typescriptlang.org/tsconfig&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;TSConfig Reference - Docs on every TSConfig option&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;From allowJs to useDefineForClassFields the TSConfig reference includes information about all of the active compiler flags setting up a TypeScript project.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.typescriptlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/TypeScript</category>
      <category>tsconfig</category>
      <category>tsconfig.json</category>
      <category>TypeScript</category>
      <category>설정파일</category>
      <category>타입스크립트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/86</guid>
      <comments>https://babycoder05.tistory.com/entry/tsconfigjson-%ED%8C%8C%EC%9D%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0#entry86comment</comments>
      <pubDate>Wed, 19 Oct 2022 13:22:09 +0900</pubDate>
    </item>
    <item>
      <title>React Router v6 자주 쓰는 훅(hook) 정리</title>
      <link>https://babycoder05.tistory.com/entry/React-Router-v6-%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%ED%9B%85hook-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;React Router는 리액트의 라우팅 관련 라이브러리들 중에서 가장 오래됐고, 가장 많이 사용되고 있다. v6로 업그레이드 되면서 React Hooks 를 기반으로 한 React Router Hooks 들이 추가되었다. 쿼리스트링 파서(parser)를 따로 설치하지 않아도 될 정도로 편리한 기능들이 있어서 정리해보려고 한다.&lt;br&gt; &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기본 설치&lt;/b&gt;&lt;/h3&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ npx creat-react-app react-router-practice
$ npm i react-router-dom&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로젝트에 라우터 적용&lt;/b&gt;&lt;/h3&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
&amp;nbsp;&amp;nbsp;&amp;lt;BrowserRouter&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;App /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;/BrowserRouter&amp;gt;,
&amp;nbsp;&amp;nbsp;document.getElementById('root')
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 BrowserRouter로 감싸진 children 안에서 React Router 를 사용할 준비가 되었다.&lt;br&gt; &lt;br&gt;v5에서 v6로 업그레이드 되면서 가장 큰 차이점은 Switch 대신 Routes를 사용한다는 점이다. React Router의 공식문서에서는 Switch 대신 Routes를 쓰는 것이 버그를 줄여주고 더 예측 가능한 라우팅을 가능하게 한다고 설명한다.&lt;/p&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// v5의 라우팅 형식
import {
&amp;nbsp;&amp;nbsp;BrowserRouter,
&amp;nbsp;&amp;nbsp;Switch,
&amp;nbsp;&amp;nbsp;Route,
} from &quot;react-router-dom&quot;;

function App() {
&amp;nbsp;&amp;nbsp;return (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;BrowserRouter&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Switch&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Route exact path=&quot;/&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Home /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Route&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Route path=&quot;/users&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Users /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Route&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Switch&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/BrowserRouter&amp;gt;
&amp;nbsp;&amp;nbsp;);
}

// v6의 라우팅 형식
import {
&amp;nbsp;&amp;nbsp;BrowserRouter,
&amp;nbsp;&amp;nbsp;Routes,
&amp;nbsp;&amp;nbsp;Route,
&amp;nbsp;&amp;nbsp;Navigate,
} from &quot;react-router-dom&quot;;

function App() {
&amp;nbsp;&amp;nbsp;return (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;BrowserRouter&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Routes&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Route path=&quot;/&quot; element={&amp;lt;Home /&amp;gt;} /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Route path=&quot;users/*&quot; element={&amp;lt;Users /&amp;gt;} /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Route path=&quot;*&quot; element={&amp;lt;Navigate replace to=&quot;/&quot; /&amp;gt;} /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Routes&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/BrowserRouter&amp;gt;
&amp;nbsp;&amp;nbsp;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useLocation&lt;/b&gt;&lt;/h3&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckeSF1/btrOeQwhOFA/C8qkV62tfmk2QmXYCmv5F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckeSF1/btrOeQwhOFA/C8qkV62tfmk2QmXYCmv5F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckeSF1/btrOeQwhOFA/C8qkV62tfmk2QmXYCmv5F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckeSF1%2FbtrOeQwhOFA%2FC8qkV62tfmk2QmXYCmv5F1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1002&quot; height=&quot;238&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';

export default function Example() {
&amp;nbsp;&amp;nbsp;const { pathname, search, state } = useLocation();
&amp;nbsp;&amp;nbsp;const [title, setTitle] = useState();

&amp;nbsp;&amp;nbsp;useEffect(() =&amp;gt; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(pathname === '') {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setTitle('This is Home');
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} else if (pathname === 'info') {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setTitle('This is Info page');
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} else {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setTitle('This is empty page');
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;}, [pathname]);

&amp;nbsp;&amp;nbsp;return (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;{title}&amp;lt;/h1&amp;gt;
&amp;nbsp;&amp;nbsp;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useLocation 훅은 location 객체를 반환하는 훅으로 pathname, search, state 등에 접근할 수 있다. 보통 pathname에 따라 상태값을 변경하고 싶을 때 많이 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useNavigate&lt;/b&gt;&lt;/h3&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { createSearchParams, useNavigate } from 'react-router-dom';

export default function Example() {
&amp;nbsp;&amp;nbsp;const navigate = useNavigate();

&amp;nbsp;&amp;nbsp;return (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onClick={() =&amp;gt; navigate('/')}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;gt;Go to Home&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onClick={() =&amp;gt; navigate({
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pathname: '/article',
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;search: createSearchParams({
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;category: 'javascript',
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;currentPage: 1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}).toString(),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;})}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;gt;Go to Article&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/&amp;gt;
&amp;nbsp;&amp;nbsp;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useNavigate 훅은 라우팅 기능을 가지고 있는 함수를 리턴하는 훅으로 페이지 이동을 할 때 사용한다. pathname을 파라미터 값으로 바로 넣어주거나 객체에 값을 할당해 넣어줄 수 있다. createSearchParams와 함께 사용하면 쿼리 스트링(search) 값을 넣어주면서 페이지 이동을 할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useParams&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useParams는 URL 파라미터 값을 리턴하는 훅으로 자세한 내용은 이전 게시물을 참고하길 바란다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;useParams 로 세부 페이지 라우팅 구현하기&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;useParams 란? react-router에서 제공하는 Hooks 중 하나로 React 16.8 버전 이상에서만 구동이 가능하다. Parameter(파라미터) 값을 URL을 통해서 넘겨서 넘겨받은 페이지에서 사용할 수 있도록 도와준다. 예를&quot; data-og-host=&quot;babycoder05.tistory.com&quot; data-og-source-url=&quot;https://babycoder05.tistory.com/entry/useParams-로-세부-페이지-라우팅-구현하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dLeC3J/hyP8c8ZmSx/RljKmJmPKMmXSkpo4qIa21/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/vmXyO/hyP6D8edq2/nYFQMNgscqzKmgmlSIxr30/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hhZQU/hyP6oJ0hvw/wP3otKNGLtiqvXdmnySoW0/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500&quot; data-og-url=&quot;https://babycoder05.tistory.com/entry/useParams-%EB%A1%9C-%EC%84%B8%EB%B6%80-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot;&gt;
 &lt;a href=&quot;https://babycoder05.tistory.com/entry/useParams-%EB%A1%9C-%EC%84%B8%EB%B6%80-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; data-source-url=&quot;https://babycoder05.tistory.com/entry/useParams-로-세부-페이지-라우팅-구현하기&quot;&gt;
  &lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dLeC3J/hyP8c8ZmSx/RljKmJmPKMmXSkpo4qIa21/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/vmXyO/hyP6D8edq2/nYFQMNgscqzKmgmlSIxr30/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/hhZQU/hyP6oJ0hvw/wP3otKNGLtiqvXdmnySoW0/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500')&quot;&gt; 
  &lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;useParams 로 세부 페이지 라우팅 구현하기&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;useParams 란? react-router에서 제공하는 Hooks 중 하나로 React 16.8 버전 이상에서만 구동이 가능하다. Parameter(파라미터) 값을 URL을 통해서 넘겨서 넘겨받은 페이지에서 사용할 수 있도록 도와준다. 예를&lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;babycoder05.tistory.com&lt;/p&gt;
  &lt;/div&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useSearchParams&lt;/b&gt;&lt;/h3&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';

function Example() {
&amp;nbsp;&amp;nbsp;const [searchParams, setSearchParams] = useSearchParams();
&amp;nbsp;&amp;nbsp;const categoryParam = searchParams.get('category');
&amp;nbsp;&amp;nbsp;const currentPageParam = searchParams.get('currentPage');

&amp;nbsp;&amp;nbsp;useEffect(() =&amp;gt; fakeDataFetch({category: categoryParam, page: currentPageParam}), [categoryParam, currentPageParam]);

&amp;nbsp;&amp;nbsp;const handlePageClick = (pageNum) =&amp;gt; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setSearchParams({category: categoryParam, currentPage: pageNum});
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;return (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Pagination onClick={handlePageClick} /&amp;gt;
&amp;nbsp;&amp;nbsp;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useSearchParams는 쿼리스트링에 접근할 수 있게 해주는 훅이다. useState처럼 배열에서 구조분해할당으로 꺼내 쓸 수 있다. 앞에 선언된 searchParams가 getter 이고, setSearchParams가 setter 다.&lt;br&gt; &lt;br&gt;참고&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;React Router v6 튜토리얼&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;리액트 라우터 v6를 새로 접하시는 분들을 위한 튜토리얼을 작성했습니다. 리액트 라우터 v6 의 기본적인 사용법, 그리고 이 라이브러리에서 제공하는 다양한 유용한 기능들에 대해서 알아봅시&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@velopert/react-router-v6-tutorial&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/d4yF3K/hyP6AjlTP2/ILbIXyQ5j1CcmeoOrvq8q0/img.png?width=768&amp;amp;height=512&amp;amp;face=0_0_768_512,https://scrap.kakaocdn.net/dn/b8cE72/hyP6AjlTRu/UpuTrIReyF3eWGtK4RXXJ1/img.png?width=768&amp;amp;height=512&amp;amp;face=0_0_768_512,https://scrap.kakaocdn.net/dn/zgDxa/hyP8b3j1we/veKQ6GOPdvFnMrroo2clDk/img.png?width=1000&amp;amp;height=762&amp;amp;face=0_0_1000_762&quot; data-og-url=&quot;https://velog.io/@velopert/react-router-v6-tutorial&quot;&gt;
 &lt;a href=&quot;https://velog.io/@velopert/react-router-v6-tutorial&quot; target=&quot;_blank&quot; data-source-url=&quot;https://velog.io/@velopert/react-router-v6-tutorial&quot;&gt;
  &lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/d4yF3K/hyP6AjlTP2/ILbIXyQ5j1CcmeoOrvq8q0/img.png?width=768&amp;amp;height=512&amp;amp;face=0_0_768_512,https://scrap.kakaocdn.net/dn/b8cE72/hyP6AjlTRu/UpuTrIReyF3eWGtK4RXXJ1/img.png?width=768&amp;amp;height=512&amp;amp;face=0_0_768_512,https://scrap.kakaocdn.net/dn/zgDxa/hyP8b3j1we/veKQ6GOPdvFnMrroo2clDk/img.png?width=1000&amp;amp;height=762&amp;amp;face=0_0_1000_762')&quot;&gt; 
  &lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;React Router v6 튜토리얼&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;리액트 라우터 v6를 새로 접하시는 분들을 위한 튜토리얼을 작성했습니다. 리액트 라우터 v6 의 기본적인 사용법, 그리고 이 라이브러리에서 제공하는 다양한 유용한 기능들에 대해서 알아봅시&lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;velog.io&lt;/p&gt;
  &lt;/div&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Home v6.4.2&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;I'm on v5 The migration guide will help you migrate incrementally and keep shipping along the way. Or, do it all in one yolo commit! Either way, we've got you covered to start using the new features right away.&quot; data-og-host=&quot;reactrouter.com&quot; data-og-source-url=&quot;https://reactrouter.com/en/main&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dExZF7/hyP72rL8fq/GXR1zXzwgLX2nvQPkTCFH1/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/dL2ZfV/hyP73KY5dj/EGUtDiLyOm6q1y5mKQBoVk/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot; data-og-url=&quot;https://reactrouter.com/en/main&quot;&gt;
 &lt;a href=&quot;https://reactrouter.com/en/main&quot; target=&quot;_blank&quot; data-source-url=&quot;https://reactrouter.com/en/main&quot;&gt;
  &lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dExZF7/hyP72rL8fq/GXR1zXzwgLX2nvQPkTCFH1/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/dL2ZfV/hyP73KY5dj/EGUtDiLyOm6q1y5mKQBoVk/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800')&quot;&gt; 
  &lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;Home v6.4.2&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;I'm on v5 The migration guide will help you migrate incrementally and keep shipping along the way. Or, do it all in one yolo commit! Either way, we've got you covered to start using the new features right away.&lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;reactrouter.com&lt;/p&gt;
  &lt;/div&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>hooks</category>
      <category>react</category>
      <category>react-router</category>
      <category>useLocation</category>
      <category>useNavigate</category>
      <category>useSearchParams</category>
      <category>리액트</category>
      <category>리액트라우터</category>
      <category>자주 쓰는 훅</category>
      <category>훅</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/85</guid>
      <comments>https://babycoder05.tistory.com/entry/React-Router-v6-%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%ED%9B%85hook-%EC%A0%95%EB%A6%AC#entry85comment</comments>
      <pubDate>Tue, 11 Oct 2022 17:09:08 +0900</pubDate>
    </item>
    <item>
      <title>CRA + TypeScript 초기 세팅 방법 정리</title>
      <link>https://babycoder05.tistory.com/entry/CRA-TypeScript-%EC%B4%88%EA%B8%B0-%EC%84%B8%ED%8C%85-%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;CRA(Create-React-App)는 React를 사용할 때 필요한 Babel 이나 Webpack 같은 패키지들을 한번에 설치해주는 도구라서 굉장히 편리하다. CRA + TypeScript 조합으로 사용할 때도 매우 간단한 방법으로 초기 세팅을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CRA + TypeScript 명령어&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1665108653510&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npx create-react-app ts-react-practice --template typescript&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ts-react-practice 부분은 내 프로젝트 명을 써주면 되므로 그곳만 교체해서 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 ES LInt나 Prettier 설정을 해줄 수도 있지만 나는 VS code의 Extension을 사용하고 있기 때문에 설정해주지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;기존 프로젝트에 TypeScript 적용하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 만들어진 프로젝트에 TypeScript를 적용하고 싶다면 TypeScript 설정을 위한 패키지를 설치해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, typescript가 전역으로 설치되어 있지 않다면 먼저 전역으로 설치해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1666141566089&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i -g typescript&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 다음 내 프로젝트 경로 안에서 아래와 같이 설치해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1665124759153&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i typescript @types/node @types/react @types/react-dom @types/jest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 tsc 명령어를 통해 tsconfig.json 파일을 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1665124823162&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ tsc --init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 tsconfig.json 파일에 아래와 같이 설정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1665124934632&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;es5&quot;,
    &quot;lib&quot;: [
      &quot;dom&quot;,
      &quot;dom.iterable&quot;,
      &quot;esnext&quot;
    ],
    &quot;allowJs&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;esModuleInterop&quot;: true,
    &quot;allowSyntheticDefaultImports&quot;: true,
    &quot;strict&quot;: true,
    &quot;forceConsistentCasingInFileNames&quot;: true,
    &quot;noFallthroughCasesInSwitch&quot;: true,
    &quot;module&quot;: &quot;esnext&quot;,
    &quot;moduleResolution&quot;: &quot;node&quot;,
    &quot;resolveJsonModule&quot;: true,
    &quot;isolatedModules&quot;: true,
    &quot;noEmit&quot;: true,
    &quot;jsx&quot;: &quot;react-jsx&quot;,
    &quot;baseUrl&quot;: &quot;src&quot;
  },
  &quot;include&quot;: [
    &quot;src&quot;
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 기본 설정은 끝났고, 이 후 부터는 기존 jsx 문법으로 쓰인 파일들을 tsx로 변경하고 타입 지정을 해주면 된다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/TypeScript</category>
      <category>CRA</category>
      <category>react</category>
      <category>TypeScript</category>
      <category>리액트</category>
      <category>초기 세팅</category>
      <category>타입스크립트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/84</guid>
      <comments>https://babycoder05.tistory.com/entry/CRA-TypeScript-%EC%B4%88%EA%B8%B0-%EC%84%B8%ED%8C%85-%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC#entry84comment</comments>
      <pubDate>Fri, 7 Oct 2022 15:43:52 +0900</pubDate>
    </item>
    <item>
      <title>타입스크립트를 사용하는 이유</title>
      <link>https://babycoder05.tistory.com/entry/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트를 사용할 줄 알아야 프론트엔드 개발자 연봉이 높아진다던데ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 그렇게 타입스크립트를 기업에서 우대해주고 프론트엔드 개발에 있어서 점점 쓸 줄 모르면 안 되는 상황까지 가는가...?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유가 궁금해서 나름대로 이유를 찾아보고 정리해봤다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;타입스크립트(TypeScript)&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JRGMw/btrK8fLluqy/NiBibmjDMc5R7hbBmU5XL0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JRGMw/btrK8fLluqy/NiBibmjDMc5R7hbBmU5XL0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JRGMw/btrK8fLluqy/NiBibmjDMc5R7hbBmU5XL0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJRGMw%2FbtrK8fLluqy%2FNiBibmjDMc5R7hbBmU5XL0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1203&quot; height=&quot;1080&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트는 자바스크립트의 기본적인 틀을 가져가되, 거기에 타입 지정이라는 옵션을 얹은 것이라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초창기의 웹페이지는 지금의 웹페이지처럼 사용자와 인터랙티브한 기능이 많지 않았고, 도서관에서 책을 꺼내보듯이 인터넷 생태계에 많은 양의 정보를 저장해두고 꺼내보는 형식이었다. 그래서 자바스크립트는 견고한 구조를 짜기보다는 빠르게 어떤 기능을 만들기 위해 개발된 언어였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 복잡한 웹앱들이 등장하고 프론트 쪽에서 할 일이 많아짐에 따라 견고한 프로그램이 만들어질 필요성이 요구되자 다른 정적 타입 언어들처럼 자바스크립트에도 견고하게 만들 수 있는 언어의 기능이 필요해졌다. 그래서 등장한 것이 타입스크립트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 51.5116%;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Javascript&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 48.3721%;&quot;&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;&lt;b&gt;Typescript&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 51.5116%;&quot;&gt;동적타입 언어&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 48.3721%;&quot;&gt;정적타입 언어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 51.5116%;&quot;&gt;인터프리터 언어&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 48.3721%;&quot;&gt;컴파일 언어&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 51.5116%;&quot;&gt;독립적으로 사용가능&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 48.3721%;&quot;&gt;자바스크립트에 의존적임 (자바스크립트로 컴파일된 후 실행)&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 51.5116%;&quot;&gt;좀 더 유연함 (타입에 제한을 받지 않으므로)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 48.3721%;&quot;&gt;더 나은 구조와 간결함, 일관성, 재사용성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 51.5116%;&quot;&gt;.js 확장자&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 48.3721%;&quot;&gt;.ts 확장자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 51.5116%;&quot;&gt;작고 간단한 프로젝트에 적합함&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 48.3721%;&quot;&gt;복잡한 프로젝트에 적합함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트와 타입스크립트의 가장 큰 차이점은 타입을 지정 여부 말고도 한 가지가 더 있다. 바로 자바스크립트는 인터프리터 언어이고, 타입스크립트는 컴파일 언어라는 것이다. 인터프리터 언어는 웹 브라우저 혹은 런타임 환경에서 코드를 한 줄 한 줄 읽어내려가 해석하도록 만들어진 언어고, 컴파일 언어는 컴파일링(컴퓨터가 해석할 수 있는 언어로 변환하는 작업) 과정을 거쳐서 실행되어지는 언어다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트는 결국 컴파일링을 거쳐 자바스크립트로 변환된다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;타입스크립트를 왜 사용해야 하는가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트를 사용하는 이유에 대해 구글 선생님께 여쭤보면 대표적으로 언급되는 이유가&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 오류를 잡아내기 쉽다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트는 컴파일 언어라고 했는데, 바로 이 컴파일 과정에서 오류를 잡아내기 때문에 오류를 잡아내기 쉽다는 장점이 있다. 자바스크립트는 반면, 실제로 동작하는 과정(런타임 환경)에서 오류를 잡아낼 수 밖에 없다. 가령, 개발자가 예전에 만들어놓은 함수에 파라미터 값으로 숫자 타입을 받아야 하는데 한참 뒤에 그 함수를 사용하면서 스트링 타입의 파라미터를 받았을 경우, 자바스크립트는 이것을 잘못됐다고 하지 않는다. 그러나 타입스크립트는 컴파일 단계에서 잘못됐다고 잡아준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트를 사용하면 '잔소리 많은 엄마가 옆에서 자꾸 잔소리하는 기분'이라고 표현하는 글을 봤는데, 자바스크립트보다 지정해줘야 할 것도 많고 생각보다 귀찮은 작업이 많다. 그러나 결국 타입스크립트를 사용하는 목적을 견고한 앱을 만드는 것이기 때문에 귀찮더라도 세세하게 다 잡아주는 것이 오류를 막는데 도움이 될 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 협업에 도움이 된다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자들은 상대적으로 이직이 쉬운 직업이므로 다른 사람이 만들고 튀어버린(?) 코드를 들여다봐야 할 일이 많다. 그럴 때, 타입스크립트로 타입 지정이 다 되어있다면, 이것은 주석을 세세하게 단 것과 마찬가지라고 할 수 있다. 어떤 타입의 값을 리턴해야하는지가 다 나와있으므로 함수 이름이나 변수명을 거지같이 짓더라도 어느 정도는 쉽게 흐름을 파악할 수 있도록 도와줄 수 있다. 바로 이러한 이유로 (견고한 앱 + 인수인계) 기업들이 타입스크립트를 우대해주는 것이 아닌가 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'타입스크립트 안 써도 개발 잘 하고 있는데 왜 귀찮게 자꾸 타입스크립트 쓰라고 하지?' 라고 생각했는데, 타입스크립트를 배우는 것이 생각보다 어렵지 않다. 그리고 꼼꼼한 스타일의 사람이 깔끔하게 노션 작업 해놓은 것을 보면 기분이 좋아지듯이 깔끔하게 타입 지정을 잘 해놓은 코드를 보면 그 사람에 대한 신뢰도가 높아진다고 해야되나?ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 무지성으로 타입스크립트 써야돼!!! 가 아닌, 필요 시에는 사용할 줄 아는 개발자가 되면 좋으니까&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 스스로에게 세뇌를 시키고 타입스크립트 공부를 하러 가본다...ㅋㅋㅋ&lt;/p&gt;</description>
      <category>코딩 공부 일지/TypeScript</category>
      <category>TypeScript</category>
      <category>사용하는 이유</category>
      <category>타입스크립트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/83</guid>
      <comments>https://babycoder05.tistory.com/entry/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0#entry83comment</comments>
      <pubDate>Thu, 1 Sep 2022 14:25:39 +0900</pubDate>
    </item>
    <item>
      <title>프론트엔드 개발자 공부 로드맵</title>
      <link>https://babycoder05.tistory.com/entry/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EA%B3%B5%EB%B6%80-%EB%A1%9C%EB%93%9C%EB%A7%B5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1662000917197&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Developer Roadmaps&quot; data-og-description=&quot;Community driven roadmaps, articles, guides, quizzes, tips and resources for developers to learn from, identify their career paths, know what they don't know, find out the knowledge gaps, learn and improve.&quot; data-og-host=&quot;roadmap.sh&quot; data-og-source-url=&quot;https://roadmap.sh/frontend&quot; data-og-url=&quot;https://roadmap.sh&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zzvpR/hyPENbc6bM/6Tkd4QOZzcNCXm4VCjA1a1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/crwUqV/hyPEEL7lCf/AbQSgiueooFiy02jOhdngk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://roadmap.sh/frontend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://roadmap.sh/frontend&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zzvpR/hyPENbc6bM/6Tkd4QOZzcNCXm4VCjA1a1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/crwUqV/hyPEEL7lCf/AbQSgiueooFiy02jOhdngk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Developer Roadmaps&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Community driven roadmaps, articles, guides, quizzes, tips and resources for developers to learn from, identify their career paths, know what they don't know, find out the knowledge gaps, learn and improve.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;roadmap.sh&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프론트엔드 개발자 공부 로드맵&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;2780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blhfR7/btrK9x5uaRC/WUSqzK4BBCKM9fl7XxTq50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blhfR7/btrK9x5uaRC/WUSqzK4BBCKM9fl7XxTq50/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blhfR7/btrK9x5uaRC/WUSqzK4BBCKM9fl7XxTq50/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblhfR7%2FbtrK9x5uaRC%2FWUSqzK4BBCKM9fl7XxTq50%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1039&quot; height=&quot;2780&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;2780&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>코딩 공부 일지/ETC</category>
      <category>frontend</category>
      <category>roadmap</category>
      <category>개발공부</category>
      <category>로드맵</category>
      <category>프론트엔드</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/82</guid>
      <comments>https://babycoder05.tistory.com/entry/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EA%B3%B5%EB%B6%80-%EB%A1%9C%EB%93%9C%EB%A7%B5#entry82comment</comments>
      <pubDate>Thu, 1 Sep 2022 11:57:09 +0900</pubDate>
    </item>
    <item>
      <title>비전공자 개발자 취업 후 5개월 차 느낀점</title>
      <link>https://babycoder05.tistory.com/entry/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%97%85-%ED%9B%84-5%EA%B0%9C%EC%9B%94-%EC%B0%A8-%EB%8A%90%EB%82%80%EC%A0%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;약 6개월 간의 치열한 공부를 마치고 2022년 5월 초, 드디어 개발자로 첫 회사에 온보딩하게 되었다. 3개월 간의 수습 기간도 잘 버텨내서 정규직이 되었다. 그래서 지금 쯤에 나의 과거를 되돌아보고 미래를 계획하는 시간이 필요하다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 개발자로 전직하고자 했던 이유는 개발이 무진장 재밌어서가 아니라 &lt;u&gt;&lt;b&gt;미래에 대한 불안감&lt;/b&gt;&lt;/u&gt; 때문이었다. 이전에는 패션회사에서 근무했기 때문에 '내가 몇 살까지 이 일을 할 수 있을까'에 대한 불안감, 그리고 여러가지 이유로 일에 &lt;u&gt;&lt;b&gt;보람을 느끼지도 못했다&lt;/b&gt;&lt;/u&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위의 이유들로 인해 이직한 결과 현재의 직업 만족도를 생각해보자면, 50% 정도인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 이직 이유였던 불안감에 대해서 얘기해보자면, &lt;u&gt;&lt;b&gt;개발자가 되어도 불안감은 여전하다는 것&lt;/b&gt;&lt;/u&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 사무직의 경우, 업무 성과는 거의 근면성실함으로 판가름 되어진다. (패션 쪽에서 일했지만 디자이너가 아니었기 때문에 내 이전 직업은 일반 사무직에 가까웠다.) 누구나 숙련도가 쌓이면 이 사람이나 저 사람이나 비슷한 업무 성과를 내기 때문에 나의 업무 성과를 가지고 연봉 협상을 하는 것이 어렵다. (물론, 어느 분야에나 천상계의 업무 퍼포먼스를 내시는 분들은 있다.) 하지만 연봉이 큰 폭으로 오르진 않더라도 연차가 쌓이면 연봉이 웬만큼은 오르는 구조다. 그러나 개발자는 실력에 따라, 회사의 성장에 얼마나 기여를 했느냐에 따라 업무 성과가 확연하게 보여지기 때문에 그 결과에 따라 나의 몸값을 올리기 쉽기도 하고 가만히 머물러 있으면 도태되는 직종이다. 이것이 장점이기도 하고 힘든 점이라는 생각이 무척 든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'니가 열심히 한 만큼 보상받는 게 왜 힘들어? 나약한 소리 하지마!' 라고 누군가는 생각할 수 있다. 사실 묵묵하게 열심히 공부해서 실력을 끌어올리면 전혀 문제가 되지 않는다. 그러나 현실은 생각한 것과 달랐다. 초반 정규직이 되기 전 3개월 동안은 정말 퇴근하고서도 공부하고 주말에도 공부하고 무척 열심히 했다. 그러나 사람은 기계가 아닌지라 내가 해낼 수 있는 열심의 정도를 초과하자 번아웃이 찾아왔다. 무기력해지고 아무것도 하기 싫고 누워있고만 싶고... (실제로 누워서 유튜브만 보는 시간이 늘었다.) 개발자는 평생 공부해야 하는 직업이라던데, 공부를 하지 않으면 죄책감이 자꾸만 들고...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 일을 하면 할 수록 비전공자로서 기초지식이 부족함을 계속해서 느끼니까 자신감도 점점 줄어들었다. 나는 왜 이런 것도 모를까 자책하게 됐다. 이제 겨우 5개월차 개발자이긴 하지만 나는 앞으로 큰 이변이 생기지 않는 한 늙어죽기 직전까지는 개발자를 하려고 생각했기 때문에 3년차, 5년차, 10년차가 되었을 때 나에게 기대하는 퍼포먼스만큼 내가 실력을 갖출 수 있을까에 대한 불안감이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 이직 이유였던, 일에 대한 보람에 대해서 얘기해보자면, 어떤 서비스를 만드느냐에 따라 달린 것 같기도 하고 그냥 단순히 무언가를 만들어냈다는 것만으로도 약간의 성취감을 느낄 수도 있다. &lt;u&gt;&lt;b&gt;보람은 내가 느끼기 나름이다&lt;/b&gt;&lt;/u&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 어쨌든 무언가를 만들어내기 때문에 그것만 가지고도 성취감을 느끼는 것 같다. 새로운 기술을 써봤는데 잘 적용을 시켜서 좋은 결과물이 나왔을 때도 성취감이 느껴진다. 물론 내가 만든 서비스를 많은 사람들이 사용하는 것은 정말 뿌듯한 일이 될 것 같다. 꼭 그렇지 않더라도 공부해서 성장하고 할 수 있는 것들이 조금씩 느는 것들 만으로도 보람과 만족감은 충분히 느낄 수 있는 직업인 것 같다. 물론 그에 따라 연봉도 따라와 준다면 더할 나위없이 땡큐지ㅋㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 5개월차 신입으로 부족함도 많이 느끼고 번아웃이 찾아와서 좀 무기력해졌는데, 이렇게 한번 뒤를 돌아보면서 앞으로 어떤 개발자로 성장해 나가야 할지, 나의 공부 로드맵은 어떻게 그려나가야 할 지 진지하게 고민해볼 타이밍인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날씨가 선선해졌는데, 조깅도 좀 하고 건강도 챙기면서 너무 조급해하지 말고 앞으로 나아가자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캠핑에 요즘 맛을 들였는데, 날씨 좋을 때 캠핑도 부지런히 다녀야지!&lt;/p&gt;</description>
      <category>생각 끄적이기</category>
      <category>개발자</category>
      <category>비전공자</category>
      <category>취업 후 느낀점</category>
      <category>프론트엔드</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/81</guid>
      <comments>https://babycoder05.tistory.com/entry/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%97%85-%ED%9B%84-5%EA%B0%9C%EC%9B%94-%EC%B0%A8-%EB%8A%90%EB%82%80%EC%A0%90#entry81comment</comments>
      <pubDate>Thu, 1 Sep 2022 11:46:49 +0900</pubDate>
    </item>
    <item>
      <title>React Input에 defaultValue를 key와 함께 설정하기 - 에러 기록</title>
      <link>https://babycoder05.tistory.com/entry/React-Input%EC%97%90-defaultValue%EB%A5%BC-key%EC%99%80-%ED%95%A8%EA%BB%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-%EC%97%90%EB%9F%AC-%EA%B8%B0%EB%A1%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 만난 에러는 폼 양식 수정 페이지를 만들면서 마주했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폼 양식이 있고, 비동기 API로 기존 정보를 불러와 input 텍스트 타입 안에 defaultValue를 설정하는 것이었다. 당연히 수정이 가능해야 하므로 value로 설정하지 않고, defaultValue로 설정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플로우는 이러하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;useParams로 수정할 아이템의 id 값을 읽어들인다.&lt;/li&gt;
&lt;li&gt;redux-saga를 이용해 비동기 API에 id 값을 넘겨 기존 정보를 요청한다.&lt;/li&gt;
&lt;li&gt;redux의 상태 값에 받은 정보를 업데이트 하면 상태가 업데이트 되므로 리렌더링이 일어나 defaultValue 값에 설정해 둔 상태가 리렌더링 되어 기존 정보를 input 값에 보여준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 나의 시나리오였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 웬걸 defaultValue가 불러와지지 않는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오리지널 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659611007139&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect } from &quot;react&quot;;
import { useDispatch, useSelector } from &quot;react-redux&quot;;
import { itemInfoInit } from &quot;modules/List&quot;;
import { useParams } from &quot;react-router-dom&quot;;

export default function Example() {
  const { id } = useParams();
  const dispatch = useDispatch();
  const { itemInfo } = useSelector(state =&amp;gt; state.list);

  useEffect(() =&amp;gt; {
    dispatch(itemInfoInit(id));
  }, [dispatch, id]);

  return (
    &amp;lt;form&amp;gt;
      &amp;lt;ul className=&quot;form-list&quot;&amp;gt;
        &amp;lt;li className=&quot;form-item&quot;&amp;gt;
          &amp;lt;label className=&quot;form-label&quot;&amp;gt;제목&amp;lt;/label&amp;gt;
          &amp;lt;input type=&quot;text&quot; name=&quot;title&quot; id=&quot;title&quot; placeholder=&quot;제목을 입력해주세요.&quot;
            defaultValue={itemInfo.title}
          /&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li className=&quot;form-item&quot;&amp;gt;
          &amp;lt;label className=&quot;form-label&quot;&amp;gt;내용&amp;lt;/label&amp;gt;
          &amp;lt;textarea name=&quot;content&quot; id=&quot;content&quot; placeholder=&quot;내용을 입력해주세요.&quot;
            defaultValue={itemInfo.content}
          &amp;gt;&amp;lt;/textarea&amp;gt;
        &amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/form&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법을 찾기 위해 서치를 하던 중 다음과 같은 글을 읽게 되었다.&lt;/p&gt;
&lt;figure id=&quot;og_1659611117925&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;React input defaultValue doesn't update with state&quot; data-og-description=&quot;I'm trying to create a simple form with react, but facing difficulty having the data properly bind to the defaultValue of the form. The behavior I'm looking for is this: When I open my page, the Text input field should be filled in with th&quot; data-og-host=&quot;www.querythreads.com&quot; data-og-source-url=&quot;https://www.querythreads.com/react-input-default-value-doesn-t-update-with-state/&quot; data-og-url=&quot;https://querythreads.com/react-input-default-value-doesn-t-update-with-state/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/CasJM/hyPkBhCrVW/iVY2BAXqImsYIg2ltu3mgK/img.png?width=2880&amp;amp;height=1232&amp;amp;face=0_0_2880_1232&quot;&gt;&lt;a href=&quot;https://www.querythreads.com/react-input-default-value-doesn-t-update-with-state/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.querythreads.com/react-input-default-value-doesn-t-update-with-state/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/CasJM/hyPkBhCrVW/iVY2BAXqImsYIg2ltu3mgK/img.png?width=2880&amp;amp;height=1232&amp;amp;face=0_0_2880_1232');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;React input defaultValue doesn't update with state&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I'm trying to create a simple form with react, but facing difficulty having the data properly bind to the defaultValue of the form. The behavior I'm looking for is this: When I open my page, the Text input field should be filled in with th&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.querythreads.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1659612155766&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;How to fix React input defaultValue doesn't update with state with JavaScript? - The Web Dev&quot; data-og-description=&quot;Spread the love Related Posts How to update array state in React Native?Sometimes, we want to update array state in React Native. In this article, we'll look&amp;hellip; How to fix input losing focus when rerendering with React?Sometimes, we want to fix input losin&quot; data-og-host=&quot;thewebdev.info&quot; data-og-source-url=&quot;https://thewebdev.info/2022/05/12/how-to-fix-react-input-defaultvalue-doesnt-update-with-state-with-javascript/&quot; data-og-url=&quot;https://thewebdev.info/2022/05/12/how-to-fix-react-input-defaultvalue-doesnt-update-with-state-with-javascript/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://thewebdev.info/2022/05/12/how-to-fix-react-input-defaultvalue-doesnt-update-with-state-with-javascript/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://thewebdev.info/2022/05/12/how-to-fix-react-input-defaultvalue-doesnt-update-with-state-with-javascript/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;How to fix React input defaultValue doesn't update with state with JavaScript? - The Web Dev&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spread the love Related Posts How to update array state in React Native?Sometimes, we want to update array state in React Native. In this article, we'll look&amp;hellip; How to fix input losing focus when rerendering with React?Sometimes, we want to fix input losin&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;thewebdev.info&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하자면, defaultValue 는 initial load 즉, 맨 처음 로드를 위한 것으로 별도의 요청을 하지 않는 한 리렌더링이 되지 않는다. 그래서 별도의 요청 즉, 렌더링이 일어나도록 조건을 걸려면 key 값을 주어서 변경을 인식하도록 만들면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;수정된 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659612245160&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect } from &quot;react&quot;;
import { useDispatch, useSelector } from &quot;react-redux&quot;;
import { itemInfoInit } from &quot;modules/List&quot;;
import { useParams } from &quot;react-router-dom&quot;

export default function Example() {
  const { id } = useParams();
  const dispatch = useDispatch();
  const { itemInfo } = useSelector(state =&amp;gt; state.list);

  useEffect(() =&amp;gt; {
    dispatch(itemInfoInit(id));
  }, [dispatch, id]);

  return (
    &amp;lt;form&amp;gt;
      &amp;lt;ul className=&quot;form-list&quot;&amp;gt;
        &amp;lt;li className=&quot;form-item&quot;&amp;gt;
          &amp;lt;label className=&quot;form-label&quot;&amp;gt;제목&amp;lt;/label&amp;gt;
          &amp;lt;input type=&quot;text&quot; name=&quot;title&quot; id=&quot;title&quot; placeholder=&quot;제목을 입력해주세요.&quot;
            defaultValue={itemInfo.title}
            key={itemInfo.title}
          /&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li className=&quot;form-item&quot;&amp;gt;
          &amp;lt;label className=&quot;form-label&quot;&amp;gt;내용&amp;lt;/label&amp;gt;
          &amp;lt;textarea name=&quot;content&quot; id=&quot;content&quot; placeholder=&quot;내용을 입력해주세요.&quot;
            defaultValue={itemInfo.content}
            key={itemInfo.content}
          &amp;gt;&amp;lt;/textarea&amp;gt;
        &amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/form&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 코드를 수정했더니 defaultValue가 잘 불러와졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 해결 끝!!&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>DefaultValue</category>
      <category>input</category>
      <category>key</category>
      <category>react</category>
      <category>state</category>
      <category>Update</category>
      <category>리액트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/80</guid>
      <comments>https://babycoder05.tistory.com/entry/React-Input%EC%97%90-defaultValue%EB%A5%BC-key%EC%99%80-%ED%95%A8%EA%BB%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-%EC%97%90%EB%9F%AC-%EA%B8%B0%EB%A1%9D#entry80comment</comments>
      <pubDate>Thu, 4 Aug 2022 20:26:14 +0900</pubDate>
    </item>
    <item>
      <title>Form 양식 &amp;lt;button&amp;gt; 태그 submit 방지</title>
      <link>https://babycoder05.tistory.com/entry/Form-%EC%96%91%EC%8B%9D-button-%ED%83%9C%EA%B7%B8-submit-%EB%B0%A9%EC%A7%80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;form 태그로 감싸진 양식 안에 button 태그가 있을 경우, 버튼을 클릭하면 자동으로 폼 양식의 submit 이벤트가 발동된다. 이걸 preventDefault 로 막았었는데, 아예 그 버튼이 submit 버튼이 아니라면, 더 깔끔하게 submit 을 방지할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1658393638425&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form&amp;gt;
	&amp;lt;button type=&quot;button&quot;&amp;gt;이 버튼은 제출 버튼이 아닙니다.&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 type 속성에 button 이라고 명시해주면 끝!! 이후 onclick 이벤트를 걸더라도 submit 되지 않고 지정해준 이벤트가 발동된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝!!&lt;/p&gt;</description>
      <category>코딩 공부 일지/HTML5</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/79</guid>
      <comments>https://babycoder05.tistory.com/entry/Form-%EC%96%91%EC%8B%9D-button-%ED%83%9C%EA%B7%B8-submit-%EB%B0%A9%EC%A7%80#entry79comment</comments>
      <pubDate>Thu, 21 Jul 2022 17:55:24 +0900</pubDate>
    </item>
    <item>
      <title>인코딩 / Base64 / MIME Type</title>
      <link>https://babycoder05.tistory.com/entry/%EC%9D%B8%EC%BD%94%EB%94%A9-Base64-MIME-Type</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 프로젝트를 하면서 웹에서 이미지 파일을 업로드하고 다운로드 할 수 있는 기능들을 만들다보니 Base64와 MIME Type에 대해 많이 접하게 되었다. 한 번 제대로 정리해두면 좋을 것 같아 정리해보려고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Base64 란?&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 인코딩&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base64에 대해 알기 전에 먼저 인코딩에 대해 알 필요가 있다. 동영상 인코딩 등 흔하게 접하는 용어지만, 정확히 인코딩이 무엇을 뜻하는 말인지 몰랐다. 컴퓨터가 0과 1의 이진법으로 구성되었다는 것은 컴퓨터 전공자가 아니더라도 한 번 씩은 들어봤던 것 같다. 우리가 컴퓨터 혹은 전자기기로 접하는 모든 이미지, 동영상, 사운드 파일은 0과 1로 이루어진 데이터인데, 모두 컴퓨터가 이해하는 방식으로 인코딩된 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #1a5490;&quot;&gt;인코딩 = 컴퓨터가 이해하는 방식으로 바꾸기 = 0과 1로 이루어진 데이터로 만들기&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 사용하는 문자를 컴퓨터는 이해할 수 없기 때문에 문자열 인코딩을 통해 인코딩 된 문자를 컴퓨터가 인식하고 처리한다. 이 인코딩 방식은 한 가지가 있는 것이 아니고 여러 가지 방법이 있다. 'utf-8'은 많이 사용하는 문자열 인코딩 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코딩은 인코딩의 반댓말로 컴퓨터가 이해하는 방식으로 구성된 파일을 사람이 이해할 수 있는 형태로 바꿔주는 것을 말한다. 인코딩 기능과 디코딩 기능을 같이 가지고 있는 것을 코덱(Codec, Code + Decode 의 합성어)이라고 한다. 우리가 동영상 파일을 재생할 때 적절한 코덱이 없으면 재생할 수 없는 이유가 바로 여기에 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Base64&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base64는 직역하면 64진법이라는 뜻으로, 8비트 이진 데이터(예를 들어 실행파일이나, ZIP 파일 등)를 문자 인코딩에 영향을 받지 않는 공통 ASCII(아스키) 영역의 문자들로만 이루어진 일련의 문자열로 바꾸는 인코딩 방식을 가리키는 개념이다. 이 문자 인코딩에 영향을 받지 않는 공통 ASCII 영역의 문자를 사용한다는 특성 덕분에 시스템이 상이한 상호 간에 데이터를 주고 받을 때 안전하다는 특성이 있다. (일부 ASCII 는 시스템 간 데이터를 전달하기에 안전하지 않음.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Base64는 HTML 또는 Email과 같이 문자를 위한 Media에 이진 데이터(Binary Data)를 포함해야 할 필요가 있을 때, 포함된 Binary Data가 시스템에 영향을 받지 않고 독립적으로 동일하게 전송 또는 저장되는 것을 보장하기 위해 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base64는 어떤 문자와 기호를 쓰느냐에 따라 여러 변종이 있지만, 잘 알려진 것은 모두 처음 62개는 알파벳 A-Z, a-z와 0-9를 사용하고 있으며 마지막 두 개를 어떤 기호를 쓰느냐의 차이만 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcZrBJ/btrHMbsTOmD/SBU1kpkodrosoMNiKGMIw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcZrBJ/btrHMbsTOmD/SBU1kpkodrosoMNiKGMIw1/img.png&quot; data-alt=&quot;Base64 색인표&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcZrBJ/btrHMbsTOmD/SBU1kpkodrosoMNiKGMIw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcZrBJ%2FbtrHMbsTOmD%2FSBU1kpkodrosoMNiKGMIw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;900&quot; height=&quot;698&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Base64 색인표&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MIME Type이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MIME은 Multipurpose Internet Mail Extensions의 약자로 이메일과 함께 동봉할 파일을 텍스트 문자로 변환해서 이메일 시스템을 통해 전달하기 위해 개발되었다. MIME Type은 이러한 다양한 양식의 파일을 알려주기 위한 매커니즘으로, 수많은 종류의 파일 형태가 있으므로 많은 MIME Type이 존재한다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 일반적인 구조&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1658312751903&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type/subtype&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MIME Type은 '/'로 구분된 두 개의 문자열인 타입과 서브타입으로 구성된다. 스페이스는 허용되지 않는다. MIME Type은 대소문자를 구분하지 않지만 전통적으로 소문자로 쓰여진다.&lt;/p&gt;
&lt;pre id=&quot;code_1658312859968&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MIME Type 예시
text/plain
text/html
image/jpeg
image/png
audio/mpeg
audio/ogg
audio/*
video/mp4
application/octet-stream&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 공부 일지/ETC</category>
      <category>base64</category>
      <category>Decoding</category>
      <category>encoding</category>
      <category>MIME</category>
      <category>mime type</category>
      <category>디코딩</category>
      <category>베이스64</category>
      <category>인코딩</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/78</guid>
      <comments>https://babycoder05.tistory.com/entry/%EC%9D%B8%EC%BD%94%EB%94%A9-Base64-MIME-Type#entry78comment</comments>
      <pubDate>Wed, 20 Jul 2022 19:30:04 +0900</pubDate>
    </item>
    <item>
      <title>Redux-Saga 사용방법 정리</title>
      <link>https://babycoder05.tistory.com/entry/Redux-Saga-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;나는 어떤 일을 할 때, 그 일을 왜 하는지가 이해가 되어야 한다. Redux-Saga를 처음 배울 때 비동기를 처리하기 위해 왜 Redux-Saga를 사용할까 이해가 되지 않았다. 컴포넌트 내부에서 비동기 로직을 실행하고 난 뒤에 디스패치를 하면 되는 거 아닌가? 라고 생각했었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;왜 Redux-Saga를 사용하는가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인 패턴이라고 들어보았는가? 진짜 잘하는 개발자는 소프트웨어 설계부터 비즈니스 로직이 안정적으로 구현되고 가독성 좋은 코드를 짜기 위해 고민한다고 한다. Redux-Saga는 갑자기 어디서 뿅하고 튀어나온 게 아니라 이 디자인 패턴을 고민하다가 나온 결과물이라고 할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Saga 패턴&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Saga 패턴에 대한 설명을 찾아보면, &quot;saga 패턴은 분산 애플리케이션의 일관성을 유지하고 여러 마이크로서비스 간의 트랜잭션을 조정하여 데이터 일관성을 유지하는 데 도움이 되는 장애 관리 패턴입니다. 마이크로서비스는 모든 트랜잭션에 대한 이벤트를 게시하고 다음 트랜잭션은 이벤트의 결과에 따라 시작됩니다.&quot; 라고 되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;이게 무슨 말이야???&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 이해한 대로 설명해보자면, DB가 여러 개로 분산되어 있는데(e.g. user 정보를 관리하는 DB, 상품 정보를 관리하는 DB 등) 동시에 여러 DB를 업데이트 해야되는 상황일 때, 한 곳에서 관리해서 전체 서비스의 데이터 일관성을 유지하는 방식이 적용된 것이 Saga 패턴이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Saga 패턴에 대해 이해하고 싶으신 분들은 아래 글을 읽어보면 좋을 것 같다.&lt;/p&gt;
&lt;figure id=&quot;og_1657703638552&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;마이크로서비스 분산 트랜잭션 관리 (Saga Pattern)&quot; data-og-description=&quot;개요 앞선 포스팅에서 마이크로서비스 분산DB 환경에서 고려되어야 할 사항에 대해 살펴보았다. 자세한 내용은 아래 포스팅을 참고하기 바란다. 마이크로서비스&amp;nbsp;분산&amp;nbsp;트랜잭션&amp;nbsp;관리&amp;nbsp;(2Phase&amp;nbsp;Co&quot; data-og-host=&quot;waspro.tistory.com&quot; data-og-source-url=&quot;https://waspro.tistory.com/735&quot; data-og-url=&quot;https://waspro.tistory.com/735&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/d8mFmU/hyO4HcgqdR/F5ZMZr6rIaspjKy55M3Kg1/img.png?width=800&amp;amp;height=459&amp;amp;face=0_0_800_459,https://scrap.kakaocdn.net/dn/jMlwU/hyO4GxFc2o/6S2xDFPWeyTCMEUoQUjjr1/img.png?width=800&amp;amp;height=459&amp;amp;face=0_0_800_459,https://scrap.kakaocdn.net/dn/sEKVv/hyO4KmwPlc/KB4KBn0kzgJkt7ZwKad391/img.png?width=1747&amp;amp;height=650&amp;amp;face=0_0_1747_650&quot;&gt;&lt;a href=&quot;https://waspro.tistory.com/735&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://waspro.tistory.com/735&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/d8mFmU/hyO4HcgqdR/F5ZMZr6rIaspjKy55M3Kg1/img.png?width=800&amp;amp;height=459&amp;amp;face=0_0_800_459,https://scrap.kakaocdn.net/dn/jMlwU/hyO4GxFc2o/6S2xDFPWeyTCMEUoQUjjr1/img.png?width=800&amp;amp;height=459&amp;amp;face=0_0_800_459,https://scrap.kakaocdn.net/dn/sEKVv/hyO4KmwPlc/KB4KBn0kzgJkt7ZwKad391/img.png?width=1747&amp;amp;height=650&amp;amp;face=0_0_1747_650');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;마이크로서비스 분산 트랜잭션 관리 (Saga Pattern)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요 앞선 포스팅에서 마이크로서비스 분산DB 환경에서 고려되어야 할 사항에 대해 살펴보았다. 자세한 내용은 아래 포스팅을 참고하기 바란다. 마이크로서비스&amp;nbsp;분산&amp;nbsp;트랜잭션&amp;nbsp;관리&amp;nbsp;(2Phase&amp;nbsp;Co&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;waspro.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux-Saga는 여러 작업을 병렬로 분기하고 실행 중인 작업을 취소하거나 에러가 발생했을 때 그에 대한 처리를 할 수 있도록 도와준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Generator 함수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generator 함수는 잘 쓰이지 않기도 하고 처음 접할 때는 생소해서 이해가 잘 되지 않는데, Redux-Saga가 Generator 함수를 기반으로 만들어졌기 때문에 러닝 커브가 높다고 얘기하기도 한다. Generator 함수는 function 뒤에 *을 붙이고, yield 를 통해 실행 구간을 나눈다.&lt;/p&gt;
&lt;pre id=&quot;code_1657704227456&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* generatorFunction() {
    yield 1;
    yield 2;
    yield 3;
    return 4;
}

const generator = generatorFunction();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 함수가 있을 때, 콘솔에서 실행해보면 아래와 같은 결과를 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nrLIK/btrHcLnA0fX/k5vkTdq3d9O0GfSL9nWCr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nrLIK/btrHcLnA0fX/k5vkTdq3d9O0GfSL9nWCr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nrLIK/btrHcLnA0fX/k5vkTdq3d9O0GfSL9nWCr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnrLIK%2FbtrHcLnA0fX%2Fk5vkTdq3d9O0GfSL9nWCr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;824&quot; height=&quot;706&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;generator.next() 를 통해 다음 구간을 호출할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redux-Saga 사용방법 (로그인 구현)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React를 사용한다는 가정하에 Redux 미들웨어이므로 당연하게도 redux를 설치해주어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1657705214159&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i redux react-redux redux-saga&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;modules/login.js&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1657705673635&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { put, all, call, takeLatest } from &quot;redux-saga/effects&quot;;
import Cookies from &quot;universal-cookie&quot;;

// 액션 타입
export const LOGIN_REQUEST = 'LOGIN_REQUEST';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_FAILURE = 'LOGIN_FAILURE';
export const LOGOUT_REQUEST = 'LOGOUT_REQUEST';
export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
export const LOGOUT_FAILURE = 'LOGOUT_FAILURE';

// 액션 생성 함수
export function loginRequest(payload) {
  return {type: LOGIN_REQUEST, payload};
}
export function loginSuccess() {
  return {type: LOGIN_SUCCESS};
}
export function loginFailure(error) {
  return {type: LOGIN_FAILURE, error};
}
export function logoutRequest() {
  return {type: LOGOUT_REQUEST};
}
export function logoutSuccess() {
  return {type: LOGOUT_SUCCESS};
}
export function logoutFailure(error) {
  return {type: LOGOUT_FAILURE, error};
}

// 로그인
const cookies = new Cookies();

function* login(payload) {
  const getToken = (payload) =&amp;gt; {
    // 실제로는 payload에 들어있는 아이디와 패스워드를 post 요청으로 보내서 서버에서 검증을 받고 액세스 토큰을 얻음.
    if(payload.id &amp;amp;&amp;amp; payload.pw){
      setTimeout(() =&amp;gt; {
        return 'fake jwt accessToken';
      }, 1000);
    }
    return null;
  }
  try {
    const token = yield call(() =&amp;gt; getToken(payload.payload)); // call은 함수를 실행해줌.
    cookies.set(&quot;token&quot;, token); // 다음 실행 전에 쿠키에 값을 저장하는 등 다른 작업을 할 수 있음.
    yield put(loginSuccess()); // put은 특정 액션을 디스패치 해줌.
  } catch (err) {
    yield put(loginFailure(err));
  }
}

// 로그아웃
function* logout() {
  try {
    yield put(logoutSuccess());
    cookies.remove(&quot;token&quot;); // 쿠키 제거
  } catch (err) {
    yield put(logoutFailure(err));
  }
}

export function* getLoginSaga() {
  yield all([
  	takeLatest(LOGIN_REQUEST, login), // takelatest는 만약 동일한 디스패치 요청이 여러 번 있었다면 제일 마지막에 들어온 요청만 실행함. debounce와 동일
  	takeLatest(LOGOUT_REQUEST, logout)
  ]);
}

// 초깃값
const initiaState = {
  error: null,
  isLoggingIn: null,
};

export default function login(state = initiaState, action) {
  switch (action.type) {
    case LOGIN_REQUEST:
      return {
        ...state,
        isLoggingIn: null,
        error: null
      };  
    case LOGIN_SUCCESS:
      return {
        ...state,
        isLoggingIn: true,
        error: null,
      }
    case LOGIN_FAILURE:
      return {
        ...state,
        error: action.error
      }
    case LOGOUT_REQUEST:
      return {
        ...state,
        error: null
      }
    case LOGOUT_SUCCESS:
      return {
        ...state,
        isLoggingIn: false,
        error: null
      }
    case LOGOUT_FAILURE:
      return {
        ...state,
        error: action.error
      }   
    default:
      return state;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;modules/index.js&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1657706725327&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { combineReducers } from 'redux';
import login, { getLoginSaga } from './login';
import { all } from 'redux-saga/effects';

const rootReducer = combineReducers({ 
  login 
});

export function* rootSaga() {
  yield all([
    getLoginSaga()
  ]); // all은 배열 안의 여러 사가를 동시에 실행시켜줍니다.
}

export default rootReducer;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;index.js&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1657706837157&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import rootReducer, { rootSaga } from './modules';
import logger from 'redux-logger';
import createSagaMiddleware from '@redux-saga/core';
import { configureStore } from '@reduxjs/toolkit';

const sagaMiddleware = createSagaMiddleware(); // 사가 미들웨어를 만든다.

const store = configureStore({
  reducer: rootReducer, 
  middleware: [sagaMiddleware, logger]
});

sagaMiddleware.run(rootSaga); // 루트 사가를 실행해준다.
// 주의: 스토어 생성이 된 다음에 위 코드를 실행해야 한다.

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;Provider store={store}&amp;gt;
      &amp;lt;App /&amp;gt;
    &amp;lt;/Provider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById('root')
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redux-Saga의 주요 기능들&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- delay&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정된 시간 이후에 실행하는 Promise 객체를 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시: delay(1000)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 1초 기다리기&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- put&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 액션을 dispatch 하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시: put({ type: 'INCREMENT'})&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; INCREMENT 액션을 dispatch 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- takeEvery&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧은 시간 안에 여러 번 들어오는 모든 액션에 대해 특정 작업을 처리해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시: takeEvery(INCREASE_ASYNC, increaseSaga)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 들어오는 모든 INCREASE_ASYNC 액션에 대해 increaseSaga 함수를 실행한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- takeLatest&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧은 시간 안에 여러 번 들어오는 모든 액션 중에 가장 마지막으로 들어온 액션만 수행한다. (이전 액션은 취소처리)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시: takeLatest(INCREASE_ASYNC, increaseSaga)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 들어오는 모든 INCREASE_ASYNC 중 가장 마지막 액션에 대해 increaseSaga 함수를 실행한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- call&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 첫 번째 파라미터는 함수, 나머지 파라미터는 해당 함수에 넣은 인수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시: call(() =&amp;gt; func(), 1000)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 1초 뒤에 func 함수를 실행한다. 두 번째 파라미터가 없을 경우 기본 값은 0이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- all&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;all 함수를 사용해서 제너레이터 함수를 배열의 형태로 넣어주면, 제너레이터 함수들이 병행적으로 동시에 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시: yield all([testSaga1(), testSaga2()])&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; testSaga1과 testSaga2가 동시에 실행되고, 모두 resolve 될 때까지 기다린다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>Generator</category>
      <category>redux</category>
      <category>redux-saga</category>
      <category>saga 패턴</category>
      <category>리덕스</category>
      <category>리덕스 사용법</category>
      <category>제너레이터 함수</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/77</guid>
      <comments>https://babycoder05.tistory.com/entry/Redux-Saga-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC#entry77comment</comments>
      <pubDate>Wed, 13 Jul 2022 19:35:28 +0900</pubDate>
    </item>
    <item>
      <title>HTTP와 REST API</title>
      <link>https://babycoder05.tistory.com/entry/HTTP%EC%99%80-REST-API</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HTTP 프로토콜&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;HTTP(Hypertext Transfer Protocol)는 인터넷을 이용해서 데이터를 주고받는 통신 규약이다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 주고받기 때문에 request-response Protocol 이기도 하다. HTTP는 모든 웹 개발자라면 당연히 알고 있어야할 필수 상식이지만, 많은 프론트엔드 개발자들이 놓치기 쉬운 부분이기도 하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w1fGP/btrGP5AHIdZ/OQLprBCIrOOoxbZSoRL3tk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w1fGP/btrGP5AHIdZ/OQLprBCIrOOoxbZSoRL3tk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w1fGP/btrGP5AHIdZ/OQLprBCIrOOoxbZSoRL3tk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw1fGP%2FbtrGP5AHIdZ%2FOQLprBCIrOOoxbZSoRL3tk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;230&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트는 URL을 통해 서버에 데이터 요청을 하고 서버는 그 응답으로 데이터를 클라이언트에게 보내준다. 그 데이터를 받아 예쁘게 보여주는 역할을 하는 것이 바로 웹 브라우저다. HTTP는 1989년부터 개발이 시작되었고, 1994년에 HTTPS라는 보안이 더해진 버전이 출시가 되었다. 그리고 정식버전으로 출시가 된 것은 1997년이다. 따지고 보면 인터넷 통신의 역사가 그리 길지 않다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2486&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDwdig/btrGUrBAt98/mmMZCzsaW3K6mwkacOvyF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDwdig/btrGUrBAt98/mmMZCzsaW3K6mwkacOvyF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDwdig/btrGUrBAt98/mmMZCzsaW3K6mwkacOvyF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDwdig%2FbtrGUrBAt98%2FmmMZCzsaW3K6mwkacOvyF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2486&quot; height=&quot;74&quot; data-origin-width=&quot;2486&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷 주소창에 맨 왼쪽에 자물쇠 모양이 있는 경우, HTTPS 프로토콜을 이용하여 통신을 하고 있는 것이다. HTTPS 프로토콜을 이용해서 통신을 할 경우 데이터가 암호화되어 있기 때문에 해킹의 위험이 덜 하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;요청(Request)과 REST API&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dB80Uh/btrGQDwYnNT/mfYplJUMRnxRVYOnaKxDMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dB80Uh/btrGQDwYnNT/mfYplJUMRnxRVYOnaKxDMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dB80Uh/btrGQDwYnNT/mfYplJUMRnxRVYOnaKxDMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdB80Uh%2FbtrGQDwYnNT%2FmfYplJUMRnxRVYOnaKxDMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;565&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 프로토콜을 이용해서 서버에 요청(Request)를 보낼 때, REST API를 이용하여 요청을 보내게 된다. REST API에 대해서 많이들 얘기하지만, 다들 너무 어렵게 설명해서 이해가 잘 되지 않았는데, 결국 내가 이해한 개념은 인터넷을 이용해서 서버에서 내가 원하는 정보를 받고 싶다고 요청하는 한 방식이다. '어떤 정보를 어떻게 받기를 원하는데?' 에 관해 사람의 언어로 아무렇게나 말할 수 없으니 정해진 약속을 두고 그 방법대로 말하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- REST API의 작동 과정&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 API 문서에 따라 서버가 이해하는 방식으로 요청 형식으로 전송한다.&lt;/li&gt;
&lt;li&gt;서버가 클라이언트를 인증하고 해당 요청을 수행할 수 있는 권한이 클라이언트에 있는지 확인한다.&lt;/li&gt;
&lt;li&gt;서버가 요청을 수신하고 내부적으로 처리한다.&lt;/li&gt;
&lt;li&gt;서버가 클라이언트에 응답을 반환한다. 응답에는 요청이 성공했는지 여부를 클라이언트에 알려주는 정보(200, 400 등과 같은 코드)가 포함된다. 응답에는 클라이언트가 요청한 모든 정보도 포함된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- REST API로 요청을 하기 위해 포함해야할 것&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;리소스 식별자: 리소스를 가진 대상의 식별자 즉, 어떤 서버에 요청을 할 것인지를 말하는데, REST API에서는 URL을 통해 리소스 식별자를 전달한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;메서드: 어떤 요청을 할 것인지에 관한 것이다. 받을 것인지(GET), 보낼 것인지(POST), 기존 서버에 있는 데이터를 수정할 것인지(PUT), 삭제할 것인지(DELETE) 등이 있다.&lt;/li&gt;
&lt;li&gt;HTTP 헤더: 요청 헤더는 클라이언트와 서버 간에 주고 받는 메타 데이터가 포함되는 곳으로, URL의 세부 경로를 지정하거나 추가적인 query를 전달하거나 인증 토큰을 담아 전달하는 곳이다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;응답(Response)과 REST API&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API를 이용한 통신에서 응답에는 다음과 같은 것들을 포함한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- REST API로 받은 응답에 포함해야할 것&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;상태표시줄: 요청이 성공했는지 실패했는지를 알려주는 세자리의 코드를 담고 있다. 대표적으로 200은 성공, 201은 POST 요청 성공, 400은 서버가 처리할 수 없는 잘못된 요청, 404는 리소스를 찾을 수 없다는 뜻이다.&lt;/li&gt;
&lt;li&gt;메시지 본문: 서버에 요청해서 받은 데이터가 담겨있다. 대표적으로 JSON 형식의 데이터를 받을 수 있다.&lt;/li&gt;
&lt;li&gt;헤더: 응답에 대한 메타 데이터, 예를 들어, 인코딩, 날짜 및 콘텐츠 유형과 같은 정보를 포함한다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>코딩 공부 일지/Browser &amp;amp; Network</category>
      <category>http</category>
      <category>request-response</category>
      <category>REST API</category>
      <category>통신규약</category>
      <category>프로토콜</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/76</guid>
      <comments>https://babycoder05.tistory.com/entry/HTTP%EC%99%80-REST-API#entry76comment</comments>
      <pubDate>Sat, 9 Jul 2022 16:59:35 +0900</pubDate>
    </item>
    <item>
      <title>소프트웨어 라이센스에 대하여 / MIT License</title>
      <link>https://babycoder05.tistory.com/entry/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EB%9D%BC%EC%9D%B4%EC%84%BC%EC%8A%A4%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;소프트웨어 라이센스란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어에는 여러 가지가 해당될 수 있는데, 그 중에서도 개발자가 사용하는 소프트웨어는 라이브러리, 프레임워크 같은 것들이 있다. 모든 라이브러리와 프레임워크는 그것들의 소유권을 가진 저작권자가 있고, 라이선스의 종류에 따라 사용 가능 범위가 다르다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ISC License&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;ISC 라이선스는 Internet Systems Consortium(ISC)에 허용된 free Software license 로, ISC에서 개발한 OpenBSD베이스로 개발된 소프트웨어 릴리즈를 위해서 사용되는 라이선스이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1657346283262&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;ISC License&quot; data-og-description=&quot;ISC 라이선스는 Internet Systems Consortium(ISC)에 허용된 free Software license 로, ISC에서 개발한 OpenBSD베이스로 개발된 소프트웨어 릴리즈를 위해서 사용되는 라이선스이다.&quot; data-og-host=&quot;www.olis.or.kr&quot; data-og-source-url=&quot;https://www.olis.or.kr/license/Detailselect.do?lId=1074&quot; data-og-url=&quot;https://www.olis.or.kr/license/Detailselect.do?lId=1074&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.olis.or.kr/license/Detailselect.do?lId=1074&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.olis.or.kr/license/Detailselect.do?lId=1074&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;ISC License&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ISC 라이선스는 Internet Systems Consortium(ISC)에 허용된 free Software license 로, ISC에서 개발한 OpenBSD베이스로 개발된 소프트웨어 릴리즈를 위해서 사용되는 라이선스이다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.olis.or.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 209px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;복제, 배포, 수정의 권한 &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;허용&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;배포시 라이선스 사본 첨부&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;저작권 고지사항 또는 Attribution 고지사항 유지&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;배포시 소스코드 제공의무(Reciprocity)와 범위&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;명시되어있지않음&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;조합저작물(Lager Work) 작성 및 타 라이선스 배포 허용&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;조건부 가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;수정 시 수정내용 고지&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;명시되어있지않음&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;명시적 특허 라이선스의 허용&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;라이선시가 특허소송 제기 시 라이선스 종료&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이름, 상표, 상호에 대한 사용제한&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;보증의 부인&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;책임의&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 제한&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;명시되어있지않음&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Apache License&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;아파치 라이선스는 아파치 웹서버의 배포를 위해 만들어진 라이선스이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;아파치 재단이나 재단의 프로젝트에 의해서 만들어진 모든 소프트웨어는 현재 Apache License 2.0에 의해 배포되고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1657346764829&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Apache License 2.0&quot; data-og-description=&quot;아파치 라이선스는 아파치 웹서버의 배포를 위해 만들어진 라이선스이다. 아파치 재단이나 재단의 프로젝트에 의해서 만들어진 모든 소프트웨어는 현재 Apache License 2.0에 의해 배포되고 있다.&quot; data-og-host=&quot;www.olis.or.kr&quot; data-og-source-url=&quot;https://www.olis.or.kr/license/Detailselect.do?lId=1002&quot; data-og-url=&quot;https://www.olis.or.kr/license/Detailselect.do?lId=1002&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.olis.or.kr/license/Detailselect.do?lId=1002&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.olis.or.kr/license/Detailselect.do?lId=1002&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Apache License 2.0&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;아파치 라이선스는 아파치 웹서버의 배포를 위해 만들어진 라이선스이다. 아파치 재단이나 재단의 프로젝트에 의해서 만들어진 모든 소프트웨어는 현재 Apache License 2.0에 의해 배포되고 있다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.olis.or.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 226px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;복제,&lt;span&gt;&amp;nbsp;&lt;/span&gt;배포, 수정의 권한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;허용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;배포시 라이선스 사본 첨부&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;저작권 고지사항 또는 Attribution 고지사항 유지&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;배포시 소스코드 제공의무(Reciprocity)와 범위&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;명시되어있지않음&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;조합저작물(Lager Work) 작성 및 타 라이선스 배포 허용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;수정 시 수정내용 고지&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;명시되어있지않음&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;명시적 특허 라이선스의 허용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;라이선시가 특허소송 제기 시 라이선스 종료&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;명시되어있지않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;이름, 상표, 상호에 대한 사용제한&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;보증의 부인&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;책임의&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;제한&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MIT License&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;MIT 라이선스(MIT License)는 미국 매사추세츠 공과대학교(MIT)에서 해당 대학의 소프트웨어 공학도들을 돕기 위해 개발한 라이선스다. MIT 라이선스를 따르는 소프트웨어를 개조한 제품을 반드시 오픈 소스로 배포해야 한다는 규정이 없으며 GNU 일반 공중 라이선스의 엄격함을 피하려는 사용자들에게 인기가 있다. 이 라이선스를 따르는 대표적 소프트웨어로 X 윈도 시스템이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;복제,&lt;span&gt;&amp;nbsp;&lt;/span&gt;배포, 수정의 권한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;허용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;배포시 라이선스 사본 첨부&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;저작권 고지사항 또는 Attribution 고지사항 유지&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;배포시 소스코드 제공의무(Reciprocity)와 범위&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;명시되어있지않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;조합저작물(Lager Work) 작성 및 타 라이선스 배포 허용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;조건부 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;수정 시 수정내용 고지&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;명시되어있지않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;명시적 특허 라이선스의 허용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;명시되어있지않음&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;라이선시가 특허소송 제기 시 라이선스 종료&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;명시되어있지않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;이름, 상표, 상호에 대한 사용제한&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;명시되어있지않음&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;보증의 부인&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;책임의&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;제한&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 라이브러리를 사용하면서 가장 많이 본 라이센스가 MIT 라이센스다. MIT 라이센스의 특징은 사용자가 소프트웨어를 무상으로 제한없이 다룰 수 있다는 것이다. 쉽게 말하면, &lt;u&gt;&lt;b&gt;MIT 라이센스는 수정/배포가 자유롭고 실무적으로 상업적으로 사용할 수 있다는 뜻이다.&lt;/b&gt;&lt;/u&gt; 그러나 자유엔 책임이 따르듯이 MIT 라이센스를 가진 소프트웨어를 사용하다가 불이익을 당하더라도 저작권자에게 아무런 책임을 물을 수 없다는 것이다. 그러므로 &lt;u&gt;&lt;b&gt;라이브러리나 프레임워크를 사용할 때는 신뢰할만한 소프트웨어인지를 잘 따져보고 사용해야 한다.&lt;/b&gt;&lt;/u&gt; 특히, 실제 비즈니스를 다루는 경우라면!!&lt;/p&gt;</description>
      <category>코딩 공부 일지/ETC</category>
      <category>Apache License</category>
      <category>ISC License</category>
      <category>MIT License</category>
      <category>소프트웨어 라이센스</category>
      <category>소프트웨어 사용권</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/75</guid>
      <comments>https://babycoder05.tistory.com/entry/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EB%9D%BC%EC%9D%B4%EC%84%BC%EC%8A%A4%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC#entry75comment</comments>
      <pubDate>Sat, 9 Jul 2022 15:19:00 +0900</pubDate>
    </item>
    <item>
      <title>React 로컬 스토리지를 이용하여 하루 동안 보이지 않기 구현하기</title>
      <link>https://babycoder05.tistory.com/entry/JavaScript-%EB%A1%9C%EC%BB%AC-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%ED%95%98%EB%A3%A8-%EB%8F%99%EC%95%88-%EB%B3%B4%EC%9D%B4%EC%A7%80-%EC%95%8A%EA%B8%B0-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;세션 스토리지, 로컬 스토리지, 쿠키 등 웹 브라우저 저장소를 이용하여 다양한 정보를 저장하고 그에 따른 UI를 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 스토리지는 해당 세션이 종료될 경우에는 기록이 사라지므로, 다음 세션에서도 팝업이 보이지 않도록 기억하게 만들고 싶어서 로컬 스토리지를 이용해서 구현했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;로컬 스토리지를 이용하여 &lt;b&gt;하루&amp;nbsp;동안&amp;nbsp;보이지&amp;nbsp;않기&amp;nbsp;구현하기&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657266359349&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;today = new Date();
console.log(today.getDate());

// 1491553506653 와 같은 13자리 수를 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getTime() 메소드를 적용할 경우 해당 메소드가 실행된 바로 그 시간을 13자리로 표현한 수가 반환된다. 나열된 시간은 millisecond 즉 1/1000 초를 나타낸다. 이를 이용하여 숫자를 비교해서 boolean 값을 반환하여 팝업을 보여주고 사라지게 만들 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1657266198763&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useState } from &quot;react&quot;;

export default function Popup() {
  const [isPopupShow, setPopupShow] = useState(true);

  const closePopup = (expireDays) =&amp;gt; {
    let expire = new Date();
    expire.setTime(expire.getTime() + (expireDays * 24 * 60 * 60 * 1000));
    localStorage.setItem(&quot;popupNoShow&quot;, expire.getTime());
  }
  
  const checkPopupClose = () =&amp;gt; {
    const expireDay = localStorage.getItem(&quot;popupNoShow&quot;);
    let today = new Date();
  
    if(today.getTime() &amp;gt; expireDay) { // 이렇게 하면 localStorage에 아무것도 저장되어 있지 않을 경우 undefined 거나 null 이므로 true를 반환한다.
      return false;
    } else {
      return true;
    }
  }

  const closePopupToday = () =&amp;gt; {
    closePopup(1); // 하루 동안 표시 안 할 것임을 명시
    setPopupShow(false); // 오늘 하루 안 보기 누름과 동시에 팝업 사라짐
  };

  useEffect(() =&amp;gt; {
    checkPopupClose() ? setPopupShow(false) : setPopupShow(true);
    // 최초 컴포넌트가 마운트되었을 때, 팝업을 표시할 지 말지 조회
  }, []);

  return (
    &amp;lt;&amp;gt;
    {isPopupShow &amp;amp;&amp;amp; (
      &amp;lt;&amp;gt;
        &amp;lt;div className=&quot;popup&quot;&amp;gt;
          &amp;lt;div className='popup-close-wrap'&amp;gt;
            &amp;lt;div className='popup-noshow'
              onClick={closePopupToday}&amp;gt;
              &amp;lt;input type='checkbox' id='check' /&amp;gt;
              &amp;lt;label htmlFor='check'&amp;gt;오늘 하루 안 보기&amp;lt;/label&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div className='popup-close'
              onClick={() =&amp;gt; setPopupShow(false)}&amp;gt;
              &amp;lt;span&amp;gt;닫기&amp;lt;/span&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/&amp;gt;
      )}
    &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>JavaScript</category>
      <category>localstorage</category>
      <category>로컬스토리지</category>
      <category>자바스크립트</category>
      <category>하루동안보지않기</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/74</guid>
      <comments>https://babycoder05.tistory.com/entry/JavaScript-%EB%A1%9C%EC%BB%AC-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%ED%95%98%EB%A3%A8-%EB%8F%99%EC%95%88-%EB%B3%B4%EC%9D%B4%EC%A7%80-%EC%95%8A%EA%B8%B0-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0#entry74comment</comments>
      <pubDate>Fri, 8 Jul 2022 17:04:29 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 접속한 기기가 모바일인지 아닌지 확인하는 방법</title>
      <link>https://babycoder05.tistory.com/entry/JavaScript-%EC%A0%91%EC%86%8D%ED%95%9C-%EA%B8%B0%EA%B8%B0%EA%B0%80-%EB%AA%A8%EB%B0%94%EC%9D%BC%EC%9D%B8%EC%A7%80-%EC%95%84%EB%8B%8C%EC%A7%80-%ED%99%95%EC%9D%B8%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. 일반 PC로 접속할 경우에는 앱 다운로드 링크를 띄우지 않고, 모바일로 접속할 경우에만 앱 다운로드 링크를 띄운다던가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 일반 PC는 마우스 기반이고 모바일은 터치 기반이기 때문에 접속 기기에 따라 이벤트를 다르게 작동시킨다던가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 경우에 접속한 기기의 종류를 확인해야할 경우가 생긴다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;JavaScript&amp;nbsp;접속한&amp;nbsp;기기가&amp;nbsp;모바일인지&amp;nbsp;아닌지&amp;nbsp;확인하는&amp;nbsp;방법&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1657265772121&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const isMobile = () =&amp;gt; {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드 한 줄이면 접속한 기기가 모바일일 경우 true, 아닐 경우 false를 반환하는 함수를 구현할 수 있다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/JavaScript</category>
      <category>JavaScript</category>
      <category>Til</category>
      <category>기록</category>
      <category>모바일 PC 확인 방법</category>
      <category>자바스크립트</category>
      <category>접속한 기기</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/73</guid>
      <comments>https://babycoder05.tistory.com/entry/JavaScript-%EC%A0%91%EC%86%8D%ED%95%9C-%EA%B8%B0%EA%B8%B0%EA%B0%80-%EB%AA%A8%EB%B0%94%EC%9D%BC%EC%9D%B8%EC%A7%80-%EC%95%84%EB%8B%8C%EC%A7%80-%ED%99%95%EC%9D%B8%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry73comment</comments>
      <pubDate>Fri, 8 Jul 2022 16:38:07 +0900</pubDate>
    </item>
    <item>
      <title>React 체크박스 전체 선택/해제 기능 구현하기</title>
      <link>https://babycoder05.tistory.com/entry/React-%EC%B2%B4%ED%81%AC%EB%B0%95%EC%8A%A4-%EC%A0%84%EC%B2%B4-%EC%84%A0%ED%83%9D%ED%95%B4%EC%A0%9C-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3sCvM/btrGBifbani/SWzdh0hFHNqYDzBRqFwq21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3sCvM/btrGBifbani/SWzdh0hFHNqYDzBRqFwq21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3sCvM/btrGBifbani/SWzdh0hFHNqYDzBRqFwq21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3sCvM%2FbtrGBifbani%2FSWzdh0hFHNqYDzBRqFwq21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;486&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;체크박스 전체 선택 / 해제 기능에서 구현해야할 기능 목록&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 최상단 체크 박스 클릭 시 전체 선택 / 해제 토글 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 전체 선택 시에 하나라도 체크가 해제되면 최상단 체크 박스 선택 해제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 모든 체크박스가 선택될 경우 최상단 체크 박스 선택 활성화&lt;/p&gt;
&lt;pre id=&quot;code_1657082039325&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState } from 'react';
import styled from 'styled-components';

export default function Sample() {
  const data = [
    {id: 0, title: '선택 1'},
    {id: 1, title: '선택 2'},
    {id: 2, title: '선택 3'},
    {id: 3, title: '선택 4'}
  ];

  // 체크된 아이템을 담을 배열
  const [checkItems, setCheckItems] = useState([]);

  // 체크박스 단일 선택
  const handleSingleCheck = (checked, id) =&amp;gt; {
    if (checked) {
      // 단일 선택 시 체크된 아이템을 배열에 추가
      setCheckItems(prev =&amp;gt; [...prev, id]);
    } else {
      // 단일 선택 해제 시 체크된 아이템을 제외한 배열 (필터)
      setCheckItems(checkItems.filter((el) =&amp;gt; el !== id));
    }
  };

  // 체크박스 전체 선택
  const handleAllCheck = (checked) =&amp;gt; {
    if(checked) {
      // 전체 선택 클릭 시 데이터의 모든 아이템(id)를 담은 배열로 checkItems 상태 업데이트
      const idArray = [];
      data.forEach((el) =&amp;gt; idArray.push(el.id));
      setCheckItems(idArray);
    }
    else {
      // 전체 선택 해제 시 checkItems 를 빈 배열로 상태 업데이트
      setCheckItems([]);
    }
  }

  return (
    &amp;lt;StyledTable&amp;gt;
      &amp;lt;thead&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;th&amp;gt;
            &amp;lt;input type='checkbox' name='select-all'
              onChange={(e) =&amp;gt; handleAllCheck(e.target.checked)}
              // 데이터 개수와 체크된 아이템의 개수가 다를 경우 선택 해제 (하나라도 해제 시 선택 해제)
              checked={checkItems.length === data.length ? true : false} /&amp;gt;
          &amp;lt;/th&amp;gt;
          &amp;lt;th className='second-row'&amp;gt;목록&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;/thead&amp;gt;
      &amp;lt;tbody&amp;gt;
        {data?.map((data, key) =&amp;gt; (
          &amp;lt;tr key={key}&amp;gt;
            &amp;lt;td&amp;gt;
              &amp;lt;input type='checkbox' name={`select-${data.id}`}
                onChange={(e) =&amp;gt; handleSingleCheck(e.target.checked, data.id)}
                // 체크된 아이템 배열에 해당 아이템이 있을 경우 선택 활성화, 아닐 시 해제
                checked={checkItems.includes(data.id) ? true : false} /&amp;gt;
            &amp;lt;/td&amp;gt;
            &amp;lt;td className='second-row'&amp;gt;{data.title}&amp;lt;/td&amp;gt;
          &amp;lt;/tr&amp;gt;
        ))}
      &amp;lt;/tbody&amp;gt;
    &amp;lt;/StyledTable&amp;gt;
  )
}

const StyledTable = styled.table`
  text-align: center;
  border-collapse: collapse;
  thead{
    tr{
      th{
        padding: 10px 15px;
        background-color: #888;
        color: #fff;
        font-weight: 700;
      }
    }
  }
  tbody{
    tr{
      td{
        padding: 7px 15px;
        border-bottom: 1px solid #eee;
      }
    }
  }
  .second-row{
    width: 150px;
  }
`;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘의 TIL 기록 끝!&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>react</category>
      <category>Til</category>
      <category>기능 구현</category>
      <category>전체선택</category>
      <category>체크박스</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/72</guid>
      <comments>https://babycoder05.tistory.com/entry/React-%EC%B2%B4%ED%81%AC%EB%B0%95%EC%8A%A4-%EC%A0%84%EC%B2%B4-%EC%84%A0%ED%83%9D%ED%95%B4%EC%A0%9C-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0#entry72comment</comments>
      <pubDate>Wed, 6 Jul 2022 13:48:43 +0900</pubDate>
    </item>
    <item>
      <title>브라우저의 인증(로그인) 방식에 대해 알아보자 Session / JWT / OAuth 2.0</title>
      <link>https://babycoder05.tistory.com/entry/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%9D%98-%EC%9D%B8%EC%A6%9D%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-Session-JWT-OAuth-20</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwv4by/btrFQW5m6PS/omcr5xfXLa66FmCg7KM0IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwv4by/btrFQW5m6PS/omcr5xfXLa66FmCg7KM0IK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwv4by/btrFQW5m6PS/omcr5xfXLa66FmCg7KM0IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcwv4by%2FbtrFQW5m6PS%2Fomcr5xfXLa66FmCg7KM0IK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;514&quot; height=&quot;305&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HTTP 프로토콜의 stateless 특성과 쿠키&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP를 이용한 통신 프로토콜은 stateless의 특성을 가지고 있기 때문에, 페이지에서 링크를 타고 다른 페이지로 이동할 때 상태가 유지되지 않는다. 여기서 상태라는 것은 정보의 유지를 말하는데 예를 들어, 로그인을 했을 때 정보가 유지되거나 쇼핑몰 사이트에서 장바구니에 물건을 담은 정보가 유지되는 것을 말한다. stateless 특성은 빠르게 원하는 정보만 요청해서 받아올 수 있도록 고안된 것이기 때문에 이를 보완하기 위해 쿠키와 같은 기술이 도입되었다.&lt;/p&gt;
&lt;figure id=&quot;og_1656378048802&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;웹 브라우저 쿠키에 대해 알아보자&quot; data-og-description=&quot;쿠키의 탄생 배경 HTTP는 상태를 계속 유지하지 않는 스테이트리스(Stateless)프로토콜이다. 서버와 클라이언트 간에 리퀘스트와 리스폰스를 교환하는 동안에 상태를 관리하지 않기 때문에 이전에 &quot; data-og-host=&quot;babycoder05.tistory.com&quot; data-og-source-url=&quot;https://babycoder05.tistory.com/entry/웹-브라우저-쿠키에-대해-알아보자&quot; data-og-url=&quot;https://babycoder05.tistory.com/entry/%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%BF%A0%ED%82%A4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bhS6Tu/hyOUQtqqV6/XTbGXf4RPYH5zqJOkkVHc1/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/eE83V/hyOURToJ5V/xX0H8smulQpuy3wRD86fgK/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/ox9K7/hyOU2AEf7u/g0sPf5UW4efmDNnRppefV0/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500&quot;&gt;&lt;a href=&quot;https://babycoder05.tistory.com/entry/웹-브라우저-쿠키에-대해-알아보자&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://babycoder05.tistory.com/entry/웹-브라우저-쿠키에-대해-알아보자&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bhS6Tu/hyOUQtqqV6/XTbGXf4RPYH5zqJOkkVHc1/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/eE83V/hyOURToJ5V/xX0H8smulQpuy3wRD86fgK/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/ox9K7/hyOU2AEf7u/g0sPf5UW4efmDNnRppefV0/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;웹 브라우저 쿠키에 대해 알아보자&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;쿠키의 탄생 배경 HTTP는 상태를 계속 유지하지 않는 스테이트리스(Stateless)프로토콜이다. 서버와 클라이언트 간에 리퀘스트와 리스폰스를 교환하는 동안에 상태를 관리하지 않기 때문에 이전에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;babycoder05.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;세션과 쿠키를 이용한 인증&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 사용자 정보로 로그인을 한다.&lt;/li&gt;
&lt;li&gt;입력된 계정 정보를 서버로 보내 서버에 저장된 계정 정보와 일치함을 확인하면, 사용자에게 고유한 ID 값을 부여해서 세션 저장소에 저장한 후, 이에 대한 Session ID를 발급한다.&lt;/li&gt;
&lt;li&gt;서버는 응답으로 Session ID를 세션 쿠키에 담아 사용자의 브라우저로 보낸다. 이후 매 페이지 요청마다 HTTP 요청 헤더에 Session ID가 담긴 쿠키를 같이 보낸다.&lt;/li&gt;
&lt;li&gt;서버는 쿠키를 받아 세션 저장소에서 대조를 한 후 사용자에 맞는 데이터를 보내준다.&lt;/li&gt;
&lt;li&gt;세션이 종료되면 Session ID와 유효성이 종료된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;계정 정보를 서버에서 확인하고 그에 대해 발급된 Session ID는 유의미한 정보를 가지고 있지 않으므로 HTTP 요청 중에 Session ID가 노출되더라도 사용자의 정보를 가지고 있지 않으므로 안전하다. (추가: 그러나 Session ID만 식별자로 사용해서 모든 get, post 요청에 대한 접근 권한이 생긴다면 위험하다.)&lt;/li&gt;
&lt;li&gt;매 HTTP 요청마다 사용자의 정보 대신 Session ID를 확인하므로 상대적으로 서버가 가벼운 확인 작업을 하게 되어 서버 자원 접근에 용이하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 단점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;세션 유지 중에 해커가 쿠키를 탈취한 후 그 쿠키를 이용해 HTTP 요청을 보내면 서버는 사용자로 오인해 정보를 전달할 수 있다. 이를 세션 하이재킹 공격이라고 한다. 해결책으로는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;HTTPS 프로토콜 사용&lt;/span&gt;과 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;세션 만료 시간을 지정&lt;/span&gt;해주는 것이다.&lt;/li&gt;
&lt;li&gt;서버에서 세션 저장소를 위한 추가 저장공간을 필요로 한다. (서버 비용 증가)&lt;/li&gt;
&lt;li&gt;매 HTTP 요청마다 사용자의 정보 대신 Session ID를 확인하므로 상대적으로 가볍긴 하지만 그래도 여전히 서버 DB에서 Session ID를 조회해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Access Token을 이용한 인증&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;유저가 사용자 정보로 로그인을 한다.&lt;/li&gt;
&lt;li&gt;서버 측에서 해당 사용자 정보를 검증한다.&lt;/li&gt;
&lt;li&gt;사용자 정보가 서버에 저장된 정보와 일치하면 유저에게 signed 토큰을 발급해준다. (signed 토큰이란 서버에서 정상적으로 발급된 토큰임을 증명하는 signature를 가진 토큰을 의미한다.)&lt;/li&gt;
&lt;li&gt;유저 측에 토큰을 저장하고, 서버에 HTTP 요청을 할 때 해당 토큰을 함께 서버에 전달한다.&lt;/li&gt;
&lt;li&gt;서버는 토큰을 검증하고, 요청에 응답한다. (서버 쪽에 세션 저장소와 같은 저장소가 필요하지 않고 토큰 검증 로직만 가지고 있게 된다.)&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 토큰의 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Session ID를 통한 로그인을 할 때는 유저가 처음 로그인 요청을 보냈던 서버에만 지속적으로 통신을 하도록 설정해야 한다. 그러나 유저가 늘어나고 서비스가 커질 수록 서버량도 증설이 되어야 하는데, 서버의 확장성(scalability)이 떨어질 수밖에 없다. 토큰을 이용하면 세션 저장소가 필요 없으므로 서버 확장성이 커지게 된다.&lt;/li&gt;
&lt;li&gt;토큰을 이용한 방식으로 로그인을 구성하면, 토큰만 발급하면 되기 때문에 Facebook 로그인, Google 로그인과 같은 소셜 로그인 기능을 제공할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;JWT 방식이 웹 표준으로 채택되어, C, Java, Python, C++, R, C#, PHP, JavaScript, Ruby, Go, Swift 등 수많은 프로그래밍 언어에서 지원된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 토큰의 단점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;토큰이 탈취 당하면 만료될 때까지 대처가 불가능하다. 세션을 이용한 방식일 때는 Session ID가 탈취되었다고 인지를 하면 세션 저장소에서 그 값을 지워 연결을 끊을 수 있었지만, 토큰 방식은 토큰 발급 이후의 상황에는 유저가 토큰을 관리하는 형태이므로, 탈취 상황이 되어도 서버 쪽에서 관리할 방법이 없다. 이를 보완하기 위해 토큰의 만료 기간을 짧게 가져가기도 한다. 그러나 이 방법도 30분, 1시간 등 토큰 만료 시간이 너무 짧으면 사용자가 불편함을 느낄 수도 있다. 그래서 나온 방식이 Refresh Token을 발급하는 방식이다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Access Token이 만료될 때마다 새롭게 발급해야 하므로 서버의 자원 낭비가 생긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- JWT&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT(JSON Web Token)는 인증에 필요한 정보들을 Token에 담아 암호화시켜 사용하는 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;서명된 토큰&lt;/span&gt;&lt;/b&gt;이다. JWT는 토큰을 이용한 인증 방식에서 가장 많이 사용되고 있고, Header, Payload, Signature가 점(.)으로 연결된 구조를 가지고 있다. JSON 객체를 이용하기 때문에 가볍다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIRLwQ/btrFVizPBrW/olmfJShT8QkbRDT4GrFxuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIRLwQ/btrFVizPBrW/olmfJShT8QkbRDT4GrFxuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIRLwQ/btrFVizPBrW/olmfJShT8QkbRDT4GrFxuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIRLwQ%2FbtrFVizPBrW%2FolmfJShT8QkbRDT4GrFxuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;250&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;헤더(Header): 헤더에는 토큰의 타입이나, 서명 생성에 어떤 알고리즘이 사용되었는지 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656384669629&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;typ&quot;: &quot;JWT&quot;,
  &quot;alg&quot;: &quot;HS256&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;정보(Payload): 토큰 발급자, 토큰 제목, 토큰 대상자, 토큰 만료 시간, 토큰 활성 날짜, 토큰 발급 시간, JWT 토큰 식별자 등 토큰에 대한 정보를 담는다. 중요한 것은 아이디나 비밀번호 같은 민감 정보는 payload에 담지 않는다. 단순 JSON 객체이기 때문에 디코딩하여 누구나 정보에 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656384899927&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;sub&quot;: &quot;1&quot;,
    &quot;iss&quot;: &quot;ori&quot;,
    &quot;exp&quot;: 1636989718,
    &quot;iat&quot;: 1636987918
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;서명(Signature): JWT에서 가장 중요한 부분인 서명은 서버가 가지고 있는 개인키로만 복호화될 수 있다. 이 서명은 Base64 방식으로 인코딩한 header, payload 그리고 서버의 개인키를 더한 후 서명되기 때문에 만약 header와 payload가 변조된 상태로 요청을 하면 서버가 발급했던 signatured 안의 payload와 다르기 때문에 인증이 불가능해진다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Refresh Token&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 방식의 단점을 보완하기 위해 등장한 것으로 JWT를 처음 발급할 때 Access Token과 함께 Refresh Token을 함께 발급하여 토큰의 짧은 만료 시간을 해결한다. Refresh Token은 말 그대로 Access Token이 만료되었을 때 새로 발급받게 해주는 것을 보장하는 토큰이다.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자는 로그인 시에 Access Token과 Refresh Token을 함께 발급받고 사용자 측에 저장한다.&lt;/li&gt;
&lt;li&gt;Access Token이 만료가 되고 서버로 부터 만료되었다는 메시지를 받으면 사용자 측에 저장된 Refresh Token으로 새로운 Access Token 발급을 요청한다.&lt;/li&gt;
&lt;li&gt;서버는 서버 쪽에 있는 Refresh Token 저장소에서 사용자 측에서 받은 Refresh Token과 일치하는 것이 있는지 확인하고 새로운 Access Token을 생성해서 전달한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 세션 저장소를 이용한 방식과 유사하다. 그래서 세션 저장소가 서버에 필요한 것처럼 Refresh Token 저장소가 필요한 단점을 가지고 있다. 그러나 매 HTTP 통신마다 Session ID를 확인해야 하는 작업이 필요 없고, 서버 확장성이 여전히 유지되며, 토큰이 탈취됨을 인지하면 Refresh Token을 삭제할 수 있는 부가 옵션이 생긴다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;OAuth 2.0을 이용한 인증&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OAuth는 외부 서비스의 인증 및 권한 부여를 관리하는 범용적인 프로토콜로 현재 범용적으로 사용되는 것은 OAuth 2.0이다. 여기서 외부 서비스가 등장하는데, 많은 서비스들이 카카오 로그인, 네이버 로그인, 구글 로그인과 같은 소셜 로그인 방식을 이용하고 있다. 그러므로 여기서 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;사용자(나), 서비스(클라이언트), 로그인 기능 제공자(서버)&lt;/span&gt;&lt;/b&gt; &lt;span&gt;3개의 주체가 등장하게 된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 서비스(클라이언트)에 인증 요청을 한다.&lt;/li&gt;
&lt;li&gt;클라이언트는 사용자에 인증할 수단(카카오, 네이버, 구글 로그인 url)을 보낸다.&lt;/li&gt;
&lt;li&gt;사용자가 인증에 통과하면 로그인 기능 제공자는&amp;nbsp;클라이언트의 유저가 맞는 지 확인 후 Access Token, Refresh Token, 그리고 유저의 정보를 발급해준다. 클라이언트의 유저가 아니라면 회원가입을 진행한다.&lt;/li&gt;
&lt;li&gt;클라이언트는 Access Token을 클라이언트 DB에 저장하거나 사용자에게 넘긴다.&lt;/li&gt;
&lt;li&gt;사용자가 통신을 하면서 로그인 기능 제공자가 가지고 있는 자원이 필요하게 되면 클라이언트는 Access Token을 담아 로그인 기능 제공자에 요청한다.&lt;/li&gt;
&lt;li&gt;로그인 기능 제공자는 Access Token이 유효한 지 확인 후 클라이언트에 자원을 보낸다.&lt;/li&gt;
&lt;li&gt;만일 Access Token이 만료됐거나 위조되었다면, 클라이언트는 Refresh Token을 로그인 기능 제공자에 보내 Access Token을 재발급 받는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 참고사항&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 내가 서비스를 만들고 OAuth를 이용해서 유저들의 인증을 처리하고 싶다면, 사전에 로그인 기능을 제공하는 측에 등록하는 과정이 필요하다. 개발자가 등록하고자 하는 서비스의 웹 어플리케이션을 등록한 뒤 APP_ID와 CLIENT_ID 등을 보내야 OAuth를 제공하는 측에서 어느 서비스인지를 알 수 있다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/Browser &amp;amp; Network</category>
      <category>access token</category>
      <category>jwt</category>
      <category>oauth</category>
      <category>session</category>
      <category>로그인</category>
      <category>세션</category>
      <category>인증</category>
      <category>쿠키</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/71</guid>
      <comments>https://babycoder05.tistory.com/entry/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%9D%98-%EC%9D%B8%EC%A6%9D%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-Session-JWT-OAuth-20#entry71comment</comments>
      <pubDate>Tue, 28 Jun 2022 12:50:33 +0900</pubDate>
    </item>
    <item>
      <title>Redux 미들웨어란?</title>
      <link>https://babycoder05.tistory.com/entry/Redux-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4%EB%9E%80</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redux 미들웨어(Middleware)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 처음으로 Redux 미들웨어를 이용해서 프로젝트를 만들 일이 생겨 기존에 미들웨어로 작성된 프로젝트를 보는데 아무리 봐도 코드 구조가 이해가 안 돼서 한참을 눈이 빠지게 들여다봤다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux 미들웨어가 무엇인지, 왜 사용하는지에 대한 개념은 전혀 어렵지가 않은데, 실제로 코드를 구성하려고 보면, 코드가 다 Split 되어 있고, 여러 파일들이 구조적으로 분할되어 엮여있다보니 아예 모르는 상태에서 처음 파악하기에는 이해가 되지 않았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux를 사용할 줄 안다는 기본 가정 하에, Redux는 store, action, reducer의 구조로 이루어져 있고, 단일 store에 전역 상태를 관리하고, action 에 담겨진 명령어로 reducer에 원하는 상태 변경을 dispatch 해서 store에 담는 구조로 되어 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;action =&amp;gt; reducer =&amp;gt; store&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, reducer에서 반환하는 상태 변경 전에 어떠한 동작을 처리하고 싶을 경우에 미들웨어를 사용하게 된다. 가령, setTimeout을 이용해서 1초 뒤에 디스패치가 되게 만들거나, 디스패치 전에 들어온 액션 타입을 콘솔에 표시하거나, 디스패치 전에 비동기 데이터 요청을 한 뒤 받아온 데이터를 전역 상태에 담거나 하는 추가적인 동작을 처리하려면 미들웨어를 이용하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;action =&amp;gt; middleware =&amp;gt; reducer =&amp;gt; store&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redux 미들웨어 사용방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux 미들웨어는 src/index.js 에서 Redux store를 생성하는 부분의 두번째 인자에 넣어 적용시킬 수 있다. 보통 미들웨어로 적용할 파일을 만들거나, redux-logger, redux-thunk와 같은 패키지들을 선언해준다. 미들웨어는 콤마로 구분해서 여러 개 적용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1655631922958&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { applyMiddleware, createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './modules';
import logger from 'redux-logger';
import ReduxThunk from 'redux-thunk';

const store = createStore(rootReducer, applyMiddleware(ReduxThunk, logger));

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;Provider store={store}&amp;gt;
      &amp;lt;App /&amp;gt;
    &amp;lt;/Provider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;redux-logger&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1655633134442&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i redux-logger&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redux-logger는 dispatch가 될 때 action 타입과 변경 전 상태와 변경 후 상태를 예쁘게 콘솔에 출력해주는 패키지다. redux를 통해 변경되는 상태를 추적할 때 (개발자 입장에서) 좋은 툴이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0BqnY/btrFdGOeTIW/auYMDOV5kf8WgB5wwEGok1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0BqnY/btrFdGOeTIW/auYMDOV5kf8WgB5wwEGok1/img.png&quot; data-alt=&quot;redux-logger&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0BqnY/btrFdGOeTIW/auYMDOV5kf8WgB5wwEGok1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0BqnY%2FbtrFdGOeTIW%2FauYMDOV5kf8WgB5wwEGok1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;550&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;redux-logger&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;redux-thunk&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1655633197022&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i redux-thunk&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redux-thunk는 가장 많이 사용되는 미들웨어로 비동기 작업을 처리할 때 용이하다. redux-thunk를 사용하면 dispatch 할 때, 액션 타입 객체가 아니라 함수를 디스패치 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1655640508813&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// actions.js

// 액션 타입
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';

// 액션 생성 함수
export const increase = () =&amp;gt; ({ type: INCREASE });
export const decrease = () =&amp;gt; ({ type: DECREASE });

// redux-thunk를 이용하여 액션을 디스패치 하기 전에 setTimeout 비동기 동작을 하는 함수를 디스패치 하도록 만들 수 있다. 
export const increaseAsync = () =&amp;gt; dispatch =&amp;gt; {
  setTimeout(() =&amp;gt; dispatch(increase()), 1000);
};
export const decreaseAsync = () =&amp;gt; dispatch =&amp;gt; {
  setTimeout(() =&amp;gt; dispatch(decrease()), 1000);
};&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>Middleware</category>
      <category>react</category>
      <category>redux</category>
      <category>redux-thunk</category>
      <category>리덕스</category>
      <category>미들웨어</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/70</guid>
      <comments>https://babycoder05.tistory.com/entry/Redux-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4%EB%9E%80#entry70comment</comments>
      <pubDate>Sun, 19 Jun 2022 21:15:01 +0900</pubDate>
    </item>
    <item>
      <title>lodash throttle과 debounce를 이용해서 함수의 반복 실행 막기</title>
      <link>https://babycoder05.tistory.com/entry/lodash-throttle%EA%B3%BC-debounce%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-%ED%95%A8%EC%88%98%EC%9D%98-%EB%B0%98%EB%B3%B5-%EC%8B%A4%ED%96%89-%EB%A7%89%EA%B8%B0</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Lodash&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lodash.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://lodash.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1655622429178&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Lodash&quot; data-og-description=&quot;_.defaults({&amp;nbsp;'a':&amp;nbsp;1&amp;nbsp;},&amp;nbsp;{&amp;nbsp;'a':&amp;nbsp;3,&amp;nbsp;'b':&amp;nbsp;2&amp;nbsp;});_.partition([1,&amp;nbsp;2,&amp;nbsp;3,&amp;nbsp;4],&amp;nbsp;n&amp;nbsp;=&amp;gt;&amp;nbsp;n&amp;nbsp;%&amp;nbsp;2);DownloadLodash is released under the MIT license &amp;amp; supports modern environments. Review the build differences &amp;amp; pick one that&amp;rsquo;s right for you.InstallationIn&quot; data-og-host=&quot;lodash.com&quot; data-og-source-url=&quot;https://lodash.com/&quot; data-og-url=&quot;https://lodash.com/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://lodash.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lodash.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Lodash&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;_.defaults({&amp;nbsp;'a':&amp;nbsp;1&amp;nbsp;},&amp;nbsp;{&amp;nbsp;'a':&amp;nbsp;3,&amp;nbsp;'b':&amp;nbsp;2&amp;nbsp;});_.partition([1,&amp;nbsp;2,&amp;nbsp;3,&amp;nbsp;4],&amp;nbsp;n&amp;nbsp;=&amp;gt;&amp;nbsp;n&amp;nbsp;%&amp;nbsp;2);DownloadLodash is released under the MIT license &amp;amp; supports modern environments. Review the build differences &amp;amp; pick one that&amp;rsquo;s right for you.InstallationIn&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lodash.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1655622719953&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i lodash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lodash는 자바스크립트 패키지 중 가장 인기있는 패키지들 중 하나로 정말 많이 사용된다. 주로 array 자료형을 다루기 위해 자바스크립트에서 기본적으로 제공하지 않는 method를 사용하기 위해 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;throttle과 debounce&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lodash는 array 자료형 method 뿐 아니라 throttle과 debounce 라는 기능을 제공한다. 두 기능 모두 이벤트의 반복 실행 시 콜백 함수의 불필요한 실행을 막아주는 역할을 한다. 예를 들어, form 양식의 submit 액션을 일으키는 버튼 클릭을 사용자가 여러 번 할 경우, 여러 번 submit 액션이 일어나는 것을 한 번만 액션이 일어나도록 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;throttle과 debounce의 차이점은 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;throttle: throttle은 '목을 조르다'라는 뜻으로, 버튼 클릭이 콜백 함수를 실행하도록 요청하는 길목을 졸라서 액션이 일어나는 횟수를 줄이는 것이라고 보면 된다. 스크롤 이벤트나 마우스 움직임 감지 이벤트와 같이 동작 감지가 여러번 일어나는 경우에 코드에 지정한 시간마다 한 번 이벤트를 감지하도록 만들 수 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;debounce: throttle이 맨 처음 이벤트가 발생했을 때 일정 시간 동안의 이벤트를 막고 그 일정 시간 이후에 발생한 이벤트를 다시 실행시킨다면, debounce는 여러 번 이벤트가 발생했을 때 앞에 일어난 이벤트를 취소하고 제일 마지막에 일어난 이벤트를 정해둔 시간동안 기다렸다가 실행한다. 직접 사용해보면 debounce가 throttle보다 더 이벤트 실행을 많이 막는 느낌이다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;throttle 의 실행 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1655625189055&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import styles from 'pages/sample.module.scss';
import { throttle } from &quot;lodash&quot;;

export default function Sample() {
  const onMouseMove = () =&amp;gt; {
    console.log('no throttle');
  }
  const onMouseMoveThrottle = throttle(() =&amp;gt; {
    console.log('throttle');
  }, 1000); // 1000ms 1초에 한번씩 실행

  return (
    &amp;lt;section 
      className={styles.sample}
      onMouseMove={() =&amp;gt; {
        onMouseMove();
        onMouseMoveThrottle();
      }}&amp;gt;
    &amp;lt;/section&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsrGFM/btrE5zvSuMO/qx2ABauef71rV4UBGMoID1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsrGFM/btrE5zvSuMO/qx2ABauef71rV4UBGMoID1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsrGFM/btrE5zvSuMO/qx2ABauef71rV4UBGMoID1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsrGFM%2FbtrE5zvSuMO%2Fqx2ABauef71rV4UBGMoID1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;516&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;debounce 의 실행 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1655625534807&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import styles from 'pages/sample.module.scss';
import { debounce } from &quot;lodash&quot;;

export default function Sample() {
  const onMouseMove = e =&amp;gt; {
    console.log('no debounce');
  }
  const onMouseMoveDebounce = debounce(e =&amp;gt; {
    console.log('debounce');
  }, 1000); // 1000ms 맨 마지막 동작 요청 이후 1초 뒤 동작

  return (
    &amp;lt;section 
      className={styles.sample}
      onMouseMove={() =&amp;gt; {
        onMouseMove();
        onMouseMoveDebounce();
      }}&amp;gt;
    &amp;lt;/section&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zosic/btrFchHPVgx/prqPur8LXYaHgbShkoSZeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zosic/btrFchHPVgx/prqPur8LXYaHgbShkoSZeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zosic/btrFchHPVgx/prqPur8LXYaHgbShkoSZeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZosic%2FbtrFchHPVgx%2FprqPur8LXYaHgbShkoSZeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;282&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;throttle과 debounce 를 적절하게 사용하면 성능 향상에 큰 도움이 될 수 있다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/JavaScript</category>
      <category>debounce</category>
      <category>JavaScript</category>
      <category>lodash</category>
      <category>Throttle</category>
      <category>자바스크립트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/69</guid>
      <comments>https://babycoder05.tistory.com/entry/lodash-throttle%EA%B3%BC-debounce%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-%ED%95%A8%EC%88%98%EC%9D%98-%EB%B0%98%EB%B3%B5-%EC%8B%A4%ED%96%89-%EB%A7%89%EA%B8%B0#entry69comment</comments>
      <pubDate>Sun, 19 Jun 2022 17:02:04 +0900</pubDate>
    </item>
    <item>
      <title>React state 변경 비동기 처리에 관하여 / 여러 개 state 변경 에러 해결하기</title>
      <link>https://babycoder05.tistory.com/entry/React-state-%EB%B3%80%EA%B2%BD-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC-%EC%97%AC%EB%9F%AC-%EA%B0%9C-state-%EB%B3%80%EA%B2%BD-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React의 state 변경 비동기 처리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 에서 상태 변경을 할 때 setState 혹은 useState Hook을 써서 변경하면 상태 변경 동작은 비동기로 처리된다. (동기/비동기의 차이점을 알아야 밑에 내용이 이해가 가능하다.) React가 상태 변경을 비동기로 처리하는 이유는 효율성 때문이다. 리액트는 상태가 변경될 때마다 재렌더링을 일으키도록 설계되었는데, 만약 한꺼번에 너무 많은 state가 변경될 경우 일일이 재렌더링을 일으킨다면 너무 비효율적이기 때문에 state 값이 변경될 때 React 내부 로직 기준에 따라 한번에 state 변경을 취합해서 재렌더링을 일으킨다. 그리고 그 취합은 16ms 단위로 이루어진다고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;여러 개 State 변경 에러 해결하기&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아래 예제는 input 선택 여부에 따라 재고 개수를 하나씩 빼는 임의로 만든 예제다. 만약 과일의 개수가 너무 많아서 한꺼번에 많은 상태 변경이 일어난다면 취합이 여러 번 일어나면서 state는 객체이기 때문에 모든 상태 변경이 적용되지 않고 마지막 실행값만 덮어씌워지는 에러가 발생할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1654935729476&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;

export default function Sample() {
  const [fruits, setFruits] = useState({
    apple: {selected: false, stock: 5},
    strawberry: {selected: false, stock: 2},
    banana: {selected: false, stock: 4},
    kiwi: {selected: false, stock: 6}
  });
  
  const handleClick = () =&amp;gt; {
    fruits.apple.selected &amp;amp;&amp;amp; setFruits({...fruits, apple: {...fruits.apple, stock: fruits.apple.stock - 1}});
    fruits.strawberry.selected &amp;amp;&amp;amp; setFruits({...fruits, strawberry: {...fruits.strawberry, stock: fruits.strawberry.stock - 1}});
    fruits.banana.selected &amp;amp;&amp;amp; setFruits({...fruits, banana: {...fruits.banana, stock: fruits.banana.stock - 1}});
    fruits.kiwi.selected &amp;amp;&amp;amp; setFruits({...fruits, kiwi: {...fruits.kiwi, stock: fruits.kiwi.stock - 1}});
  };

  return (
    &amp;lt;section className=&quot;sample&quot;&amp;gt;
      &amp;lt;ul className=&quot;display-list&quot;&amp;gt;
        &amp;lt;li className=&quot;display-item&quot;&amp;gt;
          &amp;lt;input type=&quot;checkbox&quot; name=&quot;apple&quot; onChange={() =&amp;gt; setFruits({...fruits, apple: {...fruits.apple, selected: !fruits.apple.selected}})} /&amp;gt;
            : {fruits.apple.stock}
        &amp;lt;/li&amp;gt;
        &amp;lt;li className=&quot;display-item&quot;&amp;gt;
          &amp;lt;input type=&quot;checkbox&quot; name=&quot;strawberry&quot; onChange={() =&amp;gt; setFruits({...fruits, strawberry: {...fruits.strawberry, selected: !fruits.strawberry.selected}})} /&amp;gt;
            : {fruits.strawberry.stock}
        &amp;lt;/li&amp;gt;
        &amp;lt;li className=&quot;display-item&quot;&amp;gt;
          &amp;lt;input type=&quot;checkbox&quot; name=&quot;banana&quot; onChange={() =&amp;gt; setFruits({...fruits, banana: {...fruits.banana, selected: !fruits.banana.selected}})} /&amp;gt;
            : {fruits.banana.stock}
        &amp;lt;/li&amp;gt;
        &amp;lt;li className=&quot;display-item&quot;&amp;gt;
          &amp;lt;input type=&quot;checkbox&quot; name=&quot;kiwi&quot; onChange={() =&amp;gt; setFruits({...fruits, kiwi: {...fruits.kiwi, selected: !fruits.kiwi.selected}})} /&amp;gt;
            : {fruits.kiwi.stock}
        &amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
      &amp;lt;div className=&quot;btn-wrap&quot;&amp;gt;
        &amp;lt;button onClick={handleClick}&amp;gt;Checkout&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DZhUJ/btrEvgXDbHF/kWXGDOfp3eBtc7Le2OUfZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DZhUJ/btrEvgXDbHF/kWXGDOfp3eBtc7Le2OUfZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DZhUJ/btrEvgXDbHF/kWXGDOfp3eBtc7Le2OUfZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDZhUJ%2FbtrEvgXDbHF%2FkWXGDOfp3eBtc7Le2OUfZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;324&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAaXNe/btrEwy4vtRO/2z7qc3gcLV86z0dCmIf8gK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAaXNe/btrEwy4vtRO/2z7qc3gcLV86z0dCmIf8gK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAaXNe/btrEwy4vtRO/2z7qc3gcLV86z0dCmIf8gK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAaXNe%2FbtrEwy4vtRO%2F2z7qc3gcLV86z0dCmIf8gK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;324&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 상태 변경을 확실하게 적용시키기 위해서 아래와 같은 코드로 변경할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1654936166723&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;

export default function Sample() {
  const [fruits, setFruits] = useState({
    apple: {selected: false, stock: 5},
    strawberry: {selected: false, stock: 2},
    banana: {selected: false, stock: 4},
    kiwi: {selected: false, stock: 6}
  });
  
  // setState() 의 매개변수로 이전 상태값을 받아 업데이트 한다.
  const handleClick = () =&amp;gt; {
    fruits.apple.selected &amp;amp;&amp;amp; setFruits(prevState =&amp;gt; ({...prevState, apple: {...prevState.apple, stock: prevState.apple.stock - 1}}));
    fruits.strawberry.selected &amp;amp;&amp;amp; setFruits(prevState =&amp;gt; ({...prevState, strawberry: {...prevState.strawberry, stock: prevState.strawberry.stock - 1}}));
    fruits.banana.selected &amp;amp;&amp;amp; setFruits(prevState =&amp;gt; ({...prevState, banana: {...prevState.banana, stock: prevState.banana.stock - 1}}));
    fruits.kiwi.selected &amp;amp;&amp;amp; setFruits(prevState =&amp;gt; ({...prevState, kiwi: {...prevState.kiwi, stock: prevState.kiwi.stock - 1}}));
  };

  return (
    &amp;lt;section className=&quot;sample&quot;&amp;gt;
      &amp;lt;ul className=&quot;display-list&quot;&amp;gt;
        &amp;lt;li className=&quot;display-item&quot;&amp;gt;
          &amp;lt;input type=&quot;checkbox&quot; name=&quot;apple&quot; onChange={() =&amp;gt; setFruits({...fruits, apple: {...fruits.apple, selected: !fruits.apple.selected}})} /&amp;gt;
            : {fruits.apple.stock}
        &amp;lt;/li&amp;gt;
        &amp;lt;li className=&quot;display-item&quot;&amp;gt;
          &amp;lt;input type=&quot;checkbox&quot; name=&quot;strawberry&quot; onChange={() =&amp;gt; setFruits({...fruits, strawberry: {...fruits.strawberry, selected: !fruits.strawberry.selected}})} /&amp;gt;
            : {fruits.strawberry.stock}
        &amp;lt;/li&amp;gt;
        &amp;lt;li className=&quot;display-item&quot;&amp;gt;
          &amp;lt;input type=&quot;checkbox&quot; name=&quot;banana&quot; onChange={() =&amp;gt; setFruits({...fruits, banana: {...fruits.banana, selected: !fruits.banana.selected}})} /&amp;gt;
            : {fruits.banana.stock}
        &amp;lt;/li&amp;gt;
        &amp;lt;li className=&quot;display-item&quot;&amp;gt;
          &amp;lt;input type=&quot;checkbox&quot; name=&quot;kiwi&quot; onChange={() =&amp;gt; setFruits({...fruits, kiwi: {...fruits.kiwi, selected: !fruits.kiwi.selected}})} /&amp;gt;
            : {fruits.kiwi.stock}
        &amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
      &amp;lt;div className=&quot;btn-wrap&quot;&amp;gt;
        &amp;lt;button onClick={handleClick}&amp;gt;Checkout&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKmtU0/btrEuXcR8DU/nHSjUF4dPuPOqApuZZD0S1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKmtU0/btrEuXcR8DU/nHSjUF4dPuPOqApuZZD0S1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKmtU0/btrEuXcR8DU/nHSjUF4dPuPOqApuZZD0S1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKmtU0%2FbtrEuXcR8DU%2FnHSjUF4dPuPOqApuZZD0S1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;324&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDAfJQ/btrEu7mGYAG/UZ0VTYRpOm8nTMLVsopBSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDAfJQ/btrEu7mGYAG/UZ0VTYRpOm8nTMLVsopBSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDAfJQ/btrEu7mGYAG/UZ0VTYRpOm8nTMLVsopBSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDAfJQ%2FbtrEu7mGYAG%2FUZ0VTYRpOm8nTMLVsopBSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;324&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setState 함수는 매개변수로 이전 상태값을 받기 때문에 이전 상태값을 받아 로직을 작성해주면 연속 상태 변경을 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 공부하면 할 수록 너무 재미남 &lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>react</category>
      <category>setState</category>
      <category>리액트</category>
      <category>비동기</category>
      <category>상태 변경</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/68</guid>
      <comments>https://babycoder05.tistory.com/entry/React-state-%EB%B3%80%EA%B2%BD-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC-%EC%97%AC%EB%9F%AC-%EA%B0%9C-state-%EB%B3%80%EA%B2%BD-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0#entry68comment</comments>
      <pubDate>Sat, 11 Jun 2022 17:42:44 +0900</pubDate>
    </item>
    <item>
      <title>React 부모 컴포넌트에서 자식 컴포넌트 함수 호출하기 - useImperativeHandle</title>
      <link>https://babycoder05.tistory.com/entry/%EB%B6%80%EB%AA%A8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%97%90%EC%84%9C-%EC%9E%90%EC%8B%9D-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;React 에서 props 의 개념에 대해 익숙해졌다면, 부모 컴포넌트의 state 와 함수를 가져다 쓰는 것이 어렵지 않을 것이다. 그러나 만약 자식 컴포넌트의 함수를 부모 컴포넌트에서 사용해야 한다면 어떻게 할까? 자식 컴포넌트의 함수를 부모 컴포넌트로 옮기거나, 자식 컴포넌트와 부모 컴포넌트 최상단에 함수를 선언하거나, 커스텀 Hook 을 만들 수도 있을 것이다. 만약 정말 자식 컴포넌트에서 부모 컴포넌트로의 역방향 흐름을 만들어야 할 경우에는 리액트에서 제공하는 Hook 을 이용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useImperativeHandle&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useImperativeHandle 은 자식 컴포넌트에 ref 를 선언하여 자식 컴포넌트 내부에 접근을 가능하게 해주는 방법이다. 자식 컴포넌트를 forwardRef로 감싸고 그 내부에 useImperativeHandle 을 이용해서 부모 컴포넌트로 내보내고 싶은 함수를 선언해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/* 참고 */&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 공식 문서에서는 ref 를 사용한 명령형 코드를 지양하라고 권고한다. 이유는 리액트는 상태 값의 변화에 따라 재렌더링을 일으키도록 설계되어 있는데, ref와 state 모두 리액트 문서 내에서 상태 값을 가지고 있는 것들이기 때문이다. state 가 변경되면 재렌더링을 일으키지만, ref는 변경되어도 재렌더링을 일으키지 않는다. 그러나 ref의 상태를 변경할 경우 상태 변화를 감지하는 React의 성능을 저하시키거나 오류를 일으킬 수 있기 때문에 대부분의 경우 ref 를 이용한 코드를 지양하라고 권장한다.&lt;/p&gt;
&lt;figure id=&quot;og_1654927456429&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Hooks API Reference &amp;ndash; React&quot; data-og-description=&quot;A JavaScript library for building user interfaces&quot; data-og-host=&quot;ko.reactjs.org&quot; data-og-source-url=&quot;https://ko.reactjs.org/docs/hooks-reference.html#useimperativehandle&quot; data-og-url=&quot;https://ko.reactjs.org/docs/hooks-reference.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/e4jYl/hyOIj4vFCI/Tkl3mupcQYKg7EOqj3CLk1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/hooks-reference.html#useimperativehandle&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.reactjs.org/docs/hooks-reference.html#useimperativehandle&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/e4jYl/hyOIj4vFCI/Tkl3mupcQYKg7EOqj3CLk1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Hooks API Reference &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A JavaScript library for building user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.reactjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useImperativeHandle을 이용한 코드는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1654927371142&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { forwardRef, useEffect, useImperativeHandle, useRef } from &quot;react&quot;

// 부모 컴포넌트
const ParentComponent = () =&amp;gt; {
  const childComponentRef = useRef();

  useEffect(() =&amp;gt; {
    childComponentRef.current.willBeUsedInParentComponent();
  }, []);

  return (
    &amp;lt;ChildComponent ref={childComponentRef} /&amp;gt;
  )
};

// 자식 컴포넌트
const ChildComponent = forwardRef((props, ref) =&amp;gt; {
  useImperativeHandle(ref, () =&amp;gt; ({
    // 부모 컴포넌트에서 사용할 함수를 선언
    willBeUsedInParentComponent
  }))

  function willBeUsedInParentComponent() {
    console.log('Hi Parent');
  }

  return (
    &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;
  )
});

export default ParentComponent;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 화면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKSZ0W/btrEuqzMLpw/RnoDwQ5rly0D9cNAphtdE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKSZ0W/btrEuqzMLpw/RnoDwQ5rly0D9cNAphtdE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKSZ0W/btrEuqzMLpw/RnoDwQ5rly0D9cNAphtdE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKSZ0W%2FbtrEuqzMLpw%2FRnoDwQ5rly0D9cNAphtdE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;290&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>fowardRef</category>
      <category>react</category>
      <category>useImperativeHandle</category>
      <category>리액트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/67</guid>
      <comments>https://babycoder05.tistory.com/entry/%EB%B6%80%EB%AA%A8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%97%90%EC%84%9C-%EC%9E%90%EC%8B%9D-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0#entry67comment</comments>
      <pubDate>Sat, 11 Jun 2022 15:13:03 +0900</pubDate>
    </item>
    <item>
      <title>react-paginate 로 페이지네이션 구현하기</title>
      <link>https://babycoder05.tistory.com/entry/react-paginate-%EB%A1%9C-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKtiOA/btrD8OfYynq/WvEhxCCAzL22X8NgIacFQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKtiOA/btrD8OfYynq/WvEhxCCAzL22X8NgIacFQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKtiOA/btrD8OfYynq/WvEhxCCAzL22X8NgIacFQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKtiOA%2FbtrD8OfYynq%2FWvEhxCCAzL22X8NgIacFQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;758&quot; height=&quot;404&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 페이지네이션 기능을 구현할 일이 생겼다. 직접 기능을 만들면 좋겠지만, 생각보다 너무 어려웠다. 그래서 찾아보니 리액트 라이브러리 중 react-paginate를 이용해서 페이지네이션을 구현할 수 있다는 것을 찾았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/react-paginate&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.npmjs.com/package/react-paginate&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1654561546019&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;react-paginate&quot; data-og-description=&quot;A ReactJS component that creates a pagination.. Latest version: 8.1.3, last published: 2 months ago. Start using react-paginate in your project by running &amp;#96;npm i react-paginate&amp;#96;. There are 422 other projects in the npm registry using react-paginate.&quot; data-og-host=&quot;www.npmjs.com&quot; data-og-source-url=&quot;https://www.npmjs.com/package/react-paginate&quot; data-og-url=&quot;https://www.npmjs.com/package/react-paginate&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/G2Ydt/hyOFudAZLR/bGiaJQin34vUn9qV07DBg0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/react-paginate&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.npmjs.com/package/react-paginate&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/G2Ydt/hyOFudAZLR/bGiaJQin34vUn9qV07DBg0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;react-paginate&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A ReactJS component that creates a pagination.. Latest version: 8.1.3, last published: 2 months ago. Start using react-paginate in your project by running `npm i react-paginate`. There are 422 other projects in the npm registry using react-paginate.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.npmjs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1654561571817&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install react-paginate --save&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 매우 간단하다. 예제 코드에 내가 원하는 자료를 붙여주기만 하면 된다. 라우팅으로 페이지가 이동하는 것이 아니라 자바스크립트로 가변적으로 자료를 바꿔준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1654561728862&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import ReactPaginate from 'react-paginate';

// 내가 보여줄 아이템의 리스트 - 배열 자료형으로 만들어야 한다.
const items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];

// 아이템을 map을 이용해서 뿌려주는 구간 - 내가 보여줄 모양으로 마크업한다.
function Items({ currentItems }) {
  return (
    &amp;lt;&amp;gt;
      {currentItems &amp;amp;&amp;amp;
        currentItems.map((item) =&amp;gt; (
          &amp;lt;div&amp;gt;
            &amp;lt;h3&amp;gt;Item #{item}&amp;lt;/h3&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
    &amp;lt;/&amp;gt;
  );
};

// 하단 페이지네이션 네비게이션과 페이지 리스트를 보여주는 컴포넌트
// props를 통해 한 페이지에 보여줄 아이템의 개수를 받아 재사용 가능한 컴포넌트로 사용할 수 있다.
function PaginatedItems({ itemsPerPage }) {
  // We start with an empty list of items.
  const [currentItems, setCurrentItems] = useState(null);
  const [pageCount, setPageCount] = useState(0);
  // Here we use item offsets; we could also use page offsets
  // following the API or data you're working with.
  const [itemOffset, setItemOffset] = useState(0);

  useEffect(() =&amp;gt; {
    // Fetch items from another resources.
    const endOffset = itemOffset + itemsPerPage;
    console.log(`Loading items from ${itemOffset} to ${endOffset}`);
    setCurrentItems(items.slice(itemOffset, endOffset));
    setPageCount(Math.ceil(items.length / itemsPerPage));
  }, [itemOffset, itemsPerPage]);

  // Invoke when user click to request another page.
  const handlePageClick = (event) =&amp;gt; {
    const newOffset = (event.selected * itemsPerPage) % items.length;
    console.log(
      `User requested page number ${event.selected}, which is offset ${newOffset}`
    );
    setItemOffset(newOffset);
  };

  return (
    &amp;lt;&amp;gt;
      &amp;lt;Items currentItems={currentItems} /&amp;gt;
      &amp;lt;ReactPaginate
        breakLabel=&quot;...&quot;
        nextLabel=&quot;next &amp;gt;&quot;
        onPageChange={handlePageClick}
        pageRangeDisplayed={5}
        pageCount={pageCount}
        previousLabel=&quot;&amp;lt; previous&quot;
        renderOnZeroPageCount={null}
      /&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

// Add a &amp;lt;div id=&quot;container&quot;&amp;gt; to your HTML to see the componend rendered.
ReactDOM.render(
  &amp;lt;PaginatedItems itemsPerPage={4} /&amp;gt;,
  document.getElementById('container')
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지네이션 커스터마이징을 위한 다양한 Props들의 설명도 Document에 설명이 되어 있다. 커스텀하기가 매우 용이해서 사용하기 매우 좋은 것 같다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>react</category>
      <category>react-paginate</category>
      <category>라이브러리</category>
      <category>리액트</category>
      <category>페이지네이션</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/66</guid>
      <comments>https://babycoder05.tistory.com/entry/react-paginate-%EB%A1%9C-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0#entry66comment</comments>
      <pubDate>Tue, 7 Jun 2022 09:35:48 +0900</pubDate>
    </item>
    <item>
      <title>리액트에서 useRef를 이용해서 현명하게 setInterval 사용하기</title>
      <link>https://babycoder05.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-useRef%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-%ED%98%84%EB%AA%85%ED%95%98%EA%B2%8C-setInterval-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React와 setInterval의 관계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 상태값이 변하면 재렌더링을 한다. 그래서 리액트에서 상태라는 개념에 대해 알고 있어야 앞으로의 내용이 이해가 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 1초에 한번씩 count의 숫자를 1씩 증가시켜서 렌더링을 하도록 만드는 것이다. 문법적으로는 잘 동작하는 코드처럼 보이지만, 리액트에서는 상태값이 변화될 때 마다 재렌더링이 되기 때문에 setInterval 함수가 여러번 다시 실행되어 에러가 난다. 나의 경우에는 숫자가 변하는 속도가 갈수록 빨라졌다. (setInterval이 여러 번 실행되어 중첩된 것으로 추정)&lt;/p&gt;
&lt;pre id=&quot;code_1653480890914&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

export default function Example() {
  let [count, setCount] = useState(0);

  setInterval(() =&amp;gt; {
    setCount(count += 1);
  }, 1000);

  return (
    &amp;lt;div&amp;gt;{count}&amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useEffect를 활용하여 setInterval 한번만 등록하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setInterval이 여러번 실행되는 것을 방지하기 위해 useEffect를 통해서 처음 렌더링 시에만 setInterval을 등록해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1653481415438&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useState } from &quot;react&quot;;

export default function Example() {
  let [count, setCount] = useState(0);

  useEffect(() =&amp;gt; {
    setInterval(() =&amp;gt; {
      setCount(count += 1);
    }, 1000);
  }, []);

  return (
    &amp;lt;div&amp;gt;{count}&amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect의 dependency에 아무것도 선언하지 않아서 컴포넌트가 최초 마운트 될 때만 setInterval이 등록되도록 만들었다. 그러나 이 방법도 완벽한 방법은 아니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1602&quot; data-origin-height=&quot;111&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VOvww/btrC8C8CfH8/d3KmHr03unkvxziGJ4zYy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VOvww/btrC8C8CfH8/d3KmHr03unkvxziGJ4zYy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VOvww/btrC8C8CfH8/d3KmHr03unkvxziGJ4zYy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVOvww%2FbtrC8C8CfH8%2Fd3KmHr03unkvxziGJ4zYy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1602&quot; height=&quot;111&quot; data-origin-width=&quot;1602&quot; data-origin-height=&quot;111&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 주는 경고 메세지를 보면, useEffect가 다시 렌더될 때마다 변수 값을 잃어버릴 수 있다고 경고하고 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useRef를 사용하여 interval을 기억하도록 만들기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useRef는 current에 참조 값을 저장해두어 사용하는 훅(Hook)이다. useRef에 interval을 저장해두어 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1653483268162&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useState, useRef } from &quot;react&quot;;

export default function Example() {
  let [count, setCount] = useState(0);
  const interval = useRef();

  useEffect(() =&amp;gt; {
    interval.current = setInterval(() =&amp;gt; {
      setCount(prev =&amp;gt; prev + 1);
    }, 1000);

    return () =&amp;gt; {
      clearInterval(interval.current);
    }
  }, []);

  return (
    &amp;lt;div&amp;gt;{count}&amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect의 return 값으로 역시 clearInterval을 사용하여 메모리 누수를 방지한다. useEffect의 return 값으로 clearInterval을 호출하면 컴포넌트가 언마운트 될 때 실행된다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>react</category>
      <category>setInterval</category>
      <category>useEffect</category>
      <category>useRef</category>
      <category>리액트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/65</guid>
      <comments>https://babycoder05.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-useRef%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-%ED%98%84%EB%AA%85%ED%95%98%EA%B2%8C-setInterval-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#entry65comment</comments>
      <pubDate>Wed, 25 May 2022 21:56:20 +0900</pubDate>
    </item>
    <item>
      <title>간단하게 파일 다운로드 기능 HTML로 구현하기</title>
      <link>https://babycoder05.tistory.com/entry/%EA%B0%84%EB%8B%A8%ED%95%98%EA%B2%8C-%ED%8C%8C%EC%9D%BC-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EA%B8%B0%EB%8A%A5-HTML%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 앱을 만들다가 회사 소개서 PDF 파일을 웹에서 다운로드 할 수 있도록 버튼을 만들어야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 선생님 구글에 검색해보니 대다수가 백엔드 개발자들이 구현한 예제들 뿐이었다. 이 간단한 기능을 만들기 위해 서버를 만들 필요는 없다고 생각했기에 더 구글링을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 HTML 만으로 다운로드 기능을 만들 수 있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1652757278143&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;./images/파일명.png&quot; download=&quot;&quot;&amp;gt;회사 소개서 다운로드&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드를 제공할 파일을 프로젝트 폴더 안에 담아두고 a태그의 경로로 지정해주면 간단하게 구현이 가능했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 배운 거 기록하기!&lt;/p&gt;</description>
      <category>코딩 공부 일지/HTML5</category>
      <category>HTML</category>
      <category>다운로드</category>
      <category>파일</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/64</guid>
      <comments>https://babycoder05.tistory.com/entry/%EA%B0%84%EB%8B%A8%ED%95%98%EA%B2%8C-%ED%8C%8C%EC%9D%BC-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EA%B8%B0%EB%8A%A5-HTML%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0#entry64comment</comments>
      <pubDate>Tue, 17 May 2022 12:19:47 +0900</pubDate>
    </item>
    <item>
      <title>비전공자 프론트엔드 개발자 취업 성공기</title>
      <link>https://babycoder05.tistory.com/entry/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%97%85-%EC%84%B1%EA%B3%B5%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;6개월 간의 노력 끝에 프론트엔드 개발자로 취업하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비전공자로 시작하여 과연 취업할 수 있을까 망망대해에 놓인 기분이었다. 국비지원 학원을 통해 공부하면서 과연 이 커리큘럼으로 취업할 수 있을까에 대한 고민, 그리고 스스로 공부하는 방법을 찾아가는 과정에 대한 고민 등 많은 생각의 과정들이 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 프론트엔드 개발자로 취업에 성공하긴 했지만, 아직도 내 실력에 대한 의문들이 가득하고 이 빈틈을 메우기 위해 계속해서 공부하고 있다. 그렇다고 하지만 취업을 준비할 때 만큼의 열정은 좀 식은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취업하게 된 회사는 스타트업으로 30명 남짓되는 구성원이 있는 회사다. 다들 으쌰으쌰 하는 좋은 분위기이지만, 사실 코드 리뷰 문화도 없고 사수가 전담마크해서 가르쳐주는 분위기가 아니라서 각자 알아서 잘 해야 하는 것 같다. 코드 리뷰 문화가 있는 곳으로 취업했다면 좋았겠지만, 내 실력이 아직은 좋은 회사로 들어가기엔 부족한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이력서를 대략 4~50 군데 정도 넣었고, 그 중에는 코딩 테스트를 요구하는 곳, 라이브 코딩을 요구하는 곳도 있었다. 그런 곳들은 대부분 광탈을 했다. 코딩 테스트를 따로 준비하지 않고, 프로그래머스에서 대충 몇 문제 풀어본 게 다였다. 코딩 테스트는 단기간에 잘 할 수 있는 영역은 아닌 것 같았다. (물론, 천재들은 단기간에 해도 잘 할 수 있다.) 라이브 코딩이나 구현과제를 요구하는 곳은 말 그대로 리액트를 통해 구현 과제가 주어지면 그것을 구현해서 기한 내에 제출하거나 면접관들이 보는 앞에서 구현을 해야했다. 도저히 못 풀겠는 문제도 있었고, 전에 해봤던 거라 간단하게 구현할 수 있는 문제도 있었다. 그러나 통과하더라도 기술 면접에서 물어보는 것에 대답을 못하거나 그랬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술 면접을 위해서는 인터넷에 프론트엔드 기술 면접 예상 질문을 검색해서 나오는 것들을 위주로 공부했다. 대부분 브라우저 렌더링 과정, CSS 속성, JavaScript ES6와 관련된 질문들 등 공통으로 나오는 질문들이 있었고, 아예 몰라서 대답하지 못하는 질문들도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접을 통해 느낀 것은 대부분의 스타트업 회사들은 내가 아무리 신입으로 지원했다 하더라도 당장 바로 투입해서 써먹을 수 있을 정도로 지식과 경험을 갖춘 지원자를 원하는 것 같았다. 내가 물론 그런 지원자가 되면 좋았겠지만, 나는 돈을 버는 것이 급했기에 빨리 취업을 하고 싶었고, 회사들이 요구하는 실력보다 부족하다는 느낌을 받았지만 계속해서 구직활동을 했다. 최종적으로 3군데에서 합격 연락을 받았고, 그 중 복지와 연봉이 가장 마음에 드는 곳으로 결정을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 회사에서 맡게 된 일은 여기에 말할 수는 없지만, 내가 구현할 수 있는 정도의 일을 주셔서 열심히 하고 있다. 확실히 스타트업으로 오게 되니 복지가 전에 일하던 환경과는 많이 달랐다. 일단, 재택근무 제도가 있고, 자율출퇴근제라 출퇴근 시간을 마음대로 조정할 수 있어서 매우 만족스럽다. 하지만 코드 리뷰 문화가 없는 것은 좀 많이 아쉽다. 내가 잘 하고 있는 건지 아닌지 알기가 어렵기 때문이다. 그러나 100% 만족스러운 회사를 찾는 것은 매우 어렵고, 어쨌든 간에 프로그래머는 문제를 해결해야 하는 사람이기 때문에 선배가 날 위해 무언가를 해결해 줄 수 있을 거라는 기대를 갖는 것보다는 내가 문제를 해결하겠다는 마음으로 접근해야 앞으로 어떤 일을 받더라도 해낼 수 있을 거라는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 드디어 개발자 인생에 첫 발을 내딛었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직은 두려움으로 가득하지만 계속해서 실력을 쌓아서 복잡한 앱도 개발할 수 있는 잘하는 개발자가 되고 싶다.&lt;/p&gt;</description>
      <category>생각 끄적이기</category>
      <category>개발자</category>
      <category>비전공자</category>
      <category>취업</category>
      <category>프론트엔드</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/63</guid>
      <comments>https://babycoder05.tistory.com/entry/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%97%85-%EC%84%B1%EA%B3%B5%EA%B8%B0#entry63comment</comments>
      <pubDate>Sun, 15 May 2022 22:46:55 +0900</pubDate>
    </item>
    <item>
      <title>CRA로 생성된 프로젝트에 절대경로 설정하기</title>
      <link>https://babycoder05.tistory.com/entry/CRA%EB%A1%9C-%EC%83%9D%EC%84%B1%EB%90%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EC%A0%88%EB%8C%80%EA%B2%BD%EB%A1%9C-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;create-react-app을 통해 프로젝트를 생성하면 상대 경로로 import를 하게 되는데, depth가 깊어질 수록 상대 경로 import가 불편해지기 때문에 절대 경로를 사용하는 것이 편하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;사용방법&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- tsconfig.json / jsconfig.json&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json 과 같은 레벨에 위의 이름으로 파일을 생성해준다.(TypeScript/JavaScript)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 json 파일 내부에 아래와 같이 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1651805272226&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;src&quot;
  },
  &quot;include&quot;: [&quot;src&quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 다시 실행해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1651805357946&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import '../components/Component';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서&lt;/p&gt;
&lt;pre id=&quot;code_1651805390645&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'components/component';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 변경해서 작성할 수 있다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/62</guid>
      <comments>https://babycoder05.tistory.com/entry/CRA%EB%A1%9C-%EC%83%9D%EC%84%B1%EB%90%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EC%A0%88%EB%8C%80%EA%B2%BD%EB%A1%9C-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0#entry62comment</comments>
      <pubDate>Fri, 6 May 2022 11:50:08 +0900</pubDate>
    </item>
    <item>
      <title>JS toLocaleDateString 을 이용하여 Date 표현 변경하기</title>
      <link>https://babycoder05.tistory.com/entry/JS-toLocaleDateString-%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-Date-%ED%91%9C%ED%98%84-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트로 날짜를 나타낼 때, Date 함수를 사용하면 된다는 건 알았지만, 그 날짜를 한국식으로 표현할 때 일일이 getFullYear() 나 getMonth() 등을 이용해서 커스터마이징을 해서 사용했었는데, toLocaleDateString() 을 이용하면 아주 간단하게 표현 방법을 변경할 수 있다는 것을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 오늘은 이에 관련해서 정리를 해보고자 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;JavaScript 로 날짜와 시간 표현하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 날짜를 가져오려면 아래 코드와 같이 Date 생성자 함수를 호출해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1651631553508&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const today = new Date();
console.log(today);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔에 찍힌 모습을 보면 아래와 같이 출력된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;25&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bygOKA/btrBavj8Roa/oNqUA51LLz8ZFOkCLrhSrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bygOKA/btrBavj8Roa/oNqUA51LLz8ZFOkCLrhSrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bygOKA/btrBavj8Roa/oNqUA51LLz8ZFOkCLrhSrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbygOKA%2FbtrBavj8Roa%2FoNqUA51LLz8ZFOkCLrhSrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;307&quot; height=&quot;25&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;25&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표현을 커스터마이징 하려면 아래와 같이 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1651631803891&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const today = new Date();
const dateString = today.toLocaleDateString('ko-KR', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
});
console.log(dateString);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔에는 아래와 같이 나타난다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;97&quot; data-origin-height=&quot;26&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WO0pR/btrBbQuhAZS/ZiG60nX0z0oyN2qcbmLhiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WO0pR/btrBbQuhAZS/ZiG60nX0z0oyN2qcbmLhiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WO0pR/btrBbQuhAZS/ZiG60nX0z0oyN2qcbmLhiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWO0pR%2FbtrBbQuhAZS%2FZiG60nX0z0oyN2qcbmLhiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;97&quot; height=&quot;26&quot; data-origin-width=&quot;97&quot; data-origin-height=&quot;26&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 많은 옵션을 보기 위해 해당 문서로 가서 확인했더니 아래와 같은 옵션들이 있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1651631900818&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface DateTimeFormatOptions {
    localeMatcher?: &quot;best fit&quot; | &quot;lookup&quot; | undefined;
    weekday?: &quot;long&quot; | &quot;short&quot; | &quot;narrow&quot; | undefined;
    era?: &quot;long&quot; | &quot;short&quot; | &quot;narrow&quot; | undefined;
    year?: &quot;numeric&quot; | &quot;2-digit&quot; | undefined;
    month?: &quot;numeric&quot; | &quot;2-digit&quot; | &quot;long&quot; | &quot;short&quot; | &quot;narrow&quot; | undefined;
    day?: &quot;numeric&quot; | &quot;2-digit&quot; | undefined;
    hour?: &quot;numeric&quot; | &quot;2-digit&quot; | undefined;
    minute?: &quot;numeric&quot; | &quot;2-digit&quot; | undefined;
    second?: &quot;numeric&quot; | &quot;2-digit&quot; | undefined;
    timeZoneName?: &quot;long&quot; | &quot;short&quot; | undefined;
    formatMatcher?: &quot;best fit&quot; | &quot;basic&quot; | undefined;
    hour12?: boolean | undefined;
    timeZone?: string | undefined;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간도 아래와 같이 표현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1651632234419&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const today = new Date();
const time = today.toLocaleDateString('ko-KR', {
    weekday: 'long',
    hour: 'numeric',
    hour12: true,
    minute: 'numeric',
    second: 'numeric'
});
console.log(time);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;137&quot; data-origin-height=&quot;25&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhW0oT/btrBbLmRkep/CpbXsrncVyTLO1z1OpjfJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhW0oT/btrBbLmRkep/CpbXsrncVyTLO1z1OpjfJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhW0oT/btrBbLmRkep/CpbXsrncVyTLO1z1OpjfJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhW0oT%2FbtrBbLmRkep%2FCpbXsrncVyTLO1z1OpjfJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;137&quot; height=&quot;25&quot; data-origin-width=&quot;137&quot; data-origin-height=&quot;25&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/JavaScript</category>
      <category>JavaScript</category>
      <category>toLocaleDateString</category>
      <category>날짜</category>
      <category>변경</category>
      <category>시간</category>
      <category>자바스크립트</category>
      <category>표현</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/61</guid>
      <comments>https://babycoder05.tistory.com/entry/JS-toLocaleDateString-%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-Date-%ED%91%9C%ED%98%84-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0#entry61comment</comments>
      <pubDate>Wed, 4 May 2022 11:45:36 +0900</pubDate>
    </item>
    <item>
      <title>styled-components를 이용하여 스타일링 변경이 가능한 컴포넌트 만들기</title>
      <link>https://babycoder05.tistory.com/entry/styled-components%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81-%EB%B3%80%EA%B2%BD%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;styled-components를 사용하는 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS in JS 는 웹 개발 방식의 한 방법으로 JS 파일 안에 CSS 작성해서 경우에 따라 JS 로 CSS 변경을 용이하게 만들어 스타일링 변경이 가능한 컴포넌트를&amp;nbsp; 사용할 수 있게 해주는 하나의 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tagged Template Literal을 이용하여 함수의 파라미터로 템플릿 리터럴을 받는 기술인데 복잡하므로 원리를 이해할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1651564316588&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i styled-components&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기본 문법&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1651564481837&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import styled from 'styled-components';

// Create a Title component that'll render an &amp;lt;h1&amp;gt; tag with some styles
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

// Create a Wrapper component that'll render a &amp;lt;section&amp;gt; tag with some styles
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

// Use Title and Wrapper like any other React component &amp;ndash; except they're styled!
function App() {
	return (
    &amp;lt;Wrapper&amp;gt;
        &amp;lt;Title&amp;gt;
          Hello World!
        &amp;lt;/Title&amp;gt;
    &amp;lt;/Wrapper&amp;gt;
);

export default App;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;styled-components 를 처음 사용하면서 헷갈렸던 것은 기존에 JSX 문법으로 컴포넌트를 불러와 return을 시키는 것이 아니라 html 태그 대신 스타일링이 적용된 컴포넌트를 상단에 구성하고 JSX 문법으로 사용하는 것이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS 문법과 매우 유사하고 SASS와 같이 nesting 문법이 사용 가능하기 때문에 조금만 익숙해지면 매우 쉽게 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;styled-components를 이용해서 재사용 가능한 버튼 컴포넌트 만들기&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1651564899968&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ./components/Button.jsx

import styled, { css } from 'styled-components';
import { darken, lighten } from 'polished';

const colorStyles = css`
  ${({theme, color}) =&amp;gt; {
    const selected = theme.palette[color];
    return css`
      background: ${selected};
      &amp;amp;:hover {
        background: ${lighten(0.1, selected)};
      }
      &amp;amp;:active {
        background: ${darken(0.1, selected)}
      }
      ${props =&amp;gt; props.outline &amp;amp;&amp;amp; 
      css`
        color: ${selected};
        background: none;
        border: 1px solid ${selected};
        &amp;amp;:hover {
          background: ${selected};
          color: white;
        }
      `}
    `;
    /* 
    props =&amp;gt; {
      const selected = props.theme.palette[props.color]
    }
    */
  }}
`;

const sizes = {
  large: {
    padding: '1.5rem 2rem',
    fontSize: '1.5rem'
  },
  medium: {
    padding: '1rem 1.5rem',
    fontSize: '1.25rem'
  },
  small: {
    padding: '0.7rem 1rem',
    fontSize: '1rem'
  }
};

const sizeStyles = css`
  ${({size}) =&amp;gt; css`
    padding: ${sizes[size].padding};
    font-size: ${sizes[size].fontSize};
  `}
`;

const fullWidthStyle = css`
  ${props =&amp;gt; props.fullWidth &amp;amp;&amp;amp;
  css`
    width: 100%;
    justify-content: center;
    &amp;amp;:not(:first-child) {
      margin-left: 0;
      margin-top: 1rem;
    }
  `}
`;

const StyledButton = styled.button`
  /* 공통 스타일 */
  display: inline-flex;
  outline: none;
  border: none;
  border-radius: 4px;
  color: white;
  font-weight: bold;
  cursor: pointer;
  padding-left: 1rem;
  padding-right: 1rem;

  /* 크기 */
  ${sizeStyles}

  /* 색상 */
  ${colorStyles}

  /* 기타 */
  &amp;amp;:not(:first-child) {
    margin-left: 1rem;
  }

  ${fullWidthStyle}
`;

Button.defaultProps = {
  color: 'blue',
  size: 'medium'
}

export default function Button({children, color, size, outline, fullWidth, ...rest}) {
  return &amp;lt;StyledButton 
    color={color} 
    size={size} 
    outline={outline} 
    fullWidth={fullWidth}
    {...rest}&amp;gt;
      {children}
    &amp;lt;/StyledButton&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;styled-components를 이용해 color, size, outline, fullWidth 적용 여부를 선택할 수 있는 스타일링 변경이 가능한 컴포넌트가 만들어졌다. (위의 예제는 '벨로퍼트와 함께 하는 모던 리액트'를 참고해서 공부했습니다.)&lt;/p&gt;
&lt;figure id=&quot;og_1651565057352&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;3. styled-components &amp;middot; GitBook&quot; data-og-description=&quot;03. styled-components 이번에 배워볼 기술은 CSS in JS 라는 기술입니다. 이 문구가 뜻하는 그대로, 이 기술은 JS 안에 CSS 를 작성하는 것을 의미하는데요, 우리는 이번 튜토리얼에서 해당 기술을 사용하&quot; data-og-host=&quot;react.vlpt.us&quot; data-og-source-url=&quot;https://react.vlpt.us/styling/03-styled-components.html&quot; data-og-url=&quot;https://react.vlpt.us/styling/03-styled-components.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bfPU27/hyOffuR54S/ZAVCHthQwnDOdHNDpKEbqk/img.png?width=729&amp;amp;height=734&amp;amp;face=0_0_729_734,https://scrap.kakaocdn.net/dn/gW6gH/hyOfhffE7R/iKfPJy8zHwQAVpmEeWsfzk/img.png?width=729&amp;amp;height=734&amp;amp;face=0_0_729_734,https://scrap.kakaocdn.net/dn/BRe35/hyOgwuZPqJ/XTch8Q2KDUGEGzmvKR8OlK/img.png?width=638&amp;amp;height=661&amp;amp;face=0_0_638_661&quot;&gt;&lt;a href=&quot;https://react.vlpt.us/styling/03-styled-components.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react.vlpt.us/styling/03-styled-components.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bfPU27/hyOffuR54S/ZAVCHthQwnDOdHNDpKEbqk/img.png?width=729&amp;amp;height=734&amp;amp;face=0_0_729_734,https://scrap.kakaocdn.net/dn/gW6gH/hyOfhffE7R/iKfPJy8zHwQAVpmEeWsfzk/img.png?width=729&amp;amp;height=734&amp;amp;face=0_0_729_734,https://scrap.kakaocdn.net/dn/BRe35/hyOgwuZPqJ/XTch8Q2KDUGEGzmvKR8OlK/img.png?width=638&amp;amp;height=661&amp;amp;face=0_0_638_661');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;3. styled-components &amp;middot; GitBook&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;03. styled-components 이번에 배워볼 기술은 CSS in JS 라는 기술입니다. 이 문구가 뜻하는 그대로, 이 기술은 JS 안에 CSS 를 작성하는 것을 의미하는데요, 우리는 이번 튜토리얼에서 해당 기술을 사용하&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react.vlpt.us&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 컴포넌트를 가져다가 사용할 때는 아래와 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1651565207787&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.jsx

import { useState } from 'react';
import styled, { css, ThemeProvider } from 'styled-components';
import Button from './components/Button';

const Block = styled.div`
    width: 512px;
    margin: 0 auto;
    margin-top: 4rem;
    border: 1px solid black;
    padding: 1rem;
`;

const ButtonGroup = styled.div`
    &amp;amp;:not(:first-child) {
        margin-top: 1rem;
    }
`;

export default function Styled() {
    const [dialog, setDialog] = useState(false);
    const onClick = () =&amp;gt; {
        setDialog(true);
    }
    const onConfirm = () =&amp;gt; {
        setDialog(false);
    }
    const onCancel = () =&amp;gt; {
        setDialog(false);
    }
    return (
        &amp;lt;main&amp;gt;
            &amp;lt;ThemeProvider
                theme={{
                    palette: {
                        blue: '#228be6',
                        gray: '#495057',
                        pink: '#f06595'
                    }
                }}
            &amp;gt;
                &amp;lt;Block&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button size=&quot;large&quot;&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                        &amp;lt;Button&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                        &amp;lt;Button size=&quot;small&quot;&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button color=&quot;gray&quot; size=&quot;large&quot;&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;gray&quot;&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;gray&quot; size=&quot;small&quot;&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button color=&quot;pink&quot; size=&quot;large&quot;&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;pink&quot;&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;pink&quot; size=&quot;small&quot;&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button size=&quot;large&quot; outline&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;gray&quot; outline&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;pink&quot; size=&quot;small&quot; outline&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button size=&quot;large&quot; fullWidth&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button size=&quot;large&quot; color=&quot;gray&quot; fullWidth&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button size=&quot;large&quot; color=&quot;pink&quot; fullWidth onClick={onClick}&amp;gt;
                        삭제
                        &amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                &amp;lt;/Block&amp;gt;
            &amp;lt;/ThemeProvider&amp;gt;
        &amp;lt;/main&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;709&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIHXHz/btrA7PchX5i/dsZE5E3uwkfmMJ4638aC51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIHXHz/btrA7PchX5i/dsZE5E3uwkfmMJ4638aC51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIHXHz/btrA7PchX5i/dsZE5E3uwkfmMJ4638aC51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIHXHz%2FbtrA7PchX5i%2FdsZE5E3uwkfmMJ4638aC51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;916&quot; height=&quot;709&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;709&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;styled-components로 애니메이션 적용하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;styled-components를 이용하여 모달 창 애니메이션을 구현해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단, 먼저 모달 창 컴포넌트의 코드는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1651565698790&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ./components/Dialog.jsx

import { useEffect, useState } from 'react';
import styled, { keyframes, css } from 'styled-components';
import Button from &quot;./Button&quot;;

const fadeIn = keyframes`
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
`;

const fadeOut = keyframes`
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
`;

const slideUp = keyframes`
  from {
    transform: translateY(200px);
  }
  to {
    transform: translateY(0px);
  }
`;

const slideDown = keyframes`
  from {
    transform: translateY(0px);
  }
  to {
    transform: translateY(200px);
  }
`;

const DarkBackground = styled.div`
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.8);

  animation-duration: 0.25s;
  animation-timing-function: ease-out;
  animation-name: ${fadeIn};
  animation-fill-mode: forwards;

  ${props =&amp;gt;
    props.disappear &amp;amp;&amp;amp;
    css`
      animation-name: ${fadeOut};
    `}
`;

const DialogBlock = styled.div`
  width: 320px;
  padding: 1.5rem;
  background: white;
  border-radius: 2px;
  h3{
    margin: 0;
    font-size: 1.5rem;
  }
  p{
    font-size: 1.125rem;
  }

  animation-duration: 0.25s;
  animation-timing-function: ease-out;
  animation-name: ${slideUp};
  animation-fill-mode: forwards;

  ${props =&amp;gt; 
    props.disappear &amp;amp;&amp;amp;
    css`
      animation-name: ${slideDown};
    `}
`;

const ButtonGroup = styled.div`
  margin-top: 3rem;
  display: flex;
  justify-content: flex-end;
`;

const ShortMarginButton = styled(Button)`
  &amp;amp;:not(:first-child) {
    margin-left: 0.5rem;
  }
`;

Dialog.defaultProps = {
  confirmText: '확인',
  cancelText: '취소'
}

export default function Dialog({ 
  title, 
  children, 
  confirmText, 
  cancelText, 
  onConfirm, 
  onCancel, 
  visible 
}) {
  const [animate, setAnimate] = useState(false);
  const [localVisible, setLocalVisible] = useState(visible);

  useEffect(() =&amp;gt; {
    // visible 값이 true -&amp;gt; false가 되는 것을 감지
    if(localVisible &amp;amp;&amp;amp; !visible) {
      setAnimate(true);
      setTimeout(() =&amp;gt; setAnimate(false), 250);
    }
    setLocalVisible(visible);
  }, [localVisible, visible]);

  if(!animate &amp;amp;&amp;amp; !localVisible) return null;
  return (
    &amp;lt;DarkBackground disappear={!visible}&amp;gt;
      &amp;lt;DialogBlock disappear={!visible}&amp;gt;
        &amp;lt;h3&amp;gt;{title}&amp;lt;/h3&amp;gt;
        &amp;lt;p&amp;gt;{children}&amp;lt;/p&amp;gt;
        &amp;lt;ButtonGroup&amp;gt;
          &amp;lt;ShortMarginButton 
          color=&quot;gray&quot; 
          size=&quot;small&quot;
          onClick={onCancel}&amp;gt;
            {cancelText}
          &amp;lt;/ShortMarginButton&amp;gt;
          &amp;lt;ShortMarginButton 
          color=&quot;pink&quot; 
          size=&quot;small&quot;
          onClick={onConfirm}&amp;gt;
            {confirmText}
          &amp;lt;/ShortMarginButton&amp;gt;
        &amp;lt;/ButtonGroup&amp;gt;
      &amp;lt;/DialogBlock&amp;gt;
    &amp;lt;/DarkBackground&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;styled-components 에서 애니메이션을 적용하기 위해서는 keyframes를 import 해서 사용할 수 있다. state 값을 설정해 상태 값에 따라 모달 창이 보였다가 사라지게 구현하는 기본 원리를 두고, 거기에 localVisible 이라는 상태 값을 모달 창 컴포넌트에 내부적으로 추가해 setTimeout을 이용하여 상태값을 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모달 창을 불러오는 곳에서는 아래와 같이 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1651565967714&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.jsx

import { useState } from 'react';
import styled, { ThemeProvider } from 'styled-components';
import Button from './components/Button';
import Dialog from './components/Dialog';

const Block = styled.div`
    width: 512px;
    margin: 0 auto;
    margin-top: 4rem;
    border: 1px solid black;
    padding: 1rem;
`;

const ButtonGroup = styled.div`
    &amp;amp;:not(:first-child) {
        margin-top: 1rem;
    }
`;

export default function Styled() {
    const [dialog, setDialog] = useState(false);
    const onClick = () =&amp;gt; {
        setDialog(true);
    }
    const onConfirm = () =&amp;gt; {
        setDialog(false);
    }
    const onCancel = () =&amp;gt; {
        setDialog(false);
    }
    return (
        &amp;lt;main&amp;gt;
            &amp;lt;ThemeProvider
                theme={{
                    palette: {
                        blue: '#228be6',
                        gray: '#495057',
                        pink: '#f06595'
                    }
                }}
            &amp;gt;
                &amp;lt;Block&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button size=&quot;large&quot;&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                        &amp;lt;Button&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                        &amp;lt;Button size=&quot;small&quot;&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button color=&quot;gray&quot; size=&quot;large&quot;&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;gray&quot;&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;gray&quot; size=&quot;small&quot;&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button color=&quot;pink&quot; size=&quot;large&quot;&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;pink&quot;&amp;gt;BUTTON&amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;pink&quot; size=&quot;small&quot;&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button size=&quot;large&quot; outline&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;gray&quot; outline&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button color=&quot;pink&quot; size=&quot;small&quot; outline&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                    &amp;lt;ButtonGroup&amp;gt;
                        &amp;lt;Button size=&quot;large&quot; fullWidth&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button size=&quot;large&quot; color=&quot;gray&quot; fullWidth&amp;gt;
                        BUTTON
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;Button size=&quot;large&quot; color=&quot;pink&quot; fullWidth onClick={onClick}&amp;gt;
                        삭제
                        &amp;lt;/Button&amp;gt;
                    &amp;lt;/ButtonGroup&amp;gt;
                &amp;lt;/Block&amp;gt;
                &amp;lt;Dialog 
                title=&quot;정말로 삭제하시겠습니까?&quot;
                confirmText=&quot;삭제&quot;
                onConfirm={onConfirm}
                onCancel={onCancel}
                visible={dialog}&amp;gt;
                    데이터를 정말로 삭제하시겠습니까?
                &amp;lt;/Dialog&amp;gt;
            &amp;lt;/ThemeProvider&amp;gt;
        &amp;lt;/main&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제 버튼을 누르면 모달 창이 보이고, '삭제' 혹은 '취소' 버튼을 누르면 모달 창이 사라지도록 구현되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;사용 후기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 공통으로 사용되는 컴포넌트가 있을 경우, CSS 파일이 분리되어 있다면 JS로 수정하기가 불편했던 점이 있었는데, styled-components를 이용해서 수정이 가능하게 되어 컴포넌트 재사용이 훨씬 용이해진 것을 느낄 수 있었다.&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>react</category>
      <category>styled-components</category>
      <category>리액트</category>
      <category>컴포넌트 재사용</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/60</guid>
      <comments>https://babycoder05.tistory.com/entry/styled-components%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81-%EB%B3%80%EA%B2%BD%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0#entry60comment</comments>
      <pubDate>Tue, 3 May 2022 17:24:30 +0900</pubDate>
    </item>
    <item>
      <title>리액트 React 에서 탭 기능 구현하기</title>
      <link>https://babycoder05.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-React-%EC%97%90%EC%84%9C-%ED%83%AD-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 탭 기능 구현은 state를 이용해서 간단하게 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완성예제&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQL2KA/btrzEBx41uk/ZLxwYxSWkFXd0wgIzPvnpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQL2KA/btrzEBx41uk/ZLxwYxSWkFXd0wgIzPvnpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQL2KA/btrzEBx41uk/ZLxwYxSWkFXd0wgIzPvnpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQL2KA%2FbtrzEBx41uk%2FZLxwYxSWkFXd0wgIzPvnpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;898&quot; height=&quot;358&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스코드&lt;/p&gt;
&lt;pre id=&quot;code_1650181980713&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;;
import styles from './scss/tab.module.css';

export default function Tab() {
  const data = [
    {
      id: 0,
      title: &quot;HTML&quot;,
      description: &quot;HTML (HyperText Markup Language) is the most basic building block of the Web. It defines the meaning and structure of web content. Other technologies besides HTML are generally used to describe a web page's appearance/presentation (CSS) or functionality/behavior (JavaScript).&quot;
    },
    {
      id: 1,
      title: &quot;CSS&quot;,
      description: &quot;Cascading Style Sheets (CSS) is a stylesheet language used to describe the presentation of a document written in HTML or XML (including XML dialects such as SVG, MathML or XHTML). CSS describes how elements should be rendered on screen, on paper, in speech, or on other media.&quot;
    },
    {
      id: 2,
      title: &quot;JavaScript&quot;,
      description: &quot;JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions. While it is most well-known as the scripting language for Web pages, many non-browser environments also use it, such as Node.js, Apache CouchDB and Adobe Acrobat.&quot;
    }
  ];
  const [index, setIndex] = useState(0);

  return (
    &amp;lt;section className={styles.tabContainer}&amp;gt;
      &amp;lt;ul className={styles.tabMenu}&amp;gt;
        {data.map(item =&amp;gt; (
          &amp;lt;li 
          key={item.id}
          className={index === item.id ? styles.active : null}
          onClick={() =&amp;gt; setIndex(item.id)}&amp;gt;{item.title}&amp;lt;/li&amp;gt;
        ))}
      &amp;lt;/ul&amp;gt;
      {data.filter(item =&amp;gt; index === item.id).map(item =&amp;gt; (
        &amp;lt;div className={styles.tabContent}&amp;gt;{item.description}&amp;lt;/div&amp;gt;
      ))}
    &amp;lt;/section&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>react</category>
      <category>구현</category>
      <category>리액트</category>
      <category>탭</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/59</guid>
      <comments>https://babycoder05.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-React-%EC%97%90%EC%84%9C-%ED%83%AD-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0#entry59comment</comments>
      <pubDate>Sun, 17 Apr 2022 17:09:55 +0900</pubDate>
    </item>
    <item>
      <title>useParams 로 세부 페이지 라우팅 구현하기</title>
      <link>https://babycoder05.tistory.com/entry/useParams-%EB%A1%9C-%EC%84%B8%EB%B6%80-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useParams 란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-router에서 제공하는 Hooks 중 하나로 React 16.8 버전 이상에서만 구동이 가능하다. Parameter(파라미터) 값을 URL을 통해서 넘겨서 넘겨받은 페이지에서 사용할 수 있도록 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 여러 개의 영화 정보가 담겨있는 데이터를 출력해준다고 가정할 때, 영화 제목을 클릭해서 세부 페이지로 이동을 하도록 구현한다면, 영화의 id 값을 URL로 넘겨 세부 페이지에 id 값에 해당하는 영화 정보만 출력하도록 만들 수 있도록 도와준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useParams 문법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useParams를 사용하기 위해서는 먼저, react-router-dom을 설치해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1649920260760&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install react-router-dom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 라우팅을 구현할 페이지들을 생성한 뒤에 디테일 페이지의 path에 :id 값을 넣어준다. 여기에서 id는 객체의 key 값으로 개발자의 편의에 따라 다른 명칭으로 만들 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649920373427&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.jsx

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import './App.css';
import Home from './routes/Home';
import Detail from './routes/Detail';
import Context from './context/context';

function App() {
  return (
    &amp;lt;Context&amp;gt;
      &amp;lt;Router&amp;gt;
        &amp;lt;Routes&amp;gt;
          &amp;lt;Route path=&quot;/&quot; exact element={&amp;lt;Home /&amp;gt;} /&amp;gt;
          &amp;lt;Route path=&quot;/detail/:id&quot; element={&amp;lt;Detail /&amp;gt;} /&amp;gt;
        &amp;lt;/Routes&amp;gt;
      &amp;lt;/Router&amp;gt;
    &amp;lt;/Context&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 데이터들이 나열되어 있는 리스트 페이지에서 링크 값에 넘겨줄 id 정보를 담아준다. 이 정보는 객체의 value 값으로 파라미터로 넘겨지는 데이터는 key와 value 값을 가진 객체 형태로 넘겨진다는 것을 알 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1649920471734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// List.jsx

import { useEffect, useContext, useState } from 'react';
import { Link } from &quot;react-router-dom&quot;;
import { Context } from '../context/context';

export default function Home() {
  const context = useContext(Context);
  const [data, setData] = useState([]);

  useEffect(() =&amp;gt; {
    fetch('../data.json')
    .then(res =&amp;gt; res.json())
    .then(res =&amp;gt; {
      context.data = res.data;
      setData(res.data);
    })
  }, [])

  return (
    &amp;lt;section&amp;gt;
      {data.map(item =&amp;gt; (
        &amp;lt;Link to={`/detail/${item.id}`} key={item.id}&amp;gt;
          &amp;lt;h2&amp;gt;{item.title}&amp;lt;/h2&amp;gt;
        &amp;lt;/Link&amp;gt;
      ))}
    &amp;lt;/section&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구성해준 뒤 리스트 중 하나를 클릭하게 되면 id 값을 파라미터로 가지고 라우팅이 이루어진다. 아래의 URL을 보면 객체의 value 값을 넘겨준 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;47&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj4QsO/btrzpkpYStu/ZkoLXKJNCpLZcSovytSPsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj4QsO/btrzpkpYStu/ZkoLXKJNCpLZcSovytSPsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj4QsO/btrzpkpYStu/ZkoLXKJNCpLZcSovytSPsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj4QsO%2FbtrzpkpYStu%2FZkoLXKJNCpLZcSovytSPsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;47&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;47&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시와 같은 데이터가 있고, 이것을 불러온다고 가정해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1649920635734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// data.json

{
  &quot;data&quot;: [
    {
      &quot;id&quot;: 0,
      &quot;title&quot;: &quot;HTML&quot;,
      &quot;description&quot;: &quot;HTML (HyperText Markup Language) is the most basic building block of the Web. It defines the meaning and structure of web content. Other technologies besides HTML are generally used to describe a web page's appearance/presentation (CSS) or functionality/behavior (JavaScript).&quot;
    },
    {
      &quot;id&quot;: 1,
      &quot;title&quot;: &quot;CSS&quot;,
      &quot;description&quot;: &quot;Cascading Style Sheets (CSS) is a stylesheet language used to describe the presentation of a document written in HTML or XML (including XML dialects such as SVG, MathML or XHTML). CSS describes how elements should be rendered on screen, on paper, in speech, or on other media.&quot;
    },
    {
      &quot;id&quot;: 2,
      &quot;title&quot;: &quot;JavaScript&quot;,
      &quot;description&quot;: &quot;JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions. While it is most well-known as the scripting language for Web pages, many non-browser environments also use it, such as Node.js, Apache CouchDB and Adobe Acrobat.&quot;
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터로 넘겨지는 id 값에 따라 원하는 데이터를 불러올 수 있다. 불러올 때 useParams를 이용해서 id 값을 가져오고 그 데이터 값을 이용해서 세부 페이지에서 활용하는 구조로 만들면 된다. 여기서 id 는 객체의 key 값으로 아래와 같이 비구조화 할당으로 꺼내 쓸 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1649920687739&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useContext } from &quot;react&quot;;
import { useParams } from &quot;react-router-dom&quot;;
import { Context } from &quot;../context/context&quot;;

export default function Detail() {
  const {id} = useParams();
  const context = useContext(Context);

  return (
    &amp;lt;section&amp;gt;
      &amp;lt;h1&amp;gt;{context.data[id].title}&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;{context.data[id].description}&amp;lt;/p&amp;gt;
    &amp;lt;/section&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>react</category>
      <category>react-router</category>
      <category>useParams</category>
      <category>리액트</category>
      <category>세부 페이지</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/58</guid>
      <comments>https://babycoder05.tistory.com/entry/useParams-%EB%A1%9C-%EC%84%B8%EB%B6%80-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0#entry58comment</comments>
      <pubDate>Thu, 14 Apr 2022 16:36:25 +0900</pubDate>
    </item>
    <item>
      <title>React useEffect와 addEventListener - window 이벤트 렌더링 규칙</title>
      <link>https://babycoder05.tistory.com/entry/React-useEffect%EC%99%80-addEventListener-window-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B7%9C%EC%B9%99</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useEffect 란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;React 의 함수형 컴포넌트에서 라이프사이클을 감지하기 위한 HOOK 이다.&lt;/b&gt;&lt;/u&gt; 처음에 라이프사이클이란 말을 들었을 때, 너무 거창한 단어가 붙어있는 것 같아서 이해가 잘 되지 않았었다. 그러나 React가 CSR(클라이언트 사이드 렌더링) 방식이라는 점을 기억하고 있으면, 렌더링 규칙에 대해 알고 있어야 컴포넌트 제어가 가능하다는 것을 느낄 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- React 라이프사이클&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 라이프사이클에는 아래와 같은 것들이 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;917&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkErU1/btryrVYUo2j/xs9baNEiIpbFrA2KvV2hk0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkErU1/btryrVYUo2j/xs9baNEiIpbFrA2KvV2hk0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkErU1/btryrVYUo2j/xs9baNEiIpbFrA2KvV2hk0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkErU1%2FbtryrVYUo2j%2Fxs9baNEiIpbFrA2KvV2hk0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1674&quot; height=&quot;917&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;917&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.186%;&quot;&gt;라이프사이클 명&lt;/td&gt;
&lt;td style=&quot;width: 75.814%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.186%;&quot;&gt;componentDidMount&lt;/td&gt;
&lt;td style=&quot;width: 75.814%;&quot;&gt;컴포넌트의 첫번째 렌더링을 마치고 난 뒤 호출되는 메소드다.&amp;nbsp;&lt;br /&gt;이미 컴포넌트가 화면에 나타난 상태이고, 이 시점에 보통 해당 컴포넌트에서 필요로 하는 데이터를 요청하기 위해 axios, fetch 등을 이용해 데이터를 불러온다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.186%;&quot;&gt;shouldComponentUpdate&lt;/td&gt;
&lt;td style=&quot;width: 75.814%;&quot;&gt;컴포넌트가 업데이트 되어야 할지 말지 결정하는 시점이다. 주로 최적화와 관련된 메소드다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.186%;&quot;&gt;componentDidUpdate&lt;/td&gt;
&lt;td style=&quot;width: 75.814%;&quot;&gt;리렌더링이 일어난 후에 호출되는 메소드다.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.186%;&quot;&gt;componentWillUnmount&lt;/td&gt;
&lt;td style=&quot;width: 75.814%;&quot;&gt;컴포넌트가 언마운트 되기 직전에 호출되는 메소드다. 여기서는 주로 DOM에 직접 등록했었던 이벤트를 제거하고, 만약에 setTimeout 을 사용했다면, clearTimeout으로 제거를 해준다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- useEffect 가 라이프사이클을 구분하는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 컴포넌트에서는 각각의 라이프사이클 메소드를 useEffect 하나로 통일시킬 수 있다. 대신 dependency를 선언해줘 시점을 구분한다.&lt;/p&gt;
&lt;pre id=&quot;code_1649062511486&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    console.log('hi')
  }, [/* dependency 선언하는 곳 */])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect 안에 콜백함수를 불러온 뒤 2번째 인자의 배열 안에 업데이트의 기준이 되는 상태를 선언하면 그 상태의 값에 따라 컴포넌트를 업데이트 할 수 있고, 빈 배열을 선언하면 최초 로드 시에만 렌더링이 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;useEffect와 addEventListener&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 window나 document에 addEventListener를 통해 이벤트를 추가할 때, 그냥 사용하게 되면 렌더링 시점이 구분이 되지 않기 때문에 에러가 난다. useEffect를 이용해서 최초 로드 시에 이벤트를 추가해주면 이벤트 감지가 시작되어 에러를 일으키지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 특정 DOM이 마우스의 움직임에 따라 따라다니도록 구현하고 싶었는데, 그냥 이벤트를 등록하지 자꾸 에러가 났다. 구글링을 통해 useEffect를 사용해야 한다는 것을 알게 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1649063649540&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useRef, useState } from 'react';

export default function TextBtn() {
  const textBox = useRef();
  const [circlePosition, setCirclePosition] = useState({
    left: 0,
    top: 0
  })

  useEffect(() =&amp;gt; {
    document.addEventListener('mousemove', (e) =&amp;gt; {
       setCirclePosition({left: e.clientX - 500, top: e.clientY - 500})
    })
  }, [])

  return (
    &amp;lt;&amp;gt;
    &amp;lt;section className={styles.textWrap}&amp;gt;
      &amp;lt;h2&amp;gt;
        &amp;lt;div 
        ref={textBox}
        className={styles.textBg}&amp;gt;
          &amp;lt;div 
          className={`${styles.textBox} ${styles.textBox1}`}
          style={{left: circlePosition.left, top: circlePosition.top}}&amp;gt;&amp;lt;/div&amp;gt;
          &amp;lt;div 
          className={`${styles.textBox} ${styles.textBox2}`}
          style={{left: circlePosition.left, top: circlePosition.top}}&amp;gt;&amp;lt;/div&amp;gt;
          &amp;lt;div 
          className={`${styles.textBox} ${styles.textBox3}`}
          style={{left: circlePosition.left, top: circlePosition.top}}&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/h2&amp;gt;
    &amp;lt;/section&amp;gt;
    &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 dependency를 최초 로드 시로 만들고 이벤트를 등록해주니 이벤트가 잘 작동되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이벤트 등록 해제하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addEventListner를 통해 이벤트를 등록하고 난 뒤 이벤트 등록을 해제해주지 않으면, mouseover 혹은 scroll 이벤트의 경우 계속해서 이벤트를 감지하기 때문에 성능 저하를 일으킬 수 있다. 그러므로 컴포넌트가 언마운트 될 때 꼭 이벤트 등록을 해제해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 등록을 해제할 때는 useEffect의 내부 함수의 return 값으로 removeEventListener를 통해 해제해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1653479745503&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useRef, useState } from 'react';

export default function TextBtn() {
  const textBox = useRef();
  const [circlePosition, setCirclePosition] = useState({
    left: 0,
    top: 0
  })

  useEffect(() =&amp;gt; {
    const onMouseMove = (e) =&amp;gt; {
      setCirclePosition({left: e.clientX - 500, top: e.clientY - 500})
    };

    document.addEventListener('mousemove', onMouseMove);

    return () =&amp;gt; {
      document.removeEventListener('mousemove', onMouseMove);
    }
  }, [])

  return (
    &amp;lt;&amp;gt;
    &amp;lt;section className={styles.textWrap}&amp;gt;
      &amp;lt;h2&amp;gt;
        &amp;lt;div 
        ref={textBox}
        className={styles.textBg}&amp;gt;
          &amp;lt;div 
          className={`${styles.textBox} ${styles.textBox1}`}
          style={{left: circlePosition.left, top: circlePosition.top}}&amp;gt;&amp;lt;/div&amp;gt;
          &amp;lt;div 
          className={`${styles.textBox} ${styles.textBox2}`}
          style={{left: circlePosition.left, top: circlePosition.top}}&amp;gt;&amp;lt;/div&amp;gt;
          &amp;lt;div 
          className={`${styles.textBox} ${styles.textBox3}`}
          style={{left: circlePosition.left, top: circlePosition.top}}&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/h2&amp;gt;
    &amp;lt;/section&amp;gt;
    &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;useEffect의 콜백함수를 통해 document 객체에 addEventListner가 이벤트를 바인딩한다.&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;useEffect는 최초 렌더링이 끝나고 나서 실행된다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;useEffect 내부에 addEventListner로 실행되는 이벤트를 감지하는 감시자가 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;eventListner는 이벤트 트리거가 작동되기 만을 계속 기다리고 있을 수 있다. 이벤트를 종료시켜야 한다면, return 값으로 removeEventListner를 실행시켜주면 된다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>addEventListner</category>
      <category>react</category>
      <category>useEffect</category>
      <category>리액트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/57</guid>
      <comments>https://babycoder05.tistory.com/entry/React-useEffect%EC%99%80-addEventListener-window-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B7%9C%EC%B9%99#entry57comment</comments>
      <pubDate>Mon, 4 Apr 2022 18:21:52 +0900</pubDate>
    </item>
    <item>
      <title>React Context API</title>
      <link>https://babycoder05.tistory.com/entry/React-Context-API</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Context API 란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context API는 React에서 16.3 버전부터 공식적으로 제공하는 기능으로 Redux와 비슷하지만, Redux는 '상태(state)'를 관리하는 툴이고, Context API는 React 컴포넌트들끼리 props로 정보를 주고 받지 않고 전역적으로 데이터를 가져다 쓸 수 있게 만든 툴로써 약간 역할이 다르다고 볼 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Context API 문법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 Context API를 사용하기 위해서는 공통 데이터를 사용하는 컴포넌트들의 최상위 컴포넌트에 createContext()를 만들어주어야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ApAWd/btrxYbPGs2V/bxlR3ZqnGJWRggS1c7Z2S0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ApAWd/btrxYbPGs2V/bxlR3ZqnGJWRggS1c7Z2S0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ApAWd/btrxYbPGs2V/bxlR3ZqnGJWRggS1c7Z2S0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FApAWd%2FbtrxYbPGs2V%2FbxlR3ZqnGJWRggS1c7Z2S0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;447&quot; height=&quot;474&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 위의 사진처럼 구성되어 있을 때, 4번 컴포넌트와 5번 컴포넌트가 공통으로 사용하는 데이터를 전역으로 관리하고 싶으면, 2번 컴포넌트 혹은 그 상위 컴포넌트에 createContext()를 만들어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 위의 예시 같은 경우에는 Depth가 한 계단밖에 되지 않으므로 props로 전달해주는 것이 더 효율적일 수도 있다. 그러나 Depth가 깊어질 경우, Context API를 이용하면 일일이 props로 전달해주지 않아도 되는 장점이 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- createContext&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1648622344179&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/store/store.js

import { createContext } from 'react';

export const Context = createContext();

export default const Store = props =&amp;gt; {
	// 담아두고 싶은 상태를 객체 형태로 보관, 초기 생성 시에 빈 객체로 둘 수도 있고, 
    // 값을 담아둘 수도 있다.
	const users = {
    		name: &quot;Sophia&quot;,
        	gender: &quot;female&quot;
    	}
    
    return (
    	&amp;lt;Context.Provider value={users}&amp;gt;{props.children}&amp;lt;/Context.Provider&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;store.js 파일은 src 하위 경로에 생성해주고, 위와 같이 작성하면 일단 기본 context를 가져다 쓸 수 있는 구조가 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고나서 가져다가 쓸 때는 공통된 최상위 컴포넌트에 아래와 같이 Store를 import 하고 데이터를 가져다 쓸 컴포넌트들을 Store로 감싸주면 된다. 이제 데이터를 전역으로 가져다 쓸 수 있는 준비가 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1648623923162&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/App.js

import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './routes/Home';
import About from './routes/About';
import Store from './store/store';

export default function App() {
	return (
    	&amp;lt;Store&amp;gt;
    		&amp;lt;Router&amp;gt;
        		&amp;lt;Routes&amp;gt;
            			&amp;lt;Route path=&quot;/&quot; element={&amp;lt;Home /&amp;gt;} /&amp;gt;
                		&amp;lt;Route path=&quot;/about&quot; element={&amp;lt;About /&amp;gt;} /&amp;gt;
            		&amp;lt;/Routes&amp;gt;
        	&amp;lt;/Router&amp;gt;
        &amp;lt;/Store&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- useContext&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 데이터를 가져다 쓸 때는 useContext를 이용해서 store에 있는 Context를 불러와 아래와 같이 작성해주면, context는 users 객체 자체를 반환한다. context.name을 출력하면, Sophia라는 값이 나온다.&lt;/p&gt;
&lt;pre id=&quot;code_1648624731429&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useContext } from 'react';
import { Context } from '../store/store';

export default function About() {
	const context = useContext(Context);
    
	return (
    	&amp;lt;h1&amp;gt;{context.name}&amp;lt;/h1&amp;gt;
    )
}

// 화면에 Sophia 출력&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Context API vs. Redux&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context API와 Redux는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;중앙 데이터&lt;/b&gt;&lt;/span&gt;라는 측면에서 비슷하다. 그러나 Context API는 단순히 props로 계속해서 데이터를 넘겨주는 것을 피하기 위한 목적이고, Redux는 로직이 복잡한 상태 업데이트 코드를 작성할 수 있기 때문에 상태 관리를 위한 목적이라는 점에서 다르다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Context API는 실제로 아무것도 관리하지 않는다. 단순히 데이터를 뿌려주는 역할에 불과하며, 상태 관리는 실제로 useState가 한다.&lt;/li&gt;
&lt;li&gt;Redux는 상태 중앙 저장소 역할을 하며, Action과 Reducer를 통해 예측 가능한 상태 업데이트를 도와주는 라이브러리다.&lt;/li&gt;
&lt;li&gt;Redux는 미들웨어 기능을 통해 액션이 전달될 때 추가적인 작업을 할 수 있다.&lt;/li&gt;
&lt;li&gt;Redux가 많은 기능을 제공하지만 Context API에 비해 작성해야하는 코드도 많고 복잡하기 때문에 Context API가 더 가벼운 로직에 적합하다.&lt;/li&gt;
&lt;li&gt;그러나 상태 변경이 자주 일어나는 경우에는 Context API를 이용하면 성능상 이슈가 있을 수 있으므로 그럴 때는 Redux를 사용하는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>contextapi</category>
      <category>react</category>
      <category>리액트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/56</guid>
      <comments>https://babycoder05.tistory.com/entry/React-Context-API#entry56comment</comments>
      <pubDate>Wed, 30 Mar 2022 19:07:27 +0900</pubDate>
    </item>
    <item>
      <title>React Virtual DOM 비교 원리와 얕은 비교</title>
      <link>https://babycoder05.tistory.com/entry/React-Virtual-DOM-%EA%B3%BC-%EB%B9%84%EA%B5%90-%EC%9B%90%EB%A6%AC%EC%99%80-%EC%96%95%EC%9D%80-%EB%B9%84%EA%B5%90</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React 가상 돔(Virtual DOM)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서는 가상 돔을 사용한다. 이 가상 돔은 실제 DOM(Document Object Model)을 조작하는 방식이 아니라 실제 DOM을 모방한 가상의 DOM을 구성해서 원래 DOM과 비교해서 달라진 부분을 리렌더링 시켜주는 방식으로 작동을 한다. 그런데 이 때 가상 돔을 잘 이해해야만 React의 '상태'를 잘 다룰 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Virtual DOM을 사용하는 이유&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 왜 실제 DOM을 조작하지 않는지에 대해서 알아보자. Vanilla JS를 이용하여 DOM을 조작하는 방식은 무거운 작동방식이다. 실제 DOM에는 브라우저가 화면을 그리는데 필요한 모든 정보가 들어있기 때문이다. 그래서 React는 깜박거림 없이 부드러운 UX를 사용자에게 제공하기 위해 변경사항만 빠르게 파악하고 리렌더링 하기 위해 가상 DOM을 만들어 비교하는 방식을 채택했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;React는 성능 향상을 위해 실제 렌더링 된 UI를 내부적으로 JavaScript 객체로 따로 관리한다.&lt;/b&gt;&lt;/u&gt; 직접 DOM 노드를 생성하거나 접근해서 변경을 하는 것이 JavaScript 객체로 표현된 DOM 트리를 조작하는 것보다 훨씬 느리기 때문이다. (그러나 JavaScript 객체를 따로 관리하므로 메모리 사용량이 늘어난다는 단점이 있다. 일종의 DOM 캐싱으로 볼 수 있다.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React가 가상 돔을 비교하는 원리&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1173&quot; data-origin-height=&quot;785&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbrkzu/btrxGcIHqgk/EM1BkewdlBXGPKld8vgyC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbrkzu/btrxGcIHqgk/EM1BkewdlBXGPKld8vgyC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbrkzu/btrxGcIHqgk/EM1BkewdlBXGPKld8vgyC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcbrkzu%2FbtrxGcIHqgk%2FEM1BkewdlBXGPKld8vgyC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1173&quot; height=&quot;785&quot; data-origin-width=&quot;1173&quot; data-origin-height=&quot;785&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React는 실제 DOM의 UI를 가진 JavaScript 객체를 메모리상에 가지고 있다. 이 Vitual DOM은 변화를 감지하면 재조정(Reconcilation) 과정을 통해 실제 DOM과 동기화 한다. 재조정 과정은 크게 3단계로 나뉜다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;UI가 변경을 감지하면 UI를 Virtual DOM으로 렌더링한다. (실제 화면 상에 렌더링 되는 것이 아니라 비교를 위한 가상 렌더링이다.)&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;현재 Virtual DOM과 이전 Virtual DOM을 비교해 차이를 계산한다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;변경된 부분을 실제 DOM에 반영한다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React 얕은 비교(Shallow Compare)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React는 노드를 비교할 때 얕은 비교를 한다. 이 말이 무엇인가 하면, 먼저 JavaScript의 원시 자료형과 참조 자료형에 대한 이해가 바탕이 되어야 한다.&lt;/p&gt;
&lt;figure id=&quot;og_1648520858022&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;원시 자료형과 참조 자료형&quot; data-og-description=&quot;원시 자료형과 참조 자료형 원시 자료형(Primitive Data Type) 원시 자료형은 객체가 아니면서 동시에 메소드도 가지지 않는 자료형을 말하며, 다음의 자료형을 말한다. string, number, bigint, boolean, undefin&quot; data-og-host=&quot;babycoder05.tistory.com&quot; data-og-source-url=&quot;https://babycoder05.tistory.com/entry/원시-자료형과-참조-자료형&quot; data-og-url=&quot;https://babycoder05.tistory.com/entry/%EC%9B%90%EC%8B%9C-%EC%9E%90%EB%A3%8C%ED%98%95%EA%B3%BC-%EC%B0%B8%EC%A1%B0-%EC%9E%90%EB%A3%8C%ED%98%95&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b9ehd2/hyNQ7303Jq/GfpCnkXqh7AfxFUzTZKjcK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/HdeDT/hyNQ1QfkvF/ccZ5Av3uEHyJZlRjo220B0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cyYg7C/hyNQZx7eto/uszKvIlyIxNk3tchk3aKM0/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500&quot;&gt;&lt;a href=&quot;https://babycoder05.tistory.com/entry/원시-자료형과-참조-자료형&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://babycoder05.tistory.com/entry/원시-자료형과-참조-자료형&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b9ehd2/hyNQ7303Jq/GfpCnkXqh7AfxFUzTZKjcK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/HdeDT/hyNQ1QfkvF/ccZ5Av3uEHyJZlRjo220B0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cyYg7C/hyNQZx7eto/uszKvIlyIxNk3tchk3aKM0/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;원시 자료형과 참조 자료형&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;원시 자료형과 참조 자료형 원시 자료형(Primitive Data Type) 원시 자료형은 객체가 아니면서 동시에 메소드도 가지지 않는 자료형을 말하며, 다음의 자료형을 말한다. string, number, bigint, boolean, undefin&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;babycoder05.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React의 얕은 비교는 같은 레벨에서만 일어난다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;숫자, 문자열, Boolean과 같은 원시 자료형은 값을 비교한다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;배열, 객체 등 참조 자료형은 그 안의 값 혹은 attribute를 비교하지 않고, 그들의 레퍼런스(참조되는 위치)를 비교한다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 배열을 직접 수정하는 방식, 예를 들어, push, pop 과 같은 메소드로 배열을 수정한 뒤 setState에 담아주어도 기본적으로 같은 참조 위치를 가지고 있기 때문에 값의 변화를 감지하지 못한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- React에서 객체 불변성을 유지하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 배열 값을 변경할 때는 객체와 마찬가지로, &lt;u&gt;&lt;b&gt;불변성&lt;/b&gt;&lt;/u&gt;을 지켜주어야 한다. 이는 무슨 말이냐고 하면, 배열 혹은 객체의 원본을 수정하지 않고 상태 변경을 원하는 배열과 함수를 복사(깊은 복사, 얕은 복사는 안 됨)하고 나서 사용해야 한다는 뜻이다.&lt;/p&gt;
&lt;figure id=&quot;og_1648521558038&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;전개구문과 얕은 복사 &amp;amp; 깊은 복사&quot; data-og-description=&quot;전개구문 ES6 에 추가된 문법으로 '...' 으로 표시하며, 구조분해할당과 함께 사용할 수 있고, 배열과 객체 등에 할당된 값을 전개해서 사용한다. 참조 자료형과 얕은 복사 &amp;amp; 깊은 복사 - 얕은 복사 &quot; data-og-host=&quot;babycoder05.tistory.com&quot; data-og-source-url=&quot;https://babycoder05.tistory.com/entry/전개구문과-얕은-복사-깊은-복사&quot; data-og-url=&quot;https://babycoder05.tistory.com/entry/%EC%A0%84%EA%B0%9C%EA%B5%AC%EB%AC%B8%EA%B3%BC-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/rkh5f/hyNQWnUt7p/h5GTVXx4B6jl0UK413Hk01/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bHHddT/hyNQ2BC3jr/0B8Sjy0olEqeo5bt5ikYU0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/mmjMu/hyNRaTX1yu/HGGiYRDIsYAk86XeaXEDW1/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500&quot;&gt;&lt;a href=&quot;https://babycoder05.tistory.com/entry/전개구문과-얕은-복사-깊은-복사&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://babycoder05.tistory.com/entry/전개구문과-얕은-복사-깊은-복사&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/rkh5f/hyNQWnUt7p/h5GTVXx4B6jl0UK413Hk01/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bHHddT/hyNQ2BC3jr/0B8Sjy0olEqeo5bt5ikYU0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/mmjMu/hyNRaTX1yu/HGGiYRDIsYAk86XeaXEDW1/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;전개구문과 얕은 복사 &amp;amp; 깊은 복사&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;전개구문 ES6 에 추가된 문법으로 '...' 으로 표시하며, 구조분해할당과 함께 사용할 수 있고, 배열과 객체 등에 할당된 값을 전개해서 사용한다. 참조 자료형과 얕은 복사 &amp;amp; 깊은 복사 - 얕은 복사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;babycoder05.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 원본을 수정하는 메소드인 push(), pop()과 같은 메소드를 사용해서 원본을 직접 수정하는 것은 React를 사용할 때 지양해야 한다. 대신, &lt;u&gt;&lt;b&gt;assign() 메소드를 사용하거나, 전개구문을 사용해서 복사를 한 뒤 그 복사값을 수정하고 setState에 담아주면 변경을 감지할 수 있다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;</description>
      <category>코딩 공부 일지/React JS</category>
      <category>react</category>
      <category>Virtual DOM</category>
      <category>가상 돔</category>
      <category>리액트</category>
      <category>비교 원리</category>
      <category>얕은 비교</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/55</guid>
      <comments>https://babycoder05.tistory.com/entry/React-Virtual-DOM-%EA%B3%BC-%EB%B9%84%EA%B5%90-%EC%9B%90%EB%A6%AC%EC%99%80-%EC%96%95%EC%9D%80-%EB%B9%84%EA%B5%90#entry55comment</comments>
      <pubDate>Tue, 29 Mar 2022 11:45:36 +0900</pubDate>
    </item>
    <item>
      <title>TDZ를 아시나요? - 에러 기록</title>
      <link>https://babycoder05.tistory.com/entry/TDZ%EB%A5%BC-%EC%95%84%EC%8B%9C%EB%82%98%EC%9A%94-%EC%97%90%EB%9F%AC-%EA%B8%B0%EB%A1%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 대망의 첫 면접을 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접관들의 질문에 어찌저찌 잘 대답을 하고, 드디어 코딩테스트!! 두둥!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트로 구현하는 문제였는데, 1에서 25까지 숫자를 5X5 빙고 배열에 랜덤으로 출력하고 랜덤 버튼을 누를 때 마다 다시 랜덤으로 숫자 위치가 바뀌는 것을 구현하는 것이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 랜덤 배열을 구성하기 위해 아래와 같은 코드를 작성했다.&lt;/p&gt;
&lt;pre id=&quot;code_1648465688440&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function randomBingo() {
    let index = bingoEl.length - 1
    while(index &amp;gt; 0) {
      const randomIndex = Math.floor(Math.random() * bingoEl.length)
      [bingoEl[index], bingoEl[randomIndex]] = [bingoEl[randomIndex], bingoEl[index]]
      index--
    }
    return bingoEl
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 아래와 같은 에러가 났다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bezrPI/btrxHYvMjNo/wPf0SaARmy1OgC028ON4M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bezrPI/btrxHYvMjNo/wPf0SaARmy1OgC028ON4M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bezrPI/btrxHYvMjNo/wPf0SaARmy1OgC028ON4M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbezrPI%2FbtrxHYvMjNo%2FwPf0SaARmy1OgC028ON4M0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;483&quot; height=&quot;245&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;245&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 구글링을 하고 싶은 마음이 굴뚝 같았다ㅠㅠ 그런데 시험에서 구글링을 하면 안 될 것 같아서 이유도 모르는 에러를 가지고 낑낑대다가 결국 풀지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 집에 와서 검색해보니... TDZ 문제라는 것을 알았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;TDZ 란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDZ는 'Temporal Dead Zone'의 약자로 변수가 선언되기 전에 변수에 접근할 때 (var는 해당 안 됨, 호이스팅이 일어나니까) 변수에 접근하지 못하도록 금지하는 규칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 아래와 같은 코드가 있다고 가정하자.&lt;/p&gt;
&lt;pre id=&quot;code_1648466179186&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(fruit)
const fruit = 'strawberry'&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1648466365705&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fruit = 'strawberry'
console.log(fruit)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 코드에서는 console.log가 변수 선언 전에, 두번째 코드는 변수 선언 후에 실행되도록 만들었다. 첫번째 코드를 실행하면 아래와 같은 에러가 뜬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdFPRJ/btrxB4w3MAq/A75o9TaQQJq8UKGHILWP4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdFPRJ/btrxB4w3MAq/A75o9TaQQJq8UKGHILWP4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdFPRJ/btrxB4w3MAq/A75o9TaQQJq8UKGHILWP4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdFPRJ%2FbtrxB4w3MAq%2FA75o9TaQQJq8UKGHILWP4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;482&quot; height=&quot;54&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 코드는 정상 작동한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;28&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H1j93/btrxIMBSqvS/555SbPXyLp5xOXVoUKiLyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H1j93/btrxIMBSqvS/555SbPXyLp5xOXVoUKiLyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H1j93/btrxIMBSqvS/555SbPXyLp5xOXVoUKiLyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH1j93%2FbtrxIMBSqvS%2F555SbPXyLp5xOXVoUKiLyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;483&quot; height=&quot;28&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;28&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 코드에서는 변수가 선언되기 전에 변수에 접근했기 때문이다. 이 때 변수가 선언되기 이전의 공간을 TDZ라고 한다. 변수가 선언되기 전에는 변수에 접근할 수 없다. 이러한 경우는 let과 const로 변수를 선언하거나 Class형 객체를 선언할 때 일어난다. (함수선언문은 해당 안 됨, 마찬가지로 호이스팅이 일어나기 때문에!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 다시 나의 코드로 돌아가보자.&lt;/p&gt;
&lt;pre id=&quot;code_1648466762314&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function randomBingo() {
    let index = bingoEl.length - 1
    while(index &amp;gt; 0) {
      const randomIndex = Math.floor(Math.random() * bingoEl.length)
      [bingoEl[index], bingoEl[randomIndex]] = [bingoEl[randomIndex], bingoEl[index]]
      index--
    }
    return bingoEl
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 코드를 보면 전혀 문제될 것이 없어보인다. 모두 선언 이후에 변수에 접근했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 한 가지 간과한 사실이 있었다. randomIndex 선언 이후 구조분해할당으로 배열을 바꿀 때 배열만 코드에 이루어져 있으니 코드를 구분하기가 어려웠던 것이다. 다시 말하면, const randomIndex를 선언하는 줄 마지막에 세미 콜론을 찍어주니 오류가 사라졌다.... randomIndex를 선언하는 것을 끝내지 않은 것으로 해석이 된 것 같았다. 그래서 randomIndex 변수에 접근을 할 수가 없었던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하아... 세미콜론을 잘 찍자....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 계속해서 TDZ에 대해 알아보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;TDZ의 여러 예시&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. const, let 변수&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1648467203382&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(fruit); // 변수 선언 이전에 접근할 경우 TDZ에 속하므로 에러를 일으킨다.
const fruit = 'strawberry';

console.log(num); // let 도 마찬가지다.
let num = 2;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Class 구문&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1648467351403&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const sally = new Student('sally') // 클래스 선언 전에 새로운 인스턴스를 생성할 수 없다.

class Student {
	constructor(name) {
    	this.name = name;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. contructor() 내부의 super()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1648467595590&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SmartStudent extends Student {
	constructor(name, score) {
    	this.score = score;
        super(name) // super가 constructor 최상단에 선언된 후 this 바인딩에 접근할 수 있다. TDZ 에러
    }
}

const sally = new SmartStudent('sally', '100')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDZ는 인스턴스를 초기화하기 위해 부모 클래스의 생성자를 먼저 호출할 것을 제안한다. 부모 클래스 생성자를 호출하고 인스턴스가 준비되면 자식 클래스에서 this 값을 변경할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 변수의 유효범위에 대해 생각하자. 그리고 변수는 가급적 유효범위 안의 최상단에 선언하고, 세미콜론을 잘 찍자...&lt;/p&gt;</description>
      <category>코딩 공부 일지/JavaScript</category>
      <category>JavaScript</category>
      <category>TDZ</category>
      <category>자바스크립트</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/54</guid>
      <comments>https://babycoder05.tistory.com/entry/TDZ%EB%A5%BC-%EC%95%84%EC%8B%9C%EB%82%98%EC%9A%94-%EC%97%90%EB%9F%AC-%EA%B8%B0%EB%A1%9D#entry54comment</comments>
      <pubDate>Mon, 28 Mar 2022 20:45:03 +0900</pubDate>
    </item>
    <item>
      <title>브라우저 렌더링 동작 과정</title>
      <link>https://babycoder05.tistory.com/entry/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. HTML 파일과 CSS 파일을 파싱(Parsing)해서 각각 Tree를 만든다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 HTTP 프로토콜을 통해 IP 주소에 해당하는 서버에 접속해서 웹사이트를 구성하기 위한 리소스(HTML, CSS, JavaScript)를 받아오면 가장 먼저 HTML 파일을 해석(Parsing)한다. 그리고 해석 단계를 거쳐 DOM(Document Object Model) 트리를 구성한다. HTML 파일에 스타일시트도 연결이 되어 있다면, 스타일시트도 해석해서 CSSOM(CSS Object Model) 트리를 구성한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- DOM 트리 생성&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저가 HTML의 원시 바이트를 읽어와서, HTML 파일에 정의된 인코딩 방식(예: UTF-8)에 따라 개별 문자로 변환한다.&lt;/li&gt;
&lt;li&gt;브라우저가 문자열을 W3C 표준에 지정된 고유 토큰으로 변환한다.&lt;/li&gt;
&lt;li&gt;방출된 토큰은 해당 속성 및 규칙을 정의하는 객체로 변환된다.&lt;/li&gt;
&lt;li&gt;마지막으로 HTML 마크업에 정의된 여러 태그 간의 관계를 해석해서 트리 구조로 연결한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- CSSOM 트리 생성&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML 문서 내부에 style 태그로 작성되어 있든 외부 파일로 CSS 파일이 연결되어 있든 상관없이 브라우저가 이해하는 방식으로 변환한다.&lt;/li&gt;
&lt;li&gt;DOM 트리 생성과정과 비슷한 과정으로 해석해서 트리 구조로 연결한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- HTML 파일 파싱 중에 &amp;lt;script&amp;gt; 태그를 만나면?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 파일을 파싱하는 중에 &amp;lt;head&amp;gt; 영역에 선언된 &amp;lt;script&amp;gt; 태그를 만나면 진행중인 HTML 파싱을 중지하고 JS 파일 혹은 작성된 스크립트들을 읽어들인다. 그리고 나서 다시 HTML 파일로 돌아가 파싱이 중단된 시점부터 다시 파싱을 시작한다. 이렇게 되면 아직 DOM 트리가 다 구성되지 않은 상태에서 &amp;lt;script&amp;gt;를 읽기 때문에 스크립트가 제대로 동작하지 않게 되고(DOM 요소를 제어할 경우), 레이아웃 구성이 느려져서 사용자 경험을 떨어뜨리는 결과를 초래할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로 &lt;u&gt;&lt;b&gt;&amp;lt;script&amp;gt; 태그는 HTML 문서의 제일 마지막에 위치시키거나, defer나 async 속성을 명시하는 것을 권장한다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1648026104219&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;코딩독학) script 태그, async와 defer&quot; data-og-description=&quot;async와&amp;nbsp;defer Parsing 이란? 영단어 &amp;lsquo;Parse&amp;rsquo; 의 뜻은 &amp;lsquo;문장을 문법적으로 분석하다.&amp;rsquo; 라는 뜻으로 프로그래밍의 개념에서는 브라우저가 html, css, javascript 같은 프로그래밍 언어를 읽고 해석하여 &quot; data-og-host=&quot;babycoder05.tistory.com&quot; data-og-source-url=&quot;https://babycoder05.tistory.com/entry/코딩독학-script-태그-async와-defer&quot; data-og-url=&quot;https://babycoder05.tistory.com/entry/%EC%BD%94%EB%94%A9%EB%8F%85%ED%95%99-script-%ED%83%9C%EA%B7%B8-async%EC%99%80-defer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/coV4qw/hyNNuZGAIU/pEFtOSw8uoTe0CzFxKKzvK/img.png?width=636&amp;amp;height=201&amp;amp;face=0_0_636_201,https://scrap.kakaocdn.net/dn/cMVyCu/hyNNp488ik/CfZeGnK8ZtTn2KSKdGajkK/img.png?width=636&amp;amp;height=201&amp;amp;face=0_0_636_201,https://scrap.kakaocdn.net/dn/o6h6c/hyNNaNGIqm/WV6SyWbMk9slA6yIZtOCH0/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500&quot;&gt;&lt;a href=&quot;https://babycoder05.tistory.com/entry/코딩독학-script-태그-async와-defer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://babycoder05.tistory.com/entry/코딩독학-script-태그-async와-defer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/coV4qw/hyNNuZGAIU/pEFtOSw8uoTe0CzFxKKzvK/img.png?width=636&amp;amp;height=201&amp;amp;face=0_0_636_201,https://scrap.kakaocdn.net/dn/cMVyCu/hyNNp488ik/CfZeGnK8ZtTn2KSKdGajkK/img.png?width=636&amp;amp;height=201&amp;amp;face=0_0_636_201,https://scrap.kakaocdn.net/dn/o6h6c/hyNNaNGIqm/WV6SyWbMk9slA6yIZtOCH0/img.jpg?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩독학) script 태그, async와 defer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;async와&amp;nbsp;defer Parsing 이란? 영단어 &amp;lsquo;Parse&amp;rsquo; 의 뜻은 &amp;lsquo;문장을 문법적으로 분석하다.&amp;rsquo; 라는 뜻으로 프로그래밍의 개념에서는 브라우저가 html, css, javascript 같은 프로그래밍 언어를 읽고 해석하여&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;babycoder05.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. HTML 트리와 CSS 트리를 결합하여 렌더링 트리를 만든다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 파일로부터 구성된 DOM 트리와 스타일시트로부터 구성된 CSSOM 트리가 생성되면 그 둘을 매칭시켜 렌더링 트리를 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링 트리에는 페이지를 렌더링 하는데 필요한 노드만 포함된다. 대표적으로, visibility: hidden 의 스타일 속성을 가진 요소는 렌더링 트리에 포함이 되지만, display: none 의 속성을 가질 경우 렌더링 트리에서 제외된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 렌더링 트리에서 각 노드의 위치와 크기를 계산한다. (Layout)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음엔 각 노드를 화면에 배치하기 위해 위치와 크기를 계산한다. 이 때, 모든 상대적인 측정값은 화면에서 절대적인 픽셀로 변환된다. 만약 단위를 %로 지정했다면, 브라우저는 % 값을 계산해서 픽셀 단위로 변환한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 계산된 값을 이용해 각 노드를 화면 상의 실제 픽셀로 변환하고, 레이어를 만든다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산이 완료되면 실제로 화면 상에 각 노드를 픽셀로 구현한다. 이를 &amp;lsquo;페인팅&amp;rsquo; 또는 &amp;lsquo;래스터화&amp;rsquo;라고 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- 리플로우(Reflow), 리페인트(Repaint)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초의 페이지 접속 시 렌더링 과정을 거쳐 화면에 페인팅이 종료된 이후에 사용자의 여러 가지 상호작용으로 인해 새로운 HTML 요소가 추가되거나, 기존 요소의 스타일이 바뀌는 변경이 일어난다. 이 경우에는 렌더링 트리 생성과 레이아웃을 계산하는 과정을 다시 수행한다. 이러한 과정을 리플로우(Reflow)라고 한다. 그리고 그 결과로 다시 화면에 구현하는 것을 리페인트(Repaint)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 요소에 변경사항이 생겼을 때 모든 경우에 리플로우-리페인트가 일어나는 것은 아니고, 레이아웃에 영향이 미치지 않는 단순한 색상 변경 같은 변경사항은 리플로우 수행 없이 바로 리페인트만 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩 공부 일지/Browser &amp;amp; Network</category>
      <category>DOM트리</category>
      <category>동작과정</category>
      <category>렌더링</category>
      <category>리페인트</category>
      <category>리플로우</category>
      <category>브라우저</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/53</guid>
      <comments>https://babycoder05.tistory.com/entry/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95#entry53comment</comments>
      <pubDate>Wed, 23 Mar 2022 18:04:46 +0900</pubDate>
    </item>
    <item>
      <title>웹 브라우저 쿠키에 대해 알아보자</title>
      <link>https://babycoder05.tistory.com/entry/%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%BF%A0%ED%82%A4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;쿠키의 탄생 배경&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;853&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqT5iO/btrwWfMGWg4/uDzxAmijXX0hlONAshWYik/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqT5iO/btrwWfMGWg4/uDzxAmijXX0hlONAshWYik/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqT5iO/btrwWfMGWg4/uDzxAmijXX0hlONAshWYik/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqT5iO%2FbtrwWfMGWg4%2FuDzxAmijXX0hlONAshWYik%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;853&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;853&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP는 상태를 계속 유지하지 않는 스테이트리스(Stateless)프로토콜이다. 서버와 클라이언트 간에 리퀘스트와 리스폰스를 교환하는 동안에 상태를 관리하지 않기 때문에 이전에 되돌려준 리스폰스에 대해 기억하지 못한다. 이는 많은 데이터를 매우 빠르고 확실하게 처리하기 위해 설계되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 웹이 진화함에 따라 스테이트리스 특성만으로 처리하기 어려운 일이 증가하게 되었다. 예를 들어, 로그인을 했을 때 다른 페이지로 이동하더라도 로그인 상태를 유지할 필요가 있다. 그러나 페이지를 넘나들 때 즉, 리퀘스트 요청과 리스폰스 결과를 받을 때 로그인 상태를 주고 받지 않으므로, 이러한 스테이트리스의 특성을 보완하기 위해 쿠키(Cookie) 라는 기술이 도입되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;쿠키란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠키는 리퀘스트와 리스폰스에 쿠키 정보를 추가해서 클라이언트의 상태를 파악하는 시스템이다. 쿠키는 서버에서 리스폰스로 보내진 Set-Cookie 라는 헤더 필드에 의해 쿠키를 클라이언트에 보존한다. 다음 번에 클라이언트가 같은 서버로 리퀘스트를 보낼 때 자동으로 쿠키 값을 넣어서 송신한다. 서버는 클라이언트가 보내온 쿠키를 확인해서 어느 클라이언트가 접속했는지 체크하고 서버 상의 기록을 확인해서 이전 상태를 알 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;쿠키 종류&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분석 쿠키: 무엇을 검색하는지, 많이 검색하는지, 시간, 언어 대상 등의 정보를 수집&lt;/li&gt;
&lt;li&gt;광고 쿠키: 검색 내용, 국가, 언어에 따라 광고 게재&lt;/li&gt;
&lt;li&gt;사용자 쿠키: 쇼핑몰 장바구니 저장 기능, 로그인 상태 유지, 3일 간, 7일 간 등 일정 기간 다시 보지 않기 체크&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>코딩 공부 일지/Browser &amp;amp; Network</category>
      <category>http</category>
      <category>쿠키</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/52</guid>
      <comments>https://babycoder05.tistory.com/entry/%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%BF%A0%ED%82%A4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90#entry52comment</comments>
      <pubDate>Wed, 23 Mar 2022 17:13:55 +0900</pubDate>
    </item>
    <item>
      <title>www.google.com 을 주소창에 치면 일어나는 일 (What happens when type www.google.com)</title>
      <link>https://babycoder05.tistory.com/entry/wwwgooglecom-%EC%9D%84-%EC%A3%BC%EC%86%8C%EC%B0%BD%EC%97%90-%EC%B9%98%EB%A9%B4-%EC%9D%BC%EC%96%B4%EB%82%98%EB%8A%94-%EC%9D%BC-What-happens-when-type-wwwgooglecom</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 기본적인 네트워크 지식을 쌓기 위해 제 방식대로 정리한 글로 오류가 있을 수 있습니다. 오류가 있으면 댓글로 알려주시면 감사하겠습니다 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. &lt;a href=&quot;http://www.google.com&quot;&gt;www.google.com&lt;/a&gt; 을 브라우저 주소창에 친다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 브라우저는 캐싱된 DNS 기록들에서 &lt;a href=&quot;http://www.google.com&quot;&gt;www.google.com&lt;/a&gt; 에 대응되는 IP 주소가 있는지 확인한다.&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DNS 란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS(Domain Name System)은 URL들의 이름과 IP주소를 저장하고 있는 데이터베이스다. 인터넷에 있는 모든 URL들에는 고유의 IP 주소가 지정되어있다. 이 IP 주소를 통해서 해당 웹사이트를 호스팅하고 있는 서버 컴퓨터에 접근을 할 수 있다. (예를 들어, &lt;a href=&quot;http://www.google.com&quot;&gt;www.google.com&lt;/a&gt;의 IP 주소를 알아보기 위해서는 &lt;b&gt;nslookup &lt;a href=&quot;http://www.google.com&quot;&gt;www.google.com&lt;/a&gt;&lt;/b&gt; 을 터미널에 작성하면 해당 사이트의 IP 주소를 알려준다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 내가 있는 지역에서 해당 명령어를 작성했을 때 142.250.66.132이 나왔고, 해당 IP 주소를 브라우저에 검색했을 때 &lt;a href=&quot;http://www.google.com&quot;&gt;www.google.com&lt;/a&gt;의 결과와 같았다. 구글을 사용하는 사용자가 매우 많기 때문에 구글의 서버 IP 주소는 여러 개가 있고, nslookup으로 그 중 내가 접근이 가능한 IP 주소를 보여준 것이다. 이처럼 DNS는 경도 위도로 주소를 외우지 않고 도로명 주소로 주소를 외우는 것과 비슷하다. 사용자가 외우기 쉬운 주소를 컴퓨터가 해석할 수 있는 IP 주소로 바꿔주는 시스템이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CACHE 란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터는 사용자의 명령이 입력되면 CPU는 연산을 하고 동작에 필요한 모든 내용이 전원이 유지되는 내내 RAM에 저장된다. 컴퓨터가 발전하면서 CPU의 처리속도가 RAM에 비해 훨씬 빨라져 왔기 때문에 CPU가 RAM에 접근할 때 병목현상이 일어나게 되었다. 이를 방지하고자 CPU와 RAM 사이에 용량은 작지만 속도가 빠른 작은 메모리를 탑재했는데 이것이 바로 캐시다. 캐시에는 CPU 캐시, 디스크 캐시, 브라우저 캐시 등 다양하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. DNS 쿼리를 캐시에서 먼저 실행한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터는 주소창에 입력된 주소를 브라우저 캐시에서 가장 먼저 확인한다. 브라우저는 일정 기간동안의 DNS 기록들을 저장하고 있다. (매번 새로 데이터를 받아오는 것보다 일정 기간동안 데이터를 보관해두는 것이 속도를 더 빠르게 만들 수 있기 때문이다.) DNS 쿼리(대응되는 IP 주소를 찾는 것)가 캐시에서 가장 먼저 실행이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 그 다음엔 브라우저는 OS(운영체제) 캐시를 확인한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 캐시에서 해당 IP 를 발견하지 못하면, systemcall을 통해서 OS가 저장하고 있는 DNS 기록들의 캐시에 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 그 다음엔 router 캐시를 확인한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터에서 DNS 기록을 찾지 못하면 브라우저는 DNS 기록을 캐싱하고 있는 router 와 통신을 해서 찾으려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 그래도 못 찾으면 마지막으로 ISP 캐시를 확인한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ISP는 인터넷 망을 제공해주는 KT, SKT와 같은 업체를 말한다. 이러한 ISP 들은 DNS 서버를 구축하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이렇게 많은 곳에서 캐시들을 저장하는지 궁금할 것이다. 개인정보를 생각했을 때 정보가 여기저기에 캐싱된 게 조금 불편할 수 있겠지만, 캐시는 네트워크 트래픽을 조절하고 데이터 전송 시간을 줄이기 위해 매우 중요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 요청한 URL이 캐시에 없으면, ISP의 DNS 서버가 &lt;a href=&quot;http://www.google.com/&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;www.google.com&lt;/span&gt;&lt;/a&gt;을 호스팅하고 있는 서버의 IP 주소를 찾기 위해 DNS query를 날린다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ISP의 DNS 서버는 인터넷을 통해 다른 DNS 서버들에게 물어 도메인 이름의 올바른 IP 주소를 찾는데 책임을 가지고 있다. ISP의 DNS 서버를 DNS recursor라고 하고, 다른 DNS 서버를 name server 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS recursor는 name server에 연락한다. 다른 name server 들 중에서 &lt;a href=&quot;http://www.google.com&quot;&gt;www.google.com&lt;/a&gt; 에 매칭되는 IP 주소를 찾으면 DNS recursor로 전송한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모든 요청들은 작은 데이터 패킷들을 통해서 보내진다. 패킷 안에는 보내는 요청의 내용과 DNS recursor의 IP 주소가 포함되어 있다. 이 패킷들은 원하는 DNS 기록을 가진 DNS 서버에 도달할 때까지 클라이언트와 서버를 여러 번 오간다. 패킷들이 움직이는 것도 Routing table 에 기반한다. Routing table을 통해서 어떤 길로 가야 가장 빠른지 확인할 수 있다. 만약 패킷이 도중에 손실되면 Request fail error가 발생하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. Browser가 서버와 TCP connection 을 한다.&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;TCP/IP 란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터들끼리 네트워크로 소통을 할 때 필요한 통신규약을 말한다. IP 주소 지정 방법, 떨어진 상대를 찾기 위한 방법과 그 곳에 도달하는 순서, 그리고 웹을 표시하기 위한 순서들에 대해 정의한 규칙이다. 이러한 통신규약에는 여러 가지가 있는데 대표적인 것이 HTTP 프로토콜이고, IP 프로토콜을 사용하는 모든 프로토콜을 총칭하여 TCP/IP 프로토콜이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 올바른 IP 주소를 받게 되면 IP 주소에 해당하는 서버와 connection 을 빌드하게 된다. 브라우저는 인터넷 프로토콜을 사용해서 서버와 연결이 된다. 인터넷 프로토콜의 종류는 여러가지가 있지만, 웹사이트의 HTTP 요청의 경우에는 일반적으로 TCP 를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트와 서버 간의 데이터 패킷들이 오가려면 TCP connection 이 되어야 한다. TCP/IP three-way handshake 라는 프로세스를 통해 클라이언트와 서버 간의 connection 이 이뤄지게 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트 머신이 SYN 패킷을 서버에 보내고 connection 을 열어달라고 물어본다.&lt;/li&gt;
&lt;li&gt;서버가 새로운 connection 을 시작할 수 있는 포트가 있다면 SYN/ACK 패킷으로 대답을 한다.&lt;/li&gt;
&lt;li&gt;클라이언트는 SYN/ACK 패킷을 서버로부터 받으면 서버에게 ACK 패킷을 보낸다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정이 끝나면 TCP connection 이 완성된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. Browser가 웹 서버에 HTTP 프로토콜에 따른 요청을 한다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 로 연결이 되었다면, 데이터를 전송하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트의 브라우저는 GET 요청을 통해 서버에게 &lt;a href=&quot;http://www.google.com&quot;&gt;www.google.com&lt;/a&gt; 의 웹페이지를 요구한다. 요청을 할 때 비밀 자료들을 포함하던지, form을 제출하는 상황에서는 POST 요청을 사용할 수도 있다. 이 요청을 할 때 다른 부가적인 정보들도 함께 전달이 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;browser identification(User-Agent 헤더)&lt;/li&gt;
&lt;li&gt;받아들일 요청의 종류(Accept 헤더)&lt;/li&gt;
&lt;li&gt;추가적인 요청을 위해 TCP connection 을 유지를 요청하는 connection 헤더&lt;/li&gt;
&lt;li&gt;브라우저에서 얻은 쿠키 정보&lt;/li&gt;
&lt;li&gt;기타 등등&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6. 서버가 요청을 처리하고 response를 생성하고 클라이언트로 보낸다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 웹서버를 가지고 있다.(i.e. Apache, IIS...) 이들은 브라우저로부터 요청을 받고 request handler한테 요청을 전달해서 요청을 읽고 response를 생성하게 된다. Request handler 란 ASP.NET, PHP, Ruby 등으로 작성된 프로그램을 의미한다. 이 request handler는 요청과 요청의 헤더, 쿠키를 읽어서 요청이 무엇인지 파악하고 필요하다면 서버에 정보를 업데이트 한다. 그 다음에 response를 특정한 포맷으로(JSON, XML, HTML) 작성을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 response에는 요청한 웹페이지, status code, compression type(Content-Encoding) - 어떻게 인코딩 되어 있는지, 어떻게 페이지를 캐싱할지(Cache-control), 설정할 쿠키가 있다면 쿠키, 개인정보 등이 포함된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7. Browser가 HTML content를 보여준다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 HTML content를 단계적으로 보여준다. 처음에는 HTML의 스켈레톤을 렌더링한다. 그 다음에는 HTML tag들을 체크하고 추가적으로 필요한 웹페이지 요소들(이미지 파일, CSS 스타일시트, JavaScript 파일 등)을 GET으로 요청한다. 이 정적인 파일들은 브라우저에 의해 캐싱이 되어 나중에 해당 페이지를 방문할 때 다시 서버로부터 불러와지지 않도록 한다. 그 다음에는 그토록 원했던 &lt;a href=&quot;http://www.google.com/&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;www.google.com&lt;/span&gt;&lt;/a&gt; 의 모습이 보이게 된다.&lt;/p&gt;</description>
      <category>코딩 공부 일지/Browser &amp;amp; Network</category>
      <category>http</category>
      <category>ip</category>
      <category>what happens when type google</category>
      <category>네트워크 과정</category>
      <author>헬로코딩</author>
      <guid isPermaLink="true">https://babycoder05.tistory.com/51</guid>
      <comments>https://babycoder05.tistory.com/entry/wwwgooglecom-%EC%9D%84-%EC%A3%BC%EC%86%8C%EC%B0%BD%EC%97%90-%EC%B9%98%EB%A9%B4-%EC%9D%BC%EC%96%B4%EB%82%98%EB%8A%94-%EC%9D%BC-What-happens-when-type-wwwgooglecom#entry51comment</comments>
      <pubDate>Wed, 23 Mar 2022 16:42:55 +0900</pubDate>
    </item>
  </channel>
</rss>